Source of file WrapperConfig.php

Size: 43,548 Bytes - Last Modified: 2020-10-25T23:00:04+00:00

/root/gitwork/work/tornelib-php-netcurl-6.1/src/Module/Config/WrapperConfig.php

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672
<?php
/** @noinspection PhpUndefinedMethodInspection */
/** @noinspection PhpComposerExtensionStubsInspection */

/**
 * Copyright © Tomas Tornevall / Tornevall Networks. All rights reserved.
 * See LICENSE.md for license details.
 */

namespace TorneLIB\Module\Config;

use Exception;
use TorneLIB\Config\Flag;
use TorneLIB\Exception\Constants;
use TorneLIB\Exception\ExceptionHandler;
use TorneLIB\Flags;
use TorneLIB\Helpers\Browsers;
use TorneLIB\IO\Data\Content;
use TorneLIB\IO\Data\Strings;
use TorneLIB\Model\Type\authSource;
use TorneLIB\Model\Type\authType;
use TorneLIB\Model\Type\dataType;
use TorneLIB\Model\Type\requestMethod;
use TorneLIB\Utils\Ini;

/**
 * Class WrapperConfig
 * Configuration handler. All wrapper services that needs shared configuration like credentials, SSL setup, etc.
 *
 * @package Module\Config
 * @since 6.1.0
 * @version 6.1.2
 */
class WrapperConfig
{
    /**
     * @var string Requested URL.
     * @since 6.1.0
     */
    private $requestUrl = '';

    /**
     * @var array Postdata.
     * @since 6.1.0
     */
    private $requestData = [];

    /**
     * @var
     * @since 6.1.0
     */
    private $requestDataContainer;

    /**
     * @var int Default method. Postdata will in the case of GET generate postdata in the link.
     * @since 6.1.0
     */
    private $requestMethod = requestMethod::METHOD_GET;

    /**
     * Datatype to post in (default = uses ?key=value for GET and &key=value in body for POST).
     * @var int
     * @since 6.1.0
     */
    private $requestDataType = dataType::NORMAL;
    /**
     * @var array Options that sets up each request engine. On curl, it is CURLOPT.
     * @since 6.1.0
     */
    private $options = [];

    /**
     * @var string $currentWrapper
     * @since 6.1.0
     */
    private $currentWrapper;

    /**
     * @var bool
     * @since 6.1.0
     */
    private $isNetWrapper = false;

    /**
     * @var string
     * @since 6.1.0
     */
    private static $userAgentSignature;

    /**
     * Allow WrapperConfig to push out netcurl identification instead of a spoofed browser.
     * @var bool
     * @since 6.1.0
     */
    private $identifierAgent = false;

    /**
     * If netcurl identification is allowed, also allow PHP version to be pushed into the useragent, unless
     * it's already done somewhere else.
     * @var bool
     * @since 6.1.0
     */
    private $identifierAgentPhp = false;

    /**
     * @var bool
     * @since 6.1.0
     */
    private $isCustomUserAgent = false;

    /**
     * @var string
     * @since 6.1.0
     */
    private $proxyAddress = '';
    /**
     * @var int
     * @since 6.1.0
     */
    private $proxyType = 0;

    /**
     * @var array Initial SoapOptions
     *
     *   WSDL_CACHE_NONE = 0
     *   WSDL_CACHE_DISK = 1
     *   WSDL_CACHE_MEMORY = 2
     *   WSDL_CACHE_BOTH = 3
     *
     * @since 6.1.0
     */
    private $streamOptions = [
        'exceptions' => true,
        'trace' => true,
        'cache_wsdl' => 0,
        'stream_context' => null,
    ];

    /**
     * Static header content. Used to replicate through multiple instances but will never reset between requests.
     * @var array
     * @since 6.1.2
     */
    private $streamContextStatic = [];

    /**
     * Stored headers, used to replicate through multiple instances when NetWrapper is in use.
     * @var array
     * @since 6.1.2
     */
    private $storedHeaders = [];

    /**
     * @var array Authentication data.
     * @since 6.1.0
     */
    private $authData = ['username' => '', 'password' => '', 'type' => 1];

    /**
     * @var array Throwable HTTP codes.
     * @since 6.1.0
     */
    private $throwableHttpCodes;

    /**
     * @var array
     * @since 6.1.0
     */
    private $configData = [];

    /**
     * @var WrapperSSL SSL helper and context renderer.
     * @since 6.1.0
     */
    private $SSL;

    /**
     * User data that normally can not be overwritten more than once (when not exists).
     * @var array
     * @since 6.1.0
     */
    private $irreplacable = ['user_agent'];

    /**
     * If discovered soaprequest.
     * @var bool $isSoapRequest
     * @since 6.1.0
     */
    private $isSoapRequest = false;

    /**
     * If discovered stream request.
     * @var bool
     * @since 6.1.0
     */
    private $isStreamRequest = false;

    /**
     * @var
     * @since 6.1.0
     */
    private $authSource;

    /**
     * @var bool
     * @since 6.1.0
     */
    private $staging = false;

    /**
     * WrapperConfig constructor.
     * @since 6.1.0
     */
    public function __construct()
    {
        $this->SSL = new WrapperSSL();
        $this->setThrowableHttpCodes();
        $this->setCurlDefaults();
    }

