Snapshot: MLS sync fixes, image refresh, plugin/theme updates
MLS plugin fixes from this session: - Fix silent insert failures: location column NOT NULL was rejecting wpdb->insert calls, causing ~18k new properties since Dec 2025 to be lost. Inserts now build raw SQL with ST_PointFromText so the spatial column is populated atomically. - Auto-refresh expired media URLs in MLS_Media_Handler::fetch_and_cache(), guarded by a property-level GET_LOCK so concurrent fetches share one API refresh. - Normalize WP_Error to null in mls_get_property_image() so callers can rely on the documented string|null contract. - Support comma-separated property_type filters in MLS_Query and MLS_Cluster so the homepage "View All Commercial" link (?property_type=Commercial+Sale,Land,Farm) actually filters correctly. - Incremental sync now looks back 10 minutes past the latest modification timestamp as a safety margin against missed records. - Smart sync exits silently (info-level, not warning) when a full sync is in progress. Operational: - New cron: weekly full sync Sundays at 3 AM (/usr/local/bin/mls-full-sync). - New cron: hourly 2GB cap on mls-thumbnails/ and cache/transformed-images/ (/usr/local/bin/mls-image-cache-cap). - Logrotate config for wp-content/debug.log (2-day retention, daily rotation, delaycompress). Repo policy: - CLAUDE.md updated with explicit "commit everything except build artifacts" policy. - .gitignore: untrack runtime image caches and debug.log rotations. Other modifications in this snapshot are pre-existing in-flight theme/plugin/db_content_updates work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 APIMatic Ltd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core\Authentication;
|
||||
|
||||
interface AuthGroup
|
||||
{
|
||||
public const AND = "And";
|
||||
public const OR = "Or";
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core\Authentication;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestSetterInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\TypeValidatorInterface;
|
||||
use InvalidArgumentException;
|
||||
interface AuthInterface
|
||||
{
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(TypeValidatorInterface $validator) : void;
|
||||
public function apply(RequestSetterInterface $request) : void;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Response\ResponseInterface;
|
||||
interface ContextInterface
|
||||
{
|
||||
public function getRequest() : RequestInterface;
|
||||
public function getResponse() : ResponseInterface;
|
||||
}
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core;
|
||||
|
||||
interface Format
|
||||
{
|
||||
public const JSON = 'application/json';
|
||||
public const XML = 'application/xml';
|
||||
public const SCALAR = 'text/plain; charset=utf-8';
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core\Logger;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Response\ResponseInterface;
|
||||
interface ApiLoggerInterface
|
||||
{
|
||||
/**
|
||||
* Log the provided request.
|
||||
*
|
||||
* @param $request RequestInterface HTTP requests to be logged.
|
||||
*/
|
||||
public function logRequest(RequestInterface $request) : void;
|
||||
/**
|
||||
* Log the provided response.
|
||||
*
|
||||
* @param $response ResponseInterface HTTP responses to be logged.
|
||||
*/
|
||||
public function logResponse(ResponseInterface $response) : void;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core\Request;
|
||||
|
||||
interface NonEmptyParamInterface extends ParamInterface
|
||||
{
|
||||
public function requiredNonEmpty();
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core\Request;
|
||||
|
||||
use InvalidArgumentException;
|
||||
interface ParamInterface
|
||||
{
|
||||
/**
|
||||
* Pick current parameter's value from a collected parameters array,
|
||||
* if key is not found then use the given default value
|
||||
*/
|
||||
public function extract(string $key, $defaultValue = null);
|
||||
public function required();
|
||||
/**
|
||||
* To perform validation and serialization for un unusual types.
|
||||
*/
|
||||
public function serializeBy(callable $serializerMethod);
|
||||
/**
|
||||
* @param string $strictType Strict single type i.e. string, ModelName, etc. or group of types
|
||||
* in string format i.e. oneof(...), anyof(...)
|
||||
* @param string[] $serializerMethods Methods required for the serialization of specific types in
|
||||
* in the provided strict types/type, should be an array in the format:
|
||||
* ['path/to/method argumentType', ...]. Default: []
|
||||
*/
|
||||
public function strictType(string $strictType, array $serializerMethods = []);
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(TypeValidatorInterface $validator) : void;
|
||||
public function apply(RequestSetterInterface $request) : void;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core\Request;
|
||||
|
||||
interface RequestArraySerialization
|
||||
{
|
||||
public const INDEXED = "Indexed:&";
|
||||
public const UN_INDEXED = "UnIndexed:&";
|
||||
public const PLAIN = "Plain:&";
|
||||
public const CSV = "Csv:,";
|
||||
public const PSV = "Psv:|";
|
||||
public const TSV = "Tsv:\t";
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core\Request;
|
||||
|
||||
interface RequestInterface
|
||||
{
|
||||
public function getHttpMethod() : string;
|
||||
public function getQueryUrl() : string;
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public function getHeaders() : array;
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public function getParameters() : array;
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public function getEncodedParameters() : array;
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public function getMultipartParameters() : array;
|
||||
public function getBody();
|
||||
public function getRetryOption() : string;
|
||||
public function convert();
|
||||
public function toApiException(string $message);
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core\Request;
|
||||
|
||||
interface RequestMethod
|
||||
{
|
||||
public const GET = "Get";
|
||||
public const POST = "Post";
|
||||
public const PUT = "Put";
|
||||
public const PATCH = "Patch";
|
||||
public const DELETE = "Delete";
|
||||
public const HEAD = "Head";
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core\Request;
|
||||
|
||||
interface RequestSetterInterface extends RequestInterface
|
||||
{
|
||||
public function setHttpMethod(string $requestMethod) : void;
|
||||
public function appendPath(string $path) : void;
|
||||
public function addTemplate(string $key, $value) : void;
|
||||
public function addHeader(string $key, $value) : void;
|
||||
public function addEncodedFormParam(string $key, $value, $realValue) : void;
|
||||
public function addMultipartFormParam(string $key, $value) : void;
|
||||
public function addBodyParam($value, string $key = '') : void;
|
||||
public function setBodyFormat(string $format, callable $serializer) : void;
|
||||
public function setRetryOption(string $retryOption) : void;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core\Request;
|
||||
|
||||
use InvalidArgumentException;
|
||||
interface TypeValidatorInterface
|
||||
{
|
||||
/**
|
||||
* @param mixed $value Value to be verified against the types
|
||||
* @param string $strictType Strict single type i.e. string, ModelName, etc. or group of types
|
||||
* in string format i.e. oneof(...), anyof(...)
|
||||
* @param array $serializationMethods Methods required for the serialization of specific types in
|
||||
* in the provided types/type, should be an array in the format:
|
||||
* ['path/to/method argumentType', ...]. Default: []
|
||||
* @return mixed Returns validated and serialized $value
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function verifyTypes($value, string $strictType, array $serializationMethods = []);
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Core\Response;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Sdk\ConverterInterface;
|
||||
interface ResponseInterface
|
||||
{
|
||||
public function getStatusCode() : int;
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public function getHeaders() : array;
|
||||
public function getRawBody() : string;
|
||||
public function getBody();
|
||||
public function convert(ConverterInterface $converter);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Http;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Response\ResponseInterface;
|
||||
interface HttpClientInterface
|
||||
{
|
||||
/**
|
||||
* Sends request and receive response from server.
|
||||
*
|
||||
* @param RequestInterface $request Request to be sent
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function execute(RequestInterface $request) : ResponseInterface;
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Http;
|
||||
|
||||
interface HttpConfigurations
|
||||
{
|
||||
/**
|
||||
* Get timeout for API calls in seconds.
|
||||
*/
|
||||
public function getTimeout() : int;
|
||||
/**
|
||||
* Get whether to enable retries and backoff feature.
|
||||
*/
|
||||
public function shouldEnableRetries() : bool;
|
||||
/**
|
||||
* Get the number of retries to make.
|
||||
*/
|
||||
public function getNumberOfRetries() : int;
|
||||
/**
|
||||
* Get the retry time interval between the endpoint calls.
|
||||
*/
|
||||
public function getRetryInterval() : float;
|
||||
/**
|
||||
* Get exponential backoff factor to increase interval between retries.
|
||||
*/
|
||||
public function getBackOffFactor() : float;
|
||||
/**
|
||||
* Get the maximum wait time in seconds for overall retrying requests.
|
||||
*/
|
||||
public function getMaximumRetryWaitTime() : int;
|
||||
/**
|
||||
* Get whether to retry on request timeout.
|
||||
*/
|
||||
public function shouldRetryOnTimeout() : bool;
|
||||
/**
|
||||
* Get http status codes to retry against.
|
||||
*/
|
||||
public function getHttpStatusCodesToRetry() : array;
|
||||
/**
|
||||
* Get http methods to retry against.
|
||||
*/
|
||||
public function getHttpMethodsToRetry() : array;
|
||||
}
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Http;
|
||||
|
||||
interface RetryOption
|
||||
{
|
||||
/**
|
||||
* To retry request, ignoring httpMethods whitelist.
|
||||
*/
|
||||
const ENABLE_RETRY = "enableRetries";
|
||||
/**
|
||||
* To disable retries, ignoring httpMethods whitelist.
|
||||
*/
|
||||
const DISABLE_RETRY = "disableRetries";
|
||||
/**
|
||||
* To use global httpMethods whitelist to determine if request needs retrying.
|
||||
*/
|
||||
const USE_GLOBAL_SETTINGS = "useGlobalSettings";
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Sdk;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\ContextInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Response\ResponseInterface;
|
||||
interface ConverterInterface
|
||||
{
|
||||
public function createApiException(string $message, RequestInterface $request, ?ResponseInterface $response);
|
||||
public function createHttpContext(ContextInterface $context);
|
||||
public function createHttpRequest(RequestInterface $request);
|
||||
public function createHttpResponse(ResponseInterface $response);
|
||||
public function createApiResponse(ContextInterface $context, $deserializedBody);
|
||||
public function createFileWrapper(string $realFilePath, ?string $mimeType, ?string $filename);
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\CoreInterfaces\Sdk;
|
||||
|
||||
interface ExceptionInterface extends \Throwable
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 APIMatic Ltd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core;
|
||||
|
||||
use WPForms\Vendor\Core\Request\RequestBuilder;
|
||||
use WPForms\Vendor\Core\Response\Context;
|
||||
use WPForms\Vendor\Core\Response\ResponseHandler;
|
||||
class ApiCall
|
||||
{
|
||||
private $coreClient;
|
||||
/**
|
||||
* @var RequestBuilder|null
|
||||
*/
|
||||
private $requestBuilder;
|
||||
/**
|
||||
* @var ResponseHandler
|
||||
*/
|
||||
private $responseHandler;
|
||||
public function __construct(Client $coreClient)
|
||||
{
|
||||
$this->coreClient = $coreClient;
|
||||
$this->responseHandler = $coreClient->getGlobalResponseHandler();
|
||||
}
|
||||
public function requestBuilder(RequestBuilder $requestBuilder) : self
|
||||
{
|
||||
$this->requestBuilder = $requestBuilder;
|
||||
return $this;
|
||||
}
|
||||
public function responseHandler(ResponseHandler $responseHandler) : self
|
||||
{
|
||||
$this->responseHandler = $responseHandler;
|
||||
return $this;
|
||||
}
|
||||
public function execute()
|
||||
{
|
||||
$request = $this->requestBuilder->build($this->coreClient);
|
||||
$request->addAcceptHeader($this->responseHandler->getFormat());
|
||||
$this->coreClient->beforeRequest($request);
|
||||
$response = $this->coreClient->getHttpClient()->execute($request);
|
||||
$context = new Context($request, $response, $this->coreClient);
|
||||
$this->coreClient->afterResponse($context);
|
||||
return $this->responseHandler->getResult($context);
|
||||
}
|
||||
}
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Authentication;
|
||||
|
||||
use WPForms\Vendor\Core\Exceptions\AuthValidationException;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Authentication\AuthGroup;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Authentication\AuthInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestSetterInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\TypeValidatorInterface;
|
||||
use InvalidArgumentException;
|
||||
/**
|
||||
* Use to group multiple Auth schemes with either `AND` or `OR`
|
||||
*/
|
||||
class Auth implements AuthInterface
|
||||
{
|
||||
/**
|
||||
* @param self|string ...$auths
|
||||
*/
|
||||
public static function and(...$auths) : self
|
||||
{
|
||||
return new self($auths, AuthGroup::AND);
|
||||
}
|
||||
/**
|
||||
* @param self|string ...$auths
|
||||
*/
|
||||
public static function or(...$auths) : self
|
||||
{
|
||||
return new self($auths, AuthGroup::OR);
|
||||
}
|
||||
/**
|
||||
* @var array<Auth|string>
|
||||
*/
|
||||
private $auths;
|
||||
/**
|
||||
* @var AuthInterface[]
|
||||
*/
|
||||
private $selectedAuthGroups = [];
|
||||
/**
|
||||
* @var AuthInterface[]
|
||||
*/
|
||||
private $validatedAuthGroups = [];
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $groupType;
|
||||
/**
|
||||
* @param array $auths
|
||||
* @param string $groupType
|
||||
*/
|
||||
private function __construct(array $auths, string $groupType)
|
||||
{
|
||||
$this->auths = $auths;
|
||||
$this->groupType = $groupType;
|
||||
}
|
||||
/**
|
||||
* @param array<string,AuthInterface> $authManagers
|
||||
*/
|
||||
public function withAuthManagers(array $authManagers) : self
|
||||
{
|
||||
$this->selectedAuthGroups = \array_map(function ($auth) use($authManagers) {
|
||||
if (\is_string($auth) && isset($authManagers[$auth])) {
|
||||
return $authManagers[$auth];
|
||||
} elseif ($auth instanceof Auth) {
|
||||
return $auth->withAuthManagers($authManagers);
|
||||
}
|
||||
throw new InvalidArgumentException("AuthManager not found with name: " . \json_encode($auth));
|
||||
}, $this->auths);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @throws AuthValidationException
|
||||
*/
|
||||
public function validate(TypeValidatorInterface $validator) : void
|
||||
{
|
||||
$this->validatedAuthGroups = [];
|
||||
$errors = \array_filter(\array_map(function ($authGroup) use($validator) {
|
||||
try {
|
||||
$authGroup->validate($validator);
|
||||
if ($this->groupType == AuthGroup::AND || empty($this->validatedAuthGroups)) {
|
||||
// Add all authGroups as validated in AND group
|
||||
// but only the first one in OR group
|
||||
$this->validatedAuthGroups[] = $authGroup;
|
||||
}
|
||||
return \false;
|
||||
} catch (InvalidArgumentException $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
}, $this->selectedAuthGroups));
|
||||
if (empty($errors) || $this->groupType == AuthGroup::OR && !empty($this->validatedAuthGroups)) {
|
||||
return;
|
||||
}
|
||||
// throw exception if unable to apply Any Single authentication in AND group
|
||||
// OR if unable to apply All authentication in OR group
|
||||
throw AuthValidationException::init($errors);
|
||||
}
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function apply(RequestSetterInterface $request) : void
|
||||
{
|
||||
$this->validatedAuthGroups = \array_map(function ($authGroup) use($request) {
|
||||
$authGroup->apply($request);
|
||||
return $authGroup;
|
||||
}, $this->validatedAuthGroups);
|
||||
}
|
||||
}
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Authentication;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Authentication\AuthInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\ParamInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestSetterInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\TypeValidatorInterface;
|
||||
use InvalidArgumentException;
|
||||
/**
|
||||
* Use to apply authentication parameters to the request
|
||||
*/
|
||||
class CoreAuth implements AuthInterface
|
||||
{
|
||||
private $parameters;
|
||||
private $isValid = \false;
|
||||
/**
|
||||
* @param ParamInterface ...$parameters
|
||||
*/
|
||||
public function __construct(...$parameters)
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(TypeValidatorInterface $validator) : void
|
||||
{
|
||||
\array_walk($this->parameters, function ($param) use($validator) : void {
|
||||
$param->validate($validator);
|
||||
});
|
||||
$this->isValid = \true;
|
||||
}
|
||||
public function apply(RequestSetterInterface $request) : void
|
||||
{
|
||||
if (!$this->isValid) {
|
||||
return;
|
||||
}
|
||||
\array_walk($this->parameters, function ($param) use($request) : void {
|
||||
$param->apply($request);
|
||||
});
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core;
|
||||
|
||||
use WPForms\Vendor\Core\Authentication\Auth;
|
||||
use WPForms\Vendor\Core\Request\Parameters\MultipleParams;
|
||||
use WPForms\Vendor\Core\Request\Request;
|
||||
use WPForms\Vendor\Core\Response\Context;
|
||||
use WPForms\Vendor\Core\Response\ResponseHandler;
|
||||
use WPForms\Vendor\Core\Response\Types\ErrorType;
|
||||
use WPForms\Vendor\Core\Types\Sdk\CoreCallback;
|
||||
use WPForms\Vendor\Core\Utils\JsonHelper;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Authentication\AuthInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Logger\ApiLoggerInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\ParamInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Http\HttpClientInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Sdk\ConverterInterface;
|
||||
class Client
|
||||
{
|
||||
private static $converter;
|
||||
private static $jsonHelper;
|
||||
public static function getConverter(?Client $client = null) : ConverterInterface
|
||||
{
|
||||
if (isset($client)) {
|
||||
return $client->localConverter;
|
||||
}
|
||||
return self::$converter;
|
||||
}
|
||||
public static function getJsonHelper(?Client $client = null) : JsonHelper
|
||||
{
|
||||
if (isset($client)) {
|
||||
return $client->localJsonHelper;
|
||||
}
|
||||
return self::$jsonHelper;
|
||||
}
|
||||
private $httpClient;
|
||||
private $localConverter;
|
||||
private $localJsonHelper;
|
||||
private $authManagers;
|
||||
private $serverUrls;
|
||||
private $defaultServer;
|
||||
private $globalConfig;
|
||||
private $globalRuntimeConfig;
|
||||
private $globalErrors;
|
||||
private $apiCallback;
|
||||
private $apiLogger;
|
||||
/**
|
||||
* @param HttpClientInterface $httpClient
|
||||
* @param ConverterInterface $converter
|
||||
* @param JsonHelper $jsonHelper
|
||||
* @param array<string,AuthInterface> $authManagers
|
||||
* @param array<string,string> $serverUrls
|
||||
* @param string $defaultServer
|
||||
* @param ParamInterface[] $globalConfig
|
||||
* @param ParamInterface[] $globalRuntimeConfig
|
||||
* @param array<string,ErrorType> $globalErrors
|
||||
* @param CoreCallback|null $apiCallback
|
||||
* @param ApiLoggerInterface $apiLogger
|
||||
*/
|
||||
public function __construct(HttpClientInterface $httpClient, ConverterInterface $converter, JsonHelper $jsonHelper, array $authManagers, array $serverUrls, string $defaultServer, array $globalConfig, array $globalRuntimeConfig, array $globalErrors, ?CoreCallback $apiCallback, ApiLoggerInterface $apiLogger)
|
||||
{
|
||||
$this->httpClient = $httpClient;
|
||||
self::$converter = $converter;
|
||||
$this->localConverter = $converter;
|
||||
self::$jsonHelper = $jsonHelper;
|
||||
$this->localJsonHelper = $jsonHelper;
|
||||
$this->authManagers = $authManagers;
|
||||
$this->serverUrls = $serverUrls;
|
||||
$this->defaultServer = $defaultServer;
|
||||
$this->globalConfig = $globalConfig;
|
||||
$this->globalRuntimeConfig = $globalRuntimeConfig;
|
||||
$this->globalErrors = $globalErrors;
|
||||
$this->apiCallback = $apiCallback;
|
||||
$this->apiLogger = $apiLogger;
|
||||
}
|
||||
public function getGlobalRequest(?string $server = null) : Request
|
||||
{
|
||||
$globalParams = new MultipleParams('Global Parameters');
|
||||
$globalParams->parameters($this->globalConfig)->validate(self::getJsonHelper($this));
|
||||
return new Request($this->serverUrls[$server ?? $this->defaultServer], $this, $globalParams);
|
||||
}
|
||||
public function getGlobalResponseHandler() : ResponseHandler
|
||||
{
|
||||
$responseHandler = new ResponseHandler();
|
||||
\array_walk($this->globalErrors, function (ErrorType $error, string $key) use($responseHandler) : void {
|
||||
$responseHandler->throwErrorOn($key, $error);
|
||||
});
|
||||
return $responseHandler;
|
||||
}
|
||||
public function getHttpClient() : HttpClientInterface
|
||||
{
|
||||
return $this->httpClient;
|
||||
}
|
||||
public function getApiLogger() : ApiLoggerInterface
|
||||
{
|
||||
return $this->apiLogger;
|
||||
}
|
||||
public function validateAuth(Auth $auth) : Auth
|
||||
{
|
||||
$auth->withAuthManagers($this->authManagers)->validate(self::getJsonHelper($this));
|
||||
return $auth;
|
||||
}
|
||||
/**
|
||||
* @param ParamInterface[] $parameters
|
||||
*/
|
||||
public function validateParameters(array $parameters) : MultipleParams
|
||||
{
|
||||
$parameters = \array_merge($parameters, $this->globalRuntimeConfig);
|
||||
$paramGroup = new MultipleParams('Endpoint Parameters');
|
||||
$paramGroup->parameters($parameters)->validate(self::getJsonHelper($this));
|
||||
return $paramGroup;
|
||||
}
|
||||
public function beforeRequest(Request $request)
|
||||
{
|
||||
if (isset($this->apiCallback)) {
|
||||
$this->apiCallback->callOnBeforeWithConversion($request, self::getConverter($this));
|
||||
}
|
||||
$this->apiLogger->logRequest($request);
|
||||
}
|
||||
public function afterResponse(Context $context)
|
||||
{
|
||||
if (isset($this->apiCallback)) {
|
||||
$this->apiCallback->callOnAfterWithConversion($context, self::getConverter($this));
|
||||
}
|
||||
$this->apiLogger->logResponse($context->getResponse());
|
||||
}
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core;
|
||||
|
||||
use WPForms\Vendor\Core\Logger\ApiLogger;
|
||||
use WPForms\Vendor\Core\Logger\Configuration\LoggingConfiguration;
|
||||
use WPForms\Vendor\Core\Logger\NullApiLogger;
|
||||
use WPForms\Vendor\Core\Request\Parameters\HeaderParam;
|
||||
use WPForms\Vendor\Core\Response\Types\ErrorType;
|
||||
use WPForms\Vendor\Core\Types\Sdk\CoreCallback;
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
use WPForms\Vendor\Core\Utils\JsonHelper;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Authentication\AuthInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\ParamInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Http\HttpClientInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Sdk\ConverterInterface;
|
||||
class ClientBuilder
|
||||
{
|
||||
public static function init(HttpClientInterface $httpClient) : self
|
||||
{
|
||||
return new ClientBuilder($httpClient);
|
||||
}
|
||||
/**
|
||||
* @var HttpClientInterface
|
||||
*/
|
||||
private $httpClient;
|
||||
/**
|
||||
* @var ConverterInterface
|
||||
*/
|
||||
private $converter;
|
||||
/**
|
||||
* @var array<string,AuthInterface>
|
||||
*/
|
||||
private $authManagers = [];
|
||||
/**
|
||||
* @var array<string,ErrorType>
|
||||
*/
|
||||
private $globalErrors = [];
|
||||
/**
|
||||
* @var array<string,string>
|
||||
*/
|
||||
private $serverUrls = [];
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $defaultServer;
|
||||
/**
|
||||
* @var ParamInterface[]
|
||||
*/
|
||||
private $globalConfig = [];
|
||||
/**
|
||||
* @var ParamInterface[]
|
||||
*/
|
||||
private $globalRuntimeConfig = [];
|
||||
/**
|
||||
* @var CoreCallback|null
|
||||
*/
|
||||
private $apiCallback;
|
||||
/**
|
||||
* @var LoggingConfiguration|null
|
||||
*/
|
||||
private $loggingConfig;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $userAgent;
|
||||
/**
|
||||
* @var array<string,string>
|
||||
*/
|
||||
private $userAgentConfig = [];
|
||||
/**
|
||||
* @var JsonHelper
|
||||
*/
|
||||
private $jsonHelper;
|
||||
private function __construct(HttpClientInterface $httpClient)
|
||||
{
|
||||
$this->httpClient = $httpClient;
|
||||
}
|
||||
public function converter(ConverterInterface $converter) : self
|
||||
{
|
||||
$this->converter = $converter;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param array<string,AuthInterface> $authManagers
|
||||
* @return $this
|
||||
*/
|
||||
public function authManagers(array $authManagers) : self
|
||||
{
|
||||
$this->authManagers = $authManagers;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param array<string,ErrorType> $globalErrors
|
||||
* @return $this
|
||||
*/
|
||||
public function globalErrors(array $globalErrors) : self
|
||||
{
|
||||
$this->globalErrors = $globalErrors;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param array<string,string> $serverUrls
|
||||
* @return $this
|
||||
*/
|
||||
public function serverUrls(array $serverUrls, string $defaultServer) : self
|
||||
{
|
||||
$this->serverUrls = $serverUrls;
|
||||
$this->defaultServer = $defaultServer;
|
||||
return $this;
|
||||
}
|
||||
public function apiCallback($apiCallback) : self
|
||||
{
|
||||
if ($apiCallback instanceof CoreCallback) {
|
||||
$this->apiCallback = $apiCallback;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
public function loggingConfiguration(?LoggingConfiguration $loggingConfig) : self
|
||||
{
|
||||
$this->loggingConfig = $loggingConfig;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param ParamInterface[] $globalParams
|
||||
* @return $this
|
||||
*/
|
||||
public function globalConfig(array $globalParams) : self
|
||||
{
|
||||
$this->globalConfig = $globalParams;
|
||||
return $this;
|
||||
}
|
||||
public function globalRuntimeParam(ParamInterface $globalRuntimeParam) : self
|
||||
{
|
||||
$this->globalRuntimeConfig[] = $globalRuntimeParam;
|
||||
return $this;
|
||||
}
|
||||
public function userAgent(string $userAgent) : self
|
||||
{
|
||||
$this->userAgent = $userAgent;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param array<string,string> $userAgentConfig
|
||||
* @return $this
|
||||
*/
|
||||
public function userAgentConfig(array $userAgentConfig) : self
|
||||
{
|
||||
$this->userAgentConfig = $userAgentConfig;
|
||||
return $this;
|
||||
}
|
||||
public function jsonHelper(JsonHelper $jsonHelper) : self
|
||||
{
|
||||
$this->jsonHelper = $jsonHelper;
|
||||
return $this;
|
||||
}
|
||||
private function addUserAgentToGlobalHeaders() : void
|
||||
{
|
||||
if (\is_null($this->userAgent)) {
|
||||
return;
|
||||
}
|
||||
$placeHolders = ['{engine}' => 'PHP', '{engine-version}' => \phpversion(), '{os-info}' => CoreHelper::getOsInfo()];
|
||||
$placeHolders = \array_merge($placeHolders, $this->userAgentConfig);
|
||||
$this->userAgent = \str_replace(\array_keys($placeHolders), \array_values($placeHolders), $this->userAgent);
|
||||
$this->globalConfig[] = HeaderParam::init('user-agent', $this->userAgent);
|
||||
$this->userAgent = null;
|
||||
}
|
||||
public function build() : Client
|
||||
{
|
||||
$this->addUserAgentToGlobalHeaders();
|
||||
return new Client($this->httpClient, $this->converter, $this->jsonHelper, $this->authManagers, $this->serverUrls, $this->defaultServer, $this->globalConfig, $this->globalRuntimeConfig, $this->globalErrors, $this->apiCallback, \is_null($this->loggingConfig) ? new NullApiLogger() : new ApiLogger($this->loggingConfig));
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Exceptions;
|
||||
|
||||
use InvalidArgumentException;
|
||||
/**
|
||||
* Authentication Validation Exception.
|
||||
*/
|
||||
class AuthValidationException extends InvalidArgumentException
|
||||
{
|
||||
private const ERROR_MESSAGE_PREFIX = "Following authentication credentials are required:\n-> ";
|
||||
/**
|
||||
* Initialize a new instance of AuthValidationException
|
||||
*
|
||||
* @param string[] $errors An array of errors in authentication validation
|
||||
*/
|
||||
public static function init(array $errors) : AuthValidationException
|
||||
{
|
||||
return new self(self::ERROR_MESSAGE_PREFIX . \join("\n-> ", $errors));
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\Core\Logger;
|
||||
|
||||
use WPForms\Vendor\Core\Logger\Configuration\LoggingConfiguration;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Logger\ApiLoggerInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Response\ResponseInterface;
|
||||
class ApiLogger implements ApiLoggerInterface
|
||||
{
|
||||
private $config;
|
||||
public function __construct(LoggingConfiguration $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function logRequest(RequestInterface $request) : void
|
||||
{
|
||||
$contentType = $this->getHeaderValue(LoggerConstants::CONTENT_TYPE_HEADER, $request->getHeaders());
|
||||
$this->config->logMessage('Request {' . LoggerConstants::METHOD . '} {' . LoggerConstants::URL . '} {' . LoggerConstants::CONTENT_TYPE . '}', [LoggerConstants::METHOD => $request->getHttpMethod(), LoggerConstants::URL => $this->getRequestUrl($request), LoggerConstants::CONTENT_TYPE => $contentType]);
|
||||
if ($this->config->getRequestConfig()->shouldLogHeaders()) {
|
||||
$headers = $this->config->getRequestConfig()->getLoggableHeaders($request->getHeaders(), $this->config->shouldMaskSensitiveHeaders());
|
||||
$this->config->logMessage('Request Headers {' . LoggerConstants::HEADERS . '}', [LoggerConstants::HEADERS => $headers]);
|
||||
}
|
||||
if ($this->config->getRequestConfig()->shouldLogBody()) {
|
||||
$body = $request->getParameters();
|
||||
if (empty($body)) {
|
||||
$body = $request->getBody();
|
||||
}
|
||||
$this->config->logMessage('Request Body {' . LoggerConstants::BODY . '}', [LoggerConstants::BODY => $body]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function logResponse(ResponseInterface $response) : void
|
||||
{
|
||||
$contentLength = $this->getHeaderValue(LoggerConstants::CONTENT_LENGTH_HEADER, $response->getHeaders());
|
||||
$contentType = $this->getHeaderValue(LoggerConstants::CONTENT_TYPE_HEADER, $response->getHeaders());
|
||||
$this->config->logMessage('Response {' . LoggerConstants::STATUS_CODE . '} {' . LoggerConstants::CONTENT_LENGTH . '} {' . LoggerConstants::CONTENT_TYPE . '}', [LoggerConstants::STATUS_CODE => $response->getStatusCode(), LoggerConstants::CONTENT_LENGTH => $contentLength, LoggerConstants::CONTENT_TYPE => $contentType]);
|
||||
if ($this->config->getResponseConfig()->shouldLogHeaders()) {
|
||||
$headers = $this->config->getResponseConfig()->getLoggableHeaders($response->getHeaders(), $this->config->shouldMaskSensitiveHeaders());
|
||||
$this->config->logMessage('Response Headers {' . LoggerConstants::HEADERS . '}', [LoggerConstants::HEADERS => $headers]);
|
||||
}
|
||||
if ($this->config->getResponseConfig()->shouldLogBody()) {
|
||||
$this->config->logMessage('Response Body {' . LoggerConstants::BODY . '}', [LoggerConstants::BODY => $response->getRawBody()]);
|
||||
}
|
||||
}
|
||||
private function getHeaderValue(string $key, array $headers) : ?string
|
||||
{
|
||||
$key = \strtolower($key);
|
||||
foreach ($headers as $k => $value) {
|
||||
if (\strtolower($k) === $key) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private function getRequestUrl(RequestInterface $request) : string
|
||||
{
|
||||
$queryUrl = $request->getQueryUrl();
|
||||
if ($this->config->getRequestConfig()->shouldIncludeQueryInPath()) {
|
||||
return $queryUrl;
|
||||
}
|
||||
return \explode("?", $queryUrl)[0];
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Logger\Configuration;
|
||||
|
||||
use WPForms\Vendor\Core\Logger\LoggerConstants;
|
||||
class BaseHttpLoggingConfiguration
|
||||
{
|
||||
private $logBody;
|
||||
private $logHeaders;
|
||||
private $headersToInclude;
|
||||
private $headersToExclude;
|
||||
private $headersToUnmask;
|
||||
/**
|
||||
* Construct an instance of ResponseConfig for logging
|
||||
*
|
||||
* @param bool $logBody
|
||||
* @param bool $logHeaders
|
||||
* @param string[] $headersToInclude
|
||||
* @param string[] $headersToExclude
|
||||
* @param string[] $headersToUnmask
|
||||
*/
|
||||
public function __construct(bool $logBody, bool $logHeaders, array $headersToInclude, array $headersToExclude, array $headersToUnmask)
|
||||
{
|
||||
$this->logBody = $logBody;
|
||||
$this->logHeaders = $logHeaders;
|
||||
$this->headersToInclude = \array_map('strtolower', $headersToInclude);
|
||||
$this->headersToExclude = empty($headersToInclude) ? \array_map('strtolower', $headersToExclude) : [];
|
||||
$this->headersToUnmask = \array_merge(\array_map('strtolower', LoggerConstants::NON_SENSITIVE_HEADERS), \array_map('strtolower', $headersToUnmask));
|
||||
}
|
||||
/**
|
||||
* Indicates whether to log the body.
|
||||
*/
|
||||
public function shouldLogBody() : bool
|
||||
{
|
||||
return $this->logBody;
|
||||
}
|
||||
/**
|
||||
* Indicates whether to log the headers.
|
||||
*/
|
||||
public function shouldLogHeaders() : bool
|
||||
{
|
||||
return $this->logHeaders;
|
||||
}
|
||||
/**
|
||||
* Select the headers from the list of provided headers for logging.
|
||||
*
|
||||
* @param string[] $headers
|
||||
* @param bool $maskSensitiveHeaders
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getLoggableHeaders(array $headers, bool $maskSensitiveHeaders) : array
|
||||
{
|
||||
$sensitiveHeaders = [];
|
||||
$filteredHeaders = \array_filter($headers, function ($key) use($maskSensitiveHeaders, &$sensitiveHeaders) {
|
||||
$lowerCaseKey = \strtolower(\strval($key));
|
||||
if ($maskSensitiveHeaders && $this->isSensitiveHeader($lowerCaseKey)) {
|
||||
$sensitiveHeaders[$key] = '**Redacted**';
|
||||
}
|
||||
if ((empty($this->headersToInclude) || \in_array($lowerCaseKey, $this->headersToInclude, \true)) && (empty($this->headersToExclude) || !\in_array($lowerCaseKey, $this->headersToExclude, \true))) {
|
||||
return \true;
|
||||
}
|
||||
unset($sensitiveHeaders[$key]);
|
||||
return \false;
|
||||
}, \ARRAY_FILTER_USE_KEY);
|
||||
return \array_merge($filteredHeaders, $sensitiveHeaders);
|
||||
}
|
||||
private function isSensitiveHeader($headerKey) : bool
|
||||
{
|
||||
if (\in_array($headerKey, $this->headersToUnmask, \true)) {
|
||||
return \false;
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Logger\Configuration;
|
||||
|
||||
use WPForms\Vendor\Core\Logger\ConsoleLogger;
|
||||
use WPForms\Vendor\Psr\Log\LoggerInterface;
|
||||
class LoggingConfiguration
|
||||
{
|
||||
private $logger;
|
||||
private $level;
|
||||
private $maskSensitiveHeaders;
|
||||
private $requestConfig;
|
||||
private $responseConfig;
|
||||
public function __construct(?LoggerInterface $logger, string $level, bool $maskSensitiveHeaders, RequestConfiguration $requestConfig, ResponseConfiguration $responseConfig)
|
||||
{
|
||||
$this->logger = $logger ?? new ConsoleLogger('printf');
|
||||
$this->level = $level;
|
||||
$this->maskSensitiveHeaders = $maskSensitiveHeaders;
|
||||
$this->requestConfig = $requestConfig;
|
||||
$this->responseConfig = $responseConfig;
|
||||
}
|
||||
/**
|
||||
* Log the given message using the context array. This function uses the
|
||||
* LogLevel and Logger instance set via constructor of this class.
|
||||
*/
|
||||
public function logMessage(string $message, array $context) : void
|
||||
{
|
||||
$this->logger->log($this->level, $message, $context);
|
||||
}
|
||||
/**
|
||||
* Indicates whether sensitive headers should be masked in logs.
|
||||
*
|
||||
* @return bool True if sensitive headers should be masked, false otherwise.
|
||||
*/
|
||||
public function shouldMaskSensitiveHeaders() : bool
|
||||
{
|
||||
return $this->maskSensitiveHeaders;
|
||||
}
|
||||
/**
|
||||
* Gets the request configuration for logging.
|
||||
*
|
||||
* @return RequestConfiguration The request configuration.
|
||||
*/
|
||||
public function getRequestConfig() : RequestConfiguration
|
||||
{
|
||||
return $this->requestConfig;
|
||||
}
|
||||
/**
|
||||
* Gets the response configuration for logging.
|
||||
*
|
||||
* @return ResponseConfiguration The response configuration.
|
||||
*/
|
||||
public function getResponseConfig() : ResponseConfiguration
|
||||
{
|
||||
return $this->responseConfig;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Logger\Configuration;
|
||||
|
||||
class RequestConfiguration extends BaseHttpLoggingConfiguration
|
||||
{
|
||||
private $includeQueryInPath;
|
||||
/**
|
||||
* Construct an instance of RequestConfig for logging
|
||||
*
|
||||
* @param bool $includeQueryInPath
|
||||
* @param bool $logBody
|
||||
* @param bool $logHeaders
|
||||
* @param string[] $headersToInclude
|
||||
* @param string[] $headersToExclude
|
||||
* @param string[] $headersToUnmask
|
||||
*/
|
||||
public function __construct(bool $includeQueryInPath, bool $logBody, bool $logHeaders, array $headersToInclude, array $headersToExclude, array $headersToUnmask)
|
||||
{
|
||||
parent::__construct($logBody, $logHeaders, $headersToInclude, $headersToExclude, $headersToUnmask);
|
||||
$this->includeQueryInPath = $includeQueryInPath;
|
||||
}
|
||||
/**
|
||||
* Indicates whether to include query parameters in the logged path.
|
||||
*/
|
||||
public function shouldIncludeQueryInPath() : bool
|
||||
{
|
||||
return $this->includeQueryInPath;
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Logger\Configuration;
|
||||
|
||||
class ResponseConfiguration extends BaseHttpLoggingConfiguration
|
||||
{
|
||||
}
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\Core\Logger;
|
||||
|
||||
use Closure;
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
use WPForms\Vendor\Psr\Log\AbstractLogger;
|
||||
use WPForms\Vendor\Psr\Log\InvalidArgumentException;
|
||||
class ConsoleLogger extends AbstractLogger
|
||||
{
|
||||
/**
|
||||
* A callable function that takes in a format and any number of parameters to satisfy that format.
|
||||
* For example: "printf", will be called like printf('%s %s', 'a', 'b')
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
private $printer;
|
||||
public function __construct(callable $printer)
|
||||
{
|
||||
$this->printer = $printer;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function log($level, $message, array $context = []) : void
|
||||
{
|
||||
if (!\in_array($level, LoggerConstants::ALLOWED_LOG_LEVELS, \true)) {
|
||||
throw new InvalidArgumentException("Invalid LogLevel: {$level}. See Psr\\Log\\LogLevel.php for possible values of log levels.");
|
||||
}
|
||||
Closure::fromCallable($this->printer)("%s: %s\n", $level, \str_replace(\array_map(function ($key) {
|
||||
return '{' . $key . '}';
|
||||
}, \array_keys($context)), \array_map(function ($value) {
|
||||
return CoreHelper::serialize($value);
|
||||
}, $context), $message));
|
||||
}
|
||||
}
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\Core\Logger;
|
||||
|
||||
use WPForms\Vendor\Psr\Log\LogLevel;
|
||||
class LoggerConstants
|
||||
{
|
||||
public const NON_SENSITIVE_HEADERS = ["Accept", "Accept-Charset", "Accept-Encoding", "Accept-Language", "Access-Control-Allow-Origin", "Cache-Control", "Connection", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type", "Date", "ETag", "Expect", "Expires", "From", "Host", "If-Match", "If-Modified-Since", "If-None-Match", "If-Range", "If-Unmodified-Since", "Keep-Alive", "Last-Modified", "Location", "Max-Forwards", "Pragma", "Range", "Referer", "Retry-After", "Server", "Trailer", "Transfer-Encoding", "Upgrade", "User-Agent", "Vary", "Via", "Warning", "X-Forwarded-For", "X-Requested-With", "X-Powered-By"];
|
||||
public const ALLOWED_LOG_LEVELS = [LogLevel::EMERGENCY, LogLevel::ALERT, LogLevel::CRITICAL, LogLevel::ERROR, LogLevel::WARNING, LogLevel::NOTICE, LogLevel::INFO, LogLevel::DEBUG];
|
||||
public const METHOD = 'method';
|
||||
public const URL = 'url';
|
||||
public const HEADERS = 'headers';
|
||||
public const BODY = 'body';
|
||||
public const STATUS_CODE = 'statusCode';
|
||||
public const CONTENT_LENGTH = 'contentLength';
|
||||
public const CONTENT_TYPE = 'contentType';
|
||||
public const CONTENT_LENGTH_HEADER = 'content-length';
|
||||
public const CONTENT_TYPE_HEADER = 'content-type';
|
||||
}
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\Core\Logger;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Logger\ApiLoggerInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Response\ResponseInterface;
|
||||
class NullApiLogger implements ApiLoggerInterface
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function logRequest(RequestInterface $request) : void
|
||||
{
|
||||
// noop
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function logResponse(ResponseInterface $response) : void
|
||||
{
|
||||
// noop
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request\Parameters;
|
||||
|
||||
class AdditionalFormParams extends MultipleParams
|
||||
{
|
||||
/**
|
||||
* Initializes an AdditionalFormParams object.
|
||||
*/
|
||||
public static function init(?array $values) : self
|
||||
{
|
||||
return new self($values ?? []);
|
||||
}
|
||||
private function __construct(array $values)
|
||||
{
|
||||
parent::__construct('additional form');
|
||||
$this->parameters = \array_map(function ($key, $val) {
|
||||
return FormParam::init($key, $val);
|
||||
}, \array_keys($values), $values);
|
||||
}
|
||||
/**
|
||||
* Turns all parameters of the object to unIndexed.
|
||||
*/
|
||||
public function unIndexed() : self
|
||||
{
|
||||
$this->parameters = \array_map(function ($param) {
|
||||
return $param->unIndexed();
|
||||
}, $this->parameters);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Turns all parameters of the object to plain.
|
||||
*/
|
||||
public function plain() : self
|
||||
{
|
||||
$this->parameters = \array_map(function ($param) {
|
||||
return $param->plain();
|
||||
}, $this->parameters);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request\Parameters;
|
||||
|
||||
class AdditionalHeaderParams extends MultipleParams
|
||||
{
|
||||
public static function init(?array $values) : self
|
||||
{
|
||||
return new self($values ?? []);
|
||||
}
|
||||
private function __construct(array $values)
|
||||
{
|
||||
parent::__construct('additional header');
|
||||
$this->parameters = \array_map(function ($key, $val) {
|
||||
return HeaderParam::init($key, $val);
|
||||
}, \array_keys($values), $values);
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request\Parameters;
|
||||
|
||||
class AdditionalQueryParams extends MultipleParams
|
||||
{
|
||||
/**
|
||||
* Initializes a new AdditionalQueryParams object.
|
||||
*/
|
||||
public static function init(?array $values) : self
|
||||
{
|
||||
return new self($values ?? []);
|
||||
}
|
||||
private function __construct(array $values)
|
||||
{
|
||||
parent::__construct('additional query');
|
||||
$this->parameters = \array_map(function ($key, $val) {
|
||||
return QueryParam::init($key, $val);
|
||||
}, \array_keys($values), $values);
|
||||
}
|
||||
/**
|
||||
* Turns all parameters of the object to unIndexed.
|
||||
*/
|
||||
public function unIndexed() : self
|
||||
{
|
||||
$this->parameters = \array_map(function ($param) {
|
||||
return $param->unIndexed();
|
||||
}, $this->parameters);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Turns all parameters of the object to plain.
|
||||
*/
|
||||
public function plain() : self
|
||||
{
|
||||
$this->parameters = \array_map(function ($param) {
|
||||
return $param->plain();
|
||||
}, $this->parameters);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Turns all parameters of the object to comma separated.
|
||||
*/
|
||||
public function commaSeparated() : self
|
||||
{
|
||||
$this->parameters = \array_map(function ($param) {
|
||||
return $param->commaSeparated();
|
||||
}, $this->parameters);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Turns all parameters of the object to tab separated.
|
||||
*/
|
||||
public function tabSeparated() : self
|
||||
{
|
||||
$this->parameters = \array_map(function ($param) {
|
||||
return $param->tabSeparated();
|
||||
}, $this->parameters);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Turns all parameters of the object to pipe separated.
|
||||
*/
|
||||
public function pipeSeparated() : self
|
||||
{
|
||||
$this->parameters = \array_map(function ($param) {
|
||||
return $param->pipeSeparated();
|
||||
}, $this->parameters);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request\Parameters;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestSetterInterface;
|
||||
class BodyParam extends Parameter
|
||||
{
|
||||
/**
|
||||
* Initializes a body parameter with the value specified.
|
||||
*/
|
||||
public static function init($value) : self
|
||||
{
|
||||
return new self('', $value);
|
||||
}
|
||||
/**
|
||||
* Initializes a body parameter with the value and key provided.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function initWrapped(string $key, $value) : self
|
||||
{
|
||||
return new self($key, $value);
|
||||
}
|
||||
private function __construct(string $key, $value)
|
||||
{
|
||||
parent::__construct($key, $value, 'body');
|
||||
}
|
||||
/**
|
||||
* Adds the parameter to the request provided.
|
||||
*
|
||||
* @param RequestSetterInterface $request The request to add the parameter to.
|
||||
*/
|
||||
public function apply(RequestSetterInterface $request) : void
|
||||
{
|
||||
if ($this->validated) {
|
||||
$request->addBodyParam($this->value, $this->key);
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request\Parameters;
|
||||
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestArraySerialization;
|
||||
abstract class EncodedParam extends Parameter
|
||||
{
|
||||
protected $format = RequestArraySerialization::INDEXED;
|
||||
protected function __construct(string $key, $value, string $typeName)
|
||||
{
|
||||
parent::__construct($key, $value, $typeName);
|
||||
}
|
||||
/**
|
||||
* Generate URL-encoded query string from the giving list of parameters.
|
||||
*
|
||||
* @param array $data Input data to be encoded
|
||||
* @param string $parent Parent name accessor
|
||||
*
|
||||
* @return string Url encoded query string
|
||||
*/
|
||||
protected function httpBuildQuery(array $data, string $format, string $parent = '') : string
|
||||
{
|
||||
if ($format == RequestArraySerialization::INDEXED) {
|
||||
return \http_build_query($data);
|
||||
}
|
||||
$separatorFormat = \in_array($format, [RequestArraySerialization::TSV, RequestArraySerialization::PSV, RequestArraySerialization::CSV], \true);
|
||||
$innerAssociativeArray = !empty($parent) && CoreHelper::isAssociative($data);
|
||||
$first = \true;
|
||||
$separator = \substr($format, \strpos($format, ':') + 1);
|
||||
$result = [];
|
||||
\array_walk($data, function ($value, $key) use(&$result, &$first, $parent, $format, $separatorFormat, $separator, $innerAssociativeArray) : void {
|
||||
if (\is_null($value)) {
|
||||
return;
|
||||
}
|
||||
$key = $this->generateKeyWithParent($format, $key, $parent, \is_scalar($value));
|
||||
if (\is_array($value)) {
|
||||
$result[] = $this->httpBuildQuery($value, $format, $key);
|
||||
return;
|
||||
}
|
||||
if (!$separatorFormat) {
|
||||
$result[] = \http_build_query([$key => $value]);
|
||||
return;
|
||||
}
|
||||
$associativePartParam = "&" . \http_build_query([$key => $value]);
|
||||
if ($first) {
|
||||
$result[] = $associativePartParam;
|
||||
$first = \false;
|
||||
return;
|
||||
}
|
||||
if ($innerAssociativeArray) {
|
||||
$result[] = $associativePartParam;
|
||||
return;
|
||||
}
|
||||
$result[] = \urlencode($separator) . \urlencode(\strval($value));
|
||||
});
|
||||
return \implode($separatorFormat ? '' : '&', $result);
|
||||
}
|
||||
private function generateKeyWithParent(string $format, $key, string $parent, bool $isScalarValue) : string
|
||||
{
|
||||
if (empty($parent)) {
|
||||
return $key;
|
||||
}
|
||||
$keyForCurrentNonScalarNonAssociativeArray = "{$parent}[{$key}]";
|
||||
if (!\is_numeric($key)) {
|
||||
return $keyForCurrentNonScalarNonAssociativeArray;
|
||||
}
|
||||
if (!$isScalarValue) {
|
||||
return $keyForCurrentNonScalarNonAssociativeArray;
|
||||
}
|
||||
return $parent . $this->getKeyPostFix($format);
|
||||
}
|
||||
private function getKeyPostFix(string $format) : string
|
||||
{
|
||||
if ($format == RequestArraySerialization::UN_INDEXED) {
|
||||
return '[]';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request\Parameters;
|
||||
|
||||
use WPForms\Vendor\Core\Types\Sdk\CoreFileWrapper;
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestArraySerialization;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestSetterInterface;
|
||||
class FormParam extends EncodedParam
|
||||
{
|
||||
/**
|
||||
* Initializes a form parameter with the key and value provided.
|
||||
*/
|
||||
public static function init(string $key, $value) : self
|
||||
{
|
||||
return new self($key, $value);
|
||||
}
|
||||
/**
|
||||
* @var array<string,string>
|
||||
*/
|
||||
private $encodingHeaders = [];
|
||||
private function __construct(string $key, $value)
|
||||
{
|
||||
parent::__construct($key, $value, 'form');
|
||||
}
|
||||
/**
|
||||
* Sets encoding header with the key and value provided.
|
||||
*/
|
||||
public function encodingHeader(string $key, string $value) : self
|
||||
{
|
||||
$this->encodingHeaders[\strtolower($key)] = $value;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the parameter format to un-indexed.
|
||||
*/
|
||||
public function unIndexed() : self
|
||||
{
|
||||
$this->format = RequestArraySerialization::UN_INDEXED;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the parameter format to plain.
|
||||
*/
|
||||
public function plain() : self
|
||||
{
|
||||
$this->format = RequestArraySerialization::PLAIN;
|
||||
return $this;
|
||||
}
|
||||
private function isMultipart() : bool
|
||||
{
|
||||
return isset($this->encodingHeaders['content-type']) && $this->encodingHeaders['content-type'] != 'application/x-www-form-urlencoded';
|
||||
}
|
||||
/**
|
||||
* Adds the parameter to the request provided.
|
||||
*
|
||||
* @param RequestSetterInterface $request The request to add the parameter to.
|
||||
*/
|
||||
public function apply(RequestSetterInterface $request) : void
|
||||
{
|
||||
if (!$this->validated) {
|
||||
return;
|
||||
}
|
||||
if ($this->value instanceof CoreFileWrapper) {
|
||||
if (isset($this->encodingHeaders['content-type'])) {
|
||||
$this->value = $this->value->createCurlFileInstance($this->encodingHeaders['content-type']);
|
||||
} else {
|
||||
$this->value = $this->value->createCurlFileInstance();
|
||||
}
|
||||
$request->addMultipartFormParam($this->key, $this->value);
|
||||
return;
|
||||
}
|
||||
$this->value = CoreHelper::prepareValue($this->value, !$this->isMultipart());
|
||||
if ($this->isMultipart()) {
|
||||
$request->addMultipartFormParam($this->key, CoreHelper::serialize($this->value));
|
||||
return;
|
||||
}
|
||||
$encodedValue = $this->httpBuildQuery([$this->key => $this->value], $this->format);
|
||||
if (empty($encodedValue)) {
|
||||
return;
|
||||
}
|
||||
$request->addEncodedFormParam($this->key, $encodedValue, $this->value);
|
||||
}
|
||||
}
|
||||
wp-content/plugins/wpforms-lite/vendor_prefixed/apimatic/core/src/Request/Parameters/HeaderParam.php
Executable
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request\Parameters;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestSetterInterface;
|
||||
class HeaderParam extends Parameter
|
||||
{
|
||||
/**
|
||||
* Initializes a header parameter with the key and value provided.
|
||||
*/
|
||||
public static function init(string $key, $value) : self
|
||||
{
|
||||
return new self($key, $value);
|
||||
}
|
||||
private function __construct(string $key, $value)
|
||||
{
|
||||
parent::__construct($key, $value, 'header');
|
||||
}
|
||||
/**
|
||||
* Adds the parameter to the request provided.
|
||||
*
|
||||
* @param RequestSetterInterface $request The request to add the parameter to.
|
||||
*/
|
||||
public function apply(RequestSetterInterface $request) : void
|
||||
{
|
||||
if ($this->validated) {
|
||||
$request->addHeader($this->key, $this->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request\Parameters;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\ParamInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestSetterInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\TypeValidatorInterface;
|
||||
use InvalidArgumentException;
|
||||
class MultipleParams extends Parameter
|
||||
{
|
||||
/**
|
||||
* @var ParamInterface[]
|
||||
*/
|
||||
protected $parameters;
|
||||
public function __construct(string $typeName)
|
||||
{
|
||||
parent::__construct('', null, $typeName);
|
||||
}
|
||||
/**
|
||||
* @param ParamInterface[] $parameters
|
||||
*/
|
||||
public function parameters(array $parameters) : self
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Validates all parameters of the object.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(TypeValidatorInterface $validator) : void
|
||||
{
|
||||
if ($this->validated) {
|
||||
return;
|
||||
}
|
||||
\array_walk($this->parameters, function ($param) use($validator) : void {
|
||||
$param->validate($validator);
|
||||
});
|
||||
$this->validated = \true;
|
||||
}
|
||||
/**
|
||||
* Applies all parameters to the request provided.
|
||||
*/
|
||||
public function apply(RequestSetterInterface $request) : void
|
||||
{
|
||||
\array_walk($this->parameters, function ($param) use($request) : void {
|
||||
$param->apply($request);
|
||||
});
|
||||
}
|
||||
}
|
||||
Executable
+130
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request\Parameters;
|
||||
|
||||
use Closure;
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\NonEmptyParamInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\TypeValidatorInterface;
|
||||
use InvalidArgumentException;
|
||||
use Throwable;
|
||||
abstract class Parameter implements NonEmptyParamInterface
|
||||
{
|
||||
protected $key;
|
||||
protected $value;
|
||||
protected $validated = \false;
|
||||
private $valueMissing = \false;
|
||||
private $serializationError;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $paramStrictType;
|
||||
private $typeGroupSerializers = [];
|
||||
private $typeName;
|
||||
protected function __construct(string $key, $value, string $typeName)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
$this->typeName = $typeName;
|
||||
}
|
||||
private function getName() : string
|
||||
{
|
||||
return $this->key == '' ? $this->typeName : $this->key;
|
||||
}
|
||||
/**
|
||||
* Extracting inner value using a `key` only if the current value is a collection i.e. array/object,
|
||||
* If key is not found in the value array then defaultValue will be used.
|
||||
*/
|
||||
public function extract(string $key, $defaultValue = null) : self
|
||||
{
|
||||
if (\is_array($this->value)) {
|
||||
return $this->extractFromArray($key, $defaultValue);
|
||||
}
|
||||
if (\is_object($this->value)) {
|
||||
$this->value = (array) $this->value;
|
||||
return $this->extractFromArray($key, $defaultValue);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
private function extractFromArray(string $key, $defaultValue) : self
|
||||
{
|
||||
if (isset($this->value[$key])) {
|
||||
$this->value = $this->value[$key];
|
||||
return $this;
|
||||
}
|
||||
$this->value = $defaultValue;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Marks the value of the parameter as required and
|
||||
* throws an exception on validate if the value is missing.
|
||||
*/
|
||||
public function required() : self
|
||||
{
|
||||
if (\is_null($this->value)) {
|
||||
$this->valueMissing = \true;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Marks the value of the parameter as required + non-empty and
|
||||
* throws an exception on validate if the value is missing.
|
||||
*/
|
||||
public function requiredNonEmpty() : self
|
||||
{
|
||||
if (CoreHelper::isNullOrEmpty($this->value)) {
|
||||
$this->valueMissing = \true;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Serializes the parameter using the method provided.
|
||||
*
|
||||
* @param callable $serializerMethod The method to use for serialization.
|
||||
*/
|
||||
public function serializeBy(callable $serializerMethod) : self
|
||||
{
|
||||
try {
|
||||
$this->value = Closure::fromCallable($serializerMethod)($this->value);
|
||||
} catch (Throwable $e) {
|
||||
$this->serializationError = new InvalidArgumentException("Unable to serialize field: " . "{$this->getName()}, Due to:\n{$e->getMessage()}");
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param string $strictType Strict single type i.e. string, ModelName, etc. or group of types
|
||||
* in string format i.e. oneOf(...), anyOf(...)
|
||||
* @param string[] $serializerMethods Methods required for the serialization of specific types in
|
||||
* in the provided strict types/type, should be an array in the format:
|
||||
* ['path/to/method argumentType', ...]. Default: []
|
||||
*/
|
||||
public function strictType(string $strictType, array $serializerMethods = []) : self
|
||||
{
|
||||
$this->paramStrictType = $strictType;
|
||||
$this->typeGroupSerializers = $serializerMethods;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Validates if the parameter is in a valid state i.e. checks for missing value, serialization errors
|
||||
* and strict types.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(TypeValidatorInterface $validator) : void
|
||||
{
|
||||
if ($this->validated) {
|
||||
return;
|
||||
}
|
||||
if ($this->valueMissing) {
|
||||
throw new InvalidArgumentException("Missing required {$this->typeName} field: {$this->getName()}");
|
||||
}
|
||||
if (isset($this->serializationError)) {
|
||||
throw $this->serializationError;
|
||||
}
|
||||
if (isset($this->paramStrictType)) {
|
||||
$this->value = $validator->verifyTypes($this->value, $this->paramStrictType, $this->typeGroupSerializers);
|
||||
}
|
||||
$this->validated = \true;
|
||||
}
|
||||
}
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request\Parameters;
|
||||
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestArraySerialization;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestSetterInterface;
|
||||
class QueryParam extends EncodedParam
|
||||
{
|
||||
/**
|
||||
* Initializes a query parameter with the key and value provided.
|
||||
*/
|
||||
public static function init(string $key, $value) : self
|
||||
{
|
||||
return new self($key, $value);
|
||||
}
|
||||
private function __construct(string $key, $value)
|
||||
{
|
||||
parent::__construct($key, $value, 'query');
|
||||
}
|
||||
/**
|
||||
* Sets the parameter format to un-indexed.
|
||||
*/
|
||||
public function unIndexed() : self
|
||||
{
|
||||
$this->format = RequestArraySerialization::UN_INDEXED;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the parameter format to plain.
|
||||
*/
|
||||
public function plain() : self
|
||||
{
|
||||
$this->format = RequestArraySerialization::PLAIN;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the parameter format to comma separated.
|
||||
*/
|
||||
public function commaSeparated() : self
|
||||
{
|
||||
$this->format = RequestArraySerialization::CSV;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the parameter format to tab separated.
|
||||
*/
|
||||
public function tabSeparated() : self
|
||||
{
|
||||
$this->format = RequestArraySerialization::TSV;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the parameter format to pipe separated.
|
||||
*/
|
||||
public function pipeSeparated() : self
|
||||
{
|
||||
$this->format = RequestArraySerialization::PSV;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Adds the parameter to the request provided.
|
||||
*
|
||||
* @param RequestSetterInterface $request The request to add the parameter to.
|
||||
*/
|
||||
public function apply(RequestSetterInterface $request) : void
|
||||
{
|
||||
if (!$this->validated) {
|
||||
return;
|
||||
}
|
||||
$value = CoreHelper::prepareValue($this->value);
|
||||
$query = $this->httpBuildQuery([$this->key => $value], $this->format);
|
||||
if (empty($query)) {
|
||||
return;
|
||||
}
|
||||
$hasParams = \strrpos($request->getQueryUrl(), '?') > 0;
|
||||
$separator = $hasParams ? '&' : '?';
|
||||
$request->appendPath($separator . $query);
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request\Parameters;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestSetterInterface;
|
||||
class TemplateParam extends Parameter
|
||||
{
|
||||
/**
|
||||
* Initializes a template parameter with the key and value provided.
|
||||
*/
|
||||
public static function init(string $key, $value) : self
|
||||
{
|
||||
return new self($key, $value);
|
||||
}
|
||||
private $encode = \true;
|
||||
private function __construct(string $key, $value)
|
||||
{
|
||||
parent::__construct($key, $value, 'template');
|
||||
}
|
||||
/**
|
||||
* Disables http encoding for the parameter.
|
||||
*/
|
||||
public function dontEncode() : self
|
||||
{
|
||||
$this->encode = \false;
|
||||
return $this;
|
||||
}
|
||||
private function getReplacerValue($value) : string
|
||||
{
|
||||
if (\is_null($value)) {
|
||||
return '';
|
||||
}
|
||||
if (\is_bool($value)) {
|
||||
return $this->getEncodedReplacer(\var_export($value, \true));
|
||||
}
|
||||
if (\is_object($value)) {
|
||||
return $this->getReplacerForArray((array) $value);
|
||||
}
|
||||
if (\is_array($value)) {
|
||||
return $this->getReplacerForArray($value);
|
||||
}
|
||||
return $this->getEncodedReplacer($value);
|
||||
}
|
||||
private function getReplacerForArray(array $value) : string
|
||||
{
|
||||
return \implode("/", \array_map([$this, 'getReplacerValue'], $value));
|
||||
}
|
||||
private function getEncodedReplacer($value) : string
|
||||
{
|
||||
$value = \strval($value);
|
||||
return $this->encode ? \urlencode($value) : $value;
|
||||
}
|
||||
/**
|
||||
* Adds the parameter to the request provided.
|
||||
*
|
||||
* @param RequestSetterInterface $request The request to add the parameter to.
|
||||
*/
|
||||
public function apply(RequestSetterInterface $request) : void
|
||||
{
|
||||
if ($this->validated) {
|
||||
$request->addTemplate($this->key, $this->getReplacerValue($this->value));
|
||||
}
|
||||
}
|
||||
}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request;
|
||||
|
||||
use Closure;
|
||||
use WPForms\Vendor\Core\Client;
|
||||
use WPForms\Vendor\Core\Request\Parameters\MultipleParams;
|
||||
use WPForms\Vendor\Core\Types\Sdk\CoreFileWrapper;
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Format;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestMethod;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestSetterInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Http\RetryOption;
|
||||
class Request implements RequestSetterInterface
|
||||
{
|
||||
private $converter;
|
||||
private $queryUrl;
|
||||
private $requestMethod = RequestMethod::GET;
|
||||
private $headers = [];
|
||||
private $parameters = [];
|
||||
private $parametersEncoded = [];
|
||||
private $parametersMultipart = [];
|
||||
private $body;
|
||||
private $retryOption = RetryOption::USE_GLOBAL_SETTINGS;
|
||||
private $allowContentType = \true;
|
||||
/**
|
||||
* Creates a new Request object.
|
||||
*/
|
||||
public function __construct(string $queryUrl, ?Client $client = null, ?MultipleParams $globalParams = null)
|
||||
{
|
||||
$this->queryUrl = $queryUrl;
|
||||
$this->converter = Client::getConverter($client);
|
||||
if ($globalParams != null) {
|
||||
$globalParams->apply($this);
|
||||
}
|
||||
$this->queryUrl = CoreHelper::validateUrl($this->queryUrl);
|
||||
}
|
||||
/**
|
||||
* Returns the http method to be used for the call.
|
||||
*/
|
||||
public function getHttpMethod() : string
|
||||
{
|
||||
return $this->requestMethod;
|
||||
}
|
||||
/**
|
||||
* Returns the query URL for the request.
|
||||
*/
|
||||
public function getQueryUrl() : string
|
||||
{
|
||||
return $this->queryUrl;
|
||||
}
|
||||
/**
|
||||
* Returns the headers associated with the request.
|
||||
*/
|
||||
public function getHeaders() : array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
/**
|
||||
* Returns the parameters for the request.
|
||||
*/
|
||||
public function getParameters() : array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
/**
|
||||
* Returns encoded parameters associated the request.
|
||||
*/
|
||||
public function getEncodedParameters() : array
|
||||
{
|
||||
return $this->parametersEncoded;
|
||||
}
|
||||
/**
|
||||
* Returns multipart parameters associated with the request.
|
||||
*/
|
||||
public function getMultipartParameters() : array
|
||||
{
|
||||
return $this->parametersMultipart;
|
||||
}
|
||||
/**
|
||||
* Returns body associated with the request.
|
||||
*/
|
||||
public function getBody()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
/**
|
||||
* Returns the state of retryOption for the request.
|
||||
*/
|
||||
public function getRetryOption() : string
|
||||
{
|
||||
return $this->retryOption;
|
||||
}
|
||||
/**
|
||||
* Converts the request to HttpRequest.
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
return $this->converter->createHttpRequest($this);
|
||||
}
|
||||
/**
|
||||
* Creates an ApiException with the message provided.
|
||||
*/
|
||||
public function toApiException(string $message)
|
||||
{
|
||||
return $this->converter->createApiException($message, $this, null);
|
||||
}
|
||||
/**
|
||||
* Adds accept header to the request.
|
||||
*/
|
||||
public function addAcceptHeader(string $accept) : void
|
||||
{
|
||||
if (!$this->allowContentType) {
|
||||
return;
|
||||
}
|
||||
if ($accept == Format::SCALAR) {
|
||||
return;
|
||||
}
|
||||
$this->addHeader('Accept', $accept);
|
||||
}
|
||||
/**
|
||||
* Sets the Http Method to be used for current request.
|
||||
*/
|
||||
public function setHttpMethod(string $requestMethod) : void
|
||||
{
|
||||
$this->requestMethod = $requestMethod;
|
||||
}
|
||||
/**
|
||||
* Appends path to the query URL.
|
||||
*/
|
||||
public function appendPath(string $path) : void
|
||||
{
|
||||
$this->queryUrl .= $path;
|
||||
}
|
||||
/**
|
||||
* Add or replace a single header
|
||||
*
|
||||
* @param string $key key for the header
|
||||
* @param mixed $value value of the header
|
||||
*/
|
||||
public function addHeader(string $key, $value) : void
|
||||
{
|
||||
$this->headers[$key] = CoreHelper::serialize($value);
|
||||
}
|
||||
/**
|
||||
* Adds template param value to the query URL, corresponding to the key provided.
|
||||
*/
|
||||
public function addTemplate(string $key, $value) : void
|
||||
{
|
||||
$this->queryUrl = \str_replace("{{$key}}", $value, $this->queryUrl);
|
||||
}
|
||||
/**
|
||||
* Adds an encoded form param to the request.
|
||||
*/
|
||||
public function addEncodedFormParam(string $key, $value, $realValue) : void
|
||||
{
|
||||
$this->parametersEncoded[$key] = $value;
|
||||
$this->parameters[$key] = $realValue;
|
||||
}
|
||||
/**
|
||||
* Adds a multipart form param to the request.
|
||||
*/
|
||||
public function addMultipartFormParam(string $key, $value) : void
|
||||
{
|
||||
$this->parametersMultipart[$key] = $value;
|
||||
$this->parameters[$key] = $value;
|
||||
}
|
||||
/**
|
||||
* Adds a body param to the current request.
|
||||
*/
|
||||
public function addBodyParam($value, string $key = '') : void
|
||||
{
|
||||
if (empty($key)) {
|
||||
$this->body = $value;
|
||||
return;
|
||||
}
|
||||
if (\is_array($this->body)) {
|
||||
$this->body[$key] = $value;
|
||||
} else {
|
||||
$this->body = [$key => $value];
|
||||
}
|
||||
}
|
||||
private function addContentType(string $format) : void
|
||||
{
|
||||
if (!$this->allowContentType) {
|
||||
return;
|
||||
}
|
||||
if (\array_key_exists('content-type', \array_change_key_case($this->headers))) {
|
||||
return;
|
||||
}
|
||||
// if request has body, and content-type header is not already added
|
||||
// then add content-type, based on type and format of body
|
||||
if ($this->body instanceof CoreFileWrapper) {
|
||||
$this->addHeader('content-type', 'application/octet-stream');
|
||||
return;
|
||||
}
|
||||
if ($format != Format::JSON) {
|
||||
$this->addHeader('content-type', $format);
|
||||
return;
|
||||
}
|
||||
if (\is_array($this->body)) {
|
||||
$this->addHeader('content-type', Format::JSON);
|
||||
return;
|
||||
}
|
||||
if (\is_object($this->body)) {
|
||||
$this->addHeader('content-type', Format::JSON);
|
||||
return;
|
||||
}
|
||||
$this->addHeader('content-type', Format::SCALAR);
|
||||
}
|
||||
/**
|
||||
* Sets body format for the request and returns the body in a serialized format.
|
||||
*/
|
||||
public function setBodyFormat(string $format, callable $serializer) : void
|
||||
{
|
||||
if (!empty($this->parameters)) {
|
||||
return;
|
||||
}
|
||||
if (\is_null($this->body)) {
|
||||
return;
|
||||
}
|
||||
$this->addContentType($format);
|
||||
$this->body = Closure::fromCallable($serializer)($this->body);
|
||||
}
|
||||
/**
|
||||
* Sets value for retryOption for the request.
|
||||
*/
|
||||
public function setRetryOption(string $retryOption) : void
|
||||
{
|
||||
$this->retryOption = $retryOption;
|
||||
}
|
||||
/**
|
||||
* Sets if the request has an allowContentType header or not.
|
||||
*/
|
||||
public function shouldAddContentType(bool $allowContentType) : void
|
||||
{
|
||||
$this->allowContentType = $allowContentType;
|
||||
}
|
||||
}
|
||||
Executable
+132
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Request;
|
||||
|
||||
use WPForms\Vendor\Core\Authentication\Auth;
|
||||
use WPForms\Vendor\Core\Client;
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
use WPForms\Vendor\Core\Utils\XmlSerializer;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Format;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\ParamInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Http\RetryOption;
|
||||
class RequestBuilder
|
||||
{
|
||||
private $requestMethod;
|
||||
private $path;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $server;
|
||||
private $retryOption = RetryOption::USE_GLOBAL_SETTINGS;
|
||||
private $allowContentType = \true;
|
||||
/**
|
||||
* @var ParamInterface[]
|
||||
*/
|
||||
private $parameters = [];
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $bodySerializer = [CoreHelper::class, 'serialize'];
|
||||
private $bodyFormat = Format::JSON;
|
||||
/**
|
||||
* @var Auth|null
|
||||
*/
|
||||
private $auth;
|
||||
public function __construct(string $requestMethod, string $path)
|
||||
{
|
||||
$this->requestMethod = $requestMethod;
|
||||
$this->path = $path;
|
||||
}
|
||||
/**
|
||||
* The server URL to be set for the request.
|
||||
*/
|
||||
public function server(string $server) : self
|
||||
{
|
||||
$this->server = $server;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the retryOption value that is to be set for the request on creation.
|
||||
*/
|
||||
public function retryOption(string $retryOption) : self
|
||||
{
|
||||
$this->retryOption = $retryOption;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Disables setting of allowContentType for request on creation.
|
||||
*/
|
||||
public function disableContentType() : self
|
||||
{
|
||||
$this->allowContentType = \false;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param Auth|string ...$auths
|
||||
* @return $this
|
||||
*/
|
||||
public function auth(...$auths) : self
|
||||
{
|
||||
$this->auth = Auth::or(...$auths);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Parameters to be set on request creation.
|
||||
*/
|
||||
public function parameters(ParamInterface ...$parameters) : self
|
||||
{
|
||||
$this->parameters = \array_merge($this->parameters, $parameters);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets body format to xml and serializes the body to xml.
|
||||
*/
|
||||
public function bodyXml(string $rootName) : self
|
||||
{
|
||||
$this->bodyFormat = Format::XML;
|
||||
$this->bodySerializer = function ($value) use($rootName) : string {
|
||||
return (new XmlSerializer([]))->serialize($rootName, $value);
|
||||
};
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets body format to xml and serializes the body to xml.
|
||||
*/
|
||||
public function bodyXmlArray(string $rootName, string $itemName) : self
|
||||
{
|
||||
$this->bodyFormat = Format::XML;
|
||||
$this->bodySerializer = function ($value) use($rootName, $itemName) : string {
|
||||
return (new XmlSerializer([]))->serializeArray($rootName, $itemName, $value);
|
||||
};
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets body format to xml and serializes the body to xml.
|
||||
*/
|
||||
public function bodyXmlMap(string $rootName) : self
|
||||
{
|
||||
$this->bodyFormat = Format::XML;
|
||||
$this->bodySerializer = function ($value) use($rootName) : string {
|
||||
return (new XmlSerializer([]))->serializeMap($rootName, $value);
|
||||
};
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Initializes a new Request object with the properties set within RequestBuilder.
|
||||
*/
|
||||
public function build(CLient $coreClient) : Request
|
||||
{
|
||||
$request = $coreClient->getGlobalRequest($this->server);
|
||||
$request->appendPath($this->path);
|
||||
$request->setHttpMethod($this->requestMethod);
|
||||
$request->setRetryOption($this->retryOption);
|
||||
$request->shouldAddContentType($this->allowContentType);
|
||||
$coreClient->validateParameters($this->parameters)->apply($request);
|
||||
if (isset($this->auth)) {
|
||||
$coreClient->validateAuth($this->auth)->apply($request);
|
||||
}
|
||||
$request->setBodyFormat($this->bodyFormat, $this->bodySerializer);
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Response;
|
||||
|
||||
use WPForms\Vendor\Core\Client;
|
||||
use WPForms\Vendor\Core\Utils\JsonHelper;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\ContextInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Response\ResponseInterface;
|
||||
class Context implements ContextInterface
|
||||
{
|
||||
private $request;
|
||||
private $response;
|
||||
private $converter;
|
||||
private $jsonHelper;
|
||||
/**
|
||||
* Initializes a new Context with the request, response, jsonHelper and the converter set.
|
||||
*/
|
||||
public function __construct(RequestInterface $request, ResponseInterface $response, Client $client)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
$this->converter = Client::getConverter($client);
|
||||
$this->jsonHelper = Client::getJsonHelper($client);
|
||||
}
|
||||
/**
|
||||
* Returns Request object.
|
||||
*/
|
||||
public function getRequest() : RequestInterface
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
/**
|
||||
* Returns Response object.
|
||||
*/
|
||||
public function getResponse() : ResponseInterface
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
/**
|
||||
* Returns Response body as a scalar or an associative array.
|
||||
*/
|
||||
public function getResponseBody()
|
||||
{
|
||||
$responseBody = $this->response->getBody();
|
||||
if (\is_object($responseBody)) {
|
||||
return (array) $responseBody;
|
||||
}
|
||||
return $responseBody;
|
||||
}
|
||||
/**
|
||||
* Is successful response.
|
||||
*/
|
||||
public function isFailure() : bool
|
||||
{
|
||||
$statusCode = $this->response->getStatusCode();
|
||||
return $statusCode !== \min(\max($statusCode, 200), 208);
|
||||
// [200,208] = HTTP OK
|
||||
}
|
||||
/**
|
||||
* Is response body missing.
|
||||
*/
|
||||
public function isBodyMissing() : bool
|
||||
{
|
||||
$rawBody = $this->response->getRawBody();
|
||||
return \trim($rawBody) === '';
|
||||
}
|
||||
/**
|
||||
* Returns JsonHelper object.
|
||||
*/
|
||||
public function getJsonHelper() : JsonHelper
|
||||
{
|
||||
return $this->jsonHelper;
|
||||
}
|
||||
/**
|
||||
* Returns an ApiException with errorMessage and childClass set, if not null.
|
||||
*/
|
||||
public function toApiException(string $errorMessage, ?string $childClass = null)
|
||||
{
|
||||
$responseBody = $this->response->getBody();
|
||||
if (\is_null($childClass)) {
|
||||
return $this->converter->createApiException($errorMessage, $this->request, $this->response);
|
||||
}
|
||||
if (!\is_object($responseBody)) {
|
||||
return $this->converter->createApiException($errorMessage, $this->request, $this->response);
|
||||
}
|
||||
$responseBody->reason = $errorMessage;
|
||||
$responseBody->request = $this->request->convert();
|
||||
$responseBody->response = $this->response->convert($this->converter);
|
||||
return $this->jsonHelper->mapClass($responseBody, $childClass);
|
||||
}
|
||||
/**
|
||||
* Returns a MockApiResponse object from the context and the deserializedBody provided.
|
||||
*/
|
||||
public function toApiResponse($deserializedBody)
|
||||
{
|
||||
return $this->converter->createApiResponse($this, $deserializedBody);
|
||||
}
|
||||
}
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Response;
|
||||
|
||||
use WPForms\Vendor\Core\Response\Types\ErrorType;
|
||||
class ResponseError
|
||||
{
|
||||
/**
|
||||
* @var array<string,ErrorType>
|
||||
*/
|
||||
private $errors;
|
||||
private $useApiResponse = \false;
|
||||
private $nullOn404 = \false;
|
||||
/**
|
||||
* Adds an error to the errors array with the errorCode and ErrorType provided.
|
||||
*/
|
||||
public function addError(string $errorCode, ErrorType $error) : void
|
||||
{
|
||||
$this->errors[$errorCode] = $error;
|
||||
}
|
||||
public function returnApiResponse() : void
|
||||
{
|
||||
$this->useApiResponse = \true;
|
||||
}
|
||||
/**
|
||||
* Sets the nullOn404 flag.
|
||||
*/
|
||||
public function nullOn404() : void
|
||||
{
|
||||
$this->nullOn404 = \true;
|
||||
}
|
||||
private function shouldReturnNull(int $statusCode) : bool
|
||||
{
|
||||
if (!$this->nullOn404) {
|
||||
return \false;
|
||||
}
|
||||
if ($statusCode !== 404) {
|
||||
return \false;
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* Returns calculated result on failure or throws an exception.
|
||||
*/
|
||||
public function getResult(Context $context)
|
||||
{
|
||||
$statusCode = $context->getResponse()->getStatusCode();
|
||||
if ($this->shouldReturnNull($statusCode)) {
|
||||
if ($this->useApiResponse) {
|
||||
return $context->toApiResponse(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if ($this->useApiResponse) {
|
||||
return $context->toApiResponse($context->getResponseBody());
|
||||
}
|
||||
if (isset($this->errors[\strval($statusCode)])) {
|
||||
throw $this->errors[\strval($statusCode)]->throwable($context);
|
||||
}
|
||||
if (isset($this->errors[\strval(0)])) {
|
||||
throw $this->errors[\strval(0)]->throwable($context);
|
||||
// throw default error (if set)
|
||||
}
|
||||
throw $context->toApiException('HTTP Response Not OK');
|
||||
}
|
||||
}
|
||||
Executable
+175
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Response;
|
||||
|
||||
use WPForms\Vendor\Core\Response\Types\DeserializableType;
|
||||
use WPForms\Vendor\Core\Response\Types\ErrorType;
|
||||
use WPForms\Vendor\Core\Response\Types\ResponseMultiType;
|
||||
use WPForms\Vendor\Core\Response\Types\ResponseType;
|
||||
use WPForms\Vendor\Core\Utils\XmlDeserializer;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Format;
|
||||
class ResponseHandler
|
||||
{
|
||||
private $format = Format::SCALAR;
|
||||
private $deserializableType;
|
||||
private $responseType;
|
||||
private $responseMultiType;
|
||||
private $responseError;
|
||||
private $useApiResponse = \false;
|
||||
private $nullableType = \false;
|
||||
public function __construct()
|
||||
{
|
||||
$this->responseError = new ResponseError();
|
||||
$this->deserializableType = new DeserializableType();
|
||||
$this->responseType = new ResponseType();
|
||||
$this->responseMultiType = new ResponseMultiType();
|
||||
}
|
||||
/**
|
||||
* Associates an ErrorType object to the statusCode provided.
|
||||
*/
|
||||
public function throwErrorOn(string $statusCode, ErrorType $error) : self
|
||||
{
|
||||
$this->responseError->addError($statusCode, $error);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Wrap the actual success/failure response in ApiResponse
|
||||
*/
|
||||
public function returnApiResponse() : self
|
||||
{
|
||||
$this->useApiResponse = \true;
|
||||
$this->responseError->returnApiResponse();
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the nullOn404 flag in ResponseError.
|
||||
*/
|
||||
public function nullOn404() : self
|
||||
{
|
||||
$this->responseError->nullOn404();
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the return type as nullable.
|
||||
*/
|
||||
public function nullableType() : self
|
||||
{
|
||||
$this->nullableType = \true;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the deserializer method to the one provided, for deserializableType.
|
||||
*/
|
||||
public function deserializerMethod(callable $deserializerMethod) : self
|
||||
{
|
||||
$this->deserializableType->setDeserializerMethod($deserializerMethod);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets response type to the one provided and format to JSON.
|
||||
*
|
||||
* @param string $responseClass Response type class
|
||||
* @param int $dimensions Dimensions to be provided in case of an array
|
||||
*/
|
||||
public function type(string $responseClass, int $dimensions = 0) : self
|
||||
{
|
||||
$this->format = Format::JSON;
|
||||
$this->responseType->setResponseClass($responseClass);
|
||||
$this->responseType->setDimensions($dimensions);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets response type to the one provided and format to XML.
|
||||
*
|
||||
* @param string $responseClass Type of Response class
|
||||
* @param string $rootName Name of the root in xml response
|
||||
*/
|
||||
public function typeXml(string $responseClass, string $rootName) : self
|
||||
{
|
||||
$this->format = Format::XML;
|
||||
$this->responseType->setResponseClass($responseClass);
|
||||
$this->responseType->setXmlDeserializer(function ($value, $class) use($rootName) {
|
||||
return (new XmlDeserializer())->deserialize($value, $rootName, $class);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets response type to the one provided and format to XML.
|
||||
*
|
||||
* @param string $responseClass Type of Response class
|
||||
* @param string $rootName Name of the root for map in xml response
|
||||
*/
|
||||
public function typeXmlMap(string $responseClass, string $rootName) : self
|
||||
{
|
||||
$this->format = Format::XML;
|
||||
$this->responseType->setResponseClass($responseClass);
|
||||
$this->responseType->setXmlDeserializer(function ($value, $class) use($rootName) : ?array {
|
||||
return (new XmlDeserializer())->deserializeToMap($value, $rootName, $class);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets response type to the one provided and format to XML.
|
||||
*
|
||||
* @param string $responseClass Type of Response class
|
||||
* @param string $rootName Name of the root for array in xml response
|
||||
* @param string $itemName Name of each item in array
|
||||
*/
|
||||
public function typeXmlArray(string $responseClass, string $rootName, string $itemName) : self
|
||||
{
|
||||
$this->format = Format::XML;
|
||||
$this->responseType->setResponseClass($responseClass);
|
||||
$this->responseType->setXmlDeserializer(function ($value, $class) use($rootName, $itemName) : ?array {
|
||||
return (new XmlDeserializer())->deserializeToArray($value, $rootName, $itemName, $class);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param string $typeGroup Group of types in string format i.e. oneOf(...), anyOf(...)
|
||||
* @param string[] $typeGroupDeserializers Methods required for deserialization of specific types in
|
||||
* in the provided typeGroup, should be an array in the format:
|
||||
* ['path/to/method returnType', ...]. Default: []
|
||||
*/
|
||||
public function typeGroup(string $typeGroup, array $typeGroupDeserializers = []) : self
|
||||
{
|
||||
$this->format = Format::JSON;
|
||||
$this->responseMultiType->setTypeGroup($typeGroup);
|
||||
$this->responseMultiType->setDeserializers($typeGroupDeserializers);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Returns current set format.
|
||||
*/
|
||||
public function getFormat() : string
|
||||
{
|
||||
return $this->format;
|
||||
}
|
||||
/**
|
||||
* Returns response from the context provided.
|
||||
*
|
||||
* @param Context $context
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResult(Context $context)
|
||||
{
|
||||
if ($context->isFailure()) {
|
||||
return $this->responseError->getResult($context);
|
||||
}
|
||||
if ($this->nullableType && $context->isBodyMissing()) {
|
||||
return $this->getResponse($context, null);
|
||||
}
|
||||
$result = $this->deserializableType->getFrom($context);
|
||||
$result = $result ?? $this->responseType->getFrom($context);
|
||||
$result = $result ?? $this->responseMultiType->getFrom($context);
|
||||
$result = $result ?? $context->getResponseBody();
|
||||
return $this->getResponse($context, $result);
|
||||
}
|
||||
private function getResponse(Context $context, $result)
|
||||
{
|
||||
if ($this->useApiResponse) {
|
||||
return $context->toApiResponse($result);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\Core\Response\Types;
|
||||
|
||||
use Closure;
|
||||
use WPForms\Vendor\Core\Response\Context;
|
||||
class DeserializableType
|
||||
{
|
||||
/**
|
||||
* @var callable|null
|
||||
*/
|
||||
private $deserializerMethod;
|
||||
/**
|
||||
* Sets deserializer method to the one provided.
|
||||
*/
|
||||
public function setDeserializerMethod(callable $deserializerMethod) : void
|
||||
{
|
||||
$this->deserializerMethod = $deserializerMethod;
|
||||
}
|
||||
/**
|
||||
* Returns the deserializer method if already set.
|
||||
*/
|
||||
public function getFrom(Context $context)
|
||||
{
|
||||
if (\is_null($this->deserializerMethod)) {
|
||||
return null;
|
||||
}
|
||||
return Closure::fromCallable($this->deserializerMethod)($context->getResponse()->getBody());
|
||||
}
|
||||
}
|
||||
Executable
+127
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Response\Types;
|
||||
|
||||
use WPForms\Vendor\Core\Response\Context;
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Response\ResponseInterface;
|
||||
use WPForms\Vendor\Rs\Json\Pointer;
|
||||
class ErrorType
|
||||
{
|
||||
/**
|
||||
* Initializes a new object with the description and class name provided.
|
||||
*/
|
||||
public static function init(string $description, ?string $className = null) : self
|
||||
{
|
||||
return new self($description, $className, \false);
|
||||
}
|
||||
/**
|
||||
* Initializes a new object with error template and class name provided.
|
||||
*/
|
||||
public static function initWithErrorTemplate(string $errorTemplate, ?string $className = null) : self
|
||||
{
|
||||
return new self($errorTemplate, $className, \true);
|
||||
}
|
||||
private $description;
|
||||
private $className;
|
||||
private $hasErrorTemplate;
|
||||
private function __construct(string $description, ?string $className, bool $hasErrorTemplate)
|
||||
{
|
||||
$this->description = $description;
|
||||
$this->className = $className;
|
||||
$this->hasErrorTemplate = $hasErrorTemplate;
|
||||
}
|
||||
/**
|
||||
* Throws an Api exception from the context provided.
|
||||
*/
|
||||
public function throwable(Context $context)
|
||||
{
|
||||
$this->updateErrorDescriptionTemplate($context->getResponse());
|
||||
return $context->toApiException($this->description, $this->className);
|
||||
}
|
||||
private function updateErrorDescriptionTemplate($response) : void
|
||||
{
|
||||
if (!$this->hasErrorTemplate) {
|
||||
return;
|
||||
}
|
||||
$errorDescriptionTemplate = $this->description;
|
||||
$jsonPointersInTemplate = $this->getJsonPointersFromTemplate($errorDescriptionTemplate);
|
||||
$errorDescription = $this->updateResponsePlaceholderValues($errorDescriptionTemplate, $jsonPointersInTemplate, $response);
|
||||
$errorDescription = $this->updateHeaderPlaceHolderValues($errorDescription, $response);
|
||||
$errorDescription = $this->addPlaceHolderValue($errorDescription, '{$statusCode}', $response->getStatusCode());
|
||||
$this->description = $errorDescription;
|
||||
}
|
||||
private function updateHeaderPlaceHolderValues(string $errorDescription, ResponseInterface $response) : string
|
||||
{
|
||||
$headers = $response->getHeaders();
|
||||
$headerKeys = \array_keys($headers);
|
||||
for ($x = 0; $x < \count($headerKeys); $x++) {
|
||||
$errorDescription = $this->addPlaceHolderValue($errorDescription, '{$response.header.' . $headerKeys[$x] . '}', $headers[$headerKeys[$x]], \true);
|
||||
}
|
||||
return $errorDescription;
|
||||
}
|
||||
/**
|
||||
* @param $errorDescription string
|
||||
* @param $jsonPointersInTemplate string[]
|
||||
* @param $response ResponseInterface
|
||||
* @return string Updated error string template.
|
||||
*/
|
||||
private function updateResponsePlaceholderValues(string $errorDescription, array $jsonPointersInTemplate, ResponseInterface $response) : string
|
||||
{
|
||||
if (\count($jsonPointersInTemplate[0]) < 1) {
|
||||
return $this->addPlaceHolderValue($errorDescription, '{$response.body}', $response->getRawBody());
|
||||
}
|
||||
$jsonResponsePointer = $this->initializeJsonPointer($response);
|
||||
$jsonPointers = $jsonPointersInTemplate[0];
|
||||
for ($x = 0; $x < \count($jsonPointers); $x++) {
|
||||
$placeHolderValue = $this->getJsonPointerValue($jsonResponsePointer, \ltrim($jsonPointers[$x], '#'));
|
||||
$errorDescription = $this->addPlaceHolderValue($errorDescription, '{$response.body' . $jsonPointers[$x] . '}', $placeHolderValue);
|
||||
}
|
||||
return $errorDescription;
|
||||
}
|
||||
private function getJsonPointersFromTemplate(string $template) : array
|
||||
{
|
||||
$pointerPattern = '/#[\\w\\/]*/i';
|
||||
\preg_match_all($pointerPattern, $template, $matches);
|
||||
return $matches;
|
||||
}
|
||||
private function addPlaceHolderValue(string $template, string $placeHolder, $value, bool $searchCaseInsensitive = \false) : string
|
||||
{
|
||||
if (!\is_string($value)) {
|
||||
$value = \var_export($value, \true);
|
||||
}
|
||||
if ($searchCaseInsensitive) {
|
||||
return \str_ireplace($placeHolder, $value, $template);
|
||||
}
|
||||
return \str_replace($placeHolder, $value, $template);
|
||||
}
|
||||
/**
|
||||
* @param $jsonPointer ?Pointer
|
||||
* @param $pointer string
|
||||
* @return mixed Json pointer value from the JSON provided.
|
||||
*/
|
||||
private function getJsonPointerValue(?Pointer $jsonPointer, string $pointer)
|
||||
{
|
||||
if ($jsonPointer == null || \trim($pointer) === '') {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
$pointerValue = $jsonPointer->get($pointer);
|
||||
if (\is_object($pointerValue)) {
|
||||
return CoreHelper::serialize($pointerValue);
|
||||
}
|
||||
return $pointerValue;
|
||||
} catch (\Exception $ex) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
private function initializeJsonPointer(ResponseInterface $response) : ?Pointer
|
||||
{
|
||||
try {
|
||||
return new Pointer($response->getRawBody());
|
||||
} catch (\Exception $ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Response\Types;
|
||||
|
||||
use WPForms\Vendor\Core\Response\Context;
|
||||
class ResponseMultiType
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $typeGroup;
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $deserializers = [];
|
||||
/**
|
||||
* Sets type group to the one provided.
|
||||
*/
|
||||
public function setTypeGroup(string $typeGroup) : void
|
||||
{
|
||||
$this->typeGroup = $typeGroup;
|
||||
}
|
||||
/**
|
||||
* Sets deserializers array to the one provided.
|
||||
*/
|
||||
public function setDeserializers(array $deserializers) : void
|
||||
{
|
||||
$this->deserializers = $deserializers;
|
||||
}
|
||||
/**
|
||||
* Returns ResponseMultiType from the body of response within the context provided.
|
||||
*/
|
||||
public function getFrom(Context $context)
|
||||
{
|
||||
if (\is_null($this->typeGroup) || $context->isBodyMissing()) {
|
||||
return null;
|
||||
}
|
||||
$responseBody = $context->getResponse()->getBody();
|
||||
return $context->getJsonHelper()->mapTypes($responseBody, $this->typeGroup, $this->deserializers);
|
||||
}
|
||||
}
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Response\Types;
|
||||
|
||||
use Closure;
|
||||
use WPForms\Vendor\Core\Response\Context;
|
||||
class ResponseType
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $responseClass;
|
||||
/**
|
||||
* @var callable|null
|
||||
*/
|
||||
private $xmlDeserializer;
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $dimensions;
|
||||
/**
|
||||
* Sets response class to the one provided.
|
||||
*/
|
||||
public function setResponseClass(string $responseClass) : void
|
||||
{
|
||||
$this->responseClass = $responseClass;
|
||||
}
|
||||
/**
|
||||
* Sets xml deserializer to the one provided.
|
||||
*/
|
||||
public function setXmlDeserializer(callable $xmlDeserializer) : void
|
||||
{
|
||||
$this->xmlDeserializer = $xmlDeserializer;
|
||||
}
|
||||
/**
|
||||
* Sets dimensions of the object.
|
||||
*/
|
||||
public function setDimensions(int $dimensions) : void
|
||||
{
|
||||
$this->dimensions = $dimensions;
|
||||
}
|
||||
/**
|
||||
* Returns ResponseClass from the context provided.
|
||||
*/
|
||||
public function getFrom(Context $context)
|
||||
{
|
||||
if (\is_null($this->responseClass) || $context->isBodyMissing()) {
|
||||
return null;
|
||||
}
|
||||
if (isset($this->xmlDeserializer)) {
|
||||
return Closure::fromCallable($this->xmlDeserializer)($context->getResponse()->getRawBody(), $this->responseClass);
|
||||
}
|
||||
return $context->getJsonHelper()->mapClass($context->getResponse()->getBody(), $this->responseClass, $this->dimensions);
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\TestCase\BodyMatchers;
|
||||
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
class BodyComparator
|
||||
{
|
||||
private $allowExtra;
|
||||
private $isOrdered;
|
||||
private $checkValues;
|
||||
private $nativeMatching;
|
||||
/**
|
||||
* @param bool $allowExtra Are extra elements allowed in right array?
|
||||
* @param bool $isOrdered Should elements in right array be compared in order to the left array?
|
||||
* @param bool $checkValues Check primitive values for equality?
|
||||
* @param bool $nativeMatching Should check arrays natively? i.e. allowExtra can be applied
|
||||
* on either expected list or actual list
|
||||
*/
|
||||
public function __construct(bool $allowExtra = \true, bool $isOrdered = \false, bool $checkValues = \true, bool $nativeMatching = \false)
|
||||
{
|
||||
$this->allowExtra = $allowExtra;
|
||||
$this->isOrdered = $isOrdered;
|
||||
$this->checkValues = $checkValues;
|
||||
$this->nativeMatching = $nativeMatching;
|
||||
}
|
||||
/**
|
||||
* Recursively check whether the expected value is a proper subset of the right value
|
||||
*
|
||||
* @param mixed $expected Expected value
|
||||
* @param mixed $actual Actual value
|
||||
*
|
||||
* @return bool True if Expected is a subset of Actual
|
||||
*/
|
||||
public function compare($expected, $actual) : bool
|
||||
{
|
||||
$bothNull = $this->checkForNull($expected, $actual);
|
||||
if (isset($bothNull)) {
|
||||
return !$this->checkValues || $bothNull;
|
||||
}
|
||||
$expected = $this->convertObjectToArray($expected);
|
||||
$actual = $this->convertObjectToArray($actual);
|
||||
$bothEqualPrimitive = $this->checkForPrimitive($expected, $actual);
|
||||
if (isset($bothEqualPrimitive)) {
|
||||
return !$this->checkValues || $bothEqualPrimitive;
|
||||
}
|
||||
// Return false if size different and checking was strict
|
||||
if (!$this->allowExtra && \count($expected) != \count($actual)) {
|
||||
return \false;
|
||||
}
|
||||
if (!CoreHelper::isAssociative($expected)) {
|
||||
// If expected array is indexed, actual array should also be indexed
|
||||
if (CoreHelper::isAssociative($actual)) {
|
||||
return !$this->checkValues;
|
||||
}
|
||||
if ($this->nativeMatching && $this->allowExtra && \count($expected) > \count($actual)) {
|
||||
// Special IndexedArray case:
|
||||
// replacing expected with actual, as expected array has more
|
||||
// elements and can not be proper subset of actual array
|
||||
$tempLeft = $expected;
|
||||
$expected = $actual;
|
||||
$actual = $tempLeft;
|
||||
}
|
||||
return !$this->checkValues || $this->isListProperSubsetOf($expected, $actual);
|
||||
}
|
||||
// If expected value is tree, actual value should also be tree
|
||||
if (!CoreHelper::isAssociative($actual)) {
|
||||
return !$this->checkValues;
|
||||
}
|
||||
$actualKeyNumber = 0;
|
||||
$success = \true;
|
||||
\array_walk($expected, function ($expectedInner, $key) use($actual, &$actualKeyNumber, &$success) : void {
|
||||
if (!$success) {
|
||||
return;
|
||||
}
|
||||
// Check if key exists
|
||||
if (!\array_key_exists($key, $actual)) {
|
||||
$success = \false;
|
||||
return;
|
||||
}
|
||||
if ($this->isOrdered) {
|
||||
$actualKeys = \array_keys($actual);
|
||||
// When $isOrdered, check if key exists at some next position
|
||||
if (!\in_array($key, \array_slice($actualKeys, $actualKeyNumber), \true)) {
|
||||
$success = \false;
|
||||
return;
|
||||
}
|
||||
$actualKeyNumber = \array_search($key, $actualKeys, \true);
|
||||
}
|
||||
$actualInner = $actual[$key];
|
||||
$actualKeyNumber += 1;
|
||||
if (!$this->compare($expectedInner, $actualInner)) {
|
||||
$success = \false;
|
||||
}
|
||||
});
|
||||
return $success;
|
||||
}
|
||||
/**
|
||||
* Return True, if both are null, False if anyone is null, Null otherwise
|
||||
*/
|
||||
private function checkForNull($left, $right) : ?bool
|
||||
{
|
||||
if (\is_null($left)) {
|
||||
if (\is_null($right)) {
|
||||
return \true;
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
if (\is_null($right)) {
|
||||
return \false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Return True, if both are equal primitive, False if anyone is primitive, Null otherwise
|
||||
*/
|
||||
private function checkForPrimitive($left, $right) : ?bool
|
||||
{
|
||||
if (!\is_array($left)) {
|
||||
if (!\is_array($right)) {
|
||||
return $left === $right;
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
if (!\is_array($right)) {
|
||||
return \false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Check whether the list is a subset of another list.
|
||||
*
|
||||
* @param array $leftList Expected left list
|
||||
* @param array $rightList Right List to check
|
||||
*
|
||||
* @return bool True if $leftList is a subset of $rightList
|
||||
*/
|
||||
private function isListProperSubsetOf(array $leftList, array $rightList) : bool
|
||||
{
|
||||
if ($this->isOrdered) {
|
||||
if ($this->allowExtra) {
|
||||
return $leftList === \array_slice($rightList, 0, \count($leftList));
|
||||
}
|
||||
return $leftList === $rightList;
|
||||
}
|
||||
return $leftList == $this->intersectArrays($leftList, $rightList);
|
||||
}
|
||||
/**
|
||||
* Computes the intersection of arrays, even for arrays of arrays
|
||||
*
|
||||
* @param array $leftList The array with main values to check
|
||||
* @param array $rightList An array to compare values against
|
||||
*
|
||||
* @return array An array containing all the values in the leftList
|
||||
* which are also present in the rightList
|
||||
*/
|
||||
private function intersectArrays(array $leftList, array $rightList) : array
|
||||
{
|
||||
$commonList = [];
|
||||
foreach ($leftList as $leftVal) {
|
||||
foreach ($rightList as $rightVal) {
|
||||
if ($this->compare($leftVal, $rightVal)) {
|
||||
$commonList[] = $leftVal;
|
||||
\array_splice($rightList, \array_search($rightVal, $rightList, \true), 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $commonList;
|
||||
}
|
||||
/**
|
||||
* If passed instance is an object, cast it as an array
|
||||
*/
|
||||
private function convertObjectToArray($value)
|
||||
{
|
||||
if (\is_object($value)) {
|
||||
return \array_map([$this, 'convertObjectToArray'], (array) $value);
|
||||
}
|
||||
if (\is_array($value)) {
|
||||
return \array_map([$this, 'convertObjectToArray'], $value);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\TestCase\BodyMatchers;
|
||||
|
||||
use WPForms\Vendor\PHPUnit\Framework\TestCase;
|
||||
class BodyMatcher
|
||||
{
|
||||
protected $expectedBody;
|
||||
protected $bodyComparator;
|
||||
protected $defaultMessage = '';
|
||||
/**
|
||||
* @var TestCase
|
||||
*/
|
||||
public $testCase;
|
||||
public $result;
|
||||
public $shouldAssert = \true;
|
||||
/**
|
||||
* Initializes a new BodyMatcher with the parameters provided.
|
||||
*/
|
||||
public function __construct(BodyComparator $bodyComparator, $expectedBody = null)
|
||||
{
|
||||
$this->bodyComparator = $bodyComparator;
|
||||
$this->expectedBody = $expectedBody;
|
||||
}
|
||||
/**
|
||||
* Returns already set default message.
|
||||
*/
|
||||
public function getDefaultMessage() : string
|
||||
{
|
||||
return $this->defaultMessage;
|
||||
}
|
||||
/**
|
||||
* Sets testCase and result to the ones provided.
|
||||
*/
|
||||
public function set(TestCase $testCase, $result)
|
||||
{
|
||||
$this->testCase = $testCase;
|
||||
$this->result = $result;
|
||||
}
|
||||
/**
|
||||
* Asserts if the testCase results to true or not.
|
||||
*/
|
||||
public function assert(string $rawBody)
|
||||
{
|
||||
if ($this->shouldAssert) {
|
||||
$this->testCase->assertNotNull($this->result, 'Result does not exist');
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\TestCase\BodyMatchers;
|
||||
|
||||
class KeysAndValuesBodyMatcher extends KeysBodyMatcher
|
||||
{
|
||||
/**
|
||||
* Initializes a new KeysAndValuesBodyMatcher object with the parameters provided.
|
||||
*/
|
||||
public static function init($expectedBody, bool $matchArrayOrder = \false, bool $matchArrayCount = \false) : KeysBodyMatcher
|
||||
{
|
||||
$matcher = new self(new BodyComparator(!$matchArrayCount, $matchArrayOrder, \true), $expectedBody);
|
||||
$matcher->defaultMessage = 'Response body does not match in keys and/or values';
|
||||
return $matcher;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\TestCase\BodyMatchers;
|
||||
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
class KeysBodyMatcher extends BodyMatcher
|
||||
{
|
||||
/**
|
||||
* Initializes a new KeysBodyMatcher object with the parameters provided.
|
||||
*/
|
||||
public static function init($expectedBody, bool $matchArrayOrder = \false, bool $matchArrayCount = \false) : self
|
||||
{
|
||||
$matcher = new self(new BodyComparator(!$matchArrayCount, $matchArrayOrder, \false), $expectedBody);
|
||||
$matcher->defaultMessage = 'Response body does not match in keys';
|
||||
return $matcher;
|
||||
}
|
||||
/**
|
||||
* Compares rawBody with expectedBody and asserts if expectedBody is a subset of rawBody or not.
|
||||
*/
|
||||
public function assert(string $rawBody)
|
||||
{
|
||||
parent::assert($rawBody);
|
||||
$this->testCase->assertTrue($this->bodyComparator->compare($this->expectedBody, CoreHelper::deserialize($rawBody)), $this->defaultMessage);
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\TestCase\BodyMatchers;
|
||||
|
||||
class NativeBodyMatcher extends BodyMatcher
|
||||
{
|
||||
/**
|
||||
* Initializes a new NativeBodyMatcher object with the parameters provided.
|
||||
*/
|
||||
public static function init($expectedBody, bool $matchArrayOrder = \false, bool $matchArrayCount = \false) : self
|
||||
{
|
||||
$matcher = new self(new BodyComparator(!$matchArrayCount, $matchArrayOrder, \true, \true), $expectedBody);
|
||||
if (\is_scalar($expectedBody)) {
|
||||
$matcher->defaultMessage = 'Response values does not match';
|
||||
return $matcher;
|
||||
}
|
||||
$type = \getType($expectedBody);
|
||||
$strategy = self::getMatchingStrategy($matchArrayOrder, $matchArrayCount);
|
||||
$matcher->defaultMessage = "Response {$type} values does not match{$strategy}";
|
||||
return $matcher;
|
||||
}
|
||||
private static function getMatchingStrategy(bool $matchArrayOrder, bool $matchArrayCount) : string
|
||||
{
|
||||
if (!$matchArrayOrder) {
|
||||
if (!$matchArrayCount) {
|
||||
return '';
|
||||
}
|
||||
return ' in size';
|
||||
}
|
||||
if (!$matchArrayCount) {
|
||||
return ' in order';
|
||||
}
|
||||
return ' in order or size';
|
||||
}
|
||||
/**
|
||||
* Asserts if rawBody matches the criteria set within NativeBodyMatcher while initialization,
|
||||
* and if expectedBody is a subset of rawBody.
|
||||
*/
|
||||
public function assert(string $rawBody)
|
||||
{
|
||||
parent::assert($rawBody);
|
||||
$this->testCase->assertTrue($this->bodyComparator->compare($this->expectedBody, $this->result), $this->defaultMessage);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\TestCase\BodyMatchers;
|
||||
|
||||
use WPForms\Vendor\Core\Types\Sdk\CoreFileWrapper;
|
||||
class RawBodyMatcher extends BodyMatcher
|
||||
{
|
||||
/**
|
||||
* Initializes a RawBodyMatcher object with the expectedBody provided.
|
||||
*/
|
||||
public static function init($expectedBody) : self
|
||||
{
|
||||
$matcher = new self(new BodyComparator(), $expectedBody);
|
||||
$matcher->defaultMessage = 'Response body does not match exactly';
|
||||
return $matcher;
|
||||
}
|
||||
/**
|
||||
* Asserts if rawBody matches expectedBody.
|
||||
*/
|
||||
public function assert(string $rawBody)
|
||||
{
|
||||
parent::assert($rawBody);
|
||||
if ($this->expectedBody instanceof CoreFileWrapper) {
|
||||
$this->expectedBody = $this->expectedBody->getFileContent();
|
||||
$this->defaultMessage = 'Binary result does not match the given file';
|
||||
}
|
||||
$this->testCase->assertEquals($this->expectedBody, $rawBody, $this->defaultMessage);
|
||||
}
|
||||
}
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\TestCase;
|
||||
|
||||
use WPForms\Vendor\Core\TestCase\BodyMatchers\BodyComparator;
|
||||
use WPForms\Vendor\Core\TestCase\BodyMatchers\BodyMatcher;
|
||||
use WPForms\Vendor\Core\Types\CallbackCatcher;
|
||||
use WPForms\Vendor\PHPUnit\Framework\TestCase;
|
||||
class CoreTestCase
|
||||
{
|
||||
private $callback;
|
||||
private $statusCodeMatcher;
|
||||
private $headersMatcher;
|
||||
private $bodyMatcher;
|
||||
/**
|
||||
* Initializes a new CoreTestCase object with the parameters provided.
|
||||
*/
|
||||
public function __construct(TestCase $testCase, CallbackCatcher $callbackCatcher, $result)
|
||||
{
|
||||
$this->callback = $callbackCatcher;
|
||||
$this->statusCodeMatcher = new StatusCodeMatcher($testCase);
|
||||
$this->headersMatcher = new HeadersMatcher($testCase);
|
||||
$this->bodyMatcher = new BodyMatcher(new BodyComparator());
|
||||
$this->bodyMatcher->shouldAssert = \false;
|
||||
$this->bodyMatcher->set($testCase, $result);
|
||||
}
|
||||
/**
|
||||
* Sets the expected status value for the test case.
|
||||
*/
|
||||
public function expectStatus(int $statusCode) : self
|
||||
{
|
||||
$this->statusCodeMatcher->setStatusCode($statusCode);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets expected status range in case expected statuses are within a certain range.
|
||||
*/
|
||||
public function expectStatusRange(int $lowerStatusCode, int $upperStatusCode) : self
|
||||
{
|
||||
$this->statusCodeMatcher->setStatusRange($lowerStatusCode, $upperStatusCode);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets headers expected from the response within a test case.
|
||||
*/
|
||||
public function expectHeaders(array $headers) : self
|
||||
{
|
||||
$this->headersMatcher->setHeaders($headers);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets allowExtra flag to true, which allows headers other than the one specified to be present
|
||||
* within the response.
|
||||
*/
|
||||
public function allowExtraHeaders() : self
|
||||
{
|
||||
$this->headersMatcher->allowExtra();
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets bodyMatcher of the object to the one provided.
|
||||
*/
|
||||
public function bodyMatcher(BodyMatcher $bodyMatcher) : self
|
||||
{
|
||||
$bodyMatcher->set($this->bodyMatcher->testCase, $this->bodyMatcher->result);
|
||||
$this->bodyMatcher = $bodyMatcher;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Calls assert on statusCodeMatcher, headersMatcher and bodyMatcher set within the object.
|
||||
*/
|
||||
public function assert()
|
||||
{
|
||||
$response = $this->callback->getResponse();
|
||||
$this->statusCodeMatcher->assert($response->getStatusCode());
|
||||
$this->headersMatcher->assert($response->getHeaders());
|
||||
$this->bodyMatcher->assert($response->getRawBody());
|
||||
}
|
||||
}
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\TestCase;
|
||||
|
||||
use WPForms\Vendor\PHPUnit\Framework\TestCase;
|
||||
class HeadersMatcher
|
||||
{
|
||||
private $headers = [];
|
||||
private $allowExtra = \false;
|
||||
private $testCase;
|
||||
public function __construct(TestCase $testCase)
|
||||
{
|
||||
$this->testCase = $testCase;
|
||||
}
|
||||
/**
|
||||
* Set an array of arrays, where inner arrays must be of length 2,
|
||||
* i.e. index0 => headerValue, index1 => checkValueBool
|
||||
*
|
||||
* @param array<string,array> $headers
|
||||
*/
|
||||
public function setHeaders(array $headers) : void
|
||||
{
|
||||
$this->headers = $headers;
|
||||
}
|
||||
/**
|
||||
* Sets allowExtra flag to true.
|
||||
*/
|
||||
public function allowExtra() : void
|
||||
{
|
||||
$this->allowExtra = \true;
|
||||
}
|
||||
/**
|
||||
* Asserts if provided headers match according to the properties set within object.
|
||||
*/
|
||||
public function assert(array $headers)
|
||||
{
|
||||
if (empty($this->headers)) {
|
||||
return;
|
||||
}
|
||||
// Http headers are case-insensitive
|
||||
$expected = \array_change_key_case($this->headers);
|
||||
$actual = \array_change_key_case($headers);
|
||||
$message = "Headers do not match";
|
||||
if (!$this->allowExtra) {
|
||||
$message = "{$message} strictly";
|
||||
$this->testCase->assertCount(\count($expected), $actual, $message);
|
||||
}
|
||||
$actualKeys = \array_keys($actual);
|
||||
\array_walk($expected, function ($valueArray, $key) use($actual, $actualKeys, $message) : void {
|
||||
$this->testCase->assertTrue(\in_array($key, $actualKeys, \true), $message);
|
||||
if (!\is_bool($valueArray[1])) {
|
||||
return;
|
||||
}
|
||||
if ($valueArray[1]) {
|
||||
$this->testCase->assertEquals($valueArray[0], $actual[$key], $message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\TestCase;
|
||||
|
||||
use WPForms\Vendor\PHPUnit\Framework\TestCase;
|
||||
class StatusCodeMatcher
|
||||
{
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $statusCode;
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $lowerStatusCode;
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $upperStatusCode;
|
||||
private $assertStatusRange = \false;
|
||||
private $testCase;
|
||||
/**
|
||||
* Creates a new StatusCodeMatcher object.
|
||||
*/
|
||||
public function __construct(TestCase $testCase)
|
||||
{
|
||||
$this->testCase = $testCase;
|
||||
}
|
||||
/**
|
||||
* Sets statusCode of the object to the value provided.
|
||||
*/
|
||||
public function setStatusCode(int $statusCode) : void
|
||||
{
|
||||
$this->statusCode = $statusCode;
|
||||
}
|
||||
/**
|
||||
* Sets an expected status code range. Used in case the test case expects a status from a range of status codes.
|
||||
*/
|
||||
public function setStatusRange(int $lowerStatusCode, int $upperStatusCode) : void
|
||||
{
|
||||
$this->assertStatusRange = \true;
|
||||
$this->lowerStatusCode = $lowerStatusCode;
|
||||
$this->upperStatusCode = $upperStatusCode;
|
||||
}
|
||||
/**
|
||||
* Assert required assertions according to the properties set within the object.
|
||||
*/
|
||||
public function assert(int $statusCode)
|
||||
{
|
||||
if (isset($this->statusCode)) {
|
||||
$this->testCase->assertEquals($this->statusCode, $statusCode, "Status is not {$this->statusCode}");
|
||||
return;
|
||||
}
|
||||
if (!$this->assertStatusRange) {
|
||||
return;
|
||||
}
|
||||
$message = "Status is not between {$this->lowerStatusCode} and {$this->upperStatusCode}";
|
||||
$this->testCase->assertGreaterThanOrEqual($this->lowerStatusCode, $statusCode, $message);
|
||||
$this->testCase->assertLessThanOrEqual($this->upperStatusCode, $statusCode, $message);
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\TestCase;
|
||||
|
||||
use WPForms\Vendor\apimatic\jsonmapper\JsonMapperException;
|
||||
use Closure;
|
||||
use WPForms\Vendor\Core\Client;
|
||||
use WPForms\Vendor\Core\Types\Sdk\CoreFileWrapper;
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
use Exception;
|
||||
class TestParam
|
||||
{
|
||||
/**
|
||||
* Returns a typeGroup type TestParam.
|
||||
*
|
||||
* @param string $json Json value to be mapped by the typeGroup
|
||||
* @param string $typeGroup Group of types in string format i.e. oneOf(...), anyOf(...)
|
||||
* @param array $deserializers Methods required for the de-serialization of specific types in
|
||||
* in the provided typeGroup, should be an array in the format:
|
||||
* ['path/to/method returnType', ...]. Default: []
|
||||
* @return mixed Returns the mapped value from json
|
||||
* @throws JsonMapperException
|
||||
*/
|
||||
public static function typeGroup(string $json, string $typeGroup, array $deserializers = [])
|
||||
{
|
||||
return Client::getJsonHelper()->mapTypes(CoreHelper::deserialize($json, \false), $typeGroup, $deserializers);
|
||||
}
|
||||
/**
|
||||
* Returns an object type TestParam.
|
||||
*
|
||||
* @param string $json Json value to be mapped by the class
|
||||
* @param string|null $classname Name of the class inclusive of its namespace,
|
||||
* Default: object
|
||||
* @param int $dimension Greater than 0 if trying to map an array of
|
||||
* class with some dimensions, Default: 0
|
||||
* @return mixed Returns the mapped value from json
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function object(string $json, ?string $classname = null, int $dimension = 0)
|
||||
{
|
||||
if (\is_null($classname)) {
|
||||
return CoreHelper::deserialize($json);
|
||||
}
|
||||
return Client::getJsonHelper()->mapClass(CoreHelper::deserialize($json, \false), $classname, $dimension);
|
||||
}
|
||||
/**
|
||||
* Returns a custom TestParam.
|
||||
*
|
||||
* @param string $json Json value to be deserialized using custom callback
|
||||
* @param callable $callback Callback use to deserialize the given value
|
||||
* @return mixed Returns the result from the callback
|
||||
*/
|
||||
public static function custom(string $json, callable $callback)
|
||||
{
|
||||
return Closure::fromCallable($callback)(CoreHelper::deserialize($json, \false));
|
||||
}
|
||||
/**
|
||||
* Returns a file type TestParam.
|
||||
*
|
||||
* @param string $url URL of the file to download
|
||||
*/
|
||||
public static function file(string $url)
|
||||
{
|
||||
$realPath = CoreFileWrapper::getDownloadedRealFilePath($url);
|
||||
return self::localFile($realPath);
|
||||
}
|
||||
/**
|
||||
* Returns a localFile TestParam.
|
||||
*
|
||||
* @param string $realPath Local path to the file
|
||||
*/
|
||||
public static function localFile(string $realPath)
|
||||
{
|
||||
return Client::getConverter()->createFileWrapper($realPath, null, '');
|
||||
}
|
||||
}
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Types;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\ContextInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Response\ResponseInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Sdk\ConverterInterface;
|
||||
use WPForms\Vendor\Core\Types\Sdk\CoreCallback;
|
||||
class CallbackCatcher extends CoreCallback
|
||||
{
|
||||
/**
|
||||
* @var RequestInterface
|
||||
*/
|
||||
private $request;
|
||||
/**
|
||||
* @var ResponseInterface
|
||||
*/
|
||||
private $response;
|
||||
/**
|
||||
* Create instance
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$instance = $this;
|
||||
parent::__construct(null, function (ContextInterface $httpContext) use($instance) : void {
|
||||
$instance->request = $httpContext->getRequest();
|
||||
$instance->response = $httpContext->getResponse();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get the Request object associated with this API call
|
||||
*/
|
||||
public function getRequest() : RequestInterface
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
/**
|
||||
* Get the Response object associated with this API call
|
||||
*/
|
||||
public function getResponse() : ResponseInterface
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
public function callOnBeforeWithConversion(RequestInterface $request, ConverterInterface $converter)
|
||||
{
|
||||
parent::callOnBeforeRequest($request);
|
||||
}
|
||||
public function callOnAfterWithConversion(ContextInterface $context, ConverterInterface $converter)
|
||||
{
|
||||
parent::callOnAfterRequest($context);
|
||||
}
|
||||
}
|
||||
Executable
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Types\Sdk;
|
||||
|
||||
abstract class CoreApiResponse
|
||||
{
|
||||
protected $request;
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $statusCode;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $reasonPhrase;
|
||||
/**
|
||||
* @var array|null
|
||||
*/
|
||||
private $headers;
|
||||
private $result;
|
||||
private $body;
|
||||
public function __construct($request, ?int $statusCode, ?string $reasonPhrase, ?array $headers, $result, $body)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->statusCode = $statusCode;
|
||||
$this->reasonPhrase = $reasonPhrase;
|
||||
$this->headers = $headers;
|
||||
$this->result = $result;
|
||||
$this->body = $body;
|
||||
}
|
||||
/**
|
||||
* Returns the original request that resulted in this response.
|
||||
*/
|
||||
public abstract function getRequest();
|
||||
/**
|
||||
* Returns the response status code.
|
||||
*/
|
||||
public function getStatusCode() : ?int
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
/**
|
||||
* Returns the HTTP reason phrase from the response.
|
||||
*/
|
||||
public function getReasonPhrase() : ?string
|
||||
{
|
||||
return $this->reasonPhrase;
|
||||
}
|
||||
/**
|
||||
* Returns the response headers.
|
||||
*/
|
||||
public function getHeaders() : ?array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
/**
|
||||
* Returns the response data.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
/**
|
||||
* Returns the original body from the response.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBody()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
/**
|
||||
* Is response OK?
|
||||
*/
|
||||
public function isSuccess() : bool
|
||||
{
|
||||
if ($this->statusCode == null) {
|
||||
return \false;
|
||||
}
|
||||
if ($this->statusCode < 200) {
|
||||
return \false;
|
||||
}
|
||||
if ($this->statusCode > 299) {
|
||||
return \false;
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* Is response missing or not OK?
|
||||
*/
|
||||
public function isError() : bool
|
||||
{
|
||||
return !$this->isSuccess();
|
||||
}
|
||||
}
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Types\Sdk;
|
||||
|
||||
use Closure;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\ContextInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Sdk\ConverterInterface;
|
||||
class CoreCallback
|
||||
{
|
||||
/**
|
||||
* Callable for on-before event of API calls
|
||||
*/
|
||||
private $onBeforeRequest;
|
||||
/**
|
||||
* Callable for on-after event of API calls
|
||||
*/
|
||||
private $onAfterRequest;
|
||||
/**
|
||||
* Create a new HttpCallBack instance
|
||||
*
|
||||
* @param callable|null $onBeforeRequest Called before an API call
|
||||
* @param callable|null $onAfterRequest Called after an API call
|
||||
*/
|
||||
public function __construct(?callable $onBeforeRequest = null, ?callable $onAfterRequest = null)
|
||||
{
|
||||
$this->onBeforeRequest = $onBeforeRequest;
|
||||
$this->onAfterRequest = $onAfterRequest;
|
||||
}
|
||||
/**
|
||||
* Set On-before API call event callable
|
||||
*
|
||||
* @param callable $func On-before event callable
|
||||
*/
|
||||
public function setOnBeforeRequest(callable $func) : void
|
||||
{
|
||||
$this->onBeforeRequest = $func;
|
||||
}
|
||||
/**
|
||||
* Get On-before API call event callable
|
||||
*
|
||||
* @return callable|null Callable
|
||||
*/
|
||||
public function getOnBeforeRequest() : ?callable
|
||||
{
|
||||
return $this->onBeforeRequest;
|
||||
}
|
||||
/**
|
||||
* Set On-after API call event callable
|
||||
*
|
||||
* @param callable $func On-after event callable
|
||||
*/
|
||||
public function setOnAfterRequest(callable $func) : void
|
||||
{
|
||||
$this->onAfterRequest = $func;
|
||||
}
|
||||
/**
|
||||
* Get On-After API call event callable
|
||||
*
|
||||
* @return callable|null On-after event callable
|
||||
*/
|
||||
public function getOnAfterRequest() : ?callable
|
||||
{
|
||||
return $this->onAfterRequest;
|
||||
}
|
||||
/**
|
||||
* Call on-before event callable
|
||||
*
|
||||
* @param mixed $request Request for this call
|
||||
*/
|
||||
public function callOnBeforeRequest($request) : void
|
||||
{
|
||||
if ($this->onBeforeRequest != null) {
|
||||
Closure::fromCallable($this->onBeforeRequest)($request);
|
||||
}
|
||||
}
|
||||
public function callOnBeforeWithConversion(RequestInterface $request, ConverterInterface $converter)
|
||||
{
|
||||
$this->callOnBeforeRequest($converter->createHttpRequest($request));
|
||||
}
|
||||
/**
|
||||
* Call on-after event callable
|
||||
*
|
||||
* @param mixed $context HttpContext for this call
|
||||
*/
|
||||
public function callOnAfterRequest($context) : void
|
||||
{
|
||||
if ($this->onAfterRequest != null) {
|
||||
Closure::fromCallable($this->onAfterRequest)($context);
|
||||
}
|
||||
}
|
||||
public function callOnAfterWithConversion(ContextInterface $context, ConverterInterface $converter)
|
||||
{
|
||||
$this->callOnAfterRequest($converter->createHttpContext($context));
|
||||
}
|
||||
}
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Types\Sdk;
|
||||
|
||||
abstract class CoreContext
|
||||
{
|
||||
protected $request;
|
||||
protected $response;
|
||||
/**
|
||||
* Create an instance of HttpContext for a Http Call
|
||||
*
|
||||
* @param mixed $request Request first sent on http call
|
||||
* @param mixed $response Response received from http call
|
||||
*/
|
||||
public function __construct($request, $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
/**
|
||||
* Returns the HTTP request
|
||||
*/
|
||||
public abstract function getRequest();
|
||||
/**
|
||||
* Returns the HTTP response
|
||||
*/
|
||||
public abstract function getResponse();
|
||||
}
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Types\Sdk;
|
||||
|
||||
use WPForms\Vendor\Core\Utils\CoreHelper;
|
||||
use CURLFile;
|
||||
use SplFileObject;
|
||||
class CoreFileWrapper
|
||||
{
|
||||
/**
|
||||
* Downloads and gets a local path to a file URL.
|
||||
* Subsequent calls to the same URL will get the cached file.
|
||||
*
|
||||
* @param string $url URL of the file to download
|
||||
* @return string Local path to the file
|
||||
*/
|
||||
public static function getDownloadedRealFilePath(string $url) : string
|
||||
{
|
||||
$realFilePath = \sys_get_temp_dir() . \DIRECTORY_SEPARATOR . "sdktests" . \sha1($url) . "tmp";
|
||||
if (!\file_exists($realFilePath)) {
|
||||
\file_put_contents($realFilePath, \fopen($url, 'r'));
|
||||
}
|
||||
return $realFilePath;
|
||||
}
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $realFilePath;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $mimeType;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $filename;
|
||||
public function __construct(string $realFilePath, ?string $mimeType, ?string $filename)
|
||||
{
|
||||
$this->realFilePath = $realFilePath;
|
||||
$this->mimeType = $mimeType;
|
||||
$this->filename = $filename;
|
||||
}
|
||||
/**
|
||||
* Get mime-type to be sent with the file
|
||||
*/
|
||||
public function getMimeType() : ?string
|
||||
{
|
||||
return $this->mimeType;
|
||||
}
|
||||
/**
|
||||
* Get name of the file to be used in the upload data
|
||||
*/
|
||||
public function getFilename() : ?string
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
/**
|
||||
* Converts the CoreFileWrapper object to a human-readable string representation.
|
||||
*
|
||||
* @return string The string representation of the CoreFileWrapper object.
|
||||
*/
|
||||
public function __toString() : string
|
||||
{
|
||||
return CoreHelper::stringify('CoreFileWrapper', ['realFilePath' => $this->realFilePath, 'mimeType' => $this->mimeType, 'filename' => $this->filename]);
|
||||
}
|
||||
/**
|
||||
* Internal method: Do not use directly!
|
||||
*/
|
||||
public function createCurlFileInstance(string $defaultMimeType = 'application/octet-stream') : CURLFile
|
||||
{
|
||||
$mimeType = $this->mimeType ?? $defaultMimeType;
|
||||
return new CURLFile($this->realFilePath, $mimeType, $this->filename);
|
||||
}
|
||||
/**
|
||||
* Internal method: Do not use directly!
|
||||
*/
|
||||
public function getFileContent() : ?string
|
||||
{
|
||||
$thisFile = new SplFileObject($this->realFilePath);
|
||||
$content = $thisFile->fread($thisFile->getSize());
|
||||
return CoreHelper::convertToNullableString($content);
|
||||
}
|
||||
}
|
||||
Executable
+127
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Types\Sdk;
|
||||
|
||||
class CoreRequest
|
||||
{
|
||||
/**
|
||||
* HTTP method as defined in RequestMethod interface
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $httpMethod;
|
||||
/**
|
||||
* Headers
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $headers;
|
||||
/**
|
||||
* Query url
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $queryUrl;
|
||||
/**
|
||||
* Input parameters
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $parameters;
|
||||
/**
|
||||
* Create a new HttpRequest
|
||||
*
|
||||
* @param string $httpMethod HTTP method
|
||||
* @param array $headers Map of headers
|
||||
* @param string $queryUrl Query url
|
||||
* @param array $parameters Map of parameters sent
|
||||
*/
|
||||
public function __construct(string $httpMethod, array $headers = [], string $queryUrl = "", array $parameters = [])
|
||||
{
|
||||
$this->httpMethod = $httpMethod;
|
||||
$this->headers = $headers;
|
||||
$this->queryUrl = $queryUrl;
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
/**
|
||||
* Get HTTP method
|
||||
*/
|
||||
public function getHttpMethod() : string
|
||||
{
|
||||
return $this->httpMethod;
|
||||
}
|
||||
/**
|
||||
* Set HTTP method
|
||||
*
|
||||
* @param string $httpMethod HTTP Method as defined in HttpMethod class
|
||||
*/
|
||||
public function setHttpMethod(string $httpMethod) : void
|
||||
{
|
||||
$this->httpMethod = $httpMethod;
|
||||
}
|
||||
/**
|
||||
* Get headers
|
||||
*
|
||||
* @return array Map of headers
|
||||
*/
|
||||
public function getHeaders() : array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
/**
|
||||
* Set headers
|
||||
*
|
||||
* @param array $headers Headers as map
|
||||
*/
|
||||
public function setHeaders(array $headers) : void
|
||||
{
|
||||
$this->headers = $headers;
|
||||
}
|
||||
/**
|
||||
* Add or replace a single header
|
||||
*
|
||||
* @param string $key key for the header
|
||||
* @param string $value value of the header
|
||||
*/
|
||||
public function addHeader(string $key, string $value) : void
|
||||
{
|
||||
$this->headers[$key] = $value;
|
||||
}
|
||||
/**
|
||||
* Get query url
|
||||
*
|
||||
* @return string Query url
|
||||
*/
|
||||
public function getQueryUrl() : string
|
||||
{
|
||||
return $this->queryUrl;
|
||||
}
|
||||
/**
|
||||
* Set query url
|
||||
*
|
||||
* @param string $queryUrl Query url
|
||||
*/
|
||||
public function setQueryUrl(string $queryUrl) : void
|
||||
{
|
||||
$this->queryUrl = $queryUrl;
|
||||
}
|
||||
/**
|
||||
* Get parameters
|
||||
*
|
||||
* @return array Map of input parameters
|
||||
*/
|
||||
public function getParameters() : array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
/**
|
||||
* Set parameters
|
||||
*
|
||||
* @param array $parameters Map of input parameters
|
||||
*/
|
||||
public function setParameters(array $parameters) : void
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
}
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Types\Sdk;
|
||||
|
||||
class CoreResponse
|
||||
{
|
||||
/**
|
||||
* Status code of response
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $statusCode;
|
||||
/**
|
||||
* Headers received
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $headers;
|
||||
/**
|
||||
* Raw body of the response
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $rawBody;
|
||||
/**
|
||||
* Create a new instance of a HttpResponse
|
||||
*
|
||||
* @param int $statusCode Response code
|
||||
* @param array $headers Map of headers
|
||||
* @param string $rawBody Raw response body
|
||||
*/
|
||||
public function __construct(int $statusCode, array $headers, string $rawBody)
|
||||
{
|
||||
$this->statusCode = $statusCode;
|
||||
$this->headers = $headers;
|
||||
$this->rawBody = $rawBody;
|
||||
}
|
||||
/**
|
||||
* Get status code
|
||||
*
|
||||
* @return int Status code
|
||||
*/
|
||||
public function getStatusCode() : int
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
/**
|
||||
* Get headers
|
||||
*
|
||||
* @return array Map of headers
|
||||
*/
|
||||
public function getHeaders() : array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
/**
|
||||
* Get raw response body
|
||||
*
|
||||
* @return string Raw body
|
||||
*/
|
||||
public function getRawBody() : string
|
||||
{
|
||||
return $this->rawBody;
|
||||
}
|
||||
}
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Utils;
|
||||
|
||||
use WPForms\Vendor\Core\Types\Sdk\CoreFileWrapper;
|
||||
use DateTime;
|
||||
use InvalidArgumentException;
|
||||
use JsonSerializable;
|
||||
use stdClass;
|
||||
class CoreHelper
|
||||
{
|
||||
/**
|
||||
* Serialize any given mixed value.
|
||||
*
|
||||
* @param mixed $value Any value to be serialized
|
||||
*
|
||||
* @return string|null serialized value
|
||||
*/
|
||||
public static function serialize($value) : ?string
|
||||
{
|
||||
if ($value instanceof CoreFileWrapper) {
|
||||
return $value->getFileContent();
|
||||
}
|
||||
if (\is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
if (\is_null($value)) {
|
||||
return null;
|
||||
}
|
||||
return \json_encode($value);
|
||||
}
|
||||
/**
|
||||
* Deserialize a Json string
|
||||
*
|
||||
* @param string|null $json A valid Json string
|
||||
*
|
||||
* @return mixed Decoded Json
|
||||
*/
|
||||
public static function deserialize(?string $json, bool $associative = \true)
|
||||
{
|
||||
return \json_decode($json, $associative) ?? $json;
|
||||
}
|
||||
/**
|
||||
* Validates and processes the given Url to ensure safe usage with cURL.
|
||||
* @param string $url The given Url to process
|
||||
* @return string Pre-processed Url as string
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function validateUrl(string $url) : string
|
||||
{
|
||||
// ensure that the urls are absolute
|
||||
$matchCount = \preg_match("#^(https?://[^/]+)#", $url, $matches);
|
||||
if ($matchCount == 0) {
|
||||
throw new InvalidArgumentException('Invalid Url format.');
|
||||
}
|
||||
// separate out protocol and path
|
||||
$protocol = $matches[1];
|
||||
$path = \substr($url, \strlen($protocol));
|
||||
// replace multiple consecutive forward slashes by single ones
|
||||
$path = \preg_replace("#//+#", "/", $path);
|
||||
// remove forward slash from end
|
||||
$path = \rtrim($path, '/');
|
||||
return $protocol . $path;
|
||||
}
|
||||
/**
|
||||
* Check if an array isAssociative (has string keys)
|
||||
*
|
||||
* @param array $array Any value to be tested for associative array
|
||||
* @return boolean True if the array is Associative, false if it is Indexed
|
||||
*/
|
||||
public static function isAssociative(array $array) : bool
|
||||
{
|
||||
foreach ($array as $key => $value) {
|
||||
if (\is_string($key)) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* Check if provided value is null or empty.
|
||||
*
|
||||
* @param $value mixed Value to be checked.
|
||||
* @return bool True if given value is empty of null.
|
||||
*/
|
||||
public static function isNullOrEmpty($value) : bool
|
||||
{
|
||||
if (\is_string($value) && $value == '0') {
|
||||
return \false;
|
||||
}
|
||||
return empty($value);
|
||||
}
|
||||
/**
|
||||
* Check if all the given value or values are present in the provided list.
|
||||
*
|
||||
* @param mixed $value Value to be checked, could be scalar, array, 2D array, etc.
|
||||
* @param array $listOfValues List to be searched for values
|
||||
* @return bool Whether given value is present in the provided list
|
||||
*/
|
||||
public static function checkValueOrValuesInList($value, array $listOfValues) : bool
|
||||
{
|
||||
if (\is_null($value)) {
|
||||
return \true;
|
||||
}
|
||||
if (!\is_array($value)) {
|
||||
return \in_array($value, $listOfValues, \true);
|
||||
}
|
||||
foreach ($value as $v) {
|
||||
if (!self::checkValueOrValuesInList($v, $listOfValues)) {
|
||||
return \false;
|
||||
}
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* Clone the given value
|
||||
*
|
||||
* @param mixed $value Value to be cloned.
|
||||
* @return mixed Cloned value
|
||||
*/
|
||||
public static function clone($value)
|
||||
{
|
||||
if (\is_array($value)) {
|
||||
return \array_map([self::class, 'clone'], $value);
|
||||
}
|
||||
if (\is_object($value)) {
|
||||
return clone $value;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
/**
|
||||
* Converts provided value to ?string type.
|
||||
*
|
||||
* @param $value false|string
|
||||
*/
|
||||
public static function convertToNullableString($value) : ?string
|
||||
{
|
||||
if ($value === \false) {
|
||||
return null;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
/**
|
||||
* Return basic OS info.
|
||||
*/
|
||||
public static function getOsInfo(string $osFamily = \PHP_OS_FAMILY, string $functionName = 'php_uname') : string
|
||||
{
|
||||
if ($osFamily === 'Unknown' || empty($osFamily)) {
|
||||
return '';
|
||||
}
|
||||
if (!\function_exists($functionName)) {
|
||||
return $osFamily;
|
||||
}
|
||||
return $osFamily . '-' . \call_user_func($functionName, 'r');
|
||||
}
|
||||
/**
|
||||
* Return base64 encoded string for given username and password, prepended with Basic substring.
|
||||
*/
|
||||
public static function getBasicAuthEncodedString(string $username, string $password) : string
|
||||
{
|
||||
if ($username == '' || $password == '') {
|
||||
return '';
|
||||
}
|
||||
return 'Basic ' . \base64_encode("{$username}:{$password}");
|
||||
}
|
||||
/**
|
||||
* Return the accessToken prepended with Bearer substring.
|
||||
*/
|
||||
public static function getBearerAuthString(string $accessToken) : string
|
||||
{
|
||||
if ($accessToken == '') {
|
||||
return '';
|
||||
}
|
||||
return 'Bearer ' . $accessToken;
|
||||
}
|
||||
/**
|
||||
* Prepare a mixed typed value or array into a readable form.
|
||||
*
|
||||
* @param mixed $value Any mixed typed value.
|
||||
* @param bool $exportBoolAsString Should export boolean values as string? Default: true
|
||||
* @param bool $castAsString Should cast the output into string? Default: false
|
||||
*
|
||||
* @return mixed A valid readable instance to be sent in form/query.
|
||||
*/
|
||||
public static function prepareValue($value, bool $exportBoolAsString = \true, bool $castAsString = \false)
|
||||
{
|
||||
if (\is_null($value)) {
|
||||
return null;
|
||||
}
|
||||
if (\is_bool($value)) {
|
||||
return $exportBoolAsString ? \var_export($value, \true) : $value;
|
||||
}
|
||||
return $castAsString ? (string) $value : self::prepareCollectedValues($value, $exportBoolAsString);
|
||||
}
|
||||
/**
|
||||
* Prepare a mixed typed value or array into a readable form.
|
||||
*
|
||||
* @param mixed $value Any mixed typed value.
|
||||
* @param bool $exportBoolAsString Should export boolean values as string? Default: true
|
||||
*
|
||||
* @return mixed A valid readable instance to be sent in form/query.
|
||||
*/
|
||||
private static function prepareCollectedValues($value, bool $exportBoolAsString)
|
||||
{
|
||||
$selfCaller = function ($v) use($exportBoolAsString) {
|
||||
return self::prepareValue($v, $exportBoolAsString);
|
||||
};
|
||||
if (\is_array($value)) {
|
||||
// recursively calling this function to resolve all types in any array
|
||||
return \array_map($selfCaller, $value);
|
||||
}
|
||||
if ($value instanceof JsonSerializable) {
|
||||
$modelArray = $value->jsonSerialize();
|
||||
// recursively calling this function to resolve all types in any model
|
||||
return \array_map($selfCaller, $modelArray instanceof stdClass ? [] : $modelArray);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
/**
|
||||
* Converts the properties to a human-readable string representation.
|
||||
*
|
||||
* Sample output:
|
||||
*
|
||||
* $prefix [$properties:key: $properties:value, $processedProperties]
|
||||
*/
|
||||
public static function stringify(string $prefix, array $properties, string $processedProperties = '') : string
|
||||
{
|
||||
$formattedProperties = \array_map([self::class, 'stringifyProperty'], \array_keys($properties), $properties);
|
||||
if (!empty($processedProperties)) {
|
||||
$formattedProperties[] = \substr($processedProperties, \strpos($processedProperties, '[') + 1, -1);
|
||||
}
|
||||
$formattedPropertiesString = \implode(', ', \array_filter($formattedProperties));
|
||||
return \ltrim("{$prefix} [{$formattedPropertiesString}]");
|
||||
}
|
||||
/**
|
||||
* Converts the provided key value pair into a human-readable string representation.
|
||||
*/
|
||||
private static function stringifyProperty($key, $value)
|
||||
{
|
||||
if (\is_null($value)) {
|
||||
return null;
|
||||
// Skip null values
|
||||
}
|
||||
$value = self::handleNonConvertibleTypes($value);
|
||||
$value = \is_array($value) ? self::stringify('', $value) : self::prepareValue($value, \true, \true);
|
||||
if (\is_string($key)) {
|
||||
return "{$key}: {$value}";
|
||||
}
|
||||
// Skip keys representation for numeric keys (i.e. non associative arrays)
|
||||
return $value;
|
||||
}
|
||||
private static function handleNonConvertibleTypes($value)
|
||||
{
|
||||
if ($value instanceof stdClass) {
|
||||
return (array) $value;
|
||||
}
|
||||
if ($value instanceof DateTime) {
|
||||
return DateHelper::toRfc3339DateTime($value);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
+582
@@ -0,0 +1,582 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Utils;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use InvalidArgumentException;
|
||||
use stdClass;
|
||||
class DateHelper
|
||||
{
|
||||
/**
|
||||
* Match the pattern for a datetime string in simple date format
|
||||
*/
|
||||
public const SIMPLE_DATE = 'Y-m-d';
|
||||
/**
|
||||
* Match the pattern for a datetime string in Rfc1123 format
|
||||
*/
|
||||
public const RFC1123 = 'D, d M Y H:i:s T';
|
||||
/**
|
||||
* Match the pattern for a datetime string in RFC3339 format
|
||||
*/
|
||||
public const RFC3339 = 'Y-m-d\\TH:i:sP';
|
||||
/**
|
||||
* Convert a DateTime object to a string in simple date format
|
||||
*
|
||||
* @param DateTime|null $date The DateTime object to convert
|
||||
*
|
||||
* @return string|null The datetime as a string in simple date format
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function toSimpleDate(?DateTime $date) : ?string
|
||||
{
|
||||
if (\is_null($date)) {
|
||||
return null;
|
||||
}
|
||||
return $date->format(static::SIMPLE_DATE);
|
||||
}
|
||||
/**
|
||||
* Convert an array of DateTime objects to an array of strings in simple date format
|
||||
*
|
||||
* @param array|null $dates The array of DateTime objects to convert
|
||||
*
|
||||
* @return array|null The array of datetime strings in simple date format
|
||||
*/
|
||||
public static function toSimpleDateArray(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'toSimpleDate'], $dates);
|
||||
}
|
||||
/**
|
||||
* Convert a 2D array of DateTime objects to a 2D array of strings in simple date format
|
||||
*
|
||||
* @param array|null $dates The 2D array of DateTime objects to convert
|
||||
*
|
||||
* @return array|null The 2D array of datetime strings in simple date format
|
||||
*/
|
||||
public static function toSimpleDate2DArray(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'toSimpleDateArray'], $dates);
|
||||
}
|
||||
/**
|
||||
* Parse a datetime string in simple date format to a DateTime object
|
||||
*
|
||||
* @param string|null $date A datetime string in simple date format
|
||||
*
|
||||
* @return DateTime|null The parsed DateTime object
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fromSimpleDate(?string $date) : ?DateTime
|
||||
{
|
||||
if (\is_null($date)) {
|
||||
return null;
|
||||
}
|
||||
$x = DateTime::createFromFormat(static::SIMPLE_DATE, $date);
|
||||
if ($x instanceof DateTime) {
|
||||
return $x->setTime(0, 0);
|
||||
}
|
||||
throw new InvalidArgumentException('Incorrect format.');
|
||||
}
|
||||
/**
|
||||
* Parse a datetime string in simple date format to a DateTime object
|
||||
*
|
||||
* @param string|null $date A datetime string in simple date format
|
||||
*
|
||||
* @return DateTime The parsed DateTime object
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fromSimpleDateRequired(?string $date) : DateTime
|
||||
{
|
||||
$result = DateHelper::fromSimpleDate($date);
|
||||
if (isset($result)) {
|
||||
return $result;
|
||||
}
|
||||
throw new \InvalidArgumentException('Date is null, empty or not in required format.');
|
||||
}
|
||||
/**
|
||||
* Parse an array of datetime strings in simple date format to an array of DateTime objects
|
||||
*
|
||||
* @param array|null $dates An array of datetime strings in simple date format
|
||||
*
|
||||
* @return array|null An array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromSimpleDateArray(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'fromSimpleDate'], $dates);
|
||||
}
|
||||
/**
|
||||
* Parse an array of map of datetime strings in simple date format to a 2D array of DateTime objects
|
||||
*
|
||||
* @param array|null $dates An array of map of datetime strings in simple date format
|
||||
*
|
||||
* @return array|null A 2D array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromSimpleDateArrayOfMap(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'fromSimpleDateMap'], $dates);
|
||||
}
|
||||
/**
|
||||
* Parse a class of datetime strings in simple date format to an array of DateTime objects
|
||||
*
|
||||
* @param stdClass|null $dates A class of datetime strings in simple date format
|
||||
*
|
||||
* @return array|null An array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromSimpleDateMap(?stdClass $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
$array = \json_decode(\json_encode($dates), \true);
|
||||
return \array_map([self::class, 'fromSimpleDate'], $array);
|
||||
}
|
||||
/**
|
||||
* Parse a map of array of datetime strings in simple date format to a 2D array of DateTime objects
|
||||
*
|
||||
* @param stdClass|null $dates A map of array of datetime strings in simple date format
|
||||
*
|
||||
* @return array|null A 2D array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromSimpleDateMapOfArray(?stdClass $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
$array = \json_decode(\json_encode($dates), \true);
|
||||
return \array_map([self::class, 'fromSimpleDateArray'], $array);
|
||||
}
|
||||
/**
|
||||
* Convert a DateTime object to a string in Rfc1123 format
|
||||
*
|
||||
* @param DateTime|null $date The DateTime object to convert
|
||||
*
|
||||
* @return string|null The datetime as a string in Rfc1123 format
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function toRfc1123DateTime(?DateTime $date) : ?string
|
||||
{
|
||||
if (\is_null($date)) {
|
||||
return null;
|
||||
}
|
||||
return $date->setTimeZone(new DateTimeZone('GMT'))->format(static::RFC1123);
|
||||
}
|
||||
/**
|
||||
* Convert an array of DateTime objects to an array of strings in Rfc1123 format
|
||||
*
|
||||
* @param array|null $dates The array of DateTime objects to convert
|
||||
*
|
||||
* @return array|null The array of datetime strings in Rfc1123 format
|
||||
*/
|
||||
public static function toRfc1123DateTimeArray(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'toRfc1123DateTime'], $dates);
|
||||
}
|
||||
/**
|
||||
* Convert a 2D array of DateTime objects to a 2D array of strings in Rfc1123 format
|
||||
*
|
||||
* @param array|null $dates The 2D array of DateTime objects to convert
|
||||
*
|
||||
* @return array|null The 2D array of datetime strings in Rfc1123 format
|
||||
*/
|
||||
public static function toRfc1123DateTime2DArray(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'toRfc1123DateTimeArray'], $dates);
|
||||
}
|
||||
/**
|
||||
* Parse a datetime string in Rfc1123 format to a DateTime object
|
||||
*
|
||||
* @param string|null $date A datetime string in Rfc1123 format
|
||||
*
|
||||
* @return DateTime|null The parsed DateTime object
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fromRfc1123DateTime(?string $date) : ?DateTime
|
||||
{
|
||||
if (\is_null($date)) {
|
||||
return null;
|
||||
}
|
||||
$x = DateTime::createFromFormat(static::RFC1123, $date);
|
||||
if ($x instanceof DateTime) {
|
||||
return $x->setTimeZone(new DateTimeZone('GMT'));
|
||||
}
|
||||
throw new InvalidArgumentException('Incorrect format.');
|
||||
}
|
||||
/**
|
||||
* Parse a datetime string in Rfc1123 format to a DateTime object
|
||||
*
|
||||
* @param string|null $datetime A datetime string in Rfc1123 format
|
||||
*
|
||||
* @return DateTime The parsed DateTime object
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fromRfc1123DateTimeRequired(?string $datetime) : DateTime
|
||||
{
|
||||
$result = DateHelper::fromRfc1123DateTime($datetime);
|
||||
if (isset($result)) {
|
||||
return $result;
|
||||
}
|
||||
throw new \InvalidArgumentException('DateTime is null, empty or not in required format.');
|
||||
}
|
||||
/**
|
||||
* Parse an array of datetime strings in Rfc1123 format to an array of DateTime objects
|
||||
*
|
||||
* @param array|null $dates An array of datetime strings in Rfc1123 format
|
||||
*
|
||||
* @return array|null An array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromRfc1123DateTimeArray(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'fromRfc1123DateTime'], $dates);
|
||||
}
|
||||
/**
|
||||
* Parse an array of map of datetime strings in Rfc1123 format to a 2D array of DateTime objects
|
||||
*
|
||||
* @param array|null $dates An array of map of datetime strings in Rfc1123 format
|
||||
*
|
||||
* @return array|null A 2D array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromRfc1123DateTimeArrayOfMap(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'fromRfc1123DateTimeMap'], $dates);
|
||||
}
|
||||
/**
|
||||
* Parse a class of datetime strings in Rfc1123 format to an array of DateTime objects
|
||||
*
|
||||
* @param stdClass|null $dates A class of datetime strings in Rfc1123 format
|
||||
*
|
||||
* @return array|null An array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromRfc1123DateTimeMap(?stdClass $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
$array = \json_decode(\json_encode($dates), \true);
|
||||
return \array_map([self::class, 'fromRfc1123DateTime'], $array);
|
||||
}
|
||||
/**
|
||||
* Parse a map of array of datetime strings in Rfc1123 format to a 2D array of DateTime objects
|
||||
*
|
||||
* @param stdClass|null $dates A map of array of datetime strings in Rfc1123 format
|
||||
*
|
||||
* @return array|null A 2D array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromRfc1123DateTimeMapOfArray(?stdClass $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
$array = \json_decode(\json_encode($dates), \true);
|
||||
return \array_map([self::class, 'fromRfc1123DateTimeArray'], $array);
|
||||
}
|
||||
/**
|
||||
* Convert a DateTime object to a string in Rfc3339 format
|
||||
*
|
||||
* @param DateTime|null $date The DateTime object to convert
|
||||
*
|
||||
* @return string|null The datetime as a string in Rfc3339 format
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function toRfc3339DateTime(?DateTime $date) : ?string
|
||||
{
|
||||
if (\is_null($date)) {
|
||||
return null;
|
||||
}
|
||||
return $date->setTimeZone(new DateTimeZone('UTC'))->format(static::RFC3339);
|
||||
}
|
||||
/**
|
||||
* Convert an array of DateTime objects to an array of strings in Rfc3339 format
|
||||
*
|
||||
* @param array|null $dates The array of DateTime objects to convert
|
||||
*
|
||||
* @return array|null The array of datetime strings in Rfc3339 format
|
||||
*/
|
||||
public static function toRfc3339DateTimeArray(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'toRfc3339DateTime'], $dates);
|
||||
}
|
||||
/**
|
||||
* Convert a 2D array of DateTime objects to a 2D array of strings in Rfc3339 format
|
||||
*
|
||||
* @param array|null $dates The 2D array of DateTime objects to convert
|
||||
*
|
||||
* @return array|null The 2D array of datetime strings in Rfc3339 format
|
||||
*/
|
||||
public static function toRfc3339DateTime2DArray(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'toRfc3339DateTimeArray'], $dates);
|
||||
}
|
||||
/**
|
||||
* Parse a datetime string in Rfc3339 format to a DateTime object
|
||||
*
|
||||
* @param string|null $date A datetime string in Rfc3339 format
|
||||
*
|
||||
* @return DateTime|null The parsed DateTime object
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fromRfc3339DateTime(?string $date) : ?DateTime
|
||||
{
|
||||
if (\is_null($date)) {
|
||||
return null;
|
||||
}
|
||||
// Check for timezone information and append it if missing
|
||||
if (empty(\preg_match("/T.*[+-]|T.*Z/", $date))) {
|
||||
$date .= 'Z';
|
||||
}
|
||||
$x = DateTime::createFromFormat(static::RFC3339, $date);
|
||||
if ($x instanceof DateTime) {
|
||||
return $x->setTimeZone(new DateTimeZone('UTC'));
|
||||
}
|
||||
$x = DateTime::createFromFormat("Y-m-d\\TH:i:s.uP", $date);
|
||||
// parse with up to 6 microseconds
|
||||
if ($x instanceof DateTime) {
|
||||
return $x->setTimeZone(new DateTimeZone('UTC'));
|
||||
}
|
||||
$x = DateTime::createFromFormat("Y-m-d\\TH:i:s.uuP", $date);
|
||||
// parse with up to 12 microseconds
|
||||
if ($x instanceof DateTime) {
|
||||
return $x->setTimeZone(new DateTimeZone('UTC'));
|
||||
}
|
||||
throw new InvalidArgumentException('Incorrect format.');
|
||||
}
|
||||
/**
|
||||
* Parse a datetime string in Rfc3339 format to a DateTime object
|
||||
*
|
||||
* @param string|null $datetime A datetime string in Rfc3339 format
|
||||
*
|
||||
* @return DateTime The parsed DateTime object
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fromRfc3339DateTimeRequired(?string $datetime) : DateTime
|
||||
{
|
||||
$result = DateHelper::fromRfc3339DateTime($datetime);
|
||||
if (isset($result)) {
|
||||
return $result;
|
||||
}
|
||||
throw new \InvalidArgumentException('DateTime is null, empty or not in required format.');
|
||||
}
|
||||
/**
|
||||
* Parse an array of datetime strings in Rfc3339 format to an array of DateTime objects
|
||||
*
|
||||
* @param array|null $dates An array of datetime strings in Rfc3339 format
|
||||
*
|
||||
* @return array|null An array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromRfc3339DateTimeArray(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'fromRfc3339DateTime'], $dates);
|
||||
}
|
||||
/**
|
||||
* Parse an array of map of datetime strings in Rfc3339 format to a 2D array DateTime objects
|
||||
*
|
||||
* @param array|null $dates An array of map of datetime strings in Rfc3339 format
|
||||
*
|
||||
* @return array|null A 2D array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromRfc3339DateTimeArrayOfMap(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'fromRfc3339DateTimeMap'], $dates);
|
||||
}
|
||||
/**
|
||||
* Parse a class of datetime strings in Rfc3339 format to an array of DateTime objects
|
||||
*
|
||||
* @param stdClass|null $dates A class of datetime strings in Rfc3339 format
|
||||
*
|
||||
* @return array|null An array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromRfc3339DateTimeMap(?stdClass $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
$array = \json_decode(\json_encode($dates), \true);
|
||||
return \array_map([self::class, 'fromRfc3339DateTime'], $array);
|
||||
}
|
||||
/**
|
||||
* Parse a map of array of datetime strings in Rfc3339 format to a 2D array of DateTime objects
|
||||
*
|
||||
* @param stdClass|null $dates A map of array of datetime strings in Rfc3339 format
|
||||
*
|
||||
* @return array|null A 2D array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromRfc3339DateTimeMapOfArray(?stdClass $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
$array = \json_decode(\json_encode($dates), \true);
|
||||
return \array_map([self::class, 'fromRfc3339DateTimeArray'], $array);
|
||||
}
|
||||
/**
|
||||
* Convert a DateTime object to a Unix Timestamp
|
||||
*
|
||||
* @param DateTime|null $date The DateTime object to convert
|
||||
*
|
||||
* @return int|null The converted Unix Timestamp
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function toUnixTimestamp(?DateTime $date) : ?int
|
||||
{
|
||||
if (\is_null($date)) {
|
||||
return null;
|
||||
}
|
||||
return $date->getTimestamp();
|
||||
}
|
||||
/**
|
||||
* Convert an array of DateTime objects to an array of Unix timestamps
|
||||
*
|
||||
* @param array|null $dates The array of DateTime objects to convert
|
||||
*
|
||||
* @return array|null The array of integers representing date-time in Unix timestamp
|
||||
*/
|
||||
public static function toUnixTimestampArray(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'toUnixTimestamp'], $dates);
|
||||
}
|
||||
/**
|
||||
* Convert a 2D array of DateTime objects to a 2D array of Unix timestamps
|
||||
*
|
||||
* @param array|null $dates The 2D array of DateTime objects to convert
|
||||
*
|
||||
* @return array|null The 2D array of integers representing date-time in Unix timestamp
|
||||
*/
|
||||
public static function toUnixTimestamp2DArray(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'toUnixTimestampArray'], $dates);
|
||||
}
|
||||
/**
|
||||
* Parse a Unix Timestamp to a DateTime object
|
||||
*
|
||||
* @param string|null $date The Unix Timestamp
|
||||
*
|
||||
* @return DateTime|null The parsed DateTime object
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fromUnixTimestamp(?string $date) : ?DateTime
|
||||
{
|
||||
if (empty($date)) {
|
||||
return null;
|
||||
}
|
||||
$x = DateTime::createFromFormat("U", $date);
|
||||
if ($x instanceof DateTime) {
|
||||
return $x;
|
||||
}
|
||||
throw new InvalidArgumentException('Incorrect format.');
|
||||
}
|
||||
/**
|
||||
* Parse a Unix Timestamp to a DateTime object
|
||||
*
|
||||
* @param string|null $datetime The Unix Timestamp
|
||||
*
|
||||
* @return DateTime The parsed DateTime object
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fromUnixTimestampRequired(?string $datetime) : DateTime
|
||||
{
|
||||
$result = DateHelper::fromUnixTimestamp($datetime);
|
||||
if (isset($result)) {
|
||||
return $result;
|
||||
}
|
||||
throw new \InvalidArgumentException('DateTime is null, empty or not in required format.');
|
||||
}
|
||||
/**
|
||||
* Parse an array of Unix Timestamps to an array of DateTime objects
|
||||
*
|
||||
* @param array|null $dates An array of Unix Timestamps
|
||||
*
|
||||
* @return array|null An array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromUnixTimestampArray(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'fromUnixTimestamp'], \array_map('strval', $dates));
|
||||
}
|
||||
/**
|
||||
* Parse an array of map of Unix Timestamps to a 2D array of DateTime objects
|
||||
*
|
||||
* @param array|null $dates An array of map of Unix Timestamps
|
||||
*
|
||||
* @return array|null A 2D array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromUnixTimestampArrayOfMap(?array $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
return \array_map([self::class, 'fromUnixTimestampMap'], $dates);
|
||||
}
|
||||
/**
|
||||
* Parse a class of Unix Timestamps to an array of DateTime objects
|
||||
*
|
||||
* @param stdClass|null $dates A class of Unix Timestamps
|
||||
*
|
||||
* @return array|null An array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromUnixTimestampMap(?stdClass $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
$array = \json_decode(\json_encode($dates), \true);
|
||||
return \array_map([self::class, 'fromUnixTimestamp'], \array_map('strval', $array));
|
||||
}
|
||||
/**
|
||||
* Parse a map of array of Unix Timestamps to a 2D array of DateTime objects
|
||||
*
|
||||
* @param stdClass|null $dates A map of array of Unix Timestamps
|
||||
*
|
||||
* @return array|null A 2D array of parsed DateTime objects
|
||||
*/
|
||||
public static function fromUnixTimestampMapOfArray(?stdClass $dates) : ?array
|
||||
{
|
||||
if (\is_null($dates)) {
|
||||
return null;
|
||||
}
|
||||
$array = \json_decode(\json_encode($dates), \true);
|
||||
return \array_map([self::class, 'fromUnixTimestampArray'], $array);
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Utils;
|
||||
|
||||
use WPForms\Vendor\apimatic\jsonmapper\JsonMapper;
|
||||
use WPForms\Vendor\apimatic\jsonmapper\JsonMapperException;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\TypeValidatorInterface;
|
||||
use Exception;
|
||||
/**
|
||||
* Internal class: Do not use directly!
|
||||
*/
|
||||
class JsonHelper implements TypeValidatorInterface
|
||||
{
|
||||
/**
|
||||
* @var JsonMapper|null
|
||||
*/
|
||||
private $jsonMapper;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $defaultNamespace;
|
||||
/**
|
||||
* @param array<string,string[]> $inheritedModels
|
||||
* @param array<string,string> $discriminatorSubstitutions
|
||||
* @param string|null $additionalPropsMethodName
|
||||
* @param string|null $defaultNamespace
|
||||
*/
|
||||
public function __construct(array $inheritedModels, array $discriminatorSubstitutions, ?string $additionalPropsMethodName, ?string $defaultNamespace)
|
||||
{
|
||||
$this->jsonMapper = new JsonMapper();
|
||||
$this->jsonMapper->arChildClasses = $inheritedModels;
|
||||
$this->jsonMapper->discriminatorSubs = $discriminatorSubstitutions;
|
||||
$this->jsonMapper->sAdditionalPropertiesCollectionMethod = $additionalPropsMethodName;
|
||||
$this->defaultNamespace = $defaultNamespace;
|
||||
}
|
||||
/**
|
||||
* @param mixed $value Value to be verified against the types
|
||||
* @param string $strictType Strict single type i.e. string, ModelName, etc. or group of types
|
||||
* in string format i.e. oneOf(...), anyOf(...)
|
||||
* @param array $serializationMethods Methods required for the serialization of specific types in
|
||||
* in the provided types/type, should be an array in the format:
|
||||
* ['path/to/method argumentType', ...]. Default: []
|
||||
* @return mixed Returns validated and serialized $value
|
||||
* @throws JsonMapperException
|
||||
*/
|
||||
public function verifyTypes($value, string $strictType, array $serializationMethods = [])
|
||||
{
|
||||
return $this->jsonMapper->checkTypeGroupFor($strictType, $value, $serializationMethods);
|
||||
}
|
||||
/**
|
||||
* @param mixed $value Value to be mapped by the class
|
||||
* @param string $classname Name of the class inclusive of its namespace
|
||||
* @param int $dimension Greater than 0 if trying to map an array of
|
||||
* class with some dimensions, Default: 0
|
||||
* @return mixed Returns the mapped $value
|
||||
* @throws Exception
|
||||
*/
|
||||
public function mapClass($value, string $classname, int $dimension = 0)
|
||||
{
|
||||
return $dimension <= 0 ? $this->jsonMapper->mapClass($value, $classname) : $this->jsonMapper->mapClassArray($value, $classname, $dimension);
|
||||
}
|
||||
/**
|
||||
* @param mixed $value Value to be mapped by the typeGroup
|
||||
* @param string $typeGroup Group of types in string format i.e. oneOf(...), anyOf(...)
|
||||
* @param array $deserializers Methods required for the de-serialization of specific types in
|
||||
* in the provided typeGroup, should be an array in the format:
|
||||
* ['path/to/method returnType', ...]. Default: []
|
||||
* @return mixed Returns the mapped $value
|
||||
* @throws JsonMapperException
|
||||
*/
|
||||
public function mapTypes($value, string $typeGroup, array $deserializers = [])
|
||||
{
|
||||
return $this->jsonMapper->mapFor($value, $typeGroup, $this->defaultNamespace, $deserializers);
|
||||
}
|
||||
}
|
||||
Executable
+160
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Utils;
|
||||
|
||||
use DOMDocument;
|
||||
use Exception;
|
||||
class XmlDeserializer
|
||||
{
|
||||
/**
|
||||
* @var DOMDocument
|
||||
*/
|
||||
private $dom;
|
||||
/**
|
||||
* @int
|
||||
*/
|
||||
private $loadOptions;
|
||||
/**
|
||||
* @param int|null $loadOptions A bit field of LIBXML_* constants
|
||||
*/
|
||||
public function __construct(?int $loadOptions = null)
|
||||
{
|
||||
$this->dom = new DOMDocument();
|
||||
$this->loadOptions = $loadOptions ?? \LIBXML_NONET | \LIBXML_NOBLANKS;
|
||||
}
|
||||
public function deserialize(string $xml, string $rootName, string $clazz)
|
||||
{
|
||||
$this->dom->loadXML($xml, $this->loadOptions);
|
||||
return $this->fromElement($this->dom, $rootName, $clazz);
|
||||
}
|
||||
public function deserializeToArray(string $xml, string $rootName, string $itemName, string $clazz)
|
||||
{
|
||||
$this->dom->loadXML($xml, $this->loadOptions);
|
||||
return $this->fromElementToArray($this->dom, $itemName, $clazz, $rootName);
|
||||
}
|
||||
public function deserializeToMap(string $xml, string $rootName, string $clazz)
|
||||
{
|
||||
$this->dom->loadXML($xml, $this->loadOptions);
|
||||
return $this->fromElementToMap($this->dom, $rootName, $clazz);
|
||||
}
|
||||
public function fromAttribute(\DOMElement $parent, string $name, string $clazz)
|
||||
{
|
||||
if (!$parent->hasAttribute($name)) {
|
||||
static::assertNullable($parent, '@' . $name, $clazz);
|
||||
return null;
|
||||
}
|
||||
$attributeValue = $parent->getAttribute($name);
|
||||
return $this->convertSimple($parent->getAttributeNode($name), $attributeValue, $clazz);
|
||||
}
|
||||
public function fromElement(\DOMNode $parent, string $name, string $clazz)
|
||||
{
|
||||
$element = static::getChildNodeByTagName($parent, $name);
|
||||
if ($element === null) {
|
||||
static::assertNullable($parent, $name . '[1]', $clazz);
|
||||
return null;
|
||||
}
|
||||
return $this->convert($element, $clazz);
|
||||
}
|
||||
public function fromElementToArray(\DOMNode $parent, string $itemName, string $clazz, ?string $wrappingElementName = null)
|
||||
{
|
||||
if ($wrappingElementName === null) {
|
||||
$elements = static::getChildNodesByTagName($parent, $itemName);
|
||||
} else {
|
||||
$wrappingElement = static::getChildNodeByTagName($parent, $wrappingElementName);
|
||||
if ($wrappingElement === null) {
|
||||
static::assertNullable($parent, $wrappingElementName . '[1]', $clazz);
|
||||
return null;
|
||||
}
|
||||
$elements = static::getChildNodesByTagName($wrappingElement, $itemName);
|
||||
}
|
||||
return \array_map(function ($element) use($clazz) {
|
||||
return $this->convert($element, $clazz);
|
||||
}, $elements);
|
||||
}
|
||||
public function fromElementToMap(\DOMNode $parent, string $name, string $clazz)
|
||||
{
|
||||
$wrapper = static::getChildNodeByTagName($parent, $name);
|
||||
if ($wrapper === null) {
|
||||
static::assertNullable($parent, $name . '[1]', $clazz);
|
||||
return null;
|
||||
}
|
||||
$map = [];
|
||||
foreach ($wrapper->childNodes as $element) {
|
||||
if ($element->nodeType === \XML_ELEMENT_NODE && $element->hasAttribute('key') === \true) {
|
||||
$map[$element->getAttribute('key')] = $this->convert($element, $clazz);
|
||||
}
|
||||
}
|
||||
return $map;
|
||||
}
|
||||
private function convert(\DOMElement $node, string $clazz)
|
||||
{
|
||||
$type = static::withoutNullQualifier($clazz);
|
||||
if (\class_exists($type) && \method_exists($type, 'fromXmlElement')) {
|
||||
return \call_user_func([$type, 'fromXmlElement'], $this, $node);
|
||||
}
|
||||
return $this->convertSimple($node, $node->textContent, $clazz);
|
||||
}
|
||||
private function convertSimple(\DOMNode $node, string $value, string $clazz)
|
||||
{
|
||||
$type = static::withoutNullQualifier($clazz);
|
||||
if ($type === 'float') {
|
||||
return \is_numeric($value) ? \floatval($value) : static::throwTypeException($node, $value, $clazz);
|
||||
} elseif ($type === 'int') {
|
||||
return \is_numeric($value) ? \intval($value) : static::throwTypeException($node, $value, $clazz);
|
||||
} elseif ($type === 'bool') {
|
||||
return \strcasecmp($value, 'true') === 0 ?: (\strcasecmp($value, 'false') === 0 ? \false : static::throwTypeException($node, $value, $clazz));
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
private static function assertNullable(\DOMNode $parentNode, string $nodeSubPath, string $clazz) : void
|
||||
{
|
||||
if ($clazz[0] === '?') {
|
||||
return;
|
||||
}
|
||||
$sourceNodePath = static::makeNodePath($parentNode, $nodeSubPath);
|
||||
throw new Exception('Required value not found at XML path "' . $sourceNodePath . '" during deserialization.');
|
||||
}
|
||||
private static function throwTypeException(\DOMNode $sourceNode, $value, string $clazz) : void
|
||||
{
|
||||
throw new Exception('Expected value of type "' . $clazz . '" but got value "' . $value . '" at XML path "' . $sourceNode->getNodePath() . '" during deserialization.');
|
||||
}
|
||||
private static function withoutNullQualifier(string $type) : string
|
||||
{
|
||||
if (\strlen($type) > 1 && $type[0] === '?') {
|
||||
return \substr($type, 1);
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
private static function makeNodePath(\DOMNode $node, $subpath)
|
||||
{
|
||||
$parentPath = $node->getNodePath();
|
||||
if ($parentPath === '/') {
|
||||
$parentPath = '';
|
||||
}
|
||||
return $parentPath . '/' . $subpath;
|
||||
}
|
||||
private static function getChildNodeByTagName(\DOMNode $node, string $name) : ?\DOMElement
|
||||
{
|
||||
if ($node->hasChildNodes()) {
|
||||
foreach ($node->childNodes as $childNode) {
|
||||
if ($childNode->nodeType === \XML_ELEMENT_NODE && $childNode->tagName === $name) {
|
||||
return $childNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static function getChildNodesByTagName(\DOMNode $node, string $name) : array
|
||||
{
|
||||
$arr = [];
|
||||
if ($node->hasChildNodes()) {
|
||||
foreach ($node->childNodes as $childNode) {
|
||||
if ($childNode->nodeType === \XML_ELEMENT_NODE && $childNode->tagName === $name) {
|
||||
$arr[] = $childNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
}
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Core\Utils;
|
||||
|
||||
use DOMDocument;
|
||||
class XmlSerializer
|
||||
{
|
||||
/**
|
||||
* @var DOMDocument
|
||||
*/
|
||||
private $dom;
|
||||
public function __construct(array $options)
|
||||
{
|
||||
$this->dom = static::createDomDocumentFromContext($options);
|
||||
}
|
||||
public function serialize(string $rootName, $value) : string
|
||||
{
|
||||
$this->addAsSubelement($this->dom, $rootName, $value);
|
||||
return $this->dom->saveXML();
|
||||
}
|
||||
public function serializeArray(string $rootName, string $itemName, $value) : string
|
||||
{
|
||||
$this->addArrayAsSubelement($this->dom, $itemName, $value, $rootName);
|
||||
return $this->dom->saveXML();
|
||||
}
|
||||
public function serializeMap(string $rootName, $entries) : string
|
||||
{
|
||||
$this->addMapAsSubelement($this->dom, $rootName, $entries);
|
||||
return $this->dom->saveXML();
|
||||
}
|
||||
public function addAsAttribute(\DOMElement $element, $name, $value) : void
|
||||
{
|
||||
if ($value === null) {
|
||||
return;
|
||||
}
|
||||
$element->setAttribute($name, $this->convertSimple($value));
|
||||
}
|
||||
public function addMapAsSubelement(\DOMNode $root, string $name, $entries) : void
|
||||
{
|
||||
if ($entries === null) {
|
||||
return;
|
||||
}
|
||||
$parent = $this->createElement($name);
|
||||
$root->appendChild($parent);
|
||||
foreach ($entries as $key => $value) {
|
||||
$element = $this->addAsSubelement($parent, 'entry', $value);
|
||||
if ($element !== null) {
|
||||
$element->setAttribute('key', $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
public function addArrayAsSubelement(\DOMNode $root, string $itemName, $items, ?string $wrappingElementName = null) : void
|
||||
{
|
||||
if ($items === null) {
|
||||
return;
|
||||
}
|
||||
if ($wrappingElementName === null) {
|
||||
$parent = $root;
|
||||
} else {
|
||||
$parent = $this->createElement($wrappingElementName);
|
||||
$root->appendChild($parent);
|
||||
}
|
||||
foreach ($items as $item) {
|
||||
$this->addAsSubelement($parent, $itemName, $item);
|
||||
}
|
||||
}
|
||||
public function addAsSubelement(\DOMNode $root, string $name, $value) : ?\DOMElement
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
if (\is_object($value) && \method_exists($value, 'toXmlElement')) {
|
||||
$element = $this->createElement($name);
|
||||
$value->toXmlElement($this, $element);
|
||||
} else {
|
||||
$element = $this->createElement($name, $this->convertSimple($value));
|
||||
}
|
||||
$root->appendChild($element);
|
||||
return $element;
|
||||
}
|
||||
public function createElement(string $name, ?string $value = null) : \DOMElement
|
||||
{
|
||||
return $value === null ? $this->dom->createElement($name) : $this->dom->createElement($name, $value);
|
||||
}
|
||||
private function convertSimple($value) : string
|
||||
{
|
||||
if (\is_bool($value)) {
|
||||
return $value ? 'true' : 'false';
|
||||
} else {
|
||||
return \strval($value);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Create a DOM document, taking serializer options into account.
|
||||
*
|
||||
* @param array $context Options that the encoder has access to
|
||||
*/
|
||||
private static function createDomDocumentFromContext(array $context) : DOMDocument
|
||||
{
|
||||
$document = new DOMDocument();
|
||||
// Set an attribute on the DOM document specifying, as part of the XML declaration,
|
||||
$xmlOptions = [
|
||||
// nicely formats output with indentation and extra space
|
||||
'formatOutput',
|
||||
// the version number of the document
|
||||
'xmlVersion',
|
||||
// the encoding of the document
|
||||
'encoding',
|
||||
// whether the document is standalone
|
||||
'xmlStandalone',
|
||||
];
|
||||
foreach ($xmlOptions as $xmlOption) {
|
||||
if (isset($context[$xmlOption])) {
|
||||
$document->{$xmlOption} = $context[$xmlOption];
|
||||
}
|
||||
}
|
||||
return $document;
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
Open Software License v. 3.0 (OSL-3.0)
|
||||
|
||||
This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
|
||||
|
||||
Licensed under the Open Software License version 3.0
|
||||
|
||||
1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
|
||||
|
||||
a) to reproduce the Original Work in copies, either alone or as part of a collective work;
|
||||
|
||||
b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
|
||||
|
||||
c) to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License;
|
||||
|
||||
d) to perform the Original Work publicly; and
|
||||
|
||||
e) to display the Original Work publicly.
|
||||
|
||||
2) Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
|
||||
|
||||
3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
|
||||
|
||||
4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor’s trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
|
||||
|
||||
5) External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
|
||||
|
||||
6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
|
||||
|
||||
7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
|
||||
|
||||
8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
|
||||
|
||||
9) Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including “fair use” or “fair dealing”). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
|
||||
|
||||
10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
|
||||
|
||||
11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
|
||||
|
||||
12) Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
|
||||
|
||||
13) Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
|
||||
|
||||
14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
|
||||
|
||||
16) Modification of This License. This License is Copyright (c) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor;
|
||||
|
||||
class Address
|
||||
{
|
||||
public $street;
|
||||
public $city;
|
||||
public function getGeoCoords()
|
||||
{
|
||||
$data = \file_get_contents('http://nominatim.openstreetmap.org/search?q=' . \urlencode($this->street) . ',' . \urlencode($this->city) . '&format=json&addressdetails=1');
|
||||
$json = \json_decode($data);
|
||||
return array('lat' => $json[0]->lat, 'lon' => $json[0]->lon);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor;
|
||||
|
||||
class Contact
|
||||
{
|
||||
public $name;
|
||||
/**
|
||||
* @var Address
|
||||
*/
|
||||
public $address;
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor;
|
||||
|
||||
require_once __DIR__ . '/../src/JsonMapper.php';
|
||||
require_once 'Contact.php';
|
||||
require_once 'Address.php';
|
||||
$json = \json_decode(\file_get_contents(__DIR__ . '/single.json'));
|
||||
$mapper = new JsonMapper();
|
||||
$contact = $mapper->map($json, new Contact());
|
||||
$coords = $contact->address->getGeoCoords();
|
||||
echo $contact->name . ' lives at coordinates ' . $coords['lat'] . ',' . $coords['lon'] . "\n";
|
||||
wp-content/plugins/wpforms-lite/vendor_prefixed/apimatic/jsonmapper/src/AnyOfValidationException.php
Executable
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of JsonMapper
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Apimatic
|
||||
* @package JsonMapper
|
||||
* @author Asad Ali <asad.ali@apimatic.io>
|
||||
* @license OSL-3.0 http://opensource.org/licenses/osl-3.0
|
||||
* @link https://www.apimatic.io/
|
||||
*/
|
||||
namespace WPForms\Vendor\apimatic\jsonmapper;
|
||||
|
||||
/**
|
||||
* OneOf Validation Exception.
|
||||
*
|
||||
* @category Apimatic
|
||||
* @package JsonMapper
|
||||
* @author Asad Ali <asad.ali@apimatic.io>
|
||||
* @license OSL-3.0 http://opensource.org/licenses/osl-3.0
|
||||
* @link https://www.apimatic.io/
|
||||
*/
|
||||
class AnyOfValidationException extends JsonMapperException
|
||||
{
|
||||
/**
|
||||
* JSON does not match any of the types provided.
|
||||
*
|
||||
* @param string $type The type JSON could not be mapped to.
|
||||
* @param string $json JSON string.
|
||||
*
|
||||
* @return AnyOfValidationException
|
||||
*/
|
||||
static function cannotMapAnyOfException($type, $json)
|
||||
{
|
||||
return new self("We could not match any acceptable type from" . " {$type} on: {$json}");
|
||||
}
|
||||
}
|
||||
+1662
File diff suppressed because it is too large
Load Diff
Executable
+153
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of JsonMapper
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Netresearch
|
||||
* @package JsonMapper
|
||||
* @author Christian Weiske <christian.weiske@netresearch.de>
|
||||
* @license OSL-3.0 http://opensource.org/licenses/osl-3.0
|
||||
* @link http://www.netresearch.de/
|
||||
*/
|
||||
namespace WPForms\Vendor\apimatic\jsonmapper;
|
||||
|
||||
use RuntimeException;
|
||||
/**
|
||||
* Simple exception
|
||||
*
|
||||
* @category Netresearch
|
||||
* @package JsonMapper
|
||||
* @author Christian Weiske <christian.weiske@netresearch.de>
|
||||
* @license OSL-3.0 http://opensource.org/licenses/osl-3.0
|
||||
* @link http://www.netresearch.de/
|
||||
*/
|
||||
class JsonMapperException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Exception for discarded comments setting in configuration.
|
||||
*
|
||||
* @param array $concernedKeys Keys (PHP directives) with issues.
|
||||
*
|
||||
* @return JsonMapperException
|
||||
*/
|
||||
static function commentsDisabledInConfigurationException($concernedKeys)
|
||||
{
|
||||
return new self("Comments cannot be discarded in the configuration file i.e." . " the php.ini file; doc comments are a requirement for JsonMapper." . " Following configuration keys must have a value set to \"1\": " . \implode(", ", $concernedKeys) . ".");
|
||||
}
|
||||
/**
|
||||
* Exception for non-existent key in an object.
|
||||
*
|
||||
* @param string $key The missing key/property.
|
||||
* @param string $class The class in which the key is missing.
|
||||
* @param bool $setterException Raise an exception specific to
|
||||
* missing a setter within the class for
|
||||
* the specified string.
|
||||
*
|
||||
* @return JsonMapperException
|
||||
*/
|
||||
static function undefinedPropertyException($key, $class, $setterException = \false)
|
||||
{
|
||||
$err = $setterException ? 'has no public setter method' : 'does not exist';
|
||||
return new self("JSON property '{$key}' {$err} in object of type '{$class}'");
|
||||
}
|
||||
/**
|
||||
* Exception for non-existent key in an object.
|
||||
*
|
||||
* @param string $key The property missing type.
|
||||
* @param string $strClassName The class in which the property is missing type.
|
||||
*
|
||||
* @return JsonMapperException
|
||||
*/
|
||||
static function missingTypePropertyException($key, $strClassName)
|
||||
{
|
||||
return new self("Empty type at property '{$strClassName}::\${$key}'");
|
||||
}
|
||||
/**
|
||||
* Exception for an unCallable Factory Method.
|
||||
*
|
||||
* @param string $factoryMethod The concerned factory method.
|
||||
* @param string $strClassName Related class name.
|
||||
*
|
||||
* @return JsonMapperException
|
||||
*/
|
||||
static function unCallableFactoryMethodException($factoryMethod, $strClassName)
|
||||
{
|
||||
return new self("Factory method '{$factoryMethod}' referenced by " . "'{$strClassName}' is not callable.");
|
||||
}
|
||||
/**
|
||||
* Exception for not able to call factory method with the given value.
|
||||
*
|
||||
* @param string $argType Type of the argument passed in method.
|
||||
* @param string $reasons Exception message received from factory method.
|
||||
*
|
||||
* @return JsonMapperException
|
||||
*/
|
||||
static function invalidArgumentFactoryMethodException($argType, $reasons)
|
||||
{
|
||||
return new self("Provided factory methods are not callable with " . "the value of Type: {$argType}\n{$reasons}");
|
||||
}
|
||||
/**
|
||||
* Exception when it is not possible to map an object to a specific type.
|
||||
*
|
||||
* @param string $typeName Name of type to map json object on.
|
||||
* @param string $typeGroup Group name of the type provided.
|
||||
* @param string $value Value that should be mapped by typeGroup
|
||||
* i.e. JSON string.
|
||||
*
|
||||
* @return JsonMapperException
|
||||
*/
|
||||
static function unableToMapException($typeName, $typeGroup, $value)
|
||||
{
|
||||
return new self("Unable to map {$typeName}: {$typeGroup} on: {$value}");
|
||||
}
|
||||
/**
|
||||
* A property marked as required was missing in the object provided.
|
||||
*
|
||||
* @param string $propertyName Concerned property's name.
|
||||
* @param string $className The class name in which the property wasn't found.
|
||||
*
|
||||
* @return JsonMapperException
|
||||
*/
|
||||
static function requiredPropertyMissingException($propertyName, $className)
|
||||
{
|
||||
return new self("Required property '{$propertyName}' of class " . "'{$className}' is missing in JSON data");
|
||||
}
|
||||
/**
|
||||
* No required arguments were provided.
|
||||
*
|
||||
* @param string $class The concerned class name.
|
||||
* @param int $ctorReqParamNumber The number of req params in constructor.
|
||||
*
|
||||
* @return JsonMapperException
|
||||
*/
|
||||
static function noArgumentsException($class, $ctorReqParamNumber)
|
||||
{
|
||||
return new self("{$class} class requires {$ctorReqParamNumber} " . "arguments in constructor but none provided");
|
||||
}
|
||||
/**
|
||||
* Provided arguments were less than required.
|
||||
*
|
||||
* @param string $class The concerned class name.
|
||||
* @param array $ctorRequiredParamsName Required parameters array.
|
||||
*
|
||||
* @return JsonMapperException
|
||||
*/
|
||||
static function fewerArgumentsException($class, $ctorRequiredParamsName)
|
||||
{
|
||||
return new self("Could not find required constructor arguments for {$class}: " . \implode(", ", $ctorRequiredParamsName));
|
||||
}
|
||||
/**
|
||||
* Provided type was not applicable on the given value.
|
||||
*
|
||||
* @param string $type The type value could not be mapped to.
|
||||
* @param string $value Concerned value.
|
||||
*
|
||||
* @return JsonMapperException
|
||||
*/
|
||||
static function unableToSetTypeException($type, $value)
|
||||
{
|
||||
return new self("Could not set type '{$type}' on value: {$value}");
|
||||
}
|
||||
}
|
||||
wp-content/plugins/wpforms-lite/vendor_prefixed/apimatic/jsonmapper/src/OneOfValidationException.php
Executable
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of JsonMapper
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Apimatic
|
||||
* @package JsonMapper
|
||||
* @author Asad Ali <asad.ali@apimatic.io>
|
||||
* @license OSL-3.0 http://opensource.org/licenses/osl-3.0
|
||||
* @link https://www.apimatic.io/
|
||||
*/
|
||||
namespace WPForms\Vendor\apimatic\jsonmapper;
|
||||
|
||||
/**
|
||||
* OneOf Validation Exception.
|
||||
*
|
||||
* @category Apimatic
|
||||
* @package JsonMapper
|
||||
* @author Asad Ali <asad.ali@apimatic.io>
|
||||
* @license OSL-3.0 http://opensource.org/licenses/osl-3.0
|
||||
* @link https://www.apimatic.io/
|
||||
*/
|
||||
class OneOfValidationException extends JsonMapperException
|
||||
{
|
||||
/**
|
||||
* Exception raised when a json object maps to more
|
||||
* than one type within the types specified in OneOf.
|
||||
*
|
||||
* @param string $matchedType First type.
|
||||
* @param string $mappedWith Second type.
|
||||
* @param string $json JSON string.
|
||||
*
|
||||
* @return OneOfValidationException
|
||||
*/
|
||||
static function moreThanOneOfException($matchedType, $mappedWith, $json)
|
||||
{
|
||||
return new self("There are more than one matching types i.e." . " { {$matchedType} and {$mappedWith} } on: {$json}");
|
||||
}
|
||||
/**
|
||||
* JSON does not match any of the provided types.
|
||||
*
|
||||
* @param string $type The type JSON could not be mapped to.
|
||||
* @param string $json JSON string.
|
||||
*
|
||||
* @return OneOfValidationException
|
||||
*/
|
||||
static function cannotMapAnyOfException($type, $json)
|
||||
{
|
||||
return new self("We could not match any acceptable type from" . " {$type} on: {$json}");
|
||||
}
|
||||
}
|
||||
Executable
+393
@@ -0,0 +1,393 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of JsonMapper
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Apimatic
|
||||
* @package JsonMapper
|
||||
* @author Asad Ali <asad.ali@apimatic.io>
|
||||
* @license OSL-3.0 http://opensource.org/licenses/osl-3.0
|
||||
* @link https://www.apimatic.io/
|
||||
*/
|
||||
namespace WPForms\Vendor\apimatic\jsonmapper;
|
||||
|
||||
/**
|
||||
* Data class to hold the groups of multiple types.
|
||||
*
|
||||
* @category Apimatic
|
||||
* @package JsonMapper
|
||||
* @author Asad Ali <asad.ali@apimatic.io>
|
||||
* @license OSL-3.0 http://opensource.org/licenses/osl-3.0
|
||||
* @link https://www.apimatic.io/
|
||||
*/
|
||||
class TypeCombination
|
||||
{
|
||||
/**
|
||||
* String format of this typeCombinator group.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_format;
|
||||
/**
|
||||
* Name of this typeCombinator group i.e. oneOf/anyOf.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_groupName;
|
||||
/**
|
||||
* Name of discriminator field for this typeCombinator group.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_discriminatorField;
|
||||
/**
|
||||
* Mapping of each discriminator value on types in this typeCombinator group.
|
||||
* i.e. [typeName => discriminatorValues]
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_discriminatorMapping = [];
|
||||
/**
|
||||
* Array of string types or TypeCombination objects
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_types;
|
||||
/**
|
||||
* A list of factory methods to deserialize the given object,
|
||||
* for one of the wrapped types in this group
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $_deserializers;
|
||||
/**
|
||||
* Private constructor for TypeCombination class
|
||||
*
|
||||
* @param string $format string format value
|
||||
* @param string $groupName group name value
|
||||
* @param array $types types value
|
||||
* @param string[] $deserializers deserializers value
|
||||
*/
|
||||
private function __construct($format, $groupName, $types, $deserializers)
|
||||
{
|
||||
$this->_format = $format;
|
||||
$this->_groupName = $groupName;
|
||||
$this->_types = $types;
|
||||
$this->_deserializers = $deserializers;
|
||||
$this->_insertDiscriminators();
|
||||
}
|
||||
/**
|
||||
* String format of this typeCombinator group.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFormat()
|
||||
{
|
||||
return $this->_format;
|
||||
}
|
||||
/**
|
||||
* Name of this typeCombinator group i.e. oneOf/anyOf/array/map.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGroupName()
|
||||
{
|
||||
return $this->_groupName;
|
||||
}
|
||||
/**
|
||||
* Array of string types or TypeCombination objects
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTypes()
|
||||
{
|
||||
return $this->_types;
|
||||
}
|
||||
/**
|
||||
* A list of factory methods to deserialize the given object,
|
||||
* for one of the wrapped types in this group
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getDeserializers()
|
||||
{
|
||||
return $this->_deserializers;
|
||||
}
|
||||
/**
|
||||
* Get discriminator info as an array (if exists)
|
||||
*
|
||||
* @param string $type String type to search
|
||||
* discriminators
|
||||
* @param array<string,string> $discriminatorSubs Map of actual discriminator
|
||||
* values where keys contain
|
||||
* substituted values in the
|
||||
* typeGroup string, Default: []
|
||||
*
|
||||
* @return array|null An array with format: discriminatorFieldName
|
||||
* as element 1 and discriminatorValues as
|
||||
* element 2
|
||||
*/
|
||||
public function getDiscriminator($type, $discriminatorSubs = [])
|
||||
{
|
||||
if (!isset($this->_discriminatorField) || !isset($this->_discriminatorMapping[$type])) {
|
||||
return null;
|
||||
}
|
||||
$fieldName = $this->_discriminatorField;
|
||||
if (isset($discriminatorSubs[$fieldName])) {
|
||||
$fieldName = $discriminatorSubs[$fieldName];
|
||||
}
|
||||
$discValues = \array_map(function ($value) use($discriminatorSubs) {
|
||||
if (isset($discriminatorSubs[$value])) {
|
||||
return $discriminatorSubs[$value];
|
||||
}
|
||||
return $value;
|
||||
}, $this->_discriminatorMapping[$type]);
|
||||
return [$fieldName, $discValues];
|
||||
}
|
||||
/**
|
||||
* Extract innermost oneof/anyof group hidden inside array/map
|
||||
* type group
|
||||
*
|
||||
* @return TypeCombination
|
||||
*/
|
||||
public function extractOneOfAnyOfGroup()
|
||||
{
|
||||
$innerType = $this->getTypes()[0];
|
||||
if (\in_array($this->getGroupName(), ["array", "map"]) && $innerType instanceof TypeCombination) {
|
||||
return $innerType->extractOneOfAnyOfGroup();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Extract all internal groups similar to the given group as a list of
|
||||
* TypeCombination objects, it will only return similar array/map groups
|
||||
*
|
||||
* @param TypeCombination $group All inner groups similar to this array/map
|
||||
* type group will be extracted
|
||||
*
|
||||
* @return TypeCombination[] A list of similar TypeCombination objects
|
||||
*/
|
||||
public function extractSimilar($group)
|
||||
{
|
||||
$result = [];
|
||||
if (!\in_array($this->getGroupName(), ["array", "map"])) {
|
||||
// if group is neither array nor map then call extractSimilar for
|
||||
// each of the internal groups
|
||||
foreach ($this->getTypes() as $typ) {
|
||||
if ($typ instanceof TypeCombination) {
|
||||
$result = \array_merge($result, $typ->extractSimilar($group));
|
||||
}
|
||||
}
|
||||
} elseif ($group->getGroupName() == $this->getGroupName()) {
|
||||
// if groupName is same then check inner group type
|
||||
$internal = $this->getTypes()[0];
|
||||
$group = $group->getTypes()[0];
|
||||
if (\in_array($group->getGroupName(), ["array", "map"])) {
|
||||
// if inner group is array/map then return result after
|
||||
// extraction of groups similar to innerGroup
|
||||
$result = $internal->extractSimilar($group);
|
||||
} else {
|
||||
// if inner group is oneof/anyof then only extract $internal
|
||||
$result = [$internal];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
/**
|
||||
* Extract type info like: isMap, isArray, and inner type for maps/arrays.
|
||||
*
|
||||
* @param string $type Type to be checked and extracted for information.
|
||||
*
|
||||
* @return array An array with type info in the format:
|
||||
* (bool isMap, bool isArray, string $internalType).
|
||||
*/
|
||||
public static function extractTypeInfo($type)
|
||||
{
|
||||
// Check if the type is map, i.e. wrapped in array<string,...>
|
||||
if (\preg_match('/^array<string,.*>$/', $type)) {
|
||||
return [\true, \false, \substr($type, \strlen('array<string,'), -1)];
|
||||
}
|
||||
// Check if the type is array, i.e. ends with '[]'
|
||||
if (\preg_match('/\\[]$/', $type)) {
|
||||
return [\false, \true, \substr($type, 0, -2)];
|
||||
}
|
||||
// Check if the type is array, i.e. wrapped in 'array<...>'
|
||||
if (\preg_match('/^array<.*>$/', $type)) {
|
||||
return [\false, \true, \substr($type, \strlen('array<'), -1)];
|
||||
}
|
||||
// If the type does not match the array formats, return the original type
|
||||
return [\false, \false, $type];
|
||||
}
|
||||
/**
|
||||
* Create an oneof/anyof TypeCombination instance, by specifying inner types
|
||||
*
|
||||
* @param array $types i.e. (TypeCombination,string)[]
|
||||
* @param string $gName group name value (anyof, oneof),
|
||||
* Default: anyof
|
||||
*
|
||||
* @return TypeCombination
|
||||
*/
|
||||
public static function with($types, $gName = 'anyof')
|
||||
{
|
||||
$format = \join(',', \array_map(function ($t) {
|
||||
return \is_string($t) ? $t : $t->getFormat();
|
||||
}, $types));
|
||||
return new self("{$gName}({$format})", $gName, $types, []);
|
||||
}
|
||||
/**
|
||||
* Wrap the given typeGroup string in the TypeCombination class,
|
||||
* i.e. getTypes() method will return all the grouped types,
|
||||
* while deserializing factory methods can be obtained by
|
||||
* getDeserializers() and group name can be obtained from getGroupName()
|
||||
*
|
||||
* @param string $typeGroup Format of multiple types i.e oneOf(int,bool)[],
|
||||
* onyOf(int[], bool,anyOf(string,float)[],...),
|
||||
* array<string,oneOf(int,float)[]> here []
|
||||
* represents array types, and array<string,T>
|
||||
* represents map types, oneOf/anyOf are group
|
||||
* names, while default group name is anyOf.
|
||||
* @param string[] $deserializers Callable factory methods for the property,
|
||||
* Default: []
|
||||
*
|
||||
* @return TypeCombination
|
||||
*/
|
||||
public static function withFormat($typeGroup, $deserializers = [])
|
||||
{
|
||||
$groupName = 'anyOf';
|
||||
$start = \strpos($typeGroup, '(');
|
||||
$end = \strrpos($typeGroup, ')');
|
||||
if ($start !== \false && $end !== \false) {
|
||||
list($isMap, $isArray, $innerType) = self::extractTypeInfo($typeGroup);
|
||||
if ($isMap || $isArray) {
|
||||
return self::_createTypeGroup($isMap ? 'map' : 'array', $innerType, $deserializers);
|
||||
}
|
||||
$name = \substr($typeGroup, 0, $start);
|
||||
$groupName = empty($name) ? $groupName : $name;
|
||||
$typeGroup = \substr($typeGroup, $start + 1, -1);
|
||||
}
|
||||
$format = "({$typeGroup})";
|
||||
$types = [];
|
||||
$type = '';
|
||||
$groupCount = 0;
|
||||
foreach (\str_split($typeGroup) as $c) {
|
||||
if ($c == '(' || $c == '<') {
|
||||
$groupCount++;
|
||||
}
|
||||
if ($c == ')' || $c == '>') {
|
||||
$groupCount--;
|
||||
}
|
||||
if ($c == ',' && $groupCount == 0) {
|
||||
self::_insertType($types, $type, $deserializers);
|
||||
$type = '';
|
||||
continue;
|
||||
}
|
||||
$type .= $c;
|
||||
}
|
||||
self::_insertType($types, $type, $deserializers);
|
||||
return new self($format, $groupName, $types, $deserializers);
|
||||
}
|
||||
/**
|
||||
* Creates a TypeCombination object with the given name and inner
|
||||
* types group that must be another typeCombination object
|
||||
*
|
||||
* @param string $name Group name for the typeCombination object.
|
||||
* @param string $type typeGroup to be created and inserted.
|
||||
* @param string[] $deserializers deserializer for the type group.
|
||||
*
|
||||
* @return TypeCombination
|
||||
*/
|
||||
private static function _createTypeGroup($name, $type, $deserializers)
|
||||
{
|
||||
$format = $name == 'map' ? "array<string,{$type}>" : $type . '[]';
|
||||
return new self($format, $name, [self::withFormat($type, $deserializers)], $deserializers);
|
||||
}
|
||||
/**
|
||||
* Insert the type in the types array which is passed by reference,
|
||||
* Also check if type is not empty
|
||||
*
|
||||
* @param array $types types array reference
|
||||
* @param string $type type to be inserted
|
||||
* @param string[] $deserializers deserializer for the type group
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function _insertType(&$types, $type, $deserializers)
|
||||
{
|
||||
$type = \trim($type);
|
||||
if (\strpos($type, '(') !== \false && \strrpos($type, ')') !== \false) {
|
||||
// If type is Grouped, creating TypeCombination instance for it
|
||||
$type = self::withFormat($type, $deserializers);
|
||||
}
|
||||
if (!empty($type)) {
|
||||
$types[] = $type;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Insert discriminator and discriminators mapping from group and
|
||||
* type names.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function _insertDiscriminators()
|
||||
{
|
||||
list($this->_groupName, $this->_discriminatorField) = self::_extractDiscriminator($this->_groupName);
|
||||
$this->_types = $this->_filterUniqueTypes(\array_map(function ($type) {
|
||||
if (!\is_string($type)) {
|
||||
return $type;
|
||||
}
|
||||
list($type, $discriminator) = self::_extractDiscriminator($type);
|
||||
if (\array_key_exists($type, $this->_discriminatorMapping)) {
|
||||
$this->_discriminatorMapping[$type][] = $discriminator;
|
||||
} else {
|
||||
$this->_discriminatorMapping[$type] = [$discriminator];
|
||||
}
|
||||
return $type;
|
||||
}, $this->_types));
|
||||
if (isset($this->_discriminatorField)) {
|
||||
$this->_format .= '{' . $this->_discriminatorField . '}';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Filter out the same types.
|
||||
*
|
||||
* @param array $types Types to be checked for uniqueness.
|
||||
*
|
||||
* @return array An array with all the unique types
|
||||
*/
|
||||
private function _filterUniqueTypes($types)
|
||||
{
|
||||
$seenTypes = [];
|
||||
$uniqueTypes = [];
|
||||
foreach ($types as $type) {
|
||||
if (\is_string($type)) {
|
||||
if (\in_array($type, $seenTypes, \true)) {
|
||||
continue;
|
||||
}
|
||||
$seenTypes[] = $type;
|
||||
}
|
||||
$uniqueTypes[] = $type;
|
||||
}
|
||||
return $uniqueTypes;
|
||||
}
|
||||
/**
|
||||
* Extract type discriminator.
|
||||
*
|
||||
* @param string $type Type to be checked and extracted for discriminator.
|
||||
*
|
||||
* @return array An array with type info in the format:
|
||||
* (string $typeWithoutDiscriminator, string? discriminator).
|
||||
*/
|
||||
private function _extractDiscriminator($type)
|
||||
{
|
||||
$start = \strpos($type, '{');
|
||||
$end = \strpos($type, '}');
|
||||
$discriminator = null;
|
||||
if ($start !== \false && $end !== \false) {
|
||||
$discriminator = \substr($type, $start + 1, $end - \strlen($type));
|
||||
$type = \substr($type, 0, $start) . \substr($type, $end + 1);
|
||||
}
|
||||
return [$type, $discriminator];
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2013-2015 Mashape (https://www.mashape.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
==============
|
||||
|
||||
Modified under MIT License by APIMATIC (https://www.apimatic.io)
|
||||
Executable
+341
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Unirest;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Http\HttpConfigurations;
|
||||
class Configuration
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $cookie;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $cookieFile;
|
||||
private $curlOpts = [];
|
||||
private $jsonOpts = [];
|
||||
private $socketTimeout = 0;
|
||||
private $enableRetries = \false;
|
||||
// should we enable retries feature
|
||||
private $maxNumberOfRetries = 3;
|
||||
// total number of allowed retries
|
||||
private $retryOnTimeout = \false;
|
||||
// Should we retry on timeout?
|
||||
private $retryInterval = 1.0;
|
||||
// Initial retry interval in seconds, to be increased by backoffFactor
|
||||
private $maximumRetryWaitTime = 120;
|
||||
// maximum retry wait time (commutative)
|
||||
private $backoffFactor = 2.0;
|
||||
// backoff factor to be used to increase retry interval
|
||||
private $httpStatusCodesToRetry = [408, 413, 429, 500, 502, 503, 504, 521, 522, 524];
|
||||
private $httpMethodsToRetry = ["GET", "PUT"];
|
||||
private $verifyPeer = \true;
|
||||
private $verifyHost = \true;
|
||||
private $defaultHeaders = [];
|
||||
private $auth = ['user' => '', 'pass' => '', 'method' => \CURLAUTH_BASIC];
|
||||
private $proxy = ['port' => \false, 'tunnel' => \false, 'address' => \false, 'type' => \CURLPROXY_HTTP, 'auth' => ['user' => '', 'pass' => '', 'method' => \CURLAUTH_BASIC]];
|
||||
public static function init(?HttpConfigurations $httpConfigurations = null) : self
|
||||
{
|
||||
return new self($httpConfigurations);
|
||||
}
|
||||
private function __construct(?HttpConfigurations $httpConfigurations)
|
||||
{
|
||||
if (\is_null($httpConfigurations)) {
|
||||
return;
|
||||
}
|
||||
$this->timeout($httpConfigurations->getTimeout())->enableRetries($httpConfigurations->shouldEnableRetries())->maxNumberOfRetries($httpConfigurations->getNumberOfRetries())->retryOnTimeout($httpConfigurations->shouldRetryOnTimeout())->retryInterval($httpConfigurations->getRetryInterval())->maximumRetryWaitTime($httpConfigurations->getMaximumRetryWaitTime())->backoffFactor($httpConfigurations->getBackOffFactor())->httpStatusCodesToRetry($httpConfigurations->getHttpStatusCodesToRetry())->httpMethodsToRetry($httpConfigurations->getHttpMethodsToRetry());
|
||||
}
|
||||
/**
|
||||
* @param int $socketTimeout Timeout for API calls in seconds.
|
||||
*/
|
||||
public function timeout(int $socketTimeout) : self
|
||||
{
|
||||
$this->socketTimeout = $socketTimeout;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param bool $enableRetries Whether to enable retries and backoff feature.
|
||||
*/
|
||||
public function enableRetries(bool $enableRetries) : self
|
||||
{
|
||||
$this->enableRetries = $enableRetries;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param int $maxNumberOfRetries The number of retries to make.
|
||||
*/
|
||||
public function maxNumberOfRetries(int $maxNumberOfRetries) : self
|
||||
{
|
||||
$this->maxNumberOfRetries = $maxNumberOfRetries;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param bool $retryOnTimeout Whether to retry on timeout
|
||||
*/
|
||||
public function retryOnTimeout(bool $retryOnTimeout) : self
|
||||
{
|
||||
$this->retryOnTimeout = $retryOnTimeout;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param float $retryInterval The retry time interval between the endpoint calls.
|
||||
*/
|
||||
public function retryInterval(float $retryInterval) : self
|
||||
{
|
||||
$this->retryInterval = $retryInterval;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param int $maximumRetryWaitTime The maximum wait time in seconds for overall retrying requests.
|
||||
*/
|
||||
public function maximumRetryWaitTime(int $maximumRetryWaitTime) : self
|
||||
{
|
||||
$this->maximumRetryWaitTime = $maximumRetryWaitTime;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param float $backoffFactor Exponential backoff factor to increase interval between retries.
|
||||
*/
|
||||
public function backoffFactor(float $backoffFactor) : self
|
||||
{
|
||||
$this->backoffFactor = $backoffFactor;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param int[] $httpStatusCodesToRetry Http status codes to retry against.
|
||||
*/
|
||||
public function httpStatusCodesToRetry(array $httpStatusCodesToRetry) : self
|
||||
{
|
||||
$this->httpStatusCodesToRetry = $httpStatusCodesToRetry;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param string[] $httpMethodsToRetry Http methods to retry against.
|
||||
*/
|
||||
public function httpMethodsToRetry(array $httpMethodsToRetry) : self
|
||||
{
|
||||
$this->httpMethodsToRetry = $httpMethodsToRetry;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set JSON decode mode
|
||||
*
|
||||
* @param bool $assoc When TRUE, returned objects will be converted into associative arrays.
|
||||
* @param int $depth User specified recursion depth.
|
||||
* @param int $options Bitmask of JSON decode options. Currently only JSON_BIGINT_AS_STRING is supported
|
||||
* (default is to cast large integers as floats)
|
||||
*/
|
||||
public function jsonOpts(bool $assoc = \false, int $depth = 512, int $options = 0) : self
|
||||
{
|
||||
$this->jsonOpts = [$assoc, $depth, $options];
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Verify SSL peer
|
||||
*
|
||||
* @param bool $enabled enable SSL verification, by default is true
|
||||
*/
|
||||
public function verifyPeer(bool $enabled) : self
|
||||
{
|
||||
$this->verifyPeer = $enabled;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Verify SSL host
|
||||
*
|
||||
* @param bool $enabled enable SSL host verification, by default is true
|
||||
*/
|
||||
public function verifyHost(bool $enabled) : self
|
||||
{
|
||||
$this->verifyHost = $enabled;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set default headers to send on every request
|
||||
*
|
||||
* @param array $headers headers array
|
||||
*/
|
||||
public function defaultHeaders(array $headers) : self
|
||||
{
|
||||
$this->defaultHeaders = \array_merge($this->defaultHeaders, $headers);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set a new default header to send on every request
|
||||
*
|
||||
* @param string $name header name
|
||||
* @param string $value header value
|
||||
*/
|
||||
public function defaultHeader(string $name, string $value) : self
|
||||
{
|
||||
$this->defaultHeaders[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set curl options to send on every request
|
||||
*
|
||||
* @param array $options options array
|
||||
*/
|
||||
public function curlOpts(array $options) : self
|
||||
{
|
||||
$this->curlOpts = \array_merge($this->curlOpts, $options);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set a new default header to send on every request
|
||||
*
|
||||
* @param string|int $name header name
|
||||
* @param string $value header value
|
||||
*/
|
||||
public function curlOpt($name, string $value) : self
|
||||
{
|
||||
$this->curlOpts[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set a cookie string for enabling cookie handling
|
||||
*
|
||||
* @param string $cookie
|
||||
*/
|
||||
public function cookie(string $cookie) : self
|
||||
{
|
||||
$this->cookie = $cookie;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set a cookie file path for enabling cookie handling
|
||||
*
|
||||
* $cookieFile must be a correct path with write permission
|
||||
*
|
||||
* @param string $cookieFile - path to file for saving cookie
|
||||
*/
|
||||
public function cookieFile(string $cookieFile) : self
|
||||
{
|
||||
$this->cookieFile = $cookieFile;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set authentication method to use
|
||||
*
|
||||
* @param string $username authentication username
|
||||
* @param string $password authentication password
|
||||
* @param integer $method authentication method
|
||||
*/
|
||||
public function auth(string $username = '', string $password = '', int $method = \CURLAUTH_BASIC) : self
|
||||
{
|
||||
$this->auth['user'] = $username;
|
||||
$this->auth['pass'] = $password;
|
||||
$this->auth['method'] = $method;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set proxy to use
|
||||
*
|
||||
* @param string $address proxy address
|
||||
* @param integer $port proxy port
|
||||
* @param integer $type (Available options for this are CURLPROXY_HTTP, CURLPROXY_HTTP_1_0 CURLPROXY_SOCKS4,
|
||||
* CURLPROXY_SOCKS5, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5_HOSTNAME)
|
||||
* @param bool $tunnel enable/disable tunneling
|
||||
*/
|
||||
public function proxy(string $address, int $port = 1080, int $type = \CURLPROXY_HTTP, bool $tunnel = \false) : self
|
||||
{
|
||||
$this->proxy['type'] = $type;
|
||||
$this->proxy['port'] = $port;
|
||||
$this->proxy['tunnel'] = $tunnel;
|
||||
$this->proxy['address'] = $address;
|
||||
return $this;
|
||||
}
|
||||
public function proxyConfiguration(array $proxyConfiguration) : self
|
||||
{
|
||||
$this->proxy = \array_merge($this->proxy, $proxyConfiguration);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Set proxy authentication method to use
|
||||
*
|
||||
* @param string $username authentication username
|
||||
* @param string $password authentication password
|
||||
* @param integer $method authentication method
|
||||
*/
|
||||
public function proxyAuth(string $username = '', string $password = '', int $method = \CURLAUTH_BASIC) : self
|
||||
{
|
||||
$this->proxy['auth']['user'] = $username;
|
||||
$this->proxy['auth']['pass'] = $password;
|
||||
$this->proxy['auth']['method'] = $method;
|
||||
return $this;
|
||||
}
|
||||
public function getTimeout() : int
|
||||
{
|
||||
return $this->socketTimeout;
|
||||
}
|
||||
public function shouldEnableRetries() : bool
|
||||
{
|
||||
return $this->enableRetries;
|
||||
}
|
||||
public function getNumberOfRetries() : int
|
||||
{
|
||||
return $this->maxNumberOfRetries;
|
||||
}
|
||||
public function getRetryInterval() : float
|
||||
{
|
||||
return $this->retryInterval;
|
||||
}
|
||||
public function getBackOffFactor() : float
|
||||
{
|
||||
return $this->backoffFactor;
|
||||
}
|
||||
public function getMaximumRetryWaitTime() : int
|
||||
{
|
||||
return $this->maximumRetryWaitTime;
|
||||
}
|
||||
public function shouldRetryOnTimeout() : bool
|
||||
{
|
||||
return $this->retryOnTimeout;
|
||||
}
|
||||
public function getHttpStatusCodesToRetry() : array
|
||||
{
|
||||
return $this->httpStatusCodesToRetry;
|
||||
}
|
||||
public function getHttpMethodsToRetry() : array
|
||||
{
|
||||
return $this->httpMethodsToRetry;
|
||||
}
|
||||
public function getCookie() : ?string
|
||||
{
|
||||
return $this->cookie;
|
||||
}
|
||||
public function getCookieFile() : ?string
|
||||
{
|
||||
return $this->cookieFile;
|
||||
}
|
||||
public function getCurlOpts() : array
|
||||
{
|
||||
return $this->curlOpts;
|
||||
}
|
||||
public function getJsonOpts() : array
|
||||
{
|
||||
return $this->jsonOpts;
|
||||
}
|
||||
public function shouldVerifyPeer() : bool
|
||||
{
|
||||
return $this->verifyPeer;
|
||||
}
|
||||
public function shouldVerifyHost() : bool
|
||||
{
|
||||
return $this->verifyHost;
|
||||
}
|
||||
public function getDefaultHeaders() : array
|
||||
{
|
||||
return $this->defaultHeaders;
|
||||
}
|
||||
public function getAuth() : array
|
||||
{
|
||||
return $this->auth;
|
||||
}
|
||||
public function getProxy() : array
|
||||
{
|
||||
return $this->proxy;
|
||||
}
|
||||
}
|
||||
+310
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Unirest;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestMethod;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Response\ResponseInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Http\HttpClientInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Http\RetryOption;
|
||||
use DateTime;
|
||||
use WPForms\Vendor\Unirest\Request\Request;
|
||||
class HttpClient implements HttpClientInterface
|
||||
{
|
||||
private $handle = null;
|
||||
protected $totalNumberOfConnections = 0;
|
||||
/**
|
||||
* @var Configuration
|
||||
*/
|
||||
protected $config;
|
||||
/**
|
||||
* @param Configuration|null $configurations
|
||||
*/
|
||||
public function __construct(?Configuration $configurations = null)
|
||||
{
|
||||
$this->config = $configurations ?? Configuration::init();
|
||||
}
|
||||
public function execute(RequestInterface $request) : ResponseInterface
|
||||
{
|
||||
if ($this->handle == null) {
|
||||
$this->initializeHandle();
|
||||
} else {
|
||||
\curl_reset($this->handle);
|
||||
}
|
||||
$this->setCurlOptions($this->handle, $request);
|
||||
$retryCount = 0;
|
||||
// current retry count
|
||||
$waitTime = 0.0;
|
||||
// wait time in secs before current api call
|
||||
$allowedWaitTime = $this->config->getMaximumRetryWaitTime();
|
||||
// remaining allowed wait time in seconds
|
||||
$httpCode = null;
|
||||
$headers = [];
|
||||
do {
|
||||
// If Retrying i.e. retryCount >= 1
|
||||
if ($retryCount > 0) {
|
||||
$this->sleep($waitTime);
|
||||
// calculate remaining allowed wait Time
|
||||
$allowedWaitTime -= $waitTime;
|
||||
}
|
||||
// Execution of api call
|
||||
$response = \curl_exec($this->handle);
|
||||
$error = \curl_error($this->handle);
|
||||
$info = $this->getInfo();
|
||||
if (empty($error) && \is_string($response)) {
|
||||
$header_size = $info['header_size'];
|
||||
$httpCode = (int) $info['http_code'];
|
||||
$headers = $this->parseHeaders(\substr($response, 0, $header_size));
|
||||
}
|
||||
if ($this->shouldRetryRequest($request)) {
|
||||
// calculate wait time for retry, and should not retry when wait time becomes 0
|
||||
$waitTime = $this->getRetryWaitTime($httpCode, $headers, $error, $allowedWaitTime, $retryCount);
|
||||
$retryCount++;
|
||||
}
|
||||
} while ($waitTime > 0.0);
|
||||
if (!empty($error) || !isset($header_size, $headers, $httpCode)) {
|
||||
throw $request->toApiException($error);
|
||||
}
|
||||
// get response body
|
||||
$body = \substr($response, $header_size);
|
||||
$this->totalNumberOfConnections += $this->getInfo(\CURLINFO_NUM_CONNECTS);
|
||||
return new Response($httpCode, $body, $headers, $this->config->getJsonOpts());
|
||||
}
|
||||
protected function initializeHandle()
|
||||
{
|
||||
$this->handle = \curl_init();
|
||||
$this->totalNumberOfConnections = 0;
|
||||
}
|
||||
protected function getBody(RequestInterface $request)
|
||||
{
|
||||
if (empty($request->getParameters())) {
|
||||
return $request->getBody();
|
||||
}
|
||||
// special handling for form parameters i.e.
|
||||
// returning flatten array with encoded keys if any multipart parameter exists
|
||||
// OR returning concatenated encoded parameters string
|
||||
$encodedBody = \join('&', $request->getEncodedParameters());
|
||||
$multipartParameters = $request->getMultipartParameters();
|
||||
if (empty($multipartParameters)) {
|
||||
return $encodedBody;
|
||||
}
|
||||
if (empty($encodedBody)) {
|
||||
return $multipartParameters;
|
||||
}
|
||||
foreach (\explode('&', $encodedBody) as $param) {
|
||||
$keyValue = \explode('=', $param);
|
||||
$multipartParameters[\urldecode($keyValue[0])] = \urldecode($keyValue[1]);
|
||||
}
|
||||
return $multipartParameters;
|
||||
}
|
||||
protected function setCurlOptions($handle, RequestInterface $request) : void
|
||||
{
|
||||
$queryUrl = $request->getQueryUrl();
|
||||
$body = $this->getBody($request);
|
||||
if ($request->getHttpMethod() !== RequestMethod::GET) {
|
||||
if ($request->getHttpMethod() === RequestMethod::POST) {
|
||||
\curl_setopt($handle, \CURLOPT_POST, \true);
|
||||
\curl_setopt($handle, \CURLOPT_POSTFIELDS, \is_null($body) ? [] : $body);
|
||||
} else {
|
||||
if ($request->getHttpMethod() === RequestMethod::HEAD) {
|
||||
\curl_setopt($handle, \CURLOPT_NOBODY, \true);
|
||||
}
|
||||
\curl_setopt($handle, \CURLOPT_CUSTOMREQUEST, \strtoupper($request->getHttpMethod()));
|
||||
if (!\is_null($body)) {
|
||||
\curl_setopt($handle, \CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
}
|
||||
} elseif (\is_array($body)) {
|
||||
if (\strpos($queryUrl, '?') !== \false) {
|
||||
$queryUrl .= '&';
|
||||
} else {
|
||||
$queryUrl .= '?';
|
||||
}
|
||||
$queryUrl .= \http_build_query(Request::buildHTTPCurlQuery($body));
|
||||
}
|
||||
$curl_base_options = [
|
||||
\CURLOPT_URL => $queryUrl,
|
||||
\CURLOPT_RETURNTRANSFER => \true,
|
||||
\CURLOPT_FOLLOWLOCATION => \true,
|
||||
\CURLOPT_MAXREDIRS => 10,
|
||||
\CURLOPT_HTTPHEADER => $this->getFormattedHeaders($request),
|
||||
\CURLOPT_HEADER => \true,
|
||||
\CURLOPT_SSL_VERIFYPEER => $this->config->shouldVerifyPeer(),
|
||||
// CURLOPT_SSL_VERIFYHOST accepts only 0 (false) or 2 (true).
|
||||
// Future versions of libcurl will treat values 1 and 2 as equals
|
||||
\CURLOPT_SSL_VERIFYHOST => $this->config->shouldVerifyHost() === \false ? 0 : 2,
|
||||
// If an empty string, '', is set, a header containing all supported encoding types is sent
|
||||
\CURLOPT_ENCODING => '',
|
||||
];
|
||||
\curl_setopt_array($handle, $this->mergeCurlOptions($curl_base_options, $this->config->getCurlOpts()));
|
||||
if ($this->config->getTimeout() > 0) {
|
||||
\curl_setopt($handle, \CURLOPT_TIMEOUT, $this->config->getTimeout());
|
||||
}
|
||||
if ($this->config->getCookie() !== null) {
|
||||
\curl_setopt($handle, \CURLOPT_COOKIE, $this->config->getCookie());
|
||||
}
|
||||
if ($this->config->getCookieFile() !== null) {
|
||||
\curl_setopt($handle, \CURLOPT_COOKIEFILE, $this->config->getCookieFile());
|
||||
\curl_setopt($handle, \CURLOPT_COOKIEJAR, $this->config->getCookieFile());
|
||||
}
|
||||
if (!empty($this->config->getAuth()['user'])) {
|
||||
\curl_setopt_array($handle, [\CURLOPT_HTTPAUTH => $this->config->getAuth()['method'], \CURLOPT_USERPWD => $this->config->getAuth()['user'] . ':' . $this->config->getAuth()['pass']]);
|
||||
}
|
||||
$proxy = $this->config->getProxy();
|
||||
if (!empty($proxy['address'])) {
|
||||
\curl_setopt_array($handle, [\CURLOPT_PROXYTYPE => $proxy['type'], \CURLOPT_PROXY => $proxy['address'], \CURLOPT_PROXYPORT => $proxy['port'], \CURLOPT_HTTPPROXYTUNNEL => $proxy['tunnel'], \CURLOPT_PROXYAUTH => $proxy['auth']['method'], \CURLOPT_PROXYUSERPWD => $proxy['auth']['user'] . ':' . $proxy['auth']['pass']]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Halts program flow for given number of seconds, and microseconds
|
||||
*
|
||||
* @param float $seconds Seconds with upto 6 decimal places, here decimal part will be converted into microseconds
|
||||
*/
|
||||
protected function sleep(float $seconds)
|
||||
{
|
||||
$secs = (int) $seconds;
|
||||
// the fraction part of the $seconds will always be less than 1 sec, extracting micro seconds
|
||||
$microSecs = (int) (($seconds - $secs) * 1000000);
|
||||
\sleep($secs);
|
||||
\usleep($microSecs);
|
||||
}
|
||||
/**
|
||||
* Check if retries are enabled at global and request level,
|
||||
* also check whitelisted httpMethods, if retries are only enabled globally.
|
||||
*/
|
||||
protected function shouldRetryRequest(RequestInterface $request) : bool
|
||||
{
|
||||
switch ($request->getRetryOption()) {
|
||||
case RetryOption::ENABLE_RETRY:
|
||||
return $this->config->shouldEnableRetries();
|
||||
case RetryOption::USE_GLOBAL_SETTINGS:
|
||||
return $this->config->shouldEnableRetries() && \in_array(\strtoupper($request->getHttpMethod()), $this->config->getHttpMethodsToRetry(), \true);
|
||||
case RetryOption::DISABLE_RETRY:
|
||||
return \false;
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* Generate calculated wait time, and 0.0 if api should not be retried
|
||||
*
|
||||
* @param int|null $httpCode Http status code in response
|
||||
* @param array $headers Response headers
|
||||
* @param string $error Error returned by server
|
||||
* @param float $allowedWaitTime Remaining allowed wait time
|
||||
* @param int $retryCount Attempt number
|
||||
* @return float Wait time before sending the next apiCall
|
||||
*/
|
||||
protected function getRetryWaitTime(?int $httpCode, array $headers, string $error, float $allowedWaitTime, int $retryCount) : float
|
||||
{
|
||||
$retryWaitTime = 0.0;
|
||||
$retry_after = 0;
|
||||
if (empty($error)) {
|
||||
// Successful apiCall with some status code or with Retry-After header
|
||||
$headers_lower_keys = \array_change_key_case($headers);
|
||||
$retry_after_val = \key_exists('retry-after', $headers_lower_keys) ? $headers_lower_keys['retry-after'] : null;
|
||||
$retry_after = $this->getRetryAfterInSeconds($retry_after_val);
|
||||
$retry = isset($retry_after_val) || \in_array($httpCode, $this->config->getHttpStatusCodesToRetry(), \true);
|
||||
} else {
|
||||
$retry = $this->config->shouldRetryOnTimeout() && \curl_errno($this->handle) == \CURLE_OPERATION_TIMEDOUT;
|
||||
}
|
||||
// Calculate wait time only if max number of retries are not already attempted
|
||||
if ($retry && $retryCount < $this->config->getNumberOfRetries()) {
|
||||
// noise between 0 and 0.1 secs upto 6 decimal places
|
||||
$noise = \rand(0, 100000) / 1000000;
|
||||
// calculate wait time with exponential backoff and noise in seconds
|
||||
$waitTime = $this->config->getRetryInterval() * \pow($this->config->getBackOffFactor(), $retryCount) + $noise;
|
||||
// select maximum of waitTime and retry_after
|
||||
$waitTime = \floatval(\max($waitTime, $retry_after));
|
||||
if ($waitTime <= $allowedWaitTime) {
|
||||
// set retry wait time for next api call, only if its under allowed time
|
||||
$retryWaitTime = $waitTime;
|
||||
}
|
||||
}
|
||||
return $retryWaitTime;
|
||||
}
|
||||
/**
|
||||
* Returns the number of seconds by extracting them from $retry-after parameter
|
||||
*
|
||||
* @param int|string $retry_after Some numeric value in seconds, or it could be RFC1123
|
||||
* formatted datetime string
|
||||
* @return int Number of seconds specified by retry-after param
|
||||
*/
|
||||
protected function getRetryAfterInSeconds($retry_after) : int
|
||||
{
|
||||
if (isset($retry_after)) {
|
||||
if (\is_numeric($retry_after)) {
|
||||
return (int) $retry_after;
|
||||
// if value is already in seconds
|
||||
} else {
|
||||
// if value is a date time string in format RFC1123
|
||||
$retry_after_date = DateTime::createFromFormat('D, d M Y H:i:s O', $retry_after);
|
||||
// retry_after_date could either be undefined, or false, or a DateTime object (if valid format string)
|
||||
return !$retry_after_date ? 0 : $retry_after_date->getTimestamp() - \time();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* if PECL_HTTP is not available use a fallback function
|
||||
*
|
||||
* thanks to ricardovermeltfoort@gmail.com
|
||||
* http://php.net/manual/en/function.http-parse-headers.php#112986
|
||||
*/
|
||||
private function parseHeaders(string $raw_headers) : array
|
||||
{
|
||||
if (\function_exists('http_parse_headers')) {
|
||||
return \http_parse_headers($raw_headers);
|
||||
} else {
|
||||
$key = '';
|
||||
$headers = [];
|
||||
foreach (\explode("\n", $raw_headers) as $i => $h) {
|
||||
$h = \explode(':', $h, 2);
|
||||
if (isset($h[1])) {
|
||||
if (!isset($headers[$h[0]])) {
|
||||
$headers[$h[0]] = \trim($h[1]);
|
||||
} elseif (\is_array($headers[$h[0]])) {
|
||||
$headers[$h[0]] = \array_merge($headers[$h[0]], [\trim($h[1])]);
|
||||
} else {
|
||||
$headers[$h[0]] = \array_merge([$headers[$h[0]]], [\trim($h[1])]);
|
||||
}
|
||||
$key = $h[0];
|
||||
} else {
|
||||
if (\substr($h[0], 0, 1) == "\t") {
|
||||
$headers[$key] .= "\r\n\t" . \trim($h[0]);
|
||||
} elseif (empty($key)) {
|
||||
$headers[0] = \trim($h[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
public function getInfo(?int $option = null)
|
||||
{
|
||||
if (\is_null($option)) {
|
||||
return \curl_getinfo($this->handle);
|
||||
}
|
||||
return \curl_getinfo($this->handle, $option);
|
||||
}
|
||||
protected function getFormattedHeaders(RequestInterface $request) : array
|
||||
{
|
||||
$combinedHeaders = \array_change_key_case(\array_merge(['user-agent' => 'unirest-php/4.0', 'expect' => ''], $this->config->getDefaultHeaders(), $request->getHeaders()));
|
||||
$formattedHeaders = [];
|
||||
foreach ($combinedHeaders as $key => $val) {
|
||||
$key = \trim($key);
|
||||
if (!empty($request->getParameters()) && $key == 'content-type') {
|
||||
// special handling for form parameters i.e. removing content-type header
|
||||
// As, Curl will automatically add content-type for form params
|
||||
continue;
|
||||
}
|
||||
$formattedHeaders[] = "{$key}: {$val}";
|
||||
}
|
||||
return $formattedHeaders;
|
||||
}
|
||||
private function mergeCurlOptions(array &$existing_options, array $new_options) : array
|
||||
{
|
||||
$existing_options = $new_options + $existing_options;
|
||||
return $existing_options;
|
||||
}
|
||||
}
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\Unirest\Request;
|
||||
|
||||
use CURLFile;
|
||||
use Exception;
|
||||
class Body
|
||||
{
|
||||
/**
|
||||
* Prepares a file for upload. To be used inside the parameters declaration for a request.
|
||||
* @param string $filename The file path
|
||||
* @param string $mimetype MIME type
|
||||
* @param string $postName the file name
|
||||
* @return string|CURLFile
|
||||
*/
|
||||
public static function file(string $filename, string $mimetype = '', string $postName = '')
|
||||
{
|
||||
if (\class_exists('CURLFile')) {
|
||||
return new CURLFile($filename, $mimetype, $postName);
|
||||
}
|
||||
if (\function_exists('curl_file_create')) {
|
||||
return \curl_file_create($filename, $mimetype, $postName);
|
||||
}
|
||||
return \sprintf('@%s;filename=%s;type=%s', $filename, $postName ?: \basename($filename), $mimetype);
|
||||
}
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function json($data)
|
||||
{
|
||||
if (!\function_exists('json_encode')) {
|
||||
throw new Exception('JSON Extension not available');
|
||||
}
|
||||
return \json_encode($data);
|
||||
}
|
||||
public static function form($data)
|
||||
{
|
||||
if (\is_array($data) || \is_object($data) || $data instanceof \Traversable) {
|
||||
return \http_build_query(Request::buildHTTPCurlQuery($data));
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
public static function multipart($data, $files = \false) : array
|
||||
{
|
||||
if (\is_object($data)) {
|
||||
return \get_object_vars($data);
|
||||
}
|
||||
if (!\is_array($data)) {
|
||||
return [$data];
|
||||
}
|
||||
if ($files !== \false) {
|
||||
foreach ($files as $name => $file) {
|
||||
$data[$name] = \call_user_func([__CLASS__, 'File'], $file);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
Executable
+119
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace WPForms\Vendor\Unirest\Request;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Request\RequestMethod;
|
||||
use WPForms\Vendor\CoreInterfaces\Http\RetryOption;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
class Request implements RequestInterface
|
||||
{
|
||||
/**
|
||||
* This function is useful for serializing multidimensional arrays, and avoid getting
|
||||
* the 'Array to string conversion' notice
|
||||
* @param array|object $data array to flatten.
|
||||
* @param bool|string $parent parent key or false if no parent
|
||||
*/
|
||||
public static function buildHTTPCurlQuery($data, $parent = \false) : array
|
||||
{
|
||||
$result = [];
|
||||
if (\is_object($data)) {
|
||||
$data = \get_object_vars($data);
|
||||
}
|
||||
foreach ($data as $key => $value) {
|
||||
if (!empty($parent)) {
|
||||
$new_key = \sprintf('%s[%s]', $parent, $key);
|
||||
} else {
|
||||
$new_key = $key;
|
||||
}
|
||||
if (!$value instanceof \CURLFile and (\is_array($value) or \is_object($value))) {
|
||||
$result = \array_merge($result, self::buildHTTPCurlQuery($value, $new_key));
|
||||
} else {
|
||||
$result[$new_key] = $value;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
private $httpMethod;
|
||||
private $queryUrl;
|
||||
private $headers;
|
||||
private $body;
|
||||
private $retryOption;
|
||||
/**
|
||||
* @param string $url Query url
|
||||
* @param string $method Http method
|
||||
* @param array $headers Http request headers
|
||||
* @param mixed $body Http request body
|
||||
* @param string $retryOption To enable/disable httpMethods whitelist while retrying Api call
|
||||
*/
|
||||
public function __construct(string $url, string $method = RequestMethod::GET, array $headers = [], $body = null, string $retryOption = RetryOption::USE_GLOBAL_SETTINGS)
|
||||
{
|
||||
$this->queryUrl = $this->validateUrl($url);
|
||||
$this->httpMethod = $method;
|
||||
$this->headers = $headers;
|
||||
$this->body = $body;
|
||||
$this->retryOption = $retryOption;
|
||||
}
|
||||
/**
|
||||
* Validates and processes the given Url to ensure safe usage with cURL.
|
||||
* @param string $url The given Url to process
|
||||
* @return string Pre-processed Url as string
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function validateUrl(string $url) : string
|
||||
{
|
||||
//ensure that the urls are absolute
|
||||
$matchCount = \preg_match("#^(https?://[^/]+)#", $url, $matches);
|
||||
if ($matchCount == 0) {
|
||||
throw new InvalidArgumentException('Invalid Url format.');
|
||||
}
|
||||
//get the http protocol match
|
||||
$protocol = $matches[1];
|
||||
//remove redundant forward slashes
|
||||
$query = \substr($url, \strlen($protocol));
|
||||
$query = \preg_replace("#//+#", "/", $query);
|
||||
//return process url
|
||||
return $protocol . $query;
|
||||
}
|
||||
public function getHttpMethod() : string
|
||||
{
|
||||
return $this->httpMethod;
|
||||
}
|
||||
public function getQueryUrl() : string
|
||||
{
|
||||
return $this->queryUrl;
|
||||
}
|
||||
public function getHeaders() : array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
public function getParameters() : array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
public function getEncodedParameters() : array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
public function getMultipartParameters() : array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
public function getBody()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
public function getRetryOption() : string
|
||||
{
|
||||
return $this->retryOption;
|
||||
}
|
||||
public function convert() : Request
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
public function toApiException(string $message) : Exception
|
||||
{
|
||||
return new Exception($message);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace WPForms\Vendor\Unirest;
|
||||
|
||||
use WPForms\Vendor\CoreInterfaces\Core\Response\ResponseInterface;
|
||||
use WPForms\Vendor\CoreInterfaces\Sdk\ConverterInterface;
|
||||
class Response implements ResponseInterface
|
||||
{
|
||||
private $code;
|
||||
private $raw_body;
|
||||
private $body;
|
||||
private $headers;
|
||||
/**
|
||||
* @param int $code response code of the cURL request
|
||||
* @param string $raw_body the raw body of the cURL response
|
||||
* @param array $headers parsed headers array from cURL response
|
||||
* @param array $json_args arguments to pass to json_decode function
|
||||
*/
|
||||
public function __construct(int $code, string $raw_body, array $headers, array $json_args = [])
|
||||
{
|
||||
$this->code = $code;
|
||||
$this->headers = $headers;
|
||||
$this->raw_body = $raw_body;
|
||||
$this->body = $raw_body;
|
||||
// make sure raw_body is the first argument
|
||||
\array_unshift($json_args, $raw_body);
|
||||
if (\function_exists('json_decode')) {
|
||||
$json = \call_user_func_array('json_decode', $json_args);
|
||||
if (\json_last_error() === \JSON_ERROR_NONE) {
|
||||
$this->body = $json;
|
||||
}
|
||||
}
|
||||
}
|
||||
public function getStatusCode() : int
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
public function getHeaders() : array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
public function getRawBody() : string
|
||||
{
|
||||
return $this->raw_body;
|
||||
}
|
||||
public function getBody()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
public function convert(ConverterInterface $converter)
|
||||
{
|
||||
return $converter->createHttpResponse($this);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user