<?php namespace Cviebrock\LaravelElasticsearch;

use Elasticsearch\Client;
use Illuminate\Contracts\Container\Container;

/**
 * Class Manager
 *
 * @package Cviebrock\LaravelElasticsearch
 */
class Manager
{

    /**
     * The application instance.
     *
     * @var \Illuminate\Contracts\Container\Container
     */
    protected $app;

    /**
     * The Elasticsearch connection factory instance.
     *
     * @var \Cviebrock\LaravelElasticsearch\Factory
     */
    protected $factory;

    /**
     * The active connection instances.
     *
     * @var array
     */
    protected $connections = [];

    /**
     * @param \Illuminate\Contracts\Container\Container $app
     * @param \Cviebrock\LaravelElasticsearch\Factory $factory
     */
    public function __construct(Container $app, Factory $factory)
    {
        $this->app = $app;
        $this->factory = $factory;
    }

    /**
     * Retrieve or build the named connection.
     *
     * @param string|null $name
     *
     * @return Client
     */
    public function connection(string $name = null): Client
    {
        $name = $name ?: $this->getDefaultConnection();

        if (!isset($this->connections[$name])) {
            $client = $this->makeConnection($name);

            $this->connections[$name] = $client;
        }

        return $this->connections[$name];
    }

    /**
     * Get the default connection.
     *
     * @return string
     */
    public function getDefaultConnection(): string
    {
        return $this->app['config']['elasticsearch.defaultConnection'];
    }

    /**
     * Set the default connection.
     *
     * @param string $connection
     */
    public function setDefaultConnection(string $connection)
    {
        $this->app['config']['elasticsearch.defaultConnection'] = $connection;
    }

    /**
     * Make a new connection.
     *
     * @param string $name
     *
     * @return \Elasticsearch\Client
     */
    protected function makeConnection(string $name): Client
    {
        $config = $this->getConfig($name);

        return $this->factory->make($config);
    }

    /**
     * Get the configuration for a named connection.
     *
     * @param $name
     *
     * @return mixed
     * @throws \InvalidArgumentException
     */
    protected function getConfig(string $name)
    {
        $connections = $this->app['config']['elasticsearch.connections'];

        if (null === $config = array_get($connections, $name)) {
            throw new \InvalidArgumentException("Elasticsearch connection [$name] not configured.");
        }

        return $config;
    }

    /**
     * Return all of the created connections.
     *
     * @return array
     */
    public function getConnections(): array
    {
        return $this->connections;
    }

    /**
     * Dynamically pass methods to the default connection.
     *
     * @param  string $method
     * @param  array $parameters
     *
     * @return mixed
     */
    public function __call(string $method, array $parameters)
    {
        return call_user_func_array([$this->connection(), $method], $parameters);
    }
}


<?php
namespace QuickBooksOnline\API\Core\Http\Compression;

use QuickBooksOnline\API\Core\CoreConstants;

/**
 * Deflate compressor.
 */
class DeflateCompressor extends CompressorBase
{
    const DataCompressionFormat = CoreConstants::DataCompressionFormat_Deflate;

    /**
     * Compresses the input byte array into stream.
     * @param curl_headers Curl headers, pre-compression
     * @param requestBody POST body, pre-compression
     */
    public function Compress(&$curl_headers, &$requestBody)
    {
        if (!function_exists('gzencode')) {
            return;
        }

        $requestBody = gzencode($requestBody);

        $curl_headers['content-encoding']='deflate';
        $curl_headers['content-length']=strlen($requestBody);
    }

    /**
     * Prepares a request header that requests compressed output
     * @param curl_headers Curl headers, pre-compression
     */
    public function PrepareDecompress(&$curl_headers)
    {
        $curl_headers['accept-encoding'] = 'deflate';
    }

    /**
     * Decompresses the output response stream.
     * @param $responseBody Response body.
     * @param $response_headers Response headers
     * @return string|null Decompressed response body.
     */
    public function Decompress($responseBody, $response_headers)
    {
        return gzinflate(substr($responseBody, 10, -8));
    }
}


<?php
namespace QuickBooksOnline\API\Facades;

class Account{

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

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

}


<?php
/**
 * Zend_Console_Getopt is a class to parse options for command-line
 * applications.
 *
 * 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_Console_Getopt
 * @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: Getopt.php 22485 2010-06-25 22:54:25Z ramon $
 */

/**
 * Zend_Console_Getopt is a class to parse options for command-line
 * applications.
 *
 * Terminology:
 * Argument: an element of the argv array.  This may be part of an option,
 *   or it may be a non-option command-line argument.
 * Flag: the letter or word set off by a '-' or '--'.  Example: in '--output filename',
 *   '--output' is the flag.
 * Parameter: the additional argument that is associated with the option.
 *   Example: in '--output filename', the 'filename' is the parameter.
 * Option: the combination of a flag and its parameter, if any.
 *   Example: in '--output filename', the whole thing is the option.
 *
 * The following features are supported:
 *
 * - Short flags like '-a'.  Short flags are preceded by a single
 *   dash.  Short flags may be clustered e.g. '-abc', which is the
 *   same as '-a' '-b' '-c'.
 * - Long flags like '--verbose'.  Long flags are preceded by a
 *   double dash.  Long flags may not be clustered.
 * - Options may have a parameter, e.g. '--output filename'.
 * - Parameters for long flags may also be set off with an equals sign,
 *   e.g. '--output=filename'.
 * - Parameters for long flags may be checked as string, word, or integer.
 * - Automatic generation of a helpful usage message.
 * - Signal end of options with '--'; subsequent arguments are treated
 *   as non-option arguments, even if they begin with '-'.
 * - Raise exception Zend_Console_Getopt_Exception in several cases
 *   when invalid flags or parameters are given.  Usage message is
 *   returned in the exception object.
 *
 * The format for specifying options uses a PHP associative array.
 * The key is has the format of a list of pipe-separated flag names,
 * followed by an optional '=' to indicate a required parameter or
 * '-' to indicate an optional parameter.  Following that, the type
 * of parameter may be specified as 's' for string, 'w' for word,
 * or 'i' for integer.
 *
 * Examples:
 * - 'user|username|u=s'  this means '--user' or '--username' or '-u'
 *   are synonyms, and the option requires a string parameter.
 * - 'p=i'  this means '-p' requires an integer parameter.  No synonyms.
 * - 'verbose|v-i'  this means '--verbose' or '-v' are synonyms, and
 *   they take an optional integer parameter.
 * - 'help|h'  this means '--help' or '-h' are synonyms, and
 *   they take no parameter.
 *
 * The values in the associative array are strings that are used as
 * brief descriptions of the options when printing a usage message.
 *
 * The simpler format for specifying options used by PHP's getopt()
 * function is also supported.  This is similar to GNU getopt and shell
 * getopt format.
 *
 * Example:  'abc:' means options '-a', '-b', and '-c'
 * are legal, and the latter requires a string parameter.
 *
 * @category   Zend
 * @package    Zend_Console_Getopt
 * @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    Release: @package_version@
 * @since      Class available since Release 0.6.0
 *
 * @todo  Handle params with multiple values, e.g. --colors=red,green,blue
 *        Set value of parameter to the array of values.  Allow user to specify
 *        the separator with Zend_Console_Getopt::CONFIG_PARAMETER_SEPARATOR.
 *        If this config value is null or empty string, do not split values
 *        into arrays.  Default separator is comma (',').
 *
 * @todo  Handle params with multiple values specified with separate options
 *        e.g. --colors red --colors green --colors blue should give one
 *        option with an array(red, green, blue).
 *        Enable with Zend_Console_Getopt::CONFIG_CUMULATIVE_PARAMETERS.
 *        Default is that subsequent options overwrite the parameter value.
 *
 * @todo  Handle flags occurring multiple times, e.g. -v -v -v
 *        Set value of the option's parameter to the integer count of instances
 *        instead of a boolean.
 *        Enable with Zend_Console_Getopt::CONFIG_CUMULATIVE_FLAGS.
 *        Default is that the value is simply boolean true regardless of
 *        how many instances of the flag appear.
 *
 * @todo  Handle flags that implicitly print usage message, e.g. --help
 *
 * @todo  Handle freeform options, e.g. --set-variable
 *        Enable with Zend_Console_Getopt::CONFIG_FREEFORM_FLAGS
 *        All flag-like syntax is recognized, no flag generates an exception.
 *
 * @todo  Handle numeric options, e.g. -1, -2, -3, -1000
 *        Enable with Zend_Console_Getopt::CONFIG_NUMERIC_FLAGS
 *        The rule must specify a named flag and the '#' symbol as the
 *        parameter type. e.g.,  'lines=#'
 *
 * @todo  Enable user to specify header and footer content in the help message.
 *
 * @todo  Feature request to handle option interdependencies.
 *        e.g. if -b is specified, -a must be specified or else the
 *        usage is invalid.
 *
 * @todo  Feature request to implement callbacks.
 *        e.g. if -a is specified, run function 'handleOptionA'().
 */
class Zend_Console_Getopt
{

    /**
     * The options for a given application can be in multiple formats.
     * modeGnu is for traditional 'ab:c:' style getopt format.
     * modeZend is for a more structured format.
     */
    const MODE_ZEND                         = 'zend';
    const MODE_GNU                          = 'gnu';

    /**
     * Constant tokens for various symbols used in the mode_zend
     * rule format.
     */
    const PARAM_REQUIRED                    = '=';
    const PARAM_OPTIONAL                    = '-';
    const TYPE_STRING                       = 's';
    const TYPE_WORD                         = 'w';
    const TYPE_INTEGER                      = 'i';

    /**
     * These are constants for optional behavior of this class.
     * ruleMode is either 'zend' or 'gnu' or a user-defined mode.
     * dashDash is true if '--' signifies the end of command-line options.
     * ignoreCase is true if '--opt' and '--OPT' are implicitly synonyms.
     * parseAll is true if all options on the command line should be parsed, regardless of
     * whether an argument appears before them.
     */
    const CONFIG_RULEMODE                   = 'ruleMode';
    const CONFIG_DASHDASH                   = 'dashDash';
    const CONFIG_IGNORECASE                 = 'ignoreCase';
    const CONFIG_PARSEALL                   = 'parseAll';