    /**
     * @since 6.1.2
     */
    public function resetStreamData()
    {
        $this->streamOptions = [
            'exceptions' => true,
            'trace' => true,
            'cache_wsdl' => 0,
            'stream_context' => null,
        ];

        return $this;
    }

    /**
     * Returns compatibility functions from for example NetCurl 6.0.
     * @return array
     * @since 6.1.2
     * @noinspection PhpFullyQualifiedNameUsageInspection
     */
    public function getCompatibilityMethods()
    {
        $return = [];
        if (class_exists('\TorneLIB\Compatibility\NetCurl\Methods')) {
            /** @noinspection PhpUndefinedClassInspection */
            $return = \TorneLIB\Compatibility\NetCurl\Methods::getCompatibilityMethods();
        }

        return $return;
    }

    /**
     * Preparing curl defaults in a way we like.
     * @return $this
     * @since 6.1.0
     */
    private function setCurlDefaults()
    {
        $this->setCurlConstants([
            'CURLOPT_RETURNTRANSFER' => true,
            'CURLOPT_SSL_VERIFYPEER' => 1,
            'CURLOPT_SSL_VERIFYHOST' => 2,
            'CURLOPT_ENCODING' => 1,
            'CURLOPT_USERAGENT' => (new Browsers())->getBrowser(),
            'CURLOPT_SSLVERSION' => WrapperCurlOpt::NETCURL_CURL_SSLVERSION_DEFAULT,
            'CURLOPT_FOLLOWLOCATION' => false,
            'CURLOPT_HTTPHEADER' => ['Accept-Language: en'],
        ]);

        $this->setTimeout(10);

        return $this;
    }

    /**
     * Set up a list of which HTTP error codes that should be throwable (default: >= 400, <= 599).
     *
     * @param int $throwableMin Minimum value to throw on (Used with >=)
     * @param int $throwableMax Maxmimum last value to throw on (Used with <)
     * @return WrapperConfig
     * @since 6.0.6
     */
    public function setThrowableHttpCodes($throwableMin = 400, $throwableMax = 599)
    {
        $throwableMin = (int)$throwableMin > 0 ? $throwableMin : 400;
        $throwableMax = (int)$throwableMax > 0 ? $throwableMax : 599;
        $this->throwableHttpCodes[] = [$throwableMin, $throwableMax];

        return $this;
    }

    /**
     * Throw on any code that matches the store throwableHttpCode (use with setThrowableHttpCodes())
     *
     * @param string $httpMessageString
     * @param string $httpCode
     * @param null $previousException
     * @param null $extendException
     * @param bool $forceException
     * @throws ExceptionHandler
     * @since 6.0.6
     */
    public function getHttpException(
        $httpMessageString = '',
        $httpCode = '',
        $previousException = null,
        $extendException = null,
        $forceException = false
    ) {
        if (!is_array($this->throwableHttpCodes)) {
            $this->throwableHttpCodes = [];
        }
        foreach ($this->throwableHttpCodes as $codeListArray => $codeArray) {
            if ((isset($codeArray[1]) && $httpCode >= (int)$codeArray[0] && $httpCode <= (int)$codeArray[1]) || $forceException) {
                throw new ExceptionHandler(
                    sprintf(
                        'Error %d returned from server: "%s".',
                        $httpCode,
                        $httpMessageString
                    ),
                    $httpCode,
                    $previousException,
                    null,
                    null,
                    $extendException
                );
            }
        }
    }

    /**
     * Return the list of throwable http error codes (if set). For developers that needs to see which http codes
     * that is normally thrown on errors.
     *
     * @return array
     * @since 6.0.6
     */
    public function getThrowableHttpCodes()
    {
        return $this->throwableHttpCodes;
    }

    /**
     * Get current list of curlopts, etc.
     *
     * @return array
     * @since 6.1.0
     */
    public function getCurlDefaults()
    {
        return $this->options;
    }

    /**
     * While setting up curloptions, make sure no warnings leak from the setup if constants are missing in the system.
     * If the constants are missing, this probably means that curl is not properly installed. We've seen this in prior
     * versions of netcurl where missing constants either screams about missing constants or makes sites bail out.
     *
     * @param mixed $curlOptConstant
     * @return WrapperConfig
     * @since 6.1.0
     */
    private function setCurlConstants($curlOptConstant)
    {
        if (is_array($curlOptConstant)) {
            foreach ($curlOptConstant as $curlOptKey => $curlOptValue) {
                $constantValue = @constant($curlOptKey);
                if (empty($constantValue)) {
                    // Fall back to internally stored constants if curl is not there.
                    $constantValue = @constant('TorneLIB\Module\Config\WrapperCurlOpt::NETCURL_' . $curlOptKey);
                }
                $this->options[$constantValue] = $curlOptValue;
            }
        }

        return $this;
    }

    /**
     * Which URL is the current requested?
     *
     * @return string
     * @since 6.1.0
     */
    public function getRequestUrl()
    {
        return $this->requestUrl;
    }

