初始化项目:添加后端代码、ThinkPHP框架、前端资源
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Yansongda\Supports\Traits;
|
||||
|
||||
trait Accessable
|
||||
{
|
||||
/**
|
||||
* __get.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @param $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* __set.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @param $key
|
||||
* @param $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __set($key, $value)
|
||||
{
|
||||
return $this->set($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* get.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @param null $key
|
||||
* @param null $default
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function get($key = null, $default = null)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
return method_exists($this, 'toArray') ? $this->toArray() : $default;
|
||||
}
|
||||
|
||||
$method = 'get';
|
||||
foreach (explode('_', $key) as $item) {
|
||||
$method .= ucfirst($item);
|
||||
}
|
||||
|
||||
if (method_exists($this, $method)) {
|
||||
return $this->{$method}();
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* set.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @param $key
|
||||
* @param $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$method = 'set';
|
||||
foreach (explode('_', $key) as $item) {
|
||||
$method .= ucfirst($item);
|
||||
}
|
||||
|
||||
if (method_exists($this, $method)) {
|
||||
return $this->{$method}($value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return !is_null($this->get($offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->set($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Yansongda\Supports\Traits;
|
||||
|
||||
use ReflectionClass;
|
||||
use Yansongda\Supports\Str;
|
||||
|
||||
trait Arrayable
|
||||
{
|
||||
/**
|
||||
* toArray.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ((new ReflectionClass($this))->getProperties() as $item) {
|
||||
$k = $item->getName();
|
||||
|
||||
$result[Str::snake($k)] = $this->{$item->getName()};
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
namespace Yansongda\Supports\Traits;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Trait HasHttpRequest.
|
||||
*
|
||||
* @property string $baseUri
|
||||
* @property float $timeout
|
||||
* @property float $connectTimeout
|
||||
*/
|
||||
trait HasHttpRequest
|
||||
{
|
||||
/**
|
||||
* Http client.
|
||||
*
|
||||
* @var Client|null
|
||||
*/
|
||||
protected $httpClient = null;
|
||||
|
||||
/**
|
||||
* Http client options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $httpOptions = [];
|
||||
|
||||
/**
|
||||
* Send a GET request.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @param string $endpoint
|
||||
* @param array $query
|
||||
* @param array $headers
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public function get($endpoint, $query = [], $headers = [])
|
||||
{
|
||||
return $this->request('get', $endpoint, [
|
||||
'headers' => $headers,
|
||||
'query' => $query,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a POST request.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @param string $endpoint
|
||||
* @param string|array $data
|
||||
* @param array $options
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public function post($endpoint, $data, $options = [])
|
||||
{
|
||||
if (!is_array($data)) {
|
||||
$options['body'] = $data;
|
||||
} else {
|
||||
$options['form_params'] = $data;
|
||||
}
|
||||
|
||||
return $this->request('post', $endpoint, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send request.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @param string $method
|
||||
* @param string $endpoint
|
||||
* @param array $options
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public function request($method, $endpoint, $options = [])
|
||||
{
|
||||
return $this->unwrapResponse($this->getHttpClient()->{$method}($endpoint, $options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set http client.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHttpClient(Client $client): self
|
||||
{
|
||||
$this->httpClient = $client;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return http client.
|
||||
*/
|
||||
public function getHttpClient(): Client
|
||||
{
|
||||
if (is_null($this->httpClient)) {
|
||||
$this->httpClient = $this->getDefaultHttpClient();
|
||||
}
|
||||
|
||||
return $this->httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default http client.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*/
|
||||
public function getDefaultHttpClient(): Client
|
||||
{
|
||||
return new Client($this->getOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* setBaseUri.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setBaseUri(string $url): self
|
||||
{
|
||||
if (property_exists($this, 'baseUri')) {
|
||||
$parsedUrl = parse_url($url);
|
||||
|
||||
$this->baseUri = $parsedUrl['scheme'].'://'.
|
||||
$parsedUrl['host'].(isset($parsedUrl['port']) ? (':'.$parsedUrl['port']) : '');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* getBaseUri.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*/
|
||||
public function getBaseUri(): string
|
||||
{
|
||||
return property_exists($this, 'baseUri') ? $this->baseUri : '';
|
||||
}
|
||||
|
||||
public function getTimeout(): float
|
||||
{
|
||||
return property_exists($this, 'timeout') ? $this->timeout : 5.0;
|
||||
}
|
||||
|
||||
public function setTimeout(float $timeout): self
|
||||
{
|
||||
if (property_exists($this, 'timeout')) {
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConnectTimeout(): float
|
||||
{
|
||||
return property_exists($this, 'connectTimeout') ? $this->connectTimeout : 3.0;
|
||||
}
|
||||
|
||||
public function setConnectTimeout(float $connectTimeout): self
|
||||
{
|
||||
if (property_exists($this, 'connectTimeout')) {
|
||||
$this->connectTimeout = $connectTimeout;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default options.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*/
|
||||
public function getOptions(): array
|
||||
{
|
||||
return array_merge([
|
||||
'base_uri' => $this->getBaseUri(),
|
||||
'timeout' => $this->getTimeout(),
|
||||
'connect_timeout' => $this->getConnectTimeout(),
|
||||
], $this->getHttpOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* setOptions.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setOptions(array $options): self
|
||||
{
|
||||
return $this->setHttpOptions($options);
|
||||
}
|
||||
|
||||
public function getHttpOptions(): array
|
||||
{
|
||||
return $this->httpOptions;
|
||||
}
|
||||
|
||||
public function setHttpOptions(array $httpOptions): self
|
||||
{
|
||||
$this->httpOptions = $httpOptions;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert response.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public function unwrapResponse(ResponseInterface $response)
|
||||
{
|
||||
$contentType = $response->getHeaderLine('Content-Type');
|
||||
$contents = $response->getBody()->getContents();
|
||||
|
||||
if (false !== stripos($contentType, 'json') || stripos($contentType, 'javascript')) {
|
||||
return json_decode($contents, true);
|
||||
} elseif (false !== stripos($contentType, 'xml')) {
|
||||
return json_decode(json_encode(simplexml_load_string($contents, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Yansongda\Supports\Traits;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
trait Serializable
|
||||
{
|
||||
/**
|
||||
* toJson.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toJson()
|
||||
{
|
||||
return $this->serialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON.
|
||||
*
|
||||
* @see https://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
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
if (method_exists($this, 'toArray')) {
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* String representation of object.
|
||||
*
|
||||
* @see https://php.net/manual/en/serializable.serialize.php
|
||||
*
|
||||
* @return string the string representation of the object or null
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
if (method_exists($this, 'toArray')) {
|
||||
return json_encode($this->toArray());
|
||||
}
|
||||
|
||||
return json_encode([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the object.
|
||||
*
|
||||
* @see https://php.net/manual/en/serializable.unserialize.php
|
||||
*
|
||||
* @param string $serialized <p>
|
||||
* The string representation of the object.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$data = json_decode($serialized, true);
|
||||
|
||||
if (JSON_ERROR_NONE !== json_last_error()) {
|
||||
throw new RuntimeException('Invalid Json Format');
|
||||
}
|
||||
|
||||
foreach ($data as $key => $item) {
|
||||
if (method_exists($this, 'set')) {
|
||||
$this->set($key, $item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace Yansongda\Supports\Traits;
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
/**
|
||||
* Trait ShouldThrottle.
|
||||
*
|
||||
* @property Client $redis
|
||||
*/
|
||||
trait ShouldThrottle
|
||||
{
|
||||
/**
|
||||
* _throttle.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_throttle = [
|
||||
'limit' => 60,
|
||||
'period' => 60,
|
||||
'count' => 0,
|
||||
'reset_time' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* isThrottled.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @param string $key
|
||||
* @param int $limit
|
||||
* @param int $period
|
||||
* @param bool $auto_add
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isThrottled($key, $limit = 60, $period = 60, $auto_add = false)
|
||||
{
|
||||
if (-1 === $limit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$now = microtime(true) * 1000;
|
||||
|
||||
$this->redis->zremrangebyscore($key, 0, $now - $period * 1000);
|
||||
|
||||
$this->_throttle = [
|
||||
'limit' => $limit,
|
||||
'period' => $period,
|
||||
'count' => $this->getThrottleCounts($key, $period),
|
||||
'reset_time' => $this->getThrottleResetTime($key, $now),
|
||||
];
|
||||
|
||||
if ($this->_throttle['count'] < $limit) {
|
||||
if ($auto_add) {
|
||||
$this->throttleAdd($key, $period);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 限流 + 1.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @param string $key
|
||||
* @param int $period
|
||||
*/
|
||||
public function throttleAdd($key, $period = 60)
|
||||
{
|
||||
$now = microtime(true) * 1000;
|
||||
|
||||
$this->redis->zadd($key, [$now => $now]);
|
||||
$this->redis->expire($key, $period * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* getResetTime.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @param $key
|
||||
* @param $now
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getThrottleResetTime($key, $now)
|
||||
{
|
||||
$data = $this->redis->zrangebyscore(
|
||||
$key,
|
||||
$now - $this->_throttle['period'] * 1000,
|
||||
$now,
|
||||
['limit' => [0, 1]]
|
||||
);
|
||||
|
||||
if (0 === count($data)) {
|
||||
return $this->_throttle['reset_time'] = time() + $this->_throttle['period'];
|
||||
}
|
||||
|
||||
return intval($data[0] / 1000) + $this->_throttle['period'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取限流相关信息.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @param string|null $key
|
||||
* @param mixed|null $default
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getThrottleInfo($key = null, $default = null)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
return $this->_throttle;
|
||||
}
|
||||
|
||||
if (isset($this->_throttle[$key])) {
|
||||
return $this->_throttle[$key];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已使用次数.
|
||||
*
|
||||
* @author yansongda <me@yansongda.cn>
|
||||
*
|
||||
* @param string $key
|
||||
* @param int $period
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getThrottleCounts($key, $period = 60)
|
||||
{
|
||||
$now = microtime(true) * 1000;
|
||||
|
||||
return $this->redis->zcount($key, $now - $period * 1000, $now);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user