<?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
 * @subpackage AutoDiscover
 * @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: AutoDiscover.php 20096 2010-01-06 02:05:09Z bkarwin $
 */

/**
 * @see Zend_Server_Interface
 */
require_once 'Zend/Server/Interface.php';
/**
 * @see Zend_Soap_Wsdl
 */
require_once 'Zend/Soap/Wsdl.php';
/**
 * @see Zend_Server_Reflection
 */
require_once 'Zend/Server/Reflection.php';
/**
 * @see Zend_Server_Abstract
 */
require_once 'Zend/Server/Abstract.php';
/**
 * @see Zend_Uri
 */
require_once 'Zend/Uri.php';

/**
 * Zend_Soap_AutoDiscover
 *
 * @category   Zend
 * @package    Zend_Soap
 * @subpackage AutoDiscover
 */
class Zend_Soap_AutoDiscover implements Zend_Server_Interface
{
    /**
     * @var Zend_Soap_Wsdl
     */
    protected $_wsdl = null;

    /**
     * @var Zend_Server_Reflection
     */
    protected $_reflection = null;

    /**
     * @var array
     */
    protected $_functions = array();

    /**
     * @var boolean
     */
    protected $_strategy;

    /**
     * Url where the WSDL file will be available at.
     *
     * @var WSDL Uri
     */
    protected $_uri;

    /**
     * soap:body operation style options
     *
     * @var array
     */
    protected $_operationBodyStyle = array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/");

    /**
     * soap:operation style
     *
     * @var array
     */
    protected $_bindingStyle = array('style' => 'rpc', 'transport' => 'http://schemas.xmlsoap.org/soap/http');

    /**
     * Constructor
     *
     * @param boolean|string|Zend_Soap_Wsdl_Strategy_Interface $strategy
     * @param string|Zend_Uri $uri
     */
    public function __construct($strategy = true, $uri=null)
    {
        $this->_reflection = new Zend_Server_Reflection();
        $this->setComplexTypeStrategy($strategy);

        if ($uri !== null) {
            $this->setUri($uri);
        }
    }

    /**
     * Set the location at which the WSDL file will be availabe.
     *
     * @see Zend_Soap_Exception
     * @throws Zend_Soap_AutoDiscover_Exception
     * @param  Zend_Uri|string $uri
     * @return Zend_Soap_AutoDiscover
     */
    public function setUri($uri)
    {
        if (!is_string($uri) && !($uri instanceof Zend_Uri)) {
            require_once "Zend/Soap/AutoDiscover/Exception.php";
            throw new Zend_Soap_AutoDiscover_Exception("No uri given to Zend_Soap_AutoDiscover::setUri as string or Zend_Uri instance.");
        }
        $this->_uri = $uri;

        // change uri in WSDL file also if existant
        if ($this->_wsdl instanceof Zend_Soap_Wsdl) {
            $this->_wsdl->setUri($uri);
        }

        return $this;
    }

    /**
     * Return the current Uri that the SOAP WSDL Service will be located at.
     *
     * @return Zend_Uri
     */
    public function getUri()
    {
        if ($this->_uri !== null) {
            $uri = $this->_uri;
        } else {
            $schema     = $this->getSchema();
            $host       = $this->getHostName();
            $scriptName = $this->getRequestUriWithoutParameters();
            $uri = Zend_Uri::factory($schema . '://' . $host . $scriptName);
            $this->setUri($uri);
        }
        return $uri;
    }

    /**
     * Set options for all the binding operations soap:body elements.
     *
     * By default the options are set to 'use' => 'encoded' and
     * 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/".
     *
     * @see    Zend_Soap_AutoDiscover_Exception
     * @param  array $operationStyle
     * @return Zend_Soap_AutoDiscover
     */
    public function setOperationBodyStyle(array $operationStyle=array())
    {
        if (!isset($operationStyle['use'])) {
            require_once "Zend/Soap/AutoDiscover/Exception.php";
            throw new Zend_Soap_AutoDiscover_Exception("Key 'use' is required in Operation soap:body style.");
        }
        $this->_operationBodyStyle = $operationStyle;
        return $this;
    }

    /**
     * Set Binding soap:binding style.
     *
     * By default 'style' is 'rpc' and 'transport' is 'http://schemas.xmlsoap.org/soap/http'.
     *
     * @param  array $bindingStyle
     * @return Zend_Soap_AutoDiscover
     */
    public function setBindingStyle(array $bindingStyle=array())
    {
        if (isset($bindingStyle['style'])) {
            $this->_bindingStyle['style'] = $bindingStyle['style'];
        }
        if (isset($bindingStyle['transport'])) {
            $this->_bindingStyle['transport'] = $bindingStyle['transport'];
        }
        return $this;
    }

    /**
     * Detect and returns the current HTTP/HTTPS Schema
     *
     * @return string
     */
    protected function getSchema()
    {
        $schema = "http";
        if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
            $schema = 'https';
        }
        return $schema;
    }

    /**
     * Detect and return the current hostname
     *
     * @return string
     */
    protected function getHostName()
    {
        if (isset($_SERVER['HTTP_HOST'])) {
            $host = $_SERVER['HTTP_HOST'];
        } else {
            $host = $_SERVER['SERVER_NAME'];
        }
        return $host;
    }

    /**
     * Detect and return the current script name without parameters
     *
     * @return string
     */
    protected function getRequestUriWithoutParameters()
    {
        if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { // check this first so IIS will catch
            $requestUri = $_SERVER['HTTP_X_REWRITE_URL'];
        } elseif (isset($_SERVER['REQUEST_URI'])) {
            $requestUri = $_SERVER['REQUEST_URI'];
        } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0, PHP as CGI
            $requestUri = $_SERVER['ORIG_PATH_INFO'];
        } else {
            $requestUri = $_SERVER['SCRIPT_NAME'];
        }
        if (($pos = strpos($requestUri, "?")) !== false) {
            $requestUri = substr($requestUri, 0, $pos);
        }

        return $requestUri;
    }

    /**
     * Set the strategy that handles functions and classes that are added AFTER this call.
     *
     * @param  boolean|string|Zend_Soap_Wsdl_Strategy_Interface $strategy
     * @return Zend_Soap_AutoDiscover
     */
    public function setComplexTypeStrategy($strategy)
    {
        $this->_strategy = $strategy;
        if ($this->_wsdl instanceof  Zend_Soap_Wsdl) {
            $this->_wsdl->setComplexTypeStrategy($strategy);
        }

        return $this;
    }

    /**
     * Set the Class the SOAP server will use
     *
     * @param string $class Class Name
     * @param string $namespace Class Namspace - Not Used
     * @param array $argv Arguments to instantiate the class - Not Used
     *
     * @return $this
     */
    public function setClass($class, $namespace = '', $argv = null)
    {
        $uri = $this->getUri();

        $wsdl = new Zend_Soap_Wsdl($class, $uri, $this->_strategy);

        // The wsdl:types element must precede all other elements (WS-I Basic Profile 1.1 R2023)
        $wsdl->addSchemaTypeSection();

        $port = $wsdl->addPortType($class . 'Port');
        $binding = $wsdl->addBinding($class . 'Binding', 'tns:' .$class. 'Port');

        $wsdl->addSoapBinding($binding, $this->_bindingStyle['style'], $this->_bindingStyle['transport']);
        $wsdl->addService($class . 'Service', $class . 'Port', 'tns:' . $class . 'Binding', $uri);
        foreach ($this->_reflection->reflectClass($class)->getMethods() as $method) {
            $this->_addFunctionToWsdl($method, $wsdl, $port, $binding);
        }
        $this->_wsdl = $wsdl;
        return $this;
    }

    /**
     * Add a Single or Multiple Functions to the WSDL
     *
     * @param string $function Function Name
     * @param string $namespace Function namespace - Not Used
     */
    public function addFunction($function, $namespace = '')
    {
        static $port;
        static $operation;
        static $binding;

        if (!is_array($function)) {
            $function = (array) $function;
        }

        $uri = $this->getUri();

        if (!($this->_wsdl instanceof Zend_Soap_Wsdl)) {
            $parts = explode('.', basename($_SERVER['SCRIPT_NAME']));
            $name = $parts[0];
            $wsdl = new Zend_Soap_Wsdl($name, $uri, $this->_strategy);

            // The wsdl:types element must precede all other elements (WS-I Basic Profile 1.1 R2023)
            $wsdl->addSchemaTypeSection();

            $port = $wsdl->addPortType($name . 'Port');
            $binding = $wsdl->addBinding($name . 'Binding', 'tns:' .$name. 'Port');

            $wsdl->addSoapBinding($binding, $this->_bindingStyle['style'], $this->_bindingStyle['transport']);
            $wsdl->addService($name . 'Service', $name . 'Port', 'tns:' . $name . 'Binding', $uri);
        } else {
            $wsdl = $this->_wsdl;
        }

        foreach ($function as $func) {
            $method = $this->_reflection->reflectFunction($func);
            $this->_addFunctionToWsdl($method, $wsdl, $port, $binding);
        }
        $this->_wsdl = $wsdl;
    }

    /**
     * Add a function to the WSDL document.
     *
     * @param $function Zend_Server_Reflection_Function_Abstract function to add
     * @param $wsdl Zend_Soap_Wsdl WSDL document
     * @param $port object wsdl:portType
     * @param $binding object wsdl:binding
     * @return void
     */
    protected function _addFunctionToWsdl($function, $wsdl, $port, $binding)
    {
        $uri = $this->getUri();

        // We only support one prototype: the one with the maximum number of arguments
        $prototype = null;
        $maxNumArgumentsOfPrototype = -1;
        foreach ($function->getPrototypes() as $tmpPrototype) {
            $numParams = count($tmpPrototype->getParameters());
            if ($numParams > $maxNumArgumentsOfPrototype) {
                $maxNumArgumentsOfPrototype = $numParams;
                $prototype = $tmpPrototype;
            }
        }
        if ($prototype === null) {
            require_once "Zend/Soap/AutoDiscover/Exception.php";
            throw new Zend_Soap_AutoDiscover_Exception("No prototypes could be found for the '" . $function->getName() . "' function");
        }

        // Add the input message (parameters)
        $args = array();
        if ($this->_bindingStyle['style'] == 'document') {
            // Document style: wrap all parameters in a sequence element
            $sequence = array();
            foreach ($prototype->getParameters() as $param) {
                $sequenceElement = array(
                    'name' => $param->getName(),
                    'type' => $wsdl->getType($param->getType())
                );
                if ($param->isOptional()) {
                    $sequenceElement['nillable'] = 'true';
                }
                $sequence[] = $sequenceElement;
            }
            $element = array(
                'name' => $function->getName(),
                'sequence' => $sequence
            );
            // Add the wrapper element part, which must be named 'parameters'
            $args['parameters'] = array('element' => $wsdl->addElement($element));
        } else {
            // RPC style: add each parameter as a typed part
            foreach ($prototype->getParameters() as $param) {
                $args[$param->getName()] = array('type' => $wsdl->getType($param->getType()));
            }
        }
        $wsdl->addMessage($function->getName() . 'In', $args);

        $isOneWayMessage = false;
        if ($prototype->getReturnType() == "void") {
            $isOneWayMessage = true;
        }

        if ($isOneWayMessage == false) {
            // Add the output message (return value)
            $args = array();
            if ($this->_bindingStyle['style'] == 'document') {
                // Document style: wrap the return value in a sequence element
                $sequence = array();
                if ($prototype->getReturnType() != "void") {
                    $sequence[] = array(
                        'name' => $function->getName() . 'Result',
                        'type' => $wsdl->getType($prototype->getReturnType())
                    );
                }
                $element = array(
                    'name' => $function->getName() . 'Response',
                    'sequence' => $sequence
                );
                // Add the wrapper element part, which must be named 'parameters'
                $args['parameters'] = array('element' => $wsdl->addElement($element));
            } elseif ($prototype->getReturnType() != "void") {
                // RPC style: add the return value as a typed part
                $args['return'] = array('type' => $wsdl->getType($prototype->getReturnType()));
            }
            $wsdl->addMessage($function->getName() . 'Out', $args);
        }

        // Add the portType operation
        if ($isOneWayMessage == false) {
            $portOperation = $wsdl->addPortOperation($port, $function->getName(), 'tns:' . $function->getName() . 'In', 'tns:' . $function->getName() . 'Out');
        } else {
            $portOperation = $wsdl->addPortOperation($port, $function->getName(), 'tns:' . $function->getName() . 'In', false);
        }
        $desc = $function->getDescription();
        if (strlen($desc) > 0) {
            $wsdl->addDocumentation($portOperation, $desc);
        }

        // When using the RPC style, make sure the operation style includes a 'namespace' attribute (WS-I Basic Profile 1.1 R2717)
        if ($this->_bindingStyle['style'] == 'rpc' && !isset($this->_operationBodyStyle['namespace'])) {
            $this->_operationBodyStyle['namespace'] = ''.$uri;
        }

        // Add the binding operation
        $operation = $wsdl->addBindingOperation($binding, $function->getName(), $this->_operationBodyStyle, $this->_operationBodyStyle);
        $wsdl->addSoapOperation($operation, $uri . '#' .$function->getName());

        // Add the function name to the list
        $this->_functions[] = $function->getName();
    }

    /**
     * Action to take when an error occurs
     *
     * @param string $fault
     * @param string|int $code
     */
    public function fault($fault = null, $code = null)
    {
        require_once "Zend/Soap/AutoDiscover/Exception.php";
        throw new Zend_Soap_AutoDiscover_Exception("Function has no use in AutoDiscover.");
    }

    /**
     * Handle the Request
     *
     * @param string $request A non-standard request - Not Used
     */
    public function handle($request = false)
    {
        if (!headers_sent()) {
            header('Content-Type: text/xml');
        }
        $this->_wsdl->dump();
    }

    /**
     * Proxy to WSDL dump function
     *
     * @param string $filename
     */
    public function dump($filename)
    {
        if ($this->_wsdl !== null) {
            return $this->_wsdl->dump($filename);
        } else {
            /**
             * @see Zend_Soap_AutoDiscover_Exception
             */
            require_once "Zend/Soap/AutoDiscover/Exception.php";
            throw new Zend_Soap_AutoDiscover_Exception("Cannot dump autodiscovered contents, WSDL file has not been generated yet.");
        }
    }

    /**
     * Proxy to WSDL toXml() function
     */
    public function toXml()
    {
        if ($this->_wsdl !== null) {
            return $this->_wsdl->toXml();
        } else {
            /**
             * @see Zend_Soap_AutoDiscover_Exception
             */
            require_once "Zend/Soap/AutoDiscover/Exception.php";
            throw new Zend_Soap_AutoDiscover_Exception("Cannot return autodiscovered contents, WSDL file has not been generated yet.");
        }
    }

    /**
     * Return an array of functions in the WSDL
     *
     * @return array
     */
    public function getFunctions()
    {
        return $this->_functions;
    }

    /**
     * Load Functions
     *
     * @param unknown_type $definition
     */
    public function loadFunctions($definition)
    {
        require_once "Zend/Soap/AutoDiscover/Exception.php";
        throw new Zend_Soap_AutoDiscover_Exception("Function has no use in AutoDiscover.");
    }

    /**
     * Set Persistance
     *
     * @param int $mode
     */
    public function setPersistence($mode)
    {
        require_once "Zend/Soap/AutoDiscover/Exception.php";
        throw new Zend_Soap_AutoDiscover_Exception("Function has no use in AutoDiscover.");
    }

    /**
     * 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)
    {
        if (!($this->_wsdl instanceof Zend_Soap_Wsdl)) {
            /** @todo Exception throwing may be more correct */

            // WSDL is not defined yet, so we can't recognize type in context of current service
            return '';
        } else {
            return $this->_wsdl->getType($type);
        }
    }
}