    /**
     * Set up a new URL to be requested from the wrappers.
     *
     * @param string $requestUrl
     * @return WrapperConfig
     * @since 6.1.0
     */
    public function setRequestUrl($requestUrl)
    {
        $this->requestUrl = $requestUrl;

        return $this;
    }

    /**
     * Transform requested data into a proper format based on the content-type.
     *
     * @return array
     * @since 6.1.0
     */
    public function getRequestData()
    {
        $return = $this->requestData;

        // Return as is on string.
        if (!is_string($return)) {
            switch ($this->requestDataType) {
                case dataType::XML:
                    $this->requestDataContainer = (new Content())->getXmlFromArray($return);
                    $return = $this->requestDataContainer;
                    break;
                case dataType::JSON:
                    $this->requestDataContainer = $this->getJsonData($return);
                    $return = $this->requestDataContainer;
                    break;
                case dataType::NORMAL:
                    $requestQuery = '';
                    if ($this->requestMethod === requestMethod::METHOD_GET && !empty($this->requestData)) {
                        // Add POST data to request if anything else follows.
                        $requestQuery = '&';
                    }
                    $this->requestDataContainer = $this->requestData;
                    if (is_array($this->requestData) || is_object($this->requestData)) {
                        $httpQuery = http_build_query($this->requestData);
                        if (!empty($httpQuery)) {
                            $this->requestDataContainer = $requestQuery . $httpQuery;
                        }
                    }
                    $return = $this->requestDataContainer;
                    break;
                default:
                    break;
            }
        }

        return $return;
    }

    /**
     * Entry point.
     *
     * @return mixed
     * @since 6.1.1
     */
    public function getRequestDataContainer()
    {
        return $this->requestDataContainer;
    }

    /**
     * Handle json. Legacy. Maybe.
     *
     * @param $transformData
     * @return string
     * @since 6.1.0
     */
    private function getJsonData($transformData)
    {
        $return = $transformData;

        if (is_string($transformData)) {
            $stringTest = json_decode($transformData, false);
            if (is_object($stringTest) || is_array($stringTest)) {
                $return = $transformData;
            }
        } else {
            $return = json_encode($transformData);
        }

        return (string)$return;
    }

    /**
     * User input variables.
     *
     * @param array $requestData
     * @return WrapperConfig
     * @since 6.1.0
     */
    public function setRequestData($requestData)
    {
        $this->requestData = $requestData;

        return $this;
    }

    /**
     * Set up which method that should be used: POST, GET, DELETE, etc
     *
     * @param int $requestMethod
     * @return WrapperConfig
     * @since 6.1.0
     */
    public function setRequestMethod($requestMethod)
    {
        if (is_numeric($requestMethod)) {
            $this->requestMethod = $requestMethod;
        } else {
            $this->requestMethod = requestMethod::METHOD_GET;
        }

        return $this;
    }

    /**
     * Get information about the current request method that is used: POST, GET, DELETE, etc
     *
     * @return int
     * @since 6.1.0
     */
    public function getRequestMethod()
    {
        return $this->requestMethod;
    }

    /**
     * Flags registered in the Flags class.
     *
     * @return array
     * @since 6.1.0
     */
    public function getRequestFlags()
    {
        /** @noinspection PhpUndefinedMethodInspection */
        return Flags::_getAllFlags();
    }

    /**
     * @param array $requestFlags
     * @since 6.1.0
     */
    public function setRequestFlags($requestFlags)
    {
        /** @noinspection PhpUndefinedMethodInspection */
        Flags::_setFlags($requestFlags);
    }

    /**
     * @return array
     * @throws ExceptionHandler
     * @since 6.1.0
     */
    public function getOptions()
    {
        $this->setHandledUserAgent();

        return $this->options;
    }

    /**
     * @throws ExceptionHandler
     * @since 6.1.0
     */
    private function setHandledUserAgent()
    {
        $currentUserAgent = $this->getUserAgent();

        if ($this->getIdentifiers()) {
            if (!$this->getIsCustomUserAgent()) {
                // Reset if not custom already.
                $currentUserAgent = '';
            }

            $currentUserAgentArray = [
                $currentUserAgent,
                sprintf('netcurl-%s', NETCURL_VERSION),
                $this->getCurrentWrapperClass(true),
                $this->getNetWrapperString(),
                $this->getPhpString(),
            ];

            $currentUserAgent = $this->getUserAgentsMerged($currentUserAgentArray);
        }

        $this->setUserAgent($currentUserAgent);
    }

    /**
     * @param $userAgentArray
     * @return string
     * @since 6.1.0
     */
    private function getUserAgentsMerged($userAgentArray)
    {
        $return = [];

        if (is_array($userAgentArray)) {
            foreach ($userAgentArray as $item) {
                if (!empty($item)) {
                    $return[] = $item;
                }
            }
        }

        return implode(' +', $return);
    }

    /**
     * @return string
     * @since 6.1.0
     */
    private function getPhpString()
    {
        if ($this->identifierAgentPhp) {
            $phpString = sprintf('PHP-%s', PHP_VERSION);
        } else {
            $phpString = '';
        }
        return $phpString;
    }

    private function getNetWrapperString()
    {
        return $this->isNetWrapper ? 'NetWrapper' : 'Instant';
    }

