<?php
namespace QuickBooksOnline\API\Core;

/**
 * Constants whose values do not change.
 */
class CoreConstants
{
    //Set the default minor version to 23
    const DEFAULT_SDK_MINOR_VERSION = "36";
    const DEFAULT_LOGGINGLOCATION = "/tmp/IdsLogs";

    const PHP_CLASS_PREFIX = 'IPP';
    /*
     * All content writer strategy we support. Append this lists with new strategies
     */
    const CONTENTWRITER_STRATEGIES = array('file','handler','export');

    /*
     * Specific file strategy that we used for Content Writter
     */
    const FILE_STRATEGY    = "file";

    /*
     * Specific file strategy that we used for Content Writter
     */
    const HANDLER_STRATEGY = "handler";

    /*
     * Specific file strategy that we used for Content Writter
     */
    const EXPORT_STRATEGY  = "export";

    /**
     * The http client name for curl
     */
    const CLIENT_CURL = 'curl';

    /**
     * The standard name for guzzle.
     */
    const CLIENT_GUZZLE = 'guzzle';

    /**
     * The full name for guzzle
     */
    const CLIENT_GUZZLE_FULL = 'guzzlehttp';

    /**
     * No compression.
     * @var int None
     */
    const DataCompressionFormat_None = 0;

    /**
     * GZip compression.
     * @var int GZip
     */
    const DataCompressionFormat_GZip = 1;

    /**
     * Deflate compression.
     * @var int Deflate
     */
    const DataCompressionFormat_Deflate = 2;

    /**
     * Current QuickBooks Namespace for PHP SDK
     * @var int Deflate
     */
    const NAMEPSACE_DATA_PREFIX = 'QuickBooksOnline\\API\\Data\\';

    /**
     * OAuth 1 Mode constant.
     * @var int Deflate
     */
    const OAUTH1 = "oauth1";

    /**
     * OAuth 2 Mode constant.
     * @var int Deflate
     */
    const OAUTH2 = "oauth2";

    /**
    * HTTP GET METHOD
    * @var
    */
    const HTTP_GET = "GET";

    /**
    * HTTP POST METHOD
    * @var
    */
    const HTTP_POST = "POST";

    /**
     * QuickBooks Online Data through IDS.
     * @var string QBO
     */
    const IntuitServicesTypeQBO = "QBO";

    /**
     * Intuit Platform services.
     * @var string IPP
     */
    const IntuitServicesTypeIPP = "IPP";

    /**
     * None service type.
     * @var string None
     */
    const IntuitServicesTypeNone = "None";

    /**
     * Slash character.
     * @var string SLASH_CHAR
     */
    const SLASH_CHAR = "/";

    /**
     * The Intuit Services Version.
     * @var string VERSION
     */
    const VERSION = "v3";

    /**
     * The Resource.
     * @var string RESOURCE
     */
    const RESOURCE = "resource";

    /**
     * The Content Type String.
     * @var string CONTENT_TYPE
     */
    const CONTENT_TYPE = "Content-Type";

    /**
     * Content type: text/xml.
     * @var string CONTENTTYPE_TEXTXML
     */
    const CONTENTTYPE_TEXTXML = "text/xml";

    /**
     * Content type: text/plain.
     * @var string CONTENTTYPE_TEXTPLAIN
     */
    const CONTENTTYPE_TEXTPLAIN = "text/plain";

    /**
     * Content type: application/text.
     * @var string CONTENTTYPE_APPLICATIONTEXT
     */
    const CONTENTTYPE_APPLICATIONTEXT = "application/text";

    /**
     * Content type: application/xml.
     * @var string CONTENTTYPE_APPLICATIONXML
     */
    const CONTENTTYPE_APPLICATIONXML = "application/xml";

    /**
     * Content type: application/xml;charset=UTF-8.
     * @var string CONTENTTYPE_APPLICATIONXML_WITH_CHARSET
     */
    const CONTENTTYPE_APPLICATIONXML_WITH_CHARSET = "application/xml;charset=UTF-8";

    /**
     * Content type: application/xml.
     * @var string CONTENTTYPE_APPLICATIONJSON
     */
    const CONTENTTYPE_APPLICATIONJSON = "application/json";

    /**
     * Content type: application/x-www-form-urlencoded.
     * @var string CONTENTTYPE_URLFORMENCODED
     */
    const CONTENTTYPE_URLFORMENCODED = "application/x-www-form-urlencoded";

            /**
     * Content type: application/x-www-form-urlencoded.
     * @var string CONTENTTYPE_URLFORMENCODED
     */
    const CONTENTTYPE_OCTETSTREAM = "application/octet-stream";

        /**
     * Content type: application/pdf.
     * @var string CONTENTTYPE_APPLICATIONPDF
         */
        const CONTENTTYPE_APPLICATIONPDF = "application/pdf";

    /**
     * The Base Url for QBO.
     * @var string QBO_BASEURL
     */
    const QBO_BASEURL = "https://quickbooks.api.intuit.com/";
    const PRODUCTION_QBO = "Production";

    const IPP_BASEURL = "https://appcenter.intuit.com/api/";

    const DEVELOPMENT_SANDBOX = "Development";
    const SANDBOX_DEVELOPMENT = "https://sandbox-quickbooks.api.intuit.com";

    /**
     * Id Parameter Name.
     * @var string Id
     */
    const Id = "Id";

    /**
     * Intuit tid
     * @var string
     */
    const INTUIT_TID = "intuit_tid";

    /**
     * Sync Token Parameter Name.
     * @var string SYNC_TOKEN
     */
    const SYNC_TOKEN = "SyncToken";

    /**
     * Domain Parameter Name.
     * @var string DOMAIN
     */
    const DOMAIN = "domain";

    /**
     * MetaData Parameter Name.
     * @var string METADATA
     */
    const METADATA = "MetaData";

    /**
     * Sparse Parameter Name.
     * @var string SPARSE
     */
    const SPARSE = "sparse";

    /**
     * Status Parameter Name.
     * @var string STATUS
     */
    const STATUS = "status";

    /**
     * Id Domain Query Parameter.
     * @var string ID_DOMAIN_QUERY
     */
    const ID_DOMAIN_QUERY = "?idDomain=";

    /**
     * Authorization String For Header.
     * @var string AUTHORIZATIONSTRING_FORHEADER
     */
    const AUTHORIZATIONSTRING_FORHEADER = "Authorization";

    /**
     * Request File Name Format.
     * @var string REQUESTFILENAME_FORMAT
     */
    const REQUESTFILENAME_FORMAT = "{0}{1}Request-{2}.txt";

    /**
     * Response File Name Format.
     * @var string RESPONSEFILENAME_FORMAT
     */
    const RESPONSEFILENAME_FORMAT = "{0}{1}Response-{2}.txt";

    /**
     * Error Response File Name Format.
     * @var string ERRORRESPONSEFILENAME_FORMAT
     */
    const ERRORRESPONSEFILENAME_FORMAT = "{0}{1}Error-Response-{2}.txt";

    /**
     * The Compression format of the request data.
     * @var string CONTENTENCODING
     */
    const CONTENTENCODING = "Content-Encoding";

    /**
     * The Compression format of the response data.
     * @var string ACCEPTENCODING
     */
    const ACCEPTENCODING = "Accept-Encoding";

    /**
     * The Request source header value.
     * @var string REQUESTSOURCEHEADER
     */
    const USERAGENT = "V3PHPSDK6.0.0";

    public static function getType($string, $return=1)
    {
        $parts = explode("/", $string);
        return array_key_exists($return, $parts) ? $parts[$return] : null;
    }

    /**
        * Returns current rules for what operation is supported and what is not for QuickBooks Online API entity
        * @return array
     */
    public static function getQuickBooksOnlineAPIEntityRules()
    {
        return
                         array(
                                    '*'               => array(
                                                                "DownloadPDF" => false,
                                                                "jsonOnly" => false,
                                                                "SendEmail"=> false),
                                    "IPPTaxService"   => array( '*' => false,
                                                                'Add' => true,
                                                                'jsonOnly' => true),
                                    "IPPSalesReceipt" => array( "DownloadPDF" => true, "SendEmail" => true ),
                                    "IPPInvoice"      => array( "DownloadPDF" => true, "SendEmail" => true  ),
                                    "IPPEstimate"     => array( "DownloadPDF" => true, "SendEmail" => true  ),
                            );
    }

    /**
     * A helper method to get Access Token Key. People may have a typo on the key name
     * @param Array $settings     The array contains all the key values
     * @return String | null      The access token developer provided
     */
    public static function getAccessTokenFromArray(array $settings){
        if(array_key_exists('accessTokenKey', $settings)){
            return $settings['accessTokenKey'];
        }else if(array_key_exists('accessToken', $settings)){
            return $settings['accessToken'];
        } else if(array_key_exists('AccessToken', $settings)){
            return $settings['AccessToken'];
        } else{
            return null;
        }
    }

    /**
     * A helper method to get Refresh Token Key. People may have a typo on the key name
     * @param Array $settings     The array contains all the key values
     * @return String | null      The refresh token developer provided
     */
    public static function getRefreshTokenFromArray(array $settings){
        if(array_key_exists('refreshTokenKey', $settings)){
            return $settings['refreshTokenKey'];
        }else if(array_key_exists('refreshToken', $settings)){
            return $settings['refreshToken'];
        } else if(array_key_exists('RefreshToken', $settings)){
            return $settings['RefreshToken'];
        } else{
            return null;
        }
    }

    /**
     * A helper method to get redirect URL. People may have a typo on the key name
     * @param Array $settings     The array contains all the key values
     * @return String | null      The redirect url developer provide
     */
    public static function getRedirectURL(array $settings){
        if(array_key_exists('redirectURL', $settings)){
            return $settings['redirectURL'];
        }else if(array_key_exists('RedirectUrl', $settings)){
            return $settings['RedirectUrl'];
        } else if(array_key_exists('redirecturl', $settings)){
            return $settings['redirecturl'];
        } else if(array_key_exists('redirectUrl', $settings)){
            return $settings['redirectUrl'];
        } else if(array_key_exists('RedirectURL', $settings)){
            return $settings['RedirectURL'];
        } else if(array_key_exists('redirectURI', $settings)){
            return $settings['redirectURI'];
        }else if(array_key_exists('RedirectUri', $settings)){
            return $settings['RedirectUri'];
        } else if(array_key_exists('redirecturi', $settings)){
            return $settings['redirecturl'];
        } else if(array_key_exists('redirectUri', $settings)){
            return $settings['redirectUrl'];
        } else if(array_key_exists('RedirectURI', $settings)){
            return $settings['RedirectURI'];
        }
        else{
            return null;
        }
    }