<?php
namespace QuickBooksOnline\API\Facades;

class QuickBookClass{

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

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

}


<?php

namespace QuickBooksOnline\API\Core\HttpClients;

use QuickBooksOnline\API\Core\CoreHelper;
use QuickBooksOnline\API\Core\ServiceContext;
use \QuickBooksOnline\API\Core\Http\Serialization\JsonObjectSerializer;
use \QuickBooksOnline\API\Core\Http\Serialization\XmlObjectSerializer;
use QuickBooksOnline\API\Utility\IntuitErrorHandler;
use QuickBooksOnline\API\Diagnostics\TraceLevel;
use QuickBooksOnline\API\Core\CoreConstants;
use QuickBooksOnline\API\Exception\IdsException;
use QuickBooksOnline\API\Core\OAuth\OAuth1\OAuth1;
use QuickBooksOnline\API\Core\OAuth\OAuth2\OAuth2AccessToken;
use QuickBooksOnline\API\Exception\SdkException;
use QuickBooksOnline\API\Exception\ServiceException;
use QuickBooksOnline\API\Core\HttpClients\FaultHandler;
use QuickBooksOnline\API\Diagnostics\LogRequestsToDisk;

/**
 * Class SyncRestHandler
 *
 * SyncRestHandler contains the logic for preparing the REST request, calls REST services and returns the response.
 * @package QuickBooksOnline
 *
 */
class SyncRestHandler extends RestHandler
{
   /**
    * Store the error information on a non-200 response from QBO
    * @var FaultHandler
    */
    private $faultHandler = false;

   /**
    * The serviceContext of this request
    * @var ServiceContext
    */
    private $context = null;

   /**
    * The Http Client that is used to make QuickBooks Online API call
    * @var HttpClientInterface
    */
    private $httpClientInterface;

   /**
    * Initializes a new instance of the SyncRestHandler class.
    *
    * @param ServiceContext   $context    The service context used for the request
    * @param HttpClientInterface $client  The http client used for the request
    */
    public function __construct($context, HttpClientInterface $client = null)
    {
        parent::__construct($context);
        $this->context = $context;
        $this->httpClientInterface = isset($client) ? $client : new CurlHttpClient();
    }

   /**
    * Update the Service Context of the request.
    *
    * @param ServiceContext  $newServiceContext      The new service context that will be used for the request
    */
    public function updateContext($newServiceContext){
        if(isset($newServiceContext) && $newServiceContext instanceof ServiceContext){
            $this->context = $newServiceContext;
        }else{
           throw new SdkException("Cannot Update Service Context. The service context either is undefined or not an instance of ServiceContext.");
        }
    }

    /**
     * Return the RequestLogger
     * @return LogRequestsToDisk $requestLogger;
     */
     public function getRequestLogger(){
       return $this->RequestLogging;
     }

   /**
    * Return an representation of an error returned by the last request, or false if the last request was not an error.
    * @return bool|FaultHandler
    */
    public function getFaultHandler()
    {
       return $this->faultHandler;
    }

    /**
     * Sending an request to QuickBooks Online based on the Request parameters, body and URI.
     *
     * @param  RequestParameters $requestParameters     The request parameter for this API Call
     * @param  String           $requestBody           The body of the API call
     * @param  String           $specifiedRequestUri   The user specified URI for the request
     * @param  Boolean          $throwExceptionOnError If throw an exception whent he return http status is not 200. Default is false
     * @return null|array       APIResult              The result of this Http Request.
     */
    public function sendRequest($requestParameters, $requestBody, $specifiedRequestUri, $throwExceptionOnError = false)
    {
        // This step is required since the configuration settings might have been changed.
        $this->resetCompressorAndSerializer();
        //Get the OAuth Authorization Mode for the request, OAuth 1 or OAuth 2.
        $oMode = $this->context->IppConfiguration->OAuthMode;
        // Determine dest URI
        $requestUri = $this->getDestinationURL($requestParameters, $oMode, $specifiedRequestUri);
        //minorVersion support
        $requestUri = $this->appendMinorVersionToRequestURI($requestUri);
        //Check for the HTTP method
        $HttpMethod = $this->checkHTTPMethod($requestParameters);
        $queryParameters = $this->parseURL($requestUri);
        $baseURL = $this->getBaseURL($requestUri);
        if($oMode == CoreConstants::OAUTH1){
          return $this->OAuth1APICall($baseURL, $queryParameters, $HttpMethod, $requestUri, $requestParameters, $requestBody, $throwExceptionOnError);
        } else if ($oMode == CoreConstants::OAUTH2){
          return $this->OAuth2APICall($baseURL, $queryParameters, $HttpMethod, $requestUri, $requestParameters, $requestBody, $throwExceptionOnError);
        } else{
           throw new SdkException("OAuth Mode not supported.");
        }
    }

