<?php declare(strict_types=1);
|
|
namespace WeChatPay;
|
|
use function preg_replace_callback_array;
|
use function strtolower;
|
use function implode;
|
use function array_filter;
|
|
use ArrayIterator;
|
|
/**
|
* Chainable the client for sending HTTP requests.
|
*/
|
final class Builder
|
{
|
/**
|
* Building & decorate the chainable `GuzzleHttp\Client`
|
*
|
* Minimum mandatory \$config parameters structure
|
* - mchid: string - The merchant ID
|
* - serial: string - The serial number of the merchant certificate
|
* - privateKey: \OpenSSLAsymmetricKey|\OpenSSLCertificate|object|resource|string - The merchant private key.
|
* - certs: array<string, \OpenSSLAsymmetricKey|\OpenSSLCertificate|object|resource|string> - The wechatpay platform serial and certificate(s), `[$serial => $cert]` pair
|
* - secret?: string - The secret key string (optional)
|
* - merchant?: array{key?: string, cert?: string} - The merchant private key and certificate array. (optional)
|
* - merchant<?key, string|string[]> - The merchant private key(file path string). (optional)
|
* - merchant<?cert, string|string[]> - The merchant certificate(file path string). (optional)
|
*
|
* ```php
|
* // usage samples
|
* $instance = Builder::factory([]);
|
* $res = $instance->chain('v3/merchantService/complaintsV2')->get(['debug' => true]);
|
* $res = $instance->chain('v3/merchant-service/complaint-notifications')->get(['debug' => true]);
|
* $instance->v3->merchantService->ComplaintNotifications->postAsync([])->wait();
|
* $instance->v3->certificates->getAsync()->then(function() {})->otherwise(function() {})->wait();
|
* ```
|
*
|
* @param array<string,string|int|bool|array|mixed> $config - `\GuzzleHttp\Client`, `APIv3` and `APIv2` configuration settings.
|
*/
|
public static function factory(array $config = []): BuilderChainable
|
{
|
return new class([], new ClientDecorator($config)) extends ArrayIterator implements BuilderChainable
|
{
|
use BuilderTrait;
|
|
/**
|
* Compose the chainable `ClientDecorator` instance, most starter with the tree root point
|
* @param string[] $input
|
* @param ?ClientDecoratorInterface $instance
|
*/
|
public function __construct(array $input = [], ?ClientDecoratorInterface $instance = null) {
|
parent::__construct($input, self::STD_PROP_LIST | self::ARRAY_AS_PROPS);
|
|
$this->setDriver($instance);
|
}
|
|
/**
|
* @var ClientDecoratorInterface $driver - The `ClientDecorator` instance
|
*/
|
protected $driver;
|
|
/**
|
* `$driver` setter
|
* @param ClientDecoratorInterface $instance - The `ClientDecorator` instance
|
*/
|
public function setDriver(ClientDecoratorInterface &$instance): BuilderChainable
|
{
|
$this->driver = $instance;
|
|
return $this;
|
}
|
|
/**
|
* @inheritDoc
|
*/
|
public function getDriver(): ClientDecoratorInterface
|
{
|
return $this->driver;
|
}
|
|
/**
|
* Normalize the `$thing` by the rules: `PascalCase` -> `camelCase`
|
* & `camelCase` -> `kebab-case`
|
* & `_placeholder_` -> `{placeholder}`
|
*
|
* @param string $thing - The string waiting for normalization
|
*
|
* @return string
|
*/
|
protected function normalize(string $thing = ''): string
|
{
|
return preg_replace_callback_array([
|
'#^[A-Z]#' => static function(array $piece): string { return strtolower($piece[0]); },
|
'#[A-Z]#' => static function(array $piece): string { return '-' . strtolower($piece[0]); },
|
'#^_(.*)_$#' => static function(array $piece): string { return '{' . $piece[1] . '}'; },
|
], $thing) ?? $thing;
|
}
|
|
/**
|
* URI pathname
|
*
|
* @param string $seperator - The URI seperator, default is slash(`/`) character
|
*
|
* @return string - The URI string
|
*/
|
protected function pathname(string $seperator = '/'): string
|
{
|
return implode($seperator, $this->simplized());
|
}
|
|
/**
|
* Only retrieve a copy array of the URI segments
|
*
|
* @return string[] - The URI segments array
|
*/
|
protected function simplized(): array
|
{
|
return array_filter($this->getArrayCopy(), static function($v) { return !($v instanceof BuilderChainable); });
|
}
|
|
/**
|
* @inheritDoc
|
*/
|
public function offsetGet($key): BuilderChainable
|
{
|
if (!$this->offsetExists($key)) {
|
$indices = $this->simplized();
|
$indices[] = $this->normalize($key);
|
$this->offsetSet($key, new self($indices, $this->getDriver()));
|
}
|
|
return parent::offsetGet($key);
|
}
|
|
/**
|
* @inheritDoc
|
*/
|
public function chain(string $segment): BuilderChainable
|
{
|
return $this->offsetGet($segment);
|
}
|
};
|
}
|
|
private function __construct()
|
{
|
// cannot be instantiated
|
}
|
}
|