    //--------------------------------------------------------------------------------------------------
    //OAuth 2
    const OAUTH2_TOKEN_ENDPOINT_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
    const OAUTH2_AUTHORIZATION_REQUEST_URL = "https://appcenter.intuit.com/connect/oauth2";
    const REVOCATION_ENDPONT = "https://developer.api.intuit.com/v2/oauth2/tokens/revoke";
    const OAUTH2_REFRESH_GRANTYPE = "refresh_token";
    const OAUTH2_AUTHORIZATION_TYPE = "Basic ";
    const EXPIRES_IN = "expires_in";
    const X_REFRESH_TOKEN_EXPIRES_IN = "x_refresh_token_expires_in";
    const ACCESS_TOKEN = "access_token";

    public static function getCertPath(){
        return dirname(__FILE__) . "/OAuth/OAuth2/certs/cacert.pem"; //Pem certification Key Path
    }

    //AutoLoader Settings
    const USE_AUTOLOADER = true;
}


<?php

namespace QuickBooksOnline\API\Diagnostics;

use QuickBooksOnline\API\Exception\SdkException;

/**
 * Writes temporary file
 *
 * @author amatiushkin
 */
class ContentWriter
{

    /**
     * Contains content to write
     * @var mixed (string or binary)
     */
    private $content = null;

    /**
     * Prefix which will be added at the beginning of a filename.
     * @see setPrefix() for details
     * @var string
     */
    private $prefix = null;

    /**
     * Number of bytes in content
     * @var int
     */
    private $bytes = null;

    /**
     * Full path including filename for created temporary file
     * @var string
     */
    private $tempPath = null;

    /**
     * Contains reference to resource (if applicable)
     * @var resource
     */
    private $handler = null;

    public function __construct($content = null)
    {
        $this->content = $content;
    }

    /**
     * Set prefix for a temporary filename.
     * It is used only by saveTemp()
     * @param string $prefix
     *
     * @return $this
     */
    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;
        return $this;
    }

    /**
     * Returns prefix
     * @return string
     */
    public function getPrefix()
    {
        return $this->prefix;
    }

    /**
     * Returns content
     * @return mixed (string or binary)
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * Deletes content, however everything else remains available
     * This method is intented to use for better memory management
     */
    public function resetContent()
    {
        $this->content = null;
    }

    /**
     * Returns unique filename with prefix (if applicable)
     * note: This method is not intented to generate unpredictable strings,
     * even with very low likelihood it may happen with garbaged tmp folder.
     * Suggestion is to clean up php temp folder (which is usually sys temp folder).
     * @return string
     */
    protected function getUniqId()
    {
        return uniqid($this->getPrefix(), true);
    }

    /**
     * Creates temporary file in system temporary folder.
     * It returns path to the file
     * @return string path
     */
    private function createTempFile()
    {
        return tempnam(sys_get_temp_dir(), $this->getUniqId());
    }

    /**
     * Returns path to temporary file
     * @return string
     */
    public function getTempPath()
    {
        return $this->tempPath;
    }

    /**
     * Returns number of bytes, which were written
     * @return int
     */
    public function getBytes()
    {
        return $this->bytes;
    }

    /**
     * Returns true if this writer instance refers to file by stream reference
     * @return bool
     */
    public function isHandler()
    {
        return !is_null($this->getHandler())
               && is_resource($this->getHandler());
    }

    /**
     * Returns file handler to temporary file
     * It is the same kind as ones, which are returned by @see fopen()
     * @return resource
     */
    public function getHandler()
    {
        return $this->handler;
    }

    /**
     * Writes content into temporary file. It always creates new temp file.
     * Use getTempPath() to retrieve actual path.
     * This method doesn't delete the file after script termination.
     * @return boolean
     * @throws SdkException
     */
    public function saveTemp()
    {
        $this->tempPath = $this->createTempFile();
        if (is_writable($this->getTempPath())) {
            $result = file_put_contents($this->getTempPath(), $this->getContent());
            if (false === $result) {
                throw new SdkException('Unable to put content into temp file: ' . $this->getTempPath());
            }
            if (empty($result)) {
                throw new SdkException('Empty or zero file was received: ' . $this->getTempPath());
            }
            $this->bytes = $result;
            return true;
        }

        throw new SdkException('Unable to write temp file: ' . $this->tempPath);
    }

    /**
     * Writes content into temporary file and returns open handler to it.
     * The temporary file is removed when closed (e.g. by calling fclose()) or
     * there are no more references (e.g. variables) or with script termination.
     * @throws SdkException
     */
    public function saveAsHandler()
    {
        $this->handler = tmpfile();
        if (false === $this->handler) {
            throw new SdkException('Unable to create a handler for tempfile in ' . sys_get_temp_dir());
        }
        if (empty($this->handler)) {
            throw new SdkException('Handler has an invalid state. It is empty.');
        }
        $this->tempPath = $this->getPathFromHandler();
        if (empty($this->tempPath)) {
            throw new SdkException('Unable to locate path from stream source');
        }
        $result = fwrite($this->handler, $this->getContent());
        if (false === $result) {
            throw new SdkException('Unable to write content into temp file: ' . $this->getTempPath());
        }
        if (empty($result)) {
            throw new SdkException('Empty or zero file was received: ' . $this->getTempPath());
        }
        if (-1 === fseek($this->handler, 0)) {
            throw new SdkException('Unable to reset file pointer in the file ' . $this->getTempPath());
        }
        $this->bytes = $result;
    }

    /**
     * Moves file from temp location to specified folder and name
     * @param string $dir
     * @param string|null $name (option) if not specified it will be moved with temp name
     * @throws SdkException
     */
    public function saveFile($dir, $name=null)
    {
        if (empty($dir)) {
            throw new SdkException("Directory is empty.");
        }

        if (!file_exists($dir)) {
            throw new SdkException("Directory ($dir) doesn't exist.");
        }

        if (!is_writable($dir)) {
            throw new SdkException("Directory ($dir) is not writable");
        }
        $this->tempPath = $dir . DIRECTORY_SEPARATOR . $this->generateFileName($name);
        if (file_exists($this->tempPath)) {
            throw new SdkException("File ($this->tempPath) already exists");
        }
        try {
            $result = file_put_contents($this->tempPath, $this->getContent());
        } catch (\Exception $e) {
            if (!is_writable($this->tempPath)) {
                throw new SdkException("File ({$this->tempPath}) is not writable");
            }
            throw new SdkException("Error was thrown: " . $e->getMessage() . "\File: " .$e->getFile() ." on line " . $e->getLine());
        }
        if (false === $result) {
            throw new SdkException('Unable to write content into temp file: ' . $this->tempPath);
        }
        $this->bytes = $result;
    }

    /**
     * Creates filename based on name and prefix.
     * It generates uniqid-like string if name is ommited
     * @param string $name
     * @return string
     */
    private function generateFileName($name)
    {
        $filename = is_null($name) ? $this->getUniqId() : $name;
        return is_null($this->getPrefix()) ? $filename : $this->getPrefix() . $filename;
    }

    /**
     * Returns actual path from actual file handler
     * @return string
     */
    private function getPathFromHandler()
    {
        $metaDatas = stream_get_meta_data($this->handler);
        if (array_key_exists('uri', $metaDatas)) {
            return $metaDatas['uri'];
        }
        return null;
    }
}


<?php
namespace QuickBooksOnline\API\Facades;

class Estimate{

    public static function create(array $data, $throwException = TRUE){
        if(!isset($data) || empty($data)) throw new \Exception("Passed array for creating Estimate is Empty");
        $EstimateObject = FacadeHelper::reflectArrayToObject("Estimate", $data, $throwException );
        return $EstimateObject;
    }

    /**
     * This is an immutable function
     */
    public static function update($objToUpdate, array $data){
        $classOfObj = get_class($objToUpdate);
        if(strcmp($classOfObj, FacadeHelper::simpleAppendClassNameSpace("Estimate")) != 0){
            throw new \Exception("Target object class:{" .  $classOfObj . "} is not an instance of Estimate.");
        }
        $newEstimateObj = Estimate::create($data);
        $clonedOfObj = FacadeHelper::cloneObj($objToUpdate);
        FacadeHelper::mergeObj($clonedOfObj, $newEstimateObj);
        return $clonedOfObj;
    }

}


<?php
namespace QuickBooksOnline\API\Core\HttpClients;

use QuickBooksOnline\API\Core\ServiceContext;

/**
 * Class FaultHandler
 *
 * Record the 3xx, 4xx and 5xx response status code in the response and provide helper
 * message for developers to handle them.
 *
 * @package QuickBooksOnline
 */
class FaultHandler
{
    /**
     * The Service Context. Previous from PECL OAuth 1.2.3 extension requirement. Not used in PHP SDK > v3.4.0
     * @var ServiceContext context
     * @deprecated
     */
    private $context;

    /**
     * The status code get from the HTTP response
     * @var Integer
     */
    private $httpStatusCode;

    /**
     * The response body from the HTTP response
     * @var String
     */
    private $responseBody;

    /**
     * Previous used by PECL OAuth 1.2.3 extension for helper message.
     * Now is used to store QuickBooks Online specific help message.
     * @var String
     */
    private $helpMsg;

    /**
     * Used for Intuti to debug the cause for non-200 http status code.
     * If a developer is getting non-200 code and he is not sure what happened,
     * provide this id to the support ticket.
     * @var String
     */
     private $intuit_tid;

    //-------------------------Intuit Error helper information-------------------------
    /**
     * The Intuit Error Type specified in the HTTP Response Body
     * @var String
     */
    private $intuitErrorType = "";

    /**
     * The Intuit Error Code specified in the HTTP Response Body
     * @var Int
     */
    private $intuitErrorCode = 0;

    /**
     * The Intuit Error Element specified in the HTTP Response Body
     * @var String
     */
    private $intuitErrorElement = "";

    /**
     * The Intuit Error Message specified in the HTTP Response Body
     * @var String
     */
    private $intuitErrorMessage = "";

    /**
     * The Intuit Error Detail specified in the HTTP Response Body
     * @var String
     */
    private $intuitErrorDetail = "";

    /**
     * Initializes a new instance of the FaultHandler class. Previous Used by PECL OAuth 1.2.3 extension. Not used in PHP SDK > v3.4.0
     * @param ServiceContext context
     * @param OAuthException Coming From OAuth 1.2.3 extension
     * @deprecated
     */
    public function __construct($context = null, $OAuthException = null)
    {
        if($context == null && $OAuthException == null) return;
        $this->context = $context;
        $this->generateErrorFromOAuthMsg($OAuthException);
    }

