Browse Source

增加注释

tags/v1.0.5
wuliangbo 1 year ago
parent
commit
388ecd4679
70 changed files with 4179 additions and 3467 deletions
  1. +20
    -0
      .editorconfig
  2. +0
    -1
      .gitignore
  3. +6
    -0
      .phplint.yml
  4. +6
    -0
      .styleci.yml
  5. +19
    -15
      composer.json
  6. +0
    -1341
      composer.lock
  7. +0
    -89
      src/Api.php
  8. +0
    -151
      src/BaseApi.php
  9. +0
    -16
      src/Contracts/AccessTokenInterface.php
  10. +0
    -57
      src/Contracts/RepositoryInterface.php
  11. +0
    -14
      src/Exceptions/Exception.php
  12. +0
    -17
      src/Exceptions/InvalidArgumentException.php
  13. +20
    -13
      src/Factory.php
  14. +0
    -285
      src/Http.php
  15. +249
    -0
      src/Kernel/AccessToken.php
  16. +256
    -0
      src/Kernel/BaseClient.php
  17. +63
    -0
      src/Kernel/Clauses/Clause.php
  18. +22
    -0
      src/Kernel/Config.php
  19. +39
    -0
      src/Kernel/Contracts/AccessTokenInterface.php
  20. +28
    -0
      src/Kernel/Contracts/Arrayable.php
  21. +25
    -0
      src/Kernel/Contracts/EventHandlerInterface.php
  22. +35
    -0
      src/Kernel/Decorators/FinallyResult.php
  23. +34
    -0
      src/Kernel/Decorators/TerminateResult.php
  24. +113
    -0
      src/Kernel/Encryptor.php
  25. +21
    -0
      src/Kernel/Exceptions/BadRequestException.php
  26. +16
    -0
      src/Kernel/Exceptions/DecryptException.php
  27. +23
    -0
      src/Kernel/Exceptions/Exception.php
  28. +52
    -0
      src/Kernel/Exceptions/HttpException.php
  29. +21
    -0
      src/Kernel/Exceptions/InvalidArgumentException.php
  30. +21
    -0
      src/Kernel/Exceptions/InvalidConfigException.php
  31. +21
    -0
      src/Kernel/Exceptions/RuntimeException.php
  32. +21
    -0
      src/Kernel/Exceptions/UnboundServiceException.php
  33. +40
    -86
      src/Kernel/Helpers.php
  34. +116
    -0
      src/Kernel/Http/Response.php
  35. +546
    -0
      src/Kernel/Log/LogManager.php
  36. +39
    -0
      src/Kernel/Providers/ConfigServiceProvider.php
  37. +39
    -0
      src/Kernel/Providers/HttpClientServiceProvider.php
  38. +79
    -0
      src/Kernel/Providers/LogServiceProvider.php
  39. +39
    -0
      src/Kernel/Providers/RequestServiceProvider.php
  40. +0
    -195
      src/Kernel/Repository.php
  41. +0
    -24
      src/Kernel/ResponseCastable.php
  42. +144
    -0
      src/Kernel/ServiceContainer.php
  43. +85
    -0
      src/Kernel/Support/AES.php
  44. +39
    -115
      src/Kernel/Support/Arr.php
  45. +66
    -0
      src/Kernel/Support/ArrayAccessible.php
  46. +93
    -71
      src/Kernel/Support/Collection.php
  47. +133
    -0
      src/Kernel/Support/File.php
  48. +114
    -0
      src/Kernel/Support/Helpers.php
  49. +194
    -0
      src/Kernel/Support/Str.php
  50. +247
    -0
      src/Kernel/Traits/HasAttributes.php
  51. +217
    -0
      src/Kernel/Traits/HasHttpRequests.php
  52. +12
    -48
      src/Kernel/Traits/InteractsWithCache.php
  53. +241
    -0
      src/Kernel/Traits/Observable.php
  54. +89
    -0
      src/Kernel/Traits/ResponseCastable.php
  55. +0
    -69
      src/MiniProgram/AccessToken.php
  56. +24
    -73
      src/MiniProgram/Application.php
  57. +0
    -65
      src/MiniProgram/Auth.php
  58. +43
    -0
      src/MiniProgram/Auth/AccessToken.php
  59. +46
    -0
      src/MiniProgram/Auth/Client.php
  60. +40
    -0
      src/MiniProgram/Auth/ServiceProvider.php
  61. +49
    -0
      src/MiniProgram/Encryptor.php
  62. +58
    -0
      src/MiniProgram/KVData/Client.php
  63. +32
    -0
      src/MiniProgram/KVData/ServiceProvider.php
  64. +47
    -0
      src/MiniProgram/Message/Client.php
  65. +30
    -0
      src/MiniProgram/Message/ServiceProvider.php
  66. +46
    -0
      src/MiniProgram/QRCode/Client.php
  67. +32
    -0
      src/MiniProgram/QRCode/ServiceProvider.php
  68. +0
    -17
      src/MiniProgram/TemplateMessage.php
  69. +0
    -705
      src/Supports/Str.php
  70. +29
    -0
      src/config/bytedance.php

+ 20
- 0
.editorconfig View File

@@ -0,0 +1,20 @@
root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = false

[*.{vue,js,scss}]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

+ 0
- 1
.gitignore View File

@@ -2,6 +2,5 @@
/.vscode
/vendor
*.log
thinkphp
.env
index.php

+ 6
- 0
.phplint.yml View File

@@ -0,0 +1,6 @@
path: ./
jobs: 10
exclude:
- .github
- build
- vendor

+ 6
- 0
.styleci.yml View File

