public function LogAPIRequestToLog($requestBody, $requestUri, $httpHeaders){
      $this->RequestLogging->LogPlatformRequests($requestBody, $requestUri, $httpHeaders, true);

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

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

public function LogAPIRequestToLog ( $ requestBody , $ requestUri , $ httpHeaders ) { $ this -> RequestLogging -> LogPlatformRequests ( $ requestBody , $ requestUri , $ httpHeaders , true ) ; if ( $ requestBody && $ this -> RequestCompressor ) { $ this -> RequestCompressor -> Compress ( $ httpHeaders , $ requestBody ) ; } if ( $ this -> ResponseCompressor ) { $ this -> ResponseCompressor -> PrepareDecompress ( $ httpHeaders ) ; } }

Log API Request to the Log directory that user specified.
@param String $requestBody The requestBody
@param String $requestUri  The URI for this request
@param Array $httpHeaders  The headers for the request

Log API Request to the Log directory that user specified .


private function parseStringToDom($string){
      $dom = new \DOMDocument();
      $dom->preserveWhiteSpace = FALSE;
      $dom->loadXML($string);
      $dom->formatOutput = TRUE;
      return $dom->saveXml();
    }

private function parseStringToDom ( $ string ) { $ dom = new \ DOMDocument ( ) ; $ dom -> preserveWhiteSpace = FALSE ; $ dom -> loadXML ( $ string ) ; $ dom -> formatOutput = TRUE ; return $ dom -> saveXml ( ) ; }

Parse a String xml to DOM structure for easy read
@param String $string   The String representation
@return string|bool The DOM structured XML


private function resetCompressorAndSerializer(){
      $this->RequestCompressor = CoreHelper::GetCompressor($this->context, true);
      $this->ResponseCompressor = CoreHelper::GetCompressor($this->context, false);
      $this->RequestSerializer = CoreHelper::GetSerializer($this->context, true);
      $this->ResponseSerializer = CoreHelper::GetSerializer($this->context, false);
    }

private function resetCompressorAndSerializer ( ) { $ this -> RequestCompressor = CoreHelper :: GetCompressor ( $ this -> context , true ) ; $ this -> ResponseCompressor = CoreHelper :: GetCompressor ( $ this -> context , false ) ; $ this -> RequestSerializer = CoreHelper :: GetSerializer ( $ this -> context , true ) ; $ this -> ResponseSerializer = CoreHelper :: GetSerializer ( $ this -> context , false ) ; }

This step is required since the configuration settings might have been changed.

This step is required since the configuration settings might have been changed .


private function getDestinationURL($requestParameters, $oMode, $specifiedRequestUri){
      // For Platform Discconect/Reconenct call, only for OAuth 1
      if (isset($requestParameters->ApiName)) {
          if(strcasecmp($oMode, CoreConstants::OAUTH1) == 0)
          {
            // Example: "https://appcenter.intuit.com/api/v1/Account/AppMenu"
            $requestUri = $this->context->baseserviceURL . $requestParameters->ApiName;
          }else{
            throw new SdkException("Disconnect/Reconnect Platform Call is only available in OAuth 1.0.");
          }
      } elseif (isset($specifiedRequestUri)) {
          // Prepare the request Uri from base Uri and resource Uri.
          $requestUri = $specifiedRequestUri;
      } elseif (isset($requestParameters->ResourceUri)) {
          $requestUri = $this->context->baseserviceURL . $requestParameters->ResourceUri;
      } else {
          throw new SdkException("Internal Error. UnSpecified URI Type for sending request.");
      }

      return $requestUri;
    }

Based on the request determine the URL Endpoint
@param  RequestParameters $requestParameters    The request parameter for this API Call
@param  String           $oMode                The OAuth mode for the request
@param  String           $specifiedRequestUri  Ignore the rule, use the user specified URI for the request
@return String           Destination URL for the request


private function appendMinorVersionToRequestURI($requestUri){
      $setMinorVersion = $this->context->minorVersion;
      if (isset($setMinorVersion)) {
          if ($this->queryToArray($requestUri) == false) { //if no query string params
              $requestUri .= "?minorversion=" . $this->context->minorVersion;
          } else {
              $requestUri .= "&minorversion=" . $this->context->minorVersion;
          }
      }
      return $requestUri;
    }

private
function
appendMinorVersionToRequestURI
(
$
requestUri
)
{
$
setMinorVersion
=
$
this
->
context
->
minorVersion
;
if
(
isset
(
$
setMinorVersion
)
)
{
if
(
$
this
->
queryToArray
(
$
requestUri
)
==
false
)
{
//if no query string params
$
requestUri
.=
"?minorversion="
.
$
this
->
context
->
minorVersion
;
}
else
{
$
requestUri
.=
"&minorversion="
.
$
this
->
context
->
minorVersion
;
}
}
return
$
requestUri
;
}

Append Minor Version to the URI

@param String requestUri
@return String requestUri with Minor Version Appended


private function checkHTTPMethod($requestParameters){
      $verb = $requestParameters->HttpVerbType;
      if(strcasecmp($verb, CoreConstants::HTTP_POST) == 0){
         return CoreConstants::HTTP_POST;
      } else if(strcasecmp($verb, CoreConstants::HTTP_GET) == 0){
         return CoreConstants::HTTP_GET;
      } else{
         throw new SdkException("Internal Error. Unsupported HTTP Method:" . $verb);
      }
    }

Check The HTTP MEthod from the requestParameters, make sure it is either GET or POST

@param RequestParameters  RequestParameters  The requestParameters for the request
@return String HTTP Method

Check The HTTP MEthod from the requestParameters make sure it is either GET or POST


private function parseURL($url){
       $query_str = parse_url($url, PHP_URL_QUERY);
       parse_str($query_str, $parameters);
       return $parameters;
    }

private function parseURL ( $ url ) { $ query_str = parse_url ( $ url , PHP_URL_QUERY ) ; parse_str ( $ query_str , $ parameters ) ; return $ parameters ; }

Get the query parameters from the complete URL, used for sign signature for OAuth 1.

@param String  $url  The $url for the request
@return Array  a list of query paramters.

Get the query parameters from the complete URL used for sign signature for OAuth 1 .


private function getAcceptContentType($value)
   {
       if (CoreConstants::CONTENTTYPE_APPLICATIONXML === $value) {
           return $value;
       }

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

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

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

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

       return "*/*";
   }

private function getAcceptContentType ( $ value ) { if ( CoreConstants :: CONTENTTYPE_APPLICATIONXML === $ value ) { return $ value ; } if ( CoreConstants :: CONTENTTYPE_APPLICATIONJSON === $ value ) { return $ value ; } if ( CoreConstants :: CONTENTTYPE_APPLICATIONPDF === $ value ) { return $ value ; } if ( CoreConstants :: CONTENTTYPE_OCTETSTREAM === $ value ) { if ( $ this -> ResponseSerializer instanceof XmlObjectSerializer ) { return CoreConstants :: CONTENTTYPE_APPLICATIONXML ; } if ( $ this -> ResponseSerializer instanceof JsonObjectSerializer ) { return CoreConstants :: CONTENTTYPE_APPLICATIONJSON ; } } return "*/*" ; }

Accept anything if content type is not XML or Json
@param string $value
@return string


private function queryToArray($qry)
    {
        $result = array();
                //string must contain at least one = and cannot be in first position
                if (strpos($qry, '=')) {
                    if (strpos($qry, '?')!==false) {
                        $q = parse_url($qry);
                        $qry = $q['query'];
                    }
                } else {
                    return false;
                }

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

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

A helper function to convert Query to Array
@param String $qry   The query String
@return  False | Array   The result


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

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

Using the token to encrypt payload and compare with sig

@param $payLoad
The payload to be verified
@param $sig
The signature retrieved from header
@param null $algo
The algo used to encrypt
@return bool
True if same false otherwise


private function encryptPayLoadBasedOnToken($payLoad, $hashAlgorithm = null)
    {
        if ($hashAlgorithm == null) {
            $hashAlgorithm = TokenVerifier::HASH_ALGORITHM;
        }
        $hashedPayLoad = hash_hmac($hashAlgorithm, $payLoad, $this->verifierToken);
        $encodedHashedPayLoad = base64_encode(hex2bin($hashedPayLoad));
        return $encodedHashedPayLoad;
    }

private function encryptPayLoadBasedOnToken ( $ payLoad , $ hashAlgorithm = null ) { if ( $ hashAlgorithm == null ) { $ hashAlgorithm = TokenVerifier :: HASH_ALGORITHM ; } $ hashedPayLoad = hash_hmac ( $ hashAlgorithm , $ payLoad , $ this -> verifierToken ) ; $ encodedHashedPayLoad = base64_encode ( hex2bin ( $ hashedPayLoad ) ) ; return $ encodedHashedPayLoad ; }

Encrypt the payload based on hash algorithm and verifier Token

@param $payLoad
@param null $hashAlgorithm
@return string


public function IntuitBatchResponse($id)
    {
        if(array_key_exists($id, $this->intuitBatchItemResponses)){
            return $this->intuitBatchItemResponses[$id];
        }else{
            return null;
        }
    }

public function IntuitBatchResponse ( $ id ) { if ( array_key_exists ( $ id , $ this -> intuitBatchItemResponses ) ) { return $ this -> intuitBatchItemResponses [ $ id ] ; } else { return null ; } }

Gets the IntuitBatchResponse with the specified id.
@param string $id unique batchitem id

Gets the IntuitBatchResponse with the specified id .


public function Remove($id)
    {
        if (!$id) {
            $exception = new IdsException('BatchItemIdNotFound: id');
            IdsExceptionManager::HandleException($exception);
        }

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

public
function
Remove
(
$
id
)
{
if
(
!
$
id
)
{
$
exception
=
new
IdsException
(
'BatchItemIdNotFound: id'
)
;
IdsExceptionManager
::
HandleException
(
$
exception
)
;
}
$
revisedBatchRequests
=
array
(
)
;
foreach
(
$
this
->
batchRequests
as
$
oneBatchRequest
)
{
if
(
$
oneBatchRequest
->
bId
==
$
id
)
{
// Exclude
}
else
{
$
revisedBatchRequests
[
]
=
$
oneBatchRequest
;
}
}
$
this
->
batchRequests
=
$
revisedBatchRequests
;
}

Removes batchitem with the specified batchitem id.
@param string id unique batchitem id

Removes batchitem with the specified batchitem id .


private function arrayToMessageAndCode(array $array)
    {
        if (empty($array)) {
            return array(null,null);
        }
        if (1 == count($array)) {
            $item = array_pop($array);
            return array($item->message,$item->code);
        }

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

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

Helper function for store the error message and code from Fault returned from QuickBooks Online


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

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

            }

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

Prepare IdsException out of Fault object.
@param Fault fault Fault object.
@return IdsException IdsException object.

Prepare IdsException out of Fault object .


public function getOptions()
    {
        $options = array();

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

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

        return $options;
    }

Return array of options suitable for using with SoapClient constructor

@return array


public function setStyle($style)
    {
        if (!in_array($style, array(SOAP_RPC, SOAP_DOCUMENT))) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('Invalid request style specified. Use SOAP_RPC or SOAP_DOCUMENT constants.');
        }

        $this->_style = $style;

        $this->_soapClient = null;

        return $this;
    }

Set request style

@param  int $style One of the SOAP_RPC or SOAP_DOCUMENT constants
@return Zend_Soap_Client
@throws Zend_Soap_Client_Exception with invalid style argument


public function setEncodingMethod($use)
    {
        if (!in_array($use, array(SOAP_ENCODED, SOAP_LITERAL))) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('Invalid message encoding method. Use SOAP_ENCODED or SOAP_LITERAL constants.');
        }

        $this->_use = $use;

        $this->_soapClient = null;

        return $this;
    }

Set message encoding method

@param  int $use One of the SOAP_ENCODED or SOAP_LITERAL constants
@return Zend_Soap_Client
@throws Zend_Soap_Client_Exception with invalid message encoding method argument


public function setProxyPort($proxyPort)
    {
        $this->_proxy_port = (int)$proxyPort;

        $this->_soapClient = null;

        return $this;
    }

public function setProxyPort ( $ proxyPort ) { $ this -> _proxy_port = ( int ) $ proxyPort ; $ this -> _soapClient = null ; return $ this ; }

Set proxy port

@param  int $proxyPort
@return Zend_Soap_Client


public function setHttpsCertificate($localCert)
    {
        if (!is_readable($localCert)) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('Invalid HTTPS client certificate path.');
        }

        $this->_local_cert = $localCert;

        $this->_soapClient = null;

        return $this;
    }