    /**
     * generate Error from OAuth Exception
     * @param OAuthException  The OAuth Exception from PECL OAuth 1.2.3
     * @deprecated
     */
    private function generateErrorFromOAuthMsg($OAuthException)
    {
        if (get_class($OAuthException) == 'OAuthException') {
            $this->httpStatusCode = $OAuthException->getCode();
            $this->helpMsg = $OAuthException->getMessage();
            $this->responseBody = $OAuthException->lastResponse;
        } else {
            throw new \Exception("OAuthException required for generate error from Intuit. The passed parameters for Fault handler is not OAuthException");
        }
    }
    /**
     * Based on the HTTP Response body, set the Intuit Error elements.
     * @var String The http response body in XML format
     */
    public function parseResponse($message){
      $xmlObj = simplexml_load_string($message);

      if(!$this->isTheErrorBodyInStandardFormat($xmlObj)){
          return;
      }
      $type = (string)$xmlObj->Fault->attributes()['type'];
      if(isset($type) && !empty($type)){
        $this->intuitErrorType = $type;
      }

      $code = (string)$xmlObj->Fault->Error->attributes()['code'];
      if(isset($code) && !empty($code)){
        $this->intuitErrorCode = $code;
      }

      $element = (string)$xmlObj->Fault->Error->attributes()['element'];
      if(isset($element) && !empty($element)){
        $this->intuitErrorElement = $element;
      }

      $message = (string)$xmlObj->Fault->Error->Message;
      if(isset($message) && !empty($message)){
        $this->intuitErrorMessage = $message;
      }

      $detail = (string)$xmlObj->Fault->Error->Detail;
      if(isset($detail) && !empty($detail)){
        $this->intuitErrorDetail = $detail;
      }

    }

    /**
     * Check if the format is standard Intuit Response format. This is to prevent Intuit Return random Structured Error.
     * @param SimpleXMLElement the XML object of the Body
     * @return True | False
     */
    private function isTheErrorBodyInStandardFormat($xmlObj)
    {
        if(!isset($xmlObj->Fault) || !isset($xmlObj->Fault->Error)){
           return false;
        }
        return true;
    }

    /**
     * Set the Http Status code for the fault handler
     * @param Int   Response HTTP Status Code
     */
    public function setHttpStatusCode($statusCode)
    {
         $this->httpStatusCode = $statusCode;
    }

    /**
     * Set the Http helper message. Developer should get some hint based on the helper message
     * TO DO: It should use Intuit Response information($intuitErrorType, $intuitErrorElement, etc) to generate the message. Right now it is hard coded string.
     * @param String  help message
     */
    public function setHelpMsg($msg)
    {
         $this->helpMsg = $msg;
    }

    /**
     * Set the Intuit_tid for the response
     * @param String  help message
     */
    public function setIntuitTid($tid)
    {
         $this->intuit_tid = $tid;
    }

    /**
     * Set the body for the fault handler
     * @param String   Response body
     */
    public function setResponseBody($body)
    {
         $this->responseBody = $body;
    }

    /**
     * Return the status code of the Response
     *
     * @return Integer   The response status code
     */
    public function getHttpStatusCode()
    {
        return $this->httpStatusCode;
    }

    /**
     * Return the helper message
     *
     * @return String   The helper method to find why the return code is not 200
     */
    public function getOAuthHelperError()
    {
        return $this->helpMsg;
    }

    /**
     * Return the body of the Response
     *
     * @return String   The response body
     */
    public function getResponseBody()
    {
        return $this->responseBody;
    }

    /**
     * Return the Intuit_tid of the response
     *
     * @return String   The intuit tid
     */
    public function getIntuitTid()
    {
        return $this->intuit_tid;
    }

    /**
     * Return the Intuit Error Type of the Response
     *
     * @return String   The Intuit Error Type
     */
    public function getIntuitErrorType(){
        return $this->intuitErrorType;
    }

    /**
     * Return the Intuit Error Code of the Response
     *
     * @return Int   The Intuit Error Code
     */
    public function getIntuitErrorCode(){
        return $this->intuitErrorCode;
    }

    /**
     * Return the Intuit Error element of the Response
     *
     * @return String  The Intuit Error element
     */
    public function getIntuitErrorElement(){
        return $this->intuitErrorElement;
    }

    /**
     * Return the Intuit Error Message of the Response
     *
     * @return String   The Intuit Error Message
     */
    public function getIntuitErrorMessage(){
        return $this->intuitErrorMessage;
    }

    /**
     * Return the Intuit Error Detail of the Response
     *
     * @return String   The Intuit Error Detail
     */
    public function getIntuitErrorDetail(){
        return $this->intuitErrorDetail;
    }
}


<?php
namespace QuickBooksOnline\API\Facades;

class Deposit{

    public static function create(array $data, $throwException = TRUE){
        if(!isset($data) || empty($data)) throw new \Exception("Passed array for creating Deposit is Empty");
        $DepositObject = FacadeHelper::reflectArrayToObject("Deposit", $data, $throwException );
        return $DepositObject;
    }

    /**
     * This is an immutable function
     */
    public static function update($objToUpdate, array $data){
        $classOfObj = get_class($objToUpdate);
        if(strcmp($classOfObj, FacadeHelper::simpleAppendClassNameSpace("Deposit")) != 0){
            throw new \Exception("Target object class:{" .  $classOfObj . "} is not an instance of Deposit.");
        }
        $newDepositObj = Deposit::create($data);
        $clonedOfObj = FacadeHelper::cloneObj($objToUpdate);
        FacadeHelper::mergeObj($clonedOfObj, $newDepositObj);
        return $clonedOfObj;
    }

}


<?php
namespace QuickBooksOnline\API\Facades;

class Customer{

    public static function create(array $data, $throwException = TRUE){
        if(!isset($data) || empty($data)) throw new \Exception("Passed array for creating Customer is Empty");
        $CustomerObject = FacadeHelper::reflectArrayToObject("Customer", $data, $throwException );
        return $CustomerObject;
    }

    /**
     * This is an immutable function
     */
    public static function update($objToUpdate, array $data){
        $classOfObj = get_class($objToUpdate);
        if(strcmp($classOfObj, FacadeHelper::simpleAppendClassNameSpace("Customer")) != 0){
            throw new \Exception("Target object class:{" .  $classOfObj . "} is not an instance of Customer.");
        }
        $newCustomerObj = Customer::create($data);
        $clonedOfObj = FacadeHelper::cloneObj($objToUpdate);
        FacadeHelper::mergeObj($clonedOfObj, $newCustomerObj);
        return $clonedOfObj;
    }

}


<?php

namespace QuickBooksOnline\API\Core\HttpClients;

use QuickBooksOnline\API\Exception\SdkException;
use QuickBooksOnline\API\Core\CoreConstants;

/**
 * Class IntuitResponse
 *
 * An Object to store all response related information
 * @package QuickBooksOnline
 *
 */
class IntuitResponse{

   /**
    * The header of Intuit Response.
    * @var array
    */
   private $headers;

   /**
    * The body of Intuit Response.
    * @var string
    */
   private $body;

   /**
    * The http status code of Intuit Response.
    * @var int
    */
   private $httpResponseCode;

   /**
    * The associated error message for this response, if the status code is not 200.
    * @var FaultHandler
    */
   private $faultHandler = false;

   /**
    * The content type of response. It can be JSON or XML
    * @var String
    */
   private $contentType;

   /**
    * Used for Intuti to debug the cause for non-200 http status code.
    * If a developer is getting non-200 code and he is not sure what happened,
    * provide this id to the support ticket.
    * @var String
    */
   private $intuit_tid;

   /**
    * Constructor. Set the Intuit Response based on the curl Executation result
    * @param String | Array   http client returned eaders
    * @param String           http client returned body
    * @param Int              http client returned status code
    * @param Boolean          Should the headers,body and response code be handlered. Default to false
    * @param String           Client Name that return the response
    */
   public function __construct($passedHeaders, $passedBody, $passedHttpResponseCode, $parse = false, $clientName = CoreConstants::CLIENT_CURL){
          if($parse == false){
              foreach ($passedHeaders as &$passedHeader ){
                  if( is_array( $passedHeader ) ){
                      $passedHeader = implode( "; ", $passedHeader );
                  }
              }
              $this->setResponseAsItIs($passedHeaders, $passedBody, $passedHttpResponseCode);
          }else{
              $this->parseResponseToIntuitResponse($passedHeaders, $passedBody, $passedHttpResponseCode, $clientName);
          }
   }

   /**
    * Do not parse the response. Just set it as it is.
    * @param String | Array   http client returned eaders
    * @param String           http client returned body
    * @param Int              http client returned status code
    * @throws  SdkExcpetion   If any of the passed parameter is undefined.
    */
   private function setResponseAsItIs($passedHeaders, $passedBody, $passedHttpResponseCode){
      if(isset($passedHeaders) && isset($passedBody) && isset($passedHttpResponseCode)){
          $this->headers = $passedHeaders;
          $this->body = $passedBody;
          $this->httpResponseCode = $passedHttpResponseCode;
          $this->setContentType(CoreConstants::CONTENT_TYPE, $passedHeaders[CoreConstants::CONTENT_TYPE]);
          $this->setIntuitTid(CoreConstants::INTUIT_TID, $passedHeaders[CoreConstants::INTUIT_TID]);
          $this->setFaultHandler($passedBody, $passedHttpResponseCode, $this->getIntuitTid());
      }else{
          throw new SdkException("Passed Headers, body, or status code is Null.");
      }
   }

   /**
    * Parse the response from different http client response and set the Intuit response.
    * @param String | Array   http client returned eaders
    * @param String           http client returned body
    * @param Int              http client returned status code
    * @param String           http client name
    */
   private function parseResponseToIntuitResponse($passedHeaders, $passedBody, $passedHttpResponseCode, $clientName){
     if($clientName == CoreConstants::CLIENT_CURL){
        if(isset($passedHeaders)){
            $this->setHeaders($passedHeaders);
        }else{
            throw new SdkException("The response header from cURL is null.");
        }

        if(isset($passedBody)){
            $this->body = $passedBody;
        }else{
            throw new SdkException("The Http Response Body from cURL is null.");
        }

        if(isset($passedHttpResponseCode)){
            $this->httpResponseCode = (int)$passedHttpResponseCode;
            $this->setFaultHandler($this->getBody(), $this->getStatusCode(), $this->getIntuitTid());
        }else{
            throw new SdkException("Passed Http status code from cURL is null.");
        }
     }else{
        throw new SdkException("This should not be thrown. IntuitResponse currently don't support parse other client response to Intuit Response other than curl.");
     }

   }