@@ -0,0 +1,6 @@
preset: symfony

enabled:
- strict
- strict_param
- ordered_use

+ 19
- 15
composer.json View File

@@ -1,28 +1,32 @@
{
"name": "wuearl/toutiao",
"type": "library",
"name": "otkurbiz\/bytedance",
"description": "sdk for bytedance (douyin, toutiao, huoshan, xigua)",
"license": "MIT",
"authors": [
{
"name": "wuearl",
"email": "wuearl@qq.com"
"name": "Alim",
"email": "alim@bulutbazar.com"
}
],
"require": {
"php": ">=7.1.3",
"ext-simplexml": "*",
"ext-openssl": "*",
"ext-json": "*",
"ext-curl": "*",
"symfony/http-foundation": "^4.0",
"symfony/event-dispatcher": "^4.0",
"guzzlehttp/guzzle": "^6.2",
"pimple/pimple": "^3.0",
"symfony/cache": "^4.3",
"psr/simple-cache": "^1.0"
"ext-fileinfo": "*",
"psr/simple-cache": "^1.0",
"guzzlehttp/guzzle": "~6.2.1",
"monolog/monolog": "^1.17",
"doctrine/cache": "~1.4",
"pimple/pimple": "3.0.2",
"symfony/psr-http-message-bridge": "1.0",
"symfony/http-foundation": "3.0.9"
},
"autoload": {
"psr-4": {
"TouTiao\\": "src"
"ByteDance\\": "src"
}
},
"license": "MIT"
"require-dev": {
"phpunit/phpunit": "^7.5",
"overtrue/phplint": "^1.2"
}
}

+ 0
- 1341
composer.lock
File diff suppressed because it is too large
View File


+ 0
- 89
src/Api.php View File

@@ -1,89 +0,0 @@
<?php

/**
* @Author: brooke
* @Date: 2018-06-29 15:04:39
* @Last Modified by: brooke
* @Last Modified time: 2018-12-17 14:43:08
*/

namespace TouTiao;

use Pimple\Container;
use TouTiao\Kernel\Repository;

/**
* Class Api
* @package TouTiao
*
*/
class Api extends Container
{
protected $url;

/**
* Api constructor.
* @param array $config
*/
public function __construct($config = [])
{
$this->registerConfig($config)
->registerProviders();
}

protected function registerConfig(array $config)
{
$this['config'] = function () use ($config) {
return new Repository($config);
};

return $this;
}

protected function registerProviders()
{
foreach ($this->providers as $key => $provider) {
$this[$key] = function() use ($provider){
return new $provider($this);
};
}
return $this;
}

public function setUrl(string $url): Api
{
$this->url = $url;

return $this;
}

/**
* Get current url.
*
* @return string
*/
public function getUrl(): string
{
if ($this->url) {
return $this->url;
}

return $this['host'];
}

public function __get($id)
{
return $this->offsetGet($id);
}
/**
* Magic set access.
*
* @param string $id
* @param mixed $value
*/
public function __set($id, $value)
{
$this->offsetSet($id, $value);
}
}

+ 0
- 151
src/BaseApi.php View File

@@ -1,151 +0,0 @@
<?php

/**
* @Author: brooke
* @Date: 2018-06-29 15:04:39
* @Last Modified by: brooke
* @Last Modified time: 2019-05-27 11:24:10
*/

namespace TouTiao;

use TouTiao\Supports\Collection;
use TouTiao\Kernel\ResponseCastable;

/**
* Class BaseApi
* @package TouTiao
*
*/
class BaseApi
{
use ResponseCastable;

public $app;

protected $baseUri;

/**
* BaseApi constructor.
* @param Api $app
*/
public function __construct(Api $app)
{
$this->app = $app;
}

/**
* @param $url
* @return BaseApi
*/
public function rebindBaseUri($url): BaseApi
{
$this->baseUri = $url;

return $this;
}

/**
* @return string
*/
public function getBaseUri()
{
if(! $this->baseUri){
$this->baseUri = $this->app->getUrl();
}

return $this->baseUri;
}

/**
* @return Http
*/
public static function getHttpClient(){
return new Http();
}

/**
* @param array $headers
* @param array $data
* @return array
*/
public static function parseHeaders(array $headers, array $data = [])
{
array_walk($headers, function($header, $index) use (& $data){
$data[] = $index.':'.$header;
});

return $data;
}

/**
* @param $url
* @param array $query
* @return Collection
* @throws Exceptions\Exception
*/
public function httpGet($url, $query = [])
{
return $this->request($url, 'GET', $query);
}

/**
* @param $url
* @param array $data
* @return Collection
* @throws Exceptions\Exception
*/
public function httpPost($url, $data = [])
{
return $this->request($url, 'POST', $data);
}

/**
* @param $url
* @param array $data
* @return Collection
* @throws Exceptions\Exception
*/
public function httpPostJson($url, $data = [])
{
return $this->request($url, 'POST', $data, ['json' => true]);
}

/**
* @param string $url
* @param array $files
* @param array $form
* @param array $query
* @return Collection
* @throws Exceptions\Exception
*/
public function httpUpload(string $url, $files = [], $form = [], $query = [])
{
return $this->request($url, 'POST', [],['query' => array_merge($query, $form), 'files' => $files]);
}

/**
* @param $url
*/
public function redirect($url){
header("Location:".$this->app['host'].'/'.$url);exit;
}

/**
* @param $url
* @param string $method
* @param array $data
* @param array $options
* @return Collection
* @throws Exceptions\Exception
* @throws \Exception
*/
public function request($url, $method = 'GET', $data = [], $options = [])
{
if(isset($options['headers'])) $options['headers'] = static::parseHeaders($options['headers']);

$result = static::getHttpClient()->request($this->getBaseUri().'/'.$url, $method, $data, $options);

return $this->processing($result['data']);
}
}