    /**
     * The API call to generate OAuth 1 signatures and make API call
     *
     * @param  String           $baseURL               The request url without queryParameters
     * @param  Array            $queryParameters       A list of query parameters
     * @param  String           $HttpMethod            POST or GET
     * @param  String           $requestUri            The Complete HTTP request URI
     * @param  Array            $requestParameters     The Complete HTTP request URI
     * @param  String           $requestBody           The request body for POST request.
     * @param  Boolean          $throwExceptionOnError If throw an exception whent he return http status is not 200. Default is false
     *
     * @return Response and HTTP Status code
     */
    private function OAuth1APICall($baseURL, $queryParameters, $HttpMethod, $requestUri, $requestParameters, $requestBody, $throwExceptionOnError){
      $AuthorizationHeader = $this->getOAuth1AuthorizationHeader($baseURL, $queryParameters, $HttpMethod);
      $httpHeaders = $this->setCommonHeadersForPHPSDK($AuthorizationHeader, $requestUri, $requestParameters->ContentType, $requestBody);
      // Log Request Body to a file
      $this->LogAPIRequestToLog($requestBody, $requestUri, $httpHeaders);
      $intuitResponse = $this->httpClientInterface->makeAPICall($requestUri, $HttpMethod, $httpHeaders, $requestBody, null, false);
      $faultHandler = $intuitResponse->getFaultHandler();
      $this->LogAPIResponseToLog($intuitResponse->getBody(), $requestUri, $intuitResponse->getHeaders());
      //Based on the ducomentation, the fetch expected HTTP/1.1 20X or a redirect. If not, any 3xx, 4xx or 5xx will throw an OAuth Exception
      //for 3xx without direct, it will throw a 503 code and error saying: Invalid protected resource url, unable to generate signature base string
      if($faultHandler) {
          if($throwExceptionOnError == true){
              throw new ServiceException("Request is not made successful. Body: [" . $faultHandler->getResponseBody() . "].", $faultHandler->getHttpStatusCode());
          }else{
              $this->faultHandler = $faultHandler;
              return null;
          }
      }else{
         $this->faultHandler = false;
      }
      return array($intuitResponse->getStatusCode(),$intuitResponse->getBody());
    }

    /**
     * Get OAuth1 Authroization Header based on Query Parameters, BaseURL
     * @param String   $baseURL            The baseURL without queryParameters
     * @param Array    $queryParameters    The queryParameters list from the complete URI
     * @param String   $httpMethod         POST or GET
     * @return OAuth1 Authorization Header
     */
    private function getOAuth1AuthorizationHeader($baseURL, $queryParameters, $HttpMethod){
      $oauth1 = new OAuth1(
        $this->context->requestValidator->ConsumerKey,
        $this->context->requestValidator->ConsumerSecret,
        $this->context->requestValidator->AccessToken,
        $this->context->requestValidator->AccessTokenSecret
      );

      $AuthorizationHeader = $oauth1->getOAuthHeader($baseURL, $queryParameters, $HttpMethod);
      return $AuthorizationHeader;
    }

    /**
     * The OAuth 2 API call
     *
     * @param  String           $baseURL               The request url without queryParameters
     * @param  Array            $queryParameters       A list of query parameters
     * @param  String           $HttpMethod            POST or GET
     * @param  String           $requestUri            The Complete HTTP request URI
     * @param  Array            $requestParameters     The Complete HTTP request URI
     * @param  String           $requestBody           The request body for POST request.
     * @param  Boolean          $throwExceptionOnError If throw an exception whent he return http status is not 200. Default is false
     *
     * @return array|null Response and HTTP Status code
     */
    private function OAuth2APICall($baseURL, $queryParameters, $HttpMethod, $requestUri, $requestParameters, $requestBody, $throwExceptionOnError){
        $AuthorizationHeader = $this->getOAuth2AuthorizationHeader($this->context->requestValidator);
        //We only support QBO for PHP SDK. No QBD support, change
        // from: if ('QBO'==$this->context->serviceType || 'QBD'==$this->context->serviceType)
        if (CoreConstants::IntuitServicesTypeQBO ==$this->context->serviceType) {
            // IDS call
            $httpHeaders = $this->setCommonHeadersForPHPSDK($AuthorizationHeader, $requestUri, $requestParameters->ContentType, $requestBody);
            // Log Request Body to a file
            $this->LogAPIRequestToLog($requestBody, $requestUri, $httpHeaders);
        } else {
             throw new SdkException("IPP or other Call is not supported in OAuth2 Mode.");
        }

        $intuitResponse = $this->httpClientInterface->makeAPICall($requestUri, $HttpMethod, $httpHeaders,  $requestBody, null, true);
        $faultHandler = $intuitResponse->getFaultHandler();
        $this->LogAPIResponseToLog($intuitResponse->getBody(), $requestUri, $intuitResponse->getHeaders());
        //Based on the ducomentation, the fetch expected HTTP/1.1 20X or a redirect. If not, any 3xx, 4xx or 5xx will throw an OAuth Exception
        //for 3xx without direct, it will throw a 503 code and error saying: Invalid protected resource url, unable to generate signature base string
        if($faultHandler) {
            if($throwExceptionOnError == true){
                throw new ServiceException("Request is not made successful. Response Code:[" . $faultHandler->getHttpStatusCode() . "] with body: [" . $faultHandler->getResponseBody() . "].", $faultHandler->getHttpStatusCode());
            }else{
                $this->faultHandler = $faultHandler;
                return null;
            }
        }else{
            $this->faultHandler = false;
        }
        return array($intuitResponse->getStatusCode(),$intuitResponse->getBody());
    }

    /**
     * Get OAuth2 Authroization Header based on OAuth 2 Access Token
     *
     * @param OAuth2AccessToken   $OAuth2AccessToken     The OAuth 2 token contains AccessToken, RefreshToken, ClientID and Client Secret.
     * @return string OAuth2 Authorization Header
     */
    private function getOAuth2AuthorizationHeader($OAuth2AccessToken){
      if(!$OAuth2AccessToken instanceof OAuth2AccessToken){
         throw new SdkException("Internal Error. The OAuth 2 configuration is not complete.");
      }

      $accessToken = $OAuth2AccessToken->getAccessToken();
      $AuthorizationHeader = "Bearer " . $accessToken;
      return $AuthorizationHeader;
    }

    /**
     * Set the Common Headers for PHP SDK. It is used in all HTTP Request
     * @param  String $AuthorizationHeader    The authorizationHeader
     * @param  String $requestUri             The request URI
     * @param  String $ContentType            The content type
     * @param  String $requestBody            The request Body
     *
     * @return array theStandard HTTP Header
     */
    private function setCommonHeadersForPHPSDK($AuthorizationHeader, $requestUri, $ContentType, $requestBody){
      $httpHeaders = array(
          'Authorization' => $AuthorizationHeader,
          'host'          => parse_url($requestUri, PHP_URL_HOST),
          'user-agent'    => CoreConstants::USERAGENT,
          'accept'        => $this->getAcceptContentType($ContentType),
          'connection'    => 'close',
          'content-type'  => $ContentType,
          'content-length'=> strlen($requestBody)
      );

      return $httpHeaders;
    }

    /**
     * Log API Reponse to the Log directory that user specified.
     * @param String $body The requestBody
     * @param String $requestUri  The URI for this request
     * @param Array $httpHeaders  The headers for the request
     */
    public function LogAPIResponseToLog($body, $requestUri, $httpHeaders){
      if(strcasecmp($httpHeaders[CoreConstants::CONTENT_TYPE], CoreConstants::CONTENTTYPE_APPLICATIONXML) == 0 ||
          strcasecmp($httpHeaders[CoreConstants::CONTENT_TYPE], CoreConstants::CONTENTTYPE_APPLICATIONXML_WITH_CHARSET) == 0){
             $body = $this->parseStringToDom($body);
      }

      $this->RequestLogging->LogPlatformRequests($body, $requestUri, $httpHeaders, false);
    }

    /**
     * Log API Request to the Log directory that user specified.
     * @param String $requestBody The requestBody
     * @param String $requestUri  The URI for this request
     * @param Array $httpHeaders  The headers for the request
     */
    public function LogAPIRequestToLog($requestBody, $requestUri, $httpHeaders){
      $this->RequestLogging->LogPlatformRequests($requestBody, $requestUri, $httpHeaders, true);

      if ($requestBody && $this->RequestCompressor) {
          $this->RequestCompressor->Compress($httpHeaders, $requestBody);
      }

      if ($this->ResponseCompressor) {
          $this->ResponseCompressor->PrepareDecompress($httpHeaders);
      }
    }

    /**
     * Parse a String xml to DOM structure for easy read
     * @param String $string   The String representation
     * @return string|bool The DOM structured XML
     */
    private function parseStringToDom($string){
      $dom = new \DOMDocument();
      $dom->preserveWhiteSpace = FALSE;
      $dom->loadXML($string);
      $dom->formatOutput = TRUE;
      return $dom->saveXml();
    }

    /**
     * This step is required since the configuration settings might have been changed.
     */
    private function resetCompressorAndSerializer(){
      $this->RequestCompressor = CoreHelper::GetCompressor($this->context, true);
      $this->ResponseCompressor = CoreHelper::GetCompressor($this->context, false);
      $this->RequestSerializer = CoreHelper::GetSerializer($this->context, true);
      $this->ResponseSerializer = CoreHelper::GetSerializer($this->context, false);
    }

    /**
     * Based on the request determine the URL Endpoint
     * @param  RequestParameters $requestParameters    The request parameter for this API Call
     * @param  String           $oMode                The OAuth mode for the request
     * @param  String           $specifiedRequestUri  Ignore the rule, use the user specified URI for the request
     * @return String           Destination URL for the request
     */
    private function getDestinationURL($requestParameters, $oMode, $specifiedRequestUri){
      // For Platform Discconect/Reconenct call, only for OAuth 1
      if (isset($requestParameters->ApiName)) {
          if(strcasecmp($oMode, CoreConstants::OAUTH1) == 0)
          {
            // Example: "https://appcenter.intuit.com/api/v1/Account/AppMenu"
            $requestUri = $this->context->baseserviceURL . $requestParameters->ApiName;
          }else{
            throw new SdkException("Disconnect/Reconnect Platform Call is only available in OAuth 1.0.");
          }
      } elseif (isset($specifiedRequestUri)) {
          // Prepare the request Uri from base Uri and resource Uri.
          $requestUri = $specifiedRequestUri;
      } elseif (isset($requestParameters->ResourceUri)) {
          $requestUri = $this->context->baseserviceURL . $requestParameters->ResourceUri;
      } else {
          throw new SdkException("Internal Error. UnSpecified URI Type for sending request.");
      }

      return $requestUri;
    }