   /**
    * This is used to set the detailed error coming from QuickBooks Online detail
    * @param String              Response Body
    * @param Integer             Response Status code
    * @param String              Intuit tid on the response header
    */
   private function setFaultHandler($body, $httpResponseCode, $tid){
     //If the status code is non-200
     if($httpResponseCode < 200 || $httpResponseCode >= 300){
        $this->faultHandler = new FaultHandler();
        $this->faultHandler->setHttpStatusCode($httpResponseCode);
        $this->faultHandler->setResponseBody($body);
        $this->faultHandler->setIntuitTid($tid);
        //A standard message for now.
        //TO DO: Wait V3 Team to provide different message for different response.
        $this->faultHandler->setHelpMsg("Invalid auth/bad request (got a " . $httpResponseCode . ", expected HTTP/1.1 20X or a redirect)");
        if($this->getResponseContentType() != null && strcasecmp($this->getResponseContentType(), CoreConstants::CONTENTTYPE_APPLICATIONXML) == 0){
            $this->faultHandler->parseResponse($body);
        }
     }else{
        $this->faultHandler = false;
     }
   }

   /**
    * Parse the raw Http Response Header to associated array to be consumered.
    * It will also store the Content-Type of the response.
    * @param string | Array rawHeaders
    */
   public function setHeaders($rawHeaders){

        $rawHeaders = str_replace("\r\n", "\n", $rawHeaders);
        $response_headers_rows = explode("\n", trim($rawHeaders));
        foreach ($response_headers_rows as $line) {
            if(strpos($line, ': ') == false){
                continue;
            }else {
                list($key, $value) = explode(': ', $line);
                $this->headers[$key] = $value;
                //set response content type
                $this->setContentType($key, $value);
                $this->setIntuitTid($key, $value);
            }
        }

   }

   /**
    * Find the headers for response, find the Content-type header and intuit_tid header and set it.
    * @param String   Header key
    * @param String   Header value
    */
   private function setContentType($key, $val){
     $trimedKey = trim($key);
     if(strcasecmp($trimedKey, CoreConstants::CONTENT_TYPE) == 0){
         $this->contentType = trim($val);
     }
   }

   /**
    * Find the headers for response, find the intuit_tid header and set it.
    * @param String   Header key
    * @param String   Header value
    */
   private function setIntuitTid($key, $val){
     $trimedKey = trim($key);
     if(strcasecmp($trimedKey, CoreConstants::INTUIT_TID) == 0){
         $this->intuit_tid = trim($val);
     }
   }

   /**
    * Return the header of the Response
    *
    * @return array Response Headers
    */
   public function getHeaders(){
       return $this->headers;
   }

   /**
    * Return the body of the Response
    *
    * @return String     Response Body
    */
   public function getBody(){
       return $this->body;
   }

   /**
    * Return the status code of the Response
    *
    * @return Integer   The response status code
    */
   public function getStatusCode(){
       return $this->httpResponseCode;
   }

   /**
    * Return the fault handler object to debug the request error.
    * @return Faulthandler The faultHandler that has helper method to debug the error.
    */
   public function getFaultHandler(){
       return $this->faultHandler;
   }

   /**
    * Return the content type of the Response
    *
    * @return string JSON or XML
    */
   public function getResponseContentType(){
       return $this->contentType;
   }

   /**
    * Return the Intuit Tid of the response
    *
    * @return string Intuit tid
    */
   public function getIntuitTid(){
       return $this->intuit_tid;
   }

}


<?php
namespace QuickBooksOnline\API\Facades;

class Department{

    public static function create(array $data, $throwException = TRUE){
        if(!isset($data) || empty($data)) throw new \Exception("Passed array for creating Department is Empty");
        $DepartmentObject = FacadeHelper::reflectArrayToObject("Department", $data, $throwException );
        return $DepartmentObject;
    }

    /**
     * This is an immutable function
     */
    public static function update($objToUpdate, array $data){
        $classOfObj = get_class($objToUpdate);
        if(strcmp($classOfObj, FacadeHelper::simpleAppendClassNameSpace("Department")) != 0){
            throw new \Exception("Target object class:{" .  $classOfObj . "} is not an instance of Department.");
        }
        $newDepartmentObj = Department::create($data);
        $clonedOfObj = FacadeHelper::cloneObj($objToUpdate);
        FacadeHelper::mergeObj($clonedOfObj, $newDepartmentObj);
        return $clonedOfObj;
    }

}


<?php

namespace QuickBooksOnline\API\PlatformService;

use QuickBooksOnline\API\Core\CoreConstants;
use QuickBooksOnline\API\Core\ServiceContext;
use QuickBooksOnline\API\Exception\SdkExceptions\InvalidParameterException;
use QuickBooksOnline\API\Core\HttpClients\RequestParameters;
use QuickBooksOnline\API\Core\HttpClients\SyncRestHandler;
use QuickBooksOnline\API\Core\Http\Serialization\SerializationFormat;
use QuickBooksOnline\API\Core\Http\Compression\CompressionFormat;
use QuickBooksOnline\API\Core\HttpClients\RestHandler;

/**
 * This file contains Intuit Platform services.
 */
class PlatformService
{

    /**
     * The Service context object.
     * @var ServiceContext
     */
    public $serviceContext;

    /**
     * The Rest request handler object.
     * @var RestHandler
     */
    public $restRequestHandler;

    /**
     * Request Xml Document
     * @var string
     */
    private $requestXmlDocument;

    /**
     * Initializes a new instance of the PlatformService class.
     *
     * @param ServiceContext IPP Service Context
     * @throws ArgumentException:  The exception that is thrown when one of the argument(s) provided to a method  is not valid.
     * @throws InvalidTokenException:  When invalid tokens are passed.
     * @throws ArgumentNullException:  When Service context parameter sent as null.
     */
    public function __construct($serviceContext)
    {
        if (null == $serviceContext) {
            throw new \InvalidArgumentException('Resources.ServiceContextCannotBeNull');
        }

        if (!is_object($serviceContext)) {
            throw new InvalidParameterException('Wrong arg type passed - is not an object.');
        }

        if (!$serviceContext instanceof ServiceContext) {
            throw new InvalidParameterException('Wrong arg type passed - is not the correct class.');
        }

        $this->serviceContext = $serviceContext;
        $this->restRequestHandler = new SyncRestHandler($serviceContext);

        // Set the serviceContext IPP configuration to what IPP is accepting.
        $this->serviceContext->IppConfiguration->Message->Request->SerializationFormat = SerializationFormat::Xml;
        $this->serviceContext->IppConfiguration->Message->Request->CompressionFormat = CompressionFormat::None;
        $this->serviceContext->IppConfiguration->Message->Response->SerializationFormat = SerializationFormat::Xml;
        $this->serviceContext->IppConfiguration->Message->Response->CompressionFormat = CompressionFormat::None;

        // Set the service type to IPP by calling a method
        $this->serviceContext->UsePlatformServices();

        $this->serviceContext = $serviceContext;
        $this->restRequestHandler = new SyncRestHandler($serviceContext);
    }

    /**
     * Get App Menu
     *
     * @return string Html of App Menu
     */
    public function GetAppMenu()
    {
        $this->requestXmlDocument = '';
        $uriFragment = implode(CoreConstants::SLASH_CHAR, array('v1', 'Account', 'AppMenu'));
        $requestParameters = new RequestParameters(null, 'GET', null, $uriFragment);
        list($respCode, $respHtml) = $this->restRequestHandler->sendRequest($requestParameters, $this->requestXmlDocument, null);
        return $respHtml;
    }

    /**
     * Reconnect generates a new OAuth access token and invalidates the OAuth access token used
     * in the request, thereby extending the life span by six months.
     *
     * @return \SimpleXMLElement Xml of Reconnect response
     */
    public function Reconnect()
    {
        $this->requestXmlDocument = '';
        $uriFragment = implode(CoreConstants::SLASH_CHAR, array('v1', 'Connection', 'Reconnect'));
        $requestParameters = new RequestParameters(null, 'GET', null, $uriFragment);
        list($respCode, $respXml) = $this->restRequestHandler->sendRequest($requestParameters, $this->requestXmlDocument, null);
        return simplexml_load_string($respXml);
    }

    /**
     * Disconnect invalidates the OAuth access token in the request, thereby disconnecting
     * the user from QuickBooks for this app.
     *
     * @return \SimpleXMLElement Xml of Disconnect response
     */
    public function Disconnect()
    {
        $this->requestXmlDocument = '';
        $uriFragment = implode(CoreConstants::SLASH_CHAR, array('v1', 'Connection', 'Disconnect'));
        $requestParameters = new RequestParameters(null, 'GET', null, $uriFragment);
        list($respCode, $respXml) = $this->restRequestHandler->sendRequest($requestParameters, $this->requestXmlDocument, null);
        return simplexml_load_string($respXml);
    }

    /**
     * Returns user information such as first name, last name, and email address.
     *
     * @return \SimpleXMLElement Xml of Disconnect response
     */
    public function CurrentUser()
    {
        $this->requestXmlDocument = '';
        $uriFragment = implode(CoreConstants::SLASH_CHAR, array('v1', 'user', 'current'));
        $requestParameters = new RequestParameters(null, 'GET', null, $uriFragment);
        list($respCode, $respXml) = $this->restRequestHandler->sendRequest($requestParameters, $this->requestXmlDocument, null);
        return simplexml_load_string($respXml);
    }
}


<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Soap
 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Wsdl.php 20096 2010-01-06 02:05:09Z bkarwin $
 */

/**
 * @see Zend_Soap_Wsdl_Strategy_Interface
 */
require_once "Zend/Soap/Wsdl/Strategy/Interface.php";

/**
 * @see Zend_Soap_Wsdl_Strategy_Abstract
 */
require_once "Zend/Soap/Wsdl/Strategy/Abstract.php";

/**
 * Zend_Soap_Wsdl
 *
 * @category   Zend
 * @package    Zend_Soap
 */
class Zend_Soap_Wsdl
{
    /**
     * @var object DomDocument Instance
     */
    private $_dom;

    /**
     * @var object WSDL Root XML_Tree_Node
     */
    private $_wsdl;

    /**
     * @var string URI where the WSDL will be available
     */
    private $_uri;

    /**
     * @var DOMElement
     */
    private $_schema = null;

    /**
     * Types defined on schema
     *
     * @var array
     */
    private $_includedTypes = array();

    /**
     * Strategy for detection of complex types
     */
    protected $_strategy = null;

    /**
     * Constructor
     *
     * @param string  $name Name of the Web Service being Described
     * @param string  $uri URI where the WSDL will be available
     * @param boolean|string|Zend_Soap_Wsdl_Strategy_Interface $strategy
     */
    public function __construct($name, $uri, $strategy = true)
    {
        if ($uri instanceof Zend_Uri_Http) {
            $uri = $uri->getUri();
        }
        $this->_uri = $uri;

        /**
         * @todo change DomDocument object creation from cparsing to construxting using API
         * It also should authomatically escape $name and $uri values if necessary
         */
        $wsdl = "<?xml version='1.0' ?>
                <definitions name='$name' targetNamespace='$uri'
                    xmlns='http://schemas.xmlsoap.org/wsdl/'
                    xmlns:tns='$uri'
                    xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
                    xmlns:xsd='http://www.w3.org/2001/XMLSchema'
                    xmlns:soap-enc='http://schemas.xmlsoap.org/soap/encoding/'
                    xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'></definitions>";
        $this->_dom = new DOMDocument();
        if (!$this->_dom->loadXML($wsdl)) {
            require_once 'Zend/Server/Exception.php';
            throw new Zend_Server_Exception('Unable to create DomDocument');
        } else {
            $this->_wsdl = $this->_dom->documentElement;
        }

        $this->setComplexTypeStrategy($strategy);
    }