    /**
     * Update stream options (which transforms into stream_context).
     *
     * @param array $streamOptions
     * @return WrapperConfig
     * @since 6.1.0
     */
    public function setStreamOptions($streamOptions)
    {
        $this->streamOptions = $streamOptions;

        return $this;
    }

    /**
     * @param $key
     * @param $value
     * @param null $subKey
     * @return WrapperConfig
     * @since 6.1.0
     */
    public function setStreamContext($key, $value, $subKey = null)
    {
        $currentStreamContext = $this->getStreamContext();

        if (is_resource($currentStreamContext)) {
            $currentStreamContext = stream_context_get_options($currentStreamContext);
        } else {
            $currentStreamContext = [];
        }

        if (is_array($currentStreamContext)) {
            if (is_null($subKey)) {
                if (
                    (isset($currentStreamContext[$key]) && $this->canOverwrite($key)) ||
                    !isset($currentStreamContext[$key])
                ) {
                    $currentStreamContext[$key] = $value;
                }
            } else {
                if (!isset($currentStreamContext[$subKey])) {
                    $currentStreamContext[$subKey] = [];
                }
                if (
                    (
                        isset($currentStreamContext[$subKey][$key]) && $this->canOverwrite($key)
                    ) ||
                    !isset($currentStreamContext[$subKey][$key])
                ) {
                    if (!isset($currentStreamContext[$subKey][$key])) {
                        $currentStreamContext[$subKey][$key] = $value;
                    } elseif ($key === 'header') {
                        $currentStreamContext[$subKey][$key] .= "\r\n" . $value;
                    } else {
                        // Overwrite if not header context.
                        $currentStreamContext[$subKey][$key] = $value;
                    }
                }
            }
        }

        // This can throw an exception if something is not properly set.
        // stream_context_create(): options should have the form ["wrappername"]["optionname"] = $value
        $this->streamOptions['stream_context'] = stream_context_create($currentStreamContext);

        return $this;
    }

    /**
     * @param $key
     * @return bool
     * @since 6.1.0
     */
    private function canOverwrite($key)
    {
        $dynamicOverwrites = Flag::getFlag('canoverwrite');

        $return = in_array(
            $key,
            array_map('strtolower', $this->irreplacable),
            false
        ) ? false : true;

        // Dynamic override.
        if (
            is_array($dynamicOverwrites) &&
            in_array(
                $key,
                $dynamicOverwrites,
                false
            )
        ) {
            $return = true;
        }

        return $return;
    }

    /**
     * @param bool $decoded
     * @return mixed
     * @since 6.1.0
     */
    public function getStreamContext($decoded = false)
    {
        return !$decoded ? $this->streamOptions['stream_context'] : stream_context_get_options($this->streamOptions['stream_context']);
    }

    /**
     * @param $contextBlock
     * @return array|null
     * @since 6.1.2
     */
    public function getContentFromStreamContext($contextBlock)
    {
        $return = null;
        if (is_resource($contextBlock)) {
            $return = stream_context_get_options($contextBlock);
        }
        return $return;
    }

    /**
     * Get current soapoptions.
     *
     * @return array
     * @throws ExceptionHandler
     * @since 6.1.0
     */
    public function getStreamOptions()
    {
        $this->setRenderedStreamOptions();
        $this->setStreamContext('ssl', $this->SSL->getContext());

        return $this->streamOptions;
    }

    /**
     * Prepare streamoption array.
     *
     * @return $this
     * @throws ExceptionHandler
     * @since 6.1.0
     */
    private function setRenderedStreamOptions()
    {
        $this->setRenderedUserAgent();

        return $this;
    }

    /**
     * Handle user-agent in streams.
     *
     * @return $this
     * @throws ExceptionHandler
     * @since 6.1.0
     */
    private function setRenderedUserAgent()
    {
        $this->setDualStreamHttp('user_agent', $this->getUserAgent());

        return $this;
    }

    /**
     * @param array $options
     * @return WrapperConfig
     * @since 6.1.0
     */
    public function setOptions($options)
    {
        $this->options = $options;

        return $this;
    }

    /**
     * Find out if there is a predefined constant for CURL-options and if the curl library actually exists.
     * If the constants don't exist, fall back to NETCURL constants so that we can still fetch the setup.
     *
     * @param $key
     * @return mixed|null
     * @since 6.1.0
     */
    private function getOptionCurl($key)
    {
        $return = null;

        if (false !== strpos($key, 'CURL')) {
            $constantValue = @constant('TorneLIB\Module\Config\WrapperCurlOpt::NETCURL_' . $key);
            if (!empty($constantValue)) {
                $return = $constantValue;
            }
        }

        return $return;
    }

    /**
     * @param $key
     * @param $value
     * @param bool $isSoap
     * @return $this
     * @since 6.1.0
     */
    public function setOption($key, $value, $isSoap = false)
    {
        $preKey = $this->getOptionCurl($key);
        if (!empty($preKey)) {
            $key = $preKey;
        }

        if (!$isSoap) {
            $this->options[$key] = $value;
        } else {
            $this->streamOptions[$key] = $value;
        }

        return $this;
    }