    /**
     * Append Minor Version to the URI
     *
     * @param String requestUri
     * @return String requestUri with Minor Version Appended
     */
    private function appendMinorVersionToRequestURI($requestUri){
      $setMinorVersion = $this->context->minorVersion;
      if (isset($setMinorVersion)) {
          if ($this->queryToArray($requestUri) == false) { //if no query string params
              $requestUri .= "?minorversion=" . $this->context->minorVersion;
          } else {
              $requestUri .= "&minorversion=" . $this->context->minorVersion;
          }
      }
      return $requestUri;
    }

    /**
     * Check The HTTP MEthod from the requestParameters, make sure it is either GET or POST
     *
     * @param RequestParameters  RequestParameters  The requestParameters for the request
     * @return String HTTP Method
     */
    private function checkHTTPMethod($requestParameters){
      $verb = $requestParameters->HttpVerbType;
      if(strcasecmp($verb, CoreConstants::HTTP_POST) == 0){
         return CoreConstants::HTTP_POST;
      } else if(strcasecmp($verb, CoreConstants::HTTP_GET) == 0){
         return CoreConstants::HTTP_GET;
      } else{
         throw new SdkException("Internal Error. Unsupported HTTP Method:" . $verb);
      }
    }

    /**
     * Get the base URL.
     *
     * @param String  $url  The complete URL for the request
     * @return String       The baseURL.
     */
    private function getBaseURL($url){
      return strtok($url, '?');
    }

    /**
     * Get the query parameters from the complete URL, used for sign signature for OAuth 1.
     *
     * @param String  $url  The $url for the request
     * @return Array  a list of query paramters.
     */
    private function parseURL($url){
       $query_str = parse_url($url, PHP_URL_QUERY);
       parse_str($query_str, $parameters);
       return $parameters;
    }

  /**
   * Accept anything if content type is not XML or Json
   * @param string $value
   * @return string
   */
   private function getAcceptContentType($value)
   {
       if (CoreConstants::CONTENTTYPE_APPLICATIONXML === $value) {
           return $value;
       }

       if (CoreConstants::CONTENTTYPE_APPLICATIONJSON === $value) {
           return $value;
       }

       if (CoreConstants::CONTENTTYPE_APPLICATIONPDF === $value) {
           return $value;
       }

       if (CoreConstants::CONTENTTYPE_OCTETSTREAM === $value) {
           if ($this->ResponseSerializer instanceof XmlObjectSerializer) {
               return CoreConstants::CONTENTTYPE_APPLICATIONXML;
           }

           if ($this->ResponseSerializer instanceof JsonObjectSerializer) {
               return CoreConstants::CONTENTTYPE_APPLICATIONJSON;
           }
       }

       return "*/*";
   }

   /**
    * A helper function to convert Query to Array
    * @param String $qry   The query String
    * @return  False | Array   The result
    */
    private function queryToArray($qry)
    {
        $result = array();
                //string must contain at least one = and cannot be in first position
                if (strpos($qry, '=')) {
                    if (strpos($qry, '?')!==false) {
                        $q = parse_url($qry);
                        $qry = $q['query'];
                    }
                } else {
                    return false;
                }

        foreach (explode('&', $qry) as $couple) {
            list($key, $val) = explode('=', $couple);
            $result[$key] = $val;
        }

        return empty($result) ? false : $result;
    }

    /**
     * Calls the rest service.
     *
     * @param RequestParameters $requestParameters The parameters
     * @param string $requestBody The request body
     * @param string $oauthRequestUri The OAuth request uri
     * @return array elements are 0: HTTP response code; 1: HTTP response body
     * @deprecated
     */
    private function CallRestService($requestParameters, $requestBody, $oauthRequestUri)
    {
        throw new \BadMethodCallException('Function has been removed');
    }

    /**
     * Returns the response by calling REST service.
     * deprecated on Version 2.6.0. Need to refactor @Hao
     *
     * @param RequestParameters $requestParameters The parameters
     * @param string $requestBody The request body
     * @param string $oauthRequestUri The OAuth request uri
     * @deprecated
     */
    public function GetResponseSyncRest($requestParameters, $requestBody, $oauthRequestUri)
    {
        $handler = new FaultHandler($this->context);

        // Create a variable for storing the response.
        $response = '';
        $responseCode = null;
        try {
            // Check whether the retryPolicy is null.
            if ($this->context->IppConfiguration->RetryPolicy == null) {
                // If yes then call the rest service without retry framework enabled.
                list($responseCode, $response) = $this->CallRestService($requestParameters, $requestBody, $oauthRequestUri);
            } else {
                // Not yet implemented
                throw new IdsException("Retry policy not available in this SDK");

                // If no then call the rest service using the execute action of retry framework.
                // $this->context->IppConfiguration->RetryPolicy->ExecuteAction(() =>
                // {
                //     $response = $this->CallRestService($requestParameters, $requestBody, $oauthRequestUri);
                // });
            }
        } catch (\Exception $webException) {
            // System.Net.HttpWebRequest.Abort() was previously called.-or- The time-out
            // period for the request expired.-or- An error occurred while processing the request.
            $isIpp = false;
            if ($this->context->ServiceType == CoreConstants::IntuitServicesTypeIPP) {
                $isIpp = true;
            }

            $idsException = $handler->ParseResponseAndThrowException($webException, $responseCode, $isIpp);
            if ($idsException != null) {
                $this->context->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, $idsException->getMessage());
                throw $idsException;
            }
        }

        if ($this->context->ServiceType == CoreConstants::IntuitServicesTypeIPP) {
            // Handle errors here
            IntuitErrorHandler::HandleErrors($response);
        } else {
            // Check the response if there are any fault tags and throw appropriate exceptions.
            $oneException = $handler->ParseErrorResponseAndPrepareException($response);
            if ($oneException != null) {
                throw $oneException;
            }
        }

        // Return the response.
        return $response;
    }
}


<?php

namespace QuickBooksOnline\API\WebhooksService;

final class TokenVerifier
{
    /**
     * The encryption algorithm using for TokenVerifier
     */
    const HASH_ALGORITHM = 'SHA256';

    /**
     * The verfier Token for Wehbook assigned by Intuit
     *
     * @var null
     */
    private $verifierToken = null;

    /**
     * Private TokenVerifier constructor.
     * @param $token
     */
    public function __construct($token)
    {
        $this->verifierToken = $token;
    }

    /**
     * Using the token to encrypt payload and compare with sig
     *
     * @param $payLoad
     *     The payload to be verified
     * @param $sig
     *      The signature retrieved from header
     * @param null $algo
     *      The algo used to encrypt
     * @return bool
     *      True if same false otherwise
     */

