From ef6fc94832fc59f49a9b4637d0f09b5d7d71e0d8 Mon Sep 17 00:00:00 2001 From: Niknoc Date: Sat, 23 Nov 2024 11:22:21 -0700 Subject: [PATCH 1/2] Delete src/DataService/DataService.php --- src/DataService/DataService.php | 1982 ------------------------------- 1 file changed, 1982 deletions(-) delete mode 100644 src/DataService/DataService.php diff --git a/src/DataService/DataService.php b/src/DataService/DataService.php deleted file mode 100644 index 688ab41b..00000000 --- a/src/DataService/DataService.php +++ /dev/null @@ -1,1982 +0,0 @@ -updateServiceContextSettingsForOthers($serviceContext); - } - - /** - * Set the corresponding settings for the dataService based on ServiceContext - * @var ServiceContext $serviceContext The service Context for this DataService - */ - public function updateServiceContextSettingsForOthers($serviceContext) - { - $this->setupServiceContext($serviceContext); - $this->setupSerializers(); - $this->useMinorVersion(); - $this->setupRestHandler($serviceContext); - } - - /** - * Set or Update the ServiceContext of this DataService. - * - * @var ServiceContext $serviceContext The new ServiceContext passed by. - * @return $this - */ - private function setupServiceContext($serviceContext) - { - $this->serviceContext = $serviceContext; - return $this; - } - - /** - * Return the ServiceContext of this DataService - * - * @return ServiceContext - * @throws \Exception ServiceContext is NULL. - */ - public function getServiceContext() - { - if (isset($this->serviceContext)) { - return $this->serviceContext; - } else { - throw new SdkException("Trying to Return an Empty Service Context."); - } - } - - /** - * Set the SyncRest Handler for the DataService. If the client Name changed, the underlying Client that SyncRestHandler used will also changed. - * - * @var ServiceContext $serviceContext The service Context for this DataService - * @return $this - * - */ - protected function setupRestHandler($serviceContext) - { - if(isset($serviceContext)){ - $client = ClientFactory::createClient($this->getClientName()); - $this->restHandler = new SyncRestHandler($serviceContext, $client); - }else{ - throw new SdkException("Can not set the Rest Client based on null ServiceContext."); - } - return $this; - } - - /** - * Return the current Client Name used by DataService - * @return String clientName - */ - public function getClientName(){ - return $this->clientName; - } - /** - * PHP SDK currently only support XML for Object Serialization and Deserialization, except for Report Service - * - * @return $this - */ - public function useXml() - { - $serviceContext = $this->getServiceContext(); - $serviceContext->useXml(); - $this->updateServiceContextSettingsForOthers($serviceContext); - return $this; - } - - /** - * PHP SDK currently only support XML for Object Serialization and Deserialization, except for Report Service - * - * @return $this - */ - public function useJson() - { - $serviceContext = $this->getServiceContext(); - $serviceContext->useJson(); - $this->updateServiceContextSettingsForOthers($serviceContext); - return $this; - } - - /** - * Set a new directory for request and response log - * - * @param String $new_log_location The directory path for storing request and response log - * - * @return $this - */ - public function setLogLocation($new_log_location) - { - $restHandler = $this->restHandler; - $loggerUsedByRestHandler = $restHandler->getRequestLogger(); - $loggerUsedByRestHandler->setLogDirectory($new_log_location); - return $this; - } - - /** - * Set logging for OAuth calls - * - * @param Boolean $enableLogs Turns on logging for OAuthCalls - * - * @param Boolean $debugMode Turns on debug mode to log tokens - * - * @param String $new_log_location The directory path for storing request and response log - * - * @return $this - */ - public function setLogForOAuthCalls($enableLogs, $debugMode, $new_log_location) - { - $this->OAuth2LoginHelper->setLogForOAuthCalls($enableLogs, $debugMode, $new_log_location); - return $this; - } - - /** - * Set a new Minor Version - * - * @param String $newMinorVersion The new minor version that passed - * - * @return $this - */ - public function setMinorVersion($newMinorVersion) - { - $serviceContext = $this->getServiceContext(); - $serviceContext->setMinorVersion($newMinorVersion); - $this->updateServiceContextSettingsForOthers($serviceContext); - return $this; - } - - /** - * Disable the logging function - * - * @return $this - */ - public function disableLog() - { - $restHandler = $this->restHandler; - $loggerUsedByRestHandler = $restHandler->getRequestLogger(); - $loggerUsedByRestHandler->setLogStatus(false); - return $this; - } - - /** - * Enable the logging function - * - * @return $this - */ - public function enableLog() - { - $restHandler = $this->restHandler; - $loggerUsedByRestHandler = $restHandler->getRequestLogger(); - $loggerUsedByRestHandler->setLogStatus(true); - return $this; - } - - - /** - * Choose if want to throw exception when there is an non-200 http status code returned. - * @param Boolean $bool Turn on exception throwing error or not - */ - public function throwExceptionOnError($bool){ - $this->throwExceptionOnError = $bool; - return $this; - } - - /** - * Return the settings for thrown exception on non-200 error code - * @return Boolean Thrown Exception on Error or not - */ - public function isThrownExceptionOnError(){ - return $this->throwExceptionOnError; - } - - /** - * Return the client Name used by this DataSerivce - * @return String the Client Name. It can be curl or GuzzleHttpClient - * @deprecated since version 5.0.4 - * @see $this->getClientName() - */ - public function getClinetName(){ - return $this->getClientName(); - } - - - /** - * The client Name can be either 'curl', 'guzzle', or 'guzzlehttp'. - * - * @param String $clientName The client Name used by the service - * - * @return $this - */ - public function setClientName($clientName){ - $this->clientName = $clientName; - $serviceContext = $this->getServiceContext(); - $this->setupRestHandler($serviceContext); - return $this; - } - - /** - * New Static function for static Reading from Config or Passing Array - * The config needs to include - * - * @param $settings - * @return DataService - * @throws SdkException - * @throws SdkException - */ - public static function Configure($settings) - { - if (isset($settings)) { - if (is_array($settings)) { - $ServiceContext = ServiceContext::ConfigureFromPassedArray($settings); - if (!isset($ServiceContext)) { - throw new SdkException('Construct ServiceContext from OAuthSettigs failed.'); - } - $DataServiceInstance = new DataService($ServiceContext); - - } elseif (is_string($settings)) { - $ServiceContext = ServiceContext::ConfigureFromLocalFile($settings); - if (!isset($ServiceContext)) { - throw new SdkException('Construct ServiceContext from File failed.'); - } - $DataServiceInstance = new DataService($ServiceContext); - - } - - if($ServiceContext->IppConfiguration->OAuthMode == CoreConstants::OAUTH2) - { - $oauth2Config = $ServiceContext->IppConfiguration->Security; - if($oauth2Config instanceof OAuth2AccessToken){ - $DataServiceInstance->configureOAuth2LoginHelper($oauth2Config, $settings); - }else{ - throw new SdkException("SDK Error. OAuth mode is not OAuth 2."); - } - } - - return $DataServiceInstance; - - } else { - throw new SdkException("Passed Null to Configure method. It expects either a file path for the config file or an array containing OAuth settings and BaseURL."); - } - } - - /** - * After the ServiceContext is complete, also set the LoginHelper based on the ServiceContext. - * @param OAuth2AccessToken $oauth2Conifg OAuth 2 Token related information - * @param Array $settings The array that include the redirectURL, scope, state information - */ - private function configureOAuth2LoginHelper($oauth2Conifg, $settings) - { - $refreshToken = CoreConstants::getRefreshTokenFromArray($settings); - if(isset($refreshToken)){ - //Login helper for refresh token API call - $this->OAuth2LoginHelper = new OAuth2LoginHelper(null, - null, - null, - null, - null, - $this->getServiceContext()); - }else{ - $redirectURL = CoreConstants::getRedirectURL($settings); - $scope = array_key_exists('scope', $settings) ? $settings['scope'] : null; - $state = array_key_exists('state', $settings) ? $settings['state'] : null; - $this->OAuth2LoginHelper = new OAuth2LoginHelper($oauth2Conifg->getClientID(), - $oauth2Conifg->getClientSecret(), - $redirectURL, - $scope, - $state); - } - } - - - - /** - * Return the OAuth 2 Login Helper. The OAuth 2 Login helper can be used to generate OAuth code, get refresh Token, etc. - * @return $OAuth2LoginHelper A helper to get OAuth 2 related values. - */ - public function getOAuth2LoginHelper() - { - return $this->OAuth2LoginHelper; - } - - /** - * Update the OAuth 2 Token that will be used for API calls later. - * - * @param OAuth2AccessToken $newOAuth2AccessToken The OAuth 2 Access Token that will be used later. - * - * @return $this - */ - public function updateOAuth2Token($newOAuth2AccessToken) - { - try{ - $this->serviceContext->updateOAuth2Token($newOAuth2AccessToken); - $realmID = $newOAuth2AccessToken->getRealmID(); - $this->serviceContext->realmId = $realmID; - $this->setupRestHandler($this->serviceContext); - } catch (SdkException $e){ - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "Encountered an error while updating OAuth2Token." . $e->getMessage()); - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "Stack Trace: " . $e->getTraceAsString()); - } - return $this; - } - - /** - * Get the error from last request - * - * @return FaultHandler lastError - */ - public function getLastError() - { - return $this->lastError; - } - - /** - * Setups serializers objects for responces and requests based on service context - */ - public function setupSerializers() - { - $this->responseSerializer = CoreHelper::GetSerializer($this->serviceContext, false); - $this->requestSerializer = CoreHelper::GetSerializer($this->serviceContext, true); - } - - private function useMinorVersion() - { - $version = $this->serviceContext->IppConfiguration->minorVersion; - if (is_numeric($version) && !empty($version)) { - $this->serviceContext->minorVersion = $version; - } - return $this; - } - - /** - * @return string - */ - public function getMinorVersion() - { - return $this->serviceContext->minorVersion; - } - - /** - * Force json serializers for request and response - */ - public function forceJsonSerializers() - { - $this->requestSerializer = new JsonObjectSerializer(); - $this->responseSerializer = new JsonObjectSerializer(); - } - - /** - * Returns serializer for responce objects - * @return IEntitySerializer - */ - protected function getResponseSerializer() - { - return $this->responseSerializer; - } - - /** - * Returns serializer for request objects - * @return IEntitySerializer - */ - protected function getRequestSerializer() - { - return $this->requestSerializer; - } - - /** - * Marshall a POPO object to XML, presumably for inclusion on an IPP v3 API call - * - * - * - * @param object $phpObj inbound POPO object - * @return string XML output derived from POPO object - * @deprecated since version ? - */ - private function getXmlFromObj($phpObj) - { - if (!$phpObj) { - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "getXmlFromObj NULL arg."); - - return false; - } - - $php2xml = new Php2Xml(CoreConstants::PHP_CLASS_PREFIX); - $php2xml->overrideAsSingleNamespace = 'http://schema.intuit.com/finance/v3'; - - try { - return $php2xml->getXml($phpObj); - } catch (\Exception $e) { - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "Encountered an error parsing Object to XML." . $e->getMessage()); - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "Stack Trace: " . $e->getTraceAsString()); - - return false; - } - } - - /** - * Decorate an IPP v3 Entity name (like 'Class') to be a POPO class name (like 'IPPClass') - * - * @param string Intuit Entity name - * @return string POPO class name - */ - private static function decorateIntuitEntityToPhpClassName($intuitEntityName) - { - $className = CoreConstants::PHP_CLASS_PREFIX . $intuitEntityName; - $className = trim($className); - - return $className; - } - - - //Since we add the namespace, this one needs to be changed as well. - private static function getEntityResourceName($entity) - { - return strtolower(self::cleanPhpClassNameToIntuitEntityName(get_class($entity))); - } - - /** - * Clean a POPO class name (like 'IPPClass') to be an IPP v3 Entity name (like 'Class') - * - * @param string $phpClassName POPO class name - * @return string|null Intuit Entity name - */ - private static function cleanPhpClassNameToIntuitEntityName($phpClassName) - { - $phpClassName = self::removeNameSpaceFromPhpClassName($phpClassName); - if (0 == strpos($phpClassName, CoreConstants::PHP_CLASS_PREFIX)) { - return substr($phpClassName, strlen(CoreConstants::PHP_CLASS_PREFIX)); - } - - return null; - } - - /** - * Remove the Namespace from a php class name - * - * @param string $phpClassName QuickBooksOnline\API\Data\... - * @return string ipp... - */ - private static function removeNameSpaceFromPhpClassName($phpClassName) - { - $lists = explode('\\', $phpClassName); - $ippEntityName = end($lists); - - return $ippEntityName; - } - - /** - * Using the @entity and @uri to generate Request. - * Response will parsed. It will store any Error Code in 3xx to 5xx level. - * - * @param IPPIntuitEntity $entity - * @param string $uri - * @param string $httpsPostBody - * @param string $CALLINGMETHOD - * @param string|null $boundaryString - * @param string|null $email - * @return null|string - */ - private function sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, $httpsPostBody, $CALLINGMETHOD, $boundaryString = null, $email = null) - { - if ($this->isCreditCardPaymentTxn($entity)) { - $uri = str_replace("creditcardpaymenttxn", "creditcardpayment", $uri); - } - - switch ($CALLINGMETHOD) { - case DataService::DELETE: - case DataService::ADD: - case DataService::VOID: - case DataService::UPDATE: - $requestParameters = $this->initPostRequest($entity, $uri); - break; - case DataService::FINDBYID: - if ($this->serviceContext->IppConfiguration->Message->Request->SerializationFormat == SerializationFormat::Json) { - $requestParameters = new RequestParameters($uri, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONJSON, null); - } else { - $requestParameters = new RequestParameters($uri, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); - } - break; - case DataService::UPLOAD: - if (!isset($boundaryString)) { - throw new \Exception("Upload Image has unset value: boundaryString."); - } - // Creates request parameters - $requestParameters = $this->getPostRequestParameters($uri, "multipart/form-data; boundary={$boundaryString}"); - break; - case DataService::SENDEMAIL: - $requestParameters = $this->getPostRequestParameters($uri . (is_null($email) ? '' : '?sendTo=' . urlencode($email)), CoreConstants::CONTENTTYPE_OCTETSTREAM); - break; - } - $restRequestHandler = $this->getRestHandler(); - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - if ($faultHandler) { - $this->lastError = $faultHandler; - return null; - } else { - $this->lastError = false; - if (strcmp($CALLINGMETHOD, DataService::ADD) == 0) { - $responseBody = $this->fixTaxServicePayload($entity, $responseBody); - } - try { - $parsedResponseBody = $this->getResponseSerializer()->Deserialize($responseBody, true); - } catch (\Exception $e) { - return new \Exception("Exception in deserialize ResponseBody."); - } - - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Finished Executing Method " . $CALLINGMETHOD); - - return $parsedResponseBody; - } - } - - /** - * Updates an entity under the specified realm. The realm must be set in the context. - * - * @param IPPIntuitEntity $entity Entity to Update. - * @return IPPIntuitEntity Returns an updated version of the entity with updated identifier and sync token. - * @throws IdsException - */ - public function Update($entity) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method: Update."); - - // Validate parameter - if (!$entity) { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); - throw new IdsException('Argument Null Exception'); - } - $this->verifyOperationAccess($entity, __FUNCTION__); - - $httpsPostBody = $this->executeObjectSerializer($entity, $urlResource); - - // Builds resource Uri - // Handle some special cases - if ((strtolower('preferences') == strtolower($urlResource)) && - (CoreConstants::IntuitServicesTypeQBO == $this->serviceContext->serviceType) - ) { - // URL format for *QBO* prefs request is different than URL format for *QBD* prefs request - $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource)); - } - //We no longer support QBD on PHP SDK The code is removed. - /*else if ((strtolower('company') == strtolower($urlResource)) && - (CoreConstants::IntuitServicesTypeQBO == $this->serviceContext->serviceType)) { - // URL format for *QBD* companyinfo request is different than URL format for *QBO* companyinfo request - $urlResource = 'companyInfo'; - $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource . '?operation=update')); - }*/ else { - // Normal case - $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource . '?operation=update')); - } - - // Send Request and return response - return $this->sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, $httpsPostBody, DataService::UPDATE); - } - - /** - * The Read request. You can either pass an object that contains the Id that you want to read, or - * pass the Entity Name and the Id. - * Before v4.0.0, it supports the read of CompanyInfo and Preferences. - * After v4.0.0, it DOES NOT support read of CompanyInfo or Preferences. Please use getCompanyInfo() or getCompanyPreferences() method instead. - * Only use this one to do READ request with ID. - * - * Developer has two ways to call the GET request. Take Invoice for an example: - * 1) FindById($invoice); - * or - * 2) FindById("invoice", 1); - * - * @param object|String $entity Entity to Find, or the String Name of the Entity - * @return IPPIntuitEntity Returns an entity of specified Id. - * @throws IdsException - */ - public function FindById($entity, $Id = null) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method FindById."); - if(is_object($entity)){ - $httpsPostBody = $this->executeObjectSerializer($entity, $urlResource); - // Validate parameter - if (!$entity || !$entity->Id) { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); - throw new IdsException('Argument Null Exception when calling FindById for Endpoint:' . get_class($entity)); - } - $this->verifyOperationAccess($entity, __FUNCTION__); - $entityId = $this->getIDString($entity->Id); - // Normal case - $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource, $entityId)); - // Send request - return $this->sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, null, DataService::FINDBYID); - }else if(is_string($entity) && isset($Id)){ - $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, strtolower($entity), $Id)); - if ($this->isCreditCardPaymentTxn($entity)) { - $uri = str_replace("creditcardpaymenttxn", "creditcardpayment", $uri); - } - $requestParameters = new RequestParameters($uri, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); - $restRequestHandler = $this->getRestHandler(); - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, null, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - //$faultHandler now is true or false - if ($faultHandler) { - $this->lastError = $faultHandler; - return null; - } else { - //clean the error - $this->lastError = false; - $parsedResponseBody = $this->getResponseSerializer()->Deserialize($responseBody, true); - return $parsedResponseBody; - } - } - } - - /** - * Creates an entity under the specified realm. The realm must be set in the context. - * - * @param IPPIntuitEntity $entity Entity to Create. - * @return IPPIntuitEntity Returns the created version of the entity. - * @throws IdsException - */ - public function Add($entity) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Add."); - - // Validate parameter - if (!$entity) { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); - throw new IdsException('Argument Null Exception'); - } - $this->verifyOperationAccess($entity, __FUNCTION__); - if ($this->isJsonOnly($entity)) { - $this->forceJsonSerializers(); - } - - $httpsPostBody = $this->executeObjectSerializer($entity, $urlResource); - // Builds resource Uri - $resourceURI = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource)); - - $uri = $this->handleTaxService($entity, $resourceURI); - // Send request - return $this->sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, $httpsPostBody, DataService::ADD); - } - - /** - * Deletes an entity under the specified realm. The realm must be set in the context. - * - * @param IPPIntuitEntity $entity Entity to Delete. - * @return null|string - * @throws IdsException - */ - public function Delete($entity) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Delete."); - - // Validate parameter - if (!$entity) { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); - throw new IdsException('Argument Null Exception'); - } - $this->verifyOperationAccess($entity, __FUNCTION__); - - // Builds resource Uri - $httpsPostBody = $this->executeObjectSerializer($entity, $urlResource); - $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource . '?operation=delete')); - - // Creates request - return $this->sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, $httpsPostBody, DataService::DELETE); - } - - /** - * Voids an entity under the specified realm. The realm must be set in the context. - * - * @param IPPIntuitEntity $entity Entity to Void. - * @return null|string - * @throws IdsException - */ - public function Void($entity) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Void."); - // Validate parameter - if (!$entity) { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); - throw new IdsException('Argument Null Exception'); - } - $this->verifyOperationAccess($entity, __FUNCTION__); - - // Builds resource Uri - $httpsPostBody = $this->executeObjectSerializer($entity, $urlResource); - $className = $this->getEntityResourceName($entity); - if(in_array($className, CoreConstants::PAYMENTCLASSNAME)) { - $appendString = CoreConstants::VOID_QUERYPARAMETER_PAYMENT; - } else{ - $appendString = CoreConstants::VOID_QUERYPARAMETER_GENERAL; - } - $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource . $appendString)); - // Creates request - return $this->sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, $httpsPostBody, DataService::VOID); - } - - /** - * Uploads an attachment to an Entity on QuickBooks Online. For security reason, text file is not supported for uploading. - * - * @param string $bits Encoded Base64 bytes for the attachment - * @param string $fileName Filename to use for this file - * @param string $mimeType MIME type to send in the HTTP Headers - * @param IPPAttachable $objAttachable Entity including the attachment, it can be invoice, bill, etc - * @return array Returns an array of entities fulfilling the query. - * @throws IdsException - */ - public function Upload($imgBits, $fileName, $mimeType, $objAttachable) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Upload."); - - // Validate parameter - if (!$imgBits || !$mimeType || !$fileName) { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); - throw new IdsException('Argument Null Exception'); - } - // Builds resource Uri - $urlResource = "upload"; - $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource)); - - $boundaryString = md5(time()); - - $MetaData = $this->executeObjectSerializer($objAttachable, $urlResource); - - $desiredIdentifier = '0'; - $newline = "\r\n"; - $dataMultipart = ''; - $dataMultipart .= '--' . $boundaryString . $newline; - $dataMultipart .= "Content-Disposition: form-data; name=\"file_metadata_{$desiredIdentifier}\"" . $newline; - $dataMultipart .= "Content-Type: " . CoreConstants::CONTENTTYPE_APPLICATIONXML . '; charset=UTF-8' . $newline; - $dataMultipart .= 'Content-Transfer-Encoding: 8bit' . $newline . $newline; - $dataMultipart .= $MetaData; - $dataMultipart .= '--' . $boundaryString . $newline; - $dataMultipart .= "Content-Disposition: form-data; name=\"file_content_{$desiredIdentifier}\"; filename=\"{$fileName}\"" . $newline; - $dataMultipart .= "Content-Type: {$mimeType}" . $newline; - $dataMultipart .= 'Content-Transfer-Encoding: base64' . $newline . $newline; - $dataMultipart .= chunk_split($imgBits) . $newline; - $dataMultipart .= "--" . $boundaryString . "--" . $newline . $newline; // finish with two eol's!! - - return $this->sendRequestParseResponseBodyAndHandleHttpError(null, $uri, $dataMultipart, DataService::UPLOAD, $boundaryString); - } - - /** - * Returns PDF for entities which can be downloaded as PDF - * @param IPPIntuitEntity $entity - * @param Directory a writable directory for the PDF to be saved. - * @return boolean - * @throws IdsException, SdkException - * - */ - public function DownloadPDF($entity, $dir=null, $returnPdfString = false) - { - $this->validateEntityId($entity); - $this->verifyOperationAccess($entity, __FUNCTION__); - - //Find the ID - $entityID = $this->getIDString($entity->Id); - $uri = implode(CoreConstants::SLASH_CHAR, array('company', - $this->serviceContext->realmId, - self::getEntityResourceName($entity), - $entityID, - CoreConstants::getType(CoreConstants::CONTENTTYPE_APPLICATIONPDF))); - $requestParameters = $this->getGetRequestParameters($uri, CoreConstants::CONTENTTYPE_APPLICATIONPDF); - $restRequestHandler = $this->getRestHandler(); - - - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, null, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - if ($faultHandler) { - $this->lastError = $faultHandler; - //Add allow for through exception if users set it up - return null; - } elseif ($returnPdfString) { - return $responseBody; - } else { - $this->lastError = false; - - return $this->processDownloadedContent(new ContentWriter($responseBody), $responseCode, $dir, $this->getExportFileNameForPDF($entity, "pdf")); - } - } - - /** - * Sends entity by email for entities that have this operation - * - * @param IPPIntuitEntity $entity - * @param string|null $email - * @return boolean - * @throws IdsException, SdkException - * - */ - public function SendEmail($entity, $email = null) - { - $this->validateEntityId($entity); - $this->verifyOperationAccess($entity, __FUNCTION__); - - $entityId=$this->getIDString($entity->Id); - $uri = implode(CoreConstants::SLASH_CHAR, array('company', - $this->serviceContext->realmId, - self::getEntityResourceName($entity), - $entityId, - 'send')); - - if (is_null($email)) { - $this->logInfo("Entity " . get_class($entity) . " with id=" . $entityId . " is using default email"); - } else { - $this->logInfo("Entity " . get_class($entity) . " with id=" . $entityId . " is using $email"); - if (!$this->verifyEmailAddress($email)) { - $this->logError("Valid email is expected, but received $email"); - throw new SdkException("Valid email is expected, but received $email"); - } - } - - return $this->sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, null, DataService::SENDEMAIL, null, $email); - } - - /** - * Retrieves specified entities based passed page number and page size and query - * - * @param string $query Query to issue - * @param int $startPosition Starting page number - * @param int $maxResults Page size - * @param string $includes A list of additional fields requested in the entities response - * @return array Returns an array of entities fulfilling the query. If the response is Empty, it will return NULL - */ - public function Query($query, $startPosition = null, $maxResults = null, $includes = null) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Query."); - - if ('QBO' == $this->serviceContext->serviceType) { - $httpsContentType = CoreConstants::CONTENTTYPE_APPLICATIONTEXT; - } else { - $httpsContentType = CoreConstants::CONTENTTYPE_TEXTPLAIN; - } - - $httpsUri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, 'query')); - $httpsPostBody = $this->appendPaginationInfo($query, $startPosition, $maxResults); - - if(!is_null($includes)) { - $httpsUri .= "?include=$includes"; - } - - $requestParameters = $this->getPostRequestParameters($httpsUri, $httpsContentType); - $restRequestHandler = $this->getRestHandler(); - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - if ($faultHandler) { - $this->lastError = $faultHandler; - return null; - } else { - $this->lastError = false; - $parsedResponseBody = null; - try { - $responseXmlObj = simplexml_load_string($responseBody); - if ($responseXmlObj && $responseXmlObj->QueryResponse) { - $tmpXML = $responseXmlObj->QueryResponse->asXML(); - $parsedResponseBody = $this->responseSerializer->Deserialize($tmpXML, false); - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, $parsedResponseBody); - } - - } catch (\Exception $e) { - throw new \Exception("Exception appears in converting Response to XML."); - } - - return $parsedResponseBody; - } - } - - /** - * Append the Pagination Data to the Query string if it is not appended - * @param Integer StartPostion - * @param Integer MaxResults - * @return String The query string - */ - private function appendPaginationInfo($query, $startPosition, $maxResults){ - $query = trim($query); - if(isset($startPosition) && !empty($startPosition)){ - if(stripos($query, "STARTPOSITION") === false){ - if(stripos($query, "MAXRESULTS") !== false){ - //In MaxResult is defined,we don't set startPosition - }else{ - $query = $query . " " . "STARTPOSITION " . $startPosition; - } - }else{ - //Ignore the startPosition if it is already used on the query - } - } - - if(isset($maxResults) && !empty($maxResults)){ - if(stripos($query, "MAXRESULTS") === false){ - $query = $query . " " . "MAXRESULTS " . $maxResults; - }else{ - //Ignore the maxResults if it is already used on the query - } - } - - return $query; - - } - - /** - * Retrieves all entities by name - * - * @param string $entityName - * @param int $pageNumber - * @param int $pageSize - * @return array Returns an array of entities of specified type. - */ - public function FindAll($entityName, $pageNumber = 0, $pageSize = 500) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method FindAll."); - - $phpClassName = DataService::decorateIntuitEntityToPhpClassName($entityName); - - // Handle some special cases - if (strtolower('company') == strtolower($entityName)) { - $entityName = 'CompanyInfo'; - } - - if ('QBO' == $this->serviceContext->serviceType) { - $httpsContentType = CoreConstants::CONTENTTYPE_APPLICATIONTEXT; - } else { - $httpsContentType = CoreConstants::CONTENTTYPE_TEXTPLAIN; - } - - $httpsUri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, 'query')); - $httpsPostBody = "select * from $entityName startPosition $pageNumber maxResults $pageSize"; - - $requestParameters = $this->getPostRequestParameters($httpsUri, $httpsContentType); - $restRequestHandler = $this->getRestHandler(); - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - if ($faultHandler) { - $this->lastError = $faultHandler; - return null; - } else { - $this->lastError = false; - $parsedResponseBody = null; - try { - $responseXmlObj = simplexml_load_string($responseBody); - if ($responseXmlObj && $responseXmlObj->QueryResponse) { - $parsedResponseBody = $this->responseSerializer->Deserialize($responseXmlObj->QueryResponse->asXML(), false); - } - } catch (\Exception $e) { - throw new \Exception("Exception appears in converting Response to XML."); - } - - return $parsedResponseBody; - } - } - - /** - * Returns List of entities changed after certain time. - * - * @param array entityList List of entity. - * @param long changedSince DateTime of timespan after which entities were changed. - * @return IntuitCDCResponse Returns an IntuitCDCResponse. - */ - public function CDC($entityList, $changedSince) - { - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Called Method CDC."); - - // Validate parameter - if (count($entityList) <= 0) { - $exception = new IdsException('ParameterNotNullMessage'); - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "ParameterNotNullMessage"); - IdsExceptionManager::HandleException($exception); - } - - $entityString = implode(",", $entityList); - - $query = null; - $uri = null; - - if(is_string($changedSince)){ - $formattedChangedSince = trim($changedSince); - }else{ - $formattedChangedSince = date("Y-m-d\TH:i:s", $this->verifyChangedSince($changedSince)); - } - - $query = "entities=" . $entityString . "&changedSince=" . $formattedChangedSince; - $uri = "company/{1}/cdc?{2}"; - //$uri = str_replace("{0}", CoreConstants::VERSION, $uri); - $uri = str_replace("{1}", $this->serviceContext->realmId, $uri); - $uri = str_replace("{2}", $query, $uri); - - // Creates request parameters - $requestParameters = $this->getGetRequestParameters($uri, CoreConstants::CONTENTTYPE_APPLICATIONXML); - $restRequestHandler = $this->getRestHandler(); - - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, null, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - if ($faultHandler) { - $this->lastError = $faultHandler; - return null; - } else { - $this->lastError = false; - $returnValue = new IntuitCDCResponse(); - try { - $xmlObj = simplexml_load_string($responseBody); - $responseArray = $xmlObj->CDCResponse->QueryResponse; - if(sizeof($responseArray) != sizeof($entityList)){ - throw new ServiceException("The number of Entities requested on CDC does not match the number of Response."); - } - - for($i = 0; $i < sizeof($responseArray); $i++){ - $currentResponse = $responseArray[$i]; - $currentEntityName = $entityList[$i]; - $entities = $this->responseSerializer->Deserialize($currentResponse->asXML(), false); - $entityName = $currentEntityName; - //If we find the actual name, update it. - foreach ($currentResponse->children() as $currentResponseChild) { - $entityName = (string)$currentResponseChild->getName(); - break; - } - $returnValue->entities[$entityName] = $entities; - } - } catch (\Exception $e) { - IdsExceptionManager::HandleException($e); - } - - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Finished Executing Method CDC."); - return $returnValue; - } - } - - /** - * Creates a RecurringTransaction Entity under the specified realm. The realm must be set in the context. - * - * @param IPPIntuitEntity $entity Entity to Create. - * @return IntuitRecurringTransactionResponse Returns the RecurringTransaction created for the entity. - * @throws IdsException - */ - public function addRecurringTxn($entity) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Add."); - - // Validate parameter - if (!$entity) { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); - throw new IdsException('Argument Null Exception'); - } - // Verify operation access - $this->verifyOperationAccess($entity, __FUNCTION__); - if ($this->isJsonOnly($entity)) { - $this->forceJsonSerializers(); - } - // Create recurring transaction object - $recurringtxn = RecurringTransactionAdapter::createRecurringTransactionObject($entity); - - // Create recurring transaction Post Body - $httpsPostBody = RecurringTransactionAdapter::getRecurringTxnBody($recurringtxn); - - // Builds resource Uri - $resourceURI = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, 'recurringtransaction')); - - $requestParameters = new RequestParameters($resourceURI, 'POST', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); - $restRequestHandler = $this->getRestHandler(); - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - if ($faultHandler) { - $this->lastError = $faultHandler; - return null; - } else { - $this->lastError = false; - $returnValue = new IntuitRecurringTransactionResponse(); - try { - $xmlObj = simplexml_load_string($responseBody); - // deserialize the response body - $deserializedResponse = $this->responseSerializer->Deserialize($xmlObj->RecurringTransaction->asXML(), false); - $entityName = XmlObjectSerializer::cleanPhpClassNameToIntuitEntityName(get_class($entity)); - $returnValue->entities[$entityName] = $deserializedResponse; - } catch (\Exception $e) { - IdsExceptionManager::HandleException($e); - } - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Finished Executing Recurring Transaction."); - return $returnValue; - } - } - - /** - * Query RecurringTransaction Entity under the specified realm. The realm must be set in the context. - * - * @param query ex : SELECT * FROM RecurringTransaction - * @return IntuitRecurringTransactionResponse Returns the RecurringTransaction created for the entity. - * @throws IdsException - */ - public function recurringTransaction($query) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Query."); - - if ('QBO' == $this->serviceContext->serviceType) { - $httpsContentType = CoreConstants::CONTENTTYPE_APPLICATIONTEXT; - } else { - $httpsContentType = CoreConstants::CONTENTTYPE_TEXTPLAIN; - } - - $httpsUri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, 'query')); - $httpsPostBody = $this->appendPaginationInfo($query, $startPosition, $maxResults); - - $requestParameters = $this->getPostRequestParameters($httpsUri, $httpsContentType); - $restRequestHandler = $this->getRestHandler(); - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - if ($faultHandler) { - $this->lastError = $faultHandler; - return null; - } else { - $this->lastError = false; - $returnValue = new IntuitRecurringTransactionResponse(); - try { - $xmlObj = simplexml_load_string($responseBody); - $responseArray = $xmlObj->QueryResponse->RecurringTransaction; - if(sizeof($responseArray) <= 0){ - throw new ServiceException("No recurring transactions found."); - } - - for($i = 0; $i < sizeof($responseArray); $i++){ - $currentResponse = $responseArray[$i]; - $currentEntityName = $entityList[$i]; - $entities = $this->responseSerializer->Deserialize($currentResponse->asXML(), false); - $entityName = $currentEntityName; - //If we find the actual name, update it. - foreach ($currentResponse->children() as $currentResponseChild) { - $entityName = (string)$currentResponseChild->getName(); - break; - } - $returnValue->entities[$entityName][] = $entities; - } - } catch (\Exception $e) { - IdsExceptionManager::HandleException($e); - } - - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Finished Executing Recurring Transaction."); - return $returnValue; - } - } - - /** - * Find a RecurringTransaction Entity By ID under the specified realm. The realm must be set in the context. - * - * @param $Id Id of the IPPIntuitEntity Object - * @return IntuitRecurringTransactionResponse Returns the RecurringTransaction created for the entity. - * @throws IdsException - */ - public function findRecurringTransactionById($Id = null) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method findRecurringTransactionById."); - - if (!$Id) { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); - throw new IdsException('Argument [Id] Null Exception'); - } - - // Builds resource Uri - $resourceURI = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, 'recurringtransaction/'. $Id)); - - // Make the GET request to fetch the recurring transaction - $requestParameters = new RequestParameters($resourceURI, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); - $restRequestHandler = $this->getRestHandler(); - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, null, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - //$faultHandler now is true or false - if ($faultHandler) { - $this->lastError = $faultHandler; - return null; - } else { - //clean the error - $this->lastError = false; - $returnValue = new IntuitRecurringTransactionResponse(); - try { - $xmlObj = simplexml_load_string($responseBody); - - // deserialize the response body - $deserializedResponse = $this->responseSerializer->Deserialize($xmlObj->RecurringTransaction->asXML(), false); - $entityName = XmlObjectSerializer::cleanPhpClassNameToIntuitEntityName(get_class($deserializedResponse[0])); - $returnValue->entities[$entityName] = $deserializedResponse; - } catch (\Exception $e) { - IdsExceptionManager::HandleException($e); - } - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Finished Executing Recurring Transaction."); - return $returnValue; - } - } - - /** - * Find a RecurringTransaction Entity By ID under the specified realm. The realm must be set in the context. - * - * @param IntuitRecurringTransactionResponse RecurringTransaction Response from findRecurringTransactionById method. - * @return IntuitRecurringTransactionResponse Returns the RecurringTransaction created for the entity. - * @throws IdsException - */ - public function deleteRecurringTransaction($recurringTransaction) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Delete."); - - // Validate parameter - if (!$recurringTransaction) { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); - throw new IdsException('Argument Null Exception'); - } - $this->verifyOperationAccess($recurringTransaction, __FUNCTION__); - - // Get the Entity Name from $recurringTransaction - $entityName = array_keys($recurringTransaction->entities)[0]; - - // Get the IPPEntity Object from $recurringTransaction - $entity = $recurringTransaction->entities[$entityName][0]; - - // Create recurring transaction object - $recurringtxn = RecurringTransactionAdapter::createRecurringTransactionObject($entity); - // Create recurring transaction Post Body - $httpsPostBody = RecurringTransactionAdapter::getRecurringTxnBody($recurringtxn); - - // Builds resource Uri - $resourceURI = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, 'recurringtransaction' . '?operation=delete')); - - // Make the GET request to fetch the recurring transaction - $requestParameters = new RequestParameters($resourceURI, 'POST', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); - $restRequestHandler = $this->getRestHandler(); - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - //$faultHandler now is true or false - if ($faultHandler) { - $this->lastError = $faultHandler; - return null; - } else { - //clean the error - $this->lastError = false; - $returnValue = new IntuitRecurringTransactionResponse(); - try { - $xmlObj = simplexml_load_string($responseBody); - - // deserialize the response body - $deserializedResponse = $this->responseSerializer->Deserialize($xmlObj->RecurringTransaction->asXML(), false); - $entityName = XmlObjectSerializer::cleanPhpClassNameToIntuitEntityName(get_class($entity)); - $returnValue->entities[$entityName] = $deserializedResponse; - } catch (\Exception $e) { - IdsExceptionManager::HandleException($e); - } - $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Finished Executing Recurring Transaction."); - return $returnValue; - } - } - - /** - * Returns an entity under the specified realm. The realm must be set in the context. - * - * @param object $entity Entity to Find - * @return IPPIntuitEntity Returns an entity of specified Id. - */ - public function Retrieve($entity) - { - $this->verifyOperationAccess($entity, __FUNCTION__); - - return $this->FindById($entity); - } - - /** - * Serializes oblect into specified format - * @param IPPIntuitEntity $entity - * @param String $urlResource - * @return object - */ - protected function executeObjectSerializer($entity, &$urlResource) - { - $result = $this->getRequestSerializer()->Serialize($entity); - $urlResource = $this->getRequestSerializer()->getResourceURL(); - - return $result; - } - - /** - * Returns post request, depends on configuration and entity rule "jsonOnly" - * @param IPPIntuitEntity $entity - * @param string $uri - * @return RequestParameters - */ - protected function initPostRequest($entity, $uri) - { - return $this->isJsonOnly($entity) - ? $this->getPostJsonRequest($uri) - : $this->getPostRequest($uri); - } - - /** - * Returns content type depends from serialization format - * @return string - */ - private function getContentType() - { - return ($this->getSerializationFormat() == SerializationFormat::Json) - ? CoreConstants::CONTENTTYPE_APPLICATIONJSON - : CoreConstants::CONTENTTYPE_APPLICATIONXML; - } - - /** - * Returns post request with pre-defined POST method and JSON serialization - * @param string $uri - * @return RequestParameters - */ - private function getPostJsonRequest($uri) - { - return $this->getPostRequestParameters($uri, CoreConstants::CONTENTTYPE_APPLICATIONJSON); - } - - /** - * Return true if specified entity can work with JSON only serialization - * @param IPPIntuitEntity $entity - * @return boolean - */ - private function isJsonOnly($entity) - { - return $this->isAllowed($entity, "jsonOnly"); - } - - /** - * Returns pre-defined request with POST method and content type from settings - * @param string $uri - * @return RequestParameters - */ - private function getPostRequest($uri) - { - return $this->getPostRequestParameters($uri, $this->getContentType()); - } - - /** - * Returns pre-defined request with GET method - * @param string $uri - * @param string $type - * @return RequestParameters - */ - private function getGetRequestParameters($uri, $type) - { - return $this->getRequestParameters($uri, 'GET', $type); - } - - /** - * Returns pre-defined request with POST method - * @param string $uri - * @param string $type - * @return RequestParameters - */ - private function getPostRequestParameters($uri, $type) - { - return $this->getRequestParameters($uri, 'POST', $type); - } - - /** - * Wraps and returns RequestParameters object - * @param string $uri - * @param string $method - * @param string $type - * @param string $apiName - * @return RequestParameters - */ - protected function getRequestParameters($uri, $method, $type, $apiName = null) - { - return new RequestParameters($uri, $method, $type, $apiName); - } - - /** - * Returns current serialization format - * - * @return string - */ - protected function getSerializationFormat() - { - return $this->serviceContext->IppConfiguration->Message->Request->SerializationFormat; - } - - /** - * Handles unusual URL mapping for TaxService - * @param IPPIntuitEntity $entity - * @param string $uri - * @return string - */ - private function handleTaxService($entity, $uri) - { - if ($this->isTaxServiceSafe($entity)) { - return $uri . '/taxcode'; - } - - return $uri; - } - - /** - * Verifies that entity has TaxService type - * update the TaxService with namespace added - * If this class is not available on include_path or wab't loaded the method will return false - * - * @param IPPTaxService $entity - * @return bool - */ - private function isTaxServiceSafe($entity) - { - $IPPTaxServiceClassWIthNameSpace = "QuickBooksOnline\\API\\Data\\IPPTaxService"; - - return class_exists($IPPTaxServiceClassWIthNameSpace) && ($entity instanceof $IPPTaxServiceClassWIthNameSpace); - } - - private function isCreditCardPaymentTxn($entity) - { - $IPPCreditCardPaymentTxnClass = "QuickBooksOnline\\API\\Data\\IPPCreditCardPaymentTxn"; - - if (class_exists($IPPCreditCardPaymentTxnClass) && ($entity instanceof $IPPCreditCardPaymentTxnClass)) - return true; - else if (is_string($entity) && $entity == "CreditCardPaymentTxn") - return true; - - return false; - } - - /** - * Methods provides workaround to successfully process TaxService response - * @param $entity - * @param $content - * @return string - */ - private function fixTaxServicePayload($entity, $content) - { - if ($this->isTaxServiceSafe($entity)) { - //get first "line" to make sure we don't have TaxService in response - $sample = substr(trim($content), 0, 20); - $taxServiceName = self::cleanPhpClassNameToIntuitEntityName(get_class($entity)); - if (false === strpos($sample, $taxServiceName)) { - //last attempt to verify content before - if (0 === strpos($sample, '{"TaxCode":')) { - return "{\"$taxServiceName\":$content}"; - } - } - } - - return $content; - } - - - /** - * Returns an downloaded entity under the specified realm. The realm must be set in the context. - * - * @param object $entity Entity to Find - * @return string IPPIntuitEntity Returns an entity of specified Id. - * @deprecated The download for QuickBooksOnline is only supporting download PDF for Invoice and SalesReceipt. Other download function are not defined now. - */ - public function Download($entity) - { - return $this->FindById($entity); - } - - /** - * Verifies string as email agains RFC 822 - * @param string $email - * @return type - */ - public function verifyEmailAddress($email) - { - return filter_var($email, FILTER_VALIDATE_EMAIL); - } - - /** - * Returns PDF filename based on entity type and id - * @param IPPIntuitEntity $entity - * @param string $ext - * @return string - */ - public function getExportFileNameForPDF($entity, $ext, $usetimestamp = true) - { - //TODO: add timestamp or GUID - $this->validateEntityId($entity); - - return self::getEntityResourceName($entity) . "_" . $this->getIDString($entity->Id) . ($usetimestamp ? "_" . time() : "") . ".$ext"; - } - - /** - * Returns the Sync Resthandler associated with the dataserivce - * @return SyncRestHandler The SyncRestHandler with the DataService - */ - protected function getRestHandler() - { - if(isset($this->restHandler)){ - return $this->restHandler; - }else{ - throw new SdkException("The SyncRest handler associated with the DataService is not set."); - } - } - - /** - * Saves exported (e.g. check DownloadPDF) entity into file according to strategy in settings - * @param ContentWriter $writer - * @param int $responseCode - * @param string $fileName - * @return mixed full path with filename or open handler - */ - protected function processDownloadedContent(ContentWriter $writer, $responseCode, $dir, $fileName = null) - { - $writer->setPrefix($this->getPrefixFromSettings()); - try { - if(isset($dir) && !empty($dir)){ - $writer->saveFile($dir, $fileName); - }else if ($this->isTempFile()) { - $writer->saveTemp(); - } elseif ($this->isFileExport()) { - $writer->saveFile($this->getFileExportDir(), $fileName); - } else { - $writer->saveAsHandler(); - } - //return object as is - if ($this->isReturnContentWriter()) { - return $writer; - } - $writer->resetContent(); - $this->logInfo("File was downloaded (http response = $responseCode), bytes written: {$writer->getBytes()}"); - } catch (\Exception $ex) { - $this->logError("Exception appears during response processing. Http response was $responseCode: " . $ex->getMessage() . "\n" . $ex->getTraceAsString()); - - return null; - } - - return $writer->isHandler() ? $writer->getHandler() : $writer->getTempPath(); - } - - protected function processRecurringTransactionResponse($recurringTransaction) { - //Return an array of objects - $entities = []; - foreach ($recurringTransaction as $oneResponse) { - $oneEntity = $this->responseSerializer->Deserialize(''.$oneResponse->children()->asXML().''); - $entities = array_merge($entities, $oneEntity); - } - - return $entities; - } - - /** - * Returns true or false if writer object is allowed as return result - * @return boolean - */ - private function isReturnContentWriter() - { - return $this->serviceContext->IppConfiguration->ContentWriter->returnOject; - } - - /** - * Returns prefix for contentWriter - * @return string - */ - private function getPrefixFromSettings() - { - return $this->serviceContext->IppConfiguration->ContentWriter->prefix; - } - - /** - * Returns true if SDK is configured to save temporary files - * @return boolean - */ - private function isTempFile() - { - return (CoreConstants::FILE_STRATEGY === $this->serviceContext->IppConfiguration->ContentWriter->strategy); - } - - /** - * Returns true if SDK is configured to export files to another location - * @return boolean - */ - private function isFileExport() - { - return (CoreConstants::EXPORT_STRATEGY === $this->serviceContext->IppConfiguration->ContentWriter->strategy); - } - - /** - * Returns path to directory where SDK should save files - * @return string - */ - private function getFileExportDir() - { - return $this->serviceContext->IppConfiguration->ContentWriter->exportDir; - } - - - /** - * Simple verification for entities which can be returned as PDF - */ - private function isAllowed($entity, $method) - { - $className = get_class($entity); - if (!$className) { - $this->logError("Intuit entity is expected here instead of $entity"); - throw new IdsException('Unexpected Argument Exception'); - } - - $classArray = explode('\\', $className); - $trimedClassName = end($classArray); - - return $this->serviceContext->IppConfiguration->OpControlList->isAllowed($trimedClassName, $method); - } - - - private function verifyOperationAccess($entity, $func) - { - if (!$this->isAllowed($entity, $func)) { - $this->logError("Cannot invoke " . $func . " for \"" . get_class($entity) . "\" because of operation contstrains."); - throw new IdsException('Operation ' . $func . ' is not allowed for entity ' . get_class($entity)); - } - - return true; - } - - - private function validateEntityId($entity) - { - if (empty($entity)) { - $this->logError("Argument Null Exception"); - throw new IdsException('Argument Null Exception'); - } - if (!isset($entity->Id)) { - $this->logError("Property ID doesn't exist"); - throw new IdsException('Property ID is not set'); - } - - if (empty($entity->Id)) { - $this->logError("Property ID is empty"); - throw new IdsException('Property ID is empty'); - } - - return true; - } - - private function logError($message) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, $message); - } - - private function logInfo($message) - { - $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, $message); - } - - - /** - * Creates new batch - * - * @return Batch returns the batch object - */ - public function CreateNewBatch() - { - $batch = new Batch($this->serviceContext, $this->getRestHandler(), $this->isThrownExceptionOnError()); - - return $batch; - } - - - /** - * Parse input date-time string into unix timestamp - * @param string $str - * @return int timestamp or false overwise - * @throws SdkException - */ - private function convertToTimestamp($str) - { - $result = date_parse($str); - if (!$result) { - return false; - } - if (empty($result) || !is_array($result)) { - return false; - } - extract($result); - if (!empty($errors)) { - throw new SdkException("SDK failed to parse date value \"$str\":" - . (is_array($errors) ? implode("\n", $errors) : $errors) - ); - } - - //@TODO: mktime is deprecated since 5.3.0, this package needs 5.6 - return mktime($hour, $minute, $second, $month, $day, $year); - } - - /** - * Checks if input string is a valid timestamp or not - * @param string timestamp contains input timestamp-like string - * @return bool - */ - public function isValidTimeStamp($timestamp) - { - return ((string)(int)$timestamp === $timestamp) && ($timestamp <= PHP_INT_MAX) && ($timestamp >= ~PHP_INT_MAX); - } - - /** - * Verifies input and returns unix timestamp - * @param mixed $value - * @return int - * @throws SdkException - */ - protected function verifyChangedSince($value) - { - if (is_int($value)) { - return $value; - } - // remove whitespaces, tabulation etc - $trimmed = trim($value); - if ($this->isValidTimeStamp($trimmed)) { - return $trimmed; - } - //at this point we have numeric string which is not timestamp - if (is_numeric($value)) { - throw new SdkException("Input string doesn't look as unix timestamp or date string"); - } - // trying to parse string into timestamp - $converted = $this->convertToTimestamp($value); - if (!$converted) { - throw new SdkException("Input value should be unix timestamp or valid date string"); - } - - return $converted; - } - - /** - * Get the Company Information - * @return IPPCompanyInfo CompanyInformation - */ - public function getCompanyInfo() - { - $currentServiceContext = $this->getServiceContext(); - if (!isset($currentServiceContext) || empty($currentServiceContext->realmId)) { - throw new SdkException("Please Setup Service Context before making get CompanyInfo call."); - } - //The CompanyInfo URL - $uri = implode(CoreConstants::SLASH_CHAR, array('company', $currentServiceContext->realmId, 'companyinfo', $currentServiceContext->realmId)); - $requestParameters = new RequestParameters($uri, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); - $restRequestHandler = $this->getRestHandler(); - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, null, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - //$faultHandler now is true or false - if ($faultHandler) { - $this->lastError = $faultHandler; - return null; - } else { - $this->lastError = false; - $parsedResponseBody = $this->getResponseSerializer()->Deserialize($responseBody, true); - return $parsedResponseBody; - } - } - - /** - * Get the Company Preferences Information - * @return IPPPreferences CompanyInformation - */ - public function getCompanyPreferences() - { - $currentServiceContext = $this->getServiceContext(); - if (!isset($currentServiceContext) || empty($currentServiceContext->realmId)) { - throw new SdkException("Please Setup Service Context before making get Company Preferences call."); - } - //The Preferences URL - $uri = implode(CoreConstants::SLASH_CHAR, array('company', $currentServiceContext->realmId, 'preferences')); - $requestParameters = new RequestParameters($uri, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); - $restRequestHandler = $this->getRestHandler(); - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, null, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - //$faultHandler now is true or false - if ($faultHandler) { - $this->lastError = $faultHandler; - return null; - } else { - $this->lastError = false; - $parsedResponseBody = $this->getResponseSerializer()->Deserialize($responseBody, true); - return $parsedResponseBody; - } - } - - /** - * Get the Entitlement Response - * @return \SimpleXMLElement Xml - */ - public function getEntitlementsResponse() - { - $currentServiceContext = $this->getServiceContext(); - if (!isset($currentServiceContext) || empty($currentServiceContext->realmId)) { - throw new SdkException("Please Setup Service Context before making get entitlements response call."); - } - //The Preferences URL - $uri = $this->getServiceContext()->IppConfiguration->BaseUrl->Qbo; - $requestParameters = new RequestParameters($uri, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); - $restRequestHandler = $this->getRestHandler(); - $entitlementsUri = $this->getServiceContext()->IppConfiguration->BaseUrl->Qbo . "manage/entitlements/v3/" . $this->serviceContext->realmId; - list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, $entitlementsUri, $this->isThrownExceptionOnError()); - $faultHandler = $restRequestHandler->getFaultHandler(); - //$faultHandler now is true or false - if ($faultHandler) { - $this->lastError = $faultHandler; - return null; - } else { - $this->lastError = false; - return simplexml_load_string($responseBody); - } - } - - /** - * Get the actual ID string value of either an IPPid object, or an Id string - * @param Object $id - * @return String Id - */ - private function getIDString($id){ - if($id instanceof IPPid){ - return (String)$id->value; - }else{ - return (String)$id; - } - } -} From c948ea9a363bfc6009364709a5cc1c0c5852edbd Mon Sep 17 00:00:00 2001 From: Niknoc Date: Sat, 23 Nov 2024 11:23:04 -0700 Subject: [PATCH 2/2] Fixed typo in getClientName function in DataService.php It was called "getClinetName" and I changed it to "getClientName" :) --- src/DataService/DataService.php | 1982 +++++++++++++++++++++++++++++++ 1 file changed, 1982 insertions(+) create mode 100644 src/DataService/DataService.php diff --git a/src/DataService/DataService.php b/src/DataService/DataService.php new file mode 100644 index 00000000..aa5275a1 --- /dev/null +++ b/src/DataService/DataService.php @@ -0,0 +1,1982 @@ +updateServiceContextSettingsForOthers($serviceContext); + } + + /** + * Set the corresponding settings for the dataService based on ServiceContext + * @var ServiceContext $serviceContext The service Context for this DataService + */ + public function updateServiceContextSettingsForOthers($serviceContext) + { + $this->setupServiceContext($serviceContext); + $this->setupSerializers(); + $this->useMinorVersion(); + $this->setupRestHandler($serviceContext); + } + + /** + * Set or Update the ServiceContext of this DataService. + * + * @var ServiceContext $serviceContext The new ServiceContext passed by. + * @return $this + */ + private function setupServiceContext($serviceContext) + { + $this->serviceContext = $serviceContext; + return $this; + } + + /** + * Return the ServiceContext of this DataService + * + * @return ServiceContext + * @throws \Exception ServiceContext is NULL. + */ + public function getServiceContext() + { + if (isset($this->serviceContext)) { + return $this->serviceContext; + } else { + throw new SdkException("Trying to Return an Empty Service Context."); + } + } + + /** + * Set the SyncRest Handler for the DataService. If the client Name changed, the underlying Client that SyncRestHandler used will also changed. + * + * @var ServiceContext $serviceContext The service Context for this DataService + * @return $this + * + */ + protected function setupRestHandler($serviceContext) + { + if(isset($serviceContext)){ + $client = ClientFactory::createClient($this->getClientName()); + $this->restHandler = new SyncRestHandler($serviceContext, $client); + }else{ + throw new SdkException("Can not set the Rest Client based on null ServiceContext."); + } + return $this; + } + + /** + * Return the current Client Name used by DataService + * @return String clientName + */ + public function getClientName(){ + return $this->clientName; + } + /** + * PHP SDK currently only support XML for Object Serialization and Deserialization, except for Report Service + * + * @return $this + */ + public function useXml() + { + $serviceContext = $this->getServiceContext(); + $serviceContext->useXml(); + $this->updateServiceContextSettingsForOthers($serviceContext); + return $this; + } + + /** + * PHP SDK currently only support XML for Object Serialization and Deserialization, except for Report Service + * + * @return $this + */ + public function useJson() + { + $serviceContext = $this->getServiceContext(); + $serviceContext->useJson(); + $this->updateServiceContextSettingsForOthers($serviceContext); + return $this; + } + + /** + * Set a new directory for request and response log + * + * @param String $new_log_location The directory path for storing request and response log + * + * @return $this + */ + public function setLogLocation($new_log_location) + { + $restHandler = $this->restHandler; + $loggerUsedByRestHandler = $restHandler->getRequestLogger(); + $loggerUsedByRestHandler->setLogDirectory($new_log_location); + return $this; + } + + /** + * Set logging for OAuth calls + * + * @param Boolean $enableLogs Turns on logging for OAuthCalls + * + * @param Boolean $debugMode Turns on debug mode to log tokens + * + * @param String $new_log_location The directory path for storing request and response log + * + * @return $this + */ + public function setLogForOAuthCalls($enableLogs, $debugMode, $new_log_location) + { + $this->OAuth2LoginHelper->setLogForOAuthCalls($enableLogs, $debugMode, $new_log_location); + return $this; + } + + /** + * Set a new Minor Version + * + * @param String $newMinorVersion The new minor version that passed + * + * @return $this + */ + public function setMinorVersion($newMinorVersion) + { + $serviceContext = $this->getServiceContext(); + $serviceContext->setMinorVersion($newMinorVersion); + $this->updateServiceContextSettingsForOthers($serviceContext); + return $this; + } + + /** + * Disable the logging function + * + * @return $this + */ + public function disableLog() + { + $restHandler = $this->restHandler; + $loggerUsedByRestHandler = $restHandler->getRequestLogger(); + $loggerUsedByRestHandler->setLogStatus(false); + return $this; + } + + /** + * Enable the logging function + * + * @return $this + */ + public function enableLog() + { + $restHandler = $this->restHandler; + $loggerUsedByRestHandler = $restHandler->getRequestLogger(); + $loggerUsedByRestHandler->setLogStatus(true); + return $this; + } + + + /** + * Choose if want to throw exception when there is an non-200 http status code returned. + * @param Boolean $bool Turn on exception throwing error or not + */ + public function throwExceptionOnError($bool){ + $this->throwExceptionOnError = $bool; + return $this; + } + + /** + * Return the settings for thrown exception on non-200 error code + * @return Boolean Thrown Exception on Error or not + */ + public function isThrownExceptionOnError(){ + return $this->throwExceptionOnError; + } + + /** + * Return the client Name used by this DataSerivce + * @return String the Client Name. It can be curl or GuzzleHttpClient + * @deprecated since version 5.0.4 + * @see $this->getClientName() + */ + public function getClientName(){ + return $this->getClientName(); + } + + + /** + * The client Name can be either 'curl', 'guzzle', or 'guzzlehttp'. + * + * @param String $clientName The client Name used by the service + * + * @return $this + */ + public function setClientName($clientName){ + $this->clientName = $clientName; + $serviceContext = $this->getServiceContext(); + $this->setupRestHandler($serviceContext); + return $this; + } + + /** + * New Static function for static Reading from Config or Passing Array + * The config needs to include + * + * @param $settings + * @return DataService + * @throws SdkException + * @throws SdkException + */ + public static function Configure($settings) + { + if (isset($settings)) { + if (is_array($settings)) { + $ServiceContext = ServiceContext::ConfigureFromPassedArray($settings); + if (!isset($ServiceContext)) { + throw new SdkException('Construct ServiceContext from OAuthSettigs failed.'); + } + $DataServiceInstance = new DataService($ServiceContext); + + } elseif (is_string($settings)) { + $ServiceContext = ServiceContext::ConfigureFromLocalFile($settings); + if (!isset($ServiceContext)) { + throw new SdkException('Construct ServiceContext from File failed.'); + } + $DataServiceInstance = new DataService($ServiceContext); + + } + + if($ServiceContext->IppConfiguration->OAuthMode == CoreConstants::OAUTH2) + { + $oauth2Config = $ServiceContext->IppConfiguration->Security; + if($oauth2Config instanceof OAuth2AccessToken){ + $DataServiceInstance->configureOAuth2LoginHelper($oauth2Config, $settings); + }else{ + throw new SdkException("SDK Error. OAuth mode is not OAuth 2."); + } + } + + return $DataServiceInstance; + + } else { + throw new SdkException("Passed Null to Configure method. It expects either a file path for the config file or an array containing OAuth settings and BaseURL."); + } + } + + /** + * After the ServiceContext is complete, also set the LoginHelper based on the ServiceContext. + * @param OAuth2AccessToken $oauth2Conifg OAuth 2 Token related information + * @param Array $settings The array that include the redirectURL, scope, state information + */ + private function configureOAuth2LoginHelper($oauth2Conifg, $settings) + { + $refreshToken = CoreConstants::getRefreshTokenFromArray($settings); + if(isset($refreshToken)){ + //Login helper for refresh token API call + $this->OAuth2LoginHelper = new OAuth2LoginHelper(null, + null, + null, + null, + null, + $this->getServiceContext()); + }else{ + $redirectURL = CoreConstants::getRedirectURL($settings); + $scope = array_key_exists('scope', $settings) ? $settings['scope'] : null; + $state = array_key_exists('state', $settings) ? $settings['state'] : null; + $this->OAuth2LoginHelper = new OAuth2LoginHelper($oauth2Conifg->getClientID(), + $oauth2Conifg->getClientSecret(), + $redirectURL, + $scope, + $state); + } + } + + + + /** + * Return the OAuth 2 Login Helper. The OAuth 2 Login helper can be used to generate OAuth code, get refresh Token, etc. + * @return $OAuth2LoginHelper A helper to get OAuth 2 related values. + */ + public function getOAuth2LoginHelper() + { + return $this->OAuth2LoginHelper; + } + + /** + * Update the OAuth 2 Token that will be used for API calls later. + * + * @param OAuth2AccessToken $newOAuth2AccessToken The OAuth 2 Access Token that will be used later. + * + * @return $this + */ + public function updateOAuth2Token($newOAuth2AccessToken) + { + try{ + $this->serviceContext->updateOAuth2Token($newOAuth2AccessToken); + $realmID = $newOAuth2AccessToken->getRealmID(); + $this->serviceContext->realmId = $realmID; + $this->setupRestHandler($this->serviceContext); + } catch (SdkException $e){ + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "Encountered an error while updating OAuth2Token." . $e->getMessage()); + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "Stack Trace: " . $e->getTraceAsString()); + } + return $this; + } + + /** + * Get the error from last request + * + * @return FaultHandler lastError + */ + public function getLastError() + { + return $this->lastError; + } + + /** + * Setups serializers objects for responces and requests based on service context + */ + public function setupSerializers() + { + $this->responseSerializer = CoreHelper::GetSerializer($this->serviceContext, false); + $this->requestSerializer = CoreHelper::GetSerializer($this->serviceContext, true); + } + + private function useMinorVersion() + { + $version = $this->serviceContext->IppConfiguration->minorVersion; + if (is_numeric($version) && !empty($version)) { + $this->serviceContext->minorVersion = $version; + } + return $this; + } + + /** + * @return string + */ + public function getMinorVersion() + { + return $this->serviceContext->minorVersion; + } + + /** + * Force json serializers for request and response + */ + public function forceJsonSerializers() + { + $this->requestSerializer = new JsonObjectSerializer(); + $this->responseSerializer = new JsonObjectSerializer(); + } + + /** + * Returns serializer for responce objects + * @return IEntitySerializer + */ + protected function getResponseSerializer() + { + return $this->responseSerializer; + } + + /** + * Returns serializer for request objects + * @return IEntitySerializer + */ + protected function getRequestSerializer() + { + return $this->requestSerializer; + } + + /** + * Marshall a POPO object to XML, presumably for inclusion on an IPP v3 API call + * + * + * + * @param object $phpObj inbound POPO object + * @return string XML output derived from POPO object + * @deprecated since version ? + */ + private function getXmlFromObj($phpObj) + { + if (!$phpObj) { + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "getXmlFromObj NULL arg."); + + return false; + } + + $php2xml = new Php2Xml(CoreConstants::PHP_CLASS_PREFIX); + $php2xml->overrideAsSingleNamespace = 'http://schema.intuit.com/finance/v3'; + + try { + return $php2xml->getXml($phpObj); + } catch (\Exception $e) { + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "Encountered an error parsing Object to XML." . $e->getMessage()); + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "Stack Trace: " . $e->getTraceAsString()); + + return false; + } + } + + /** + * Decorate an IPP v3 Entity name (like 'Class') to be a POPO class name (like 'IPPClass') + * + * @param string Intuit Entity name + * @return string POPO class name + */ + private static function decorateIntuitEntityToPhpClassName($intuitEntityName) + { + $className = CoreConstants::PHP_CLASS_PREFIX . $intuitEntityName; + $className = trim($className); + + return $className; + } + + + //Since we add the namespace, this one needs to be changed as well. + private static function getEntityResourceName($entity) + { + return strtolower(self::cleanPhpClassNameToIntuitEntityName(get_class($entity))); + } + + /** + * Clean a POPO class name (like 'IPPClass') to be an IPP v3 Entity name (like 'Class') + * + * @param string $phpClassName POPO class name + * @return string|null Intuit Entity name + */ + private static function cleanPhpClassNameToIntuitEntityName($phpClassName) + { + $phpClassName = self::removeNameSpaceFromPhpClassName($phpClassName); + if (0 == strpos($phpClassName, CoreConstants::PHP_CLASS_PREFIX)) { + return substr($phpClassName, strlen(CoreConstants::PHP_CLASS_PREFIX)); + } + + return null; + } + + /** + * Remove the Namespace from a php class name + * + * @param string $phpClassName QuickBooksOnline\API\Data\... + * @return string ipp... + */ + private static function removeNameSpaceFromPhpClassName($phpClassName) + { + $lists = explode('\\', $phpClassName); + $ippEntityName = end($lists); + + return $ippEntityName; + } + + /** + * Using the @entity and @uri to generate Request. + * Response will parsed. It will store any Error Code in 3xx to 5xx level. + * + * @param IPPIntuitEntity $entity + * @param string $uri + * @param string $httpsPostBody + * @param string $CALLINGMETHOD + * @param string|null $boundaryString + * @param string|null $email + * @return null|string + */ + private function sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, $httpsPostBody, $CALLINGMETHOD, $boundaryString = null, $email = null) + { + if ($this->isCreditCardPaymentTxn($entity)) { + $uri = str_replace("creditcardpaymenttxn", "creditcardpayment", $uri); + } + + switch ($CALLINGMETHOD) { + case DataService::DELETE: + case DataService::ADD: + case DataService::VOID: + case DataService::UPDATE: + $requestParameters = $this->initPostRequest($entity, $uri); + break; + case DataService::FINDBYID: + if ($this->serviceContext->IppConfiguration->Message->Request->SerializationFormat == SerializationFormat::Json) { + $requestParameters = new RequestParameters($uri, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONJSON, null); + } else { + $requestParameters = new RequestParameters($uri, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); + } + break; + case DataService::UPLOAD: + if (!isset($boundaryString)) { + throw new \Exception("Upload Image has unset value: boundaryString."); + } + // Creates request parameters + $requestParameters = $this->getPostRequestParameters($uri, "multipart/form-data; boundary={$boundaryString}"); + break; + case DataService::SENDEMAIL: + $requestParameters = $this->getPostRequestParameters($uri . (is_null($email) ? '' : '?sendTo=' . urlencode($email)), CoreConstants::CONTENTTYPE_OCTETSTREAM); + break; + } + $restRequestHandler = $this->getRestHandler(); + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + if ($faultHandler) { + $this->lastError = $faultHandler; + return null; + } else { + $this->lastError = false; + if (strcmp($CALLINGMETHOD, DataService::ADD) == 0) { + $responseBody = $this->fixTaxServicePayload($entity, $responseBody); + } + try { + $parsedResponseBody = $this->getResponseSerializer()->Deserialize($responseBody, true); + } catch (\Exception $e) { + return new \Exception("Exception in deserialize ResponseBody."); + } + + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Finished Executing Method " . $CALLINGMETHOD); + + return $parsedResponseBody; + } + } + + /** + * Updates an entity under the specified realm. The realm must be set in the context. + * + * @param IPPIntuitEntity $entity Entity to Update. + * @return IPPIntuitEntity Returns an updated version of the entity with updated identifier and sync token. + * @throws IdsException + */ + public function Update($entity) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method: Update."); + + // Validate parameter + if (!$entity) { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); + throw new IdsException('Argument Null Exception'); + } + $this->verifyOperationAccess($entity, __FUNCTION__); + + $httpsPostBody = $this->executeObjectSerializer($entity, $urlResource); + + // Builds resource Uri + // Handle some special cases + if ((strtolower('preferences') == strtolower($urlResource)) && + (CoreConstants::IntuitServicesTypeQBO == $this->serviceContext->serviceType) + ) { + // URL format for *QBO* prefs request is different than URL format for *QBD* prefs request + $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource)); + } + //We no longer support QBD on PHP SDK The code is removed. + /*else if ((strtolower('company') == strtolower($urlResource)) && + (CoreConstants::IntuitServicesTypeQBO == $this->serviceContext->serviceType)) { + // URL format for *QBD* companyinfo request is different than URL format for *QBO* companyinfo request + $urlResource = 'companyInfo'; + $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource . '?operation=update')); + }*/ else { + // Normal case + $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource . '?operation=update')); + } + + // Send Request and return response + return $this->sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, $httpsPostBody, DataService::UPDATE); + } + + /** + * The Read request. You can either pass an object that contains the Id that you want to read, or + * pass the Entity Name and the Id. + * Before v4.0.0, it supports the read of CompanyInfo and Preferences. + * After v4.0.0, it DOES NOT support read of CompanyInfo or Preferences. Please use getCompanyInfo() or getCompanyPreferences() method instead. + * Only use this one to do READ request with ID. + * + * Developer has two ways to call the GET request. Take Invoice for an example: + * 1) FindById($invoice); + * or + * 2) FindById("invoice", 1); + * + * @param object|String $entity Entity to Find, or the String Name of the Entity + * @return IPPIntuitEntity Returns an entity of specified Id. + * @throws IdsException + */ + public function FindById($entity, $Id = null) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method FindById."); + if(is_object($entity)){ + $httpsPostBody = $this->executeObjectSerializer($entity, $urlResource); + // Validate parameter + if (!$entity || !$entity->Id) { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); + throw new IdsException('Argument Null Exception when calling FindById for Endpoint:' . get_class($entity)); + } + $this->verifyOperationAccess($entity, __FUNCTION__); + $entityId = $this->getIDString($entity->Id); + // Normal case + $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource, $entityId)); + // Send request + return $this->sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, null, DataService::FINDBYID); + }else if(is_string($entity) && isset($Id)){ + $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, strtolower($entity), $Id)); + if ($this->isCreditCardPaymentTxn($entity)) { + $uri = str_replace("creditcardpaymenttxn", "creditcardpayment", $uri); + } + $requestParameters = new RequestParameters($uri, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); + $restRequestHandler = $this->getRestHandler(); + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, null, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + //$faultHandler now is true or false + if ($faultHandler) { + $this->lastError = $faultHandler; + return null; + } else { + //clean the error + $this->lastError = false; + $parsedResponseBody = $this->getResponseSerializer()->Deserialize($responseBody, true); + return $parsedResponseBody; + } + } + } + + /** + * Creates an entity under the specified realm. The realm must be set in the context. + * + * @param IPPIntuitEntity $entity Entity to Create. + * @return IPPIntuitEntity Returns the created version of the entity. + * @throws IdsException + */ + public function Add($entity) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Add."); + + // Validate parameter + if (!$entity) { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); + throw new IdsException('Argument Null Exception'); + } + $this->verifyOperationAccess($entity, __FUNCTION__); + if ($this->isJsonOnly($entity)) { + $this->forceJsonSerializers(); + } + + $httpsPostBody = $this->executeObjectSerializer($entity, $urlResource); + // Builds resource Uri + $resourceURI = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource)); + + $uri = $this->handleTaxService($entity, $resourceURI); + // Send request + return $this->sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, $httpsPostBody, DataService::ADD); + } + + /** + * Deletes an entity under the specified realm. The realm must be set in the context. + * + * @param IPPIntuitEntity $entity Entity to Delete. + * @return null|string + * @throws IdsException + */ + public function Delete($entity) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Delete."); + + // Validate parameter + if (!$entity) { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); + throw new IdsException('Argument Null Exception'); + } + $this->verifyOperationAccess($entity, __FUNCTION__); + + // Builds resource Uri + $httpsPostBody = $this->executeObjectSerializer($entity, $urlResource); + $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource . '?operation=delete')); + + // Creates request + return $this->sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, $httpsPostBody, DataService::DELETE); + } + + /** + * Voids an entity under the specified realm. The realm must be set in the context. + * + * @param IPPIntuitEntity $entity Entity to Void. + * @return null|string + * @throws IdsException + */ + public function Void($entity) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Void."); + // Validate parameter + if (!$entity) { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); + throw new IdsException('Argument Null Exception'); + } + $this->verifyOperationAccess($entity, __FUNCTION__); + + // Builds resource Uri + $httpsPostBody = $this->executeObjectSerializer($entity, $urlResource); + $className = $this->getEntityResourceName($entity); + if(in_array($className, CoreConstants::PAYMENTCLASSNAME)) { + $appendString = CoreConstants::VOID_QUERYPARAMETER_PAYMENT; + } else{ + $appendString = CoreConstants::VOID_QUERYPARAMETER_GENERAL; + } + $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource . $appendString)); + // Creates request + return $this->sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, $httpsPostBody, DataService::VOID); + } + + /** + * Uploads an attachment to an Entity on QuickBooks Online. For security reason, text file is not supported for uploading. + * + * @param string $bits Encoded Base64 bytes for the attachment + * @param string $fileName Filename to use for this file + * @param string $mimeType MIME type to send in the HTTP Headers + * @param IPPAttachable $objAttachable Entity including the attachment, it can be invoice, bill, etc + * @return array Returns an array of entities fulfilling the query. + * @throws IdsException + */ + public function Upload($imgBits, $fileName, $mimeType, $objAttachable) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Upload."); + + // Validate parameter + if (!$imgBits || !$mimeType || !$fileName) { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); + throw new IdsException('Argument Null Exception'); + } + // Builds resource Uri + $urlResource = "upload"; + $uri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, $urlResource)); + + $boundaryString = md5(time()); + + $MetaData = $this->executeObjectSerializer($objAttachable, $urlResource); + + $desiredIdentifier = '0'; + $newline = "\r\n"; + $dataMultipart = ''; + $dataMultipart .= '--' . $boundaryString . $newline; + $dataMultipart .= "Content-Disposition: form-data; name=\"file_metadata_{$desiredIdentifier}\"" . $newline; + $dataMultipart .= "Content-Type: " . CoreConstants::CONTENTTYPE_APPLICATIONXML . '; charset=UTF-8' . $newline; + $dataMultipart .= 'Content-Transfer-Encoding: 8bit' . $newline . $newline; + $dataMultipart .= $MetaData; + $dataMultipart .= '--' . $boundaryString . $newline; + $dataMultipart .= "Content-Disposition: form-data; name=\"file_content_{$desiredIdentifier}\"; filename=\"{$fileName}\"" . $newline; + $dataMultipart .= "Content-Type: {$mimeType}" . $newline; + $dataMultipart .= 'Content-Transfer-Encoding: base64' . $newline . $newline; + $dataMultipart .= chunk_split($imgBits) . $newline; + $dataMultipart .= "--" . $boundaryString . "--" . $newline . $newline; // finish with two eol's!! + + return $this->sendRequestParseResponseBodyAndHandleHttpError(null, $uri, $dataMultipart, DataService::UPLOAD, $boundaryString); + } + + /** + * Returns PDF for entities which can be downloaded as PDF + * @param IPPIntuitEntity $entity + * @param Directory a writable directory for the PDF to be saved. + * @return boolean + * @throws IdsException, SdkException + * + */ + public function DownloadPDF($entity, $dir=null, $returnPdfString = false) + { + $this->validateEntityId($entity); + $this->verifyOperationAccess($entity, __FUNCTION__); + + //Find the ID + $entityID = $this->getIDString($entity->Id); + $uri = implode(CoreConstants::SLASH_CHAR, array('company', + $this->serviceContext->realmId, + self::getEntityResourceName($entity), + $entityID, + CoreConstants::getType(CoreConstants::CONTENTTYPE_APPLICATIONPDF))); + $requestParameters = $this->getGetRequestParameters($uri, CoreConstants::CONTENTTYPE_APPLICATIONPDF); + $restRequestHandler = $this->getRestHandler(); + + + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, null, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + if ($faultHandler) { + $this->lastError = $faultHandler; + //Add allow for through exception if users set it up + return null; + } elseif ($returnPdfString) { + return $responseBody; + } else { + $this->lastError = false; + + return $this->processDownloadedContent(new ContentWriter($responseBody), $responseCode, $dir, $this->getExportFileNameForPDF($entity, "pdf")); + } + } + + /** + * Sends entity by email for entities that have this operation + * + * @param IPPIntuitEntity $entity + * @param string|null $email + * @return boolean + * @throws IdsException, SdkException + * + */ + public function SendEmail($entity, $email = null) + { + $this->validateEntityId($entity); + $this->verifyOperationAccess($entity, __FUNCTION__); + + $entityId=$this->getIDString($entity->Id); + $uri = implode(CoreConstants::SLASH_CHAR, array('company', + $this->serviceContext->realmId, + self::getEntityResourceName($entity), + $entityId, + 'send')); + + if (is_null($email)) { + $this->logInfo("Entity " . get_class($entity) . " with id=" . $entityId . " is using default email"); + } else { + $this->logInfo("Entity " . get_class($entity) . " with id=" . $entityId . " is using $email"); + if (!$this->verifyEmailAddress($email)) { + $this->logError("Valid email is expected, but received $email"); + throw new SdkException("Valid email is expected, but received $email"); + } + } + + return $this->sendRequestParseResponseBodyAndHandleHttpError($entity, $uri, null, DataService::SENDEMAIL, null, $email); + } + + /** + * Retrieves specified entities based passed page number and page size and query + * + * @param string $query Query to issue + * @param int $startPosition Starting page number + * @param int $maxResults Page size + * @param string $includes A list of additional fields requested in the entities response + * @return array Returns an array of entities fulfilling the query. If the response is Empty, it will return NULL + */ + public function Query($query, $startPosition = null, $maxResults = null, $includes = null) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Query."); + + if ('QBO' == $this->serviceContext->serviceType) { + $httpsContentType = CoreConstants::CONTENTTYPE_APPLICATIONTEXT; + } else { + $httpsContentType = CoreConstants::CONTENTTYPE_TEXTPLAIN; + } + + $httpsUri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, 'query')); + $httpsPostBody = $this->appendPaginationInfo($query, $startPosition, $maxResults); + + if(!is_null($includes)) { + $httpsUri .= "?include=$includes"; + } + + $requestParameters = $this->getPostRequestParameters($httpsUri, $httpsContentType); + $restRequestHandler = $this->getRestHandler(); + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + if ($faultHandler) { + $this->lastError = $faultHandler; + return null; + } else { + $this->lastError = false; + $parsedResponseBody = null; + try { + $responseXmlObj = simplexml_load_string($responseBody); + if ($responseXmlObj && $responseXmlObj->QueryResponse) { + $tmpXML = $responseXmlObj->QueryResponse->asXML(); + $parsedResponseBody = $this->responseSerializer->Deserialize($tmpXML, false); + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, $parsedResponseBody); + } + + } catch (\Exception $e) { + throw new \Exception("Exception appears in converting Response to XML."); + } + + return $parsedResponseBody; + } + } + + /** + * Append the Pagination Data to the Query string if it is not appended + * @param Integer StartPostion + * @param Integer MaxResults + * @return String The query string + */ + private function appendPaginationInfo($query, $startPosition, $maxResults){ + $query = trim($query); + if(isset($startPosition) && !empty($startPosition)){ + if(stripos($query, "STARTPOSITION") === false){ + if(stripos($query, "MAXRESULTS") !== false){ + //In MaxResult is defined,we don't set startPosition + }else{ + $query = $query . " " . "STARTPOSITION " . $startPosition; + } + }else{ + //Ignore the startPosition if it is already used on the query + } + } + + if(isset($maxResults) && !empty($maxResults)){ + if(stripos($query, "MAXRESULTS") === false){ + $query = $query . " " . "MAXRESULTS " . $maxResults; + }else{ + //Ignore the maxResults if it is already used on the query + } + } + + return $query; + + } + + /** + * Retrieves all entities by name + * + * @param string $entityName + * @param int $pageNumber + * @param int $pageSize + * @return array Returns an array of entities of specified type. + */ + public function FindAll($entityName, $pageNumber = 0, $pageSize = 500) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method FindAll."); + + $phpClassName = DataService::decorateIntuitEntityToPhpClassName($entityName); + + // Handle some special cases + if (strtolower('company') == strtolower($entityName)) { + $entityName = 'CompanyInfo'; + } + + if ('QBO' == $this->serviceContext->serviceType) { + $httpsContentType = CoreConstants::CONTENTTYPE_APPLICATIONTEXT; + } else { + $httpsContentType = CoreConstants::CONTENTTYPE_TEXTPLAIN; + } + + $httpsUri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, 'query')); + $httpsPostBody = "select * from $entityName startPosition $pageNumber maxResults $pageSize"; + + $requestParameters = $this->getPostRequestParameters($httpsUri, $httpsContentType); + $restRequestHandler = $this->getRestHandler(); + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + if ($faultHandler) { + $this->lastError = $faultHandler; + return null; + } else { + $this->lastError = false; + $parsedResponseBody = null; + try { + $responseXmlObj = simplexml_load_string($responseBody); + if ($responseXmlObj && $responseXmlObj->QueryResponse) { + $parsedResponseBody = $this->responseSerializer->Deserialize($responseXmlObj->QueryResponse->asXML(), false); + } + } catch (\Exception $e) { + throw new \Exception("Exception appears in converting Response to XML."); + } + + return $parsedResponseBody; + } + } + + /** + * Returns List of entities changed after certain time. + * + * @param array entityList List of entity. + * @param long changedSince DateTime of timespan after which entities were changed. + * @return IntuitCDCResponse Returns an IntuitCDCResponse. + */ + public function CDC($entityList, $changedSince) + { + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Called Method CDC."); + + // Validate parameter + if (count($entityList) <= 0) { + $exception = new IdsException('ParameterNotNullMessage'); + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "ParameterNotNullMessage"); + IdsExceptionManager::HandleException($exception); + } + + $entityString = implode(",", $entityList); + + $query = null; + $uri = null; + + if(is_string($changedSince)){ + $formattedChangedSince = trim($changedSince); + }else{ + $formattedChangedSince = date("Y-m-d\TH:i:s", $this->verifyChangedSince($changedSince)); + } + + $query = "entities=" . $entityString . "&changedSince=" . $formattedChangedSince; + $uri = "company/{1}/cdc?{2}"; + //$uri = str_replace("{0}", CoreConstants::VERSION, $uri); + $uri = str_replace("{1}", $this->serviceContext->realmId, $uri); + $uri = str_replace("{2}", $query, $uri); + + // Creates request parameters + $requestParameters = $this->getGetRequestParameters($uri, CoreConstants::CONTENTTYPE_APPLICATIONXML); + $restRequestHandler = $this->getRestHandler(); + + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, null, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + if ($faultHandler) { + $this->lastError = $faultHandler; + return null; + } else { + $this->lastError = false; + $returnValue = new IntuitCDCResponse(); + try { + $xmlObj = simplexml_load_string($responseBody); + $responseArray = $xmlObj->CDCResponse->QueryResponse; + if(sizeof($responseArray) != sizeof($entityList)){ + throw new ServiceException("The number of Entities requested on CDC does not match the number of Response."); + } + + for($i = 0; $i < sizeof($responseArray); $i++){ + $currentResponse = $responseArray[$i]; + $currentEntityName = $entityList[$i]; + $entities = $this->responseSerializer->Deserialize($currentResponse->asXML(), false); + $entityName = $currentEntityName; + //If we find the actual name, update it. + foreach ($currentResponse->children() as $currentResponseChild) { + $entityName = (string)$currentResponseChild->getName(); + break; + } + $returnValue->entities[$entityName] = $entities; + } + } catch (\Exception $e) { + IdsExceptionManager::HandleException($e); + } + + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Finished Executing Method CDC."); + return $returnValue; + } + } + + /** + * Creates a RecurringTransaction Entity under the specified realm. The realm must be set in the context. + * + * @param IPPIntuitEntity $entity Entity to Create. + * @return IntuitRecurringTransactionResponse Returns the RecurringTransaction created for the entity. + * @throws IdsException + */ + public function addRecurringTxn($entity) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Add."); + + // Validate parameter + if (!$entity) { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); + throw new IdsException('Argument Null Exception'); + } + // Verify operation access + $this->verifyOperationAccess($entity, __FUNCTION__); + if ($this->isJsonOnly($entity)) { + $this->forceJsonSerializers(); + } + // Create recurring transaction object + $recurringtxn = RecurringTransactionAdapter::createRecurringTransactionObject($entity); + + // Create recurring transaction Post Body + $httpsPostBody = RecurringTransactionAdapter::getRecurringTxnBody($recurringtxn); + + // Builds resource Uri + $resourceURI = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, 'recurringtransaction')); + + $requestParameters = new RequestParameters($resourceURI, 'POST', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); + $restRequestHandler = $this->getRestHandler(); + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + if ($faultHandler) { + $this->lastError = $faultHandler; + return null; + } else { + $this->lastError = false; + $returnValue = new IntuitRecurringTransactionResponse(); + try { + $xmlObj = simplexml_load_string($responseBody); + // deserialize the response body + $deserializedResponse = $this->responseSerializer->Deserialize($xmlObj->RecurringTransaction->asXML(), false); + $entityName = XmlObjectSerializer::cleanPhpClassNameToIntuitEntityName(get_class($entity)); + $returnValue->entities[$entityName] = $deserializedResponse; + } catch (\Exception $e) { + IdsExceptionManager::HandleException($e); + } + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Finished Executing Recurring Transaction."); + return $returnValue; + } + } + + /** + * Query RecurringTransaction Entity under the specified realm. The realm must be set in the context. + * + * @param query ex : SELECT * FROM RecurringTransaction + * @return IntuitRecurringTransactionResponse Returns the RecurringTransaction created for the entity. + * @throws IdsException + */ + public function recurringTransaction($query) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Query."); + + if ('QBO' == $this->serviceContext->serviceType) { + $httpsContentType = CoreConstants::CONTENTTYPE_APPLICATIONTEXT; + } else { + $httpsContentType = CoreConstants::CONTENTTYPE_TEXTPLAIN; + } + + $httpsUri = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, 'query')); + $httpsPostBody = $this->appendPaginationInfo($query, $startPosition, $maxResults); + + $requestParameters = $this->getPostRequestParameters($httpsUri, $httpsContentType); + $restRequestHandler = $this->getRestHandler(); + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + if ($faultHandler) { + $this->lastError = $faultHandler; + return null; + } else { + $this->lastError = false; + $returnValue = new IntuitRecurringTransactionResponse(); + try { + $xmlObj = simplexml_load_string($responseBody); + $responseArray = $xmlObj->QueryResponse->RecurringTransaction; + if(sizeof($responseArray) <= 0){ + throw new ServiceException("No recurring transactions found."); + } + + for($i = 0; $i < sizeof($responseArray); $i++){ + $currentResponse = $responseArray[$i]; + $currentEntityName = $entityList[$i]; + $entities = $this->responseSerializer->Deserialize($currentResponse->asXML(), false); + $entityName = $currentEntityName; + //If we find the actual name, update it. + foreach ($currentResponse->children() as $currentResponseChild) { + $entityName = (string)$currentResponseChild->getName(); + break; + } + $returnValue->entities[$entityName][] = $entities; + } + } catch (\Exception $e) { + IdsExceptionManager::HandleException($e); + } + + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Finished Executing Recurring Transaction."); + return $returnValue; + } + } + + /** + * Find a RecurringTransaction Entity By ID under the specified realm. The realm must be set in the context. + * + * @param $Id Id of the IPPIntuitEntity Object + * @return IntuitRecurringTransactionResponse Returns the RecurringTransaction created for the entity. + * @throws IdsException + */ + public function findRecurringTransactionById($Id = null) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method findRecurringTransactionById."); + + if (!$Id) { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); + throw new IdsException('Argument [Id] Null Exception'); + } + + // Builds resource Uri + $resourceURI = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, 'recurringtransaction/'. $Id)); + + // Make the GET request to fetch the recurring transaction + $requestParameters = new RequestParameters($resourceURI, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); + $restRequestHandler = $this->getRestHandler(); + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, null, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + //$faultHandler now is true or false + if ($faultHandler) { + $this->lastError = $faultHandler; + return null; + } else { + //clean the error + $this->lastError = false; + $returnValue = new IntuitRecurringTransactionResponse(); + try { + $xmlObj = simplexml_load_string($responseBody); + + // deserialize the response body + $deserializedResponse = $this->responseSerializer->Deserialize($xmlObj->RecurringTransaction->asXML(), false); + $entityName = XmlObjectSerializer::cleanPhpClassNameToIntuitEntityName(get_class($deserializedResponse[0])); + $returnValue->entities[$entityName] = $deserializedResponse; + } catch (\Exception $e) { + IdsExceptionManager::HandleException($e); + } + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Finished Executing Recurring Transaction."); + return $returnValue; + } + } + + /** + * Find a RecurringTransaction Entity By ID under the specified realm. The realm must be set in the context. + * + * @param IntuitRecurringTransactionResponse RecurringTransaction Response from findRecurringTransactionById method. + * @return IntuitRecurringTransactionResponse Returns the RecurringTransaction created for the entity. + * @throws IdsException + */ + public function deleteRecurringTransaction($recurringTransaction) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called Method Delete."); + + // Validate parameter + if (!$recurringTransaction) { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, "Argument Null Exception"); + throw new IdsException('Argument Null Exception'); + } + $this->verifyOperationAccess($recurringTransaction, __FUNCTION__); + + // Get the Entity Name from $recurringTransaction + $entityName = array_keys($recurringTransaction->entities)[0]; + + // Get the IPPEntity Object from $recurringTransaction + $entity = $recurringTransaction->entities[$entityName][0]; + + // Create recurring transaction object + $recurringtxn = RecurringTransactionAdapter::createRecurringTransactionObject($entity); + // Create recurring transaction Post Body + $httpsPostBody = RecurringTransactionAdapter::getRecurringTxnBody($recurringtxn); + + // Builds resource Uri + $resourceURI = implode(CoreConstants::SLASH_CHAR, array('company', $this->serviceContext->realmId, 'recurringtransaction' . '?operation=delete')); + + // Make the GET request to fetch the recurring transaction + $requestParameters = new RequestParameters($resourceURI, 'POST', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); + $restRequestHandler = $this->getRestHandler(); + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + //$faultHandler now is true or false + if ($faultHandler) { + $this->lastError = $faultHandler; + return null; + } else { + //clean the error + $this->lastError = false; + $returnValue = new IntuitRecurringTransactionResponse(); + try { + $xmlObj = simplexml_load_string($responseBody); + + // deserialize the response body + $deserializedResponse = $this->responseSerializer->Deserialize($xmlObj->RecurringTransaction->asXML(), false); + $entityName = XmlObjectSerializer::cleanPhpClassNameToIntuitEntityName(get_class($entity)); + $returnValue->entities[$entityName] = $deserializedResponse; + } catch (\Exception $e) { + IdsExceptionManager::HandleException($e); + } + $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Finished Executing Recurring Transaction."); + return $returnValue; + } + } + + /** + * Returns an entity under the specified realm. The realm must be set in the context. + * + * @param object $entity Entity to Find + * @return IPPIntuitEntity Returns an entity of specified Id. + */ + public function Retrieve($entity) + { + $this->verifyOperationAccess($entity, __FUNCTION__); + + return $this->FindById($entity); + } + + /** + * Serializes oblect into specified format + * @param IPPIntuitEntity $entity + * @param String $urlResource + * @return object + */ + protected function executeObjectSerializer($entity, &$urlResource) + { + $result = $this->getRequestSerializer()->Serialize($entity); + $urlResource = $this->getRequestSerializer()->getResourceURL(); + + return $result; + } + + /** + * Returns post request, depends on configuration and entity rule "jsonOnly" + * @param IPPIntuitEntity $entity + * @param string $uri + * @return RequestParameters + */ + protected function initPostRequest($entity, $uri) + { + return $this->isJsonOnly($entity) + ? $this->getPostJsonRequest($uri) + : $this->getPostRequest($uri); + } + + /** + * Returns content type depends from serialization format + * @return string + */ + private function getContentType() + { + return ($this->getSerializationFormat() == SerializationFormat::Json) + ? CoreConstants::CONTENTTYPE_APPLICATIONJSON + : CoreConstants::CONTENTTYPE_APPLICATIONXML; + } + + /** + * Returns post request with pre-defined POST method and JSON serialization + * @param string $uri + * @return RequestParameters + */ + private function getPostJsonRequest($uri) + { + return $this->getPostRequestParameters($uri, CoreConstants::CONTENTTYPE_APPLICATIONJSON); + } + + /** + * Return true if specified entity can work with JSON only serialization + * @param IPPIntuitEntity $entity + * @return boolean + */ + private function isJsonOnly($entity) + { + return $this->isAllowed($entity, "jsonOnly"); + } + + /** + * Returns pre-defined request with POST method and content type from settings + * @param string $uri + * @return RequestParameters + */ + private function getPostRequest($uri) + { + return $this->getPostRequestParameters($uri, $this->getContentType()); + } + + /** + * Returns pre-defined request with GET method + * @param string $uri + * @param string $type + * @return RequestParameters + */ + private function getGetRequestParameters($uri, $type) + { + return $this->getRequestParameters($uri, 'GET', $type); + } + + /** + * Returns pre-defined request with POST method + * @param string $uri + * @param string $type + * @return RequestParameters + */ + private function getPostRequestParameters($uri, $type) + { + return $this->getRequestParameters($uri, 'POST', $type); + } + + /** + * Wraps and returns RequestParameters object + * @param string $uri + * @param string $method + * @param string $type + * @param string $apiName + * @return RequestParameters + */ + protected function getRequestParameters($uri, $method, $type, $apiName = null) + { + return new RequestParameters($uri, $method, $type, $apiName); + } + + /** + * Returns current serialization format + * + * @return string + */ + protected function getSerializationFormat() + { + return $this->serviceContext->IppConfiguration->Message->Request->SerializationFormat; + } + + /** + * Handles unusual URL mapping for TaxService + * @param IPPIntuitEntity $entity + * @param string $uri + * @return string + */ + private function handleTaxService($entity, $uri) + { + if ($this->isTaxServiceSafe($entity)) { + return $uri . '/taxcode'; + } + + return $uri; + } + + /** + * Verifies that entity has TaxService type + * update the TaxService with namespace added + * If this class is not available on include_path or wab't loaded the method will return false + * + * @param IPPTaxService $entity + * @return bool + */ + private function isTaxServiceSafe($entity) + { + $IPPTaxServiceClassWIthNameSpace = "QuickBooksOnline\\API\\Data\\IPPTaxService"; + + return class_exists($IPPTaxServiceClassWIthNameSpace) && ($entity instanceof $IPPTaxServiceClassWIthNameSpace); + } + + private function isCreditCardPaymentTxn($entity) + { + $IPPCreditCardPaymentTxnClass = "QuickBooksOnline\\API\\Data\\IPPCreditCardPaymentTxn"; + + if (class_exists($IPPCreditCardPaymentTxnClass) && ($entity instanceof $IPPCreditCardPaymentTxnClass)) + return true; + else if (is_string($entity) && $entity == "CreditCardPaymentTxn") + return true; + + return false; + } + + /** + * Methods provides workaround to successfully process TaxService response + * @param $entity + * @param $content + * @return string + */ + private function fixTaxServicePayload($entity, $content) + { + if ($this->isTaxServiceSafe($entity)) { + //get first "line" to make sure we don't have TaxService in response + $sample = substr(trim($content), 0, 20); + $taxServiceName = self::cleanPhpClassNameToIntuitEntityName(get_class($entity)); + if (false === strpos($sample, $taxServiceName)) { + //last attempt to verify content before + if (0 === strpos($sample, '{"TaxCode":')) { + return "{\"$taxServiceName\":$content}"; + } + } + } + + return $content; + } + + + /** + * Returns an downloaded entity under the specified realm. The realm must be set in the context. + * + * @param object $entity Entity to Find + * @return string IPPIntuitEntity Returns an entity of specified Id. + * @deprecated The download for QuickBooksOnline is only supporting download PDF for Invoice and SalesReceipt. Other download function are not defined now. + */ + public function Download($entity) + { + return $this->FindById($entity); + } + + /** + * Verifies string as email agains RFC 822 + * @param string $email + * @return type + */ + public function verifyEmailAddress($email) + { + return filter_var($email, FILTER_VALIDATE_EMAIL); + } + + /** + * Returns PDF filename based on entity type and id + * @param IPPIntuitEntity $entity + * @param string $ext + * @return string + */ + public function getExportFileNameForPDF($entity, $ext, $usetimestamp = true) + { + //TODO: add timestamp or GUID + $this->validateEntityId($entity); + + return self::getEntityResourceName($entity) . "_" . $this->getIDString($entity->Id) . ($usetimestamp ? "_" . time() : "") . ".$ext"; + } + + /** + * Returns the Sync Resthandler associated with the dataserivce + * @return SyncRestHandler The SyncRestHandler with the DataService + */ + protected function getRestHandler() + { + if(isset($this->restHandler)){ + return $this->restHandler; + }else{ + throw new SdkException("The SyncRest handler associated with the DataService is not set."); + } + } + + /** + * Saves exported (e.g. check DownloadPDF) entity into file according to strategy in settings + * @param ContentWriter $writer + * @param int $responseCode + * @param string $fileName + * @return mixed full path with filename or open handler + */ + protected function processDownloadedContent(ContentWriter $writer, $responseCode, $dir, $fileName = null) + { + $writer->setPrefix($this->getPrefixFromSettings()); + try { + if(isset($dir) && !empty($dir)){ + $writer->saveFile($dir, $fileName); + }else if ($this->isTempFile()) { + $writer->saveTemp(); + } elseif ($this->isFileExport()) { + $writer->saveFile($this->getFileExportDir(), $fileName); + } else { + $writer->saveAsHandler(); + } + //return object as is + if ($this->isReturnContentWriter()) { + return $writer; + } + $writer->resetContent(); + $this->logInfo("File was downloaded (http response = $responseCode), bytes written: {$writer->getBytes()}"); + } catch (\Exception $ex) { + $this->logError("Exception appears during response processing. Http response was $responseCode: " . $ex->getMessage() . "\n" . $ex->getTraceAsString()); + + return null; + } + + return $writer->isHandler() ? $writer->getHandler() : $writer->getTempPath(); + } + + protected function processRecurringTransactionResponse($recurringTransaction) { + //Return an array of objects + $entities = []; + foreach ($recurringTransaction as $oneResponse) { + $oneEntity = $this->responseSerializer->Deserialize(''.$oneResponse->children()->asXML().''); + $entities = array_merge($entities, $oneEntity); + } + + return $entities; + } + + /** + * Returns true or false if writer object is allowed as return result + * @return boolean + */ + private function isReturnContentWriter() + { + return $this->serviceContext->IppConfiguration->ContentWriter->returnOject; + } + + /** + * Returns prefix for contentWriter + * @return string + */ + private function getPrefixFromSettings() + { + return $this->serviceContext->IppConfiguration->ContentWriter->prefix; + } + + /** + * Returns true if SDK is configured to save temporary files + * @return boolean + */ + private function isTempFile() + { + return (CoreConstants::FILE_STRATEGY === $this->serviceContext->IppConfiguration->ContentWriter->strategy); + } + + /** + * Returns true if SDK is configured to export files to another location + * @return boolean + */ + private function isFileExport() + { + return (CoreConstants::EXPORT_STRATEGY === $this->serviceContext->IppConfiguration->ContentWriter->strategy); + } + + /** + * Returns path to directory where SDK should save files + * @return string + */ + private function getFileExportDir() + { + return $this->serviceContext->IppConfiguration->ContentWriter->exportDir; + } + + + /** + * Simple verification for entities which can be returned as PDF + */ + private function isAllowed($entity, $method) + { + $className = get_class($entity); + if (!$className) { + $this->logError("Intuit entity is expected here instead of $entity"); + throw new IdsException('Unexpected Argument Exception'); + } + + $classArray = explode('\\', $className); + $trimedClassName = end($classArray); + + return $this->serviceContext->IppConfiguration->OpControlList->isAllowed($trimedClassName, $method); + } + + + private function verifyOperationAccess($entity, $func) + { + if (!$this->isAllowed($entity, $func)) { + $this->logError("Cannot invoke " . $func . " for \"" . get_class($entity) . "\" because of operation contstrains."); + throw new IdsException('Operation ' . $func . ' is not allowed for entity ' . get_class($entity)); + } + + return true; + } + + + private function validateEntityId($entity) + { + if (empty($entity)) { + $this->logError("Argument Null Exception"); + throw new IdsException('Argument Null Exception'); + } + if (!isset($entity->Id)) { + $this->logError("Property ID doesn't exist"); + throw new IdsException('Property ID is not set'); + } + + if (empty($entity->Id)) { + $this->logError("Property ID is empty"); + throw new IdsException('Property ID is empty'); + } + + return true; + } + + private function logError($message) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Error, $message); + } + + private function logInfo($message) + { + $this->serviceContext->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, $message); + } + + + /** + * Creates new batch + * + * @return Batch returns the batch object + */ + public function CreateNewBatch() + { + $batch = new Batch($this->serviceContext, $this->getRestHandler(), $this->isThrownExceptionOnError()); + + return $batch; + } + + + /** + * Parse input date-time string into unix timestamp + * @param string $str + * @return int timestamp or false overwise + * @throws SdkException + */ + private function convertToTimestamp($str) + { + $result = date_parse($str); + if (!$result) { + return false; + } + if (empty($result) || !is_array($result)) { + return false; + } + extract($result); + if (!empty($errors)) { + throw new SdkException("SDK failed to parse date value \"$str\":" + . (is_array($errors) ? implode("\n", $errors) : $errors) + ); + } + + //@TODO: mktime is deprecated since 5.3.0, this package needs 5.6 + return mktime($hour, $minute, $second, $month, $day, $year); + } + + /** + * Checks if input string is a valid timestamp or not + * @param string timestamp contains input timestamp-like string + * @return bool + */ + public function isValidTimeStamp($timestamp) + { + return ((string)(int)$timestamp === $timestamp) && ($timestamp <= PHP_INT_MAX) && ($timestamp >= ~PHP_INT_MAX); + } + + /** + * Verifies input and returns unix timestamp + * @param mixed $value + * @return int + * @throws SdkException + */ + protected function verifyChangedSince($value) + { + if (is_int($value)) { + return $value; + } + // remove whitespaces, tabulation etc + $trimmed = trim($value); + if ($this->isValidTimeStamp($trimmed)) { + return $trimmed; + } + //at this point we have numeric string which is not timestamp + if (is_numeric($value)) { + throw new SdkException("Input string doesn't look as unix timestamp or date string"); + } + // trying to parse string into timestamp + $converted = $this->convertToTimestamp($value); + if (!$converted) { + throw new SdkException("Input value should be unix timestamp or valid date string"); + } + + return $converted; + } + + /** + * Get the Company Information + * @return IPPCompanyInfo CompanyInformation + */ + public function getCompanyInfo() + { + $currentServiceContext = $this->getServiceContext(); + if (!isset($currentServiceContext) || empty($currentServiceContext->realmId)) { + throw new SdkException("Please Setup Service Context before making get CompanyInfo call."); + } + //The CompanyInfo URL + $uri = implode(CoreConstants::SLASH_CHAR, array('company', $currentServiceContext->realmId, 'companyinfo', $currentServiceContext->realmId)); + $requestParameters = new RequestParameters($uri, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); + $restRequestHandler = $this->getRestHandler(); + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, null, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + //$faultHandler now is true or false + if ($faultHandler) { + $this->lastError = $faultHandler; + return null; + } else { + $this->lastError = false; + $parsedResponseBody = $this->getResponseSerializer()->Deserialize($responseBody, true); + return $parsedResponseBody; + } + } + + /** + * Get the Company Preferences Information + * @return IPPPreferences CompanyInformation + */ + public function getCompanyPreferences() + { + $currentServiceContext = $this->getServiceContext(); + if (!isset($currentServiceContext) || empty($currentServiceContext->realmId)) { + throw new SdkException("Please Setup Service Context before making get Company Preferences call."); + } + //The Preferences URL + $uri = implode(CoreConstants::SLASH_CHAR, array('company', $currentServiceContext->realmId, 'preferences')); + $requestParameters = new RequestParameters($uri, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); + $restRequestHandler = $this->getRestHandler(); + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, null, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + //$faultHandler now is true or false + if ($faultHandler) { + $this->lastError = $faultHandler; + return null; + } else { + $this->lastError = false; + $parsedResponseBody = $this->getResponseSerializer()->Deserialize($responseBody, true); + return $parsedResponseBody; + } + } + + /** + * Get the Entitlement Response + * @return \SimpleXMLElement Xml + */ + public function getEntitlementsResponse() + { + $currentServiceContext = $this->getServiceContext(); + if (!isset($currentServiceContext) || empty($currentServiceContext->realmId)) { + throw new SdkException("Please Setup Service Context before making get entitlements response call."); + } + //The Preferences URL + $uri = $this->getServiceContext()->IppConfiguration->BaseUrl->Qbo; + $requestParameters = new RequestParameters($uri, 'GET', CoreConstants::CONTENTTYPE_APPLICATIONXML, null); + $restRequestHandler = $this->getRestHandler(); + $entitlementsUri = $this->getServiceContext()->IppConfiguration->BaseUrl->Qbo . "manage/entitlements/v3/" . $this->serviceContext->realmId; + list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, null, $entitlementsUri, $this->isThrownExceptionOnError()); + $faultHandler = $restRequestHandler->getFaultHandler(); + //$faultHandler now is true or false + if ($faultHandler) { + $this->lastError = $faultHandler; + return null; + } else { + $this->lastError = false; + return simplexml_load_string($responseBody); + } + } + + /** + * Get the actual ID string value of either an IPPid object, or an Id string + * @param Object $id + * @return String Id + */ + private function getIDString($id){ + if($id instanceof IPPid){ + return (String)$id->value; + }else{ + return (String)$id; + } + } +}