    /**
     * @param $proxyAddress
     * @param int $proxyType
     * @return WrapperConfig
     * @since 6.1.0
     */
    public function setProxy($proxyAddress, $proxyType = 0)
    {
        $this->proxyAddress = $proxyAddress;
        $this->proxyType = $proxyType;

        $this->setOption(WrapperCurlOpt::NETCURL_CURLOPT_PROXY, $proxyAddress);
        $this->setOption(WrapperCurlOpt::NETCURL_CURLOPT_PROXYTYPE, $proxyType);

        // If the current wrapper class that is used is. Saved for later use.
        //if ($this->getCurrentWrapperClass(true) === 'SimpleStreamWrapper') {}
        $this->setDualStreamHttp(
            'proxy',
            sprintf(
                'tcp://%s',
                $proxyAddress
            )
        );
        $this->setDualStreamHttp('request_fulluri', true);

        return $this;
    }

    /**
     * @return string
     * @since 6.1.0
     */
    public function getProxy()
    {
        return $this->proxyAddress;
    }

    /**
     * @return int
     * @since 6.1.0
     */
    public function getProxyType()
    {
        return $this->proxyType;
    }

    /**
     * @param $key
     * @param bool $isSoap
     * @return mixed
     * @throws ExceptionHandler
     * @since 6.1.0
     */
    public function getOption($key, $isSoap = false)
    {
        $preKey = $this->getOptionCurl($key);
        if (!empty($preKey)) {
            $key = $preKey;
        }

        if (!$isSoap) {
            if (isset($this->options[$key])) {
                return $this->options[$key];
            }
        } elseif (isset($this->streamOptions[$key])) {
            return $this->streamOptions[$key];
        }

        throw new ExceptionHandler(
            sprintf('%s: Option "%s" not set.', __CLASS__, $key),
            Constants::LIB_UNHANDLED
        );
    }

    /**
     * @param $isSoapRequest
     * @since 6.1.0
     */
    public function setSoapRequest($isSoapRequest)
    {
        $this->isSoapRequest = $isSoapRequest;
    }

    /**
     * @param $isStreamRequest
     * @since 6.1.0
     */
    public function setStreamRequest($isStreamRequest)
    {
        $this->isStreamRequest = $isStreamRequest;
    }

    /**
     * @return bool
     * @since 6.1.0
     */
    public function getSoapRequest()
    {
        return $this->isSoapRequest;
    }

    /**
     * @return bool
     */
    public function getStreamRequest()
    {
        return $this->isStreamRequest;
    }

    /**
     * @param $key
     * @param $value
     * @return $this
     * @since 6.1.0
     */
    public function setStreamOption($key, $value)
    {
        return $this->setOption($key, $value, true);
    }

    /**
     * @param $key
     * @param $value
     * @return $this
     */
    public function setDualStreamHttp($key, $value)
    {
        $this->setStreamContext($key, $value, 'https');
        $this->setStreamContext($key, $value, 'http');

        return $this;
    }

    /**
     * @param string $key
     * @param string $value
     * @param false $static
     * @return $this
     * @since 6.1.2
     */
    public function setHeader($key = '', $value = '', $static = false)
    {
        $this->setDualStreamHttp('header', sprintf('%s: %s', $key, $value));
        $this->storedHeaders[$key] = $value;

        if ($static) {
            $this->streamContextStatic[$key] = $value;
        }

        return $this;
    }

    /**
     * If NetWrapper is the primary engine, we need to extract all headers from this section.
     *
     * @return array
     * @since 6.1.2
     */
    public function getHeader()
    {
        return array_merge($this->storedHeaders, $this->streamContextStatic);
    }

    /**
     * @return array
     * @since 6.1.2
     */
    public function getStreamHeader()
    {
        if (is_array($this->streamContextStatic)) {
            foreach ($this->streamContextStatic as $key => $value) {
                $this->setHeader($key, $value);
            }
        }

        return $this->streamContextStatic;
    }

    /**
     * @param $key
     * @return mixed
     * @throws ExceptionHandler
     * @since 6.1.0
     */
    public function getStreamOption($key)
    {
        return $this->getOption($key, true);
    }

    /**
     * @param $key
     * @return $this
     * @since 6.1.0
     */
    public function deleteOption($key)
    {
        $preKey = $this->getOptionCurl($key);
        if (!empty($preKey)) {
            $key = $preKey;
        }

        if (isset($this->options[$key])) {
            unset($this->options[$key]);
        }

        return $this;
    }

    /**
     * Replace an option with another.
     *
     * @param $key
     * @param $value
     * @param $replace
     * @return $this
     * @since 6.1.0
     */
    public function replaceOption($key, $value, $replace)
    {
        $this->deleteOption($replace);
        $this->setOption($key, $value);
        return $this;
    }

    /**
     * Datatype of request (json, etc).
     * @param $requestDataType
     * @since 6.1.0
     */
    public function setRequestDataType($requestDataType)
    {
        $this->requestDataType = $requestDataType;
    }

    /**
     * Datatype of request (json, etc).
     * @return int
     * @since 6.1.0
     */
    public function getRequestDataType()
    {
        return $this->requestDataType;
    }