+ 0
- 16
src/Contracts/AccessTokenInterface.php View File

@@ -1,16 +0,0 @@
<?php

namespace TouTiao\Contracts;

interface AccessTokenInterface
{
/**
* @return array
*/
public function getToken(): array;

/**
* @return array
*/
public function getCredentials(): array;
}

+ 0
- 57
src/Contracts/RepositoryInterface.php View File

@@ -1,57 +0,0 @@
<?php

namespace TouTiao\Contracts;

interface RepositoryInterface
{
/**
* Determine if the given configuration value exists.
*
* @param string $key
* @return bool
*/
public function has($key);

/**
* Get the specified configuration value.
*
* @param array|string $key
* @param mixed $default
* @return mixed
*/
public function get($key, $default = null);

/**
* Get all of the configuration items for the application.
*
* @return array
*/
public function all();

/**
* Set a given configuration value.
*
* @param array|string $key
* @param mixed $value
* @return void
*/
public function set($key, $value = null);

/**
* Prepend a value onto an array configuration value.
*
* @param string $key
* @param mixed $value
* @return void
*/
public function prepend($key, $value);

/**
* Push a value onto an array configuration value.
*
* @param string $key
* @param mixed $value
* @return void
*/
public function push($key, $value);
}

+ 0
- 14
src/Exceptions/Exception.php View File

@@ -1,14 +0,0 @@
<?php

/**
* @Author: brooke
* @Date: 2018-06-29 15:04:39
* @Last Modified by: win7
* @Last Modified time: 2018-07-02 10:18:31
*/

namespace TouTiao\Exceptions;

class Exception extends \Exception
{
}

+ 0
- 17
src/Exceptions/InvalidArgumentException.php View File

@@ -1,17 +0,0 @@
<?php

/**
* @Author: brooke
* @Date: 2018-06-29 15:04:39
* @Last Modified by: win7
* @Last Modified time: 2018-07-02 10:18:44
*/

namespace TouTiao\Exceptions;

/**
* Class InvalidArgumentException.
*/
class InvalidArgumentException extends Exception
{
}

+ 20
- 13
src/Factory.php View File

@@ -1,39 +1,46 @@
<?php
/**
* Created by PhpStorm.
* User: wuliangbo
* Date: 2019/12/4
* Time: 18:57
/*
* 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 TouTiao;
namespace ByteDance;

/**
* Class Factory.
*
* @method static \TouTiao\MiniProgram\Application miniProgram(array $config)
* @method static \ByteDance\MiniProgram\Application miniProgram(array $config)
*/
class Factory
{
/**
* @param $name
* @param array $config
* @return mixed
*
* @return \ByteDance\Kernel\ServiceContainer
*/
public static function make($name, array $config)
{
$namespace = Supports\Str::studly($name);
$application = "\\TouTiao\\{$namespace}\\Application";
$namespace = Kernel\Support\Str::studly($name);
$application = "\\ByteDance\\{$namespace}\\Application";

return new $application($config);
}

/**
* @param $name
* @param $arguments
* Dynamically pass methods to the application.
*
* @param string $name
* @param array $arguments
*
* @return mixed
*/
public static function __callStatic($name, $arguments)
{
return self::make($name, ...$arguments);
}
}
}

+ 0
- 285
src/Http.php View File

@@ -1,285 +0,0 @@
<?php

/**
* @Author: brooke
* @Date: 2018-06-29 15:04:39
* @Last Modified by: brooke
* @Last Modified time: 2019-06-24 18:45:57
*/

namespace TouTiao;