    public function verifyPayLoad($payLoad, $sig, $algo = null)
    {
        $encryptedPayload = $this->encryptPayLoadBasedOnToken($payLoad, $algo);
        if (strcmp($sig, $encryptedPayload) == 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Encrypt the payload based on hash algorithm and verifier Token
     *
     * @param $payLoad
     * @param null $hashAlgorithm
     * @return string
     */
    private function encryptPayLoadBasedOnToken($payLoad, $hashAlgorithm = null)
    {
        if ($hashAlgorithm == null) {
            $hashAlgorithm = TokenVerifier::HASH_ALGORITHM;
        }
        $hashedPayLoad = hash_hmac($hashAlgorithm, $payLoad, $this->verifierToken);
        $encodedHashedPayLoad = base64_encode(hex2bin($hashedPayLoad));
        return $encodedHashedPayLoad;
    }
}


<?php
namespace QuickBooksOnline\API\Facades;

class Employee{

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

    /**
     * This is an immutable function
     */
    public static function update($objToUpdate, array $data){
        $classOfObj = get_class($objToUpdate);
        if(strcmp($classOfObj, FacadeHelper::simpleAppendClassNameSpace("Employee")) != 0){
            throw new \Exception("Target object class:{" .  $classOfObj . "} is not an instance of Employee.");
        }
        $newEmployeeObj = Employee::create($data);
        $clonedOfObj = FacadeHelper::cloneObj($objToUpdate);
        FacadeHelper::mergeObj($clonedOfObj, $newEmployeeObj);
        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\Core\Http\Serialization\XmlObjectSerializer;
use QuickBooksOnline\API\Exception\IdsExceptionManager;
use QuickBooksOnline\API\Exception\IdsException;
use QuickBooksOnline\API\Exception\IdsError;
use QuickBooksOnline\API\Exception\ValidationException;
use QuickBooksOnline\API\Exception\ServiceException;
use QuickBooksOnline\API\Exception\SecurityException;
use QuickBooksOnline\API\Core\CoreHelper;
use QuickBooksOnline\API\Core\ServiceContext;
use QuickBooksOnline\API\Core\HttpClients\FaultHandler;
use QuickBooksOnline\API\Core\HttpClients\RestHandler;
use QuickBooksOnline\API\Data\IPPBatchItemRequest;
use QuickBooksOnline\API\Data\IPPIntuitBatchRequest;
use QuickBooksOnline\API\Core\CoreConstants;
use QuickBooksOnline\API\Core\HttpClients\SyncRestHandler as RestServiceSyncRestHandler;
use QuickBooksOnline\API\Diagnostics\TraceLevel;
use QuickBooksOnline\API\Core\HttpClients\RequestParameters;
use QuickBooksOnline\API\Utility\UtilityConstants;
use \QuickBooksOnline\API\Core\Http\Serialization\IEntitySerializer;

/**
 * This class contains code for Batch Processing.
 */
class Batch
{

    /**
     * batch requests
     * @var array batchRequests
     */
    private $batchRequests;

    /**
     * batch responses
     * @deprecated
     * @var array batchResponses
     */
    private $batchResponses;

    /**
     * Intuit batch item responses list.
     * @var array batchResponses
     */
    public $intuitBatchItemResponses;

    /**
     * service context object.
     * @var ServiceContext serviceContext
     */
    private $serviceContext;

    /**
     * rest handler object.
     * @var RestHandler restHandler
     */
    private $restHandler;

    /**
     * serializer to be used.
     * @var IEntitySerializer responseSerializer
     */
    private $responseSerializer;

    /**
    * If not false, the request from last dataService did not return 2xx
    * @var FaultHandler
    */
    private $lastError = false;

    /**
    * Throw Exception on Error or not. Default is false.
    * @var boolean
    */
    private $isThrowExceptionOnError = false;

    /**
    * Get the error from last request
    * @return FaultHandler
    */
    public function getLastError()
    {
        return $this->lastError;
    }

    /**
     * Initializes a new instance of the Batch class.
     * @param $serviceContext The service context.
     * @param $restHandler The rest handler.
     */
    public function __construct($serviceContext, $restHandler, $isThrowExceptionOnError)
    {
        $this->serviceContext = $serviceContext;
        $this->restHandler = $restHandler;
        $this->isThrowExceptionOnError = $isThrowExceptionOnError;
        $this->responseSerializer = CoreHelper::GetSerializer($this->serviceContext, false);
        $this->batchRequests = array();
        $this->batchResponses = array();
        $this->intuitBatchItemResponses = array();
    }

    /**
     * Gets the count.
     * @return int count
     */
    public function Count()
    {
        return count($this->batchRequests);
    }

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

    /**
     * Gets the IntuitBatchResponse with the specified id.
     * @param string $id unique batchitem id
     */
    public function IntuitBatchResponse($id)
    {
        if(array_key_exists($id, $this->intuitBatchItemResponses)){
            return $this->intuitBatchItemResponses[$id];
        }else{
            return null;
        }
    }

    /**
     * Adds the specified query.
     * @param string $query IDS query.
     * @param string $id unique batchitem id.
     */
    public function AddQuery($query, $id)
    {
        if (!$query) {
            $exception = new IdsException('StringParameterNullOrEmpty: query');
            IdsExceptionManager::HandleException($exception);
        }

        if (!$id) {
            $exception = new IdsException('StringParameterNullOrEmpty: id');
            IdsExceptionManager::HandleException($exception);
        }

        if (count($this->batchRequests)>25) {
            $exception = new IdsException('BatchItemsExceededException');
            IdsExceptionManager::HandleException($exception);
        }

        $batchItem = new IPPBatchItemRequest();
        $batchItem->Query = $query;
        $batchItem->bId = $id;
        $batchItem->operationSpecified = true;
        //$batchItem->ItemElementName = ItemChoiceType6::Query;
        $this->batchRequests[] = $batchItem;
    }

    /**
     * Adds the specified query.
     * @param IEntity entity entitiy for the batch operation.
     * @param string id Unique batchitem id
     * @param OperationEnum operation operation to be performed for the entity.
     */
     public function AddEntity($entity, $id, $operation)
     {
         if (!$entity) {
             $exception = new IdsException('StringParameterNullOrEmpty: entity');
             IdsExceptionManager::HandleException($exception);
         }

         if (!$id) {
             $exception = new IdsException('StringParameterNullOrEmpty: id');
             IdsExceptionManager::HandleException($exception);
         }

         if (!$operation) {
             $exception = new IdsException('StringParameterNullOrEmpty: operation');
             IdsExceptionManager::HandleException($exception);
         }

         foreach ($this->batchRequests as $oneBatchRequest) {
             if ($oneBatchRequest->bId == $id) {
                 $exception = new IdsException('BatchIdAlreadyUsed');
                 IdsExceptionManager::HandleException($exception);
             }
         }

         $batchItem = new IPPBatchItemRequest();
         $batchItem->IntuitObject = $entity;
         $batchItem->bId = $id;
         $batchItem->operation = $operation;
         $batchItem->operationSpecified = true;

         $this->batchRequests[] = $batchItem;
     }

    /**
     * Removes batchitem with the specified batchitem id.
     * @param string id unique batchitem id
     */
    public function Remove($id)
    {
        if (!$id) {
            $exception = new IdsException('BatchItemIdNotFound: id');
            IdsExceptionManager::HandleException($exception);
        }

        $revisedBatchRequests = array();
        foreach ($this->batchRequests as $oneBatchRequest) {
            if ($oneBatchRequest->bId == $id) {
                // Exclude
            } else {
                $revisedBatchRequests[] = $oneBatchRequest;
            }
        }
        $this->batchRequests = $revisedBatchRequests;
    }

    /**
     * Remove all the batchitem requests.
     */
    public function RemoveAll()
    {
        $this->batchRequests = array();
    }

    /**
     * This method executes the batch request.
     */
    public function Execute()
    {
       $requestID = rand() . rand();
       $this->sendRequest($requestID);
    }

    /**
     * Use this function to do Batch Request instead of Execute()
     */
    public function ExecuteWithRequestID($requestID)
    {
      if(isset($requestID) && !empty($requestID)){
          $this->sendRequest($requestID);
      }else{
        throw new \Exception("ExecuteWithRequestID called with Empty or Null request ID");
      }

    }

    private function sendRequest($requestID)
    {
      $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Started Executing Method Execute for Batch");

      // Create Intuit Batch Request
      $intuitBatchRequest = new IPPIntuitBatchRequest();
      $intuitBatchRequest->BatchItemRequest = $this->batchRequests;
      $uri = "company/{1}/batch?requestid=" . $requestID;
      $uri = str_replace('{1}', $this->serviceContext->realmId, $uri);

      // Creates request parameters
      $requestParameters = new RequestParameters($uri, 'POST', CoreConstants::CONTENTTYPE_APPLICATIONXML, null);

      $restRequestHandler = $this->getRestHandler();
      try {
          // Get literal XML representation of IntuitBatchRequest into a DOMDocument
          $httpsPostBodyPreProcessed = XmlObjectSerializer::getPostXmlFromArbitraryEntity($intuitBatchRequest, $urlResource);
          $doc = new \DOMDocument();
          $domObj = $doc->loadXML($httpsPostBodyPreProcessed);
          $xpath = new \DOMXPath($doc);

          // Replace generically-named IntuitObject nodes with tags that describe contained objects
          $objectIndex = 0;
          while (1) {
              $matchingElementArray = $xpath->query("//IntuitObject");
              if (is_null($matchingElementArray)) {
                  break;
              }

              if ($objectIndex>=count($intuitBatchRequest->BatchItemRequest)) {
                  break;
              }

              foreach ($matchingElementArray as $oneNode) {

                  // Found a DOMNode currently named "IntuitObject".  Need to rename to
                  // entity that describes it's contents, like "ns0:Customer" (determine correct
                  // name by inspecting IntuitObject's class).
                  if ($intuitBatchRequest->BatchItemRequest[$objectIndex]->IntuitObject) {
                      // Determine entity name to use
                      $entityClassName = get_class($intuitBatchRequest->BatchItemRequest[$objectIndex]->IntuitObject);
                      $entityTransferName = XmlObjectSerializer::cleanPhpClassNameToIntuitEntityName($entityClassName);
                      $entityTransferName = 'ns0:'.$entityTransferName;

                      // Replace old-named DOMNode with new-named DOMNode
                      $newNode = $oneNode->ownerDocument->createElement($entityTransferName);
                      if ($oneNode->attributes->length) {
                          foreach ($oneNode->attributes as $attribute) {
                              $newNode->setAttribute($attribute->nodeName, $attribute->nodeValue);
                          }
                      }
                      while ($oneNode->firstChild) {
                          $newNode->appendChild($oneNode->firstChild);
                      }
                      $oneNode->parentNode->replaceChild($newNode, $oneNode);
                  }
                  break;
              }
              $objectIndex++;
          }
          $httpsPostBody = $doc->saveXML();
          list($responseCode, $responseBody) = $restRequestHandler->sendRequest($requestParameters, $httpsPostBody, null, $this->isThrowExceptionOnError);
          $faultHandler = $restRequestHandler->getFaultHandler();
          if ($faultHandler) {
              $this->lastError = $faultHandler;
              return null;
          }else{
              $this->lastError = false;
          }
      } catch (\Exception $e) {
          IdsExceptionManager::HandleException($e);
      }

      try {
          // No JSON support here yet
          // de serialize object
          $responseXmlObj = simplexml_load_string($responseBody);
          foreach ($responseXmlObj as $oneXmlObj) {
              // process batch item
              $intuitBatchItemResponse = $this->ProcessBatchItemResponse($oneXmlObj);
              $this->intuitBatchItemResponses[$intuitBatchItemResponse->batchItemId] = $intuitBatchItemResponse;
          }
      } catch (\Exception $e) {
          $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "Encountered an error parsing the batch response." . $e->getMessage());
          $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Error, "Stack Trace: " . $e->getTraceAsString());
          return null;
      }

      $this->serviceContext->IppConfiguration->Logger->CustomLogger->Log(TraceLevel::Info, "Finished Execute method for batch.");
    }

    /**
     * Returns handler to communicate with service
     * @return RestHandler
     */
    protected function getRestHandler()
    {
        return $this->restHandler;
    }

    private function verifyFault($fault)
    {
        if ($fault == null) {
            return null;
        }
        if (empty($fault)) {
            return null;
        }
        if (!$fault instanceof \SimpleXMLElement) {
            return null;
        }
        if (!$fault->attributes() instanceof \SimpleXMLElement) {
            return null;
        }
        if (!isset($fault->attributes()->type)) {
            return null;
        }

        return true;
    }

    private function collectErrors($fault)
    {
        $errors = array();
        if (isset($fault->Error)
                && ($fault->Error instanceof \SimpleXMLElement)
                && $fault->Error->count()) {
            foreach ($fault->Error as $item) {
                if (!isset($item->Message)) {
                    continue;
                }
                if (!$item->Message instanceof \SimpleXMLElement) {
                    continue;
                }
                $error = new \stdClass();
                $detail = (string)$item->Detail;
                if($detail) {
                    $error->message = (string)$item->Message . ' - ' . $detail;
                } else {
                    $error->message = (string)$item->Message;
                }
                $error->code = null;
                if ($item->attributes() instanceof \SimpleXMLElement
                            && isset($item->attributes()->code)) {
                    $error->code = (string)$item->attributes()->code;
                }
                $errors[] =$error;
            }
        }
        return $errors;
    }

    /**
     * Helper function for store the error message and code from Fault returned from QuickBooks Online
     */
    private function arrayToMessageAndCode(array $array)
    {
        if (empty($array)) {
            return array(null,null);
        }
        if (1 == count($array)) {
            $item = array_pop($array);
            return array($item->message,$item->code);
        }

        $message = "";
        $code = "";
        foreach ($array as $item) {
            $message .= "Exception: ".$item->message . "\n";
            if (empty($code) && !empty($item->code)) {
                $code = $item->code;
            }
        }
        return array($message,$code);
    }

    /**
     * Prepare IdsException out of Fault object.
     * @param Fault fault Fault object.
     * @return IdsException IdsException object.
     */
     public function IterateFaultAndPrepareException($fault)
     {
         if (!$this->verifyFault($fault)) {
             return null;
         }
            // Collect information from XML entity
            $type = (string)$fault->attributes()->type;
         list($message, $code) = $this->arrayToMessageAndCode($this->collectErrors($fault));
         if (is_null($message)) {
             return new IdsException("Fault Exception of type: " . $type . " has been generated.");
         }
         $idsException = null;

            // Fault types can be of Validation, Service, Authentication and Authorization. Run them through the switch case.
            switch ($type) {
                // If Validation errors iterate the Errors and add them to the list of exceptions.
                case "Validation":
                case "ValidationFault":
                        // Throw specific exception like ValidationException.
                        $idsException = new ValidationException($message, $code);
                    break;
                // If Validation errors iterate the Errors and add them to the list of exceptions.
                case "Service":
                case "ServiceFault":
                        // Throw specific exception like ServiceException.
                        $idsException = new ServiceException($message, $code);
                    break;
                // If Validation errors iterate the Errors and add them to the list of exceptions.
                case "Authentication":
                case "AuthenticationFault":
                case "Authorization":
                case "AuthorizationFault":
                        $idsException = new SecurityException($message, $code);
                    break;
                // Use this as default if there was some other type of Fault
                default:
                        $idsException = new IdsException($message, $code);

            }

        // Return idsException which will be of type Validation, Service or Security.
        return $idsException;
     }

    /**
     * process batch item response
     * @param BatchItemResponse oneXmlObj The batchitem response.
     * @return IntuitBatchResponse IntuitBatchResponse object.
     */
    private function ProcessBatchItemResponse($oneXmlObj)
    {
        $result = new IntuitBatchResponse();
        if (null==$oneXmlObj) {
            return $result;
        }

        if(isset($oneXmlObj["bId"])){
            $bid = (String)$oneXmlObj["bId"];
            $result->batchItemId = $bid;
        }else{
            throw new \Exception("No bid Found on the Batch Response.");
        }

        $firstChild = null;
        foreach ($oneXmlObj->children() as $oneChild) {
            $firstChild = $oneChild;
            break;
        }
        if (!$firstChild) {
            return $result;
        }

        $firstChildName = (string)$firstChild->getName();

        switch ($firstChildName) {
              //For batch query result
              case "QueryResponse":
                  $result->responseType = UtilityConstants::Query;
                  $queryResult = array();
                  foreach ($oneXmlObj->QueryResponse->children() as $oneResponse) {
                      $oneEntity = $this->responseSerializer->Deserialize('<RestResponse>'.$oneResponse->asXML().'</RestResponse>');
                      $queryResult = array_merge($queryResult, $oneEntity);
                  }
                  $result->setEntities($queryResult);
                  $result->successFlagOn();
                  break;
              //For batch failure result
              case "Fault":
                  $result->responseType = UtilityConstants::Exception;
                  $idsException = $this->IterateFaultAndPrepareException($firstChild);
                  $result->exception = $idsException;
                  break;
              //For batch Entity Result
              default:
                  $result->responseType = UtilityConstants::Entity;
                  $oneEntityArray = $this->responseSerializer->Deserialize('<RestResponse>'.$firstChild->asXML().'</RestResponse>');
                  $oneEntity = $oneEntityArray[0];
                  $result->entity = $oneEntity;
                  $result->successFlagOn();
                  break;
        }

        return $result;
    }
}


<?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
 * @subpackage Client
 * @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: Client.php 20096 2010-01-06 02:05:09Z bkarwin $
 */

/**
 * @see Zend_Soap_Server
 */
require_once 'Zend/Soap/Server.php';

/**
 * @see Zend_Soap_Client_Local
 */
require_once 'Zend/Soap/Client/Local.php';

/**
 * @see Zend_Soap_Client_Common
 */
require_once 'Zend/Soap/Client/Common.php';

/**
 * Zend_Soap_Client
 *
 * @category   Zend
 * @package    Zend_Soap
 * @subpackage Client
 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Soap_Client
{
    /**
     * Encoding
     * @var string
     */
    protected $_encoding = 'UTF-8';

    /**
     * Array of SOAP type => PHP class pairings for handling return/incoming values
     * @var array
     */
    protected $_classmap = null;

    /**
     * Registered fault exceptions
     * @var array
     */
    protected $_faultExceptions = array();

    /**
     * SOAP version to use; SOAP_1_2 by default, to allow processing of headers
     * @var int
     */
    protected $_soapVersion = SOAP_1_2;

    /** Set of other SoapClient options */
    protected $_uri                 = null;
    protected $_location            = null;
    protected $_style               = null;
    protected $_use                 = null;
    protected $_login               = null;
    protected $_password            = null;
    protected $_proxy_host          = null;
    protected $_proxy_port          = null;
    protected $_proxy_login         = null;
    protected $_proxy_password      = null;
    protected $_local_cert          = null;
    protected $_passphrase          = null;
    protected $_compression         = null;
    protected $_connection_timeout  = null;
    protected $_stream_context      = null;
    protected $_features            = null;
    protected $_cache_wsdl          = null;
    protected $_user_agent          = null;

    /**
     * WSDL used to access server
     * It also defines Zend_Soap_Client working mode (WSDL vs non-WSDL)
     *
     * @var string
     */
    protected $_wsdl = null;

    /**
     * SoapClient object
     *
     * @var SoapClient
     */
    protected $_soapClient;

    /**
     * Last invoked method
     *
     * @var string
     */
    protected $_lastMethod = '';

    /**
     * SOAP request headers.
     *
     * Array of SoapHeader objects
     *
     * @var array
     */
    protected $_soapInputHeaders = array();

    /**
     * Permanent SOAP request headers (shared between requests).
     *
     * Array of SoapHeader objects
     *
     * @var array
     */
    protected $_permanentSoapInputHeaders = array();

    /**
     * Output SOAP headers.
     *
     * Array of SoapHeader objects
     *
     * @var array
     */
    protected $_soapOutputHeaders = array();

    /**
     * Constructor
     *
     * @param string $wsdl
     * @param array $options
     */
    public function __construct($wsdl = null, $options = null)
    {
        if (!extension_loaded('soap')) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('SOAP extension is not loaded.');
        }

        if ($wsdl !== null) {
            $this->setWsdl($wsdl);
        }
        if ($options !== null) {
            $this->setOptions($options);
        }
    }