    /**
     * Set authdata.
     *
     * @param $username
     * @param $password
     * @param int $authType
     * @param int $authSource
     * @return WrapperConfig
     * @since 6.1.0
     */
    public function setAuthentication(
        $username,
        $password,
        $authType = authType::BASIC,
        $authSource = authSource::NORMAL
    ) {
        $this->authSource = $authSource;

        switch ($authSource) {
            case authSource::STREAM:
                if ($authType === authType::BASIC) {
                    $this->setDualStreamHttp(
                        'header',
                        'Authorization: Basic ' . base64_encode("$username:$password")
                    );
                }
                break;
            case authSource::SOAP:
                $this->authData['login'] = $username;
                $this->authData['password'] = $password;
                $this->setStreamOption('login', $this->authData['login']);
                $this->setStreamOption('password', $this->authData['password']);
                break;
            default:
                $this->authData['username'] = $username;
                $this->authData['password'] = $password;
                $this->authData['type'] = $authType;
                // Always push streamOptions for user/pass into the default flow to be compatible with soap
                // setups.
                $this->setStreamOption('login', $this->authData['username']);
                $this->setStreamOption('password', $this->authData['password']);
                break;
        }

        return $this;
    }

    /**
     * Replace current authdata manually to stream source if the default is set by mistake.
     *
     * @return $this
     * @since 6.1.0
     */
    public function setAuthStream()
    {
        if ($this->authSource === authSource::NORMAL) {
            $this->setAuthentication(
                $this->authData['username'],
                $this->authData['password'],
                $this->authData['type'],
                authSource::STREAM
            );
        }

        return $this;
    }

    /**
     * Get authdata.
     *
     * @return array
     * @since 6.1.0
     */
    public function getAuthentication()
    {
        return $this->authData;
    }

    /**
     * @param int $timeout Defaults to the default connect timeout in curl (300).
     * @param bool $useMillisec Set timeouts in milliseconds instead of seconds.
     * @return $this
     * @link https://curl.haxx.se/libcurl/c/curl_easy_setopt.html
     * @since 6.1.0
     */
    private function setTimeout($timeout = 300, $useMillisec = false)
    {
        /**
         * CURLOPT_TIMEOUT (Entire request) Everything has to be established and get finished on this time limit.
         * CURLOPT_CONNECTTIMEOUT (Connection phase) We set this to half the time of the entire timeout request.
         * CURLOPT_ACCEPTTIMEOUT (Waiting for connect back to be accepted). Defined in MS only.
         *
         * @link https://curl.haxx.se/libcurl/c/curl_easy_setopt.html
         */

        // Using internal WrapperCurlOpts if curl is not a present driver. Otherwise, this
        // setup may be a showstopper that no other driver can use.
        if (!$useMillisec) {
            $this->replaceOption(
                WrapperCurlOpt::NETCURL_CURLOPT_CONNECTTIMEOUT,
                ceil($timeout / 2),
                WrapperCurlOpt::NETCURL_CURLOPT_CONNECTTIMEOUT_MS
            );
            $this->replaceOption(
                WrapperCurlOpt::NETCURL_CURLOPT_TIMEOUT,
                ceil($timeout),
                WrapperCurlOpt::NETCURL_CURLOPT_TIMEOUT_MS
            );
        } else {
            $this->replaceOption(
                WrapperCurlOpt::NETCURL_CURLOPT_CONNECTTIMEOUT_MS,
                ceil($timeout / 2),
                WrapperCurlOpt::NETCURL_CURLOPT_CONNECTTIMEOUT
            );
            $this->replaceOption(
                WrapperCurlOpt::NETCURL_CURLOPT_TIMEOUT_MS,
                ceil($timeout),
                WrapperCurlOpt::NETCURL_CURLOPT_TIMEOUT
            );
        }

        $this->setStreamContext('timeout', (int)ceil($timeout), 'http');
        $this->setStreamContext('connection_timeout', (int)ceil($timeout / 2), 'http');

        return $this;
    }

    /**
     * @param $userAgentString
     * @return $this
     * @since 6.1.0
     */
    public function setUserAgent($userAgentString)
    {
        $this->isCustomUserAgent = true;

        $this->setOption(
            WrapperCurlOpt::NETCURL_CURLOPT_USERAGENT,
            $userAgentString
        );

        return $this;
    }

    /**
     * @return bool
     * @since 6.1.0
     */
    public function getIsCustomUserAgent()
    {
        return $this->isCustomUserAgent;
    }

    /**
     * Allows strict identification in user-agent header.
     * @param $activate
     * @param bool $allowPhpRelease
     * @since 6.1.0
     */
    public function setIdentifiers($activate, $allowPhpRelease = false)
    {
        $this->identifierAgent = $activate;
        $this->identifierAgentPhp = $allowPhpRelease;
    }

    /**
     * Status of identifierAgent, if enable or not.
     * @return bool
     * @since 6.1.0
     */
    public function getIdentifiers()
    {
        return $this->identifierAgent;
    }