    /**
     * Set a new uri for this WSDL
     *
     * @param  string|Zend_Uri_Http $uri
     * @return Zend_Server_Wsdl
     */
    public function setUri($uri)
    {
        if ($uri instanceof Zend_Uri_Http) {
            $uri = $uri->getUri();
        }
        $oldUri = $this->_uri;
        $this->_uri = $uri;

        if ($this->_dom !== null) {
            // @todo: This is the worst hack ever, but its needed due to design and non BC issues of WSDL generation
            $xml = $this->_dom->saveXML();
            $xml = str_replace($oldUri, $uri, $xml);
            $this->_dom = new DOMDocument();
            $this->_dom->loadXML($xml);
        }

        return $this;
    }

    /**
     * Set a strategy for complex type detection and handling
     *
     * @todo Boolean is for backwards compability with extractComplexType object var. Remove it in later versions.
     * @param boolean|string|Zend_Soap_Wsdl_Strategy_Interface $strategy
     * @return Zend_Soap_Wsdl
     */
    public function setComplexTypeStrategy($strategy)
    {
        if ($strategy === true) {
            require_once "Zend/Soap/Wsdl/Strategy/DefaultComplexType.php";
            $strategy = new Zend_Soap_Wsdl_Strategy_DefaultComplexType();
        } elseif ($strategy === false) {
            require_once "Zend/Soap/Wsdl/Strategy/AnyType.php";
            $strategy = new Zend_Soap_Wsdl_Strategy_AnyType();
        } elseif (is_string($strategy)) {
            if (class_exists($strategy)) {
                $strategy = new $strategy();
            } else {
                require_once "Zend/Soap/Wsdl/Exception.php";
                throw new Zend_Soap_Wsdl_Exception(
                    sprintf("Strategy with name '%s does not exist.", $strategy
                ));
            }
        }

        if (!($strategy instanceof Zend_Soap_Wsdl_Strategy_Interface)) {
            require_once "Zend/Soap/Wsdl/Exception.php";
            throw new Zend_Soap_Wsdl_Exception("Set a strategy that is not of type 'Zend_Soap_Wsdl_Strategy_Interface'");
        }
        $this->_strategy = $strategy;
        return $this;
    }

    /**
     * Get the current complex type strategy
     *
     * @return Zend_Soap_Wsdl_Strategy_Interface
     */
    public function getComplexTypeStrategy()
    {
        return $this->_strategy;
    }

    /**
     * Add a {@link http://www.w3.org/TR/wsdl#_messages message} element to the WSDL
     *
     * @param string $name Name for the {@link http://www.w3.org/TR/wsdl#_messages message}
     * @param array $parts An array of {@link http://www.w3.org/TR/wsdl#_message parts}
     *                     The array is constructed like: 'name of part' => 'part xml schema data type'
     *                     or 'name of part' => array('type' => 'part xml schema type')
     *                     or 'name of part' => array('element' => 'part xml element name')
     * @return object The new message's XML_Tree_Node for use in {@link function addDocumentation}
     */
    public function addMessage($name, $parts)
    {
        $message = $this->_dom->createElement('message');

        $message->setAttribute('name', $name);

        if (sizeof($parts) > 0) {
            foreach ($parts as $name => $type) {
                $part = $this->_dom->createElement('part');
                $part->setAttribute('name', $name);
                if (is_array($type)) {
                    foreach ($type as $key => $value) {
                        $part->setAttribute($key, $value);
                    }
                } else {
                    $part->setAttribute('type', $type);
                }
                $message->appendChild($part);
            }
        }

        $this->_wsdl->appendChild($message);

        return $message;
    }

    /**
     * Add a {@link http://www.w3.org/TR/wsdl#_porttypes portType} element to the WSDL
     *
     * @param string $name portType element's name
     * @return object The new portType's XML_Tree_Node for use in {@link function addPortOperation} and {@link function addDocumentation}
     */
    public function addPortType($name)
    {
        $portType = $this->_dom->createElement('portType');
        $portType->setAttribute('name', $name);
        $this->_wsdl->appendChild($portType);

        return $portType;
    }

    /**
     * Add an {@link http://www.w3.org/TR/wsdl#_request-response operation} element to a portType element
     *
     * @param object $portType a portType XML_Tree_Node, from {@link function addPortType}
     * @param string $name Operation name
     * @param string $input Input Message
     * @param string $output Output Message
     * @param string $fault Fault Message
     * @return object The new operation's XML_Tree_Node for use in {@link function addDocumentation}
     */
    public function addPortOperation($portType, $name, $input = false, $output = false, $fault = false)
    {
        $operation = $this->_dom->createElement('operation');
        $operation->setAttribute('name', $name);

        if (is_string($input) && (strlen(trim($input)) >= 1)) {
            $node = $this->_dom->createElement('input');
            $node->setAttribute('message', $input);
            $operation->appendChild($node);
        }
        if (is_string($output) && (strlen(trim($output)) >= 1)) {
            $node= $this->_dom->createElement('output');
            $node->setAttribute('message', $output);
            $operation->appendChild($node);
        }
        if (is_string($fault) && (strlen(trim($fault)) >= 1)) {
            $node = $this->_dom->createElement('fault');
            $node->setAttribute('message', $fault);
            $operation->appendChild($node);
        }

        $portType->appendChild($operation);

        return $operation;
    }

    /**
     * Add a {@link http://www.w3.org/TR/wsdl#_bindings binding} element to WSDL
     *
     * @param string $name Name of the Binding
     * @param string $type name of the portType to bind
     * @return object The new binding's XML_Tree_Node for use with {@link function addBindingOperation} and {@link function addDocumentation}
     */
    public function addBinding($name, $portType)
    {
        $binding = $this->_dom->createElement('binding');
        $binding->setAttribute('name', $name);
        $binding->setAttribute('type', $portType);

        $this->_wsdl->appendChild($binding);

        return $binding;
    }

    /**
     * Add an operation to a binding element
     *
     * @param object $binding A binding XML_Tree_Node returned by {@link function addBinding}
     * @param array $input An array of attributes for the input element, allowed keys are: 'use', 'namespace', 'encodingStyle'. {@link http://www.w3.org/TR/wsdl#_soap:body More Information}
     * @param array $output An array of attributes for the output element, allowed keys are: 'use', 'namespace', 'encodingStyle'. {@link http://www.w3.org/TR/wsdl#_soap:body More Information}
     * @param array $fault An array of attributes for the fault element, allowed keys are: 'name', 'use', 'namespace', 'encodingStyle'. {@link http://www.w3.org/TR/wsdl#_soap:body More Information}
     * @return object The new Operation's XML_Tree_Node for use with {@link function addSoapOperation} and {@link function addDocumentation}
     */
    public function addBindingOperation($binding, $name, $input = false, $output = false, $fault = false)
    {
        $operation = $this->_dom->createElement('operation');
        $operation->setAttribute('name', $name);

        if (is_array($input)) {
            $node = $this->_dom->createElement('input');
            $soap_node = $this->_dom->createElement('soap:body');
            foreach ($input as $name => $value) {
                $soap_node->setAttribute($name, $value);
            }
            $node->appendChild($soap_node);
            $operation->appendChild($node);
        }

        if (is_array($output)) {
            $node = $this->_dom->createElement('output');
            $soap_node = $this->_dom->createElement('soap:body');
            foreach ($output as $name => $value) {
                $soap_node->setAttribute($name, $value);
            }
            $node->appendChild($soap_node);
            $operation->appendChild($node);
        }

        if (is_array($fault)) {
            $node = $this->_dom->createElement('fault');
            if (isset($fault['name'])) {
                $node->setAttribute('name', $fault['name']);
            }
            $soap_node = $this->_dom->createElement('soap:body');
            foreach ($output as $name => $value) {
                $soap_node->setAttribute($name, $value);
            }
            $node->appendChild($soap_node);
            $operation->appendChild($node);
        }

        $binding->appendChild($operation);

        return $operation;
    }

    /**
     * Add a {@link http://www.w3.org/TR/wsdl#_soap:binding SOAP binding} element to a Binding element
     *
     * @param object $binding A binding XML_Tree_Node returned by {@link function addBinding}
     * @param string $style binding style, possible values are "rpc" (the default) and "document"
     * @param string $transport Transport method (defaults to HTTP)
     * @return boolean
     */
    public function addSoapBinding($binding, $style = 'document', $transport = 'http://schemas.xmlsoap.org/soap/http')
    {
        $soap_binding = $this->_dom->createElement('soap:binding');
        $soap_binding->setAttribute('style', $style);
        $soap_binding->setAttribute('transport', $transport);

        $binding->appendChild($soap_binding);

        return $soap_binding;
    }

    /**
     * Add a {@link http://www.w3.org/TR/wsdl#_soap:operation SOAP operation} to an operation element
     *
     * @param object $operation An operation XML_Tree_Node returned by {@link function addBindingOperation}
     * @param string $soap_action SOAP Action
     * @return boolean
     */
    public function addSoapOperation($binding, $soap_action)
    {
        if ($soap_action instanceof Zend_Uri_Http) {
            $soap_action = $soap_action->getUri();
        }
        $soap_operation = $this->_dom->createElement('soap:operation');
        $soap_operation->setAttribute('soapAction', $soap_action);

        $binding->insertBefore($soap_operation, $binding->firstChild);

        return $soap_operation;
    }

    /**
     * Add a {@link http://www.w3.org/TR/wsdl#_services service} element to the WSDL
     *
     * @param string $name Service Name
     * @param string $port_name Name of the port for the service
     * @param string $binding Binding for the port
     * @param string $location SOAP Address for the service
     * @return object The new service's XML_Tree_Node for use with {@link function addDocumentation}
     */
    public function addService($name, $port_name, $binding, $location)
    {
        if ($location instanceof Zend_Uri_Http) {
            $location = $location->getUri();
        }
        $service = $this->_dom->createElement('service');
        $service->setAttribute('name', $name);

        $port = $this->_dom->createElement('port');
        $port->setAttribute('name', $port_name);
        $port->setAttribute('binding', $binding);

        $soap_address = $this->_dom->createElement('soap:address');
        $soap_address->setAttribute('location', $location);

        $port->appendChild($soap_address);
        $service->appendChild($port);

        $this->_wsdl->appendChild($service);

        return $service;
    }