    /**
     * Set wsdl
     *
     * @param string $wsdl
     * @return Zend_Soap_Client
     */
    public function setWsdl($wsdl)
    {
        $this->_wsdl = $wsdl;
        $this->_soapClient = null;

        return $this;
    }

    /**
     * Get wsdl
     *
     * @return string
     */
    public function getWsdl()
    {
        return $this->_wsdl;
    }

    /**
     * Set Options
     *
     * Allows setting options as an associative array of option => value pairs.
     *
     * @param  array|Zend_Config $options
     * @return Zend_Soap_Client
     * @throws Zend_SoapClient_Exception
     */
    public function setOptions($options)
    {
        if ($options instanceof Zend_Config) {
            $options = $options->toArray();
        }

        foreach ($options as $key => $value) {
            switch ($key) {
                case 'classmap':
                case 'classMap':
                    $this->setClassmap($value);
                    break;
                case 'encoding':
                    $this->setEncoding($value);
                    break;
                case 'soapVersion':
                case 'soap_version':
                    $this->setSoapVersion($value);
                    break;
                case 'wsdl':
                    $this->setWsdl($value);
                    break;
                case 'uri':
                    $this->setUri($value);
                    break;
                case 'location':
                    $this->setLocation($value);
                    break;
                case 'style':
                    $this->setStyle($value);
                    break;
                case 'use':
                    $this->setEncodingMethod($value);
                    break;
                case 'login':
                    $this->setHttpLogin($value);
                    break;
                case 'password':
                    $this->setHttpPassword($value);
                    break;
                case 'proxy_host':
                    $this->setProxyHost($value);
                    break;
                case 'proxy_port':
                    $this->setProxyPort($value);
                    break;
                case 'proxy_login':
                    $this->setProxyLogin($value);
                    break;
                case 'proxy_password':
                    $this->setProxyPassword($value);
                    break;
                case 'local_cert':
                    $this->setHttpsCertificate($value);
                    break;
                case 'passphrase':
                    $this->setHttpsCertPassphrase($value);
                    break;
                case 'compression':
                    $this->setCompressionOptions($value);
                    break;
                case 'stream_context':
                    $this->setStreamContext($value);
                    break;
                case 'features':
                    $this->setSoapFeatures($value);
                    break;
                case 'cache_wsdl':
                    $this->setWsdlCache($value);
                    break;
                case 'useragent':
                case 'userAgent':
                case 'user_agent':
                    $this->setUserAgent($value);
                    break;

                // Not used now
                // case 'connection_timeout':
                //     $this->_connection_timeout = $value;
                //    break;

                default:
                    require_once 'Zend/Soap/Client/Exception.php';
                    throw new Zend_Soap_Client_Exception('Unknown SOAP client option');
                    break;
            }
        }

        return $this;
    }

    /**
     * Return array of options suitable for using with SoapClient constructor
     *
     * @return array
     */
    public function getOptions()
    {
        $options = array();

        $options['classmap']       = $this->getClassmap();
        $options['encoding']       = $this->getEncoding();
        $options['soap_version']   = $this->getSoapVersion();
        $options['wsdl']           = $this->getWsdl();
        $options['uri']            = $this->getUri();
        $options['location']       = $this->getLocation();
        $options['style']          = $this->getStyle();
        $options['use']            = $this->getEncodingMethod();
        $options['login']          = $this->getHttpLogin();
        $options['password']       = $this->getHttpPassword();
        $options['proxy_host']     = $this->getProxyHost();
        $options['proxy_port']     = $this->getProxyPort();
        $options['proxy_login']    = $this->getProxyLogin();
        $options['proxy_password'] = $this->getProxyPassword();
        $options['local_cert']     = $this->getHttpsCertificate();
        $options['passphrase']     = $this->getHttpsCertPassphrase();
        $options['compression']    = $this->getCompressionOptions();
        //$options['connection_timeout'] = $this->_connection_timeout;
        $options['stream_context'] = $this->getStreamContext();
        $options['cache_wsdl']     = $this->getWsdlCache();
        $options['features']       = $this->getSoapFeatures();
        $options['user_agent']     = $this->getUserAgent();

        foreach ($options as $key => $value) {
            /*
             * ugly hack as I don't know if checking for '=== null'
             * breaks some other option
             */
            if ($key == 'user_agent') {
                if ($value === null) {
                    unset($options[$key]);
                }
            } else {
                if ($value == null) {
                    unset($options[$key]);
                }
            }
        }

        return $options;
    }

