wuliangbo
5 years ago
35 changed files with 3187 additions and 481 deletions
@ -0,0 +1,216 @@
@@ -0,0 +1,216 @@
|
||||
<?php |
||||
/** |
||||
* Created by PhpStorm. |
||||
* User: wuliangbo |
||||
* Date: 2020/1/6 |
||||
* Time: 15:09 |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel; |
||||
|
||||
use EasyAlipay\Kernel\Http\Response; |
||||
use EasyAlipay\Kernel\Traits\HasHttpRequests; |
||||
use GuzzleHttp\MessageFormatter; |
||||
use GuzzleHttp\Middleware; |
||||
use Psr\Http\Message\RequestInterface; |
||||
use Psr\Http\Message\ResponseInterface; |
||||
use Psr\Log\LogLevel; |
||||
|
||||
class BaseClient |
||||
{ |
||||
use HasHttpRequests { |
||||
request as performRequest; |
||||
} |
||||
/** |
||||
* @var \EasyAlipay\Kernel\ServiceContainer |
||||
*/ |
||||
protected $app; |
||||
|
||||
/** |
||||
* @var string |
||||
*/ |
||||
protected $baseUri; |
||||
|
||||
/** |
||||
* BaseClient constructor. |
||||
* |
||||
* @param \EasyAlipay\Kernel\ServiceContainer $app |
||||
*/ |
||||
public function __construct(ServiceContainer $app) |
||||
{ |
||||
$this->app = $app; |
||||
} |
||||
|
||||
/** |
||||
* GET request. |
||||
* |
||||
* @param string $url |
||||
* @param array $query |
||||
* |
||||
* @return \Psr\Http\Message\ResponseInterface|\EasyAlipay\Kernel\Support\Collection|array|object|string |
||||
* |
||||
* @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException |
||||
* @throws \GuzzleHttp\Exception\GuzzleException |
||||
*/ |
||||
public function httpGet(string $url, array $query = []) |
||||
{ |
||||
return $this->request($url, 'GET', ['query' => $query]); |
||||
} |
||||
|
||||
/** |
||||
* POST request. |
||||
* |
||||
* @param string $url |
||||
* @param array $data |
||||
* |
||||
* @return \Psr\Http\Message\ResponseInterface|\EasyAlipay\Kernel\Support\Collection|array|object|string |
||||
* |
||||
* @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException |
||||
* @throws \GuzzleHttp\Exception\GuzzleException |
||||
*/ |
||||
public function httpPost(string $url, array $data = []) |
||||
{ |
||||
return $this->request($url, 'POST', ['form_params' => $data]); |
||||
} |
||||
|
||||
/** |
||||
* JSON request. |
||||
* |
||||
* @param string $url |
||||
* @param array $data |
||||
* @param array $query |
||||
* |
||||
* @return \Psr\Http\Message\ResponseInterface|\EasyAlipay\Kernel\Support\Collection|array|object|string |
||||
* |
||||
* @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException |
||||
* @throws \GuzzleHttp\Exception\GuzzleException |
||||
*/ |
||||
public function httpPostJson(string $url, array $data = [], array $query = []) |
||||
{ |
||||
return $this->request($url, 'POST', ['query' => $query, 'json' => $data]); |
||||
} |
||||
|
||||
/** |
||||
* Upload file. |
||||
* |
||||
* @param string $url |
||||
* @param array $files |
||||
* @param array $form |
||||
* @param array $query |
||||
* |
||||
* @return \Psr\Http\Message\ResponseInterface|\EasyAlipay\Kernel\Support\Collection|array|object|string |
||||
* |
||||
* @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException |
||||
* @throws \GuzzleHttp\Exception\GuzzleException |
||||
*/ |
||||
public function httpUpload(string $url, array $files = [], array $form = [], array $query = []) |
||||
{ |
||||
$multipart = []; |
||||
|
||||
foreach ($files as $name => $path) { |
||||
$multipart[] = [ |
||||
'name' => $name, |
||||
'contents' => fopen($path, 'r'), |
||||
]; |
||||
} |
||||
|
||||
foreach ($form as $name => $contents) { |
||||
$multipart[] = compact('name', 'contents'); |
||||
} |
||||
|
||||
return $this->request($url, 'POST', ['query' => $query, 'multipart' => $multipart, 'connect_timeout' => 30, 'timeout' => 30, 'read_timeout' => 30]); |
||||
} |
||||
|
||||
/** |
||||
* @param string $url |
||||
* @param string $method |
||||
* @param array $options |
||||
* @param bool $returnRaw |
||||
* |
||||
* @return \Psr\Http\Message\ResponseInterface|\EasyAlipay\Kernel\Support\Collection|array|object|string |
||||
* |
||||
* @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException |
||||
* @throws \GuzzleHttp\Exception\GuzzleException |
||||
*/ |
||||
public function request(string $url, string $method = 'GET', array $options = [], $returnRaw = false) |
||||
{ |
||||
if (empty($this->middlewares)) { |
||||
$this->registerHttpMiddlewares(); |
||||
} |
||||
|
||||
$response = $this->performRequest($url, $method, $options); |
||||
|
||||
$this->app->events->dispatch(new Events\HttpResponseCreated($response)); |
||||
|
||||
return $returnRaw ? $response : $this->castResponseToType($response, $this->app->config->get('response_type')); |
||||
} |
||||
|
||||
/** |
||||
* @param string $url |
||||
* @param string $method |
||||
* @param array $options |
||||
* |
||||
* @return \EasyAlipay\Kernel\Http\Response |
||||
* |
||||
* @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException |
||||
* @throws \GuzzleHttp\Exception\GuzzleException |
||||
*/ |
||||
public function requestRaw(string $url, string $method = 'GET', array $options = []) |
||||
{ |
||||
return Response::buildFromPsrResponse($this->request($url, $method, $options, true)); |
||||
} |
||||
|
||||
/** |
||||
* Register Guzzle middlewares. |
||||
*/ |
||||
protected function registerHttpMiddlewares() |
||||
{ |
||||
// retry |
||||
$this->pushMiddleware($this->retryMiddleware(), 'retry'); |
||||
// log |
||||
$this->pushMiddleware($this->logMiddleware(), 'log'); |
||||
} |
||||
|
||||
/** |
||||
* Log the request. |
||||
* |
||||
* @return \Closure |
||||
*/ |
||||
protected function logMiddleware() |
||||
{ |
||||
$formatter = new MessageFormatter($this->app['config']['http.log_template'] ?? MessageFormatter::DEBUG); |
||||
|
||||
return Middleware::log($this->app['logger'], $formatter, LogLevel::DEBUG); |
||||
} |
||||
|
||||
/** |
||||
* Return retry middleware. |
||||
* |
||||
* @return \Closure |
||||
*/ |
||||
protected function retryMiddleware() |
||||
{ |
||||
return Middleware::retry(function ( |
||||
$retries, |
||||
RequestInterface $request, |
||||
ResponseInterface $response = null |
||||
) { |
||||
// Limit the number of retries to 2 |
||||
if ($retries < $this->app->config->get('http.max_retries', 1) && $response && $body = $response->getBody()) { |
||||
// Retry on server errors |
||||
$response = json_decode($body, true); |
||||
|
||||
if (!empty($response['errcode']) && in_array(abs($response['errcode']), [40001, 40014, 42001], true)) { |
||||
$this->accessToken->refresh(); |
||||
$this->app['logger']->debug('Retrying with refreshed access token.'); |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
|
||||
return false; |
||||
}, function () { |
||||
return abs($this->app->config->get('http.retry_delay', 500)); |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
<?php |
||||
/** |
||||
* Created by PhpStorm. |
||||
* User: wuliangbo |
||||
* Date: 2020/1/6 |
||||
* Time: 14:53 |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Contracts; |
||||
|
||||
use ArrayAccess; |
||||
|
||||
interface Arrayable extends ArrayAccess |
||||
{ |
||||
/** |
||||
* Get the instance as an array. |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function toArray(); |
||||
} |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Events; |
||||
|
||||
use EasyAlipay\Kernel\ServiceContainer; |
||||
|
||||
/** |
||||
* Class ApplicationInitialized. |
||||
* |
||||
* @author mingyoung <mingyoungcheung@gmail.com> |
||||
*/ |
||||
class ApplicationInitialized |
||||
{ |
||||
/** |
||||
* @var \EasyAlipay\Kernel\ServiceContainer |
||||
*/ |
||||
public $app; |
||||
|
||||
/** |
||||
* @param \EasyAlipay\Kernel\ServiceContainer $app |
||||
*/ |
||||
public function __construct(ServiceContainer $app) |
||||
{ |
||||
$this->app = $app; |
||||
} |
||||
} |
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Contracts; |
||||
|
||||
/** |
||||
* Interface EventHandlerInterface. |
||||
* |
||||
* @author mingyoung <mingyoungcheung@gmail.com> |
||||
*/ |
||||
interface EventHandlerInterface |
||||
{ |
||||
/** |
||||
* @param mixed $payload |
||||
*/ |
||||
public function handle($payload = null); |
||||
} |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Events; |
||||
|
||||
use Psr\Http\Message\ResponseInterface; |
||||
|
||||
/** |
||||
* Class HttpResponseCreated. |
||||
* |
||||
* @author mingyoung <mingyoungcheung@gmail.com> |
||||
*/ |
||||
class HttpResponseCreated |
||||
{ |
||||
/** |
||||
* @var \Psr\Http\Message\ResponseInterface |
||||
*/ |
||||
public $response; |
||||
|
||||
/** |
||||
* @param \Psr\Http\Message\ResponseInterface $response |
||||
*/ |
||||
public function __construct(ResponseInterface $response) |
||||
{ |
||||
$this->response = $response; |
||||
} |
||||
} |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Events; |
||||
|
||||
use Symfony\Component\HttpFoundation\Response; |
||||
|
||||
/** |
||||
* Class ServerGuardResponseCreated. |
||||
* |
||||
* @author mingyoung <mingyoungcheung@gmail.com> |
||||
*/ |
||||
class ServerGuardResponseCreated |
||||
{ |
||||
/** |
||||
* @var \Symfony\Component\HttpFoundation\Response |
||||
*/ |
||||
public $response; |
||||
|
||||
/** |
||||
* @param \Symfony\Component\HttpFoundation\Response $response |
||||
*/ |
||||
public function __construct(Response $response) |
||||
{ |
||||
$this->response = $response; |
||||
} |
||||
} |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
<?php |
||||
/** |
||||
* Created by PhpStorm. |
||||
* User: wuliangbo |
||||
* Date: 2020/1/6 |
||||
* Time: 14:26 |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Exceptions; |
||||
class BadRequestException extends Exception |
||||
{ |
||||
} |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
<?php |
||||
/** |
||||
* Created by PhpStorm. |
||||
* User: wuliangbo |
||||
* Date: 2020/1/6 |
||||
* Time: 14:25 |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Exceptions; |
||||
|
||||
use Exception as BaseException; |
||||
|
||||
class Exception extends BaseException |
||||
{ |
||||
} |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Exceptions; |
||||
|
||||
/** |
||||
* Class InvalidArgumentException. |
||||
* |
||||
* @author overtrue <i@overtrue.me> |
||||
*/ |
||||
class InvalidArgumentException extends Exception |
||||
{ |
||||
} |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Exceptions; |
||||
|
||||
/** |
||||
* Class InvalidConfigException. |
||||
* |
||||
* @author overtrue <i@overtrue.me> |
||||
*/ |
||||
class InvalidConfigException extends Exception |
||||
{ |
||||
} |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
<?php |
||||
/** |
||||
* Created by PhpStorm. |
||||
* User: wuliangbo |
||||
* Date: 2020/1/6 |
||||
* Time: 14:27 |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Exceptions; |
||||
class RuntimeException extends Exception |
||||
{ |
||||
|
||||
} |
@ -0,0 +1,134 @@
@@ -0,0 +1,134 @@
|
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace EasyAlipay\Kernel; |
||||
|
||||
use EasyAlipay\Kernel\Contracts\EventHandlerInterface; |
||||
use Pimple\Container; |
||||
use ReflectionClass; |
||||
|
||||
class Extension |
||||
{ |
||||
/** |
||||
* @var \Pimple\Container |
||||
*/ |
||||
protected $app; |
||||
|
||||
/** |
||||
* @var string |
||||
*/ |
||||
protected $manifestPath; |
||||
|
||||
/** |
||||
* @var array|null |
||||
*/ |
||||
protected $manifest; |
||||
|
||||
/** |
||||
* @param \Pimple\Container $app |
||||
*/ |
||||
public function __construct(Container $app) |
||||
{ |
||||
$this->app = $app; |
||||
$this->manifestPath = __DIR__.'/../extensions.php'; |
||||
} |
||||
|
||||
/** |
||||
* Get observers. |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function observers(): array |
||||
{ |
||||
if ($this->shouldIgnore()) { |
||||
return []; |
||||
} |
||||
|
||||
$observers = []; |
||||
|
||||
foreach ($this->getManifest() as $name => $extra) { |
||||
$observers = array_merge($observers, $extra['observers'] ?? []); |
||||
} |
||||
|
||||
return array_map([$this, 'listObserver'], array_filter($observers, [$this, 'validateObserver'])); |
||||
} |
||||
|
||||
/** |
||||
* @param mixed $observer |
||||
* |
||||
* @return bool |
||||
*/ |
||||
protected function isDisable($observer): bool |
||||
{ |
||||
return in_array($observer, $this->app->config->get('disable_observers', [])); |
||||
} |
||||
|
||||
/** |
||||
* Get the observers should be ignore. |
||||
* |
||||
* @return bool |
||||
*/ |
||||
protected function shouldIgnore(): bool |
||||
{ |
||||
return !file_exists($this->manifestPath) || $this->isDisable('*'); |
||||
} |
||||
|
||||
/** |
||||
* Validate the given observer. |
||||
* |
||||
* @param mixed $observer |
||||
* |
||||
* @return bool |
||||
* |
||||
* @throws \ReflectionException |
||||
*/ |
||||
protected function validateObserver($observer): bool |
||||
{ |
||||
return !$this->isDisable($observer) |
||||
&& (new ReflectionClass($observer))->implementsInterface(EventHandlerInterface::class) |
||||
&& $this->accessible($observer); |
||||
} |
||||
|
||||
/** |
||||
* Determine whether the given observer is accessible. |
||||
* |
||||
* @param string $observer |
||||
* |
||||
* @return bool |
||||
*/ |
||||
protected function accessible($observer): bool |
||||
{ |
||||
if (!method_exists($observer, 'getAccessor')) { |
||||
return true; |
||||
} |
||||
|
||||
return in_array(get_class($this->app), (array) $observer::getAccessor()); |
||||
} |
||||
|
||||
/** |
||||
* @param mixed $observer |
||||
* |
||||
* @return array |
||||
*/ |
||||
protected function listObserver($observer): array |
||||
{ |
||||
$condition = method_exists($observer, 'onCondition') ? $observer::onCondition() : '*'; |
||||
|
||||
return [$observer, $condition]; |
||||
} |
||||
|
||||
/** |
||||
* Get the easyalipay manifest. |
||||
* |
||||
* @return array |
||||
*/ |
||||
protected function getManifest(): array |
||||
{ |
||||
if (!is_null($this->manifest)) { |
||||
return $this->manifest; |
||||
} |
||||
|
||||
return $this->manifest = file_exists($this->manifestPath) ? require $this->manifestPath : []; |
||||
} |
||||
} |
@ -0,0 +1,82 @@
@@ -0,0 +1,82 @@
|
||||
<?php |
||||
/** |
||||
* Created by PhpStorm. |
||||
* User: wuliangbo |
||||
* Date: 2020/1/6 |
||||
* Time: 15:25 |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel; |
||||
|
||||
|
||||
/** |
||||
* 加密方法 |
||||
* @param string $str |
||||
* @return string |
||||
*/ |
||||
function encrypt($str, $screct_key) |
||||
{ |
||||
//AES, 128 模式加密数据 CBC |
||||
$screct_key = base64_decode($screct_key); |
||||
$str = trim($str); |
||||
$str = addPKCS7Padding($str); |
||||
|
||||
//设置全0的IV |
||||
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); |
||||
$iv = str_repeat("\0", $iv_size); |
||||
|
||||
$encrypt_str = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $screct_key, $str, MCRYPT_MODE_CBC, $iv); |
||||
return base64_encode($encrypt_str); |
||||
} |
||||
|
||||
/** |
||||
* 解密方法 |
||||
* @param string $str |
||||
* @return string |
||||
*/ |
||||
function decrypt($str, $screct_key) |
||||
{ |
||||
//AES, 128 模式加密数据 CBC |
||||
$str = base64_decode($str); |
||||
$screct_key = base64_decode($screct_key); |
||||
|
||||
//设置全0的IV |
||||
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); |
||||
$iv = str_repeat("\0", $iv_size); |
||||
|
||||
$decrypt_str = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $screct_key, $str, MCRYPT_MODE_CBC, $iv); |
||||
$decrypt_str = stripPKSC7Padding($decrypt_str); |
||||
return $decrypt_str; |
||||
} |
||||
|
||||
/** |
||||
* 填充算法 |
||||
* @param string $source |
||||
* @return string |
||||
*/ |
||||
function addPKCS7Padding($source) |
||||
{ |
||||
$source = trim($source); |
||||
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); |
||||
|
||||
$pad = $block - (strlen($source) % $block); |
||||
if ($pad <= $block) { |
||||
$char = chr($pad); |
||||
$source .= str_repeat($char, $pad); |
||||
} |
||||
return $source; |
||||
} |
||||
|
||||
/** |
||||
* 移去填充算法 |
||||
* @param string $source |
||||
* @return string |
||||
*/ |
||||
function stripPKSC7Padding($source) |
||||
{ |
||||
$char = substr($source, -1); |
||||
$num = ord($char); |
||||
if ($num == 62) return $source; |
||||
$source = substr($source, 0, -$num); |
||||
return $source; |
||||
} |
@ -0,0 +1,121 @@
@@ -0,0 +1,121 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Http; |
||||
|
||||
use EasyAlipay\Kernel\Support\Collection; |
||||
use EasyAlipay\Kernel\Support\XML; |
||||
use GuzzleHttp\Psr7\Response as GuzzleResponse; |
||||
use Psr\Http\Message\ResponseInterface; |
||||
|
||||
/** |
||||
* Class Response. |
||||
* |
||||
* @author overtrue <i@overtrue.me> |
||||
*/ |
||||
class Response extends GuzzleResponse |
||||
{ |
||||
/** |
||||
* @return string |
||||
*/ |
||||
public function getBodyContents() |
||||
{ |
||||
$this->getBody()->rewind(); |
||||
$contents = $this->getBody()->getContents(); |
||||
$this->getBody()->rewind(); |
||||
|
||||
return $contents; |
||||
} |
||||
|
||||
/** |
||||
* @param \Psr\Http\Message\ResponseInterface $response |
||||
* |
||||
* @return \EasyAlipay\Kernel\Http\Response |
||||
*/ |
||||
public static function buildFromPsrResponse(ResponseInterface $response) |
||||
{ |
||||
return new static( |
||||
$response->getStatusCode(), |
||||
$response->getHeaders(), |
||||
$response->getBody(), |
||||
$response->getProtocolVersion(), |
||||
$response->getReasonPhrase() |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Build to json. |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function toJson() |
||||
{ |
||||
return json_encode($this->toArray()); |
||||
} |
||||
|
||||
/** |
||||
* Build to array. |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function toArray() |
||||
{ |
||||
$content = $this->removeControlCharacters($this->getBodyContents()); |
||||
|
||||
if (false !== stripos($this->getHeaderLine('Content-Type'), 'xml') || 0 === stripos($content, '<xml')) { |
||||
return XML::parse($content); |
||||
} |
||||
|
||||
$array = json_decode($content, true, 512, JSON_BIGINT_AS_STRING); |
||||
|
||||
if (JSON_ERROR_NONE === json_last_error()) { |
||||
return (array) $array; |
||||
} |
||||
|
||||
return []; |
||||
} |
||||
|
||||
/** |
||||
* Get collection data. |
||||
* |
||||
* @return \EasyAlipay\Kernel\Support\Collection |
||||
*/ |
||||
public function toCollection() |
||||
{ |
||||
return new Collection($this->toArray()); |
||||
} |
||||
|
||||
/** |
||||
* @return object |
||||
*/ |
||||
public function toObject() |
||||
{ |
||||
return json_decode($this->toJson()); |
||||
} |
||||
|
||||
/** |
||||
* @return bool|string |
||||
*/ |
||||
public function __toString() |
||||
{ |
||||
return $this->getBodyContents(); |
||||
} |
||||
|
||||
/** |
||||
* @param string $content |
||||
* |
||||
* @return string |
||||
*/ |
||||
protected function removeControlCharacters(string $content) |
||||
{ |
||||
return \preg_replace('/[\x00-\x1F\x80-\x9F]/u', '', $content); |
||||
} |
||||
} |
@ -0,0 +1,86 @@
@@ -0,0 +1,86 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Http; |
||||
|
||||
use EasyAlipay\Kernel\Exceptions\InvalidArgumentException; |
||||
use EasyAlipay\Kernel\Exceptions\RuntimeException; |
||||
use EasyAlipay\Kernel\Support\File; |
||||
|
||||
/** |
||||
* Class StreamResponse. |
||||
* |
||||
* @author overtrue <i@overtrue.me> |
||||
*/ |
||||
class StreamResponse extends Response |
||||
{ |
||||
/** |
||||
* @param string $directory |
||||
* @param string $filename |
||||
* @param bool $appendSuffix |
||||
* |
||||
* @return bool|int |
||||
* |
||||
* @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException |
||||
* @throws \EasyAlipay\Kernel\Exceptions\RuntimeException |
||||
*/ |
||||
public function save(string $directory, string $filename = '', bool $appendSuffix = true) |
||||
{ |
||||
$this->getBody()->rewind(); |
||||
|
||||
$directory = rtrim($directory, '/'); |
||||
|
||||
if (!is_dir($directory)) { |
||||
mkdir($directory, 0755, true); // @codeCoverageIgnore |
||||
} |
||||
|
||||
if (!is_writable($directory)) { |
||||
throw new InvalidArgumentException(sprintf("'%s' is not writable.", $directory)); |
||||
} |
||||
|
||||
$contents = $this->getBody()->getContents(); |
||||
|
||||
if (empty($contents) || '{' === $contents[0]) { |
||||
throw new RuntimeException('Invalid media response content.'); |
||||
} |
||||
|
||||
if (empty($filename)) { |
||||
if (preg_match('/filename="(?<filename>.*?)"/', $this->getHeaderLine('Content-Disposition'), $match)) { |
||||
$filename = $match['filename']; |
||||
} else { |
||||
$filename = md5($contents); |
||||
} |
||||
} |
||||
|
||||
if ($appendSuffix && empty(pathinfo($filename, PATHINFO_EXTENSION))) { |
||||
$filename .= File::getStreamExt($contents); |
||||
} |
||||
|
||||
file_put_contents($directory.'/'.$filename, $contents); |
||||
|
||||
return $filename; |
||||
} |
||||
|
||||
/** |
||||
* @param string $directory |
||||
* @param string $filename |
||||
* @param bool $appendSuffix |
||||
* |
||||
* @return bool|int |
||||
* |
||||
* @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException |
||||
* @throws \EasyAlipay\Kernel\Exceptions\RuntimeException |
||||
*/ |
||||
public function saveAs(string $directory, string $filename, bool $appendSuffix = true) |
||||
{ |
||||
return $this->save($directory, $filename, $appendSuffix); |
||||
} |
||||
} |
@ -0,0 +1,608 @@
@@ -0,0 +1,608 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Log; |
||||
|
||||
use EasyAlipay\Kernel\ServiceContainer; |
||||
use InvalidArgumentException; |
||||
use Monolog\Formatter\LineFormatter; |
||||
use Monolog\Handler\ErrorLogHandler; |
||||
use Monolog\Handler\FormattableHandlerInterface; |
||||
use Monolog\Handler\HandlerInterface; |
||||
use Monolog\Handler\RotatingFileHandler; |
||||
use Monolog\Handler\SlackWebhookHandler; |
||||
use Monolog\Handler\StreamHandler; |
||||
use Monolog\Handler\SyslogHandler; |
||||
use Monolog\Handler\WhatFailureGroupHandler; |
||||
use Monolog\Logger as Monolog; |
||||
use Psr\Log\LoggerInterface; |
||||
|
||||
/** |
||||
* Class LogManager. |
||||
* |
||||
* @author overtrue <i@overtrue.me> |
||||
*/ |
||||
class LogManager implements LoggerInterface |
||||
{ |
||||
/** |
||||
* @var \EasyAlipay\Kernel\ServiceContainer |
||||
*/ |
||||
protected $app; |
||||
|
||||
/** |
||||
* The array of resolved channels. |
||||
* |
||||
* @var array |
||||
*/ |
||||
protected $channels = []; |
||||
|
||||
/** |
||||
* The registered custom driver creators. |
||||
* |
||||
* @var array |
||||
*/ |
||||
protected $customCreators = []; |
||||
|
||||
/** |
||||
* The Log levels. |
||||
* |
||||
* @var array |
||||
*/ |
||||
protected $levels = [ |
||||
'debug' => Monolog::DEBUG, |
||||
'info' => Monolog::INFO, |
||||
'notice' => Monolog::NOTICE, |
||||
'warning' => Monolog::WARNING, |
||||
'error' => Monolog::ERROR, |
||||
'critical' => Monolog::CRITICAL, |
||||
'alert' => Monolog::ALERT, |
||||
'emergency' => Monolog::EMERGENCY, |
||||
]; |
||||
|
||||
/** |
||||
* LogManager constructor. |
||||
* |
||||
* @param \EasyAlipay\Kernel\ServiceContainer $app |
||||
*/ |
||||
public function __construct(ServiceContainer $app) |
||||
{ |
||||
$this->app = $app; |
||||
} |
||||
|
||||
/** |
||||
* Create a new, on-demand aggregate logger instance. |
||||
* |
||||
* @param array $channels |
||||
* @param string|null $channel |
||||
* |
||||
* @return \Psr\Log\LoggerInterface |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function stack(array $channels, $channel = null) |
||||
{ |
||||
return $this->createStackDriver(compact('channels', 'channel')); |
||||
} |
||||
|
||||
/** |
||||
* Get a log channel instance. |
||||
* |
||||
* @param string|null $channel |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function channel($channel = null) |
||||
{ |
||||
return $this->driver($channel); |
||||
} |
||||
|
||||
/** |
||||
* Get a log driver instance. |
||||
* |
||||
* @param string|null $driver |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function driver($driver = null) |
||||
{ |
||||
return $this->get($driver ?? $this->getDefaultDriver()); |
||||
} |
||||
|
||||
/** |
||||
* Attempt to get the log from the local cache. |
||||
* |
||||
* @param string $name |
||||
* |
||||
* @return \Psr\Log\LoggerInterface |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
protected function get($name) |
||||
{ |
||||
try { |
||||
return $this->channels[$name] ?? ($this->channels[$name] = $this->resolve($name)); |
||||
} catch (\Throwable $e) { |
||||
$logger = $this->createEmergencyLogger(); |
||||
|
||||
$logger->emergency('Unable to create configured logger. Using emergency logger.', [ |
||||
'exception' => $e, |
||||
]); |
||||
|
||||
return $logger; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Resolve the given log instance by name. |
||||
* |
||||
* @param string $name |
||||
* |
||||
* @return \Psr\Log\LoggerInterface |
||||
* |
||||
* @throws InvalidArgumentException |
||||
*/ |
||||
protected function resolve($name) |
||||
{ |
||||
$config = $this->app['config']->get(\sprintf('log.channels.%s', $name)); |
||||
|
||||
if (is_null($config)) { |
||||
throw new InvalidArgumentException(\sprintf('Log [%s] is not defined.', $name)); |
||||
} |
||||
|
||||
if (isset($this->customCreators[$config['driver']])) { |
||||
return $this->callCustomCreator($config); |
||||
} |
||||
|
||||
$driverMethod = 'create'.ucfirst($config['driver']).'Driver'; |
||||
|
||||
if (method_exists($this, $driverMethod)) { |
||||
return $this->{$driverMethod}($config); |
||||
} |
||||
|
||||
throw new InvalidArgumentException(\sprintf('Driver [%s] is not supported.', $config['driver'])); |
||||
} |
||||
|
||||
/** |
||||
* Create an emergency log handler to avoid white screens of death. |
||||
* |
||||
* @return \Monolog\Logger |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
protected function createEmergencyLogger() |
||||
{ |
||||
return new Monolog('EasyAlipay', $this->prepareHandlers([new StreamHandler( |
||||
\sys_get_temp_dir().'/easyalipay/easyalipay.log', |
||||
$this->level(['level' => 'debug']) |
||||
)])); |
||||
} |
||||
|
||||
/** |
||||
* Call a custom driver creator. |
||||
* |
||||
* @param array $config |
||||
* |
||||
* @return mixed |
||||
*/ |
||||
protected function callCustomCreator(array $config) |
||||
{ |
||||
return $this->customCreators[$config['driver']]($this->app, $config); |
||||
} |
||||
|
||||
/** |
||||
* Create an aggregate log driver instance. |
||||
* |
||||
* @param array $config |
||||
* |
||||
* @return \Monolog\Logger |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
protected function createStackDriver(array $config) |
||||
{ |
||||
$handlers = []; |
||||
|
||||
foreach ($config['channels'] ?? [] as $channel) { |
||||
$handlers = \array_merge($handlers, $this->channel($channel)->getHandlers()); |
||||
} |
||||
|
||||
if ($config['ignore_exceptions'] ?? false) { |
||||
$handlers = [new WhatFailureGroupHandler($handlers)]; |
||||
} |
||||
|
||||
return new Monolog($this->parseChannel($config), $handlers); |
||||
} |
||||
|
||||
/** |
||||
* Create an instance of the single file log driver. |
||||
* |
||||
* @param array $config |
||||
* |
||||
* @return \Psr\Log\LoggerInterface |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
protected function createSingleDriver(array $config) |
||||
{ |
||||
return new Monolog($this->parseChannel($config), [ |
||||
$this->prepareHandler(new StreamHandler( |
||||
$config['path'], |
||||
$this->level($config), |
||||
$config['bubble'] ?? true, |
||||
$config['permission'] ?? null, |
||||
$config['locking'] ?? false |
||||
), $config), |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* Create an instance of the daily file log driver. |
||||
* |
||||
* @param array $config |
||||
* |
||||
* @return \Psr\Log\LoggerInterface |
||||
*/ |
||||
protected function createDailyDriver(array $config) |
||||
{ |
||||
return new Monolog($this->parseChannel($config), [ |
||||
$this->prepareHandler(new RotatingFileHandler( |
||||
$config['path'], |
||||
$config['days'] ?? 7, |
||||
$this->level($config), |
||||
$config['bubble'] ?? true, |
||||
$config['permission'] ?? null, |
||||
$config['locking'] ?? false |
||||
), $config), |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* Create an instance of the Slack log driver. |
||||
* |
||||
* @param array $config |
||||
* |
||||
* @return \Psr\Log\LoggerInterface |
||||
*/ |
||||
protected function createSlackDriver(array $config) |
||||
{ |
||||
return new Monolog($this->parseChannel($config), [ |
||||
$this->prepareHandler(new SlackWebhookHandler( |
||||
$config['url'], |
||||
$config['channel'] ?? null, |
||||
$config['username'] ?? 'EasyAliPay', |
||||
$config['attachment'] ?? true, |
||||
$config['emoji'] ?? ':boom:', |
||||
$config['short'] ?? false, |
||||
$config['context'] ?? true, |
||||
$this->level($config), |
||||
$config['bubble'] ?? true, |
||||
$config['exclude_fields'] ?? [] |
||||
), $config), |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* Create an instance of the syslog log driver. |
||||
* |
||||
* @param array $config |
||||
* |
||||
* @return \Psr\Log\LoggerInterface |
||||
*/ |
||||
protected function createSyslogDriver(array $config) |
||||
{ |
||||
return new Monolog($this->parseChannel($config), [ |
||||
$this->prepareHandler(new SyslogHandler( |
||||
'EasyAlipay', |
||||
$config['facility'] ?? LOG_USER, |
||||
$this->level($config) |
||||
), $config), |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* Create an instance of the "error log" log driver. |
||||
* |
||||
* @param array $config |
||||
* |
||||
* @return \Psr\Log\LoggerInterface |
||||
*/ |
||||
protected function createErrorlogDriver(array $config) |
||||
{ |
||||
return new Monolog($this->parseChannel($config), [ |
||||
$this->prepareHandler( |
||||
new ErrorLogHandler( |
||||
$config['type'] ?? ErrorLogHandler::OPERATING_SYSTEM, |
||||
$this->level($config) |
||||
) |
||||
), |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* Prepare the handlers for usage by Monolog. |
||||
* |
||||
* @param array $handlers |
||||
* |
||||
* @return array |
||||
*/ |
||||
protected function prepareHandlers(array $handlers) |
||||
{ |
||||
foreach ($handlers as $key => $handler) { |
||||
$handlers[$key] = $this->prepareHandler($handler); |
||||
} |
||||
|
||||
return $handlers; |
||||
} |
||||
|
||||
/** |
||||
* Prepare the handler for usage by Monolog. |
||||
* |
||||
* @param \Monolog\Handler\HandlerInterface $handler |
||||
* |
||||
* @return \Monolog\Handler\HandlerInterface |
||||
*/ |
||||
protected function prepareHandler(HandlerInterface $handler, array $config = []) |
||||
{ |
||||
if (!isset($config['formatter'])) { |
||||
if ($handler instanceof FormattableHandlerInterface) { |
||||
$handler->setFormatter($this->formatter()); |
||||
} |
||||
} |
||||
|
||||
return $handler; |
||||
} |
||||
|
||||
/** |
||||
* Get a Monolog formatter instance. |
||||
* |
||||
* @return \Monolog\Formatter\FormatterInterface |
||||
*/ |
||||
protected function formatter() |
||||
{ |
||||
$formatter = new LineFormatter(null, null, true, true); |
||||
$formatter->includeStacktraces(); |
||||
|
||||
return $formatter; |
||||
} |
||||
|
||||
/** |
||||
* Extract the log channel from the given configuration. |
||||
* |
||||
* @param array $config |
||||
* |
||||
* @return string |
||||
*/ |
||||
protected function parseChannel(array $config) |
||||
{ |
||||
return $config['name'] ?? 'EasyAlipay'; |
||||
} |
||||
|
||||
/** |
||||
* Parse the string level into a Monolog constant. |
||||
* |
||||
* @param array $config |
||||
* |
||||
* @return int |
||||
* |
||||
* @throws InvalidArgumentException |
||||
*/ |
||||
protected function level(array $config) |
||||
{ |
||||
$level = $config['level'] ?? 'debug'; |
||||
|
||||
if (isset($this->levels[$level])) { |
||||
return $this->levels[$level]; |
||||
} |
||||
|
||||
throw new InvalidArgumentException('Invalid log level.'); |
||||
} |
||||
|
||||
/** |
||||
* Get the default log driver name. |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function getDefaultDriver() |
||||
{ |
||||
return $this->app['config']['log.default']; |
||||
} |
||||
|
||||
/** |
||||
* Set the default log driver name. |
||||
* |
||||
* @param string $name |
||||
*/ |
||||
public function setDefaultDriver($name) |
||||
{ |
||||
$this->app['config']['log.default'] = $name; |
||||
} |
||||
|
||||
/** |
||||
* Register a custom driver creator Closure. |
||||
* |
||||
* @param string $driver |
||||
* @param \Closure $callback |
||||
* |
||||
* @return $this |
||||
*/ |
||||
public function extend($driver, \Closure $callback) |
||||
{ |
||||
$this->customCreators[$driver] = $callback->bindTo($this, $this); |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* System is unusable. |
||||
* |
||||
* @param string $message |
||||
* @param array $context |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function emergency($message, array $context = []) |
||||
{ |
||||
return $this->driver()->emergency($message, $context); |
||||
} |
||||
|
||||
/** |
||||
* Action must be taken immediately. |
||||
* |
||||
* Example: Entire website down, database unavailable, etc. This should |
||||
* trigger the SMS alerts and wake you up. |
||||
* |
||||
* @param string $message |
||||
* @param array $context |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function alert($message, array $context = []) |
||||
{ |
||||
return $this->driver()->alert($message, $context); |
||||
} |
||||
|
||||
/** |
||||
* Critical conditions. |
||||
* |
||||
* Example: Application component unavailable, unexpected exception. |
||||
* |
||||
* @param string $message |
||||
* @param array $context |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function critical($message, array $context = []) |
||||
{ |
||||
return $this->driver()->critical($message, $context); |
||||
} |
||||
|
||||
/** |
||||
* Runtime errors that do not require immediate action but should typically |
||||
* be logged and monitored. |
||||
* |
||||
* @param string $message |
||||
* @param array $context |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function error($message, array $context = []) |
||||
{ |
||||
return $this->driver()->error($message, $context); |
||||
} |
||||
|
||||
/** |
||||
* Exceptional occurrences that are not errors. |
||||
* |
||||
* Example: Use of deprecated APIs, poor use of an API, undesirable things |
||||
* that are not necessarily wrong. |
||||
* |
||||
* @param string $message |
||||
* @param array $context |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function warning($message, array $context = []) |
||||
{ |
||||
return $this->driver()->warning($message, $context); |
||||
} |
||||
|
||||
/** |
||||
* Normal but significant events. |
||||
* |
||||
* @param string $message |
||||
* @param array $context |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function notice($message, array $context = []) |
||||
{ |
||||
return $this->driver()->notice($message, $context); |
||||
} |
||||
|
||||
/** |
||||
* Interesting events. |
||||
* |
||||
* Example: User logs in, SQL logs. |
||||
* |
||||
* @param string $message |
||||
* @param array $context |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function info($message, array $context = []) |
||||
{ |
||||
return $this->driver()->info($message, $context); |
||||
} |
||||
|
||||
/** |
||||
* Detailed debug information. |
||||
* |
||||
* @param string $message |
||||
* @param array $context |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function debug($message, array $context = []) |
||||
{ |
||||
return $this->driver()->debug($message, $context); |
||||
} |
||||
|
||||
/** |
||||
* Logs with an arbitrary level. |
||||
* |
||||
* @param mixed $level |
||||
* @param string $message |
||||
* @param array $context |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function log($level, $message, array $context = []) |
||||
{ |
||||
return $this->driver()->log($level, $message, $context); |
||||
} |
||||
|
||||
/** |
||||
* Dynamically call the default driver instance. |
||||
* |
||||
* @param string $method |
||||
* @param array $parameters |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \Exception |
||||
*/ |
||||
public function __call($method, $parameters) |
||||
{ |
||||
return $this->driver()->$method(...$parameters); |
||||
} |
||||
} |
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Providers; |
||||
|
||||
use Pimple\Container; |
||||
use Pimple\ServiceProviderInterface; |
||||
use Symfony\Component\EventDispatcher\EventDispatcher; |
||||
|
||||
/** |
||||
* Class EventDispatcherServiceProvider. |
||||
* |
||||
* @author mingyoung <mingyoungcheung@gmail.com> |
||||
*/ |
||||
class EventDispatcherServiceProvider implements ServiceProviderInterface |
||||
{ |
||||
/** |
||||
* Registers services on the given container. |
||||
* |
||||
* This method should only be used to configure services and parameters. |
||||
* It should not get services. |
||||
* |
||||
* @param Container $pimple A container instance |
||||
*/ |
||||
public function register(Container $pimple) |
||||
{ |
||||
$pimple['events'] = function ($app) { |
||||
$dispatcher = new EventDispatcher(); |
||||
|
||||
foreach ($app->config->get('events.listen', []) as $event => $listeners) { |
||||
foreach ($listeners as $listener) { |
||||
$dispatcher->addListener($event, $listener); |
||||
} |
||||
} |
||||
|
||||
return $dispatcher; |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Providers; |
||||
|
||||
use EasyAlipay\Kernel\Extension; |
||||
use Pimple\Container; |
||||
use Pimple\ServiceProviderInterface; |
||||
|
||||
/** |
||||
* Class ExtensionServiceProvider. |
||||
* |
||||
* @author overtrue <i@overtrue.me> |
||||
*/ |
||||
class ExtensionServiceProvider implements ServiceProviderInterface |
||||
{ |
||||
/** |
||||
* Registers services on the given container. |
||||
* |
||||
* This method should only be used to configure services and parameters. |
||||
* It should not get services. |
||||
* |
||||
* @param Container $pimple A container instance |
||||
*/ |
||||
public function register(Container $pimple) |
||||
{ |
||||
$pimple['extension'] = function ($app) { |
||||
return new Extension($app); |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Providers; |
||||
|
||||
use GuzzleHttp\Client; |
||||
use Pimple\Container; |
||||
use Pimple\ServiceProviderInterface; |
||||
|
||||
/** |
||||
* Class HttpClientServiceProvider. |
||||
* |
||||
* @author overtrue <i@overtrue.me> |
||||
*/ |
||||
class HttpClientServiceProvider implements ServiceProviderInterface |
||||
{ |
||||
/** |
||||
* Registers services on the given container. |
||||
* |
||||
* This method should only be used to configure services and parameters. |
||||
* It should not get services. |
||||
* |
||||
* @param Container $pimple A container instance |
||||
*/ |
||||
public function register(Container $pimple) |
||||
{ |
||||
$pimple['http_client'] = function ($app) { |
||||
return new Client($app['config']->get('http', [])); |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Providers; |
||||
|
||||
use EasyAlipay\Kernel\Log\LogManager; |
||||
use Pimple\Container; |
||||
use Pimple\ServiceProviderInterface; |
||||
|
||||
/** |
||||
* Class LoggingServiceProvider. |
||||
* |
||||
* @author overtrue <i@overtrue.me> |
||||
*/ |
||||
class LogServiceProvider implements ServiceProviderInterface |
||||
{ |
||||
/** |
||||
* Registers services on the given container. |
||||
* |
||||
* This method should only be used to configure services and parameters. |
||||
* It should not get services. |
||||
* |
||||
* @param Container $pimple A container instance |
||||
*/ |
||||
public function register(Container $pimple) |
||||
{ |
||||
$pimple['logger'] = $pimple['log'] = function ($app) { |
||||
$config = $this->formatLogConfig($app); |
||||
|
||||
if (!empty($config)) { |
||||
$app->rebind('config', $app['config']->merge($config)); |
||||
} |
||||
|
||||
return new LogManager($app); |
||||
}; |
||||
} |
||||
|
||||
public function formatLogConfig($app) |
||||
{ |
||||
if (!empty($app['config']->get('log.channels'))) { |
||||
return $app['config']->get('log'); |
||||
} |
||||
|
||||
if (empty($app['config']->get('log'))) { |
||||
return [ |
||||
'log' => [ |
||||
'default' => 'errorlog', |
||||
'channels' => [ |
||||
'errorlog' => [ |
||||
'driver' => 'errorlog', |
||||
'level' => 'debug', |
||||
], |
||||
], |
||||
], |
||||
]; |
||||
} |
||||
|
||||
return [ |
||||
'log' => [ |
||||
'default' => 'single', |
||||
'channels' => [ |
||||
'single' => [ |
||||
'driver' => 'single', |
||||
'path' => $app['config']->get('log.file') ?: \sys_get_temp_dir().'/logs/easyalipay.log', |
||||
'level' => $app['config']->get('log.level', 'debug'), |
||||
], |
||||
], |
||||
], |
||||
]; |
||||
} |
||||
} |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Providers; |
||||
|
||||
use Pimple\Container; |
||||
use Pimple\ServiceProviderInterface; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
|
||||
/** |
||||
* Class RequestServiceProvider. |
||||
* |
||||
* @author overtrue <i@overtrue.me> |
||||
*/ |
||||
class RequestServiceProvider implements ServiceProviderInterface |
||||
{ |
||||
/** |
||||
* Registers services on the given container. |
||||
* |
||||
* This method should only be used to configure services and parameters. |
||||
* It should not get services. |
||||
* |
||||
* @param Container $pimple A container instance |
||||
*/ |
||||
public function register(Container $pimple) |
||||
{ |
||||
$pimple['request'] = function () { |
||||
return Request::createFromGlobals(); |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,466 @@
@@ -0,0 +1,466 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Support; |
||||
|
||||
/** |
||||
* Array helper from Illuminate\Support\Arr. |
||||
*/ |
||||
class Arr |
||||
{ |
||||
/** |
||||
* Add an element to an array using "dot" notation if it doesn't exist. |
||||
* |
||||
* @param array $array |
||||
* @param string $key |
||||
* @param mixed $value |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function add(array $array, $key, $value) |
||||
{ |
||||
if (is_null(static::get($array, $key))) { |
||||
static::set($array, $key, $value); |
||||
} |
||||
|
||||
return $array; |
||||
} |
||||
|
||||
/** |
||||
* Cross join the given arrays, returning all possible permutations. |
||||
* |
||||
* @param array ...$arrays |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function crossJoin(...$arrays) |
||||
{ |
||||
$results = [[]]; |
||||
|
||||
foreach ($arrays as $index => $array) { |
||||
$append = []; |
||||
|
||||
foreach ($results as $product) { |
||||
foreach ($array as $item) { |
||||
$product[$index] = $item; |
||||
|
||||
$append[] = $product; |
||||
} |
||||
} |
||||
|
||||
$results = $append; |
||||
} |
||||
|
||||
return $results; |
||||
} |
||||
|
||||
/** |
||||
* Divide an array into two arrays. One with keys and the other with values. |
||||
* |
||||
* @param array $array |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function divide(array $array) |
||||
{ |
||||
return [array_keys($array), array_values($array)]; |
||||
} |
||||
|
||||
/** |
||||
* Flatten a multi-dimensional associative array with dots. |
||||
* |
||||
* @param array $array |
||||
* @param string $prepend |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function dot(array $array, $prepend = '') |
||||
{ |
||||
$results = []; |
||||
|
||||
foreach ($array as $key => $value) { |
||||
if (is_array($value) && !empty($value)) { |
||||
$results = array_merge($results, static::dot($value, $prepend.$key.'.')); |
||||
} else { |
||||
$results[$prepend.$key] = $value; |
||||
} |
||||
} |
||||
|
||||
return $results; |
||||
} |
||||
|
||||
/** |
||||
* Get all of the given array except for a specified array of items. |
||||
* |
||||
* @param array $array |
||||
* @param array|string $keys |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function except(array $array, $keys) |
||||
{ |
||||
static::forget($array, $keys); |
||||
|
||||
return $array; |
||||
} |
||||
|
||||
/** |
||||
* Determine if the given key exists in the provided array. |
||||
* |
||||
* @param array $array |
||||
* @param string|int $key |
||||
* |
||||
* @return bool |
||||
*/ |
||||
public static function exists(array $array, $key) |
||||
{ |
||||
return array_key_exists($key, $array); |
||||
} |
||||
|
||||
/** |
||||
* Return the first element in an array passing a given truth test. |
||||
* |
||||
* @param array $array |
||||
* @param callable|null $callback |
||||
* @param mixed $default |
||||
* |
||||
* @return mixed |
||||
*/ |
||||
public static function first(array $array, callable $callback = null, $default = null) |
||||
{ |
||||
if (is_null($callback)) { |
||||
if (empty($array)) { |
||||
return $default; |
||||
} |
||||
|
||||
foreach ($array as $item) { |
||||
return $item; |
||||
} |
||||
} |
||||
|
||||
foreach ($array as $key => $value) { |
||||
if (call_user_func($callback, $value, $key)) { |
||||
return $value; |
||||
} |
||||
} |
||||
|
||||
return $default; |
||||
} |
||||
|
||||
/** |
||||
* Return the last element in an array passing a given truth test. |
||||
* |
||||
* @param array $array |
||||
* @param callable|null $callback |
||||
* @param mixed $default |
||||
* |
||||
* @return mixed |
||||
*/ |
||||
public static function last(array $array, callable $callback = null, $default = null) |
||||
{ |
||||
if (is_null($callback)) { |
||||
return empty($array) ? $default : end($array); |
||||
} |
||||
|
||||
return static::first(array_reverse($array, true), $callback, $default); |
||||
} |
||||
|
||||
/** |
||||
* Flatten a multi-dimensional array into a single level. |
||||
* |
||||
* @param array $array |
||||
* @param int $depth |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function flatten(array $array, $depth = INF) |
||||
{ |
||||
return array_reduce($array, function ($result, $item) use ($depth) { |
||||
$item = $item instanceof Collection ? $item->all() : $item; |
||||
|
||||
if (!is_array($item)) { |
||||
return array_merge($result, [$item]); |
||||
} elseif (1 === $depth) { |
||||
return array_merge($result, array_values($item)); |
||||
} |
||||
|
||||
return array_merge($result, static::flatten($item, $depth - 1)); |
||||
}, []); |
||||
} |
||||
|
||||
/** |
||||
* Remove one or many array items from a given array using "dot" notation. |
||||
* |
||||
* @param array $array |
||||
* @param array|string $keys |
||||
*/ |
||||
public static function forget(array &$array, $keys) |
||||
{ |
||||
$original = &$array; |
||||
|
||||
$keys = (array) $keys; |
||||
|
||||
if (0 === count($keys)) { |
||||
return; |
||||
} |
||||
|
||||
foreach ($keys as $key) { |
||||
// if the exact key exists in the top-level, remove it |
||||
if (static::exists($array, $key)) { |
||||
unset($array[$key]); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
$parts = explode('.', $key); |
||||
|
||||
// clean up before each pass |
||||
$array = &$original; |
||||
|
||||
while (count($parts) > 1) { |
||||
$part = array_shift($parts); |
||||
|
||||
if (isset($array[$part]) && is_array($array[$part])) { |
||||
$array = &$array[$part]; |
||||
} else { |
||||
continue 2; |
||||
} |
||||
} |
||||
|
||||
unset($array[array_shift($parts)]); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Get an item from an array using "dot" notation. |
||||
* |
||||
* @param array $array |
||||
* @param string $key |
||||
* @param mixed $default |
||||
* |
||||
* @return mixed |
||||
*/ |
||||
public static function get(array $array, $key, $default = null) |
||||
{ |
||||
if (is_null($key)) { |
||||
return $array; |
||||
} |
||||
|
||||
if (static::exists($array, $key)) { |
||||
return $array[$key]; |
||||
} |
||||
|
||||
foreach (explode('.', $key) as $segment) { |
||||
if (static::exists($array, $segment)) { |
||||
$array = $array[$segment]; |
||||
} else { |
||||
return $default; |
||||
} |
||||
} |
||||
|
||||
return $array; |
||||
} |
||||
|
||||
/** |
||||
* Check if an item or items exist in an array using "dot" notation. |
||||
* |
||||
* @param array $array |
||||
* @param string|array $keys |
||||
* |
||||
* @return bool |
||||
*/ |
||||
public static function has(array $array, $keys) |
||||
{ |
||||
if (is_null($keys)) { |
||||
return false; |
||||
} |
||||
|
||||
$keys = (array) $keys; |
||||
|
||||
if (empty($array)) { |
||||
return false; |
||||
} |
||||
|
||||
if ($keys === []) { |
||||
return false; |
||||
} |
||||
|
||||
foreach ($keys as $key) { |
||||
$subKeyArray = $array; |
||||
|
||||
if (static::exists($array, $key)) { |
||||
continue; |
||||
} |
||||
|
||||
foreach (explode('.', $key) as $segment) { |
||||
if (static::exists($subKeyArray, $segment)) { |
||||
$subKeyArray = $subKeyArray[$segment]; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Determines if an array is associative. |
||||
* |
||||
* An array is "associative" if it doesn't have sequential numerical keys beginning with zero. |
||||
* |
||||
* @param array $array |
||||
* |
||||
* @return bool |
||||
*/ |
||||
public static function isAssoc(array $array) |
||||
{ |
||||
$keys = array_keys($array); |
||||
|
||||
return array_keys($keys) !== $keys; |
||||
} |
||||
|
||||
/** |
||||
* Get a subset of the items from the given array. |
||||
* |
||||
* @param array $array |
||||
* @param array|string $keys |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function only(array $array, $keys) |
||||
{ |
||||
return array_intersect_key($array, array_flip((array) $keys)); |
||||
} |
||||
|
||||
/** |
||||
* Push an item onto the beginning of an array. |
||||
* |
||||
* @param array $array |
||||
* @param mixed $value |
||||
* @param mixed $key |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function prepend(array $array, $value, $key = null) |
||||
{ |
||||
if (is_null($key)) { |
||||
array_unshift($array, $value); |
||||
} else { |
||||
$array = [$key => $value] + $array; |
||||
} |
||||
|
||||
return $array; |
||||
} |
||||
|
||||
/** |
||||
* Get a value from the array, and remove it. |
||||
* |
||||
* @param array $array |
||||
* @param string $key |
||||
* @param mixed $default |
||||
* |
||||
* @return mixed |
||||
*/ |
||||
public static function pull(array &$array, $key, $default = null) |
||||
{ |
||||
$value = static::get($array, $key, $default); |
||||
|
||||
static::forget($array, $key); |
||||
|
||||
return $value; |
||||
} |
||||
|
||||
/** |
||||
* Get a 1 value from an array. |
||||
* |
||||
* @param array $array |
||||
* @param int|null $amount |
||||
* |
||||
* @return mixed |
||||
* |
||||
* @throws \InvalidArgumentException |
||||
*/ |
||||
public static function random(array $array, int $amount = null) |
||||
{ |
||||
if (is_null($amount)) { |
||||
return $array[array_rand($array)]; |
||||
} |
||||
|
||||
$keys = array_rand($array, $amount); |
||||
|
||||
$results = []; |
||||
|
||||
foreach ((array) $keys as $key) { |
||||
$results[] = $array[$key]; |
||||
} |
||||
|
||||
return $results; |
||||
} |
||||
|
||||
/** |
||||
* Set an array item to a given value using "dot" notation. |
||||
* |
||||
* If no key is given to the method, the entire array will be replaced. |
||||
* |
||||
* @param array $array |
||||
* @param string $key |
||||
* @param mixed $value |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function set(array &$array, string $key, $value) |
||||
{ |
||||
$keys = explode('.', $key); |
||||
|
||||
while (count($keys) > 1) { |
||||
$key = array_shift($keys); |
||||
|
||||
// If the key doesn't exist at this depth, we will just create an empty array |
||||
// to hold the next value, allowing us to create the arrays to hold final |
||||
// values at the correct depth. Then we'll keep digging into the array. |
||||
if (!isset($array[$key]) || !is_array($array[$key])) { |
||||
$array[$key] = []; |
||||
} |
||||
|
||||
$array = &$array[$key]; |
||||
} |
||||
|
||||
$array[array_shift($keys)] = $value; |
||||
|
||||
return $array; |
||||
} |
||||
|
||||
/** |
||||
* Filter the array using the given callback. |
||||
* |
||||
* @param array $array |
||||
* @param callable $callback |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function where(array $array, callable $callback) |
||||
{ |
||||
return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH); |
||||
} |
||||
|
||||
/** |
||||
* If the given value is not an array, wrap it in one. |
||||
* |
||||
* @param mixed $value |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function wrap($value) |
||||
{ |
||||
return !is_array($value) ? [$value] : $value; |
||||
} |
||||
} |
@ -0,0 +1,420 @@
@@ -0,0 +1,420 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Support; |
||||
|
||||
use ArrayAccess; |
||||
use ArrayIterator; |
||||
use Countable; |
||||
use IteratorAggregate; |
||||
use JsonSerializable; |
||||
use Serializable; |
||||
|
||||
/** |
||||
* Class Collection. |
||||
*/ |
||||
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable, Serializable |
||||
{ |
||||
/** |
||||
* The collection data. |
||||
* |
||||
* @var array |
||||
*/ |
||||
protected $items = []; |
||||
|
||||
/** |
||||
* set data. |
||||
* |
||||
* @param array $items |
||||
*/ |
||||
public function __construct(array $items = []) |
||||
{ |
||||
foreach ($items as $key => $value) { |
||||
$this->set($key, $value); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Return all items. |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function all() |
||||
{ |
||||
return $this->items; |
||||
} |
||||
|
||||
/** |
||||
* Return specific items. |
||||
* |
||||
* @param array $keys |
||||
* |
||||
* @return \EasyAlipay\Kernel\Support\Collection |
||||
*/ |
||||
public function only(array $keys) |
||||
{ |
||||
$return = []; |
||||
|
||||
foreach ($keys as $key) { |
||||
$value = $this->get($key); |
||||
|
||||
if (!is_null($value)) { |
||||
$return[$key] = $value; |
||||
} |
||||
} |
||||
|
||||
return new static($return); |
||||
} |
||||
|
||||
/** |
||||
* Get all items except for those with the specified keys. |
||||
* |
||||
* @param mixed $keys |
||||
* |
||||
* @return static |
||||
*/ |
||||
public function except($keys) |
||||
{ |
||||
$keys = is_array($keys) ? $keys : func_get_args(); |
||||
|
||||
return new static(Arr::except($this->items, $keys)); |
||||
} |
||||
|
||||
/** |
||||
* Merge data. |
||||
* |
||||
* @param Collection|array $items |
||||
* |
||||
* @return \EasyAlipay\Kernel\Support\Collection |
||||
*/ |
||||
public function merge($items) |
||||
{ |
||||
$clone = new static($this->all()); |
||||
|
||||
foreach ($items as $key => $value) { |
||||
$clone->set($key, $value); |
||||
} |
||||
|
||||
return $clone; |
||||
} |
||||
|
||||
/** |
||||
* To determine Whether the specified element exists. |
||||
* |
||||
* @param string $key |
||||
* |
||||
* @return bool |
||||
*/ |
||||
public function has($key) |
||||
{ |
||||
return !is_null(Arr::get($this->items, $key)); |
||||
} |
||||
|
||||
/** |
||||
* Retrieve the first item. |
||||
* |
||||
* @return mixed |
||||
*/ |
||||
public function first() |
||||
{ |
||||
return reset($this->items); |
||||
} |
||||
|
||||
/** |
||||
* Retrieve the last item. |
||||
* |
||||
* @return bool |
||||
*/ |
||||
public function last() |
||||
{ |
||||
$end = end($this->items); |
||||
|
||||
reset($this->items); |
||||
|
||||
return $end; |
||||
} |
||||
|
||||
/** |
||||
* add the item value. |
||||
* |
||||
* @param string $key |
||||
* @param mixed $value |
||||
*/ |
||||
public function add($key, $value) |
||||
{ |
||||
Arr::set($this->items, $key, $value); |
||||
} |
||||
|
||||
/** |
||||
* Set the item value. |
||||
* |
||||
* @param string $key |
||||
* @param mixed $value |
||||
*/ |
||||
public function set($key, $value) |
||||
{ |
||||
Arr::set($this->items, $key, $value); |
||||
} |
||||
|
||||
/** |
||||
* Retrieve item from Collection. |
||||
* |
||||
* @param string $key |
||||
* @param mixed $default |
||||
* |
||||
* @return mixed |
||||
*/ |
||||
public function get($key, $default = null) |
||||
{ |
||||
return Arr::get($this->items, $key, $default); |
||||
} |
||||
|
||||
/** |
||||
* Remove item form Collection. |
||||
* |
||||
* @param string $key |
||||
*/ |
||||
public function forget($key) |
||||
{ |
||||
Arr::forget($this->items, $key); |
||||
} |
||||
|
||||
/** |
||||
* Build to array. |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function toArray() |
||||
{ |
||||
return $this->all(); |
||||
} |
||||
|
||||
/** |
||||
* Build to json. |
||||
* |
||||
* @param int $option |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function toJson($option = JSON_UNESCAPED_UNICODE) |
||||
{ |
||||
return json_encode($this->all(), $option); |
||||
} |
||||
|
||||
/** |
||||
* To string. |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function __toString() |
||||
{ |
||||
return $this->toJson(); |
||||
} |
||||
|
||||
/** |
||||
* (PHP 5 >= 5.4.0)<br/> |
||||
* Specify data which should be serialized to JSON. |
||||
* |
||||
* @see http://php.net/manual/en/jsonserializable.jsonserialize.php |
||||
* |
||||
* @return mixed data which can be serialized by <b>json_encode</b>, |
||||
* which is a value of any type other than a resource |
||||
*/ |
||||
public function jsonSerialize() |
||||
{ |
||||
return $this->items; |
||||
} |
||||
|
||||
/** |
||||
* (PHP 5 >= 5.1.0)<br/> |
||||
* String representation of object. |
||||
* |
||||
* @see http://php.net/manual/en/serializable.serialize.php |
||||
* |
||||
* @return string the string representation of the object or null |
||||
*/ |
||||
public function serialize() |
||||
{ |
||||
return serialize($this->items); |
||||
} |
||||
|
||||
/** |
||||
* (PHP 5 >= 5.0.0)<br/> |
||||
* Retrieve an external iterator. |
||||
* |
||||
* @see http://php.net/manual/en/iteratoraggregate.getiterator.php |
||||
* |
||||
* @return \ArrayIterator An instance of an object implementing <b>Iterator</b> or |
||||
* <b>Traversable</b> |
||||
*/ |
||||
public function getIterator() |
||||
{ |
||||
return new ArrayIterator($this->items); |
||||
} |
||||
|
||||
/** |
||||
* (PHP 5 >= 5.1.0)<br/> |
||||
* Count elements of an object. |
||||
* |
||||
* @see http://php.net/manual/en/countable.count.php |
||||
* |
||||
* @return int the custom count as an integer. |
||||
* </p> |
||||
* <p> |
||||
* The return value is cast to an integer |
||||
*/ |
||||
public function count() |
||||
{ |
||||
return count($this->items); |
||||
} |
||||
|
||||
/** |
||||
* (PHP 5 >= 5.1.0)<br/> |
||||
* Constructs the object. |
||||
* |
||||
* @see http://php.net/manual/en/serializable.unserialize.php |
||||
* |
||||
* @param string $serialized <p> |
||||
* The string representation of the object. |
||||
* </p> |
||||
* |
||||
* @return mixed|void |
||||
*/ |
||||
public function unserialize($serialized) |
||||
{ |
||||
return $this->items = unserialize($serialized); |
||||
} |
||||
|
||||
/** |
||||
* Get a data by key. |
||||
* |
||||
* @param string $key |
||||
* |
||||
* @return mixed |
||||
*/ |
||||
public function __get($key) |
||||
{ |
||||
return $this->get($key); |
||||
} |
||||
|
||||
/** |
||||
* Assigns a value to the specified data. |
||||
* |
||||
* @param string $key |
||||
* @param mixed $value |
||||
*/ |
||||
public function __set($key, $value) |
||||
{ |
||||
$this->set($key, $value); |
||||
} |
||||
|
||||
/** |
||||
* Whether or not an data exists by key. |
||||
* |
||||
* @param string $key |
||||
* |
||||
* @return bool |
||||
*/ |
||||
public function __isset($key) |
||||
{ |
||||
return $this->has($key); |
||||
} |
||||
|
||||
/** |
||||
* Unset an data by key. |
||||
* |
||||
* @param string $key |
||||
*/ |
||||
public function __unset($key) |
||||
{ |
||||
$this->forget($key); |
||||
} |
||||
|
||||
/** |
||||
* var_export. |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function __set_state() |
||||
{ |
||||
return $this->all(); |
||||
} |
||||
|
||||
/** |
||||
* (PHP 5 >= 5.0.0)<br/> |
||||
* Whether a offset exists. |
||||
* |
||||
* @see http://php.net/manual/en/arrayaccess.offsetexists.php |
||||
* |
||||
* @param mixed $offset <p> |
||||
* An offset to check for. |
||||
* </p> |
||||
* |
||||
* @return bool true on success or false on failure. |
||||
* The return value will be casted to boolean if non-boolean was returned |
||||
*/ |
||||
public function offsetExists($offset) |
||||
{ |
||||
return $this->has($offset); |
||||
} |
||||
|
||||
/** |
||||
* (PHP 5 >= 5.0.0)<br/> |
||||
* Offset to unset. |
||||
* |
||||
* @see http://php.net/manual/en/arrayaccess.offsetunset.php |
||||
* |
||||
* @param mixed $offset <p> |
||||
* The offset to unset. |
||||
* </p> |
||||
*/ |
||||
public function offsetUnset($offset) |
||||
{ |
||||
if ($this->offsetExists($offset)) { |
||||
$this->forget($offset); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* (PHP 5 >= 5.0.0)<br/> |
||||
* Offset to retrieve. |
||||
* |
||||
* @see http://php.net/manual/en/arrayaccess.offsetget.php |
||||
* |
||||
* @param mixed $offset <p> |
||||
* The offset to retrieve. |
||||
* </p> |
||||
* |
||||
* @return mixed Can return all value types |
||||
*/ |
||||
public function offsetGet($offset) |
||||
{ |
||||
return $this->offsetExists($offset) ? $this->get($offset) : null; |
||||
} |
||||
|
||||
/** |
||||
* (PHP 5 >= 5.0.0)<br/> |
||||
* Offset to set. |
||||
* |
||||
* @see http://php.net/manual/en/arrayaccess.offsetset.php |
||||
* |
||||
* @param mixed $offset <p> |
||||
* The offset to assign the value to. |
||||
* </p> |
||||
* @param mixed $value <p> |
||||
* The value to set. |
||||
* </p> |
||||
*/ |
||||
public function offsetSet($offset, $value) |
||||
{ |
||||
$this->set($offset, $value); |
||||
} |
||||
} |
@ -0,0 +1,135 @@
@@ -0,0 +1,135 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Support; |
||||
|
||||
use finfo; |
||||
|
||||
/** |
||||
* Class File. |
||||
*/ |
||||
class File |
||||
{ |
||||
/** |
||||
* MIME mapping. |
||||
* |
||||
* @var array |
||||
*/ |
||||
protected static $extensionMap = [ |
||||
'audio/wav' => '.wav', |
||||
'audio/x-ms-wma' => '.wma', |
||||
'video/x-ms-wmv' => '.wmv', |
||||
'video/mp4' => '.mp4', |
||||
'audio/mpeg' => '.mp3', |
||||
'audio/amr' => '.amr', |
||||
'application/vnd.rn-realmedia' => '.rm', |
||||
'audio/mid' => '.mid', |
||||
'image/bmp' => '.bmp', |
||||
'image/gif' => '.gif', |
||||
'image/png' => '.png', |
||||
'image/tiff' => '.tiff', |
||||
'image/jpeg' => '.jpg', |
||||
'application/pdf' => '.pdf', |
||||
|
||||
// 列举更多的文件 mime, 企业号是支持的,公众平台这边之后万一也更新了呢 |
||||
'application/msword' => '.doc', |
||||
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => '.docx', |
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => '.dotx', |
||||
'application/vnd.ms-word.document.macroEnabled.12' => '.docm', |
||||
'application/vnd.ms-word.template.macroEnabled.12' => '.dotm', |
||||
|
||||
'application/vnd.ms-excel' => '.xls', |
||||
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => '.xlsx', |
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => '.xltx', |
||||
'application/vnd.ms-excel.sheet.macroEnabled.12' => '.xlsm', |
||||
'application/vnd.ms-excel.template.macroEnabled.12' => '.xltm', |
||||
'application/vnd.ms-excel.addin.macroEnabled.12' => '.xlam', |
||||
'application/vnd.ms-excel.sheet.binary.macroEnabled.12' => '.xlsb', |
||||
|
||||
'application/vnd.ms-powerpoint' => '.ppt', |
||||
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => '.pptx', |
||||
'application/vnd.openxmlformats-officedocument.presentationml.template' => '.potx', |
||||
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => '.ppsx', |
||||
'application/vnd.ms-powerpoint.addin.macroEnabled.12' => '.ppam', |
||||
]; |
||||
|
||||
/** |
||||
* File header signatures. |
||||
* |
||||
* @var array |
||||
*/ |
||||
protected static $signatures = [ |
||||
'ffd8ff' => '.jpg', |
||||
'424d' => '.bmp', |
||||
'47494638' => '.gif', |
||||
'2f55736572732f6f7665' => '.png', |
||||
'89504e47' => '.png', |
||||
'494433' => '.mp3', |
||||
'fffb' => '.mp3', |
||||
'fff3' => '.mp3', |
||||
'3026b2758e66cf11' => '.wma', |
||||
'52494646' => '.wav', |
||||
'57415645' => '.wav', |
||||
'41564920' => '.avi', |
||||
'000001ba' => '.mpg', |
||||
'000001b3' => '.mpg', |
||||
'2321414d52' => '.amr', |
||||
'25504446' => '.pdf', |
||||
]; |
||||
|
||||
/** |
||||
* Return steam extension. |
||||
* |
||||
* @param string $stream |
||||
* |
||||
* @return string|false |
||||
*/ |
||||
public static function getStreamExt($stream) |
||||
{ |
||||
$ext = self::getExtBySignature($stream); |
||||
|
||||
try { |
||||
if (empty($ext) && is_readable($stream)) { |
||||
$stream = file_get_contents($stream); |
||||
} |
||||
} catch (\Exception $e) { |
||||
} |
||||
|
||||
$fileInfo = new finfo(FILEINFO_MIME); |
||||
|
||||
$mime = strstr($fileInfo->buffer($stream), ';', true); |
||||
|
||||
return isset(self::$extensionMap[$mime]) ? self::$extensionMap[$mime] : $ext; |
||||
} |
||||
|
||||
/** |
||||
* Get file extension by file header signature. |
||||
* |
||||
* @param string $stream |
||||
* |
||||
* @return string |
||||
*/ |
||||
public static function getExtBySignature($stream) |
||||
{ |
||||
$prefix = strval(bin2hex(mb_strcut($stream, 0, 10))); |
||||
|
||||
foreach (self::$signatures as $signature => $extension) { |
||||
if (0 === strpos($prefix, strval($signature))) { |
||||
return $extension; |
||||
} |
||||
} |
||||
|
||||
return ''; |
||||
} |
||||
} |
@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the overtrue/wechat. |
||||
* |
||||
* (c) overtrue <i@overtrue.me> |
||||
* |
||||
* This source file is subject to the MIT license that is bundled |
||||
* with this source code in the file LICENSE. |
||||
*/ |
||||
|
||||
namespace EasyAlipay\Kernel\Support; |
||||
|
||||
use SimpleXMLElement; |
||||
|
||||
/** |
||||
* Class XML. |
||||
*/ |
||||
class XML |
||||
{ |
||||
/** |
||||
* XML to array. |
||||
* |
||||
* @param string $xml XML string |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function parse($xml) |
||||
{ |
||||
$backup = libxml_disable_entity_loader(true); |
||||
|
||||
$result = self::normalize(simplexml_load_string(self::sanitize($xml), 'SimpleXMLElement', LIBXML_COMPACT | LIBXML_NOCDATA | LIBXML_NOBLANKS)); |
||||
|
||||
libxml_disable_entity_loader($backup); |
||||
|
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* XML encode. |
||||
* |
||||
* @param mixed $data |
||||
* @param string $root |
||||
* @param string $item |
||||
* @param string $attr |
||||
* @param string $id |
||||
* |
||||
* @return string |
||||
*/ |
||||
public static function build( |
||||
$data, |
||||
$root = 'xml', |
||||
$item = 'item', |
||||
$attr = '', |
||||
$id = 'id' |
||||
) { |
||||
if (is_array($attr)) { |
||||
$_attr = []; |
||||
|
||||
foreach ($attr as $key => $value) { |
||||
$_attr[] = "{$key}=\"{$value}\""; |
||||
} |
||||
|
||||
$attr = implode(' ', $_attr); |
||||
} |
||||
|
||||
$attr = trim($attr); |
||||
$attr = empty($attr) ? '' : " {$attr}"; |
||||
$xml = "<{$root}{$attr}>"; |
||||
$xml .= self::data2Xml($data, $item, $id); |
||||
$xml .= "</{$root}>"; |
||||
|
||||
return $xml; |
||||
} |
||||
|
||||
/** |
||||
* Build CDATA. |
||||
* |
||||
* @param string $string |
||||
* |
||||
* @return string |
||||
*/ |
||||
public static function cdata($string) |
||||
{ |
||||
return sprintf('<![CDATA[%s]]>', $string); |
||||
} |
||||
|
||||
/** |
||||
* Object to array. |
||||
* |
||||
* |
||||
* @param SimpleXMLElement $obj |
||||
* |
||||
* @return array |
||||
*/ |
||||
protected static function normalize($obj) |
||||
{ |
||||
$result = null; |
||||
|
||||
if (is_object($obj)) { |
||||
$obj = (array) $obj; |
||||
} |
||||
|
||||
if (is_array($obj)) { |
||||
foreach ($obj as $key => $value) { |
||||
$res = self::normalize($value); |
||||
if (('@attributes' === $key) && ($key)) { |
||||
$result = $res; // @codeCoverageIgnore |
||||
} else { |
||||
$result[$key] = $res; |
||||
} |
||||
} |
||||
} else { |
||||
$result = $obj; |
||||
} |
||||
|
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* Array to XML. |
||||
* |
||||
* @param array $data |
||||
* @param string $item |
||||
* @param string $id |
||||
* |
||||
* @return string |
||||
*/ |
||||
protected static function data2Xml($data, $item = 'item', $id = 'id') |
||||
{ |
||||
$xml = $attr = ''; |
||||
|
||||
foreach ($data as $key => $val) { |
||||
if (is_numeric($key)) { |
||||
$id && $attr = " {$id}=\"{$key}\""; |
||||
$key = $item; |
||||
} |
||||
|
||||
$xml .= "<{$key}{$attr}>"; |
||||
|
||||
if ((is_array($val) || is_object($val))) { |
||||
$xml .= self::data2Xml((array) $val, $item, $id); |
||||
} else { |
||||
$xml .= is_numeric($val) ? $val : self::cdata($val); |
||||
} |
||||
|
||||
$xml .= "</{$key}>"; |
||||
} |
||||
|
||||
return $xml; |
||||
} |
||||
|
||||
/** |
||||
* Delete invalid characters in XML. |
||||
* |
||||
* @see https://www.w3.org/TR/2008/REC-xml-20081126/#charsets - XML charset range |
||||
* @see http://php.net/manual/en/regexp.reference.escape.php - escape in UTF-8 mode |
||||
* |
||||
* @param string $xml |
||||
* |
||||
* @return string |
||||
*/ |
||||
public static function sanitize($xml) |
||||
{ |
||||
return preg_replace('/[^\x{9}\x{A}\x{D}\x{20}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]+/u', '', $xml); |
||||
} |
||||
} |
@ -0,0 +1,85 @@
@@ -0,0 +1,85 @@
|
||||
<?php |
||||
|
||||
namespace EasyAlipay\Kernel\Traits; |
||||
|
||||
use EasyAlipay\Kernel\Contracts\Arrayable; |
||||
use EasyAlipay\Kernel\Exceptions\InvalidArgumentException; |
||||
use EasyAlipay\Kernel\Exceptions\InvalidConfigException; |
||||
use EasyAlipay\Kernel\Http\Response; |
||||
use EasyAlipay\Kernel\Support\Collection; |
||||
use Psr\Http\Message\ResponseInterface; |
||||
|
||||
/** |
||||
* Created by PhpStorm. |
||||
* User: wuliangbo |
||||
* Date: 2020/1/6 |
||||
* Time: 14:54 |
||||
*/ |
||||
trait ResponseCastable |
||||
{ |
||||
/** |
||||
* @param \Psr\Http\Message\ResponseInterface $response |
||||
* @param string|null $type |
||||
* |
||||
* @return array|\EasyAlipay\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string |
||||
* |
||||
* @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException |
||||
*/ |
||||
protected function castResponseToType(ResponseInterface $response, $type = null) |
||||
{ |
||||
$response = Response::buildFromPsrResponse($response); |
||||
$response->getBody()->rewind(); |
||||
|
||||
switch ($type ?? 'array') { |
||||
case 'collection': |
||||
return $response->toCollection(); |
||||
case 'array': |
||||
return $response->toArray(); |
||||
case 'object': |
||||
return $response->toObject(); |
||||
case 'raw': |
||||
return $response; |
||||
default: |
||||
if (!is_subclass_of($type, Arrayable::class)) { |
||||
throw new InvalidConfigException(sprintf('Config key "response_type" classname must be an instanceof %s', Arrayable::class)); |
||||
} |
||||
|
||||
return new $type($response); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param mixed $response |
||||
* @param string|null $type |
||||
* |
||||
* @return array|\EasyAlipay\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string |
||||
* |
||||
* @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException |
||||
* @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException |
||||
*/ |
||||
protected function detectAndCastResponseToType($response, $type = null) |
||||
{ |
||||
switch (true) { |
||||
case $response instanceof ResponseInterface: |
||||
$response = Response::buildFromPsrResponse($response); |
||||
|
||||
break; |
||||
case $response instanceof Arrayable: |
||||
$response = new Response(200, [], json_encode($response->toArray())); |
||||
|
||||
break; |
||||
case ($response instanceof Collection) || is_array($response) || is_object($response): |
||||
$response = new Response(200, [], json_encode($response)); |
||||
|
||||
break; |
||||
case is_scalar($response): |
||||
$response = new Response(200, [], (string)$response); |
||||
|
||||
break; |
||||
default: |
||||
throw new InvalidArgumentException(sprintf('Unsupported response type "%s"', gettype($response))); |
||||
} |
||||
|
||||
return $this->castResponseToType($response, $type); |
||||
} |
||||
} |
Loading…
Reference in new issue