class Http
{
/**
* Constants for available HTTP methods.
*/
const GET = 'GET';
const POST = 'POST';
const PUT = 'PUT';
const PATCH = 'PATCH';
const DELETE = 'DELETE';

public static $defaults = [
'curl' => [
CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
],
];

/**
* CURL句柄.
*
* @var resource handle
*/
protected $curl;

/**
* Create the cURL resource.
*/
public function __construct()
{
$this->curl = curl_init();
}

/**
* Clean up the cURL handle.
*/
public function __destruct()
{
if (is_resource($this->curl)) {
curl_close($this->curl);
}
}

/**
* Get the cURL handle.
*
* @return resource cURL handle
*/
public function getCurl()
{
return $this->curl;
}

/**
* Make a HTTP GET request.
* @param $url
* @param array $params
* @param array $options
* @return array
* @throws \Exception
*/
public function get($url, $params = array(), $options = array())
{
return $this->request($url, self::GET, $params, $options);
}

/**
* Make a HTTP POST request.
* @param $url
* @param array $params
* @param array $options
* @return array
* @throws \Exception
*/
public function post($url, $params = array(), $options = array())
{
return $this->request($url, self::POST, $params, $options);
}

/**
* Make a HTTP PUT request.
* @param $url
* @param array $params
* @param array $options
* @return array
* @throws \Exception
*/
public function put($url, $params = array(), $options = array())
{
return $this->request($url, self::PUT, $params, $options);
}

/**
* Make a HTTP PATCH request.
* @param $url
* @param array $params
* @param array $options
* @return array
* @throws \Exception
*/
public function patch($url, $params = array(), $options = array())
{
return $this->request($url, self::PATCH, $params, $options);
}

/**
* Make a HTTP DELETE request.
* @param $url
* @param array $params
* @param array $options
* @return array
* @throws \Exception
*/
public function delete($url, $params = array(), $options = array())
{
return $this->request($url, self::DELETE, $params, $options);
}

/**
* Make a HTTP request.
* @param $url
* @param string $method
* @param array $params
* @param array $options
* @return array
* @throws \Exception
*/
public function request($url, $method = self::GET, $params = array(), $options = array())
{

if (($method === self::GET || $method === self::DELETE) && !empty($params)) {
$url .= (stripos($url, '?') ? '&' : '?').http_build_query($params);
$params = array();
}

if(isset($options['query'])){
$url .= (stripos($url, '?') ? '&' : '?').http_build_query($options['query']);
}

curl_setopt($this->curl, CURLOPT_HEADER, 1);
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($this->curl, CURLOPT_URL, $url);

if (isset($options['referer']) && count($options['referer'])) {
curl_setopt($this->curl, CURLOPT_REFERER, $options['referer']);
}


//使用证书情况
if (isset($options['sslcert_path']) && isset($options['sslkey_path'])) {
if (!file_exists($options['sslcert_path']) || !file_exists($options['sslkey_path'])) {
throw new \Exception('Certfile is not correct');
}
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
curl_setopt($this->curl, CURLOPT_SSLCERTTYPE, 'PEM');
curl_setopt($this->curl, CURLOPT_SSLCERT, $options['sslcert_path']);
curl_setopt($this->curl, CURLOPT_SSLKEYTYPE, 'PEM');
curl_setopt($this->curl, CURLOPT_SSLKEY, $options['sslkey_path']);
} else {
curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, 0);
}
// Check for files
if (isset($options['files']) && count($options['files'])) {
foreach ($options['files'] as $index => $file) {
$params[$index] = $this->createCurlFile($file);
}

version_compare(PHP_VERSION, '5.5', '>') || curl_setopt($this->curl, CURLOPT_SAFE_UPLOAD, false);

curl_setopt($this->curl, CURLOPT_POST, 1);
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $params);
} else {
if (isset($options['json'])) {
$params = json_encode($params,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$options['headers'][] = 'content-type:application/json';
}

curl_setopt($this->curl, CURLOPT_POSTFIELDS, (is_array($params) && !isset($options['wechat'])) ? http_build_query($params) : $params);
}


// Check for custom headers
if (isset($options['headers']) && count($options['headers'])) {
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $options['headers']);
}

// Check for basic auth
if (isset($options['auth']['type']) && 'basic' === $options['auth']['type']) {
curl_setopt($this->curl, CURLOPT_USERPWD, $options['auth']['username'].':'.$options['auth']['password']);
}

$response = $this->doCurl();
// Separate headers and body
$headerSize = $response['curl_info']['header_size'];
$header = substr($response['response'], 0, $headerSize);
$body = substr($response['response'], $headerSize);
$results = array(
'curl_info' => $response['curl_info'],
'content_type' => $response['curl_info']['content_type'],
'status' => $response['curl_info']['http_code'],
'headers' => $this->splitHeaders($header),
'data' => $body,
);

return $results;
}

/**
* make cURL file.
*
* @param string $filename
*
* @return \CURLFile|string
*/
protected function createCurlFile($filename)
{
if (function_exists('curl_file_create')) {
return curl_file_create($filename);
}

return "@$filename;filename=".basename($filename);
}

/**
* Split the HTTP headers.
*
* @param string $rawHeaders
*
* @return array
*/
protected function splitHeaders($rawHeaders)
{
$headers = array();

$lines = explode("\n", trim($rawHeaders));
$headers['HTTP'] = array_shift($lines);

foreach ($lines as $h) {
$h = explode(':', $h, 2);

if (isset($h[1])) {
$headers[$h[0]] = trim($h[1]);
}
}

return $headers;
}

/**
* Perform the Curl request.
* @return array
* @throws \Exception
*/
protected function doCurl()
{
$response = curl_exec($this->curl);
if (curl_errno($this->curl)) {
throw new \Exception(curl_error($this->curl), 1);
}

$curlInfo = curl_getinfo($this->curl);
$results = array(
'curl_info' => $curlInfo,
'response' => $response,
);

return $results;
}
}

+ 249
- 0
src/Kernel/AccessToken.php View File