    /**
     * Defaults for getopt configuration are:
     * ruleMode is 'zend' format,
     * dashDash (--) token is enabled,
     * ignoreCase is not enabled,
     * parseAll is enabled.
     */
    protected $_getoptConfig = array(
        self::CONFIG_RULEMODE   => self::MODE_ZEND,
        self::CONFIG_DASHDASH   => true,
        self::CONFIG_IGNORECASE => false,
        self::CONFIG_PARSEALL   => true,
    );

    /**
     * Stores the command-line arguments for the calling applicaion.
     *
     * @var array
     */
    protected $_argv = array();

    /**
     * Stores the name of the calling applicaion.
     *
     * @var string
     */
    protected $_progname = '';

    /**
     * Stores the list of legal options for this application.
     *
     * @var array
     */
    protected $_rules = array();

    /**
     * Stores alternate spellings of legal options.
     *
     * @var array
     */
    protected $_ruleMap = array();

    /**
     * Stores options given by the user in the current invocation
     * of the application, as well as parameters given in options.
     *
     * @var array
     */
    protected $_options = array();

    /**
     * Stores the command-line arguments other than options.
     *
     * @var array
     */
    protected $_remainingArgs = array();

    /**
     * State of the options: parsed or not yet parsed?
     *
     * @var boolean
     */
    protected $_parsed = false;

    /**
     * The constructor takes one to three parameters.
     *
     * The first parameter is $rules, which may be a string for
     * gnu-style format, or a structured array for Zend-style format.
     *
     * The second parameter is $argv, and it is optional.  If not
     * specified, $argv is inferred from the global argv.
     *
     * The third parameter is an array of configuration parameters
     * to control the behavior of this instance of Getopt; it is optional.
     *
     * @param  array $rules
     * @param  array $argv
     * @param  array $getoptConfig
     * @return void
     */
    public function __construct($rules, $argv = null, $getoptConfig = array())
    {
        if (!isset($_SERVER['argv'])) {
            require_once 'Zend/Console/Getopt/Exception.php';
            if (ini_get('register_argc_argv') == false) {
                throw new Zend_Console_Getopt_Exception(
                    "argv is not available, because ini option 'register_argc_argv' is set Off"
                );
            } else {
                throw new Zend_Console_Getopt_Exception(
                    '$_SERVER["argv"] is not set, but Zend_Console_Getopt cannot work without this information.'
                );
            }
        }

        $this->_progname = $_SERVER['argv'][0];
        $this->setOptions($getoptConfig);
        $this->addRules($rules);
        if (!is_array($argv)) {
            $argv = array_slice($_SERVER['argv'], 1);
        }
        if (isset($argv)) {
            $this->addArguments((array)$argv);
        }
    }

    /**
     * Return the state of the option seen on the command line of the
     * current application invocation.  This function returns true, or the
     * parameter to the option, if any.  If the option was not given,
     * this function returns null.
     *
     * The magic __get method works in the context of naming the option
     * as a virtual member of this class.
     *
     * @param  string $key
     * @return string
     */
    public function __get($key)
    {
        return $this->getOption($key);
    }

    /**
     * Test whether a given option has been seen.
     *
     * @param  string $key
     * @return boolean
     */
    public function __isset($key)
    {
        $this->parse();
        if (isset($this->_ruleMap[$key])) {
            $key = $this->_ruleMap[$key];
            return isset($this->_options[$key]);
        }
        return false;
    }

    /**
     * Set the value for a given option.
     *
     * @param  string $key
     * @param  string $value
     * @return void
     */
    public function __set($key, $value)
    {
        $this->parse();
        if (isset($this->_ruleMap[$key])) {
            $key = $this->_ruleMap[$key];
            $this->_options[$key] = $value;
        }
    }

    /**
     * Return the current set of options and parameters seen as a string.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->toString();
    }

    /**
     * Unset an option.
     *
     * @param  string $key
     * @return void
     */
    public function __unset($key)
    {
        $this->parse();
        if (isset($this->_ruleMap[$key])) {
            $key = $this->_ruleMap[$key];
            unset($this->_options[$key]);
        }
    }

    /**
     * Define additional command-line arguments.
     * These are appended to those defined when the constructor was called.
     *
     * @param  array $argv
     * @throws Zend_Console_Getopt_Exception When not given an array as parameter
     * @return Zend_Console_Getopt Provides a fluent interface
     */
    public function addArguments($argv)
    {
        if (!is_array($argv)) {
            require_once 'Zend/Console/Getopt/Exception.php';
            throw new Zend_Console_Getopt_Exception(
                "Parameter #1 to addArguments should be an array");
        }
        $this->_argv = array_merge($this->_argv, $argv);
        $this->_parsed = false;
        return $this;
    }

    /**
     * Define full set of command-line arguments.
     * These replace any currently defined.
     *
     * @param  array $argv
     * @throws Zend_Console_Getopt_Exception When not given an array as parameter
     * @return Zend_Console_Getopt Provides a fluent interface
     */
    public function setArguments($argv)
    {
        if (!is_array($argv)) {
            require_once 'Zend/Console/Getopt/Exception.php';
            throw new Zend_Console_Getopt_Exception(
                "Parameter #1 to setArguments should be an array");
        }
        $this->_argv = $argv;
        $this->_parsed = false;
        return $this;
    }

    /**
     * Define multiple configuration options from an associative array.
     * These are not program options, but properties to configure
     * the behavior of Zend_Console_Getopt.
     *
     * @param  array $getoptConfig
     * @return Zend_Console_Getopt Provides a fluent interface
     */
    public function setOptions($getoptConfig)
    {
        if (isset($getoptConfig)) {
            foreach ($getoptConfig as $key => $value) {
                $this->setOption($key, $value);
            }
        }
        return $this;
    }

    /**
     * Define one configuration option as a key/value pair.
     * These are not program options, but properties to configure
     * the behavior of Zend_Console_Getopt.
     *
     * @param  string $configKey
     * @param  string $configValue
     * @return Zend_Console_Getopt Provides a fluent interface
     */
    public function setOption($configKey, $configValue)
    {
        if ($configKey !== null) {
            $this->_getoptConfig[$configKey] = $configValue;
        }
        return $this;
    }

    /**
     * Define additional option rules.
     * These are appended to the rules defined when the constructor was called.
     *
     * @param  array $rules
     * @return Zend_Console_Getopt Provides a fluent interface
     */
    public function addRules($rules)
    {
        $ruleMode = $this->_getoptConfig['ruleMode'];
        switch ($this->_getoptConfig['ruleMode']) {
            case self::MODE_ZEND:
                if (is_array($rules)) {
                    $this->_addRulesModeZend($rules);
                    break;
                }
                // intentional fallthrough
            case self::MODE_GNU:
                $this->_addRulesModeGnu($rules);
                break;
            default:
                /**
                 * Call addRulesModeFoo() for ruleMode 'foo'.
                 * The developer should subclass Getopt and
                 * provide this method.
                 */
                $method = '_addRulesMode' . ucfirst($ruleMode);
                $this->$method($rules);
        }
        $this->_parsed = false;
        return $this;
    }

    /**
     * Return the current set of options and parameters seen as a string.
     *
     * @return string
     */
    public function toString()
    {
        $this->parse();
        $s = array();
        foreach ($this->_options as $flag => $value) {
            $s[] = $flag . '=' . ($value === true ? 'true' : $value);
        }
        return implode(' ', $s);
    }

    /**
     * Return the current set of options and parameters seen
     * as an array of canonical options and parameters.
     *
     * Clusters have been expanded, and option aliases
     * have been mapped to their primary option names.
     *
     * @return array
     */
    public function toArray()
    {
        $this->parse();
        $s = array();
        foreach ($this->_options as $flag => $value) {
            $s[] = $flag;
            if ($value !== true) {
                $s[] = $value;
            }
        }
        return $s;
    }

    /**
     * Return the current set of options and parameters seen in Json format.
     *
     * @return string
     */
    public function toJson()
    {
        $this->parse();
        $j = array();
        foreach ($this->_options as $flag => $value) {
            $j['options'][] = array(
                'option' => array(
                    'flag' => $flag,
                    'parameter' => $value
                )
            );
        }

        /**
         * @see Zend_Json
         */
        require_once 'Zend/Json.php';
        $json = Zend_Json::encode($j);

        return $json;
    }

    /**
     * Return the current set of options and parameters seen in XML format.
     *
     * @return string
     */
    public function toXml()
    {
        $this->parse();
        $doc = new DomDocument('1.0', 'utf-8');
        $optionsNode = $doc->createElement('options');
        $doc->appendChild($optionsNode);
        foreach ($this->_options as $flag => $value) {
            $optionNode = $doc->createElement('option');
            $optionNode->setAttribute('flag', utf8_encode($flag));
            if ($value !== true) {
                $optionNode->setAttribute('parameter', utf8_encode($value));
            }
            $optionsNode->appendChild($optionNode);
        }
        $xml = $doc->saveXML();
        return $xml;
    }

    /**
     * Return a list of options that have been seen in the current argv.
     *
     * @return array
     */
    public function getOptions()
    {
        $this->parse();
        return array_keys($this->_options);
    }