    /**
     * @return mixed
     * @throws ExceptionHandler
     * @since 6.1.0
     */
    public function getUserAgent()
    {
        $globalSignature = self::getSignature();
        if (!empty($globalSignature)) {
            return $globalSignature;
        }

        $return = $this->getOption(WrapperCurlOpt::NETCURL_CURLOPT_USERAGENT);

        if ($this->getSoapRequest() || $this->getStreamRequest()) {
            $currentStreamContext = $this->getStreamContext();
            if (!is_null($currentStreamContext)) {
                $currentStreamContext = stream_context_get_options($currentStreamContext);
            }

            // If it is already set from another place.
            if (isset($currentStreamContext['http']['user_agent'])) {
                $return = $currentStreamContext['http']['user_agent'];
            }
        }

        return $return;
    }

    /**
     * @param bool $staging
     * @return WrapperConfig
     * @since 6.1.0
     */
    public function setStaging($staging = true)
    {
        $this->staging = $staging;
        if (!$staging) {
            $this->setWsdlCache(3);
        } else {
            $this->setWsdlCache(0);
        }
        return $this;
    }

    /**
     * @return bool
     * @since 6.1.0
     */
    public function getStaging()
    {
        return $this->staging;
    }

    /**
     * @param bool $isProduction
     * @return $this
     * @since 6.1.0
     */
    public function setProduction($isProduction = true)
    {
        $this->setStaging($isProduction ? false : true);
        return $this;
    }

    /**
     * @return bool
     * @since 6.1.0
     */
    public function getProduction()
    {
        return !$this->getStaging();
    }

    /**
     * Returns internal information about the configured timeouts.
     *
     * @return array
     * @since 6.1.0
     * @noinspection PhpUnusedPrivateMethodInspection
     */
    private function getTimeout()
    {
        $cTimeout = null;
        $eTimeout = null;

        $timeoutIsMillisec = false;
        if (isset($this->options[WrapperCurlOpt::NETCURL_CURLOPT_CONNECTTIMEOUT])) {
            $cTimeout = $this->options[WrapperCurlOpt::NETCURL_CURLOPT_CONNECTTIMEOUT]; // connectTimeout
        }
        if (isset($this->options[WrapperCurlOpt::NETCURL_CURLOPT_CONNECTTIMEOUT_MS])) {
            $cTimeout = $this->options[WrapperCurlOpt::NETCURL_CURLOPT_CONNECTTIMEOUT_MS]; // connectTimeout
            $timeoutIsMillisec = true;
        }

        if (isset($this->options[WrapperCurlOpt::NETCURL_CURLOPT_TIMEOUT])) {
            $eTimeout = $this->options[WrapperCurlOpt::NETCURL_CURLOPT_TIMEOUT];  // entireTimeout
        }
        if (isset($this->options[WrapperCurlOpt::NETCURL_CURLOPT_TIMEOUT_MS])) {
            $eTimeout = $this->options[WrapperCurlOpt::NETCURL_CURLOPT_TIMEOUT_MS];  // entireTimeout
            $timeoutIsMillisec = true;
        }

        return [
            'CONNECT' => $cTimeout,
            'REQUEST' => $eTimeout,
            'MILLISEC' => $timeoutIsMillisec,
        ];
    }

    /**
     * Quickset WSDL cache.
     *
     *   WSDL_CACHE_NONE = 0
     *   WSDL_CACHE_DISK = 1
     *   WSDL_CACHE_MEMORY = 2
     *   WSDL_CACHE_BOTH = 3
     *
     * @param int $cacheSet
     * @param null $ttlCache Cache lifetime. If null, this won't be set.
     * @return WrapperConfig
     * @since 6.1.0
     */
    public function setWsdlCache($cacheSet = 0, $ttlCache = null)
    {
        if (PHP_VERSION_ID >= 70000 && PHP_VERSION_ID < 70100) {
            // PHP 7.0 generates exit code 1 on this row, unless it's a string - don't ask why.
            $this->streamOptions['cache_wsdl'] = (string)$cacheSet;
        } else {
            $this->streamOptions['cache_wsdl'] = $cacheSet;
        }

        if ((int)$ttlCache > 0 && (new Ini())->getIniSettable('soap.wsdl_cache_ttl')) {
            ini_set('soap.wsdl_cache_ttl', $ttlCache);
        }

        return $this;
    }

    /**
     * @return mixed|null
     * @since 6.1.0
     */
    public function getWsdlCache()
    {
        return isset($this->streamOptions['cache_wsdl']) ? $this->streamOptions['cache_wsdl'] : null;
    }

    /**
     * @param array $funcArgs
     * @return bool
     * @throws Exception
     */
    public function getCompatibilityArguments($funcArgs = [])
    {
        $return = false;

        foreach ($funcArgs as $funcIndex => $funcValue) {
            switch ($funcIndex) {
                case 0:
                    if (!empty($funcValue)) {
                        $this->setRequestUrl($funcValue);
                        $return = true;
                    }
                    break;
                case 1:
                    if (is_array($funcValue) && count($funcValue)) {
                        $this->setRequestData($funcValue);
                        $return = true;
                    }
                    break;
                case 2:
                    $this->setRequestMethod($funcValue);
                    $return = true;
                    break;
                case 3:
                    $this->setRequestFlags(is_array($funcValue) ? $funcValue : []);
                    $return = true;
                    break;
            }
        }

        return $return;
    }