@@ -0,0 +1,249 @@
<?php
/*
* This file is part of the OtkurBiz/ByteDance.
*
* (c) alim <alim@bulutbazar.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace ByteDance\Kernel;

use ByteDance\Kernel\Contracts\AccessTokenInterface;
use ByteDance\Kernel\Exceptions\HttpException;
use ByteDance\Kernel\Exceptions\InvalidArgumentException;
use ByteDance\Kernel\Exceptions\RuntimeException;
use ByteDance\Kernel\Traits\HasHttpRequests;
use ByteDance\Kernel\Traits\InteractsWithCache;
use Pimple\Container;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

/**
* Class AccessToken.
*
* @author alim <alim@bulutbazar.com>
*/
abstract class AccessToken implements AccessTokenInterface
{
use HasHttpRequests;
use InteractsWithCache;
/**
* @var \Pimple\Container
*/
protected $app;
/**
* @var string
*/
protected $requestMethod = 'GET';
/**
* @var string
*/
protected $endpointToGetToken;
/**
* @var string
*/
protected $queryName;
/**
* @var array
*/
protected $token;

/**
* @var int
*/
protected $safeSeconds = 500;
/**
* @var string
*/
protected $tokenKey = 'access_token';
/**
* @var string
*/
protected $cachePrefix = 'otkurbiz.bytedance.kernel.access_token.';

/**
* AccessToken constructor.
*
* @param \Pimple\Container $app
*/
public function __construct(Container $app)
{
$this->app = $app;
}

/**
* @param bool $refresh
*
* @throws \ByteDance\Kernel\Exceptions\HttpException
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \ByteDance\Kernel\Exceptions\InvalidConfigException
* @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
* @throws \ByteDance\Kernel\Exceptions\RuntimeException
*
* @return array
*/
public function getToken(bool $refresh = false): array
{
$cacheKey = $this->getCacheKey();
$cached = $this->getCache()->fetch($cacheKey);
if (!$refresh && !empty($cached)) {
return $cached;
}
$token = $this->requestToken($this->getCredentials(), true);
$this->setToken($token, $token['expires_in'] ?? 7200);

return $token;
}

/**
* @param string $token
* @param int $lifetime
*
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \ByteDance\Kernel\Exceptions\RuntimeException
*
* @return \ByteDance\Kernel\Contracts\AccessTokenInterface
*/
public function setToken(array $result, $lifetime = 7200): AccessTokenInterface
{
$cache = $this->getCache();
$cache->save($this->getCacheKey(), $result, $lifetime - $this->safeSeconds);
$cacheKey = $this->getCacheKey();
if (empty($this->getCache()->fetch($cacheKey))) {
throw new RuntimeException('Failed to cache access token.');
}

return $this;
}

/**
* @throws \ByteDance\Kernel\Exceptions\HttpException
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \ByteDance\Kernel\Exceptions\InvalidConfigException
* @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
* @throws \ByteDance\Kernel\Exceptions\RuntimeException
*
* @return \ByteDance\Kernel\Contracts\AccessTokenInterface
*/
public function refresh(): AccessTokenInterface
{
$this->getToken(true);

return $this;
}

/**
* @param array $credentials
* @param bool $toArray
*
* @throws \ByteDance\Kernel\Exceptions\HttpException
* @throws \ByteDance\Kernel\Exceptions\InvalidConfigException
* @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
*
* @return \Psr\Http\Message\ResponseInterface|\ByteDance\Kernel\Support\Collection|array|object|string
*/
public function requestToken(array $credentials, $toArray = false)
{
$response = $this->sendRequest($credentials);
$result = json_decode($response->getBody()->getContents(), true);
$formatted = $this->castResponseToType($response, $this->app['config']->get('response_type'));
if (empty($result[$this->tokenKey])) {
throw new HttpException('Request access_token fail: '.json_encode($result, JSON_UNESCAPED_UNICODE), $response, $formatted);
}

return $toArray ? $result : $formatted;
}

/**
* @param \Psr\Http\Message\RequestInterface $request
* @param array $requestOptions
*
* @throws \ByteDance\Kernel\Exceptions\HttpException
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \ByteDance\Kernel\Exceptions\InvalidConfigException
* @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
* @throws \ByteDance\Kernel\Exceptions\RuntimeException
*
* @return \Psr\Http\Message\RequestInterface
*/
public function applyToRequest(RequestInterface $request, array $requestOptions = []): RequestInterface
{
parse_str($request->getUri()->getQuery(), $query);
$query = http_build_query(array_merge($this->getQuery(), $query));

return $request->withUri($request->getUri()->withQuery($query));
}

/**
* Send http request.
*
* @param array $credentials
*
* @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
*
* @return ResponseInterface
*/
protected function sendRequest(array $credentials): ResponseInterface
{
$options = [
('GET' === $this->requestMethod) ? 'query' : 'json' => $credentials,
];

return $this->setHttpClient($this->app['http_client'])->request($this->getEndpoint(), $this->requestMethod, $options);
}

/**
* @return string
*/
protected function getCacheKey()
{
return $this->cachePrefix.md5(json_encode($this->getCredentials()));
}

/**
* The request query will be used to add to the request.
*
* @throws \ByteDance\Kernel\Exceptions\HttpException
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \ByteDance\Kernel\Exceptions\InvalidConfigException
* @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
* @throws \ByteDance\Kernel\Exceptions\RuntimeException
*
* @return array
*/
protected function getQuery(): array
{
return [$this->queryName ?? $this->tokenKey => $this->getToken()[$this->tokenKey]];
}

/**
* @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
*
* @return string
*/
public function getEndpoint(): string
{
if (empty($this->endpointToGetToken)) {
throw new InvalidArgumentException('No endpoint for access token request.');
}

return $this->endpointToGetToken;
}

/**
* @return string
*/
public function getTokenKey()
{
return $this->tokenKey;
}

/**
* Credential for get token.
*
* @return array
*/
abstract protected function getCredentials(): array;
}

+ 256
- 0
src/Kernel/BaseClient.php View File

@@ -0,0 +1,256 @@
<?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 ByteDance\Kernel;

use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Middleware;
use ByteDance\Kernel\Contracts\AccessTokenInterface;
use ByteDance\Kernel\Http\Response;
use ByteDance\Kernel\Traits\HasHttpRequests;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