    /**
     * Return the state of the option seen on the command line of the
     * current application invocation.
     *
     * This function returns true, or the parameter value to the option, if any.
     * If the option was not given, this function returns false.
     *
     * @param  string $flag
     * @return mixed
     */
    public function getOption($flag)
    {
        $this->parse();
        if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) {
            $flag = strtolower($flag);
        }
        if (isset($this->_ruleMap[$flag])) {
            $flag = $this->_ruleMap[$flag];
            if (isset($this->_options[$flag])) {
                return $this->_options[$flag];
            }
        }
        return null;
    }

    /**
     * Return the arguments from the command-line following all options found.
     *
     * @return array
     */
    public function getRemainingArgs()
    {
        $this->parse();
        return $this->_remainingArgs;
    }

    /**
     * Return a useful option reference, formatted for display in an
     * error message.
     *
     * Note that this usage information is provided in most Exceptions
     * generated by this class.
     *
     * @return string
     */
    public function getUsageMessage()
    {
        $usage = "Usage: {$this->_progname} [ options ]\n";
        $maxLen = 20;
        $lines = array();
        foreach ($this->_rules as $rule) {
            $flags = array();
            if (is_array($rule['alias'])) {
                foreach ($rule['alias'] as $flag) {
                    $flags[] = (strlen($flag) == 1 ? '-' : '--') . $flag;
                }
            }
            $linepart['name'] = implode('|', $flags);
            if (isset($rule['param']) && $rule['param'] != 'none') {
                $linepart['name'] .= ' ';
                switch ($rule['param']) {
                    case 'optional':
                        $linepart['name'] .= "[ <{$rule['paramType']}> ]";
                        break;
                    case 'required':
                        $linepart['name'] .= "<{$rule['paramType']}>";
                        break;
                }
            }
            if (strlen($linepart['name']) > $maxLen) {
                $maxLen = strlen($linepart['name']);
            }
            $linepart['help'] = '';
            if (isset($rule['help'])) {
                $linepart['help'] .= $rule['help'];
            }
            $lines[] = $linepart;
        }
        foreach ($lines as $linepart) {
            $usage .= sprintf("%s %s\n",
            str_pad($linepart['name'], $maxLen),
            $linepart['help']);
        }
        return $usage;
    }

    /**
     * Define aliases for options.
     *
     * The parameter $aliasMap is an associative array
     * mapping option name (short or long) to an alias.
     *
     * @param  array $aliasMap
     * @throws Zend_Console_Getopt_Exception
     * @return Zend_Console_Getopt Provides a fluent interface
     */
    public function setAliases($aliasMap)
    {
        foreach ($aliasMap as $flag => $alias) {
            if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) {
                $flag = strtolower($flag);
                $alias = strtolower($alias);
            }
            if (!isset($this->_ruleMap[$flag])) {
                continue;
            }
            $flag = $this->_ruleMap[$flag];
            if (isset($this->_rules[$alias]) || isset($this->_ruleMap[$alias])) {
                $o = (strlen($alias) == 1 ? '-' : '--') . $alias;
                require_once 'Zend/Console/Getopt/Exception.php';
                throw new Zend_Console_Getopt_Exception(
                    "Option \"$o\" is being defined more than once.");
            }
            $this->_rules[$flag]['alias'][] = $alias;
            $this->_ruleMap[$alias] = $flag;
        }
        return $this;
    }

    /**
     * Define help messages for options.
     *
     * The parameter $help_map is an associative array
     * mapping option name (short or long) to the help string.
     *
     * @param  array $helpMap
     * @return Zend_Console_Getopt Provides a fluent interface
     */
    public function setHelp($helpMap)
    {
        foreach ($helpMap as $flag => $help) {
            if (!isset($this->_ruleMap[$flag])) {
                continue;
            }
            $flag = $this->_ruleMap[$flag];
            $this->_rules[$flag]['help'] = $help;
        }
        return $this;
    }

    /**
     * Parse command-line arguments and find both long and short
     * options.
     *
     * Also find option parameters, and remaining arguments after
     * all options have been parsed.
     *
     * @return Zend_Console_Getopt|null Provides a fluent interface
     */
    public function parse()
    {
        if ($this->_parsed === true) {
            return;
        }
        $argv = $this->_argv;
        $this->_options = array();
        $this->_remainingArgs = array();
        while (count($argv) > 0) {
            if ($argv[0] == '--') {
                array_shift($argv);
                if ($this->_getoptConfig[self::CONFIG_DASHDASH]) {
                    $this->_remainingArgs = array_merge($this->_remainingArgs, $argv);
                    break;
                }
            }
            if (substr($argv[0], 0, 2) == '--') {
                $this->_parseLongOption($argv);
            } elseif (substr($argv[0], 0, 1) == '-' && ('-' != $argv[0] || count($argv) >1)) {
                $this->_parseShortOptionCluster($argv);
            } elseif ($this->_getoptConfig[self::CONFIG_PARSEALL]) {
                $this->_remainingArgs[] = array_shift($argv);
            } else {
                /*
                 * We should put all other arguments in _remainingArgs and stop parsing
                 * since CONFIG_PARSEALL is false.
                 */
                $this->_remainingArgs = array_merge($this->_remainingArgs, $argv);
                break;
            }
        }
        $this->_parsed = true;
        return $this;
    }

    /**
     * Parse command-line arguments for a single long option.
     * A long option is preceded by a double '--' character.
     * Long options may not be clustered.
     *
     * @param  mixed &$argv
     * @return void
     */
    protected function _parseLongOption(&$argv)
    {
        $optionWithParam = ltrim(array_shift($argv), '-');
        $l = explode('=', $optionWithParam, 2);
        $flag = array_shift($l);
        $param = array_shift($l);
        if (isset($param)) {
            array_unshift($argv, $param);
        }
        $this->_parseSingleOption($flag, $argv);
    }

    /**
     * Parse command-line arguments for short options.
     * Short options are those preceded by a single '-' character.
     * Short options may be clustered.
     *
     * @param  mixed &$argv
     * @return void
     */
    protected function _parseShortOptionCluster(&$argv)
    {
        $flagCluster = ltrim(array_shift($argv), '-');
        foreach (str_split($flagCluster) as $flag) {
            $this->_parseSingleOption($flag, $argv);
        }
    }

    /**
     * Parse command-line arguments for a single option.
     *
     * @param  string $flag
     * @param  mixed  $argv
     * @throws Zend_Console_Getopt_Exception
     * @return void
     */
    protected function _parseSingleOption($flag, &$argv)
    {
        if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) {
            $flag = strtolower($flag);
        }
        if (!isset($this->_ruleMap[$flag])) {
            require_once 'Zend/Console/Getopt/Exception.php';
            throw new Zend_Console_Getopt_Exception(
                "Option \"$flag\" is not recognized.",
                $this->getUsageMessage());
        }
        $realFlag = $this->_ruleMap[$flag];
        switch ($this->_rules[$realFlag]['param']) {
            case 'required':
                if (count($argv) > 0) {
                    $param = array_shift($argv);
                    $this->_checkParameterType($realFlag, $param);
                } else {
                    require_once 'Zend/Console/Getopt/Exception.php';
                    throw new Zend_Console_Getopt_Exception(
                        "Option \"$flag\" requires a parameter.",
                        $this->getUsageMessage());
                }
                break;
            case 'optional':
                if (count($argv) > 0 && substr($argv[0], 0, 1) != '-') {
                    $param = array_shift($argv);
                    $this->_checkParameterType($realFlag, $param);
                } else {
                    $param = true;
                }
                break;
            default:
                $param = true;
        }
        $this->_options[$realFlag] = $param;
    }

    /**
     * Return true if the parameter is in a valid format for
     * the option $flag.
     * Throw an exception in most other cases.
     *
     * @param  string $flag
     * @param  string $param
     * @throws Zend_Console_Getopt_Exception
     * @return bool
     */
    protected function _checkParameterType($flag, $param)
    {
        $type = 'string';
        if (isset($this->_rules[$flag]['paramType'])) {
            $type = $this->_rules[$flag]['paramType'];
        }
        switch ($type) {
            case 'word':
                if (preg_match('/\W/', $param)) {
                    require_once 'Zend/Console/Getopt/Exception.php';
                    throw new Zend_Console_Getopt_Exception(
                        "Option \"$flag\" requires a single-word parameter, but was given \"$param\".",
                        $this->getUsageMessage());
                }
                break;
            case 'integer':
                if (preg_match('/\D/', $param)) {
                    require_once 'Zend/Console/Getopt/Exception.php';
                    throw new Zend_Console_Getopt_Exception(
                        "Option \"$flag\" requires an integer parameter, but was given \"$param\".",
                        $this->getUsageMessage());
                }
                break;
            case 'string':
            default:
                break;
        }
        return true;
    }

    /**
     * Define legal options using the gnu-style format.
     *
     * @param  string $rules
     * @return void
     */
    protected function _addRulesModeGnu($rules)
    {
        $ruleArray = array();

        /**
         * Options may be single alphanumeric characters.
         * Options may have a ':' which indicates a required string parameter.
         * No long options or option aliases are supported in GNU style.
         */
        preg_match_all('/([a-zA-Z0-9]:?)/', $rules, $ruleArray);
        foreach ($ruleArray[1] as $rule) {
            $r = array();
            $flag = substr($rule, 0, 1);
            if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) {
                $flag = strtolower($flag);
            }
            $r['alias'][] = $flag;
            if (substr($rule, 1, 1) == ':') {
                $r['param'] = 'required';
                $r['paramType'] = 'string';
            } else {
                $r['param'] = 'none';
            }
            $this->_rules[$flag] = $r;
            $this->_ruleMap[$flag] = $flag;
        }
    }

    /**
     * Define legal options using the Zend-style format.
     *
     * @param  array $rules
     * @throws Zend_Console_Getopt_Exception
     * @return void
     */
    protected function _addRulesModeZend($rules)
    {
        foreach ($rules as $ruleCode => $helpMessage) {
            // this may have to translate the long parm type if there
            // are any complaints that =string will not work (even though that use
            // case is not documented)
            if (in_array(substr($ruleCode, -2, 1), array('-', '='))) {
                $flagList  = substr($ruleCode, 0, -2);
                $delimiter = substr($ruleCode, -2, 1);
                $paramType = substr($ruleCode, -1);
            } else {
                $flagList = $ruleCode;
                $delimiter = $paramType = null;
            }
            if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) {
                $flagList = strtolower($flagList);
            }
            $flags = explode('|', $flagList);
            $rule = array();
            $mainFlag = $flags[0];
            foreach ($flags as $flag) {
                if (empty($flag)) {
                    require_once 'Zend/Console/Getopt/Exception.php';
                    throw new Zend_Console_Getopt_Exception(
                        "Blank flag not allowed in rule \"$ruleCode\".");
                }
                if (strlen($flag) == 1) {
                    if (isset($this->_ruleMap[$flag])) {
                        require_once 'Zend/Console/Getopt/Exception.php';
                        throw new Zend_Console_Getopt_Exception(
                            "Option \"-$flag\" is being defined more than once.");
                    }
                    $this->_ruleMap[$flag] = $mainFlag;
                    $rule['alias'][] = $flag;
                } else {
                    if (isset($this->_rules[$flag]) || isset($this->_ruleMap[$flag])) {
                        require_once 'Zend/Console/Getopt/Exception.php';
                        throw new Zend_Console_Getopt_Exception(
                            "Option \"--$flag\" is being defined more than once.");
                    }
                    $this->_ruleMap[$flag] = $mainFlag;
                    $rule['alias'][] = $flag;
                }
            }
            if (isset($delimiter)) {
                switch ($delimiter) {
                    case self::PARAM_REQUIRED:
                        $rule['param'] = 'required';
                        break;
                    case self::PARAM_OPTIONAL:
                    default:
                        $rule['param'] = 'optional';
                }
                switch (substr($paramType, 0, 1)) {
                    case self::TYPE_WORD:
                        $rule['paramType'] = 'word';
                        break;
                    case self::TYPE_INTEGER:
                        $rule['paramType'] = 'integer';
                        break;
                    case self::TYPE_STRING:
                    default:
                        $rule['paramType'] = 'string';
                }
            } else {
                $rule['param'] = 'none';
            }
            $rule['help'] = $helpMessage;
            $this->_rules[$mainFlag] = $rule;
        }
    }
}