    /**
     * Add a documentation element to any element in the WSDL.
     *
     * Note that the WSDL {@link http://www.w3.org/TR/wsdl#_documentation specification} uses 'document',
     * but the WSDL {@link http://schemas.xmlsoap.org/wsdl/ schema} uses 'documentation' instead.
     * The {@link http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html#WSDL_documentation_Element WS-I Basic Profile 1.1} recommends using 'documentation'.
     *
     * @param object $input_node An XML_Tree_Node returned by another method to add the documentation to
     * @param string $documentation Human readable documentation for the node
     * @return DOMElement The documentation element
     */
    public function addDocumentation($input_node, $documentation)
    {
        if ($input_node === $this) {
            $node = $this->_dom->documentElement;
        } else {
            $node = $input_node;
        }

        $doc = $this->_dom->createElement('documentation');
        $doc_cdata = $this->_dom->createTextNode($documentation);
        $doc->appendChild($doc_cdata);

        if ($node->hasChildNodes()) {
            $node->insertBefore($doc, $node->firstChild);
        } else {
            $node->appendChild($doc);
        }

        return $doc;
    }

    /**
     * Add WSDL Types element
     *
     * @param object $types A DomDocument|DomNode|DomElement|DomDocumentFragment with all the XML Schema types defined in it
     */
    public function addTypes($types)
    {
        if ($types instanceof DomDocument) {
            $dom = $this->_dom->importNode($types->documentElement);
            $this->_wsdl->appendChild($types->documentElement);
        } elseif ($types instanceof DomNode || $types instanceof DomElement || $types instanceof DomDocumentFragment) {
            $dom = $this->_dom->importNode($types);
            $this->_wsdl->appendChild($dom);
        }
    }

    /**
     * Add a complex type name that is part of this WSDL and can be used in signatures.
     *
     * @param string $type
     * @return Zend_Soap_Wsdl
     */
    public function addType($type)
    {
        if (!in_array($type, $this->_includedTypes)) {
            $this->_includedTypes[] = $type;
        }
        return $this;
    }

    /**
     * Return an array of all currently included complex types
     *
     * @return array
     */
    public function getTypes()
    {
        return $this->_includedTypes;
    }

    /**
     * Return the Schema node of the WSDL
     *
     * @return DOMElement
     */
    public function getSchema()
    {
        if ($this->_schema == null) {
            $this->addSchemaTypeSection();
        }

        return $this->_schema;
    }

    /**
     * Return the WSDL as XML
     *
     * @return string WSDL as XML
     */
    public function toXML()
    {
        return $this->_dom->saveXML();
    }

    /**
     * Return DOM Document
     *
     * @return DomDocument ent
     */
    public function toDomDocument()
    {
        return $this->_dom;
    }

    /**
     * Echo the WSDL as XML
     *
     * @return boolean
     */
    public function dump($filename = false)
    {
        if (!$filename) {
            echo $this->toXML();
            return true;
        } else {
            return file_put_contents($filename, $this->toXML());
        }
    }

    /**
     * Returns an XSD Type for the given PHP type
     *
     * @param string $type PHP Type to get the XSD type for
     * @return string
     */
    public function getType($type)
    {
        switch (strtolower($type)) {
            case 'string':
            case 'str':
                return 'xsd:string';
                break;
            case 'int':
            case 'integer':
                return 'xsd:int';
                break;
            case 'float':
            case 'double':
                return 'xsd:float';
                break;
            case 'boolean':
            case 'bool':
                return 'xsd:boolean';
                break;
            case 'array':
                return 'soap-enc:Array';
                break;
            case 'object':
                return 'xsd:struct';
                break;
            case 'mixed':
                return 'xsd:anyType';
                break;
            case 'void':
                return '';
            default:
                // delegate retrieval of complex type to current strategy
                return $this->addComplexType($type);
            }
    }

    /**
     * This function makes sure a complex types section and schema additions are set.
     *
     * @return Zend_Soap_Wsdl
     */
    public function addSchemaTypeSection()
    {
        if ($this->_schema === null) {
            $this->_schema = $this->_dom->createElement('xsd:schema');
            $this->_schema->setAttribute('targetNamespace', $this->_uri);
            $types = $this->_dom->createElement('types');
            $types->appendChild($this->_schema);
            $this->_wsdl->appendChild($types);
        }
        return $this;
    }

    /**
     * Add a {@link http://www.w3.org/TR/wsdl#_types types} data type definition
     *
     * @param string $type Name of the class to be specified
     * @return string XSD Type for the given PHP type
     */
    public function addComplexType($type)
    {
        if (in_array($type, $this->getTypes())) {
            return "tns:$type";
        }
        $this->addSchemaTypeSection();

        $strategy = $this->getComplexTypeStrategy();
        $strategy->setContext($this);
        // delegates the detection of a complex type to the current strategy
        return $strategy->addComplexType($type);
    }

    /**
     * Parse an xsd:element represented as an array into a DOMElement.
     *
     * @param array $element an xsd:element represented as an array
     * @return DOMElement parsed element
     */
    private function _parseElement($element)
    {
        if (!is_array($element)) {
            require_once "Zend/Soap/Wsdl/Exception.php";
            throw new Zend_Soap_Wsdl_Exception("The 'element' parameter needs to be an associative array.");
        }

        $elementXml = $this->_dom->createElement('xsd:element');
        foreach ($element as $key => $value) {
            if (in_array($key, array('sequence', 'all', 'choice'))) {
                if (is_array($value)) {
                    $complexType = $this->_dom->createElement('xsd:complexType');
                    if (count($value) > 0) {
                        $container = $this->_dom->createElement('xsd:' . $key);
                        foreach ($value as $subelement) {
                            $subelementXml = $this->_parseElement($subelement);
                            $container->appendChild($subelementXml);
                        }
                        $complexType->appendChild($container);
                    }
                    $elementXml->appendChild($complexType);
                }
            } else {
                $elementXml->setAttribute($key, $value);
            }
        }
        return $elementXml;
    }

    /**
     * Add an xsd:element represented as an array to the schema.
     *
     * Array keys represent attribute names and values their respective value.
     * The 'sequence', 'all' and 'choice' keys must have an array of elements as their value,
     * to add them to a nested complexType.
     *
     * Example: array( 'name' => 'MyElement',
     *                 'sequence' => array( array('name' => 'myString', 'type' => 'string'),
     *                                      array('name' => 'myInteger', 'type' => 'int') ) );
     * Resulting XML: <xsd:element name="MyElement"><xsd:complexType><xsd:sequence>
     *                  <xsd:element name="myString" type="string"/>
     *                  <xsd:element name="myInteger" type="int"/>
     *                </xsd:sequence></xsd:complexType></xsd:element>
     *
     * @param array $element an xsd:element represented as an array
     * @return string xsd:element for the given element array
     */
    public function addElement($element)
    {
        $schema = $this->getSchema();
        $elementXml = $this->_parseElement($element);
        $schema->appendChild($elementXml);
        return 'tns:' . $element['name'];
    }
}


<?php
namespace QuickBooksOnline\API\Facades;

class PurchaseOrder{

    public static function create(array $data, $throwException = TRUE){
        if(!isset($data) || empty($data)) throw new \Exception("Passed array for creating PurchaseOrder is Empty");
        $PurchaseOrderObject = FacadeHelper::reflectArrayToObject("PurchaseOrder", $data, $throwException );
        return $PurchaseOrderObject;
    }

    /**
     * This is an immutable function
     */
    public static function update($objToUpdate, array $data){
        $classOfObj = get_class($objToUpdate);
        if(strcmp($classOfObj, FacadeHelper::simpleAppendClassNameSpace("PurchaseOrder")) != 0){
            throw new \Exception("Target object class:{" .  $classOfObj . "} is not an instance of PurchaseOrder.");
        }
        $newPurchaseOrderObj = PurchaseOrder::create($data);
        $clonedOfObj = FacadeHelper::cloneObj($objToUpdate);
        FacadeHelper::mergeObj($clonedOfObj, $newPurchaseOrderObj);
        return $clonedOfObj;
    }

}


<?php

namespace QuickBooksOnline\API\Core;

use QuickBooksOnline\API\Core\Configuration\IppConfiguration;
use QuickBooksOnline\API\Core\Configuration\LocalConfigReader;
use QuickBooksOnline\API\Security\OAuthRequestValidator;
use QuickBooksOnline\API\Diagnostics\TraceLevel;
use QuickBooksOnline\API\Exception\SdkException;
use QuickBooksOnline\API\Core\Http\Compression\CompressionFormat;
use QuickBooksOnline\API\Core\Http\Serialization\SerializationFormat;
use QuickBooksOnline\API\Core\CoreConstants;
use QuickBooksOnline\API\Security\RequestValidator;
use QuickBooksOnline\API\Core\OAuth\OAuth2\OAuth2AccessToken;

/**
 * THe Service Context Class contains necessary settings for RestCalls
 */
class ServiceContext
{
    /**
     * The Realm Id.
     * Realm ID is the Company ID
     * @var string
     */
    public $realmId;

    /**
     * Intuit Service Type(QBO/QB).
     * @var string
     */
    public $serviceType;

    /**
     * Base Uri for IDS Service Call.
     * @var string
     */
    public $baseserviceURL;

    /**
     * Application Token.
     * @var string
     */
    private $appToken;

    /**
     * this flag indicates if static create methods of this class has been invoked.
     * @var string
     */
    private $isCreateMethod;

    /**
     * Temporary storage for serialization and compression values for request and reponse.
     * @var string
     */
    private $messageValues;

    /**
     * The Request validator
     * @var RequestValidator
     */
    public $requestValidator;

    /**
     * The Minor Version that would that returns additinoal fields from build v71
     * @var string
     */
    public $minorVersion;

    /**
     * Gets or sets the Ipp configuration.
     * @var IppConfiguration
     */
    public $IppConfiguration;

    /**
     * Keep this method for backward compability. Remove in next Release. USe Static factory to contruct new Instance();
     * @Feb.7th, 2017 Hao
     * Old Constructor from previous SDK code. Keep unchanged for backward compability.
     *   Initializes a new instance of the ServiceContext class.
     *
     * @param string $realmId The realm id.
     * @param string $serviceType Service Type - QBO/QB.
     * @param RequestValidator $requestValidator The request validate.
     * @throws IdsException If arguments are null or empty.
     * @throws InvalidRealmException If realm id is invalid.
     * @throws InvalidTokenException If the token is invalid.
     * @return ServiceContext Returns ServiceContext object.
     */
    private function __construct($realmId, $serviceType = CoreConstants::IntuitServicesTypeQBO, $requestValidator = null, $ippConfiguration = null)
    {
        if (isset($ippConfiguration)) {
            //$this->IppConfiguration = LocalConfigReader::ReadConfiguration();
            $this->IppConfiguration = $ippConfiguration;
        } else {
            throw new SdkException("You shouldn't pass a null ippConfiguration in Current release");
        }

        $this->realmId = $realmId;
        $this->serviceType = $serviceType;
        if ($requestValidator != null) {
            $this->IppConfiguration->Security = $requestValidator;
            $this->requestValidator = $requestValidator;
        } else {
            // Leave unchanged - already loaded from config file.
            $requestValidatorFromFile = $this->IppConfiguration->Security;
            $this->requestValidator = $requestValidatorFromFile;
        }

        $this->isCreateMethod = false;
        $this->baseserviceURL = $this->getBaseURL();

        return $this;
    }