    /**
     * Set SOAP version
     *
     * @param  int $version One of the SOAP_1_1 or SOAP_1_2 constants
     * @return Zend_Soap_Client
     * @throws Zend_Soap_Client_Exception with invalid soap version argument
     */
    public function setSoapVersion($version)
    {
        if (!in_array($version, array(SOAP_1_1, SOAP_1_2))) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('Invalid soap version specified. Use SOAP_1_1 or SOAP_1_2 constants.');
        }
        $this->_soapVersion = $version;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Get SOAP version
     *
     * @return int
     */
    public function getSoapVersion()
    {
        return $this->_soapVersion;
    }

    /**
     * Set classmap
     *
     * @param  array $classmap
     * @return Zend_Soap_Client
     * @throws Zend_Soap_Client_Exception for any invalid class in the class map
     */
    public function setClassmap(array $classmap)
    {
        foreach ($classmap as $type => $class) {
            if (!class_exists($class)) {
                require_once 'Zend/Soap/Client/Exception.php';
                throw new Zend_Soap_Client_Exception('Invalid class in class map');
            }
        }

        $this->_classmap = $classmap;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Retrieve classmap
     *
     * @return mixed
     */
    public function getClassmap()
    {
        return $this->_classmap;
    }

    /**
     * Set encoding
     *
     * @param  string $encoding
     * @return Zend_Soap_Client
     * @throws Zend_Soap_Client_Exception with invalid encoding argument
     */
    public function setEncoding($encoding)
    {
        if (!is_string($encoding)) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('Invalid encoding specified');
        }

        $this->_encoding = $encoding;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Get encoding
     *
     * @return string
     */
    public function getEncoding()
    {
        return $this->_encoding;
    }

    /**
     * Check for valid URN
     *
     * @param  string $urn
     * @return true
     * @throws Zend_Soap_Client_Exception on invalid URN
     */
    public function validateUrn($urn)
    {
        $scheme = parse_url($urn, PHP_URL_SCHEME);
        if ($scheme === false || $scheme === null) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('Invalid URN');
        }

        return true;
    }