<?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\Core\OAuth\OAuth1;

use QuickBooksOnline\API\Exception\SdkException;

/**
 * Class OAuth1
 *
 * A helper class to sign the Signature based on the request
 * @package QuickBooksOnline
 *
 */

class OAuth1{

  /**
   * The OAuth 1 ConsumerKey
   * @var String $consumerKey    The OAuth 1 Consumer Key
   */
  private $consumerKey;

  /**
   * The OAuth 1 ConsumerSecret
   * @var String $consumerSecret    The OAuth 1 Consumer Secret
   */
  private $consumerSecret;

  /**
   * The OAuth 1 Access Token
   * @var String $oauthToken    The OAuth 1 Access Token key
   */
  private $oauthToken;

  /**
   * The OAuth 1 Access Token Secret
   * @var String $oauthTokenSecret    The OAuth 1 Access Token Secret
   */
  private $oauthTokenSecret;

  /**
   * The OAuth 1 Nonce for random value
   * @var String $oautNonce    The OAuth 1 Nonce
   */
  private $oauthNonce;

  /**
   * The OAuth 1 Time Stamp
   * @var String $oauthTimeStamp    The OAuth 1 Time Stamp
   */
  private $oauthTimeStamp;

  /**
   * The OAuth 1 signature method
   */
  const SIGNATURE_METHOD = 'sha1';

  /**
   * A set of characters for random selection
   */
  const NONCE_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

  /**
   * THe OAuth parameters
   * @var Array $oauthParameters   The oauthParameters to be included
   */
  public $oauthParameters;

  /**
   * The Constructor for OAuth 1
   * @param String $ck     Consumer key
   * @param String $cS     Consumer Secret
   * @param String $oT     OAuth Access Token
   * @param String $oTS    OAuth Access Token Secret
   */
  public function __construct($ck, $cS, $oT, $oTS)
  {
      if(isset($ck)){
          $this->consumerKey = $ck;
      }else{
          throw new SdkException("Consumer key is not passed.");
      }

      if(isset($cS)){
          $this->consumerSecret = $cS;
      }else{
          throw new SdkException("Consumer Secret is not passed.");
      }

      if(isset($oT)){
          $this->oauthToken = $oT;
      }else{
          throw new SdkException("OAuth Token is not passed.");
      }

      if(isset($oTS)){
          $this->oauthTokenSecret = $oTS;
      }else{
          throw new SdkException("OAuth token Secret is not passed.");
      }

      $this->setNonce();
      $this->setOAuthTimeStamp();
      $this->oauthParameters = $this->appendOAuthPartsTo(null);
  }

  /**
   * Return the completed signed Authorization Header
   * @param String $uri              The complete URL contains everything
   * @param Array $queryParameters   The QueryParaemters for the request
   * @param String $httpMethod       The Http Method. POST or GET
   * @return String Authorization Header
   */
  public function getOAuthHeader($uri, $queryParameters, $httpMethod){
      $this->sign($uri, $queryParameters, $httpMethod);
      foreach($this->oauthParameters as $k => $v){
           $this->oauthParameters[$k] = $k . '="' . rawurlencode($v) . '"';
      }
      return 'OAuth ' . implode(',', $this->oauthParameters);
  }

  /**
   * Sign the Request based on the URL, OAuth 1 values, and query parameters. It follows the spec mentioned at here: https://oauth1.wp-api.org/docs/basics/Signing.html
   * For QuickBooks online, the Body only have two format, JSON or test. Do not include them in the authorization parts.
   * @param String $uri                 The Complete URL contains everything
   * @param Array $queryparameters      The query parameters for the array
   * @param String $httpMethod       The Http Method. POST or GET
   */
  public function sign($uri, $queryParameters, $httpMethod){
      $baseString = $this->getBaseString($uri, $httpMethod, $queryParameters);
      $oauthSignature = $this->signUsingHmacSha1($baseString);
      $this->oauthParameters['oauth_signature'] = $oauthSignature;
  }

  /**
   * Prepare the base String for OAuth 1 to sign
   * @param String $uri                 The Complete URL contains everything
   * @param String $method              The Http Method. POST or GET
   * @param Array $parameters           The query parameters for the array
   * @return String   The baseString for sign
   */
  public function getBaseString($uri, $method, array $parameters = array()){
      $baseString = $this->prepareHttpMethod($method) . '&' .
                    $this->prepareURL($uri) . '&' .
                    $this->prepareQueryParams($parameters);
      return $baseString;
  }

  /**
   * Helper method to format the HTTP method
   * @param String $method    The Post or Get
   * @return String    The formatted HTTP method
   */
  private function prepareHttpMethod($method){
       $trimmedMethod = trim($method);
       $upperMethod = strtoupper($trimmedMethod);
       return rawurlencode($method);
  }

  /**
   * Helper method to format the URL
   * @param String $url    The URL to be formatted
   * @return String    The formatted URL String
   */
  private function prepareURL($url){
       $trimedURL = trim($url);
       $encodedString = rawurlencode($trimedURL);
       return  $encodedString;
  }

  /**
   * A helper method to decide which query parameters to be included
   * When we append OAuth parts to the existed queryParameters. We don't add body for Query in the signature part.
   * POST parameters should only be included in the signature if they are of content-type "application/x-www-form-urlencoded" as with a form submission.
   * @param Array $queryParameters    The QueryParameters Array for format and re-order
   * @return String The formatted string
   */
  private function prepareQueryParams($queryParameters){
       $appendedQueryParams = $this->appendOAuthPartsTo($queryParameters);

       array_walk_recursive($appendedQueryParams, function (&$key, &$value) {
         $key   = rawurlencode($key);
         $value = rawurlencode($value);
        });

       uksort($appendedQueryParams, 'strcmp');
       $pairs = [];
       foreach ($appendedQueryParams as $parameter => $value) {
           if (is_array($value)) {
               // If two or more parameters share the same name, they are sorted by their value
               // Ref: Spec: 9.1.1 (1)
               sort($value, SORT_STRING);
               foreach ($value as $duplicateValue) {
                   $pairs[] = $parameter . '=' . $duplicateValue;
               }
           } else {
               $pairs[] = $parameter . '=' . $value;
           }
       }
       // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
       // Each name-value pair is separated by an '&' character (ASCII code 38)
       $resultString = implode('&', $pairs);
       $encodedString = rawurlencode($resultString);
       return $encodedString;
  }

  /**
   * Sign the $baseString with HMAC-SHA1
   * @param String $baseString    The baseString to be signed
   * @return String Signed String
   */
  public function signUsingHmacSha1($baseString){
       $key = rawurlencode($this->consumerSecret) . '&' . rawurlencode($this->oauthTokenSecret);
       return base64_encode(hash_hmac(self::SIGNATURE_METHOD, $baseString, $key, TRUE));
  }

  /**
   * Set a random nonce for the signature
   * @param int $length   the lenght of the lenght
   */
  private function setNonce($length = 6){
        $result = '';
        $cLength = strlen(self::NONCE_CHARS);
        for ($i=0; $i < $length; $i++)
        {
           $rnum = rand(0,$cLength - 1);
           $result .= substr(self::NONCE_CHARS,$rnum,1);
        }
        $this->oauthNonce = $result;
  }

  /**
   * Set a random timestamp for the signature
   */
  private function setOAuthTimeStamp(){
        $this->oauthTimeStamp = time();
  }

  /**
   * Add all OAuth query paraemters to the signature string string
   * @param Array $queryParameters    The queryParameters to be included
   * @return Array $queryParameters   The complete query parameters
   */
  private function appendOAuthPartsTo(array $queryParameters = null){
      if($queryParameters == null){
          $queryParameters = array();
      }
      $queryParameters['oauth_consumer_key'] = $this->consumerKey;
      $queryParameters['oauth_token'] = $this->oauthToken;
      $queryParameters['oauth_signature_method'] ='HMAC-SHA1';
      $queryParameters['oauth_timestamp'] = $this->oauthTimeStamp;
      $queryParameters['oauth_nonce'] =$this->oauthNonce;
      $queryParameters['oauth_version'] = '1.0';
      return $queryParameters;
  }
}