/**
* Class BaseClient.
*
* @author overtrue <i@overtrue.me>
*/
class BaseClient
{
use HasHttpRequests { request as performRequest; }
/**
* @var \ByteDance\Kernel\ServiceContainer
*/
protected $app;
/**
* @var \ByteDance\Kernel\Contracts\AccessTokenInterface
*/
protected $accessToken;
/**
* @var
*/
protected $baseUri;

/**
* BaseClient constructor.
*
* @param \ByteDance\Kernel\ServiceContainer $app
* @param \ByteDance\Kernel\Contracts\AccessTokenInterface|null $accessToken
*/
public function __construct(ServiceContainer $app, AccessTokenInterface $accessToken = null)
{
$this->app = $app;
$this->accessToken = $accessToken ?? $this->app['access_token'];
}

/**
* GET request.
*
* @param string $url
* @param array $query
*
* @throws \ByteDance\Kernel\Exceptions\InvalidConfigException
*
* @return \Psr\Http\Message\ResponseInterface|\ByteDance\Kernel\Support\Collection|array|object|string
*/
public function httpGet(string $url, array $query = [])
{
return $this->request($url, 'GET', ['query' => $query]);
}

/**
* POST request.
*
* @param string $url
* @param array $data
*
* @throws \ByteDance\Kernel\Exceptions\InvalidConfigException
*
* @return \Psr\Http\Message\ResponseInterface|\ByteDance\Kernel\Support\Collection|array|object|string
*/
public function httpPost(string $url, array $data = [])
{
return $this->request($url, 'POST', ['form_params' => $data]);
}

/**
* JSON request.
*
* @param string $url
* @param string|array $data
* @param array $query
*
* @throws \ByteDance\Kernel\Exceptions\InvalidConfigException
*
* @return \Psr\Http\Message\ResponseInterface|\ByteDance\Kernel\Support\Collection|array|object|string
*/
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
*
* @throws \ByteDance\Kernel\Exceptions\InvalidConfigException
*
* @return \Psr\Http\Message\ResponseInterface|\ByteDance\Kernel\Support\Collection|array|object|string
*/
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]);
}

/**
* @return AccessTokenInterface
*/
public function getAccessToken(): AccessTokenInterface
{
return $this->accessToken;
}

/**
* @param \ByteDance\Kernel\Contracts\AccessTokenInterface $accessToken
*
* @return $this
*/
public function setAccessToken(AccessTokenInterface $accessToken)
{
$this->accessToken = $accessToken;

return $this;
}

/**
* @param string $url
* @param string $method
* @param array $options
* @param bool $returnRaw
*
* @return \Psr\Http\Message\ResponseInterface|\ByteDance\Kernel\Support\Collection|array|object|string
* @throws \GuzzleHttp\Exception\GuzzleException
*
* @throws \ByteDance\Kernel\Exceptions\InvalidConfigException
* @throws Exceptions\InvalidArgumentException
*/
public function request(string $url, string $method = 'GET', array $options = [], $returnRaw = false)
{
if (empty($this->middlewares)) {
$this->registerHttpMiddlewares();
}
$response = $this->performRequest($url, $method, $options);

return $returnRaw ? $response : $this->castResponseToType($response, $this->app->config->get('response_type'));
}

/**
* @param string $url
* @param string $method
* @param array $options
* @return Response
* @throws Exceptions\InvalidArgumentException
* @throws 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');
// access token
$this->pushMiddleware($this->accessTokenMiddleware(), 'access_token');
// log
$this->pushMiddleware($this->logMiddleware(), 'log');
}

/**
* Attache access token to request query.
*
* @return \Closure
*/
protected function accessTokenMiddleware()
{
return function (callable $handler) {
return function (RequestInterface $request, array $options) use ($handler) {
if ($this->accessToken) {
$request = $this->accessToken->applyToRequest($request, $options);
}

return $handler($request, $options);
};
};
}

/**
* 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);
}

/**
* 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));
});
}
}

+ 63
- 0
src/Kernel/Clauses/Clause.php View File

@@ -0,0 +1,63 @@
<?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 ByteDance\Kernel\Clauses;

/**
* Class Clause.
*
* @author mingyoung <mingyoungcheung@gmail.com>
*/
class Clause
{
/**
* @var array
*/
protected $clauses = [
'where' => [],
];

/**
* @param mixed ...$args
*
* @return $this
*/
public function where(...$args)
{
array_push($this->clauses['where'], $args);

return $this;
}

/**
* @param mixed $payload
*
* @return bool
*/
public function intercepted($payload)
{
return (bool) $this->interceptWhereClause($payload);
}

/**
* @param mixed $payload
*
* @return bool
*/
protected function interceptWhereClause($payload)
{
foreach ($this->clauses['where'] as $item) {
list($key, $value) = $item;
if (isset($payload[$key]) && $payload[$key] !== $value) {
return true;
}
}
}
}

+ 22
- 0
src/Kernel/Config.php View File

@@ -0,0 +1,22 @@
<?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 ByteDance\Kernel;

use ByteDance\Kernel\Support\Collection;

/**
* Class Config.
*
* @author overtrue <i@overtrue.me>
*/
class Config extends Collection
{
}

+ 39
- 0
src/Kernel/Contracts/AccessTokenInterface.php View File

@@ -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 ByteDance\Kernel\Contracts;

use Psr\Http\Message\RequestInterface;

/**
* Interface AuthorizerAccessToken.
*
* @author overtrue <i@overtrue.me>
*/
interface AccessTokenInterface
{
/**
* @return array
*/
public function getToken(): array;

/**
* @return \ByteDance\Kernel\Contracts\AccessTokenInterface
*/
public function refresh(): self;

/**
* @param \Psr\Http\Message\RequestInterface $request
* @param array $requestOptions
*
* @return \Psr\Http\Message\RequestInterface
*/
public function applyToRequest(RequestInterface $request, array $requestOptions = []): RequestInterface;
}

+ 28
- 0
src/Kernel/Contracts/Arrayable.php View File

@@ -0,0 +1,28 @@
<?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 ByteDance\Kernel\Contracts;

use ArrayAccess;