    /**
     * @param $isWrapped
     * @return $this
     */
    public function setNetWrapper($isWrapped)
    {
        $this->isNetWrapper = $isWrapped;

        return $this;
    }

    /**
     * Setting useragent statically and on global level.
     *
     * @param $userAgentSignature
     * @throws ExceptionHandler
     * @since 6.1.0
     */
    public static function setSignature($userAgentSignature)
    {
        if (defined('WRAPPERCONFIG_NO_SIGNATURE')) {
            throw new ExceptionHandler(
                'You are not allowed to set global signature.',
                Constants::LIB_UNHANDLED
            );
        }
        self::$userAgentSignature = $userAgentSignature;
    }

    /**
     * Remove static useragent.
     * @since 6.1.0
     */
    public static function deleteSignature()
    {
        self::$userAgentSignature = null;
    }

    /**
     * @return string
     * @since 6.1.0
     */
    public static function getSignature()
    {
        if (defined('NO_SIGNATURE')) {
            return null;
        }

        return self::$userAgentSignature;
    }

    /**
     * @param $userAgentSignatureString
     * @return $this
     * @throws ExceptionHandler
     * @since 6.1.0
     */
    public function setUserAgentSignature($userAgentSignatureString)
    {
        self::setSignature($userAgentSignatureString);
        return $this;
    }

    /**
     * @return string
     * @since 6.1.0
     */
    public function getUserAgentSignature()
    {
        return self::getSignature();
    }

    /**
     * @return string
     * @since 6.1.0
     */
    public function getCurrentWrapper()
    {
        return $this->currentWrapper;
    }

    /**
     * @param $currentWrapper
     * @return WrapperConfig
     * @since 6.1.0
     */
    public function setCurrentWrapper($currentWrapper)
    {
        if (!empty($currentWrapper)) {
            $this->currentWrapper = $currentWrapper;
        }

        return $this;
    }

    /**
     * @param bool $short
     * @return string
     * @since 6.1.0
     */
    public function getCurrentWrapperClass($short = false)
    {
        return !$short ? $this->currentWrapper : $this->getShortWrapperClass($this->currentWrapper);
    }

    /**
     * @param $namespaceClassName
     * @return mixed
     * @since 6.1.0
     */
    private function getShortWrapperClass($namespaceClassName)
    {
        $return = $namespaceClassName;

        if (!empty($this->currentWrapper)) {
            $wrapperClassExplode = explode('\\', $this->currentWrapper);
            if (is_array($wrapperClassExplode) && count($wrapperClassExplode)) {
                $return = $wrapperClassExplode[count($wrapperClassExplode) - 1];
            }
        }

        return $return;
    }

    /**
     * @param string $url
     * @param array $data
     * @param int $method
     * @param int $dataType
     * @return $this
     * @since 6.1.0
     */
    public function request($url = '', $data = [], $method = requestMethod::METHOD_GET, $dataType = dataType::NORMAL)
    {
        if (!empty($url)) {
            $this->setRequestUrl($url);
        }
        /** @noinspection CallableParameterUseCaseInTypeContextInspection */
        /** @var mixed $data */
        if ((is_string($data) && $data !== '') || (is_array($data) && count($data))) {
            $this->setRequestData($data);
        }

        if ($this->getRequestMethod() !== $method) {
            $this->setRequestMethod($method);
        }

        if ($this->getRequestDataType() !== $dataType) {
            $this->setRequestDataType($dataType);
        }

        return $this;
    }

    /**
     * Internal configset magics.
     *
     * @param $name
     * @param $arguments
     * @return $this|mixed
     * @throws ExceptionHandler
     * @since 6.1.0
     */
    public function __call($name, $arguments)
    {
        $methodType = '';
        $cutAfter = 3;
        $allowedTypes = ['get', 'is', 'set'];
        foreach ($allowedTypes as $item) {
            $testItem = @substr($name, 0, strlen($item));
            if (in_array($testItem, $allowedTypes, false)) {
                $methodType = $testItem;
                $cutAfter = strlen($item);
            }
        }
        $methodName = (new Strings())->getCamelCase(substr($name, $cutAfter));

        switch (strtolower($methodType)) {
            case 'set':
                if (method_exists($this, sprintf('set%s', ucfirst($methodName)))) {
                    call_user_func_array(
                        [
                            $this,
                            sprintf('set%s', ucfirst($methodName)),
                        ],
                        $arguments
                    );
                }

                $this->configData[$methodName] = array_pop($arguments);
                break;
            case 'get' || 'is':
                if (method_exists($this, sprintf('%s%s', 'get', ucfirst($methodName))) ||
                    method_exists($this, sprintf('%s%s', 'is', ucfirst($methodName)))
                ) {
                    return call_user_func_array(
                        [
                            $this,
                            sprintf(
                                'get%s',
                                ucfirst($methodName)
                            ),
                        ],
                        []
                    );
                }

                if (isset($this->configData[$methodName])) {
                    return $this->configData[$methodName];
                }

                throw new ExceptionHandler('Variable not set.', Constants::LIB_CONFIGWRAPPER_VAR_NOT_SET);
            default:
                break;
        }

        return $this;
    }
}