<?php
namespace QuickBooksOnline\API\QueryFilter;

//Why this folder exists? @hao
/**
 * Class to have query message
 */
class QueryMessage
{

    /**
     * variable SQL
     * @var string $sql
     */
    public $sql;

    /**
     * variable Projection
     * @var array projection
     */
    public $projection;

    /**
     * variable entity
     * @var string $entity
     */
    public $entity;

    /**
     * variable whereClause
     * @var array whereClause
     */
    public $whereClause;

    /**
     * variable startposition
     * @var int startposition
     */
    public $startposition;

    /**
     * variable
     * @var int maxresults
     */
    public $maxresults;

    /**
     * variable orderByClause
     * @var string orderByClause
     */
    public $orderByClause;

    /**
     * Initialize a QueryMessage object
     */
    public function __construct()
    {
        $this->projection = array();
        $this->whereClause = array();
    }

    /**
     * Get SQL query specified by object's members
     * @return string query
     */
    public function getString()
    {
        if (empty($this->sql) || empty($this->entity)) {
            return null;
        }

        $query = "";
        $query .= $this->sql;

        if (0==count($this->projection)) {
            $query .= " "."*";
        } else {
            if (count($this->projection)) {
                $query .= " " . implode(", ", $this->projection);
            }
        }

        $query .= " FROM " . $this->entity;

        if (!empty($this->whereClause)) {
            if (count($this->whereClause)) {
                $query .= " WHERE " . implode(" AND ", $this->whereClause);
            }
        }

        if (!empty($this->orderByClause)) {
            $query .= " ORDERBY " . $this->orderByClause;
        }

        if (!empty($this->startposition)) {
            $query .= " STARTPOSITION " . $this->startposition;
        }

        if (!empty($this->maxresults)) {
            $query .= " MAXRESULTS " . $this->maxresults;
        }

        return $query;
    }
}


<?php

 namespace QuickBooksOnline\API\Core\HttpClients;

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

/**
 * Class ClientFactory
 *
 * A client factory to create different type of Http client instance for sending HTTP/HTTPS requests over Network.
 */
class ClientFactory{

  //private constructor for static factory class
  private function __construct(){}

  /**
   * A static factory to create Http Client.
   * @param String HttpClient Name, default is using curl. Developer can set guzzleclient by passing 'guzzle' as client Name
   * @return HttpClientInterface
   */
  public static function createClient($clientName = CoreConstants::CLIENT_CURL){
      if($clientName == CoreConstants::CLIENT_CURL){
         if(extension_loaded('curl')){
            return new CurlHttpClient();
         }else{
            throw new SdkException("curl extension is not enabled. Cannot create curl http client for the SDK.");
         }
      }

      if(strcasecmp($clientName, CoreConstants::CLIENT_GUZZLE) == 0 ||strcasecmp($clientName, CoreConstants::CLIENT_GUZZLE_FULL) == 0){
          if(class_exists('GuzzleHttp\Client')){
             return new GuzzleHttpClient();
          }else{
            throw new SdkException("guzzle client cannot be found. Cannot create guzzle http client for the SDK.");
          }
      }

      throw new SdkException("The client Name you passed is not supported. Please use either 'curl' or 'guzzle' for the client Name.");
  }
}


<?php
namespace QuickBooksOnline\API\Facades;

class JournalEntry{

    public static function create(array $data, $throwException = TRUE){
        $data = JournalEntry::changeArrayName($data);
        if(!isset($data) || empty($data)) throw new \Exception("Passed array for creating JournalEntry is Empty");
        //QBO should not call the Entity as DataEnityt
        $JournalEntryObject = FacadeHelper::reflectArrayToObject("JournalEntry", $data, $throwException );
        return $JournalEntryObject;
    }

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

    private static function changeArrayName(&$array){
      if(is_array($array)){
         foreach($array as $key => &$value){
            if(is_array($value)){
               JournalEntry::changeArrayName($value);
            }

            if(strcmp($key, "Entity") == 0){
                 $array["JournalEntryEntity"] = $array["Entity"];
                 unset($array["Entity"]);
            }

            if(strcmp($key, "Type") == 0){
                 $array["JournalEntryType"] = $array["Type"];
                 unset($array["Type"]);
            }

         }
      }

      return $array;
    }

}


<?php

namespace QuickBooksOnline\API\Diagnostics;

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

/**
 * Logs API Requests/Responses To Disk
 */
class LogRequestsToDisk
{

    /**
     * Indicating whether Service Requests Logging should be enabled
     * @var bool
     */
    public $EnableServiceRequestsLogging;

    /**
     * The Service Request Logging Location.
     * @var string
     */
    public $ServiceRequestLoggingLocation;

    /**
     * Initializes a new instance of the LogRequestsToDisk class.
     * @param bool enableServiceRequestLogging Value indicating whether to log request response messages
     * @param string serviceRequestLoggingLocation Request Response logging locationl
     */
    public function __construct($enableServiceRequestLogging=false, $serviceRequestLoggingLocation=null)
    {
        $this->EnableServiceRequestsLogging = $enableServiceRequestLogging;
        $this->ServiceRequestLoggingLocation = $serviceRequestLoggingLocation;
    }

    /**
     * Enabled or disable the log
     * @param Boolean $status
     */
    public function setLogStatus($status){
        $this->EnableServiceRequestsLogging = $status;
    }

    /**
     * Set Log directory
     * @param String $logDirectory
     */
    public function setLogDirectory($logDirectory){
        $this->ServiceRequestLoggingLocation = $logDirectory;
    }

    /**
     * Gets the log destination folder
     * @return string log destination folder
     */
    public function GetLogDestination()
    {
        if ($this->EnableServiceRequestsLogging) {
            if (false === file_exists($this->ServiceRequestLoggingLocation)) {
                $this->ServiceRequestLoggingLocation = sys_get_temp_dir();
            }
        }
        return $this->ServiceRequestLoggingLocation;
    }

    /**
     * Logs the Platform Request to Disk.
     * @param string xml The xml to log.
     * @param string url of the request/response
     * @param array headers HTTP headers of the request/response
     * @param bool isRequest Specifies whether the xml is request or response.
     */
    public function LogPlatformRequests($xml, $url, $headers, $isRequest)
    {
        if ($this->EnableServiceRequestsLogging) {
            if (false === file_exists($this->ServiceRequestLoggingLocation)) {
                $this->ServiceRequestLoggingLocation = sys_get_temp_dir();
            }

            // Use filecount to have some sort of sequence number for debugging purposes - 5 digits
                  $sequenceNumber = iterator_count(new \DirectoryIterator($this->ServiceRequestLoggingLocation));
            $sequenceNumber = str_pad((int)$sequenceNumber, 5, "0", STR_PAD_LEFT);

            $iter = 0;
            $filePath = null;
            do {
                $filePath = null;
                if ($isRequest) {
                    $filePath = CoreConstants::REQUESTFILENAME_FORMAT;
                    $filePath = str_replace("{0}", $this->ServiceRequestLoggingLocation, $filePath);
                } else {
                    $filePath = CoreConstants::RESPONSEFILENAME_FORMAT;
                    $filePath = str_replace("{0}", $this->ServiceRequestLoggingLocation, $filePath);
                }
                $filePath = str_replace("{1}", CoreConstants::SLASH_CHAR.$sequenceNumber.'-', $filePath);
                $filePath = str_replace("{2}", time()."-".(int)$iter, $filePath);
                $iter++;
            } while (file_exists($filePath));

            try {
                $collapsedHeaders = array();
                foreach ($headers as $key=>$val) {
                    $collapsedHeaders[] = "{$key}: {$val}";
                }

                file_put_contents($filePath,
                                  ($isRequest?"REQUEST":"RESPONSE")." URI FOR SEQUENCE ID {$sequenceNumber}\n==================================\n{$url}\n\n",
                                  FILE_APPEND);
                file_put_contents($filePath,
                                  ($isRequest?"REQUEST":"RESPONSE")." HEADERS\n================\n".implode("\n", $collapsedHeaders)."\n\n",
                                  FILE_APPEND);
                file_put_contents($filePath,
                                  ($isRequest?"REQUEST":"RESPONSE")." BODY\n=============\n".$xml."\n\n",
                                  FILE_APPEND);
            } catch (\Exception $e) {
                throw new IdsException("Exception during LogPlatformRequests.");
            }
        }
    }
}


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

/**
 * Provides controll over executable operations per described entities
 * Supports wildcards as global level.
 *
 * For instance, following array will set any operation available for any entity:
 * array( "*" => array( "*" => true ));
 *
 * Next example will restrict an operation for all entities, except Foo
 * array(
 *  "*"   => array( "operation" => false ),
 *  "Foo" => array( "operation" => true  ),
 * );
 *
 * @author amatiushkin
 */
class OperationControlList
{
    private $operationList = array();
    const ALL = '*';

    /**
     * It is possible to specify rules upon instance creation
     * In common it used to add first global rule, e.g.
     *
     * Next line of code will enable all for all:
     *  new OperationControlList( OperationControlList::getDefaultList(true) )
     *
     * @param array $array
     */
    public function __construct($array = array())
    {
        $this->setOperationList($array);
    }

    /**
     * Defines operations control list, any previosly existing rules will be removed
     * @param array $array
     */
    public function setOperationList($array)
    {
        $this->operationList = $array;
    }

    /**
     * Returns current operations control list
     * @return array
     */
    public function getOperationList()
    {
        return $this->operationList;
    }