/**
* Interface Arrayable.
*
* @author overtrue <i@overtrue.me>
*/
interface Arrayable extends ArrayAccess
{
/**
* Get the instance as an array.
*
* @return array
*/
public function toArray();
}

+ 25
- 0
src/Kernel/Contracts/EventHandlerInterface.php View File

@@ -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 ByteDance\Kernel\Contracts;

/**
* Interface EventHandlerInterface.
*
* @author mingyoung <mingyoungcheung@gmail.com>
*/
interface EventHandlerInterface
{
/**
* @param mixed $payload
*/
public function handle($payload = null);
}

+ 35
- 0
src/Kernel/Decorators/FinallyResult.php View File

@@ -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 ByteDance\Kernel\Decorators;

/**
* Class FinallyResult.
*
* @author overtrue <i@overtrue.me>
*/
class FinallyResult
{
/**
* @var mixed
*/
public $content;

/**
* FinallyResult constructor.
*
* @param mixed $content
*/
public function __construct($content)
{
$this->content = $content;
}
}

+ 34
- 0
src/Kernel/Decorators/TerminateResult.php View File

@@ -0,0 +1,34 @@
<?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 ByteDance\Kernel\Decorators;

/**
* Class TerminateResult.
*
* @author overtrue <i@overtrue.me>
*/
class TerminateResult
{
/**
* @var mixed
*/
public $content;

/**
* FinallyResult constructor.
*
* @param mixed $content
*/
public function __construct($content)
{
$this->content = $content;
}
}

+ 113
- 0
src/Kernel/Encryptor.php View File

@@ -0,0 +1,113 @@
<?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 ByteDance\Kernel;

use ByteDance\Kernel\Exceptions\RuntimeException;
use ByteDance\Kernel\Support\AES;
use Throwable;
use function ByteDance\Kernel\Support\str_random;

/**
* Class Encryptor.
*
* @author overtrue <i@overtrue.me>
*/
class Encryptor
{
const ERROR_INVALID_SIGNATURE = -40001; // Signature verification failed
const ERROR_CALC_SIGNATURE = -40003; // Calculating the signature failed
const ERROR_INVALID_AES_KEY = -40004; // Invalid AESKey
const ERROR_INVALID_APP_ID = -40005; // Check AppID failed
const ERROR_ENCRYPT_AES = -40006; // AES EncryptionInterface failed
const ERROR_DECRYPT_AES = -40007; // AES decryption failed
const ERROR_BASE64_ENCODE = -40009; // Base64 encoding failed
const ERROR_BASE64_DECODE = -40010; // Base64 decoding failed
const ILLEGAL_BUFFER = -41003; // Illegal buffer

/**
* @var string
*/
protected $aesKey;

/**
* Block size.
*
* @var int
*/
protected $blockSize = 32;

/**
* Constructor.
*
* @param string $appId
* @param string|null $token
* @param string|null $aesKey
*/
public function __construct(string $aesKey = null)
{
$this->aesKey = base64_decode($aesKey.'=', true);
}


/**
* Get SHA1.
*
* @return string
*
* @throws self
*/
public function signature(): string
{
$array = func_get_args();
sort($array, SORT_STRING);

return sha1(implode($array));
}

/**
* PKCS#7 pad.
*
* @param string $text
* @param int $blockSize
*
* @return string
*
* @throws \ByteDance\Kernel\Exceptions\RuntimeException
*/
public function pkcs7Pad(string $text, int $blockSize): string
{
if ($blockSize > 256) {
throw new RuntimeException('$blockSize may not be more than 256');
}
$padding = $blockSize - (strlen($text) % $blockSize);
$pattern = chr($padding);

return $text.str_repeat($pattern, $padding);
}

/**
* PKCS#7 unpad.
*
* @param string $text
*
* @return string
*/
public function pkcs7Unpad(string $text): string
{
$pad = ord(substr($text, -1));
if ($pad < 1 || $pad > $this->blockSize) {
$pad = 0;
}

return substr($text, 0, (strlen($text) - $pad));
}
}

+ 21
- 0
src/Kernel/Exceptions/BadRequestException.php View File

@@ -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 ByteDance\Kernel\Exceptions;

/**
* Class BadRequestException.
*
* @author overtrue <i@overtrue.me>
*/
class BadRequestException extends Exception
{
}

+ 16
- 0
src/Kernel/Exceptions/DecryptException.php View File

@@ -0,0 +1,16 @@
<?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 ByteDance\Kernel\Exceptions;

class DecryptException extends Exception
{
}

+ 23
- 0
src/Kernel/Exceptions/Exception.php View File

@@ -0,0 +1,23 @@
<?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 ByteDance\Kernel\Exceptions;

use Exception as BaseException;

/**
* Class Exception.
*
* @author overtrue <i@overtrue.me>
*/
class Exception extends BaseException
{
}

+ 52
- 0
src/Kernel/Exceptions/HttpException.php View File

@@ -0,0 +1,52 @@
<?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 ByteDance\Kernel\Exceptions;

use Psr\Http\Message\ResponseInterface;

/**
* Class HttpException.
*
* @author overtrue <i@overtrue.me>
*/
class HttpException extends Exception
{
/**
* @var \Psr\Http\Message\ResponseInterface|null
*/
public $response;

/**
* @var \Psr\Http\Message\ResponseInterface|\ByteDance\Kernel\Support\Collection|array|object|string
*/
public $formattedResponse;

/**
* HttpException constructor.
*
* @param string $message
* @param \Psr\Http\Message\ResponseInterface|null $response
* @param null $formattedResponse
* @param int|null $code
*/
public function __construct($message, ResponseInterface $response = null, $formattedResponse = null, $code = null)
{
parent::__construct($message, $code);

$this->response = $response;
$this->formattedResponse = $formattedResponse;

if ($response) {
$response->getBody()->rewind();
}
}
}