    public static function ConfigureFromLocalFile($filePath)
    {
        $localIppConfiguration = LocalConfigReader::ReadConfigurationFromFile($filePath);
        $QBORealmID = $localIppConfiguration->RealmID;
        if (empty($QBORealmID)) {
            throw new SdkException('Sdk.config files contain empty RealmID.');
        }

        $serviceType = CoreConstants::IntuitServicesTypeQBO;
        $OAuthConfig = $localIppConfiguration->Security;
        $serviceContextInstance = new ServiceContext($QBORealmID, $serviceType, $OAuthConfig, $localIppConfiguration);

        return $serviceContextInstance;
    }

    /**
     * Configure the OAuth 1 or OAuth 2 values from a passed array
     * @param Array $settings    The array that contains OAuth 1 or OAuth 2 settings
     */
    public static function ConfigureFromPassedArray(array $settings)
    {
        ServiceContext::checkIfOAuthIsValid($settings);

        if(strcasecmp($settings['auth_mode'], CoreConstants::OAUTH1) == 0) {
             $OAuthConfig = new OAuthRequestValidator(
               $settings['accessTokenKey'],
               $settings['accessTokenSecret'],
               $settings['consumerKey'],
               $settings['consumerSecret']
             );
        }else{
             $OAuthConfig = new OAuth2AccessToken(
               $settings['ClientID'],
               $settings['ClientSecret'],
               CoreConstants::getAccessTokenFromArray($settings),
               CoreConstants::getRefreshTokenFromArray($settings)
             );
        }
        $QBORealmID = array_key_exists('QBORealmID', $settings) ? $settings['QBORealmID'] : null;
        $baseURL =    array_key_exists('baseUrl', $settings) ? $settings['baseUrl']: null;
        if(strcasecmp($baseURL, CoreConstants::DEVELOPMENT_SANDBOX) == 0){
           $baseURL = CoreConstants::SANDBOX_DEVELOPMENT;
        }else if(strcasecmp($baseURL, CoreConstants::PRODUCTION_QBO) == 0){
           $baseURL = CoreConstants::QBO_BASEURL;
        }
        $checkedBaseURL = ServiceContext::checkAndAddBaseURLSlash($baseURL);

        if($OAuthConfig instanceof OAuth2AccessToken){
           $OAuthConfig->setRealmID($QBORealmID);
           $OAuthConfig->setBaseURL($checkedBaseURL);
        }
        $serviceType = CoreConstants::IntuitServicesTypeQBO;
        $IppConfiguration = LocalConfigReader::ReadConfigurationFromParameters($OAuthConfig, $checkedBaseURL, CoreConstants::DEFAULT_LOGGINGLOCATION, CoreConstants::DEFAULT_SDK_MINOR_VERSION);
        $serviceContextInstance = new ServiceContext($QBORealmID, $serviceType, $OAuthConfig, $IppConfiguration);
        return $serviceContextInstance;
    }

    /**
     * Check if passed array has mininum required fields
     *
     * @param Array $settings        The array contain OAuth 1 or OAuth 2 settings
     */
    public static function checkIfOAuthIsValid(array $settings){
      if (!isset($settings) || empty($settings)) {
          throw new SdkException("Empty OAuth Array passed. Can't construct ServiceContext based on Empty Array.");
      }

      if(!isset($settings['auth_mode'])){
          throw new SdkException("No OAuth 1 or OAuth 2 Mode specified. Can't validate OAuth tokens.");
      }

      $mode = $settings['auth_mode'];

      //OAuth 1 settings, OAuth 1 must provide all four values.
      if (strcasecmp($mode, CoreConstants::OAUTH1) == 0) {
        if (!isset($settings['accessTokenKey'])) {
            throw new SdkException("'accessTokenKey' must be provided in OAuth1.");
        }

        if (!isset($settings['accessTokenSecret'])) {
            throw new SdkException("'accessTokenSecret' must be provided in OAuth1.");
        }
      }
      //OAuth 2 settings, Only client ID and Client Secret is required. For making API call, all four values, Client ID, Client Secret, Access Token, Access Token Secret are required.
      else if(strcasecmp($mode, CoreConstants::OAUTH2) == 0){
        if (!isset($settings['ClientID'])) {
            throw new SdkException("'ClientID' must be provided in OAuth2.");
        }

        if (!isset($settings['ClientSecret'])) {
            throw new SdkException("'ClientSecret' must be provided in OAuth2.");
        }
      }
      //Now other OAuth is supported
      else{
        throw new SdkException("OAuth Mode is not supported.");
      }
    }

    /**
     * When user passing the URL from Code. Sometimes they forget to add "/" at the end of string. Add it for them.
     * @param String $baseURL          return the formated string for the
     */
    private static function checkAndAddBaseURLSlash($baseURL){
        $lastChar = substr($baseURL, -1);
        if(strcmp($lastChar, "/") != 0){
             $baseURL = $baseURL . "/";
        }
        return $baseURL;
    }

    /**
     * Gets the base Uri for a QBO user.
     *
     * @return string Returns the base Uri endpoint for a user.
     */
    public function getBaseURL()
    {
        $this->IppConfiguration->Logger->RequestLog->Log(TraceLevel::Info, "Called GetBaseURL method.");
        try {
            if ($this->serviceType === CoreConstants::IntuitServicesTypeQBO) {
                $baseurl = $this->IppConfiguration->BaseUrl->Qbo . implode(CoreConstants::SLASH_CHAR, array(CoreConstants::VERSION)) . CoreConstants::SLASH_CHAR;
            }
            else if($this->serviceType === CoreConstants::IntuitServicesTypeIPP){
                $this->IppConfiguration->BaseUrl->Ipp  =  CoreConstants::IPP_BASEURL;
                $baseurl = $this->IppConfiguration->BaseUrl->Ipp;
            }
        } catch (\Exception $e) {
            throw new \Exception("Base URL is not setup");
        }
        return $baseurl;
    }

    /**
     * Populates the values of the service context like service type and base url to Platform Services.
     */
    public function UsePlatformServices()
    {
        $this->serviceType = CoreConstants::IntuitServicesTypeIPP;
        $this->baseserviceURL = $this->getBaseURL();
    }

    public function disableSSlCheck()
    {
        $this->IppConfiguration->SSLCheckStatus = false;
    }

    /**
     * Currently the Object serailziation and deserilization only supports XML format.
     */
    public function useXml()
    {
        $this->IppConfiguration->Message->Request->CompressionFormat = CompressionFormat::None;
        $this->IppConfiguration->Message->Response->CompressionFormat = CompressionFormat::None;
        $this->IppConfiguration->Message->Request->SerializationFormat = SerializationFormat::Xml;
        $this->IppConfiguration->Message->Response->SerializationFormat = SerializationFormat::Xml;
    }

    /**
     * Currently the Object serailziation and deserilization only supports XML format.
     * To be Supported, Currently it is only a place holder @Hao
     */
    public function useJson()
    {
        $this->IppConfiguration->Message->Request->CompressionFormat = CompressionFormat::None;
        $this->IppConfiguration->Message->Response->CompressionFormat = CompressionFormat::None;
        $this->IppConfiguration->Message->Request->SerializationFormat = SerializationFormat::Json;
        $this->IppConfiguration->Message->Response->SerializationFormat = SerializationFormat::Json;
    }

    /**
     * Disable Log the request and response to disk
     */
    public function disableLog()
    {
        try {
            $_ippConfigInstance = $this->getIppConfig();
            LocalConfigReader::setupLogger($_ippConfigInstance, CoreConstants::DEFAULT_LOGGINGLOCATION, "FALSE");
        } catch (\Exception $e) {
            throw new \Exception("Error in disable Log.");
        }
    }

    /**
     * Set a new Location for Log instead of the default Location
     * @param $new_log_location
     *        The new log directory path. It is a directory, not a file
     */
    public function setLogLocation($new_log_location)
    {
        try {
            $_ippConfigInstance = $this->getIppConfig();
            LocalConfigReader::setupLogger($_ippConfigInstance, $new_log_location, "TRUE");
        } catch (\Exception $e) {
            throw new \Exception("Error in setting up new Log Configuration: " . $new_log_location);
        }
    }

    public function setMinorVersion($new_minor_version)
    {
        try {
            $_ippConfigInstance = $this->getIppConfig();
            $_ippConfigInstance->minorVersion = $new_minor_version;
        } catch (\Exception $e) {
            throw new \Exception("Error in setting up minor version.");
        }
    }

    /**
     * Return the Ipp Configuration for the ServiceContext for changing the settings
     * @return IppConfiguration
     *            The IppConfiguration for current settings
     */
    private function getIppConfig()
    {
        $_ippConfiguration = $this->IppConfiguration;
        if (isset($_ippConfiguration)) {
            return $_ippConfiguration;
        } else {
            throw new \Exception("Return Null or Empty IppConfiguration.");
        }
    }

    /**
     * Update OAuth 2 Access Token with the new Token Value
     * @param OAuth2AccessToken  The OAuth 2 token that contains access token, refresh token
     */
    public function updateOAuth2Token($OAuth2AccessToken){
      if($OAuth2AccessToken instanceof OAuth2AccessToken && $this->requestValidator instanceof OAuth2AccessToken){
        $this->IppConfiguration->Security = $OAuth2AccessToken;
        $this->requestValidator = $OAuth2AccessToken;
      }

    }
}


<?php
namespace QuickBooksOnline\API\Diagnostics;

/**
 * This file contains Trace Logger.
 */
class TraceLogger extends LoggerBase
{

    /**
     * Provides a multilevel switch to control tracing.
     * @var int $traceSwitch
     */
    private $traceSwitchLevel;

    /**
     * Initializes a new instance of the TraceLogger class.
     */
    public function __construct()
    {
        // Searches for the switch name "IPPTraceSwitch" in the config file of the client.
        // If not found then default trace switch is OFF.
        $this->traceSwitchLevel = TraceLevel::Off;
    }

