wuliangbo
5 years ago
35 changed files with 3187 additions and 481 deletions
@ -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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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