    /**
     * Verifies if operation is available for some entity
     * @param string $entity
     * @param string $operation
     * @return bool
     */
    public function isAllowed($entity, $operation)
    {
        //fallback to global rules if entity wasn't specified in the rules
        $lookupEn = array_key_exists($entity, $this->operationList)
                            ? $entity
                            : self::ALL;

        //fallback to global rules if operation wasn't specified in the rules
        $lookupOp = array_key_exists($operation, $this->operationList[$lookupEn])
                            ? $operation
                            : self::ALL;

        // entity and operation were found as is
        if (($entity === $lookupEn) && ($operation === $lookupOp)) {
            return $this->operationList[$lookupEn][$lookupOp];
        }

        //lookup for operation for current entity if it exists
        if (array_key_exists($lookupOp, $this->operationList[$lookupEn])) {
            return $this->operationList[$lookupEn][$lookupOp];
        }

        //lookup for operation for current entity if it exists
        if (array_key_exists($operation, $this->operationList[self::ALL])) {
            return $this->operationList[self::ALL][$operation];
        }

        //fallback to global entity
        if (array_key_exists($lookupOp, $this->operationList[self::ALL])) {
            return $this->operationList[self::ALL][$lookupOp];
        }

        //fall back to global entity and operation
        return $this->operationList[self::ALL][self::ALL];
    }

    /**
     * Returns global configuration array (in other words global rule)
     * @param bool $value
     * @return array
     */
    public static function getDefaultList($value)
    {
        return array(self::ALL => array(self::ALL => $value));
    }

    /**
     * Appends rules to existing list
     * @param array $array
     */
    public function appendRules($array = array())
    {
        $this->setOperationList(array_merge_recursive($this->getOperationList(), $array));
    }
}


<?php
namespace com\mikebevz\xsd2php\wsdl;

use com\mikebevz\xsd2php;

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

abstract class AbstractWsdl
{

    /**
     * WSDL Name
     * @var string
     */
    protected $wsdlName;

    /**
     * WSDL Target namespace
     * @var string
     */
    protected $wsdlTargetNamespace;

    /**
     * Service
     * @var object
     */
    protected $service = null;

    /**
     * Service Reflection
     * @var \ReflectionClass
     */
    protected $refl;

    /**
     * DOM Instance
     * @var \DomDocument
     */
    protected $dom;

    /**
     * Copy of Current WSDL as XML string
     * @var string
     */
    protected $wsdlXmlSource;

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

    /**
     * Commons class
     * @var com\mikebevz\xsd2php\Common
     */
    protected $common;

    /**
     * Soap binding style: rpc|document
     * @var string
     */
    protected $soapBindingStyle = SOAP_DOCUMENT;

    /**
     * Soap binding transport: which transport of SOAP this binding corresponds to
     * @var string
     */
    protected $soapBindingTransport = "http://schemas.xmlsoap.org/soap/http";

     /**
     * Prefix of target namespace
     * @var string
     */
    protected $targetNsPrefix = "tns";

    /**
     * Prefix for XML Schema namespace
     * @var string
     */
    protected $xmlSchemaPreffix = "xsd";
    /**
     * Suffix for generated service tag name
     * @var string
     */
    protected $serviceNameSuffix = "_Service";

    /**
     * Suffix for generated port tag name
     * @var string
     */
    protected $portNameSuffix = "_Port";
    /**
     * Suffix for generated binding tag name
     * @var unknown_type
     */
    protected $bindingNameSuffix = "_Binding";

    /**
     * Response items suffix
     * @var string
     */
    protected $responseSuffix = "Response";

    /**
     * Request
     * @var unknown_type
     */
    protected $requestSuffix = "Request";

    /**
     * Namespaces already imported to XML Schema
     * @var array
     */
    protected $importedNamespaces = array();

    protected $docNamespaces = array();

    /**
     * Path to schema to be imported/included in XSD
     * @var string
     */
    private $schemasPath = null;

    /**
     * Path to publicly accessable folder to store imported/included schemas
     *
     * @var string
     */
    private $publicPath = null;

    private $publicUrl = null; // Relative to / without domain name

    public $lastNsKey = 0;

    private $location;

    /**
     * Definitions node
     * @var DOMNode
     */
    protected $wsDefinitions;
    protected $wsDocumentation;
    protected $wsTypes;
    protected $wsMessage;
    protected $wsPortType;
    protected $wsBinding;
    protected $wsService;
    protected $wsXmlSchema;
    protected $methodsMeta;

    protected $xmlSchemaImports = array();

    /**
     *
     *
     * @param object|class $service Class name or object of the service to build WSDL for
     *
     * @return void
     */
    public function __construct($service)
    {
        if ($service != null) {
            $this->service = $service;
        } elseif ($this->service == null) {
            throw new \RuntimeException("No class defined");
        }

        if (!is_object($this->service) && !is_string($this->service)) {
            throw new \RuntimeException("Given service is neither class name nor object");
        }

        if (is_string($this->service)) {
            if (!class_exists($this->service)) {
                throw new \RuntimeException("Class ".$service." is not found. Did you forget to include it?");
            }
            $this->service = new $this->service();
        }

        $this->common = new xsd2php\Common();

        $this->refl = new \ReflectionClass($this->service);
        $this->wsdlName = $this->refl->getShortName();
        $this->wsdlTargetNamespace = $this->namespaceToUrn($this->refl->getNamespaceName());
    }

    /**
     * Prepare DOM
     *
     * @return void
     */
    private function prepareDom()
    {
        $this->dom = new \DOMDocument('1.0', 'UTF-8');
        if ($this->debug) {
            $this->dom->formatOutput = true;
        }
        $this->wsDefinitions = $this->dom->createElementNS($this->namespaces['wsdl'], 'wsdl:definitions');
        $this->dom->appendChild($this->wsDefinitions);
        $this->wsDefinitions->setAttribute('name', $this->wsdlName);
        $this->wsDefinitions->setAttribute('targetNamespace', $this->wsdlTargetNamespace);
        $this->dom->createAttributeNS($this->wsdlTargetNamespace, $this->targetNsPrefix.':definitions'); // This NS
        $this->common->dom = $this->dom;
    }

    /**
     * Prepare service class reflection
     *
     * @return void
     */
    private function prepareReflection()
    {
        $methods = $this->getClassMethods();
        foreach ($methods as $method) {
            $this->methodsMeta[$method->name] = $this->getMethodIO($method->name);
        }
    }

    /**
     * Return method input/output
     *
     * @param string $method Method name
     *
     * @return array
     */
    private function getMethodIO($method)
    {
        $methodDocs = $this->refl->getMethod($method)->getDocComment();
        return $this->common->parseDocComments($methodDocs);
    }

    /**
     * Get service class public methods
     *
     * @return array<ReflectionMethod>
     */
    private function getClassMethods()
    {
        return $this->refl->getMethods(\ReflectionMethod::IS_PUBLIC);
    }

    /**
     * Return WSDL as XML string
     *
     * @return string
     */
    public function toXml()
    {
        if (is_string($this->wsdlXmlSource) && $this->wsdlXmlSource != '') {
            return $this->wsdlXmlSource;
        } else {
            $this->prepareDom();
            $this->prepareReflection();
            $this->prepare();
            $this->wsdlXmlSource = $this->dom->saveXML();
            return $this->wsdlXmlSource;
        }
    }

    /**
     * Convert PHP namespace to URN
     *
     * @param string $namespace
     *
     * @return string
     */
    protected function namespaceToUrn($namespace)
    {
        $namespace = "urn:".preg_replace('/\\\/', ':', $namespace);
        return $namespace;
    }

    /**
     * Get QName style for given type
     * @param string $type Type name
     *
     * @return string
     * @throws RuntimeException If class of type cannot be found
     */
    protected function getTypeName($type)
    {

        //@todo Check must be performed against PHP data types not XMLSchema
        if (!in_array($type, $this->common->phpTypes)) {
            if (!class_exists($type)) {
                throw new \RuntimeException("Cannot find class ".$type);
            }

            $typeNamespace = $this->getClassNamespace($type);
            $typeName      = $this->getShortName($type);
            $nsCode        = $this->getNsCode($typeNamespace, true); //
            //print_r($typeNamespace);
            $this->addImportToSchema($typeNamespace, $nsCode);
            return $nsCode.":".$typeName;
        } else {
            return $this->xmlSchemaPreffix.":".$type;
        }
    }

    public function getNsCode($longNs, $rt = false)
    {
        // if namespace exists - just use its name
        // otherwise add it as nsatrribute to root and use its name
        if (!is_array($this->docNamespaces)) {
            $this->docNamespaces = array();
        }
        if (array_key_exists($longNs, $this->docNamespaces)) {
            return $this->docNamespaces[$longNs];
        } else {
            $this->docNamespaces[$longNs] = 'ns'.$this->lastNsKey;
            //$this->logger->debug($this->namespaces[$longNs]);
            if ($rt === false) {
                $nsAttr = $this->dom->createAttributeNS($longNs, $this->docNamespaces[$longNs].":definitions");
            }
            $this->lastNsKey++;
            return  $this->docNamespaces[$longNs];
        }
    }

    /**
     * Add xsd:import tag to XML schema before any childs added
     *
     * @param string $namespace Namespace URI
     * @param string $code      Shortcut for namespace, fx, ns0. Returned by getNsCode()
     *
     * @return void
     */
    private function addImportToSchema($namespace, $code)
    {
        if (array_key_exists($namespace, $this->docNamespaces)) {
            if (in_array($namespace, $this->importedNamespaces)) {
                return;
            }

            //$dom = $this->wsdl->toDomDocument();
            //print_r($namespace);

            $this->dom->createAttributeNs($namespace, $code.":definitions");

            $importEl = $this->dom->createElement($this->xmlSchemaPreffix.":import");
            $nsAttr   = $this->dom->createAttribute("namespace");
            $txtNode  = $this->dom->createTextNode($namespace);
            $nsAttr->appendChild($txtNode);

            $nsAttr2   = $this->dom->createAttribute("schemaLocation");
            $schemaLocation = $this->getSchemaLocation($namespace);
            $publicSchema = $this->copyToPublic($schemaLocation, true);
            $publicSchema = $this->copyToPublic($schemaLocation, true);
            $schemaUrl = $this->importsToAbsUrl($publicSchema, $this->getSchemasPath());

            $txtNode2  = $this->dom->createTextNode($schemaUrl);
            $nsAttr2->appendChild($txtNode2);

            $importEl->appendChild($nsAttr);
            $importEl->appendChild($nsAttr2);
            //$this->wsdl->getSchema();
            //$xpath = new \DOMXPath($this->dom);
            //$query = "//*[local-name()='types']/child::*/*";
            //$firstElement = $xpath->query($query);

            //if (!is_object($firstElement->item(0))) {
            //    $query = "//*[local-name()='types']/child::*";
            //    $schema = $xpath->query($query);
            //    $schema->item(0)->appendChild($importEl);
            //} else {
            //    $this->wsdl->getSchema()->insertBefore($importEl, $firstElement->item(0));
            //}
            //$this->xmlSchemaImports
            //$this->wsXmlSchema->appendChild();
            array_push($this->xmlSchemaImports, $importEl);
            array_push($this->importedNamespaces, $namespace);
        }
    }