+ 21
- 0
src/Kernel/Exceptions/InvalidArgumentException.php View File

@@ -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 ByteDance\Kernel\Exceptions;

/**
* Class InvalidArgumentException.
*
* @author overtrue <i@overtrue.me>
*/
class InvalidArgumentException extends Exception
{
}

+ 21
- 0
src/Kernel/Exceptions/InvalidConfigException.php View File

@@ -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 ByteDance\Kernel\Exceptions;

/**
* Class InvalidConfigException.
*
* @author overtrue <i@overtrue.me>
*/
class InvalidConfigException extends Exception
{
}

+ 21
- 0
src/Kernel/Exceptions/RuntimeException.php View File

@@ -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 ByteDance\Kernel\Exceptions;

/**
* Class RuntimeException.
*
* @author overtrue <i@overtrue.me>
*/
class RuntimeException extends Exception
{
}

+ 21
- 0
src/Kernel/Exceptions/UnboundServiceException.php View File

@@ -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 ByteDance\Kernel\Exceptions;

/**
* Class InvalidConfigException.
*
* @author overtrue <i@overtrue.me>
*/
class UnboundServiceException extends Exception
{
}

+ 40
- 86
src/Kernel/Helpers.php View File

@@ -1,99 +1,53 @@
<?php

namespace TouTiao\Kernel;

/**
* Generate a signature.
/*
* This file is part of the overtrue/wechat.
*
* @param array $attributes
* @param string $key
* @param string $encryptMethod
* (c) overtrue <i@overtrue.me>
*
* @return string
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
function generate_sign(array $attributes, $key, $encryptMethod = 'md5')
{
ksort($attributes);

$attributes['key'] = $key;
namespace ByteDance\Kernel;

return strtoupper(call_user_func_array($encryptMethod, [urldecode(http_build_query($attributes))]));
}
use ByteDance\Kernel\Contracts\Arrayable;
use ByteDance\Kernel\Exceptions\RuntimeException;
use ByteDance\Kernel\Support\Arr;
use ByteDance\Kernel\Support\Collection;

/**
* Get client ip.
*
* @return string
*/
function get_client_ip()
function data_get($data, $key, $default = null)
{
if (!empty($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
} else {
// for php-cli(phpunit etc.)
$ip = defined('PHPUNIT_RUNNING') ? '127.0.0.1' : gethostbyname(gethostname());
switch (true) {
case is_array($data):
return Arr::get($data, $key, $default);
case $data instanceof Collection:
return $data->get($key, $default);
case $data instanceof Arrayable:
return Arr::get($data->toArray(), $key, $default);
case $data instanceof \ArrayIterator:
return $data->getArrayCopy()[$key] ?? $default;
case $data instanceof \ArrayAccess:
return $data[$key] ?? $default;
case $data instanceof \IteratorAggregate && $data->getIterator() instanceof \ArrayIterator:
return $data->getIterator()->getArrayCopy()[$key] ?? $default;
default:
throw new RuntimeException(sprintf('Can\'t access data with key "%s"', $key));
}

return filter_var($ip, FILTER_VALIDATE_IP) ?: '127.0.0.1';
}

/**
* Get current server ip.
*
* @return string
*/
function get_server_ip()
function data_to_array($data)
{
if (!empty($_SERVER['SERVER_ADDR'])) {
$ip = $_SERVER['SERVER_ADDR'];
} elseif (!empty($_SERVER['SERVER_NAME'])) {
$ip = gethostbyname($_SERVER['SERVER_NAME']);
} else {
// for php-cli(phpunit etc.)
$ip = defined('PHPUNIT_RUNNING') ? '127.0.0.1' : gethostbyname(gethostname());
switch (true) {
case is_array($data):
return $data;
case $data instanceof Collection:
return $data->all();
case $data instanceof Arrayable:
return $data->toArray();
case $data instanceof \IteratorAggregate && $data->getIterator() instanceof \ArrayIterator:
return $data->getIterator()->getArrayCopy();
case $data instanceof \ArrayIterator:
return $data->getArrayCopy();
default:
throw new RuntimeException(sprintf('Can\'t transform data to array'));
}

return filter_var($ip, FILTER_VALIDATE_IP) ?: '127.0.0.1';
}

/**
* Return current url.
*
* @return string
*/
function current_url()
{
$protocol = 'http://';

if ((!empty($_SERVER['HTTPS']) && 'off' !== $_SERVER['HTTPS']) || ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? 'http') === 'https') {
$protocol = 'https://';
}

return $protocol.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}

/**
* Return random string.
*
* @param string $length
*
* @return string
*/
function str_random($length)
{
return \Str::random($length);
}

/**
* @param string $content
* @param string $publicKey
*
* @return string
*/
function rsa_public_encrypt($content, $publicKey)
{
$encrypted = '';
openssl_public_encrypt($content, $encrypted, openssl_pkey_get_public($publicKey), OPENSSL_PKCS1_OAEP_PADDING);

return base64_encode($encrypted);
}

+ 116
- 0
src/Kernel/Http/Response.php View File

@@ -0,0 +1,116 @@
<?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 ByteDance\Kernel\Http;

use GuzzleHttp\Psr7\Response as GuzzleResponse;
use ByteDance\Kernel\Support\Collection;
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 \ByteDance\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());
<