    /**
     * Set URI
     *
     * URI in Web Service the target namespace
     *
     * @param  string $uri
     * @return Zend_Soap_Client
     * @throws Zend_Soap_Client_Exception with invalid uri argument
     */
    public function setUri($uri)
    {
        $this->validateUrn($uri);
        $this->_uri = $uri;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Retrieve URI
     *
     * @return string
     */
    public function getUri()
    {
        return $this->_uri;
    }

    /**
     * Set Location
     *
     * URI in Web Service the target namespace
     *
     * @param  string $location
     * @return Zend_Soap_Client
     * @throws Zend_Soap_Client_Exception with invalid uri argument
     */
    public function setLocation($location)
    {
        $this->validateUrn($location);
        $this->_location = $location;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Retrieve URI
     *
     * @return string
     */
    public function getLocation()
    {
        return $this->_location;
    }

    /**
     * Set request style
     *
     * @param  int $style One of the SOAP_RPC or SOAP_DOCUMENT constants
     * @return Zend_Soap_Client
     * @throws Zend_Soap_Client_Exception with invalid style argument
     */
    public function setStyle($style)
    {
        if (!in_array($style, array(SOAP_RPC, SOAP_DOCUMENT))) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('Invalid request style specified. Use SOAP_RPC or SOAP_DOCUMENT constants.');
        }

        $this->_style = $style;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Get request style
     *
     * @return int
     */
    public function getStyle()
    {
        return $this->_style;
    }

    /**
     * Set message encoding method
     *
     * @param  int $use One of the SOAP_ENCODED or SOAP_LITERAL constants
     * @return Zend_Soap_Client
     * @throws Zend_Soap_Client_Exception with invalid message encoding method argument
     */
    public function setEncodingMethod($use)
    {
        if (!in_array($use, array(SOAP_ENCODED, SOAP_LITERAL))) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('Invalid message encoding method. Use SOAP_ENCODED or SOAP_LITERAL constants.');
        }

        $this->_use = $use;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Get message encoding method
     *
     * @return int
     */
    public function getEncodingMethod()
    {
        return $this->_use;
    }

    /**
     * Set HTTP login
     *
     * @param  string $login
     * @return Zend_Soap_Client
     */
    public function setHttpLogin($login)
    {
        $this->_login = $login;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Retrieve HTTP Login
     *
     * @return string
     */
    public function getHttpLogin()
    {
        return $this->_login;
    }

    /**
     * Set HTTP password
     *
     * @param  string $password
     * @return Zend_Soap_Client
     */
    public function setHttpPassword($password)
    {
        $this->_password = $password;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Retrieve HTTP Password
     *
     * @return string
     */
    public function getHttpPassword()
    {
        return $this->_password;
    }

    /**
     * Set proxy host
     *
     * @param  string $proxyHost
     * @return Zend_Soap_Client
     */
    public function setProxyHost($proxyHost)
    {
        $this->_proxy_host = $proxyHost;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Retrieve proxy host
     *
     * @return string
     */
    public function getProxyHost()
    {
        return $this->_proxy_host;
    }

    /**
     * Set proxy port
     *
     * @param  int $proxyPort
     * @return Zend_Soap_Client
     */
    public function setProxyPort($proxyPort)
    {
        $this->_proxy_port = (int)$proxyPort;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Retrieve proxy port
     *
     * @return int
     */
    public function getProxyPort()
    {
        return $this->_proxy_port;
    }

    /**
     * Set proxy login
     *
     * @param  string $proxyLogin
     * @return Zend_Soap_Client
     */
    public function setProxyLogin($proxyLogin)
    {
        $this->_proxy_login = $proxyLogin;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Retrieve proxy login
     *
     * @return string
     */
    public function getProxyLogin()
    {
        return $this->_proxy_login;
    }

    /**
     * Set proxy password
     *
     * @param  string $proxyLogin
     * @return Zend_Soap_Client
     */
    public function setProxyPassword($proxyPassword)
    {
        $this->_proxy_password = $proxyPassword;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Set HTTPS client certificate path
     *
     * @param  string $localCert local certificate path
     * @return Zend_Soap_Client
     * @throws Zend_Soap_Client_Exception with invalid local certificate path argument
     */
    public function setHttpsCertificate($localCert)
    {
        if (!is_readable($localCert)) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('Invalid HTTPS client certificate path.');
        }

        $this->_local_cert = $localCert;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Get HTTPS client certificate path
     *
     * @return string
     */
    public function getHttpsCertificate()
    {
        return $this->_local_cert;
    }

    /**
     * Set HTTPS client certificate passphrase
     *
     * @param  string $passphrase
     * @return Zend_Soap_Client
     */
    public function setHttpsCertPassphrase($passphrase)
    {
        $this->_passphrase = $passphrase;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Get HTTPS client certificate passphrase
     *
     * @return string
     */
    public function getHttpsCertPassphrase()
    {
        return $this->_passphrase;
    }

    /**
     * Set compression options
     *
     * @param  int $compressionOptions
     * @return Zend_Soap_Client
     */
    public function setCompressionOptions($compressionOptions)
    {
        $this->_compression = $compressionOptions;

        $this->_soapClient = null;

        return $this;
    }

    /**
     * Get Compression options
     *
     * @return int
     */
    public function getCompressionOptions()
    {
        return $this->_compression;
    }

    /**
     * Retrieve proxy password
     *
     * @return string
     */
    public function getProxyPassword()
    {
        return $this->_proxy_password;
    }

    /**
     * Set Stream Context
     *
     * @return Zend_Soap_Client
     */
    public function setStreamContext($context)
    {
        if (!is_resource($context) || get_resource_type($context) !== "stream-context") {
            /**
             * @see Zend_Soap_Client_Exception
             */
            require_once "Zend/Soap/Client/Exception.php";
            throw new Zend_Soap_Client_Exception(
                "Invalid stream context resource given."
            );
        }

        $this->_stream_context = $context;
        return $this;
    }

    /**
     * Get Stream Context
     *
     * @return resource
     */
    public function getStreamContext()
    {
        return $this->_stream_context;
    }

    /**
     * Set the SOAP Feature options.
     *
     * @param  string|int $feature
     * @return Zend_Soap_Client
     */
    public function setSoapFeatures($feature)
    {
        $this->_features = $feature;

        $this->_soapClient = null;
        return $this;
    }

    /**
     * Return current SOAP Features options
     *
     * @return int
     */
    public function getSoapFeatures()
    {
        return $this->_features;
    }

    /**
     * Set the SOAP Wsdl Caching Options
     *
     * @param string|int|boolean $caching
     * @return Zend_Soap_Client
     */
    public function setWsdlCache($options)
    {
        $this->_cache_wsdl = $options;
        return $this;
    }

    /**
     * Get current SOAP Wsdl Caching option
     */
    public function getWsdlCache()
    {
        return $this->_cache_wsdl;
    }

    /**
     * Set the string to use in User-Agent header
     *
     * @param  string|null $userAgent
     * @return Zend_Soap_Client
     */
    public function setUserAgent($userAgent)
    {
        if ($userAgent === null) {
            $this->_user_agent = null;
        } else {
            $this->_user_agent = (string)$userAgent;
        }
        return $this;
    }

    /**
     * Get current string to use in User-Agent header
     *
     * @return string|null
     */
    public function getUserAgent()
    {
        return $this->_user_agent;
    }

    /**
     * Retrieve request XML
     *
     * @return string
     */
    public function getLastRequest()
    {
        if ($this->_soapClient !== null) {
            return $this->_soapClient->__getLastRequest();
        }

        return '';
    }

    /**
     * Get response XML
     *
     * @return string
     */
    public function getLastResponse()
    {
        if ($this->_soapClient !== null) {
            return $this->_soapClient->__getLastResponse();
        }

        return '';
    }

    /**
     * Retrieve request headers
     *
     * @return string
     */
    public function getLastRequestHeaders()
    {
        if ($this->_soapClient !== null) {
            return $this->_soapClient->__getLastRequestHeaders();
        }

        return '';
    }

    /**
     * Retrieve response headers (as string)
     *
     * @return string
     */
    public function getLastResponseHeaders()
    {
        if ($this->_soapClient !== null) {
            return $this->_soapClient->__getLastResponseHeaders();
        }

        return '';
    }

    /**
     * Retrieve last invoked method
     *
     * @return string
     */
    public function getLastMethod()
    {
        return $this->_lastMethod;
    }

    /**
     * Do request proxy method.
     *
     * May be overridden in subclasses
     *
     * @internal
     * @param Zend_Soap_Client_Common $client
     * @param string $request
     * @param string $location
     * @param string $action
     * @param int    $version
     * @param int    $one_way
     * @return mixed
     */
    public function _doRequest(Zend_Soap_Client_Common $client, $request, $location, $action, $version, $one_way = null)
    {
        // Perform request as is
        if ($one_way == null) {
            return call_user_func(array($client,'SoapClient::__doRequest'), $request, $location, $action, $version);
        } else {
            return call_user_func(array($client,'SoapClient::__doRequest'), $request, $location, $action, $version, $one_way);
        }
    }

    /**
     * Initialize SOAP Client object
     *
     * @throws Zend_Soap_Client_Exception
     */
    protected function _initSoapClientObject()
    {
        $wsdl = $this->getWsdl();
        $options = array_merge($this->getOptions(), array('trace' => true));

        if ($wsdl == null) {
            if (!isset($options['location'])) {
                require_once 'Zend/Soap/Client/Exception.php';
                throw new Zend_Soap_Client_Exception('\'location\' parameter is required in non-WSDL mode.');
            }
            if (!isset($options['uri'])) {
                require_once 'Zend/Soap/Client/Exception.php';
                throw new Zend_Soap_Client_Exception('\'uri\' parameter is required in non-WSDL mode.');
            }
        } else {
            if (isset($options['use'])) {
                require_once 'Zend/Soap/Client/Exception.php';
                throw new Zend_Soap_Client_Exception('\'use\' parameter only works in non-WSDL mode.');
            }
            if (isset($options['style'])) {
                require_once 'Zend/Soap/Client/Exception.php';
                throw new Zend_Soap_Client_Exception('\'style\' parameter only works in non-WSDL mode.');
            }
        }
        unset($options['wsdl']);

        $this->_soapClient = new Zend_Soap_Client_Common(array($this, '_doRequest'), $wsdl, $options);
    }

    /**
     * Perform arguments pre-processing
     *
     * My be overridden in descendant classes
     *
     * @param array $arguments
     */
    protected function _preProcessArguments($arguments)
    {
        // Do nothing
        return $arguments;
    }

    /**
     * Perform result pre-processing
     *
     * My be overridden in descendant classes
     *
     * @param array $arguments
     */
    protected function _preProcessResult($result)
    {
        // Do nothing
        return $result;
    }

    /**
     * Add SOAP input header
     *
     * @param SoapHeader $header
     * @param boolean $permanent
     * @return Zend_Soap_Client
     */
    public function addSoapInputHeader(SoapHeader $header, $permanent = false)
    {
        if ($permanent) {
            $this->_permanentSoapInputHeaders[] = $header;
        } else {
            $this->_soapInputHeaders[] = $header;
        }

        return $this;
    }

    /**
     * Reset SOAP input headers
     *
     * @return Zend_Soap_Client
     */
    public function resetSoapInputHeaders()
    {
        $this->_permanentSoapInputHeaders = array();
        $this->_soapInputHeaders = array();

        return $this;
    }

    /**
     * Get last SOAP output headers
     *
     * @return array
     */
    public function getLastSoapOutputHeaderObjects()
    {
        return $this->_soapOutputHeaders;
    }

    /**
     * Perform a SOAP call
     *
     * @param string $name
     * @param array  $arguments
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        $soapClient = $this->getSoapClient();

        $this->_lastMethod = $name;

        $soapHeaders = array_merge($this->_permanentSoapInputHeaders, $this->_soapInputHeaders);
        $result = $soapClient->__soapCall($name,
                                                 $this->_preProcessArguments($arguments),
                                                 null, /* Options are already set to the SOAP client object */
                                                 (count($soapHeaders) > 0)? $soapHeaders : null,
                                                 $this->_soapOutputHeaders);

        // Reset non-permanent input headers
        $this->_soapInputHeaders = array();

        return $this->_preProcessResult($result);
    }

    /**
     * Return a list of available functions
     *
     * @return array
     * @throws Zend_Soap_Client_Exception
     */
    public function getFunctions()
    {
        if ($this->getWsdl() == null) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('\'getFunctions\' method is available only in WSDL mode.');
        }

        $soapClient = $this->getSoapClient();
        return $soapClient->__getFunctions();
    }

    /**
     * Get used types.
     *
     * @return array
     */

    /**
     * Return a list of SOAP types
     *
     * @return array
     * @throws Zend_Soap_Client_Exception
     */
    public function getTypes()
    {
        if ($this->getWsdl() == null) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('\'getTypes\' method is available only in WSDL mode.');
        }

        $soapClient = $this->getSoapClient();

        return $soapClient->__getTypes();
    }

    /**
     * @param SoapClient $soapClient
     * @return Zend_Soap_Client
     */
    public function setSoapClient(SoapClient $soapClient)
    {
        $this->_soapClient = $soapClient;
        return $this;
    }

    /**
     * @return SoapClient
     */
    public function getSoapClient()
    {
        if ($this->_soapClient == null) {
            $this->_initSoapClientObject();
        }
        return $this->_soapClient;
    }

    /**
     * @param string $name
     * @param string $value
     * @return Zend_Soap_Client
     */
    public function setCookie($cookieName, $cookieValue=null)
    {
        $soapClient = $this->getSoapClient();
        $soapClient->__setCookie($cookieName, $cookieValue);
        return $this;
    }
}


<?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: ArrayOfTypeSequence.php 20096 2010-01-06 02:05:09Z bkarwin $
 */

/**
 * @see Zend_Soap_Wsdl_Strategy_DefaultComplexType
 */
require_once "Zend/Soap/Wsdl/Strategy/DefaultComplexType.php";

/**
 * Zend_Soap_Wsdl_Strategy_ArrayOfTypeSequence
 *
 * @category   Zend
 * @package    Zend_Soap
 * @subpackage Wsdl
 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Soap_Wsdl_Strategy_ArrayOfTypeSequence extends Zend_Soap_Wsdl_Strategy_DefaultComplexType
{
    /**
     * Add an unbounded ArrayOfType based on the xsd:sequence syntax if type[] is detected in return value doc comment.
     *
     * @param string $type
     * @return string tns:xsd-type
     */
    public function addComplexType($type)
    {
        $nestedCounter = $this->_getNestedCount($type);

        if ($nestedCounter > 0) {
            $singularType = $this->_getSingularType($type);

            for ($i = 1; $i <= $nestedCounter; $i++) {
                $complexTypeName = substr($this->_getTypeNameBasedOnNestingLevel($singularType, $i), 4);
                $childTypeName = $this->_getTypeNameBasedOnNestingLevel($singularType, $i-1);

                $this->_addElementFromWsdlAndChildTypes($complexTypeName, $childTypeName);
            }
            // adding the PHP type which is resolved to a nested XSD type. therefore add only once.
            $this->getContext()->addType($complexTypeName);

            return "tns:$complexTypeName";
        } elseif (!in_array($type, $this->getContext()->getTypes())) {
            // New singular complex type
            return parent::addComplexType($type);
        } else {
            // Existing complex type
            return $this->getContext()->getType($type);
        }
    }

    /**
     * Return the ArrayOf or simple type name based on the singular xsdtype and the nesting level
     *
     * @param  string $singularType
     * @param  int    $level
     * @return string
     */
    protected function _getTypeNameBasedOnNestingLevel($singularType, $level)
    {
        if ($level == 0) {
            // This is not an Array anymore, return the xsd simple type
            return $singularType;
        } else {
            $prefix = str_repeat("ArrayOf", $level);
            $xsdType = $this->_getStrippedXsdType($singularType);
            $arrayType = $prefix.$xsdType;
            return "tns:$arrayType";
        }
    }

    /**
     * Strip the xsd: from a singularType and Format it nice for ArrayOf<Type> naming
     *
     * @param  string $singularType
     * @return string
     */
    protected function _getStrippedXsdType($singularType)
    {
        return ucfirst(substr(strtolower($singularType), 4));
    }

    /**
     * From a nested defintion with type[], get the singular xsd:type
     *
     * @throws Zend_Soap_Wsdl_Exception When no xsd:simpletype can be detected.
     * @param  string $type
     * @return string
     */
    protected function _getSingularType($type)
    {
        $singulartype = $this->getContext()->getType(str_replace("[]", "", $type));
        return $singulartype;
    }

    /**
     * Return the array nesting level based on the type name
     *
     * @param  string $type
     * @return integer
     */
    protected function _getNestedCount($type)
    {
        return substr_count($type, "[]");
    }

    /**
     * Append the complex type definition to the WSDL via the context access
     *
     * @param  string $arrayType
     * @param  string $childTypeName
     * @return void
     */
    protected function _addElementFromWsdlAndChildTypes($arrayType, $childTypeName)
    {
        if (!in_array($arrayType, $this->getContext()->getTypes())) {
            $dom = $this->getContext()->toDomDocument();

            $complexType = $dom->createElement('xsd:complexType');
            $complexType->setAttribute('name', $arrayType);

            $sequence = $dom->createElement('xsd:sequence');

            $element = $dom->createElement('xsd:element');
            $element->setAttribute('name', 'item');
            $element->setAttribute('type', $childTypeName);
            $element->setAttribute('minOccurs', 0);
            $element->setAttribute('maxOccurs', 'unbounded');
            $sequence->appendChild($element);

            $complexType->appendChild($sequence);

            $this->getContext()->getSchema()->appendChild($complexType);
            $this->getContext()->addType($arrayType);
        }
    }
}