    /**
     * Logs messages depending on the ids trace level.
     *
     * @param TraceLevel $idsTraceLevel IDS Trace Level
     * @param string messageToWrite The message to write.
     */
    public function Log($idsTraceLevel, $messageToWrite)
    {
        if ((int)$this->traceSwitchLevel < (int)$idsTraceLevel) {
            return;
        }

        $backTrace =  debug_backtrace();
        $callerFileName = $backTrace[0]['file'];
        $callerFileLineNumber = $backTrace[0]['line'];
        $callerFunctionName = $backTrace[0]['function'];
        $logMessage = implode(" - ", array(date('Y-m-d H:i:s'),
                                           $callerFileName,
                                           $callerFileLineNumber,
                                           $callerFunctionName,
                                           $messageToWrite));

        parent::Log($idsTraceLevel, $logMessage);
    }
}


<?php
//namespace com\mikebevz\xsd2php;
namespace QuickBooksOnline\API\XSD2PHP\src\com\mikebevz\xsd2php;

/**
 * Copyright 2010 Mike Bevz <myb@mikebevz.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//require_once dirname(__FILE__) . '/Common.php';

/**
 * PHP Class representation
 *
 * @author Mike Bevz <myb@mikebevz.com>
 * @version 0.0.1
 *
 */
class PHPClass extends Common
{
    /**
     * Class name
     *
     * @var class name
     */
    public $name;

    /**
     * Array of class level documentation
     *
     * @var array
     */
    public $classDocBlock;

    /**
     * Class type
     *
     * @var string
     */
    public $type;

    /**
     * Class namespace
     * @var string
     */
    public $namespace;

    /**
     * Class type namespace
     * @var string
     */
    public $typeNamespace;

    /**
     * Class properties
     *
     * @var array
     */
    public $classProperties;

    /**
     * Class to extend
     * @var string
     */
    public $extends;

    /**
     * Namespace of parent class
     * @var string
     */
    public $extendsNamespace;

    /**
     * Array of class properties  array(array('name'=>'propertyName', 'docs' => array('property'=>'value')))
     * @var array
     */
    public $properties;

    /**
     * Show debug info
     * @var boolean
     */
    public $debug = false;

    public $constants;

    /**
     * __construct
     *
     * @param string  $classPrefix Prefix for generated class names
     *
     * @return void
     */
    public function __construct($classPrefix='')
    {
        parent::__construct($classPrefix);
    }

    /**
     * Returns array of PHP classes
     *
     * @return array
     */
    public function getPhpCode()
    {
        $code = "\n";
        if ($this->extendsNamespace != '') {
            if (false === $this->overrideAsSingleNamespace) {
                $code .= "use ".$this->extendsNamespace.";\n";
            }
        }

        if (!empty($this->classDocBlock)) {
            $code .= $this->getDocBlock($this->classDocBlock);
        }

        if ($this->name == $this->classPrefix.$this->extends) {
            $this->name = $this->name . "Wrapper";
        }

        $code .= 'class '.$this->name."\n";
        if ($this->extends != '') {
            if ($this->extendsNamespace != '') {
                $nsLastName = array_reverse(explode('\\', $this->extendsNamespace));
                $code .= "\t".'extends '.$this->classPrefix.$nsLastName[0];
                //$code = "require_once('".$this->classPrefix.implode('/', $nsLastName).".php');\n" . $code;
            } else {
                $code .= "\t".'extends '.$this->classPrefix.$this->extends."\n";
            }
        }
        $code .= "\t".'{'."\n";

        $propNames = array();
        foreach ($this->classProperties as $oneProp) {
            $propNames[] = $oneProp['name'];
        }
        $code .= ''."\n";

        $code .= "\t\t".'/**                                                                       '."\n";
        $code .= "\t\t".'* Initializes this object, optionally with pre-defined property values    '."\n";
        $code .= "\t\t".'*                                                                         '."\n";
        $code .= "\t\t".'* Initializes this object and it\'s property members, using the dictionary'."\n";
        $code .= "\t\t".'* of key/value pairs passed as an optional argument.                      '."\n";
        $code .= "\t\t".'*                                                                         '."\n";
        $code .= "\t\t".'* @param dictionary $keyValInitializers key/value pairs to be populated into object\'s properties '."\n";
        $code .= "\t\t".'* @param boolean $verbose specifies whether object should echo warnings   '."\n";
        $code .= "\t\t".'*/                                                                        '."\n";
        $code .= "\t\t".'public function __construct($keyValInitializers=array(), $verbose=FALSE)'."\n";
        $code .= "\t\t".'{'."\n";
        $code .= "\t\t\t".'foreach($keyValInitializers as $initPropName => $initPropVal)'."\n";
        $code .= "\t\t\t".'{'."\n";
        $code .= "\t\t\t\t".'if (property_exists(\''. $this->name . '\',$initPropName) || property_exists(\'QuickBooksOnline\\API\\Data\\'. $this->name . '\',$initPropName))'."\n";
        $code .= "\t\t\t\t".'{'."\n";
        $code .= "\t\t\t\t\t".'$this->{$initPropName} = $initPropVal;'."\n";
        $code .= "\t\t\t\t".'}'."\n";
        $code .= "\t\t\t\t".'else'."\n";
        $code .= "\t\t\t\t".'{'."\n";
        $code .= "\t\t\t\t\t".'if ($verbose)'."\n";
        $code .= "\t\t\t\t\t\t".'echo "Property does not exist ($initPropName) in class (".get_class($this).")'."\";\n";
        $code .= "\t\t\t\t".'}'."\n";
        $code .= "\t\t\t".'}'."\n";
        $code .= "\t\t".'}'."\n";

        $code .= ''."\n";
        if (in_array($this->type, $this->basicTypes)) {
            $code .= "\t\t".$this->getDocBlock(array('xmlType'=>'value', 'var' => $this->normalizeType($this->type)), "\t\t");
            $code .= "\t\tpublic ".'$value;';
        }

        if (!empty($this->classProperties)) {
            $code .= $this->getClassProperties($this->classProperties);
        }

        if (!empty($this->constants)) {
            $code .= $this->getClassConstants($this->constants);
        }

        $code .= ''."\n";
        $code .= ''."\n";
        $code .= '} // end class '.$this->name."\n";
        return $code;
    }

    /**
     * Return class properties from array with indent specified
     *
     * @param array $props  Properties array
     * @param array $indent Indentation in tabs
     *
     * @return string
     */
    public function getClassProperties($props, $indent = "\t")
    {
        $code = $indent."\n";

        foreach ($props as $prop) {
            if (!empty($prop['docs'])) {
                $code .= $indent.$this->getDocBlock($prop['docs'], $indent);
            }
            $code .= $indent.'public $'.$prop['name'].";\n";
        }
        return $code;
    }

    public function getClassConstants($const, $indent = "\t")
    {
        $code = array();

        foreach ($const as $value) {
            //$code[] = $indent.'const ' . $this->constNameFromValue($value) . " = \"$value\";"  ;
        }
        return implode("\n", $code);
    }

    public function constNameFromValue($value)
    {
        return strtoupper(str_ireplace(array(" ","-"), "_", $value));
    }

    /**
     * Return docBlock
     *
     * @param array  $docs   Array of docs
     * @param string $indent Indentation
     *
     * return string
     */
    public function getDocBlock($docs, $indent = "")
    {
        $code  = '/**'."\n";
        foreach ($docs as $key => $value) {
            $code .= $indent.' * @'.$key.' '.$value."\n";
        }
        $code .= $indent.' */'."\n";
        return $code;
    }
}


<?php
namespace QuickBooksOnline\API\Facades;

class Purchase{

    public static function create(array $data, $throwException = TRUE){
        if(!isset($data) || empty($data)) throw new \Exception("Passed array for creating Purchase is Empty");
        $PurchaseObject = FacadeHelper::reflectArrayToObject("Purchase", $data, $throwException );
        return $PurchaseObject;
    }

    /**
     * This is an immutable function
     */
    public static function update($objToUpdate, array $data){
        $classOfObj = get_class($objToUpdate);
        if(strcmp($classOfObj, FacadeHelper::simpleAppendClassNameSpace("Purchase")) != 0){
            throw new \Exception("Target object class:{" .  $classOfObj . "} is not an instance of Purchase.");
        }
        $newPurchaseObj = Purchase::create($data);
        $clonedOfObj = FacadeHelper::cloneObj($objToUpdate);
        FacadeHelper::mergeObj($clonedOfObj, $newPurchaseObj);
        return $clonedOfObj;
    }

}


<?php
/*******************************************************************************
 * Copyright (c) 2017 Intuit
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
namespace QuickBooksOnline\API\DataService;

use QuickBooksOnline\API\Data\IPPIntuitEntity;
use QuickBooksOnline\API\Exception\IdsException;

/**
 * This class processes the batch request.
 */
class IntuitBatchResponse
{

    /**
     * unique batch item Id
     * @var string batchItemId
     */
    public $batchItemId;

    /**
     * enum representing ResponseType after batch execution.
     * @var int responseType
     */
    public $responseType;

    /**
     * entity in case response type is entity.
     * @var IPPIntuitEntity entity
     */
    public $entity;

    /**
     * list of entities in case ResponseType is query.
     * @var array entities
     */
    public $entities;

    /**
     * IdsException in case of ResponseType is exception.
     * @var IdsException exception
     */
    public $exception;

    /**
     * the type of the response return after batch execution
     * @var string Id
     */
    public $Id;

    /**
     * Indicate if the Batch Request for the bId is successful.
     * @var Boolean
     */
    private $isBatchItemResponseSuccess = false;

    /**
     * Initializes a new instance of the IntuitBatchResponse class.
     */
    public function __construct()
    {
        $this->entities = array();
    }

    /**
     * Gets list of entites in case ResponseType is Report.
     * @return array entities
     */
    public function ReadOnlyCollection()
    {
        return $this->entities;
    }

    /**
     * Adds the entities to entities list
     * @param entity The entity.
     */
    public function AddEntities($entity)
    {
        $this->entities[] = $entity;
    }

    /**
     * Set the Entities to an entity list
     * @param Array $newEntities
     *
     * @return $this
     */
    public function setEntities($newEntities){
        $this->entities = $newEntities;
        return $this;
    }

    /**
     * Check if the Batch Item Response is a success response or failed response, as the Batch request is always sending 200 as its status code.
     * @return Boolean
     */
    public function isSuccess(){
        return $this->isBatchItemResponseSuccess;
    }

    /**
     * Mark this batch request is a successful call
     */
    public function successFlagOn(){
        $this->isBatchItemResponseSuccess = true;
    }

    /**
     * Return the result of the Request if there is no error. If the Batch request is Query, the result will be an array of items. If the Batch request is create, update, or ddelete, it will be the entity.
     * @return Array|IPPIntuitEntity
     */
    public function getResult(){
        if(isset($this->entity)){
            return $this->entity;
        }else if(isset($this->entities)){
            return $this->entities;
        } else{
           return null;
        }
    }

    /**
     * Return the Error if the request is not made successfullu.
     * @return IdsException
     */
    public function getError(){
        return $this->exception;
    }
}