Set HTTPS client certificate path

@param  string $localCert local certificate path
@return Zend_Soap_Client
@throws Zend_Soap_Client_Exception with invalid local certificate path argument


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

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

Set Stream Context

@return Zend_Soap_Client


public function setUserAgent($userAgent)
    {
        if ($userAgent === null) {
            $this->_user_agent = null;
        } else {
            $this->_user_agent = (string)$userAgent;
        }
        return $this;
    }

public function setUserAgent ( $ userAgent ) { if ( $ userAgent === null ) { $ this -> _user_agent = null ; } else { $ this -> _user_agent = ( string ) $ userAgent ; } return $ this ; }

Set the string to use in User-Agent header

@param  string|null $userAgent
@return Zend_Soap_Client

Set the string to use in User - Agent header


public function _doRequest(Zend_Soap_Client_Common $client, $request, $location, $action, $version, $one_way = null)
    {
        // Perform request as is
        if ($one_way == null) {
            return call_user_func(array($client,'SoapClient::__doRequest'), $request, $location, $action, $version);
        } else {
            return call_user_func(array($client,'SoapClient::__doRequest'), $request, $location, $action, $version, $one_way);
        }
    }

public
function
_doRequest
(
Zend_Soap_Client_Common
$
client
,
$
request
,
$
location
,
$
action
,
$
version
,
$
one_way
=
)
{
// Perform request as is
if
(
$
one_way
==
)
{
return
call_user_func
(
array
(
$
client
,
'SoapClient::__doRequest'
)
,
$
request
,
$
location
,
$
action
,
$
version
)
;
}
else
{
return
call_user_func
(
array
(
$
client
,
'SoapClient::__doRequest'
)
,
$
request
,
$
location
,
$
action
,
$
version
,
$
one_way
)
;
}
}

Do request proxy method.

May be overridden in subclasses

@internal
@param Zend_Soap_Client_Common $client
@param string $request
@param string $location
@param string $action
@param int    $version
@param int    $one_way
@return mixed