    /**
     * Get path to schema in schemasPath with targetNamespace = $ns
     *
     * @param string $ns Namespace to match
     *
     * @return string absolut path to schema file
     * @throws RuntimeException If SchemaPath is not specified
     * @throws RuntimeException If Public folder is not specified
     * @throws RuntimeException If SchemaPath is not readable
     * @throws RuntimeException If PublicPath is not writable
     * @throws RuntimeException If namespace cannot be found in SchemaPath
     */
    public function getSchemaLocation($ns)
    {
        if ($this->getSchemasPath() == null) {
            throw new \RuntimeException("Schemas path is not specified - cannot start search for imports");
        }

        if (!file_exists($this->getSchemasPath())) {
            throw new \RuntimeException("Schemas path doesn't exist - cannot start search for imports");
        }

        if ($this->getPublicPath() == null || !file_exists($this->getPublicPath())) {
            throw new \RuntimeException("Public folder for imported schemas is not specified or is not exist - cannot save imports");
        }

        if (!is_readable($this->getSchemasPath())) {
            throw new \RuntimeException($this->getSchemasPath()." is not readable");
        }

        if (!is_writeable($this->getPublicPath())) {
            throw new \RuntimeException($this->getPublicPath()." is not writable");
        }
        // Scan directory for xsd files

        $dir   = new \RecursiveDirectoryIterator(realpath($this->getSchemasPath()));
        $iter  = new \RecursiveIteratorIterator($dir);
        $regex = new \RegexIterator($iter, '/\.xsd$/', \RecursiveRegexIterator::MATCH);
        $files = array();
        foreach ($regex as $key => $value) {
            array_push($files, $key);
        }
        $schema = "";
        foreach ($files as $file) {
            if ($this->isTargetNamespaceEqualsTo($file, $ns)) {
                $schema = $file;
            }
        }

        if ($schema == "") {
            throw new \RuntimeException("Cannot find schema for namespace ".$ns." in ".realpath($this->getSchemasPath()));
        }

        return $schema;
    }

    /**
     * Convert relative paths in XMLSchema's imports and includes to URLs using $location
     *
     * @param string $schemaPath Absolute path to XML Schema file
     *
     * @return string URL of the schema
     */
    public function importsToAbsUrl($schemaPath, $relPath = '')
    {
        $dom = new \DOMDocument();
        $dom->load($schemaPath);

        $xpath = new \DOMXPath($dom);
        $query = "//*[local-name()='schema']/*[local-name()='import']";
        $imports = $xpath->query($query);
        $urlParts = parse_url($this->getLocation());
        $rootUrl = $this->composeUrl($urlParts);
        $url = $rootUrl.$this->getPublicUrl()."/".basename($schemaPath);
        if (!is_object($imports->item(0))) {
            return $url;
        }

        $urlParts = parse_url($this->getLocation());
        $rootUrl = $this->composeUrl($urlParts);

        foreach ($imports as $import) {
            $scPath = realpath($relPath.DIRECTORY_SEPARATOR.$import->getAttribute('schemaLocation'));
            $copiedSchema = $this->copyToPublic($scPath, true);

            $schemaUrl = $rootUrl.$this->getPublicUrl()."/".basename($copiedSchema);
            $import->setAttribute('schemaLocation', $schemaUrl);

            $this->importsToAbsUrl($copiedSchema, dirname($scPath));
        }
        $dom->save($schemaPath);
        return $url;
    }

    /**
     * Return class XML namespace taken from @xmlNamespace doc
     *
     * @param string $class Class name
     *
     * @return string XML namespace
     * @throws RuntimeException If namespace cannot be found in class DocBlock
     */
    protected function getClassNamespace($class)
    {
        $refl = new \ReflectionClass($class);

        $docs = $this->common->parseDocComments($refl->getDocComment());
        if (!array_key_exists("xmlNamespace", $docs)) {
            throw new \RuntimeException('Cannot find namespace in class description in '.$class);
        }
        return $docs['xmlNamespace'];
    }

    /**
     * Get class name without namespace
     *
     * @param string $longName Class full name
     *
     * @return string Class base name
     */
    protected function getShortName($longName)
    {
        $arr = array_reverse(explode('\\', $longName));
        $shortName = $arr[0];
        return $shortName;
    }

    /**
     * Check if $schema XML Schema file belongs to $ns
     *
     * @param string $schema Path to XML Schema file
     * @param string $ns     Target namespace to match
     *
     * @return boolean
     */
    public function isTargetNamespaceEqualsTo($schema, $ns)
    {
        $dom = new \DOMDocument();
        $dom->load($schema);

        $xpath = new \DOMXPath($dom);
        $query = "//*[local-name()='schema']/@targetNamespace";
        $tNs = $xpath->query($query);

        if (!is_object($tNs->item(0))) {
            return false;
        }
        if ($tNs->item(0)->nodeValue === $ns) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Copy given file to publicSchemaPath
     *
     * @param string  $path      Path to file
     * @param boolean $overwrite Overwrite file if it exists? default false
     *
     * @return string Path to new file
     * @throws RuntimeException If file cannot be copied
     */
    public function copyToPublic($path, $overwrite = false)
    {
        $publicPath = realpath($this->getPublicPath());
        $targetFilePath   = $publicPath.DIRECTORY_SEPARATOR.basename($path);

        if (($overwrite === true && file_exists($targetFilePath)) || !file_exists($targetFilePath)) {
            if (!copy($path, $targetFilePath)) {
                throw new \RuntimeException("Cannot copy ".basename($path)." to ".$publicPath);
            }
        }

        return $targetFilePath;
    }

    /**
     * Compose URL of kind schema://host:port
     *
     * @param array $parts
     *
     *  @return string URL
     */
    public function composeUrl($parts)
    {
        $url = "";
        if (array_key_exists('scheme', $parts)) {
            $url .= $parts['scheme']."://";
        }

        if (array_key_exists('host', $parts)) {
            $url .= $parts['host'];
        }

        if (array_key_exists('port', $parts)) {
            $url .= ":".$parts['port'];
        }

        if (array_key_exists('path', $parts)) {
        }

        return $url;
    }

    /**
     * @return the $soapBindingStyle
     */
    public function getSoapBindingStyle()
    {
        return $this->soapBindingStyle;
    }

    /**
     * @param $soapBindingStyle the $soapBindingStyle to set
     */
    public function setSoapBindingStyle($soapBindingStyle)
    {
        $this->soapBindingStyle = $soapBindingStyle;
    }

/**
     * @return the $publicPath
     */
    public function getPublicPath()
    {
        return $this->publicPath;
    }

    /**
     * @param $publicPath the $publicPath to set
     */
    public function setPublicPath($publicPath)
    {
        $this->publicPath = $publicPath;
    }

/**
     * @return the $schemasPath
     */
    public function getSchemasPath()
    {
        return $this->schemasPath;
    }

    /**
     * @param $schemasPath the $schemasPath to set
     */
    public function setSchemasPath($schemasPath)
    {
        $this->schemasPath = $schemasPath;
    }

/**
     * Get SOAP service location (service->port->address->location)
     *
     * @return string $location
     */
    public function getLocation()
    {
        return $this->location;
    }

    /**
     * @param $location the $location to set
     */
    public function setLocation($location)
    {
        $this->location = $location;
    }

/**
     * @return the $publicUrl
     */
    public function getPublicUrl()
    {
        return $this->publicUrl;
    }

    /**
     * @param $publicUrl the $publicUrl to set
     */
    public function setPublicUrl($publicUrl)
    {
        $this->publicUrl = $publicUrl;
    }

/**
     * @return the $debug
     */
    public function getDebug()
    {
        return $this->debug;
    }

    /**
     * @param $debug the $debug to set
     */
    public function setDebug($debug)
    {
        $this->debug = $debug;
    }
}


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

use QuickBooksOnline\API\Core\CoreConstants;

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

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

/**
 * PHP to XML converter
 *
 * @author Mike Bevz <myb@mikebevz.com>
 * @version 0.0.4
 */
class Php2Xml extends Common
{
    /**
     * Php class to convert to XML
     * @var Object
     */
    private $phpClass = null;

    /**
     *
     * @var DOMElement
     */
    private $root;

    protected $rootTagName;

    private $logger;

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

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

        // @todo implement logger injection
        // this does not work due to PHP bug @see http://bugs.php.net/bug.php?id=46813
        if (class_exists('\Zend_Registry', false) && \Zend_Registry::isRegistered('logger')) {
            $this->logger = \Zend_Registry::get('logger');
        } else {
            $this->logger = new NullLogger();
        }

        $this->buildXml();
    }