protected function _initSoapClientObject()
    {
        $wsdl = $this->getWsdl();
        $options = array_merge($this->getOptions(), array('trace' => true));

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

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

Initialize SOAP Client object

@throws Zend_Soap_Client_Exception


public function addSoapInputHeader(SoapHeader $header, $permanent = false)
    {
        if ($permanent) {
            $this->_permanentSoapInputHeaders[] = $header;
        } else {
            $this->_soapInputHeaders[] = $header;
        }

        return $this;
    }

public function addSoapInputHeader ( SoapHeader $ header , $ permanent = false ) { if ( $ permanent ) { $ this -> _permanentSoapInputHeaders [ ] = $ header ; } else { $ this -> _soapInputHeaders [ ] = $ header ; } return $ this ; }

Add SOAP input header

@param SoapHeader $header
@param boolean $permanent
@return Zend_Soap_Client


public function getFunctions()
    {
        if ($this->getWsdl() == null) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('\'getFunctions\' method is available only in WSDL mode.');
        }

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

Return a list of available functions

@return array
@throws Zend_Soap_Client_Exception


public function getTypes()
    {
        if ($this->getWsdl() == null) {
            require_once 'Zend/Soap/Client/Exception.php';
            throw new Zend_Soap_Client_Exception('\'getTypes\' method is available only in WSDL mode.');
        }

        $soapClient = $this->getSoapClient();

        return $soapClient->__getTypes();
    }

Return a list of SOAP types

@return array
@throws Zend_Soap_Client_Exception


protected function _getTypeNameBasedOnNestingLevel($singularType, $level)
    {
        if ($level == 0) {
            // This is not an Array anymore, return the xsd simple type
            return $singularType;
        } else {
            $prefix = str_repeat("ArrayOf", $level);
            $xsdType = $this->_getStrippedXsdType($singularType);
            $arrayType = $prefix.$xsdType;
            return "tns:$arrayType";
        }
    }

Return the ArrayOf or simple type name based on the singular xsdtype and the nesting level

@param  string $singularType
@param  int    $level
@return string


protected function _addElementFromWsdlAndChildTypes($arrayType, $childTypeName)
    {
        if (!in_array($arrayType, $this->getContext()->getTypes())) {
            $dom = $this->getContext()->toDomDocument();

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

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

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

            $complexType->appendChild($sequence);

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

protected function _addElementFromWsdlAndChildTypes ( $ arrayType , $ childTypeName ) { if ( ! in_array ( $ arrayType , $ this -> getContext ( ) -> getTypes ( ) ) ) { $ dom = $ this -> getContext ( ) -> toDomDocument ( ) ; $ complexType = $ dom -> createElement ( 'xsd:complexType' ) ; $ complexType -> setAttribute ( 'name' , $ arrayType ) ; $ sequence = $ dom -> createElement ( 'xsd:sequence' ) ; $ element = $ dom -> createElement ( 'xsd:element' ) ; $ element -> setAttribute ( 'name' , 'item' ) ; $ element -> setAttribute ( 'type' , $ childTypeName ) ; $ element -> setAttribute ( 'minOccurs' , 0 ) ; $ element -> setAttribute ( 'maxOccurs' , 'unbounded' ) ; $ sequence -> appendChild ( $ element ) ; $ complexType -> appendChild ( $ sequence ) ; $ this -> getContext ( ) -> getSchema ( ) -> appendChild ( $ complexType ) ; $ this -> getContext ( ) -> addType ( $ arrayType ) ; } }

Append the complex type definition to the WSDL via the context access

@param  string $arrayType
@param  string $childTypeName
@return void


public function prepare()
    {
        $this->dom->createAttributeNS($this->namespaces['soap'], 'soap:definitions');
        $this->dom->createAttributeNS($this->namespaces[$this->xmlSchemaPreffix], $this->xmlSchemaPreffix.':definitions');

        //@todo this only required when we have arrays
        $this->dom->createAttributeNS($this->namespaces['soapenc'], 'soapenc:definitions');

        $this->wsTypes = $this->dom->createElement('wsdl:types');
        $this->wsDefinitions->appendChild($this->wsTypes);

        $wsXmlSchema = $this->dom->createElement($this->xmlSchemaPreffix.':schema');
        $wsXmlSchema->setAttribute('targetNamespace', $this->wsdlTargetNamespace);

        $this->wsXmlSchema = $this->wsTypes->appendChild($wsXmlSchema);

        $portType =  $this->dom->createElement('wsdl:portType');
        $portType->setAttribute('name', $this->wsdlName.$this->portNameSuffix);
        $this->wsPortType = $this->wsDefinitions->appendChild($portType);

        $binding = $this->dom->createElement('wsdl:binding');
        $binding->setAttribute('name', $this->wsdlName.$this->bindingNameSuffix);
        $binding->setAttribute('type', $this->targetNsPrefix.":".$this->wsdlName.$this->portNameSuffix);
        $this->wsBinding = $this->wsDefinitions->appendChild($binding);

        $soapBinding = $this->dom->createElement('soap:binding');
        if ($this->soapBindingStyle === SOAP_DOCUMENT) {
            $soapBinding->setAttribute('style', "document");
        } elseif ($this->soapBindingStyle === SOAP_RPC) {
            $soapBinding->setAttribute('style', "rpc");
        }
        $soapBinding->setAttribute('transport', $this->soapBindingTransport);
        $this->wsBinding->appendChild($soapBinding);

        $wsService = $this->dom->createElement('wsdl:service');
        $wsService->setAttribute('name', $this->wsdlName.$this->serviceNameSuffix);
        $this->wsService = $this->wsDefinitions->appendChild($wsService);

        $port = $this->dom->createElement('wsdl:port');
        $port->setAttribute('name', $this->wsdlName.$this->portNameSuffix);
        $port->setAttribute('binding', $this->targetNsPrefix.":".$this->wsdlName.$this->bindingNameSuffix);

        $soapAddress =  $this->dom->createElement('soap:address');
        $soapAddress->setAttribute('location', $this->getLocation());
        $port->appendChild($soapAddress);

        $this->wsService->appendChild($port);

        $this->extractTypes();
    }


private function addBindingOperations($operation, $input = false, $output = false)
    {
        $el = $this->dom->createElement('wsdl:operation');
        $el->setAttribute('name', $operation);

        $soapOperation = $this->dom->createElement('soap:operation');
        $soapOperation->setAttribute('soapAction', $this->wsdlTargetNamespace."/".$operation);
        $el->appendChild($soapOperation);

        if ($input === true) {
            $inp = $this->dom->createElement('wsdl:input');
            $body = $this->dom->createElement('soap:body');
            $body->setAttribute('use', 'literal');
            $inp->appendChild($body);
            $el->appendChild($inp);
        }

        if ($output === true) {
            $out = $this->dom->createElement('wsdl:output');
            $body = $this->dom->createElement('soap:body');
            $body->setAttribute('use', 'literal');
            $out->appendChild($body);
            $el->appendChild($out);
        }

        $this->wsBinding->appendChild($el);
    }

private function addBindingOperations ( $ operation , $ input = false , $ output = false ) { $ el = $ this -> dom -> createElement ( 'wsdl:operation' ) ; $ el -> setAttribute ( 'name' , $ operation ) ; $ soapOperation = $ this -> dom -> createElement ( 'soap:operation' ) ; $ soapOperation -> setAttribute ( 'soapAction' , $ this -> wsdlTargetNamespace . "/" . $ operation ) ; $ el -> appendChild ( $ soapOperation ) ; if ( $ input === true ) { $ inp = $ this -> dom -> createElement ( 'wsdl:input' ) ; $ body = $ this -> dom -> createElement ( 'soap:body' ) ; $ body -> setAttribute ( 'use' , 'literal' ) ; $ inp -> appendChild ( $ body ) ; $ el -> appendChild ( $ inp ) ; } if ( $ output === true ) { $ out = $ this -> dom -> createElement ( 'wsdl:output' ) ; $ body = $ this -> dom -> createElement ( 'soap:body' ) ; $ body -> setAttribute ( 'use' , 'literal' ) ; $ out -> appendChild ( $ body ) ; $ el -> appendChild ( $ out ) ; } $ this -> wsBinding -> appendChild ( $ el ) ; }

Add operations to bindings

<code>
<operation name="$operation">
<soap:operation
soapAction="targetNamespace/$operation" />
<input>
$input
</input>
<output>
$output
</output>
</operation>
</code>

@param string $operation Operation name
@param array  $input     Array of inputs [optional]
@param array  $output    Array of outputs [optional]

@return void


private function addPortOperations($operation, $input = false, $output = false)
    {
        $el = $this->dom->createElement("wsdl:operation");
        $el->setAttribute('name', $operation);
        if ($input === true) {
            $input = $this->dom->createElement('wsdl:input');
            $input->setAttribute('message', $this->targetNsPrefix.":".$operation);
        }
        if ($output === true) {
            $output = $this->dom->createElement('wsdl:output');
            $output->setAttribute('message', $this->targetNsPrefix.":". $operation."Response");
        }
        $el->appendChild($input);
        $el->appendChild($output);

        $this->wsPortType->appendChild($el);
    }

private function addPortOperations ( $ operation , $ input = false , $ output = false ) { $ el = $ this -> dom -> createElement ( "wsdl:operation" ) ; $ el -> setAttribute ( 'name' , $ operation ) ; if ( $ input === true ) { $ input = $ this -> dom -> createElement ( 'wsdl:input' ) ; $ input -> setAttribute ( 'message' , $ this -> targetNsPrefix . ":" . $ operation ) ; } if ( $ output === true ) { $ output = $ this -> dom -> createElement ( 'wsdl:output' ) ; $ output -> setAttribute ( 'message' , $ this -> targetNsPrefix . ":" . $ operation . "Response" ) ; } $ el -> appendChild ( $ input ) ; $ el -> appendChild ( $ output ) ; $ this -> wsPortType -> appendChild ( $ el ) ; }

Add operations to port

<code>
<operation name="$name">
<input message="tns:messageName" />
<output message="tns:messageName" />
</operation>
</code>

@param string $operation Name of the operation
@param array  $input     Array of inputs
@param array  $output    Array of outputs

@return void


private function createRefElement($ref)
    {
        $el = $this->dom->createElement($this->xmlSchemaPreffix.':element');
        $el->setAttribute('ref', $ref);

        return $el;
    }

private function createRefElement ( $ ref ) { $ el = $ this -> dom -> createElement ( $ this -> xmlSchemaPreffix . ':element' ) ; $ el -> setAttribute ( 'ref' , $ ref ) ; return $ el ; }

Create ref element

<code>
<element ref="$ref" />
</code>

@param string $ref

@return DOMElement


private function createElementWithComplexType($name, $elements)
    {
        //print_r('Create complex type '.$name."\n");
        $el = $this->dom->createElement($this->xmlSchemaPreffix.':element');
        $el->setAttribute('name', $name);

        $compType = $this->dom->createElement($this->xmlSchemaPreffix.':complexType');
        $seqType = $this->dom->createElement($this->xmlSchemaPreffix.':sequence');

        foreach ($elements as $element) {
            $seqType->appendChild($element);
        }

        $compType->appendChild($seqType);
        $el->appendChild($compType);

        return $el;
    }

Create an element with complex type

<code>
<element name="$name">
<complexType>
<sequence>
$elements
</sequence>
</complexType>
</element>
</code>

@param string $name     Element name
@param array  $elements Array of elements to include into sequence

@return DOMElement


private function createSimpleElement($name, $type)
    {
        $el = $this->dom->createElement($this->xmlSchemaPreffix.':element');
        $el->setAttribute('name', $name);
        if (preg_match('/:/', $type)) {
            $el->setAttribute('type', $type);
        } else {
            $el->setAttribute('type', $this->xmlSchemaPreffix.':'.$type);
        }

        return $el;
    }

private function createSimpleElement ( $ name , $ type ) { $ el = $ this -> dom -> createElement ( $ this -> xmlSchemaPreffix . ':element' ) ; $ el -> setAttribute ( 'name' , $ name ) ; if ( preg_match ( '/:/' , $ type ) ) { $ el -> setAttribute ( 'type' , $ type ) ; } else { $ el -> setAttribute ( 'type' , $ this -> xmlSchemaPreffix . ':' . $ type ) ; } return $ el ; }

Create simple element

<code>
<element name="$name" type="$type" />
</code>

@param string $name Element name
@param string $type Element type

@return DOMElement


public function resolveNamespace($shortNs)
    {
        if (!($this->dom instanceof \DOMDocument)) {
            throw new RuntimeException("DOM is not initialized");
        }

        if (!is_array($this->namespaces)) {
            $this->namespaces = $this->getDocNamespaces($this->dom);
        }

        if (array_key_exists($shortNs, $this->namespaces)) {
            return $this->namespaces[$shortNs];
        } else {
            return $shortNs;
        }
    }

public
function
resolveNamespace
(
$
shortNs
)
{
if
(
!
(
$
this
->
dom
instanceof
\
DOMDocument
)
)
{
throw
new
RuntimeException
(
"DOM is not initialized"
)
;
}
if
(
!
is_array
(
$
this
->
namespaces
)
)
{
$
this
->
namespaces
=
$
this
->
getDocNamespaces
(
$
this
->
dom
)
;
}
if
(
array_key_exists
(
$
shortNs
,
$
this
->
namespaces
)
)
{
return
$
this
->
namespaces
[
$
shortNs
]
;
}
else
{
return
$
shortNs
;
}
}

Resolve short namespace to long, or return the same code if not found

@param unknown_type $shortNs

@return string Long namespace

Resolve short namespace to long or return the same code if not found


public function parseDocComments($comments)
    {
        $comments = explode("\n", $comments);
        $commentsOut = array();
        $params = array();
        foreach ($comments as $com) {
            if (preg_match('/@/', $com)) {
                $com = preg_replace('/\* /', '', $com);
                $com = preg_replace('/@([a-zA-Z]*)( *)(.*)/', '$1|$3', $com);
                $com = explode('|', $com);
                //$arr = array();
                if (trim($com[0]) == 'param') {
                    $com[1] = $this->parseParamDocs(trim($com[1]));
                    array_push($params, $com[1]);
                    $com[1] = $params;
                    $com[0] = "params";
                }

                if (trim($com[0]) == 'return') {
                    $com[1] = $this->parseReturnDocs(trim($com[1]));
                }

                $commentsOut[trim($com[0])] = trim($com[1]);
                //array_push($commentsOut, array($com[0] => $com[1]));
            }
        }

        return $commentsOut;
    }

Parse Doc comments

@param string $comments

@return array


public static function isAssociateArray(array $arr)
    {
        if (!empty($arr)) {
            foreach ($arr as $k => $v) {
                if (is_int($k)) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

public static function isAssociateArray ( array $ arr ) { if ( ! empty ( $ arr ) ) { foreach ( $ arr as $ k => $ v ) { if ( is_int ( $ k ) ) { return false ; } } return true ; } return false ; }

Test if an array is an associate array

@param array $arr
@return true if $arr is an associative array


public static function getWebhooksEvent($payLoad)
    {
        JsonValidator::validate($payLoad);
        $string_arry = json_decode($payLoad, true);
        $obj = ReflectionUtil::constructObjectFromWebhooksArray($string_arry, WebhooksService::WEBHOOKSWRAPPERNAME);
        return $obj;
    }

public static function getWebhooksEvent ( $ payLoad ) { JsonValidator :: validate ( $ payLoad ) ; $ string_arry = json_decode ( $ payLoad , true ) ; $ obj = ReflectionUtil :: constructObjectFromWebhooksArray ( $ string_arry , WebhooksService :: WEBHOOKSWRAPPERNAME ) ; return $ obj ; }

Convert the payLoad of webhook to object

@param $payLoad
The payload to be converted
@return object
The deserialization format for the payload


public static function verifyPayload($token, $payload, $intuitHeaderSignature)
    {
        $verifier = new TokenVerifier($token);
        return $verifier->verifyPayLoad($payload, $intuitHeaderSignature);
    }

public static function verifyPayload ( $ token , $ payload , $ intuitHeaderSignature ) { $ verifier = new TokenVerifier ( $ token ) ; return $ verifier -> verifyPayLoad ( $ payload , $ intuitHeaderSignature ) ; }

use Token to verifier the payload is sent from Intuit

@param $token
The webhook Token get from Intuit
@param $payload
The payload for the webhook events
@param $intuitHeaderSignature
The header signature
@return bool
True if it is valid; otherwise return false;


private static function isKeyInComplexList($key, $complexList = NULL){
     if(isset($complexList)) {$ObjectMap = $complexList;}
     else {$ObjectMap = FacadeClassMapper::classMethodToList();}

     foreach($ObjectMap as $objectMethodName => $entityType)
     {
         if(in_array($key, $entityType)){
            return $objectMethodName;
         }
     }
     return false;
   }

private static function isKeyInComplexList ( $ key , $ complexList = NULL ) { if ( isset ( $ complexList ) ) { $ ObjectMap = $ complexList ; } else { $ ObjectMap = FacadeClassMapper :: classMethodToList ( ) ; } foreach ( $ ObjectMap as $ objectMethodName => $ entityType ) { if ( in_array ( $ key , $ entityType ) ) { return $ objectMethodName ; } } return false ; }

A helper to check if a key is a complex type of an Object
@param  $key, $complexList = NULL
@return true
If the key is found in the complex type
false
If the key is not found in the complex type


public static function getClassMethod($className, $methodName){
          try
          {
              $helperRefelctionMethod = new \ReflectionMethod($className, $methodName);
              return $helperRefelctionMethod;
          } catch(\Exception $e){
               return null;
          }
   }

public static function getClassMethod ( $ className , $ methodName ) { try { $ helperRefelctionMethod = new \ ReflectionMethod ( $ className , $ methodName ) ; return $ helperRefelctionMethod ; } catch ( \ Exception $ e ) { return null ; } }

Find the Method by given parameter on FacadeHelper.php Class
@param The class name
@return \ReflectionMethod|null The method if found. If not found, return null.

Find the Method by given parameter on FacadeHelper . php Class


public static function getIPPReferenceTypeBasedOnArray($data){
      $trimedDataArray = FacadeHelper::trimSpacesForArrayKeys($data);
      if(is_object($trimedDataArray)){
           if($trimedDataArray instanceof IPPReferenceType){
               return $trimedDataArray;
           }else{
               throw new \Exception("The assigned obj to IPPReferenceType is not matched with IPPReferenceType.");
           }
      }
      //THe ReferenceDataType should only contain at most Two elements
      if(is_array($trimedDataArray)){
        if(sizeof($trimedDataArray) >= 3){
          throw new \Exception("Trying to construct IPPReferenceType based on Array. The array should contain at most two fields. name and value");
        }

        $IPPReferenceType = new IPPReferenceType();
        if(isset($trimedDataArray['value']) || isset($trimedDataArray['Value']) ) {
            $val = isset($trimedDataArray['value']) ? $trimedDataArray['value'] : $trimedDataArray['Value'];
            $IPPReferenceType->value = $val;
        }else{
            throw new \Exception("Passed array has no key for 'Value' when contructing an ReferenceType");
        }

        if(isset($trimedDataArray['name']) || isset($trimedDataArray['Name']) ){
           $nam = isset($trimedDataArray['name']) ? $trimedDataArray['name'] : $trimedDataArray['Name'];
           $IPPReferenceType->name = $nam;
         }
        return $IPPReferenceType;
      }else if(is_numeric($trimedDataArray) || is_string($trimedDataArray)){
        $IPPReferenceType = new IPPReferenceType();
        $IPPReferenceType->value = $trimedDataArray;
        return $IPPReferenceType;
      }else{
        throw new \Exception("Can't convert Passed Parameter to IPPReferenceType.");
      }
  }

Construct an IPPReferenceType based on passed Array or String.
If it is passed as an array, handle it.
If it is passed as a String. Construct an array and put the String on the value
If it is passed as an obj, compare it with IPPReferenceType and return the object
@param $data
It can either be an array or a String, or obj

Construct an IPPReferenceType based on passed Array or String . If it is passed as an array handle it . If it is passed as a String . Construct an array and put the String on the value If it is passed as an obj compare it with IPPReferenceType and return the object


public static function getIPPId($data){
    //Convert an IPPId based on the Data
    if(!isset($data)) throw new \Exception("Passed param for IPPid can't be null");
    if(is_object($data)){
         if($data instanceof IPPid){
             return $data;
         }else{
             throw new \Exception("The assigned obj to IPPid is not matched with IPPid.");
         }
    }
    if(is_array($data)){
       $firstElementValue = reset($data);
    }else if(is_numeric($data)){
       $firstElementValue = $data;
    }else{
      throw new \Exception("Passed param for Ippid has either be an array or numeric value.");
    }

    $_id = new IPPid();
    $_id->value = $firstElementValue;
    return $_id;
  }

If passed params is array, the first element of Array is used in IPPid.
If passed params is not an array, the the value is used for Ippid.
If passed params is an obj, the the value is simply returned.
@param $data
It can either be an array or a numeric representation

If passed params is array the first element of Array is used in IPPid . If passed params is not an array the the value is used for Ippid . If passed params is an obj the the value is simply returned .


public static function mergeObj($objA, $objB){
      if(get_class($objA) != get_class($objB)) throw new \Exception("Can't assign object value to a different type.");
      $property_fields = get_object_vars($objA);
      foreach ($property_fields as $propertyName => $val){
          $BsValue = $objB->$propertyName;
          if(isset($BsValue)){
               $objA->$propertyName = $BsValue;
          }
      }
      return $objA;
  }

Override the content from Object B to Object A
Don't use array_merge here. As the NUll Value will be overriden as well
Remove the !empty check. For integer or string that is 0, it needs to be passed and set.

Override the content from Object B to Object A Don t use array_merge here . As the NUll Value will be overriden as well Remove the !empty check . For integer or string that is 0 it needs to be passed and set .


private function getHeaders($headers){
      if(!isset($headers) || empty($headers)){
          throw new SdkException("Error. The headers set for cURL are either NULL or Empty");
      }else{
          $convertedHeaders = $this->convertHeaderArrayToHeaders($headers);
          return $convertedHeaders;
      }
    }

Get headers for the QuciKbooks Online Response


private function setSSL(&$curl_opt, $verifySSL){
      $tlsVersion = $this->basecURL->versionOfTLS();
      $versions = ['TLS 1.2', 'TLS 1.3'];
      if(! in_array($tlsVersion, $versions)){
          throw new SdkException("Error. Checking TLS 1.2/1.3 version failed. Please make sure your PHP cURL supports TSL 1.2/1.3");
      }
      if($verifySSL){
          $curl_opt[CURLOPT_SSL_VERIFYPEER] = true;
          $curl_opt[CURLOPT_SSL_VERIFYHOST] = 2;
          //based on spec, if TSL 1.2 is supported, it will use the TLS 1.2 or latest version by default
          //$curl_opt[CURLOPT_SSLVERSION] = 6;
          $curl_opt[CURLOPT_CAINFO] = CoreConstants::getCertPath(); //Pem certification Key Path
      }
    }

Set the SSL certifcate path and corresponding varaibles for cURL


public function convertHeaderArrayToHeaders(array $headerArray){
         $headers = array();
         foreach($headerArray as $k => $v){
              $headers[] = $k . ":" . $v;
         }
         return $headers;
    }

public function convertHeaderArrayToHeaders ( array $ headerArray ) { $ headers = array ( ) ; foreach ( $ headerArray as $ k => $ v ) { $ headers [ ] = $ k . ":" . $ v ; } return $ headers ; }

Convert an Array to Curl Headers
@param array $headerArray The request headers
@return array Curl Headers


public function addWarning($warning, $stackPtr, $code, $data = [], $severity = 0, $fixable = false): bool
    {
        if (! $this->isSniffClassWarningAllowed($this->activeSniffClass)) {
            return false;
        }

        return $this->addError($warning, $stackPtr, $code, $data, $severity, $fixable);
    }

public function addWarning ( $ warning , $ stackPtr , $ code , $ data = [ ] , $ severity = 0 , $ fixable = false ) : bool { if ( ! $ this -> isSniffClassWarningAllowed ( $ this -> activeSniffClass ) ) { return false ; } return $ this -> addError ( $ warning , $ stackPtr , $ code , $ data , $ severity , $ fixable ) ; }

Allow only specific classes

{@inheritdoc}


public function resolveFromInput(InputInterface $input): void
    {
        /** @var string[] $sources */
        $sources = $input->getArgument(Option::SOURCE);
        $this->setSources($sources);
        $this->isFixer = (bool) $input->getOption(Option::FIX);
        $this->shouldClearCache = (bool) $input->getOption(Option::CLEAR_CACHE);
        $this->showProgressBar = $this->canShowProgressBar($input);
        $this->showErrorTable = ! (bool) $input->getOption(Option::NO_ERROR_TABLE);
    }

public
function
resolveFromInput
(
InputInterface
$
input
)
:
void
{
/** @var string[] $sources */
$
sources
=
$
input
->
getArgument
(
Option
::
SOURCE
)
;
$
this
->
setSources
(
$
sources
)
;
$
this
->
isFixer
=
(
bool
)
$
input
->
getOption
(
Option
::
FIX
)
;
$
this
->
shouldClearCache
=
(
bool
)
$
input
->
getOption
(
Option
::
CLEAR_CACHE
)
;
$
this
->
showProgressBar
=
$
this
->
canShowProgressBar
(
$
input
)
;
$
this
->
showErrorTable
=
!
(
bool
)
$
input
->
getOption
(
Option
::
NO_ERROR_TABLE
)
;
}

Needs to run in the start of the life cycle, since the rest of workflow uses it.

Needs to run in the start of the life cycle since the rest of workflow uses it .


public function setNameAttribute($value)
    {
        $value = trim($value);
        $this->attributes['name'] = $value;
        $this->attributes['normalized'] = app(TagService::class)->normalize($value);
    }

public function setNameAttribute ( $ value ) { $ value = trim ( $ value ) ; $ this -> attributes [ 'name' ] = $ value ; $ this -> attributes [ 'normalized' ] = app ( TagService :: class ) -> normalize ( $ value ) ; }

Set the name attribute on the model.

@param string $value


public function scopeByName(Builder $query, string $value): Builder
    {
        $normalized = app(TagService::class)->normalize($value);

        return $query->where('normalized', $normalized);
    }

public function scopeByName ( Builder $ query , string $ value ) : Builder { $ normalized = app ( TagService :: class ) -> normalize ( $ value ) ; return $ query -> where ( 'normalized' , $ normalized ) ; }

Scope to find tags by name.

@param \Illuminate\Database\Eloquent\Builder $query
@param $value

@return \Illuminate\Database\Eloquent\Builder


protected static function bootTaggable(): void
    {
        static::deleting(function ($model) {
            if (!method_exists($model, 'runSoftDelete') || $model->isForceDeleting()) {
                $model->detag();
            }
        });
    }

protected static function bootTaggable ( ) : void { static :: deleting ( function ( $ model ) { if ( ! method_exists ( $ model , 'runSoftDelete' ) || $ model -> isForceDeleting ( ) ) { $ model -> detag ( ) ; } } ) ; }

Boot the trait.

Listen for the deleting event of a model, then remove the relation between it and tags


public function tag($tags): self
    {
        $tags = app(TagService::class)->buildTagArray($tags);

        foreach ($tags as $tagName) {
            $this->addOneTag($tagName);
            $this->load('tags');
        }

        event(new ModelTagged($this, $tags));

        return $this;
    }

public function tag ( $ tags ) : self { $ tags = app ( TagService :: class ) -> buildTagArray ( $ tags ) ; foreach ( $ tags as $ tagName ) { $ this -> addOneTag ( $ tagName ) ; $ this -> load ( 'tags' ) ; } event ( new ModelTagged ( $ this , $ tags ) ) ; return $ this ; }

Attach one or multiple tags to the model.

@param string|array $tags

@return self

Attach one or multiple tags to the model .


public function untag($tags): self
    {
        $tags = app(TagService::class)->buildTagArray($tags);

        foreach ($tags as $tagName) {
            $this->removeOneTag($tagName);
        }

        event(new ModelUntagged($this, $tags));

        return $this->load('tags');
    }

public function untag ( $ tags ) : self { $ tags = app ( TagService :: class ) -> buildTagArray ( $ tags ) ; foreach ( $ tags as $ tagName ) { $ this -> removeOneTag ( $ tagName ) ; } event ( new ModelUntagged ( $ this , $ tags ) ) ; return $ this -> load ( 'tags' ) ; }

Detach one or multiple tags from the model.

@param string|array $tags

@return self

Detach one or multiple tags from the model .


protected function addOneTag(string $tagName): void
    {
        /** @var Tag $tag */
        $tag = app(TagService::class)->findOrCreate($tagName);
        $tagKey = $tag->getKey();

        if (!$this->getAttribute('tags')->contains($tagKey)) {
            $this->tags()->attach($tagKey);
        }
    }

protected
function
addOneTag
(
string
$
tagName
)
:
void
{
/** @var Tag $tag */
$
tag
=
app
(
TagService
::
class
)
->
findOrCreate
(
$
tagName
)
;
$
tagKey
=
$
tag
->
getKey
(
)
;
if
(
!
$
this
->
getAttribute
(
'tags'
)
->
contains
(
$
tagKey
)
)
{
$
this
->
tags
(
)
->
attach
(
$
tagKey
)
;
}
}

Add one tag to the model.

@param string $tagName


protected function removeOneTag(string $tagName): void
    {
        $tag = app(TagService::class)->find($tagName);

        if ($tag) {
            $this->tags()->detach($tag);
        }
    }

protected function removeOneTag ( string $ tagName ) : void { $ tag = app ( TagService :: class ) -> find ( $ tagName ) ; if ( $ tag ) { $ this -> tags ( ) -> detach ( $ tag ) ; } }

Remove one tag from the model

@param string $tagName


public function hasTag($tag): bool
    {
        if ($tag instanceof Tag) {
            $normalized = $tag->getAttribute('normalized');
        } else {
            $normalized = app(TagService::class)->normalize($tag);
        }

        return in_array($normalized, $this->getTagArrayNormalizedAttribute(), true);
    }

public function hasTag ( $ tag ) : bool { if ( $ tag instanceof Tag ) { $ normalized = $ tag -> getAttribute ( 'normalized' ) ; } else { $ normalized = app ( TagService :: class ) -> normalize ( $ tag ) ; } return in_array ( $ normalized , $ this -> getTagArrayNormalizedAttribute ( ) , true ) ; }

Determine if a given tag is attached to the model.

@param Tag|string $tag

@return bool

Determine if a given tag is attached to the model .


public function scopeWithAllTags(Builder $query, $tags): Builder
    {
        /** @var TagService $service */
        $service = app(TagService::class);
        $normalized = $service->buildTagArrayNormalized($tags);

        // If there are no tags specified, then there
        // can't be any results so short-circuit
        if (count($normalized) === 0) {
            if (config('taggable.throwEmptyExceptions')) {
                throw new NoTagsSpecifiedException('Empty tag data passed to withAllTags scope.');
            }

            return $query->where(\DB::raw(1), 0);
        }

        $tagKeys = $service->getTagModelKeys($normalized);

        // If some of the tags specified don't exist, then there can't
        // be any models with all the tags, so so short-circuit
        if (count($tagKeys) !== count($normalized)) {
            return $query->where(\DB::raw(1), 0);
        }

        $alias = $this->taggableCreateNewAlias(__FUNCTION__);
        $morphTagKeyName = $this->getQualifiedRelatedPivotKeyNameWithAlias($alias);

        return $this->prepareTableJoin($query, 'inner', $alias)
            ->whereIn($morphTagKeyName, $tagKeys)
            ->havingRaw("COUNT(DISTINCT {$morphTagKeyName}) = ?", [count($tagKeys)]);
    }

Query scope for models that have all of the given tags.

@param Builder $query
@param array|string $tags

@return Builder
@throws \Cviebrock\EloquentTaggable\Exceptions\NoTagsSpecifiedException
@throws \ErrorException

Query scope for models that have all of the given tags .


public function scopeIsTagged(Builder $query): Builder
    {
        $alias = $this->taggableCreateNewAlias(__FUNCTION__);
        return $this->prepareTableJoin($query, 'inner', $alias);
    }

public function scopeIsTagged ( Builder $ query ) : Builder { $ alias = $ this -> taggableCreateNewAlias ( __FUNCTION__ ) ; return $ this -> prepareTableJoin ( $ query , 'inner' , $ alias ) ; }

Query scope for models that have any tag.

@param Builder $query

@return Builder

Query scope for models that have any tag .


public function scopeWithoutAllTags(Builder $query, $tags, bool $includeUntagged = false): Builder
    {
        /** @var TagService $service */
        $service = app(TagService::class);
        $normalized = $service->buildTagArrayNormalized($tags);
        $tagKeys = $service->getTagModelKeys($normalized);
        $tagKeyList = implode(',', $tagKeys);

        $alias = $this->taggableCreateNewAlias(__FUNCTION__);
        $morphTagKeyName = $this->getQualifiedRelatedPivotKeyNameWithAlias($alias);

        $query = $this->prepareTableJoin($query, 'left', $alias)
            ->havingRaw("COUNT(DISTINCT CASE WHEN ({$morphTagKeyName} IN ({$tagKeyList})) THEN {$morphTagKeyName} ELSE NULL END) < ?",
                [count($tagKeys)]);

        if (!$includeUntagged) {
            $query->havingRaw("COUNT(DISTINCT {$morphTagKeyName}) > 0");
        }

        return $query;
    }

Query scope for models that do not have all of the given tags.

@param Builder $query
@param string|array $tags
@param bool $includeUntagged

@return Builder
@throws \ErrorException

Query scope for models that do not have all of the given tags .


public function scopeIsNotTagged(Builder $query): Builder
    {
        $alias = $this->taggableCreateNewAlias(__FUNCTION__);
        $morphForeignKeyName = $this->getQualifiedForeignPivotKeyNameWithAlias($alias);

        return $this->prepareTableJoin($query, 'left', $alias)
            ->havingRaw("COUNT(DISTINCT {$morphForeignKeyName}) = 0");
    }

Query scope for models that does not have have any tags.

@param Builder $query

@return Builder

Query scope for models that does not have have any tags .


public static function renameTag(string $oldTag, string $newTag): int
    {
        return app(TagService::class)->renameTags($oldTag, $newTag, static::class);
    }

public static function renameTag ( string $ oldTag , string $ newTag ) : int { return app ( TagService :: class ) -> renameTags ( $ oldTag , $ newTag , static :: class ) ; }

Rename one the tags for the called class.

@param string $oldTag
@param string $newTag

@return int

Rename one the tags for the called class .


public static function popularTags(int $limit = null, int $minCount = 1): array
    {
        /** @var Collection $tags */
        $tags = app(TagService::class)->getPopularTags($limit, static::class, $minCount);

        return $tags->pluck('taggable_count', 'name')->all();
    }

public
static
function
popularTags
(
int
$
limit
=
,
int
$
minCount
=
1
)
:
array
{
/** @var Collection $tags */
$
tags
=
app
(
TagService
::
class
)
->
getPopularTags
(
$
limit
,
static
::
class
,
$
minCount
)
;
return
$
tags
->
pluck
(
'taggable_count'
,
'name'
)
->
all
(
)
;
}

Get the most popular tags for the called class.

@param int $limit
@param int $minCount

@return array

Get the most popular tags for the called class .


private function getQualifiedRelatedPivotKeyNameWithAlias(string $alias): string
    {
        $morph = $this->tags();

        return $morph->getTable() . '_' . $alias .
            '.' . $morph->getRelatedPivotKeyName();
    }

Returns the Related Pivot Key Name with the table alias.

@param string $alias

@return string

Returns the Related Pivot Key Name with the table alias .


private function getQualifiedForeignPivotKeyNameWithAlias(string $alias): string
    {
        $morph = $this->tags();

        return $morph->getTable() . '_' . $alias .
            '.' . $morph->getForeignPivotKeyName();
    }

Returns the Foreign Pivot Key Name with the table alias.

@param string $alias

@return string

Returns the Foreign Pivot Key Name with the table alias .


private function taggableCreateNewAlias(string $scope): string
    {
        $this->taggableAliasSequence++;
        $alias = strtolower($scope) . '_' . $this->taggableAliasSequence;

        return $alias;
    }

private function taggableCreateNewAlias ( string $ scope ) : string { $ this -> taggableAliasSequence ++ ; $ alias = strtolower ( $ scope ) . '_' . $ this -> taggableAliasSequence ; return $ alias ; }

Create a new alias to use on scopes to be able to combine many scopes

@param string $scope

@return string


public function buildTagArray($tags): array
    {
        if (is_array($tags)) {
            $array = $tags;
        } elseif ($tags instanceof BaseCollection) {
            $array = $this->buildTagArray($tags->all());
        } elseif (is_string($tags)) {
            $array = preg_split(
                '#[' . preg_quote(config('taggable.delimiters'), '#') . ']#',
                $tags,
                null,
                PREG_SPLIT_NO_EMPTY
            );
        } else {

            throw new \ErrorException(
                __CLASS__ . '::' . __METHOD__ . ' expects parameter 1 to be string, array or Collection; ' .
                gettype($tags) . ' given'
            );
        }

        return array_filter(
            array_map('trim', $array)
        );
    }

Convert a delimited string into an array of tag strings.

@param string|array|\Illuminate\Support\Collection $tags

@return array
@throws \ErrorException

Convert a delimited string into an array of tag strings .


public function getTagModelKeys(array $normalized = []): array
    {
        if (count($normalized) === 0) {
            return [];
        }

        return $this->tagModel::whereIn('normalized', $normalized)
            ->pluck('tag_id')
            ->toArray();
    }

public function getTagModelKeys ( array $ normalized = [ ] ) : array { if ( count ( $ normalized ) === 0 ) { return [ ] ; } return $ this -> tagModel :: whereIn ( 'normalized' , $ normalized ) -> pluck ( 'tag_id' ) -> toArray ( ) ; }

Return an array of tag models for the given normalized tags

@param array $normalized

@return array


public function makeTagList(Model $model, string $field = 'name'): string
    {
        $tags = $this->makeTagArray($model, $field);

        return $this->joinList($tags);
    }

public function makeTagList ( Model $ model , string $ field = 'name' ) : string { $ tags = $ this -> makeTagArray ( $ model , $ field ) ; return $ this -> joinList ( $ tags ) ; }

Build a delimited string from a model's tags.

@param \Illuminate\Database\Eloquent\Model $model
@param string $field

@return string

Build a delimited string from a model s tags .


public function makeTagArray(Model $model, string $field = 'name'): array
    {
        /** @var Collection $tags */
        $tags = $model->tags;

        return $tags->pluck($field)->all();
    }

public
function
makeTagArray
(
Model
$
model
,
string
$
field
=
'name'
)
:
array
{
/** @var Collection $tags */
$
tags
=
$
model
->
tags
;
return
$
tags
->
pluck
(
$
field
)
->
all
(
)
;
}

Build a simple array of a model's tags.

@param \Illuminate\Database\Eloquent\Model $model
@param string $field

@return array

Build a simple array of a model s tags .


public function getAllTags($class = null): Collection
    {
        if ($class === null) {
            return $this->tagModel::all();
        }

        if ($class instanceof Model) {
            $class = get_class($class);
        }

        $tagTable = $this->getQualifiedTagTableName();
        $pivotTable = $this->getQualifiedPivotTableName($class);

        $sql = "SELECT DISTINCT t.*
              FROM {$pivotTable} tt
              LEFT JOIN {$tagTable} t ON tt.tag_id=t.tag_id
              WHERE tt.taggable_type = ?";

        return $this->tagModel::fromQuery($sql, [$class]);
    }

Get all Tags for the given class, or all classes.

@param \Illuminate\Database\Eloquent\Model|string|null $class

@return Collection

Get all Tags for the given class or all classes .


public function getAllUnusedTags(): Collection
    {
        $tagTable = $this->getQualifiedTagTableName();
        $pivotTable = $this->getQualifiedPivotTableName();

        $sql = "SELECT t.*
            FROM {$tagTable} t
            LEFT JOIN {$pivotTable} tt ON tt.tag_id=t.tag_id
            WHERE tt.taggable_id IS NULL";

        return $this->tagModel::fromQuery($sql);
    }

Get all Tags that are unused by any model.

@return \Illuminate\Database\Eloquent\Collection

Get all Tags that are unused by any model .


public function renameTags(string $oldName, string $newName, $class = null): int
    {
        // If no class is specified, we can do the rename with a simple SQL update
        if ($class === null) {
            return $this->tagModel::where('normalized', $this->normalize($oldName))
                ->update([
                    'name'       => $newName,
                    'normalized' => $this->normalize($newName),
                ]);
        }

        if (!($class instanceof Model)) {
            $class = new $class;
        }

        // First find the old tag
        $oldTag = $this->find($oldName);

        // If the old tag doesn't exist, we can short-circuit the process
        if (!$oldTag) {
            return 0;
        }

        // Find or create the new tag
        $newTag = $this->findOrCreate($newName);

        /** @var MorphToMany $morph */
        $morph = $class->tags();
        $pivot = $morph->newPivot();

        $relatedKeyName = $pivot->getRelatedKey();
        $relatedMorphType = $morph->getMorphType();

        return $pivot
            ->where($relatedKeyName, $oldTag->getKey())
            ->where($relatedMorphType, get_class($class))
            ->update([
                $relatedKeyName => $newTag->getKey(),
            ]);
    }

Rename tags, across all or only one model.

@param string $oldName
@param string $newName
@param \Illuminate\Database\Eloquent\Model|string|null $class

@return int

Rename tags across all or only one model .


private function getQualifiedTagTableName(): string
    {
        /** @var Tag $tag */
        $tag = new $this->tagModel;

        return $tag->getConnection()->getTablePrefix() .
                $tag->getTable();
    }

private
function
getQualifiedTagTableName
(
)
:
string
{
/** @var Tag $tag */
$
tag
=
new
$
this
->
tagModel
;
return
$
tag
->
getConnection
(
)
->
getTablePrefix
(
)
.
$
tag
->
getTable
(
)
;
}

Get the qualified table name for the Tag model.

@return string

Get the qualified table name for the Tag model .


private function getQualifiedPivotTableName(string $class=null): string
    {
        /** @var \Cviebrock\EloquentTaggable\Taggable $instance */
        $instance = $class ? new $class : new class extends Model { use Taggable; };

        return $instance->tags()->getConnection()->getTablePrefix() .
                $instance->tags()->getTable();
    }

Get the qualified table name for the Tag model's pivot table.

@param string $class

@return string

Get the qualified table name for the Tag model s pivot table .


protected function saveCompoundParameterData(array $compoundData)
    {
        foreach ($compoundData as $name => $value) {
            $matchExists = false;
            foreach ($this->parameterNameMapper as $regex => $parameterName) {
                if (preg_match($regex, $name, $matches) === 1) {
                    $parameterLastIndex = '';

                    if (isset($matches[1])) {
                        $parameterLastIndex = $matches[1];
                    }

                    $matchExists = true;
                    $this->parameters[$parameterName . $parameterLastIndex] = $value;

                    break;
                }
            }

            if (!$matchExists) {
                throw new \InvalidArgumentException("Unknown parameter $name for " . get_class($this) . ' data');
            }
        }
    }

protected
function
saveCompoundParameterData
(
array
$
compoundData
)
{
foreach
(
$
compoundData
as
$
name
=>
$
value
)
{
$
matchExists
=
false
;
foreach
(
$
this
->
parameterNameMapper
as
$
regex
=>
$
parameterName
)
{
if
(
preg_match
(
$
regex
,
$
name
,
$
matches
)
===
1
)
{
$
parameterLastIndex
=
''
;
if
(
isset
(
$
matches
[
1
]
)
)
{
$
parameterLastIndex
=
$
matches
[
1
]
;
}
$
matchExists
=
true
;
$
this
->
parameters
[
$
parameterName
.
$
parameterLastIndex
]
=
$
value
;
break
;
}
}
if
(
!
$
matchExists
)
{
throw
new
\
InvalidArgumentException
(
"Unknown parameter $name for "
.
get_class
(
$
this
)
.
' data'
)
;
}
}
}

Translates the human readable names into the payload ones and saves them along with the values.

@param array $compoundData
@throws \InvalidArgumentException

Translates the human readable names into the payload ones and saves them along with the values .


public function build($url, array $singleParameters, array $compoundParameters)
    {
        $singlesPost = $this->getSingleParametersPayload($singleParameters);

        $compoundsPost = $this->getCompoundParametersPayload($compoundParameters);

        $this->payloadParameters = array_merge($singlesPost, $compoundsPost);

        if (!empty($this->cacheBuster)) {
            $this->payloadParameters['z'] = $this->cacheBuster;
        }
        $query = http_build_query($this->payloadParameters, null, ini_get('arg_separator.output'), PHP_QUERY_RFC3986);
        return $url . '?' . $query;
    }

public function build ( $ url , array $ singleParameters , array $ compoundParameters ) { $ singlesPost = $ this -> getSingleParametersPayload ( $ singleParameters ) ; $ compoundsPost = $ this -> getCompoundParametersPayload ( $ compoundParameters ) ; $ this -> payloadParameters = array_merge ( $ singlesPost , $ compoundsPost ) ; if ( ! empty ( $ this -> cacheBuster ) ) { $ this -> payloadParameters [ 'z' ] = $ this -> cacheBuster ; } $ query = http_build_query ( $ this -> payloadParameters , null , ini_get ( 'arg_separator.output' ) , PHP_QUERY_RFC3986 ) ; return $ url . '?' . $ query ; }

Build URL which is sent to Google Analytics

@internal
@param string $url
@param SingleParameter[] $singleParameters
@param CompoundParameterCollection[] $compoundParameters
@return string


private function getSingleParametersPayload(array $singleParameters)
    {
        $postData = [];
        $cacheBuster = new CacheBuster();

        foreach ($singleParameters as $parameterObj) {
            if ($parameterObj->getName() === $cacheBuster->getName()) {
                $this->cacheBuster = $parameterObj->getValue();
                continue;
            }

            $postData[$parameterObj->getName()] = $parameterObj->getValue();
        }

        return $postData;
    }

private function getSingleParametersPayload ( array $ singleParameters ) { $ postData = [ ] ; $ cacheBuster = new CacheBuster ( ) ; foreach ( $ singleParameters as $ parameterObj ) { if ( $ parameterObj -> getName ( ) === $ cacheBuster -> getName ( ) ) { $ this -> cacheBuster = $ parameterObj -> getValue ( ) ; continue ; } $ postData [ $ parameterObj -> getName ( ) ] = $ parameterObj -> getValue ( ) ; } return $ postData ; }

Prepares all the Single Parameters to be sent to GA.

@param SingleParameter[] $singleParameters
@return array

Prepares all the Single Parameters to be sent to GA .


private function getCompoundParametersPayload(array $compoundParameters)
    {
        $postData = [];

        foreach ($compoundParameters as $compoundCollection) {
            $parameterArray = $compoundCollection->getParametersArray();
            $postData = array_merge($postData, $parameterArray);
        }

        return $postData;
    }

private function getCompoundParametersPayload ( array $ compoundParameters ) { $ postData = [ ] ; foreach ( $ compoundParameters as $ compoundCollection ) { $ parameterArray = $ compoundCollection -> getParametersArray ( ) ; $ postData = array_merge ( $ postData , $ parameterArray ) ; } return $ postData ; }

Prepares compound parameters inside collections to be sent to GA.

@param CompoundParameterCollection[] $compoundParameters
@return array

Prepares compound parameters inside collections to be sent to GA .


protected function addIndex($string, $index)
    {
        if (empty($string)) {
            throw new InvalidNameException('Name attribute not defined for class ' . get_class($this));
        }

        if (strpos($string, $this->indexPlaceholder) !== false) {
            if (!is_numeric($index) || $index < $this->minIndex() || $index > $this->maxIndex()) {
                throw new InvalidIndexException(
                    'When setting parameter ' . get_class($this)
                    . ' a numeric index between 1 - 200 must be passed for the second argument'
                );
            }
        }

        return str_replace($this->indexPlaceholder, $index, $string);
    }

Replaces a placeholder for the index passed.

@param string $string
@param int $index
@return string
@throws \TheIconic\Tracking\GoogleAnalytics\Exception\InvalidIndexException
@throws \TheIconic\Tracking\GoogleAnalytics\Exception\InvalidNameException

Replaces a placeholder for the index passed .


public function getDebugResponse()
    {
        $debugResponse = [];

        if (!empty($this->responseBody)) {
            $debugResponse = json_decode($this->responseBody, true);
            $debugResponse = (is_array($debugResponse)) ? $debugResponse : [];
        }

        return $debugResponse;
    }

public function getDebugResponse ( ) { $ debugResponse = [ ] ; if ( ! empty ( $ this -> responseBody ) ) { $ debugResponse = json_decode ( $ this -> responseBody , true ) ; $ debugResponse = ( is_array ( $ debugResponse ) ) ? $ debugResponse : [ ] ; } return $ debugResponse ; }

Gets the debug response. Returns empty array if no response found.

@api
@return array

Gets the debug response . Returns empty array if no response found .


protected function getEndpoint()
    {
        return ($this->isDebug) ? $this->uriScheme . $this->debugEndpoint : $this->uriScheme . $this->endpoint;
    }

protected function getEndpoint ( ) { return ( $ this -> isDebug ) ? $ this -> uriScheme . $ this -> debugEndpoint : $ this -> uriScheme . $ this -> endpoint ; }

Gets the full endpoint to GA.

@return string


protected function sendHit($methodName)
    {
        $hitType = strtoupper(substr($methodName, 4));

        $hitConstant = $this->getParameterClassConstant(
            'TheIconic\Tracking\GoogleAnalytics\Parameters\Hit\HitType::HIT_TYPE_' . $hitType,
            'Hit type ' . $hitType . ' is not defined, check spelling'
        );

        $this->setHitType($hitConstant);

        if (!$this->hasMinimumRequiredParameters()) {
            throw new InvalidPayloadDataException();
        }

        if ($this->isDisabled) {
            return new NullAnalyticsResponse();
        }

        return $this->getHttpClient()->post($this->getUrl(), $this->getHttpClientOptions());
    }

Sends a hit to GA. The hit will contain in the payload all the parameters added before.

@param $methodName
@return AnalyticsResponseInterface
@throws Exception\InvalidPayloadDataException

Sends a hit to GA . The hit will contain in the payload all the parameters added before .


protected function getHttpClientOptions()
    {
        $options = ['async' => $this->isAsyncRequest];

        if (isset($this->options['timeout'])) {
            $options['timeout'] = $this->options['timeout'];
        }

        return $options;
    }

protected function getHttpClientOptions ( ) { $ options = [ 'async' => $ this -> isAsyncRequest ] ; if ( isset ( $ this -> options [ 'timeout' ] ) ) { $ options [ 'timeout' ] = $ this -> options [ 'timeout' ] ; } return $ options ; }

Build the options array for the http client based on the Analytics object options.

@return array

Build the options array for the http client based on the Analytics object options .


public function getUrl()
    {
        $prepareUrl = new PrepareUrl;

        return $prepareUrl->build(
            $this->getEndpoint(),
            $this->singleParameters,
            $this->compoundParametersCollections
        );
    }

public function getUrl ( ) { $ prepareUrl = new PrepareUrl ; return $ prepareUrl -> build ( $ this -> getEndpoint ( ) , $ this -> singleParameters , $ this -> compoundParametersCollections ) ; }

Build and returns URL used to send to Google Analytics.

@api
@return string

Build and returns URL used to send to Google Analytics .


protected function hasMinimumRequiredParameters()
    {
        $minimumRequiredParameters = [
            'v' => false,
            'tid' => false,
            'cid' => false,
            'uid' => false,
            't' => false,
        ];

        foreach ($minimumRequiredParameters as $parameterName => $isParamPresent) {
            if (array_key_exists($parameterName, $this->singleParameters)) {
                $minimumRequiredParameters[$parameterName] = true;
            }
        }

        if (!$minimumRequiredParameters['cid'] && $minimumRequiredParameters['uid']) {
            $minimumRequiredParameters['cid'] = true;
        }

        if (!$minimumRequiredParameters['uid'] && $minimumRequiredParameters['cid']) {
            $minimumRequiredParameters['uid'] = true;
        }

        return !in_array(false, $minimumRequiredParameters, true);
    }

protected function hasMinimumRequiredParameters ( ) { $ minimumRequiredParameters = [ 'v' => false , 'tid' => false , 'cid' => false , 'uid' => false , 't' => false , ] ; foreach ( $ minimumRequiredParameters as $ parameterName => $ isParamPresent ) { if ( array_key_exists ( $ parameterName , $ this -> singleParameters ) ) { $ minimumRequiredParameters [ $ parameterName ] = true ; } } if ( ! $ minimumRequiredParameters [ 'cid' ] && $ minimumRequiredParameters [ 'uid' ] ) { $ minimumRequiredParameters [ 'cid' ] = true ; } if ( ! $ minimumRequiredParameters [ 'uid' ] && $ minimumRequiredParameters [ 'cid' ] ) { $ minimumRequiredParameters [ 'uid' ] = true ; } return ! in_array ( false , $ minimumRequiredParameters , true ) ; }

Validates the minimum required parameters for every GA hit are being sent.

@SuppressWarnings(PHPMD.LongVariable)

@return bool

Validates the minimum required parameters for every GA hit are being sent .


protected function setParameterActionTo($parameter, $action)
    {
        $actionConstant = $this->getParameterClassConstant(
            'TheIconic\Tracking\GoogleAnalytics\Parameters\EnhancedEcommerce\\'
            . $parameter . 'Action::ACTION_' . strtoupper($action),
            $parameter . ' action ' . $action . ' does not exist, check spelling'
        );

        $function = 'set' . $parameter . 'Action';

        $this->$function($actionConstant);

        return $this;
    }

Sets a parameter action to the value specified by the method call.

@param $parameter
@param $action
@return $this

Sets a parameter action to the value specified by the method call .


protected function setParameter($methodName, array $methodArguments)
    {
        $parameterClass = substr($methodName, 3);

        $fullParameterClass = $this->getFullParameterClass($parameterClass, $methodName);

        $parameterIndex = $this->getIndexFromArguments($methodArguments);

        /** @var SingleParameter $parameterObject */
        $parameterObject = new $fullParameterClass($parameterIndex);

        if (!isset($methodArguments[0])) {
            throw new \InvalidArgumentException(
                'For Analytics object, you must specify a value to be set for ' . $methodName
            );
        } else {
            $parameterObject->setValue($methodArguments[0]);
        }

        $this->singleParameters[$parameterObject->getName()] = $parameterObject;

        return $this;
    }

Sets the value for a parameter.

@param $methodName
@param array $methodArguments
@return $this
@throws \InvalidArgumentException


protected function addItem($methodName, array $methodArguments)
    {
        $parameterClass = substr($methodName, 3);

        $fullParameterClass = $this->getFullParameterClass($parameterClass, $methodName);

        if (!isset($methodArguments[0])) {
            throw new \InvalidArgumentException(
                'You must specify a ' . $parameterClass . ' to be add for ' . $methodName
            );
        } else {
            $parameterObject = new $fullParameterClass($methodArguments[0]);
        }

        $collectionIndex = $this->getIndexFromArguments($methodArguments);

        if (isset($this->compoundParametersCollections[$parameterClass . $collectionIndex])) {
            $this->compoundParametersCollections[$parameterClass . $collectionIndex]->add($parameterObject);
        } else {
            $fullParameterCollectionClass = $fullParameterClass . 'Collection';

            /** @var CompoundParameterCollection $parameterObjectCollection */
            $parameterObjectCollection = new $fullParameterCollectionClass($collectionIndex);

            $parameterObjectCollection->add($parameterObject);

            $this->compoundParametersCollections[$parameterClass . $collectionIndex] = $parameterObjectCollection;
        }

        return $this;
    }

Adds an item to a compund parameter collection.

@SuppressWarnings(PHPMD.LongVariable)

@param $methodName
@param array $methodArguments
@return $this
@throws \InvalidArgumentException

Adds an item to a compund parameter collection .


protected function getParameter($methodName, array $methodArguments)
    {
        $parameterClass = substr($methodName, 3);

        $fullParameterClass = $this->getFullParameterClass($parameterClass, $methodName);

        // Handle index arguments
        $parameterIndex = '';
        if (isset($methodArguments[0]) && is_numeric($methodArguments[0])) {
            $parameterIndex = $methodArguments[0];
        }

        // Handle compoundParametersCollections
        if (isset($this->compoundParametersCollections[$parameterClass . $parameterIndex])) {
            // If compoundParametersCollections contains our Objects, return them well-formatted
            return $this->compoundParametersCollections[$parameterClass . $parameterIndex]->getReadableItems();
        } else {
            $fullParameterCollectionClass = $fullParameterClass . 'Collection';

            // Test if the class Collection exist
            if (class_exists($fullParameterCollectionClass, false)) {
                return null;
            }
            // If not, it's a SingleParameter Object, continue the magic
        }

        /** @var SingleParameter $parameterObject */
        $parameterObject = new $fullParameterClass($parameterIndex);

        if (!array_key_exists($parameterObject->getName(), $this->singleParameters)) {
            return null;
        }

        $currentParameterObject = $this->singleParameters[$parameterObject->getName()];

        return $currentParameterObject->getValue();

    }

Gets the value for a parameter.

@SuppressWarnings(PHPMD.LongVariable)

@param $methodName
@param array $methodArguments
@return string
@throws \InvalidArgumentException


protected function getIndexFromArguments($methodArguments)
    {
        $index = '';
        if (isset($methodArguments[1]) && is_numeric($methodArguments[1])) {
            $index = $methodArguments[1];
        }

        return $index;
    }

protected function getIndexFromArguments ( $ methodArguments ) { $ index = '' ; if ( isset ( $ methodArguments [ 1 ] ) && is_numeric ( $ methodArguments [ 1 ] ) ) { $ index = $ methodArguments [ 1 ] ; } return $ index ; }

Gets the index value from the arguments.

@param $methodArguments
@return string

Gets the index value from the arguments .


protected function getFullParameterClass($parameterClass, $methodName)
    {
        if (empty($this->availableParameters[$parameterClass])) {
            throw new \BadMethodCallException('Method ' . $methodName . ' not defined for Analytics class');
        } else {
            return '\\TheIconic\\Tracking\\GoogleAnalytics\\Parameters\\' . $this->availableParameters[$parameterClass];
        }
    }

Gets the fully qualified name for a parameter.

@param $parameterClass
@param $methodName
@return string
@throws \BadMethodCallException

Gets the fully qualified name for a parameter .


public function post($url, array $options = [])
    {
        $request = new Request(
            'GET',
            $url,
            ['User-Agent' => self::PHP_GA_MEASUREMENT_PROTOCOL_USER_AGENT]
        );

        $opts = $this->parseOptions($options);
        $response = $this->getClient()->sendAsync($request, [
            'synchronous' => !$opts['async'],
            'timeout' => $opts['timeout'],
            'connect_timeout' => $opts['timeout'],
        ]);

        if ($opts['async']) {
            self::$promises[] = $response;
        } else {
            $response = $response->wait();
        }

        return $this->getAnalyticsResponse($request, $response);
    }

public function post ( $ url , array $ options = [ ] ) { $ request = new Request ( 'GET' , $ url , [ 'User-Agent' => self :: PHP_GA_MEASUREMENT_PROTOCOL_USER_AGENT ] ) ; $ opts = $ this -> parseOptions ( $ options ) ; $ response = $ this -> getClient ( ) -> sendAsync ( $ request , [ 'synchronous' => ! $ opts [ 'async' ] , 'timeout' => $ opts [ 'timeout' ] , 'connect_timeout' => $ opts [ 'timeout' ] , ] ) ; if ( $ opts [ 'async' ] ) { self :: $ promises [ ] = $ response ; } else { $ response = $ response -> wait ( ) ; } return $ this -> getAnalyticsResponse ( $ request , $ response ) ; }

Sends request to Google Analytics.

@internal
@param string $url
@param array $options
@return AnalyticsResponse


private function parseOptions(array $options)
    {
        $defaultOptions = [
            'timeout' => static::REQUEST_TIMEOUT_SECONDS,
            'async' => false,
        ];

        $opts = [];
        foreach ($defaultOptions as $option => $value) {
            $opts[$option] = isset($options[$option]) ? $options[$option] : $defaultOptions[$option];
        }

        if (!is_int($opts['timeout']) || $opts['timeout'] <= 0) {
            throw new \UnexpectedValueException('The timeout must be an integer with a value greater than 0');
        }

        if (!is_bool($opts['async'])) {
            throw new \UnexpectedValueException('The async option must be boolean');
        }

        return $opts;
    }

Parse the given options and fill missing fields with default values.

@param array $options
@return array

Parse the given options and fill missing fields with default values .


public function getReadableItems()
    {
        $readablesItems = [];

        foreach ($this->items as $key => $item) {
            array_push($readablesItems, $item->getReadableParameters());
        }

        return $readablesItems;
    }

public function getReadableItems ( ) { $ readablesItems = [ ] ; foreach ( $ this -> items as $ key => $ item ) { array_push ( $ readablesItems , $ item -> getReadableParameters ( ) ) ; } return $ readablesItems ; }

Gets the human readable items for the parameter.

@internal
@return array

Gets the human readable items for the parameter .


public function addSupportedBrand($name, $expression)
    {
        $known_brands = array_keys($this->supported_cards);

        if (in_array($name, $known_brands)) {
            return false;
        }

        $this->supported_cards[$name] = $expression;
        return true;
    }

public function addSupportedBrand ( $ name , $ expression ) { $ known_brands = array_keys ( $ this -> supported_cards ) ; if ( in_array ( $ name , $ known_brands ) ) { return false ; } $ this -> supported_cards [ $ name ] = $ expression ; return true ; }

Set a custom supported card brand with a regular expression to match it.

Note: The fact that a particular card is known does not imply that your
gateway supports it.

Set $add_to_front to true if the key should be added to the front of the array

@param  string  $name The name of the new supported brand.
@param  string  $expression The regular expression to check if a card is supported.
@return boolean success

Set a custom supported card brand with a regular expression to match it .


protected function setYearParameter($key, $value)
    {
        // normalize year to four digits
        if (null === $value || '' === $value) {
            $value = null;
        } else {
            $value = (int) gmdate('Y', gmmktime(0, 0, 0, 1, 1, (int) $value));
        }

        return $this->setParameter($key, $value);
    }

protected
function
setYearParameter
(
$
key
,
$
value
)
{
// normalize year to four digits
if
(
===
$
value
||
''
===
$
value
)
{
$
value
=
;
}
else
{
$
value
=
(
int
)
gmdate
(
'Y'
,
gmmktime
(
0
,
0
,
0
,
1
,
1
,
(
int
)
$
value
)
)
;
}
return
$
this
->
setParameter
(
$
key
,
$
value
)
;
}

Set the credit card year.

The input value is normalised to a 4 digit number.

@param string $key Parameter key, e.g. 'expiryYear'
@param mixed $value Parameter value
@return $this


public function validate()
    {
        $requiredParameters = array(
            'number' => 'credit card number',
            'expiryMonth' => 'expiration month',
            'expiryYear' => 'expiration year'
        );

        foreach ($requiredParameters as $key => $val) {
            if (!$this->getParameter($key)) {
                throw new InvalidCreditCardException("The $val is required");
            }
        }

        if ($this->getExpiryDate('Ym') < gmdate('Ym')) {
            throw new InvalidCreditCardException('Card has expired');
        }

        if (!Helper::validateLuhn($this->getNumber())) {
            throw new InvalidCreditCardException('Card number is invalid');
        }

        if (!is_null($this->getNumber()) && !preg_match('/^\d{12,19}$/i', $this->getNumber())) {
            throw new InvalidCreditCardException('Card number should have 12 to 19 digits');
        }
    }

Validate this credit card. If the card is invalid, InvalidCreditCardException is thrown.

This method is called internally by gateways to avoid wasting time with an API call
when the credit card is clearly invalid.

Generally if you want to validate the credit card yourself with custom error
messages, you should use your framework's validation library, not this method.

@return void
@throws Exception\InvalidRequestException
@throws InvalidCreditCardException

Validate this credit card . If the card is invalid InvalidCreditCardException is thrown .


public function getNumberMasked($mask = 'X')
    {
        $maskLength = strlen($this->getNumber()) - 4;

        return str_repeat($mask, $maskLength) . $this->getNumberLastFour();
    }

public function getNumberMasked ( $ mask = 'X' ) { $ maskLength = strlen ( $ this -> getNumber ( ) ) - 4 ; return str_repeat ( $ mask , $ maskLength ) . $ this -> getNumberLastFour ( ) ; }

Returns a masked credit card number with only the last 4 chars visible

@param string $mask Character to use in place of numbers
@return string


public function getBrand()
    {
        foreach ($this->getSupportedBrands() as $brand => $val) {
            if (preg_match($val, $this->getNumber())) {
                return $brand;
            }
        }
    }

public function getBrand ( ) { foreach ( $ this -> getSupportedBrands ( ) as $ brand => $ val ) { if ( preg_match ( $ val , $ this -> getNumber ( ) ) ) { return $ brand ; } } }

Credit Card Brand

Iterates through known/supported card brands to determine the brand of this card

@return string


protected function getTrackByPattern($pattern)
    {
        if ($tracks = $this->getTracks()) {
            if (preg_match($pattern, $tracks, $matches) === 1) {
                return $matches[0];
            }
        }
    }

protected function getTrackByPattern ( $ pattern ) { if ( $ tracks = $ this -> getTracks ( ) ) { if ( preg_match ( $ pattern , $ tracks , $ matches ) === 1 ) { return $ matches [ 0 ] ; } } }

Get raw data for a track  on the credit card magnetic strip based on the pattern for track 1 or 2.

@param $pattern
@return string|null

Get raw data for a track on the credit card magnetic strip based on the pattern for track 1 or 2 .


protected function listFirstLastName($fullName)
    {
        $names = explode(' ', $fullName, 2);

        return [$names[0], isset($names[1]) ? $names[1] : null];
    }

protected
function
listFirstLastName
(
$
fullName
)
{
$
names
=
explode
(
' '
,
$
fullName
,
2
)
;
return
[
$
names
[
0
]
,
isset
(
$
names
[
1
]
)
$
names
[
1
]
:
]
;
}

Split the full name in the first and last name.

@param $fullName
@return array with first and lastname

Split the full name in the first and last name .


public function setBillingName($value)
    {
        $names = $this->listFirstLastName($value);

        $this->setBillingFirstName($names[0]);
        $this->setBillingLastName($names[1]);

        return $this;
    }

public function setBillingName ( $ value ) { $ names = $ this -> listFirstLastName ( $ value ) ; $ this -> setBillingFirstName ( $ names [ 0 ] ) ; $ this -> setBillingLastName ( $ names [ 1 ] ) ; return $ this ; }

Sets the card billing name.

@param string $value
@return $this


public function setShippingName($value)
    {
        $names = $this->listFirstLastName($value);

        $this->setShippingFirstName($names[0]);
        $this->setShippingLastName($names[1]);

        return $this;
    }

public function setShippingName ( $ value ) { $ names = $ this -> listFirstLastName ( $ value ) ; $ this -> setShippingFirstName ( $ names [ 0 ] ) ; $ this -> setShippingLastName ( $ names [ 1 ] ) ; return $ this ; }

Sets the card shipping name.

@param string $value
@return $this


public function getBirthday($format = 'Y-m-d')
    {
        $value = $this->getParameter('birthday');

        return $value ? $value->format($format) : null;
    }

public function getBirthday ( $ format = 'Y-m-d' ) { $ value = $ this -> getParameter ( 'birthday' ) ; return $ value ? $ value -> format ( $ format ) : null ; }

Get the cardholder's birthday.

@return string


public function setBirthday($value)
    {
        if ($value) {
            $value = new DateTime($value, new DateTimeZone('UTC'));
        } else {
            $value = null;
        }

        return $this->setParameter('birthday', $value);
    }

public function setBirthday ( $ value ) { if ( $ value ) { $ value = new DateTime ( $ value , new DateTimeZone ( 'UTC' ) ) ; } else { $ value = null ; } return $ this -> setParameter ( 'birthday' , $ value ) ; }

Sets the cardholder's birthday.

@param string $value
@return $this


public function initialize(array $parameters = array())
    {
        if (null !== $this->response) {
            throw new RuntimeException('Request cannot be modified after it has been sent!');
        }

        $this->parameters = new ParameterBag;

        Helper::initialize($this, $parameters);

        return $this;
    }

Initialize the object with parameters.

If any unknown parameters passed, they will be ignored.

@param array $parameters An associative array of parameters

@return $this
@throws RuntimeException


protected function setParameter($key, $value)
    {
        if (null !== $this->response) {
            throw new RuntimeException('Request cannot be modified after it has been sent!');
        }

        return $this->traitSetParameter($key, $value);
    }

Set a single parameter

@param string $key The parameter key
@param mixed $value The value to set
@return $this
@throws RuntimeException if a request parameter is modified after the request has been sent.


public function setCard($value)
    {
        if ($value && !$value instanceof CreditCard) {
            $value = new CreditCard($value);
        }

        return $this->setParameter('card', $value);
    }

public function setCard ( $ value ) { if ( $ value && ! $ value instanceof CreditCard ) { $ value = new CreditCard ( $ value ) ; } return $ this -> setParameter ( 'card' , $ value ) ; }

Sets the card.

@param CreditCard $value
@return $this


public function getAmount()
    {
        $money = $this->getMoney();

        if ($money !== null) {
            $moneyFormatter = new DecimalMoneyFormatter($this->getCurrencies());

            return $moneyFormatter->format($money);
        }
    }

public function getAmount ( ) { $ money = $ this -> getMoney ( ) ; if ( $ money !== null ) { $ moneyFormatter = new DecimalMoneyFormatter ( $ this -> getCurrencies ( ) ) ; return $ moneyFormatter -> format ( $ money ) ; } }

Validates and returns the formatted amount.

@throws InvalidRequestException on any validation failure.
@return string The amount formatted to the correct number of decimal places for the selected currency.

Validates and returns the formatted amount .


public function setMoney(Money $value)
    {
        $currency = $value->getCurrency()->getCode();

        $this->setCurrency($currency);

        return $this->setParameter('amount', $value);
    }

public function setMoney ( Money $ value ) { $ currency = $ value -> getCurrency ( ) -> getCode ( ) ; $ this -> setCurrency ( $ currency ) ; return $ this -> setParameter ( 'amount' , $ value ) ; }

Sets the payment amount as integer.

@param Money $value
@return $this


public function setCurrency($value)
    {
        if ($value !== null) {
            $value = strtoupper($value);
        }
        return $this->setParameter('currency', $value);
    }

public function setCurrency ( $ value ) { if ( $ value !== null ) { $ value = strtoupper ( $ value ) ; } return $ this -> setParameter ( 'currency' , $ value ) ; }

Sets the payment currency code.

@param string $value
@return $this


public function getCurrencyNumeric()
    {
        if (! $this->getCurrency()) {
            return null;
        }

        $currency = new Currency($this->getCurrency());

        if ($this->getCurrencies()->contains($currency)) {
            return (string) $this->getCurrencies()->numericCodeFor($currency);
        }
    }

public function getCurrencyNumeric ( ) { if ( ! $ this -> getCurrency ( ) ) { return null ; } $ currency = new Currency ( $ this -> getCurrency ( ) ) ; if ( $ this -> getCurrencies ( ) -> contains ( $ currency ) ) { return ( string ) $ this -> getCurrencies ( ) -> numericCodeFor ( $ currency ) ; } }

Get the payment currency number.

@return string|null


public function getCurrencyDecimalPlaces()
    {
        if ($this->getCurrency()) {
            $currency = new Currency($this->getCurrency());
            if ($this->getCurrencies()->contains($currency)) {
                return $this->getCurrencies()->subunitFor($currency);
            }
        }

        return 2;
    }

public function getCurrencyDecimalPlaces ( ) { if ( $ this -> getCurrency ( ) ) { $ currency = new Currency ( $ this -> getCurrency ( ) ) ; if ( $ this -> getCurrencies ( ) -> contains ( $ currency ) ) { return $ this -> getCurrencies ( ) -> subunitFor ( $ currency ) ; } } return 2 ; }

Get the number of decimal places in the payment currency.

@return integer

Get the number of decimal places in the payment currency .