    public function getXml($phpClass = null)
    {
        if ($this->phpClass == null && $phpClass == null) {
            throw new \RuntimeException("Php class is not set");
        }

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

        $propDocs = $this->parseClass($this->phpClass, $this->dom, true);

        foreach ($propDocs as $name => $data) {
            if (is_array($data['value'])) {
                $elName = array_reverse(explode("\\", $name));
                $code = $this->getNsCode($data['xmlNamespace']);
                foreach ($data['value'] as $arrEl) {
                    //@todo fix this workaroung. it's only works for one level array
                    $dom = $this->dom->createElement($code.":".$elName[0]);
                    $this->parseObjectValue($arrEl, $dom);
                    $this->root->appendChild($dom);
                }
            } else {
                $this->addProperty($data, $this->root);
            }
        }
        $xml = $this->dom->saveXML();
        //$xml = utf8_encode($xml);

        return $xml;
    }

    /**
     * get_class_vars_fix_order
     *
     * Mitigates a problem with get_class_vars, wherein get_class_vars does not honor the
     * order of class members derived via 'extend' of a parent class (potentially even an
     * extend that has multiple layers of inheritance)
     *
     * @param string $className
     *
     * @return array
     */
    private function get_class_vars_fix_order($className)
    {
        $propNames = array();
        $parentClassName = get_parent_class($className);

        $classMembersOnlyInherited = array();
        if ($parentClassName) {
            $classMembersOnlyInherited = $this->get_class_vars_fix_order(get_parent_class($className));
        }

        $classMembersIncludingInherited = array_keys(get_class_vars($className));
        $classMembersNotInherited = array_diff($classMembersIncludingInherited, $classMembersOnlyInherited);
        $orderedClassMembers = array_merge($classMembersOnlyInherited, $classMembersNotInherited);

        return $orderedClassMembers;
    }

    private function parseClass($object, $dom, $rt = false)
    {
        $refl = new \ReflectionClass($object);
        $docs = $this->parseDocComments($refl->getDocComment());

        if (($this->overrideAsSingleNamespace) && ($docs['xmlNamespace'] == '')) {
            $docs['xmlNamespace']=$this->overrideAsSingleNamespace;
        }

        if ($docs['xmlNamespace'] != '') {

            // Xml name needs to strip class prefix during parse
            if ($this->classPrefix && (0==strpos($docs['xmlName'], $this->classPrefix))) {
                $docs['xmlName'] = substr($docs['xmlName'], strlen($this->classPrefix));
            }

            $code = '';
            if (is_object($this->root)) { // root initialized
                $code = $this->getNsCode($docs['xmlNamespace']);
                $root = $this->dom->createElement($code.":".$docs['xmlName']);
            } else { // creating root element
                $code = $this->getNsCode($docs['xmlNamespace'], true);
                $root = $this->dom->createElementNS($docs['xmlNamespace'], $code.":".$docs['xmlName']);
            }

            $dom->appendChild($root);
        } else {
            //print_r("No Namespace found \n");
            $root = $this->dom->createElement($docs['xmlName']);
            $dom->appendChild($root);
        }

        if ($rt === true) {
            $this->rootTagName = $docs['xmlName'];
            $this->rootNsName = $docs['xmlNamespace'];
            $this->root = $root;
        }

        // Beware: getProperties does not honor member order for members derived via inheritance
        $properties = $refl->getProperties();

        $propDocs = array();
        foreach ($properties as $prop) {
            $pDocs = $this->parseDocComments($prop->getDocComment());
            $propDocs[$prop->getName()] = $pDocs;
            $propDocs[$prop->getName()]['value'] = $prop->getValue($object);
        }

        // Re-order $propDocs output, into correctly-ordered $correctMemberOrder
        $correctMemberOrder = $this->get_class_vars_fix_order(get_class($object));
        $correctMemberOrder = array_flip($correctMemberOrder);
        foreach ($propDocs as $key => $val) {
            $correctMemberOrder[$key]=$val;
        }

        return $correctMemberOrder;
    }

    private function buildXml()
    {
        $this->dom = new \DOMDocument('1.0', 'UTF-8');
        $this->dom->formatOutput = true;
        $this->dom->preserveWhiteSpace = false;
        $this->dom->recover = false;
        $this->dom->encoding = 'UTF-8';
    }

    private function addProperty($docs, $dom)
    {
        if (isset($docs['value'])) {
            $el = "";

            if (array_key_exists('xmlNamespace', $docs)) {
                $code = $this->getNsCode($docs['xmlNamespace']);
                $el = $this->dom->createElement($code.":".$docs['xmlName']);
            } else {
                $el = $this->dom->createElement($docs['xmlName']);
            }

            // SDK-78
            // converts numbers into string representation from any numeric
            if (is_numeric($docs['value'])) {
                $docs['value'] = (string) $docs['value'];
            }
            //

            if (is_object($docs['value'])) {
                //print_r("Value is object \n");
                $el = $this->parseObjectValue($docs['value'], $el);
            } elseif (is_string($docs['value'])) {
                if ($docs['xmlType'] == 'attribute') {
                    $atr = $this->dom->createAttribute($docs['xmlName']);
                    $text = $this->dom->createTextNode($docs['value']);
                    $atr->appendChild($text);

                    $el = $atr;
                } elseif (array_key_exists('xmlNamespace', $docs)) {
                    $code = $this->getNsCode($docs['xmlNamespace']);
                    $el = $this->dom->createElement($code.":".$docs['xmlName'], htmlspecialchars($docs['value'], ENT_QUOTES, 'UTF-8'));
                } else {
                    $el = $this->dom->createElement($docs['xmlName'], $docs['value']);
                }
            } else {
                //print_r("Value is not string");
            }

            $dom->appendChild($el);
        }
    }

    /**
     *
     * Parse value of the object
     *
     * @param Object $obj
     * @param DOMElement $element
     *
     * @return DOMElement
     */
    private function parseObjectValue($obj, $element)
    {
        $this->logger->debug("Start with:".$element->getNodePath());

        $refl = new \ReflectionClass($obj);

        $classDocs  = $this->parseDocComments($refl->getDocComment());
        $classProps = $refl->getProperties();
        $namespace = $classDocs['xmlNamespace'];
        foreach ($classProps as $prop) {
            $propDocs = $this->parseDocComments($prop->getDocComment());
            //print_r($prop->getDocComment());

            if (is_object($prop->getValue($obj))) {
                $code = '';
                $propDocs['xmlName'] = (isset($propDocs['xmlName'])) ? trim($propDocs['xmlName']) : '';
                //print($propDocs['xmlName']."\n");
                if (array_key_exists('xmlNamespace', $propDocs)) {
                    $code = $this->getNsCode($propDocs['xmlNamespace']);
                    $el = $this->dom->createElement($code.":".$propDocs['xmlName']);
                    $el = $this->parseObjectValue($prop->getValue($obj), $el);
                } else {
                    $el = $this->dom->createElement($propDocs['xmlName']);
                    $el = $this->parseObjectValue($prop->getValue($obj), $el);
                }
                //print_r("Value is object in Parse\n");

                $element->appendChild($el);
            } else {
                //Next function fixes SDK-78
                $this->castToStringZero($prop, $obj);
                // end

                if ($prop->getValue($obj) != '') {
                    if ($propDocs['xmlType'] == 'element') {
                        $el = '';
                        $code = $this->getNsCode($propDocs['xmlNamespace']);
                        $value = $prop->getValue($obj);

                        if (is_array($value)) {

                            // Strip the class prefix, if one exists
                            if ($this->classPrefix && (0===strpos($propDocs['xmlName'], $this->classPrefix))) {
                                $propDocs['xmlName'] = substr($propDocs['xmlName'], strlen($this->classPrefix));
                            }

                            $this->logger->debug("Creating element:".$code.":".$propDocs['xmlName']);
                            $this->logger->debug(print_r($value, true));
                            foreach ($value as $node) {
                                $this->logger->debug(print_r($node, true));
                                $el = $this->dom->createElement($code.":".$propDocs['xmlName']);
                                $arrNode = $this->parseObjectValue($node, $el);
                                $element->appendChild($arrNode);
                            }
                        } else {
                            //SDK-39
                            $el = $this->dom->createElement($code.":".$propDocs['xmlName'], htmlspecialchars($value));
                            $element->appendChild($el);
                        }
                        //print_r("Added element ".$propDocs['xmlName']." with NS = ".$propDocs['xmlNamespace']." \n");
                    } elseif ($propDocs['xmlType'] == 'attribute') {
                        $atr = $this->dom->createAttribute($propDocs['xmlName']);
                        $text = $this->dom->createTextNode($prop->getValue($obj));
                        $atr->appendChild($text);
                        $element->appendChild($atr);
                    } elseif ($propDocs['xmlType'] == 'value') {
                        $this->logger->debug(print_r($prop->getValue($obj), true));

                        $txtNode = $this->dom->createTextNode($prop->getValue($obj));
                        $element->appendChild($txtNode);
                    }
                }
            }
        }

        return $element;
    }

    /**
     * Returns true if value is empty and has a numeric type.
     * Basically this is for zero (0).
     * @param type $value
     * @return type
     */
    private function isEmptyInt($value)
    {
        return empty($value) && is_numeric($value);
    }

    /**
     * Cast numeric empty value to string
     * @see SDK-78
     * @param ReflectionProperty $prop
     * @param Object $obj
     */
    private function castToStringZero($prop, $obj)
    {
        if ($this->isEmptyInt($prop->getValue($obj))) {
            //reset value in very specific case to keep it intact
            $prop->setValue($obj, (string) $prop->getValue($obj));
        }
    }
}


<?php
namespace QuickBooksOnline\API\Diagnostics;

use QuickBooksOnline\API\Core\CoreConstants;

/**
 * This file contains an interface for Logging.
 */
class LoggerBase
{

    /**
     * Logs messages depending on the ids trace level.
     *
     * @param TraceLevel $idsTraceLevel IDS Trace Level.
     * @param string $messageToWrite The message to write.
     *
     */
    public function Log($idsTraceLevel, $messageToWrite)
    {
        $fileToWrite = CoreConstants::DEFAULT_LOGGINGLOCATION . '/executionlog.txt';
        if(file_exists($fileToWrite) && is_writable($fileToWrite))
        {
           file_put_contents($fileToWrite, $messageToWrite."\n", FILE_APPEND);
        }
    }
}