aogg
11/15/2018 - 8:40 AM

我的帮助类

class类和trait类

我的帮助类

class类和trait类

BucketManager.php

重写垃圾七牛的BucketManager类

替代model的Data,放在mixed

<?php


namespace app\common\library;


class ApiResponseFormat
{
    use \app\common\library\traits\Singleton;

    protected $codeValueSuccessDefault = 1;
    protected $codeValueErrorDefault = 0;
    protected $codeValue = null;
    protected $dataValue = null;
    protected $msgValue = '';

    protected $codeKeyName = 'code';
    protected $dataKeyName = 'data';
    protected $msgKeyName = 'msg';

    protected $msgPrefix = '';

    /**
     * @param string $codeKeyName
     * @return $this
     */
    public function setCodeKeyName(string $codeKeyName)
    {
        $this->codeKeyName = $codeKeyName;
        return $this;
    }

    /**
     * @param string $dataKeyName
     * @return $this
     */
    public function setDataKeyName(string $dataKeyName)
    {
        $this->dataKeyName = $dataKeyName;
        return $this;
    }

    /**
     * @param string $msgKeyName
     * @return $this
     */
    public function setMsgKeyName(string $msgKeyName)
    {
        $this->msgKeyName = $msgKeyName;
        return $this;
    }

    /**
     * @param int $codeValue
     * @return $this
     */
    public function setCodeValue($codeValue)
    {
        $this->codeValue = $codeValue;
        return $this;
    }

    /**
     * @param int $codeValue
     * @return $this
     */
    public function setCodeErrorValue($codeValue)
    {
        $this->codeValue = $codeValue??$this->getCodeValueErrorDefault();

        return $this;
    }

    /**
     * @param null $dataValue
     * @return $this
     */
    public function setDataValue($dataValue)
    {
        $this->dataValue = $dataValue;
        return $this;
    }

    /**
     * @param string $msgValue
     * @return $this
     */
    public function setMsgValue(string $msgValue)
    {
        $this->msgValue = $msgValue;
        return $this;
    }

    public function getArr()
    {
        return [
            $this->codeKeyName => $this->codeValue??$this->codeValueSuccessDefault,
            $this->dataKeyName => $this->dataValue,
            $this->msgKeyName => $this->msgPrefix . $this->msgValue,
        ];
    }

    /**
     * @return string
     */
    public function getMsgKeyName(): string
    {
        return $this->msgKeyName;
    }

    /**
     * @param int $codeValueSuccessDefault
     * @return $this
     */
    public function setCodeValueSuccessDefault(int $codeValueSuccessDefault)
    {
        $this->codeValueSuccessDefault = $codeValueSuccessDefault;
        return $this;
    }

    /**
     * @return int
     */
    public function getCodeValueSuccessDefault()
    {
        return $this->codeValueSuccessDefault;
    }

    /**
     * @param string $msgPrefix
     * @return $this
     */
    public function setMsgPrefix(string $msgPrefix)
    {
        $this->msgPrefix = $msgPrefix;
        return $this;
    }

    /**
     * @return int
     */
    public function getCodeValueErrorDefault()
    {
        return $this->codeValueErrorDefault;
    }

    /**
     * @param int $codeValueErrorDefault
     * @return $this
     */
    public function setCodeValueErrorDefault(int $codeValueErrorDefault)
    {
        $this->codeValueErrorDefault = $codeValueErrorDefault;
        return $this;
    }


}
<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2018-12-26
 * Time: 16:59
 */

namespace App\Repositories\Common\QiNiu;

use Qiniu\Auth;
use Qiniu\Config;
use Qiniu\Http\Client;
use Qiniu\Http\Error;

/**
 * 通过赋值的方式重写BucketManager
 *
 * 七牛SDK不行
 */
class BucketManager
{
    /**
     * @var \Qiniu\Storage\BucketManager
     */
    protected $bucketManager;

    /**
     * @var Config
     */
    protected $config;

    /**
     * @var Auth
     */
    protected $auth;

    /**
     * 设置回调相关的参数
     *
     * @var array
     */
    protected $callbackData = [];

    protected $authorizationType = 'QBox';


    /**
     * fetch并制定数据
     *
     * 返回:
     * array [
            "id" => "eyJ6b25lIjoiejAiLCJxdWV1ZSI6IlNJU1lQSFVTLUpPQlMtVjMiLCJwYXJ0X2lkIjoyMiwib2Zmc2V0IjoyNjkzMTgxfQ=="
            "wait" => 12
       ]
     *
     * @param $url
     * @param $bucket
     * @param $key
     * @return bool|mixed
     */
    public function fetchV2($url, $bucket, $key)
    {
        $path = '/sisyphus/fetch';

        $ak = $this->getAuth()->getAccessKey();
        $ioHost = $this->getConfig()->getIovipHost($ak, $bucket);
        $apiHost = $this->getConfig()->getApiHost($ak, $bucket);
//        $apiHost = str_replace('api.', 'api-z0.', $apiHost);
        $path = $apiHost . $path;

        $callbackData = [];
        foreach ($this->getCallbackData() as $callbackName => $callbackDatum) { // 转为小写
            $callbackData[strtolower($callbackName)] = $callbackDatum;
        }

        $body = array_filter(array_merge([
            'url' => $url,
            'host' => parse_url($ioHost, PHP_URL_HOST),
            'bucket' => $bucket,
            'key' => $key,
        ], $callbackData)); // 将callback合并

        list($response, $error) = $this->postV2($path, $body);

        if ($error) {
            return false;
        }

        return $response;
    }

    /**
     * 获取fetch的状态
     *
     * @param $fetchId
     * @return bool|mixed
     */
    public function fetchV2Status($fetchId)
    {
        $path = "/sisyphus/fetch?id={$fetchId}";

        $ak = $this->getAuth()->getAccessKey();
        $apiHost = Config::UC_HOST;
//        $apiHost = str_replace('api.', 'api-z0.', $apiHost);
        $path = $apiHost . $path;


        list($response, $error) = $this->getV2($path);

        if ($error) {
            return false;
        }

        return $response;
    }

    /**
     * @param $url
     * @return array
     */
    public function getV2($url)
    {
        $headers = $this->auth->authorizationV2($url, 'GET');
        $ret = Client::get($url, $headers);
        if (!$ret->ok()) {
            return array(null, new Error($url, $ret));
        }
        return array($ret->json(), null);
    }

    /**
     * 公开post方法
     *
     * @see \Qiniu\Storage\BucketManager::post
     * @param $url
     * @param $body
     * @return array
     */
    public function post($url, $body)
    {
        $headers = $this->getAuth()->authorization($url, $body, 'application/x-www-form-urlencoded');
        $ret = Client::post($url, $body, $headers);
        if (!$ret->ok()) {
            return array(null, new Error($url, $ret));
        }
        $r = ($ret->body === null) ? array() : $ret->json();
        return array($r, null);
    }

    /**
     * post请求,使用最新v2七牛协议
     *
     * @param $url
     * @param array $body
     * @return array
     */
    public function postV2($url, $body)
    {
        $bodyJson = json_encode($body);
        $headers = $this->getAuth()->authorizationV2($url, 'POST', $bodyJson, 'application/json');

        $ret = Client::post($url, $bodyJson, $headers + ['Content-Type' => 'application/json']);
        if (!$ret->ok()) {
            return array(null, new Error($url, $ret));
        }
        $r = ($ret->body === null) ? array() : $ret->json();
        return array($r, null);
    }

    /**
     * 获取Authorization的header
     * 可指定上传策略
     *
     * @param $bucket
     * @param null $key 为null的时候只能新增(若已存在同名资源(且文件内容/etag不一致),上传会失败;若已存在资源的内容/etag一致,则上传会返回成功。),
     *                  存在值的时候按(表示只允许用户上传指定 key 的文件。在这种格式下文件默认允许修改,若已存在同名资源则会被覆盖。如果只希望上传指定 key 的文件,并且不允许修改,那么可以将下面的 insertOnly 属性值设为 1。)
     * @param int $expires
     * @param null|array $policy 指定哪些字段
     * @return array
     */
    public function getAuthUploadToken($bucket, $key = null, $policy = null, $expires = 3600)
    {
        // $strictPolicy是是否类型检测
        $authorization =  $this->authorizationType . ' ' . $this->getAuth()->uploadToken($bucket, $key, $expires, $this->handlePolicy($policy), true);

        return array('Authorization' => $authorization);
    }

    /**
     * 获取Authorization的header
     * 可指定上传策略
     *
     * @param $urlString
     * @param $body
     * @param null|string $contentType 当为application/x-www-form-urlencoded时body才有用
     * @return array
     */
    public function getAuthorization($urlString, $body, $contentType = null)
    {
        // $strictPolicy是是否类型检测
        $authorization =  $this->authorizationType . ' ' . $this->getAuth()->signRequest($urlString, $body, $contentType);

        return array('Authorization' => $authorization);
    }

    /**
     * 处理policy
     *
     * @param $policy
     * @return array
     */
    protected function handlePolicy($policy)
    {
        if (is_array($policy)) {
            $policy = array_merge($policy, $this->getCallbackData());
        }

        return $policy;
    }

    /**
     * 调用七牛的BucketManager类库的公有方法
     *
     * @param $name
     * @param $arguments
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        return $this->getBucketManager()->{$name}(...$arguments);
    }


    /**
     * 获取七牛的BucketManager,会初始化
     *
     * @return \Qiniu\Storage\BucketManager
     */
    public function getBucketManager()
    {
        if (is_null($this->bucketManager)){
            $this->bucketManager = new \Qiniu\Storage\BucketManager($this->getAuth(), $this->getConfig());
        }

        return $this->bucketManager;
    }

    /**
     * @param mixed $bucketManager
     * @return $this
     */
    public function setBucketManager($bucketManager)
    {
        $this->bucketManager = $bucketManager;

        return $this;
    }

    /**
     * 获取七牛config类库
     *
     * @return Config
     */
    public function getConfig()
    {
        return $this->config??($this->config = new Config());
    }

    /**
     * @param mixed $config
     * @return $this
     */
    public function setConfig($config)
    {
        $this->config = $config;

        return $this;
    }

    /**
     * @return Auth
     */
    public function getAuth()
    {
        return $this->auth;
    }

    /**
     * @param mixed $auth
     * @return $this
     */
    public function setAuth($auth)
    {
        $this->auth = $auth;

        return $this;
    }

    /**
     * 设置callback的相关数据
     *
     * @param $url
     * @param null $body
     * @param null $bodyType
     * @param null $host
     * @return $this
     */
    public function setCallbackData($url, $body, $bodyType = null, $host = null)
    {
        $callbackData = [
            'callbackUrl' => $url,
            'callbackBody' => $body, // 指定url,必须指定body
        ];

        if (isset($bodyType)) {
            $callbackData['callbackBodyType'] = $bodyType;
        }
        if (isset($host)) {
            $callbackData['callbackHost'] = $host;
        }

        $this->callbackData = $callbackData;

        return $this;
    }

    /**
     * @return array
     */
    public function getCallbackData()
    {
        return $this->callbackData;
    }


    public function setAuthorizationType(string $authorizationType)
    {
        $this->authorizationType = $authorizationType;

        return $this;
    }

    /**
     * v2版本的头
     *
     * @return BucketManager
     */
    public function setAuthorizationTypeQiNiu()
    {
        return $this->setAuthorizationType('Qiniu');
    }

}
<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2018/9/28
 * Time: 上午10:26
 */

namespace App\Repositories\Common;

use App\Repositories\Common\Traits\Singleton;

/**
 * 缓存多个单一的key
 *
 * @since 2018/9/28
 * @package App\Repositories\Common
 */
abstract class CacheMultiAbstract
{
    use Singleton;

    abstract protected function setCachePro($key, $value, $minutes);

    abstract protected function getCachePro($key);

    /**
     * @param string $prefix     缓存key的前缀
     * @param int $timeout       过期时间
     * @param array $data        需要处理的数据,key用于缓存的key
     * @param callable $func     处理没有cache的数据
     * @param bool $emptyDefault 缓存get返回的不存在的key时的值
     * @return array
     */
    public function cacheMulti($prefix, $timeout, array $data, callable $func, $emptyDefault = null)
    {
        if (empty($data)) {
            return [];
        }

        $cacheData = $result = [];
        foreach ($data as $key => $item) {
            $tempData = $this->getCachePro("${prefix}:${key}");

            if ($tempData === $emptyDefault){
                $cacheData[$key] = $item;
            }else{
                $result[$key] = $tempData;
            }
        }

        if (!empty($cacheData)) {
            $cacheData = $func($cacheData);

            if (!empty($cacheData)) {
                foreach ($cacheData as $key => $cacheDatum) {
                    $this->setCachePro("${prefix}:${key}", $cacheDatum, $timeout);
                }
            }

            $result = array_merge($result, $cacheData);
        }

        return $result;
    }
}
<?php
/**
 * User: aozhuochao
 * Date: 2020/11/2
 */

namespace app\modules\core;

/**
 * 扩展facade的方法
 * 实现门面代理的类的方法被中间件原理修改
 */
class FacadeExpand extends \think\Facade
{
    protected static $config = [
        'method' => [], // \app\class::method => function(){}
        'class_expand' => [],
    ];

    protected static $loadDataBool = false;

    protected static function loadData()
    {
        if (static::$loadDataBool){
            return;
        }

        foreach (scanDirFile(dirname(__DIR__), 'facade_expand.php') as $file) {
            $arr = include $file;

            foreach (static::$config as $key => &$item) {
                if (empty($arr[$key]) || !is_array($arr[$key])) {
                    continue;
                }

                if ($key === 'method') { // 方法
                    foreach ($arr[$key] as $localKey => $arrItem) {
                        $callbackArr = explode('::', $localKey);
                        $class = !empty($callbackArr[0])?$callbackArr[0]:'';
                        $method = !empty($callbackArr[1])?$callbackArr[1]:'';

                        if (empty($class) || empty($method)) {
                            continue;
                        }
                        $class = ltrim($class, '\\');

                        if (!isset($item[$class])) {
                            $item[$class] = [];
                        }
                        if (!isset($item[$class][$method])) {
                            $item[$class][$method] = [];
                        }

                        array_push($item[$class][$method], $arrItem);
                    }
                }else if($key === 'class_expand'){ // 类扩展
                    foreach ($arr[$key] as $originClass => $expandClass) {
                        if (!isset($item[$originClass])) {
                            $item[$originClass] = [];
                        }

                        array_push($item[$originClass], $expandClass);
                    }
                }
            }
        }

        static::$loadDataBool = true;
    }

    // 调用实际类的方法
    public static function __callStatic($method, $params)
    {
        static::loadData();

        $className = get_class(static::createFacade());

        $bool = true;
        $data = null;
        $methodBool = !empty(static::$config['method'][$className]) && !empty(static::$config['method'][$className][$method]);
        if ($methodBool) {

            foreach (static::$config['method'][$className][$method] as $arr) {

                // before
                $beforeCallback = !empty($arr[0]) ? $arr[0] : (!empty($arr['before']) ? $arr['before'] : false);
                if (!empty($beforeCallback) && is_callable($beforeCallback)) {
                    $tempBool = call_user_func($beforeCallback, $params);
                    $bool = isset($tempBool) ? $tempBool : $bool;
                }

            }

        }



        if ($bool) {
            $keyNum = count(static::$config['class_expand'][$className]);
            $currentKeyNum = 0;
            $next = function ($params) use ($method, $className, &$next, &$currentKeyNum, $keyNum) {
                if ($keyNum === $currentKeyNum) {
                    return parent::__callStatic($method, $params);
                }
                ++$currentKeyNum;

                if (empty(static::$config['class_expand'][$className])) { // 没有被设置
                    return $next($params);
                }

                /** @var \app\modules\core\FacadeExpandClassInterface $expandClass */
                $expandClass = static::$config['class_expand'][$className][$currentKeyNum - 1];
                if (
                    !isset(class_implements($expandClass)[\app\modules\core\FacadeExpandClassInterface::class])
                ) {
                    return $next($params);
                }

                if (!method_exists($expandClass, $method) || !method_exists($expandClass, 'newClass')){
                    return $next($params);
                }

                $expandClassObject = $expandClass::newClass();
                return $expandClassObject->$method($next, $params);
            };

            $data = $next($params);
        }

        if ($methodBool) {
            foreach (static::$config['method'][$className][$method] as $arr) {
                // after
                $beforeCallback = !empty($arr[1])?$arr[1]:(!empty($arr['after'])?$arr['after']:false);
                if (!empty($beforeCallback) && is_callable($beforeCallback)) {
                    $bool = true; // 使用返回的结果
                    $tempData = call_user_func($beforeCallback, $params, $data, $bool);
                    $data = isset($tempData) && $bool ? $tempData : $data;
                }
            }
            
            return $data;
        }


        return $data;
    }

}
<?php
/**
 * User: aozhuochao
 * Date: 2020/12/9
 */

namespace app\helpers\traits;


abstract class FactoryNamespaceBase
{
    /**
     * 父类
     *
     * @var object
     */
    protected $parentClass;

    public function __construct($parentClass = null)
    {
        $this->parentClass = $parentClass;
    }


    public function getParentClass()
    {
        return $this->parentClass;
    }
}
<?php
/**
 * Created by PhpStorm.
 * User: aozhuochao
 * Date: 2020-02-24
 * Time: 16:43
 */

namespace app\helpers\traits;


/**
 * 工厂trait,命名空间版
 *
 * todo 看下是否可改为非静态
 *
 * 例子:
static::pushRootNamespaceOnCurrent('grids');
static::setFactorySuffix('grid');
static::setFactoryConstructArg('*', [$this]);
static::setSaveCallType(1);
static::setFactoryDefault('test');
 */
trait FactoryNamespaceStaticTrait
{
    /**
     * 根命名空间
     *
     * @var array
     */
    protected static $rootNamespaceList = [];

    /**
     * 类别名
     *
     * @var array
     */
    protected static $classAliasArr = [];

    /**
     * 工厂子类的构造函数的参数
     *
     * 如:static::setFactoryConstructArg('*', [$this]);
     *
     * @var array
     */
    protected static $factoryConstructArg = [];

    /**
     * 全局后缀
     *
     * @var string
     */
    protected static $factorySuffix = '';

    /**
     * 全局工厂默认子类
     *
     * @var string
     */
    protected static $factoryDefaultName = '';

    /**
     * 保存的call的数据
     *
     * @var array|object[]
     */
    protected static $saveCallArr = [];

    /**
     * 保存call的类型
     *
     * @var int
     */
    protected static $saveCallType;

    /**
     * 自动单例
     * 如果有后缀instance,则会又多一个
     *
     * @var bool
     */
    protected static $autoInstance = false;

    /**
     * @param $rootNamespace
     */
    public static function pushRootNamespace($rootNamespace)
    {
        /** @noinspection PhpUndefinedFieldInspection */
        static::$rootNamespaceList[] = $rootNamespace;
    }

    /**
     * 设置相对当前类的相对命名空间
     *
     * @param string $rootNamespace
     * @param null|string $currentClass
     * @param string $classSuffix
     */
    public static function pushRootNamespaceOnCurrent($rootNamespace = '', $currentClass = null, $classSuffix = '')
    {
        $tempRootNamespace = ltrim($rootNamespace, '\\');

        $staticClassName = '\\' . (isset($currentClass) ? $currentClass : static::class);
        if ($pos = strrpos($staticClassName, '\\')){
            $staticClassName = substr($staticClassName, 0, $pos + 1);
        }
        $staticClassName = rtrim($staticClassName, '\\');

        /** @noinspection PhpUndefinedFieldInspection */
        static::$rootNamespaceList[] = [
            'rootNamespace' => $rootNamespace,
            'classNamespace' => $tempRootNamespace ? $staticClassName . '\\' . $tempRootNamespace : $staticClassName,
            'classSuffix' => $classSuffix
        ];
    }

    /**
     * @param $name
     * @param array $factoryConstructArg
     */
    public static function setFactoryConstructArg($name, array $factoryConstructArg)
    {
        static::$factoryConstructArg[$name] = $factoryConstructArg;
    }

    /**
     * 1、每个都保存
     * 2、基于根名
     * 3、基于方法名 + 实例化时第一个参数,同名覆盖
     *
     * @param $type
     * @param string $method 特定方法名
     */
    public static function setSaveCallType($type, $method = '*')
    {
        static::$saveCallType[$method] = $type;
    }

    /**
     * 获取存储的数据
     *
     * @param null $key
     * @param object[] $saveCallArr
     * @return array
     */
    public static function getSaveCall($key = null)
    {
        $saveArr = static::$saveCallArr;

        if (!isset($key)) { // null返回所有
            return $saveArr;
        }else if (!isset($saveArr[$key])){ // 不存在就返回空数组
            return [];
        }

        return $saveArr[$key];
    }

    /**
     * @param string $factorySuffix
     */
    public static function setFactorySuffix(string $factorySuffix)
    {
        static::$factorySuffix = $factorySuffix;
    }

    /**
     * @param string $factoryDefaultName
     */
    public static function setFactoryDefaultName(string $factoryDefaultName)
    {
        static::$factoryDefaultName = $factoryDefaultName;
    }

    /**
     * @param bool $autoInstance
     */
    public static function setAutoInstance(bool $autoInstance = true): void
    {
        self::$autoInstance = $autoInstance;
    }

    /**
     * 设置替换的别名
     *
     * @param $alias
     * @param string|object $class
     */
    public static function setClassAlias($alias, $class): void
    {
        self::$classAliasArr['\\' . ltrim($alias, '\\')] = $class;
    }

    /**
     * 循环处理配置
     *
     * @param $name
     * @param $arguments
     * @return \Generator
     */
    protected static function factoryCallEachYieldPro($name, $arguments)
    {
        foreach (static::$rootNamespaceList as $itemNamespaceArr) {
            $factoryDefaultAgentBool = false;
            agent:;
            $classNamespace = $itemNamespaceArr['classNamespace'] . '\\' . ucfirst($name);
            // 处理后缀
            $classNamespace .= ucfirst(
                !empty($itemNamespaceArr['classSuffix']) ? $itemNamespaceArr['classSuffix'] :
                    (static::$factorySuffix?:'')
            );

            if(isset(static::$classAliasArr[$classNamespace])){
                if (is_object(static::$classAliasArr[$classNamespace])) { // 指定实例化的类
                    yield [
                        1,
                        static::$classAliasArr[$classNamespace]
                    ];

                }else{ // 指定类别名
                    $classNamespace = static::$classAliasArr[$classNamespace];
                }
            }

            if (!class_exists($classNamespace)) {
                if (empty(static::$factoryDefaultName) || $factoryDefaultAgentBool){ // 当前空间不存在
                    continue;
                }

                // 默认工厂子类
                array_unshift($arguments, $name); // 将$name作为第一个参数
                $name = static::$factoryDefaultName;
                $factoryDefaultAgentBool = true;

                goto agent;
            }

            yield [
                2,
                $name, $arguments,
                $itemNamespaceArr,
                $classNamespace,
            ];

            break;
        }
    }


    /**
     * 1、子类可以有handle公有方法实现实例化后传参
     * 2、调用的方法带有Instance则为单例
     * 3、不存在的时候就返回false
     *
     * @param $name
     * @param $arguments
     * @return bool|object
     */
    public static function __callStatic($name, $arguments)
    {
        $result = false;

        $instanceBool = static::$autoInstance;
        $oldName = $name;
        // 处理存储方式
        $saveCallType = isset(static::$saveCallType[$name]) ? static::$saveCallType[$name] :
            (isset(static::$saveCallType['*']) ? static::$saveCallType['*'] : false);
        $saveArr = static::getSaveCall(null); // 存储的数据

        if ($instanceBool || strrpos($name, 'Instance') !== false) { // 单例
            $instanceBool = true;
            $name = str_replace('Instance', '', $name);

            // 获取已保存的
            foreach ($saveArr as $itemObjectKey => $itemObject) {
                if ($saveCallType === 1){
                    if ($oldName === $itemObjectKey) {
                        return $itemObject;
                    }
                }else if($saveCallType === 2){ // 基于根名
                    foreach ($itemObject as $itemKey => $item) {
                        if ($itemKey === $oldName) {
                            return $item;
                        }
                    }
                }else if($saveCallType === 3){ // 基于方法名 + 实例化时第一个参数,同名覆盖
                    if ($itemObjectKey === $oldName) { // 相同方法名
                        foreach ($itemObject as $itemKey => $item) {
                            $tempFirst = $arguments ? current($arguments) : 0;
                            $tempFirst = is_bool($tempFirst) ? ($tempFirst ? 'true' : 'false') : $tempFirst;
                            if ($itemKey === $tempFirst) {
                                return $item;
                            }
                        }
                    }
                }
            }
        }else if(
            $saveCallType === 3 && isset($saveArr[$oldName]) && isset($saveArr[$oldName][current($arguments)])
        ){ // 基于实例化时第一个参数,同名覆盖,自带单例
            return $saveArr[$oldName][current($arguments)];
        }

        foreach (static::factoryCallEachYieldPro($name, $arguments) as list($type, $name, $arguments, $itemNamespaceArr, $classNamespace)) {
            if ($type == 1){ // 直接return的
                return $name;
            }

            $args = isset(static::$factoryConstructArg[$name]) ? static::$factoryConstructArg[$name] : [];
            $args = array_merge(isset(static::$factoryConstructArg['*']) ? static::$factoryConstructArg['*'] : [], $args);

            if (method_exists($classNamespace, 'handle')) {
                /** @var object $object */
                $object = new $classNamespace(...$args);
                $result = $object->handle(...$arguments);
            }else{
                /** @var object $object */
                $object = new $classNamespace(...array_merge($args, $arguments));
                $result = $object;
            }


            if ($saveCallType === 1){ // 每个都保存
                if ($instanceBool){ // 单例
                    $saveArr[$oldName] = $result; // 单例的有key名
                }else{
                    $saveArr[] = $result; // 非单例则是数字key
                }
            }else if($saveCallType === 2){ // 基于根名
                if ($instanceBool){ // 单例
                    $saveArr[$itemNamespaceArr['rootNamespace']][$oldName] = $result;
                }else{
                    $saveArr[$itemNamespaceArr['rootNamespace']][] = $result;
                }
            }else if($saveCallType === 3){ // 基于方法名 + 实例化时第一个参数,同名覆盖
                // 第一个参数是false的时候存储的key为0
                $tempFirst = $arguments ? current($arguments) : 0;
                $tempFirst = is_bool($tempFirst) ? ($tempFirst ? 'true' : 'false') : $tempFirst;
                $saveArr[$oldName][$tempFirst] = $result; // 后缀是否Instance,会导致不同
            }

            break;
        }

        return $result;
    }
}
<?php
/**
 * Created by PhpStorm.
 * User: aozhuochao
 * Date: 2020-02-24
 * Time: 16:43
 */

namespace app\helpers\traits;


/**
 * 工厂trait,命名空间版
 * 不自带静态调用
 *
 * 例子:
$this->pushRootNamespaceOnCurrent('grids');
$this->setFactorySuffix('grid');
$this->setFactoryConstructArg('*', [$this]);
$this->setSaveCallType(1);
$this->setFactoryDefault('test');
 */
trait FactoryNamespaceTrait
{
    /**
     * 根命名空间
     *
     * @var array
     */
    protected $rootNamespaceList = [];

    /**
     * 类别名
     *
     * @var array
     */
    protected static $classAliasStaticArr = [];

    /**
     * 工厂子类的构造函数的参数
     *
     * 如:$this->setFactoryConstructArg('*', [$this]);
     *
     * @var array
     */
    protected $factoryConstructArg = [];

    /**
     * 全局后缀
     *
     * @var string
     */
    protected $factorySuffix = '';

    /**
     * 全局工厂默认子类
     *
     * @var string
     */
    protected $factoryDefaultName = '';

    /**
     * 保存的call的数据
     *
     * @var array|object[]
     */
    protected $saveCallArr = [];

    /**
     * 保存call的类型
     *
     * @var int
     */
    protected $saveCallType;

    /**
     * 自动单例
     * 如果有后缀instance,则会又多一个
     *
     * @var bool
     */
    protected $autoInstance = false;

    /**
     * @param $rootNamespace
     */
    public function pushRootNamespace($rootNamespace)
    {
        /** @noinspection PhpUndefinedFieldInspection */
        $this->rootNamespaceList[] = $rootNamespace;
    }

    /**
     * 设置相对当前类的相对命名空间
     *
     * @param string $rootNamespace
     * @param null|string $currentClass
     * @param string $classSuffix
     */
    public function pushRootNamespaceOnCurrent($rootNamespace = '', $currentClass = null, $classSuffix = '')
    {
        $tempRootNamespace = ltrim($rootNamespace, '\\');

        $staticClassName = '\\' . (isset($currentClass) ? $currentClass : static::class);
        if ($pos = strrpos($staticClassName, '\\')){
            $staticClassName = substr($staticClassName, 0, $pos + 1);
        }
        $staticClassName = rtrim($staticClassName, '\\');

        /** @noinspection PhpUndefinedFieldInspection */
        $this->rootNamespaceList[] = [
            'rootNamespace' => $rootNamespace,
            'classNamespace' => $tempRootNamespace ? $staticClassName . '\\' . $tempRootNamespace : $staticClassName,
            'classSuffix' => $classSuffix
        ];
    }

    /**
     * @param $name
     * @param array $factoryConstructArg
     */
    public function setFactoryConstructArg($name, array $factoryConstructArg)
    {
        $this->factoryConstructArg[$name] = $factoryConstructArg;
    }

    /**
     * 1、每个都保存
     * 2、基于根名
     * 3、基于方法名 + 实例化时第一个参数,同名覆盖
     *
     * @param $type
     * @param string $method 特定方法名
     */
    public function setSaveCallType($type, $method = '*')
    {
        $this->saveCallType[$method] = $type;
    }

    /**
     * 获取存储的数据
     *
     * @param null $key
     * @return array
     */
    protected function getSaveCall($key = null)
    {
        $saveArr = $this->saveCallArr;

        if (!isset($key)) { // null返回所有
            return $saveArr;
        }else if (!isset($saveArr[$key])){ // 不存在就返回空数组
            return [];
        }

        return $saveArr[$key];
    }

    /**
     * @param string $factorySuffix
     */
    public function setFactorySuffix(string $factorySuffix)
    {
        $this->factorySuffix = $factorySuffix;
    }

    /**
     * @param string $factoryDefaultName
     */
    public function setFactoryDefaultName(string $factoryDefaultName)
    {
        $this->factoryDefaultName = $factoryDefaultName;
    }

    /**
     * @param bool $autoInstance
     */
    public function setAutoInstance(bool $autoInstance = true)
    {
        $this->autoInstance = $autoInstance;
    }

    /**
     * 设置替换的别名
     *
     * @param $alias
     * @param string|object $class
     */
    public static function setClassAliasStatic($alias, $class)
    {
        static::$classAliasStaticArr['\\' . ltrim($alias, '\\')] = $class;
    }

    /**
     * 循环处理配置
     *
     * @param $name
     * @param $arguments
     * @return \Generator
     */
    protected function factoryCallEachYieldPro($name, $arguments)
    {
        foreach ($this->rootNamespaceList as $itemNamespaceArr) {
            $factoryDefaultAgentBool = false;
            agent:;
            $classNamespace = $itemNamespaceArr['classNamespace'] . '\\' . ucfirst($name);
            // 处理后缀
            $classNamespace .= ucfirst(
                !empty($itemNamespaceArr['classSuffix']) ? $itemNamespaceArr['classSuffix'] :
                    ($this->factorySuffix?:'')
            );

            if(isset(static::$classAliasStaticArr[$classNamespace])){
                if (is_object(static::$classAliasStaticArr[$classNamespace])) { // 指定实例化的类
                    yield [
                        1,
                        static::$classAliasStaticArr[$classNamespace]
                    ];

                }else{ // 指定类别名
                    $classNamespace = static::$classAliasStaticArr[$classNamespace];
                }
            }

            if (!class_exists($classNamespace)) {
                if (empty($this->factoryDefaultName) || $factoryDefaultAgentBool){ // 当前空间不存在
                    continue;
                }

                // 默认工厂子类
                array_unshift($arguments, $name); // 将$name作为第一个参数
                $name = $this->factoryDefaultName;
                $factoryDefaultAgentBool = true;

                goto agent;
            }

            yield [
                2,
                $name, $arguments,
                $itemNamespaceArr,
                $classNamespace,
            ];

            break;
        }
    }

    /**
     * 1、子类可以有handle公有方法实现实例化后传参
     * 2、调用的方法带有Instance则为单例
     * 3、不存在的时候就返回false
     *
     * @param $name
     * @param $arguments
     * @return bool|object
     */
    public function __call($name, $arguments)
    {
        $result = false;

        $instanceBool = $this->autoInstance;
        $oldName = $name;
        // 处理存储方式
        $saveCallType = isset($this->saveCallType[$name]) ? $this->saveCallType[$name] :
            (isset($this->saveCallType['*']) ? $this->saveCallType['*'] : false);
        $saveArr = $this->getSaveCall(null); // 存储的数据

        if ($instanceBool || strrpos($name, 'Instance') !== false) { // 单例
            $instanceBool = true;
            $name = str_replace('Instance', '', $name);

            // 获取已保存的
            foreach ($saveArr as $itemObjectKey => $itemObject) {
                if ($saveCallType === 1){
                    if ($oldName === $itemObjectKey) {
                        return $itemObject;
                    }
                }else if($saveCallType === 2){ // 基于根名
                    foreach ($itemObject as $itemKey => $item) {
                        if ($itemKey === $oldName) {
                            return $item;
                        }
                    }
                }else if($saveCallType === 3){ // 基于方法名 + 实例化时第一个参数,同名覆盖
                    if ($itemObjectKey === $oldName) { // 相同方法名
                        foreach ($itemObject as $itemKey => $item) {
                            $tempFirst = $arguments ? current($arguments) : 0;
                            $tempFirst = is_bool($tempFirst) ? ($tempFirst ? 'true' : 'false') : $tempFirst;
                            if ($itemKey === $tempFirst) {
                                return $item;
                            }
                        }
                    }
                }
            }
        }else if(
            $saveCallType === 3 && isset($saveArr[$oldName]) && isset($saveArr[$oldName][current($arguments)])
        ){ // 基于实例化时第一个参数,同名覆盖,自带单例
            return $saveArr[$oldName][current($arguments)];
        }

        foreach ($this->factoryCallEachYieldPro($name, $arguments) as list($type, $name, $arguments, $itemNamespaceArr, $classNamespace)) {
            if ($type == 1){ // 直接return的
                return $name;
            }

            $args = isset($this->factoryConstructArg[$name]) ? $this->factoryConstructArg[$name] : [];
            $args = array_merge(isset($this->factoryConstructArg['*']) ? $this->factoryConstructArg['*'] : [], $args);

            if (method_exists($classNamespace, 'handle')) {
                /** @var object $object */
                $object = new $classNamespace(...$args);
                $result = $object->handle(...$arguments);
            }else{
                /** @var object $object */
                $object = new $classNamespace(...array_merge($args, $arguments));
                $result = $object;
            }


            if ($saveCallType === 1){ // 每个都保存
                if ($instanceBool){ // 单例
                    $this->saveCallArr[$oldName] = $result; // 单例的有key名
                }else{
                    $this->saveCallArr[] = $result; // 非单例则是数字key
                }
            }else if($saveCallType === 2){ // 基于根名
                if ($instanceBool){ // 单例
                    $this->saveCallArr[$itemNamespaceArr['rootNamespace']][$oldName] = $result;
                }else{
                    $this->saveCallArr[$itemNamespaceArr['rootNamespace']][] = $result;
                }
            }else if($saveCallType === 3){ // 基于方法名 + 实例化时第一个参数,同名覆盖
                // 第一个参数是false的时候存储的key为0
                $tempFirst = $arguments ? current($arguments) : 0;
                $tempFirst = is_bool($tempFirst) ? ($tempFirst ? 'true' : 'false') : $tempFirst;
                $this->saveCallArr[$oldName][$tempFirst] = $result; // 后缀是否Instance,会导致不同
            }

            break;
        }

        return $result;
    }
}
<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2018-12-15
 * Time: 16:24
 */

namespace App\Repositories\Common;

/**
 * 方法中间件
 * 
 * Class FuncMiddleware
 * @package App\Repositories\Common
 */
class FuncMiddleware
{
    use Traits\Singleton;

    /**
     *
     * @var array
     */
    protected $map = [];

    /**
     * 调用
     * 
     *   FuncMiddleware::instance()->call('updateByIdTrait.create', function ($data)use($modelClass, $id){
     *      //@ var Model $modelData
     *      return $modelClass->create($data);
     *   }, $data);
     * 
     * @param $name
     * @param $func
     * @param mixed ...$content
     * @return mixed
     */
    public function call($name, $func, ...$content)
    {
        $data = new \ArrayIterator(array_get($this->map, $name) ?? []);

        $handle = function (...$content)use(&$data, $func, &$handle){
            $cFunc = $data->current();
            $data->next();

            if (!is_callable($cFunc) || $data->valid()){
                return $func(...$content);
            }

            return $cFunc($handle, ...$content);
        };

        return $handle(...$content);
    }

    /**
     * 设置同名的中间件
     * 
     * @param $name
     * @param $func
     * @return $this
     */
    public function setCall($name, $func)
    {
        if (!isset($this->map[$name])) {
            $this->map[$name] = [];
        }

        $this->map[$name][] = $func;

        return $this;
    }
}
<?php


namespace app\common\model\traits\common;


class GetClassSave
{
    /**
     * @var \think\model
     */
    protected $model;

    protected $fieldEmptyUpdate = []; // 字段空的时候更新
    protected $dataEmptyUpdate = []; // 整个数据空的时候更新

    protected $save = []; // 需要保存的数据

    protected $where = []; // 普通where条件
    protected $updateWhere = []; // 更新时才执行的where条件

    protected $diffUpdateBool = false; // 有差异才更新

    /**
     * GetSaveClass constructor.
     */
    public function __construct($model)
    {
        $this->model = $model;
    }

    public function saveDiff($where = [], $save = [])
    {
        return $this->setDiffUpdateBool(true)->save();
    }

    /**
     * 新增或更新
     *
     * @param array $where
     * @param array $save
     * @return mixed
     */
    public function save($where = [], $save = [])
    {
        $where = array_merge($this->where, $where);
        $save = array_merge($this->save, $save);

        $class = get_class($this->model);
        $model = new $class;
        if (!empty($where)) {
            $data = $model->where($where)->find();
        }

        if (empty($data)) { // 创建
            foreach ($this->fieldEmptyUpdate as $field => $item) {
                $save[$field] = $this->getTrueValue($item);
            }
            foreach ($this->dataEmptyUpdate as $field => $item) {
                $save[$field] = $this->getTrueValue($item);
            }

            $save = array_merge($where, $save);
            if (method_exists($model, 'getPk')) {
                unset($save[$model->getPk()]);
            }
            $modelData = $model->create($save);
        }else{ // 更新
            if ($this->diffUpdateBool) {
                foreach ($save as $field => $item) {
                    if (!array_key_exists($field, $data)) {
                        continue;
                    }

                    if ($data[$field] === $item) { // 去除无差异的
                        unset($save[$field]);
                    }
                }
            }
            foreach ($this->fieldEmptyUpdate as $field => $item) {
                if (empty($data[$field])) { // 覆盖掉
                    $save[$field] = $this->getTrueValue($item, $data);
                }
            }

            if ($save) {
                $modelData = $model->where(array_merge($where, $this->updateWhere))->update($save);
            }else{
                $modelData = $data;
            }
        }

        return $modelData;
    }

    /**
     * @param array $fieldEmptyUpdate
     * @return $this
     */
    public function setFieldEmptyUpdate(array $fieldEmptyUpdate)
    {
        $this->fieldEmptyUpdate = $fieldEmptyUpdate;

        return $this;
    }

    /**
     * @param array $dataEmptyUpdate
     * @return $this
     */
    public function setDataEmptyUpdate(array $dataEmptyUpdate)
    {
        $this->dataEmptyUpdate = $dataEmptyUpdate;

        return $this;
    }

    /**
     * @param array $updateWhere
     * @return $this
     */
    public function setUpdateWhere(array $updateWhere)
    {
        $this->updateWhere = $updateWhere;

        return $this;
    }

    /**
     * @param array $where
     * @return $this
     */
    public function setWhere(array $where)
    {
        $this->where = $where;

        return $this;
    }

    /**
     * @param array $save
     * @return $this
     */
    public function setSave(array $save)
    {
        $this->save = $save;

        return $this;
    }

    /**
     * @param bool $diffUpdateBool
     * @return $this
     */
    public function setDiffUpdateBool(bool $diffUpdateBool)
    {
        $this->diffUpdateBool = $diffUpdateBool;

        return $this;
    }

    protected function getTrueValue($value, $data = null)
    {
        if (is_callable($value)) {
            return call_user_func($value, $data);
        }

        return $value;
    }


}

/**
 * 获取修改和新增同时判断的类
 */
trait GetClassSaveTrait
{

    public function getCLassSave()
    {
        return new GetClassSave($this);
    }

}
<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2018/4/12
 * Time: 下午8:24
 */

namespace App\Repositories\Common;

/**
 * MaxJoin
 *
 * @since 2018/4/13
 * @package App\Repositories\Common
 */
class MaxJoin
{
    const MAX_GET_LIMIT = 1000;

    /**
     * 返回数据的第几页
     *
     * @var int
     */
    protected $page;

    /**
     * 返回数据个数
     *
     * @var int
     */
    protected $limit;

    /**
     * 最大获取量
     *
     * @var int
     */
    protected $maxGet;


    /**
     * 默认空的返回值
     *
     * @var mixed
     */
    protected $defaultReturn = [];


    /**
     * 先一次获取所有uid($mainFunc),
     * 再将uid分页返回($fromFunc)
     *
     *
    $data = (new MaxJoin())->setForPage($page, 20)->get(function ($maxGet) use ($uid) {

        return GuessInvites::instance()->getCacheLimitList($uid, $maxGet);
    }, 'uid', [$this,'getLastBonusTopPro']);
     *
     * MaxJoin constructor.
     * @param int $page
     * @param int $limit
     * @param int $maxGet
     */
    public function __construct($page = 1, $limit = 20, $maxGet = self::MAX_GET_LIMIT)
    {
        $this->page = $page;
        $this->limit = $limit;
        $this->maxGet = $maxGet;
    }


    /**
     * 解决有限大小的分页数据,让其分表查询
     * 将$mainFunc返回的数据通过指定的key($onKey)传参给$fromFunc并返回最终数据
     *
     * @param callable $mainFunc 一次获取的数据
     * @param string $onKey
     * @param callable $fromFunc 根据分页和$onKey获取数据
     * @return array
     */
    public function get(callable $mainFunc, $onKey, callable $fromFunc)
    {
        $mainData = $mainFunc($this->maxGet);

        $onData = [];
        foreach ($mainData as $item) {
            if (isset($item[$onKey])) {
                $onData[] = $item[$onKey];
            }
        }

        if (!empty($onData)) {
            return $fromFunc($onData, $this->page, $this->limit);
        }

        return [];
    }


    public function setForPage($page, $limit = null)
    {
        if (!empty($page)) {
            $this->page = $page;
        }

        if (!empty($limit)) {
            $this->limit = $limit;
        }

        return $this;
    }

    /**
     * @param int $maxGet
     * @return $this
     */
    public function setMaxGet($maxGet)
    {
        $this->maxGet = $maxGet;

        return $this;
    }

    /**
     * @param mixed $defaultReturn
     * @return $this
     */
    public function setDefaultReturn($defaultReturn)
    {
        $this->defaultReturn = $defaultReturn;

        return $this;
    }

}
<?php


namespace app\common\library\traits;

class NextNotThrow{

    /**
     * @var NextNotThrowTrait
     */
    protected $that;
    protected $errorFunc;
    protected $errorReturnString;

    public function __construct($that, $errorFunc, $errorReturnString = false)
    {
        $this->that = $that;
        $this->errorFunc = $errorFunc;
        $this->errorReturnString = $errorReturnString;
    }

    public function __call($name, $args)
    {

        $this->that->setNextNotThrowException(null);
        try{
            return call_user_func_array([$this->that, $name], $args);
        }catch (\Exception | \Throwable $exception){
            writeLogs([
                'NextNotThrow',
                'args' => [$name, $args],
                '$exception' => get_sys_human_debug_backtrace($exception),
            ], 'NextNotThrow--' . basename(str_replace('\\', '/', get_class($this->that))));

            $this->that->setNextNotThrowException($exception);

            if (is_callable($this->errorFunc)) {
                return call_user_func($this->errorFunc, $exception);
            }else if ($this->errorReturnString){
                return $exception->getMessage();
            }

        }

        return $this->that;
    }
}

/**
 *
 * 使用示例
 * @example
$payClass = \app\common\pay\PayFactory::makeByPayment($payment);
$weiXinOrderData = $payClass->nextNotThrow()->findOrder(
    $order->out_trade_no
);

if ($payClass->getNextNotThrowException()) {

}
 *
 *
 */
trait NextNotThrowTrait
{
    protected $nextNotThrowException;

    /**
     * 报错就返回当前对象
     *
     * @param callable|null $errorFunc
     * @return $this|NextNotThrow
     */
    public function nextNotThrow(callable $errorFunc = null)
    {
        return new NextNotThrow($this, $errorFunc);
    }

    /**
     * 报错就返回错误字符串
     *
     * @param callable|null $errorFunc
     * @return $this|NextNotThrow
     */
    public function nextNotThrowReturnString(callable $errorFunc = null)
    {
        return new NextNotThrow($this, $errorFunc, true);
    }

    /**
     * @param mixed $nextNotThrowException
     * @return $this
     */
    public function setNextNotThrowException($nextNotThrowException)
    {
        $this->nextNotThrowException = $nextNotThrowException;
        return $this;
    }

    /**
     * @return \Exception
     */
    public function getNextNotThrowException()
    {
        return $this->nextNotThrowException;
    }

}
<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2019-01-09
 * Time: 14:25
 */

namespace App\Repositories\Common;


/**
 * 代理实例化的类
 *
 * 如果当前类存在方法或属性就直接使用
 */
class ProxyInstanceClass
{
    /**
     * @var []object
     */
    protected $classes = [];


    public function __construct(...$classes)
    {
        $this->classes = $classes;
    }


    /**
     * Dynamically handle calls to the class.
     *
     * @param  string $method
     * @param  array $parameters
     * @return mixed
     *
     * @throws \Exception
     */
    public function __call($method, $parameters)
    {
        foreach ($this->classes as $class) {
            if (method_exists($class, $method)) {
                // 可通过spl实现调用下一个方法
                return call_user_func_array([$class, $method], $parameters);
            }
        }

        throw new \Exception("ProxyInstanceClass代理了不存在的方法{$method}");
    }

    public function __get($name)
    {
        foreach ($this->classes as $class) {
            if (property_exists($class, $name)) {
                return $class->{$name};
            }
        }

        return null;
    }

    public function __set($name, $value)
    {
        foreach ($this->classes as $class) {
            if (property_exists($class, $name)) {
                $class->{$name} = $value;
            }
        }
    }

}
<?php
/**
 * User: aozhuochao
 * Date: 2020/3/4
 */

namespace app\modules\api_docking;


use app\helpers\traits\Singleton;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use Throwable;


class RequestApiLog
{
    use Singleton;

    protected $host = '';

    /**
     * 不同环境的地址,key根据env来
     *
     * @var array
     */
    protected $hostArr = [];

    /**
     * 当前环境
     * production 生产环境
     * test 测试环境
     *
     * @var string
     */
    protected $env = '';

    /**
     * 设置公共header
     *
     * @var array
     */
    protected $commonHeaders = [];

    /**
     * 什么项目接口
     * 不同子类
     *
     * @var int
     */
    protected $type = 1;

    /**
     * 保存日志的方式
     * 如:TPModel
     *
     * @var string
     */
    protected $saveLogType = '';

    /**
     * 保存额外数据
     *
     * @var array
     */
    protected $onceHandleSaveValueArr = [];

    /**
     * 保存日志数据的insert时的id
     *
     * @var int|null
     */
    private $saveLogInsertId = null;

    /**
     * 调试用数据
     *
     * @var array
     */
    protected $debugData = [];

    /**
     * 最后一个请求返回的结果
     *
     * @var array
     */
    protected $lastResult = [];

    /**
     * VanKeRepository constructor.
     */
    public function __construct()
    {
        $this->host = !empty($this->hostArr[$this->env]) ? $this->hostArr[$this->env] : '';
    }

    /**
     * 设置保存额外数据的回调函数
     *
     * @example
    $this->setOnceOfHandleSaveValue('token', 'json.body.appAccessToken', function ($createData, $json){
    return $this->isSuccess($json);
    });
     *
     * @param string $key 要保存的key
     * @param string|callable $func save_value_json要保存的值
     * @param int|string|callable $status save_value_status的值
     * @return $this
     */
    public function setOnceOfHandleSaveValue($key, $func, $status = 1)
    {
        $this->onceHandleSaveValueArr[] = [$key, $func, $status];

        return $this;
    }

    /**
     * 获取最后保存的save_value_json的值
     * false就是数据不存在
     *
     * @param $saveKey
     * @param null $status
     * @param null $gtCreatedTime 限制创建时间
     * @return mixed
     */
    public function getLastSaveValue($saveKey, $status = null, $gtCreatedTime = null)
    {
        return $this->execBySaveLogType('getLastSaveValueOf', [$saveKey, $status, $gtCreatedTime]);
    }

    /**
     * 检测去重,回调时使用
     * 返回true则代表没有,false则有多次
     *
     * @param $path
     * @param $name
     * @param $json
     * @param $save_key
     * @param $checkSaveValue
     * @return bool
     */
    public function checkSaveOnce($path, $name, $json, $save_key, $checkSaveValue)
    {
        return $this->execBySaveLogType('checkSaveOnceOf', [$path, $name, $json, $save_key, $checkSaveValue]);

    }

    /**
     * 上一个保存的日志的status改为2
     *
     * @param int $status
     * @return $this
     */
    public function failLog($status = 2)
    {
        return $this->execBySaveLogType('failLogOf', [$status]);
    }

    /**
     * 用匿名函数的方式实现订单去重判断
     *
     * @param callable $func
     * @param mixed $json 回调所有数据
     * @param mixed $uniqueValue 检测这个值是否重复
     * @param string $name 当前回调名称
     * @param string $path api_log的path
     * @param string $saveKeyPrefix save_key字段的前缀,会拼接$uniqueValue
     * @return mixed|null
     */
    public function checkNotifyUnique($func, $json, $uniqueValue, $name = '回调', $path = 'notify', $saveKeyPrefix = 'notify-')
    {
        return $this->execBySaveLogType('checkNotifyUniqueOf', func_get_args());
    }

    /**
     * @return array
     */
    public function getLastResult()
    {
        return $this->lastResult;
    }

    /**
     * @return int
     */
    public function getSaveLogInsertId()
    {
        $this->saveLogOfTPModelCreate([], true);

        return $this->saveLogInsertId;
    }

    /**
     * @param mixed $debugData
     * @return $this
     */
    public function setDebugData($debugData)
    {
        $this->debugData = $debugData;

        return $this;
    }

    public function setDebugDataByPath($path, $debugData)
    {
        $this->debugData[$path] = $debugData;

        return $this;
    }

    /**
     * @param array $commonHeaders
     * @return $this
     */
    public function setCommonHeaders(array $commonHeaders)
    {
        $this->commonHeaders = $commonHeaders;

        return $this;
    }

    public function removeCommonHeader($key)
    {
        unset($this->commonHeaders[$key]);

        return $this;
    }

    public function setCommonHeader($key, $value)
    {
        $this->commonHeaders[$key] = $value;

        return $this;
    }

    /**
     * @return array
     */
    public function getCommonHeaders()
    {
        return $this->commonHeaders;
    }

    public function setHeaderForm($charset = 'utf-8')
    {
        $this->setCommonHeader('Content-Type', "application/x-www-form-urlencoded; charset={$charset}");

        return $this;
    }

    public function setHeaderAuthorization($token, $tokenType = 'Bearer')
    {
        $this->setCommonHeader('Authorization', "{$tokenType} {$token}");

        return $this;
    }

    /**
     * 建表语句
     *
     * @example
    class ApiLogModel extends BaseModel
    {

    protected $name = 'api_log';


    protected $autoWriteTimestamp = true;

    protected $json = ['json', 'save_value_json'];
    }
     *
     * @see 留意表前缀
     * @return string
     */
    private function apiLogTableSql()
    {
        return <<<SQL
CREATE TABLE `api_log` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `type` varchar(32) NOT NULL DEFAULT '' COMMENT '种类',
  `path` varchar(255) NOT NULL DEFAULT '' COMMENT '地址',
  `name` varchar(255) NOT NULL DEFAULT '' COMMENT '接口意义',
  `diff_time` decimal(11,4) unsigned NOT NULL DEFAULT '0' COMMENT '花费时间',
  `json` json DEFAULT NULL COMMENT '数据',
  `save_key` varchar(255) NOT NULL DEFAULT '' COMMENT '需要额外存储的数据的key',
  `save_value_json` json COMMENT '需要额外存储的数据',
  `save_value_status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '需要额外存储的数据的有效期,1是有效,2为无效',
  `created_time` datetime DEFAULT NULL,
  `updated_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='~~api日志';
SQL;

    }

    protected function handleSaveValue($createData)
    {
        if (!empty($this->onceHandleSaveValueArr)) {
            foreach ($this->onceHandleSaveValueArr as $key => list($saveKey, $item, $status)) {
                $createData['save_key'] = $saveKey;

                $temp = $createData;
                if (is_string($item) || is_string($status)) { // 字符串模式下, 转为json
                    $temp['json']['body'] = !empty($createData['json']) && !empty($createData['json']['body']) ? (@json_decode($createData['json']['body'], true) ?: []) : [];
                }

                if (is_string($item)) { // 字符串
                    $createData['save_value_json'] = json_encode($this->getDataByKey($temp, $item) ?: '');
                }else if(is_callable($item)){
                    $createData['save_value_json'] = json_encode(call_user_func($item, $createData, $temp['json']['body']) ?: '');
                }
                if (is_scalar($status)) { // 字符串
                    $createData['save_value_status'] = $this->getDataByKey($temp, $status)?:$status;
                }else if(is_callable($status)){
                    $createData['save_value_status'] = call_user_func($status, $createData, $temp['json']['body']);
                }

                unset($this->onceHandleSaveValueArr[$key]);
            }
        }

        return $createData;
    }

    protected function getDataByKeyOfTPModel($data, $key)
    {
        return \think\helper\Arr::get($data, $key);
    }

    protected function getDataByKey($data, $key)
    {
        return $this->execBySaveLogType('getDataByKeyOf', [$data, $key]);
    }

    protected function failLogOfTPModel($status = 2)
    {
        $model = new ApiLogModel();
        $model->update(['save_value_status' => $status], [$model->getPk() => $this->getSaveLogInsertId()]);

        return $this;
    }

    protected function saveLogOfTPModelCreate($createData, $force = false)
    {
        static $func = null,
        $createDataExecBoolArr = [];


        $tp6Bool = class_exists(\think\facade\Event::class);

        if (is_null($func)){
            $func = (function ($saveExecBool = false)use($createData, $tp6Bool, $force, &$func, &$createDataExecBoolArr){
                if ($tp6Bool) {
                    /** @var mixed $createData */
                    $tempKey = md5(json_encode($createData));

                    if (!empty($createDataExecBoolArr[$tempKey])) { // 已经执行过
                        $func = null;
                        return;
                    }
                    if ($saveExecBool){ // 记录当前执行过
                        $createDataExecBoolArr[$tempKey] = 1;
                    }
                }

                $model = ApiLogModel::create($createData);

                if ($tp6Bool && !$force) { // 有些时候不提交
                    \think\facade\Db::commit();
                }

                $this->saveLogInsertId = $model['id']??0;
                $func = null;
            })->bindTo($this);
        }

        if ($force) {
            if (is_null($this->saveLogInsertId) && is_callable($func)) { // 执行最后一个
                $func(true);
                $func = null;
            }

            return;
        }

        // 保存
        if ($tp6Bool) {
            \think\facade\Event::listen('HttpEnd', clone $func);
        }else{
            $func();
        }
        $func = null;

        return;
    }

    protected function checkSaveOnceOfTPModel($path, $name, $json, $save_key, $checkSaveValue)
    {
        $bool = true;
        if (ApiLogModel::where('path', $path)->where('type', $this->type)
            ->where('save_key', $save_key)->where('save_value_status', 1)
            ->where('save_value_json', $checkSaveValue)->value('id')) { // 存在
            $bool = false;
        }

        $this->setOnceOfHandleSaveValue($save_key, function () use ($checkSaveValue) {
            return $checkSaveValue;
        }, $bool ? 1 : 2);
        $this->saveLog($path, $name, $json);
        $this->getSaveLogInsertId();

        return $bool;
    }

    protected function checkNotifyUniqueOfTPModel($func, $json, $uniqueValue, $name = '回调', $path = 'notify', $saveKeyPrefix = 'notify-')
    {
        $save_key = $saveKeyPrefix . $uniqueValue;
        $result = false;
        $time = microtime(true);
        try{
            $bool = 1;
            if (ApiLogModel::where('path', $path)->where('type', $this->type)
                ->where('save_key', $save_key)->where('save_value_status', 1)
                ->where('save_value_json', $uniqueValue)->value('id')) { // 存在
                $bool = 2;
            }

            if ($bool === 1){ // 首次
                $this->setOnceOfHandleSaveValue($save_key, function () use ($uniqueValue) {
                    return $uniqueValue;
                }, 1);
                $this->saveLog($path, $name, ['body' => $json], microtime(true) - $time, true);
                $bool = 3;

                // 执行业务代码
                $result = call_user_func($func, $json);

                if ($result !== true) { // 回调出错
                    $this->failLog();
                }
            }
        }catch (Exception $exception){
            $bool = 2;

        }catch (Throwable $exception){
            $bool = 2;
        }

        if ($bool === 2){ // 没有成功
            \think\facade\Log::error('回调重复执行: ' . $name . var_export(['json' => $json, 'path' => $path], true));
            $this->setOnceOfHandleSaveValue($save_key, function () use ($uniqueValue) {
                return $uniqueValue;
            }, 2);
            $this->saveLog($path, $name, ['body' => $json], microtime(true) - $time, true);
        }

        return $result;
    }

    /**
     * TP获取最后保存的save_value_json
     *
     * @param $saveKey
     * @param null $status
     * @param null $gtCreatedTime
     * @return mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    protected function getLastSaveValueOfTPModel($saveKey, $status = null, $gtCreatedTime = null)
    {
        $query = ApiLogModel::where('type', $this->type)->where('save_key', $saveKey)
            ->order('id', 'desc')->limit(1);

        if (isset($status)) {
            $query = $query->where('save_value_status', $status);
        }

        if (isset($gtCreatedTime)) {
            $query = $query->where('created_time', '>', $gtCreatedTime);
        }

        $data = $query->field('save_value_json')->find();

        return $data ? $data['save_value_json'] : false;
    }

    /**
     * TP的保存请求日志的方式
     *
     * @param $path
     * @param $name
     * @param $json
     * @param int $diffTime
     * @param bool $forceSave 强制保存
     */
    protected function saveLogOfTPModel($path, $name, $json, $diffTime = 0, $forceSave = false)
    {
        $createData = [
            'type' => $this->type,
            'path' => $path,
            'name' => $name,
            'diff_time' => empty($diffTime) && !empty($json['diffTime']) ? $json['diffTime'] : $diffTime,
            'json' => $json,
            'created_time' => date('Y-m-d H:i:s'),
            'updated_time' => date('Y-m-d H:i:s'),
        ];

        $createData = $this->handleSaveValue($createData);

        unset($createData['json']['response']);

        // 保存
        $this->saveLogOfTPModelCreate($createData, $forceSave);
    }

    protected function saveLog($path, $name, $json, $diffTime = 0, $forceSave = false)
    {
        $this->execBySaveLogType('saveLogOf', [$path, $name, $json, $diffTime, $forceSave]);
    }

    /**
     * 根据定义的类型,执行特定类型的方法
     * 最好后面有个Of
     *
     * @param $prefix
     * @param $args
     * @return mixed|null
     */
    protected function execBySaveLogType($prefix, $args)
    {
        if (empty($this->saveLogType)) {
            return null;
        }

        if (is_string($this->saveLogType)){
            if (method_exists($this, $prefix . $this->saveLogType)){
                return $this->{$prefix . $this->saveLogType}(...$args);
            }
        }
    }

    /**
     * 发送数据返回json数据
     *
     * @param $name
     * @param $method
     * @param $path
     * @param array $query
     * @param array $moreOption
     * @return mixed
     */
    protected function sendOfJson($name, $method, $path, $query = [], $moreOption = [])
    {
        $result = $this->send($name, $method, $path, $query, $moreOption);

        if (!empty($result['body']) && is_string($result['body'])) {
            $result['body'] = json_decode($result['body'], true);
        }

        return $result['body'];
    }

    protected function send($name, $method, $path, $query = [], $moreOption = [])
    {
        // 清除数据
        $this->saveLogInsertId = null;

        // 正式代码
        $startTime = microtime(true);
        $error = $response = null;
        if (empty($this->host) && empty($this->debugData)) { // 没有环境
            $result = [
                'query' => '',
                'body' => '',
                'response' => $response,
                'diffTime' => 0,
                'http_error' => $error,
            ];

            return $result;
        }else if (!empty($this->debugData) && !empty($this->debugData[$path])){
            $body = $this->debugData[$path];
            goto result;
        }

        $client = new Client();

        try{
            $tempMethod = strtolower($method);
            if ($tempMethod === 'post') {
                $options = [
                    'form_params' => $query
                ];
            } else if ($tempMethod === 'post-json'){ // 发送json格式的请求,并且是post
                $method = 'post';
                $options = [
                    'json' => $query
                ];
            }else {
                $options = [
                    'query' => $query
                ];
            }

            if (isset($moreOption['body'])) { // 指定了body就不要传其他参数
                unset($options['form_params'], $options['json']);
            }

            if (!empty($this->getCommonHeaders())) {
                $options['headers'] = $this->getCommonHeaders();
            }

            $options = array_merge($options, $moreOption);

            $response = $client->request(
                $method,
                $this->host . $path,
                $options
            );
        }catch (RequestException $exception){
            $error = $exception;
        } catch (GuzzleException $exception) {
            $error = $exception;

        }
        $body = !isset($error) ? $response->getBody()->getContents() : '';

        result:
        $diffTime = microtime(true) - $startTime;

        $this->lastResult = $result = [
            'query' => isset($options)?$options:[],
            'body' => $body,
            'response' => $response, // @see \GuzzleHttp\Psr7\Response
            'diffTime' => $diffTime,
            'http_error' => $error,
        ];
        $this->saveLog($path, $name, $result);

        return $result;
    }
}
<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2019-01-09
 * Time: 15:44
 */

namespace App\Repositories\Common\Traits;

/**
 * 方法中途 保存错误信息
 */
trait SetErrorTrait
{
    protected $errorMsgTrait = '';

    /**
     * 设置错误文案
     *
     * @param string $errorMsgTrait
     * @return $this
     */
    public function setErrorMsgTrait(string $errorMsgTrait)
    {
        $this->errorMsgTrait = $errorMsgTrait;

        return $this;
    }

    /**
     * 获取错误文案
     *
     * @return string
     */
    public function getErrorMsgTrait(): string
    {
        return $this->errorMsgTrait;
    }

    /**
     * 是否报错
     *
     * @return bool
     */
    public function hasErrorMsgTrait()
    {
        return !empty($this->getErrorMsgTrait());
    }
}
<?php
/**
 * User: aozhuochao 
 */

namespace App\Repositories\Common\Traits;

/**
 * 单例类
 */
trait Singleton
{
    protected static $instance = null;


    /**
     * 单例
     *
     * @param array $args
     * @return static
     */
    public static function instance(...$args)
    {
        $key = md5(var_export(['class' => static::class, 'args' => $args], true));
        if (!isset(self::$instance[$key])){
            self::$instance[$key] = new static(...$args);
        }
    
        return self::$instance[$key];
    }

    /**
     * 实例化对象
     *
     * @param array ...$args
     * @return static
     */
    public static function instanceNew(...$args)
    {
        return new static(...$args);
    }
    
    /**
     * 设置默认实例化对象
     *
     * @param $object
     * @return mixed
     */
    public static function instanceSetDefault($object)
    {
        $key = md5(var_export(['class' => static::class, 'args' => []], true));

        return self::$instance[$key] = $object;
    }

    /**
     * 默认实例化对象
     *
     * @param array $args
     * @return mixed
     */
    public static function instanceDefault(...$args)
    {
        return static::instanceSetDefault(static::instance(...$args));
    }
    
    /**
     * 实例化对象通过传入的对象进行保存
     *
     * @param object $callObject 保存单例的对象
     * @param mixed ...$args
     * @return static
     */
    public static function instanceThis($callObject, ...$args)
    {
        $key = md5(var_export(['class' => static::class, 'args' => $args], true));

        if (!property_exists($callObject, $key)) {
            $instance = static::instanceNew(...$args);
            $callObject->$key = $instance;
        }

        return $callObject->$key;
    }
}
<?php


namespace App\Libs;


class Sms extends \Overtrue\EasySms\EasySms
{
    use \App\Libs\Traits\Singleton;

    protected $templateMap = [

    ];

    /**
     * Sms constructor.
     */
    public function __construct(array $config = [])
    {
        $config = [
            // HTTP 请求的超时时间(秒)
            'timeout' => 5.0,

            // 默认发送配置
            'default' => [
                // 网关调用策略,默认:顺序调用
                'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class,

                // 默认可用的发送网关
                'gateways' => [
                    'aliyun',
                ],
            ],
            // 可用的网关配置
            'gateways' => [
                'errorlog' => [
                    'file' => app()->storagePath() . "/logs/easy-sms-".date('Ym').".log",
                ],
                'aliyun' => [
                    'access_key_id' => env('ALIYUN_SMS_AK', ''),
                    'access_key_secret' => env('ALIYUN_SMS_AS', ''),
                    'sign_name' => env('ALIYUN_SMS_SIGN_NAME', ''), // 短信签名
                ],
                //...
            ],
        ];

        parent::__construct($config);
    }

    /**
     * 根据定义的模板id发送短信
     * 成功时返回true,失败返回错误文案
     *
     * @param $mobile
     * @param $template_id
     * @param array $data
     * @return bool|string
     * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException
     */
    public static function sendByTemplateId($mobile, $template_id, $data = [])
    {
        try{

            static::instance()->send($mobile, [
                'content'  => sprintf(static::instance()->getTemplateMap()[$template_id], $data),
                'template' => $template_id,
                'data' => $data,
            ]);
        }catch (\Overtrue\EasySms\Exceptions\NoGatewayAvailableException $exception){
            /** @var \Overtrue\EasySms\Exceptions\NoGatewayAvailableException $exception */
            /** @var \Overtrue\EasySms\Exceptions\GatewayErrorException $last */
            $last = $exception->getLastException();

            return $last->getMessage();
        }

        return true;
    }

    /**
     * @return array
     */
    public function getTemplateMap(): array
    {
        return $this->templateMap;
    }
}
<?php

namespace App\Repositories\Common;

/**
 * [
 *      'status' => [
 *          2 => [
 *              'title' => '关闭',
 *              '任意名1' => '<span class="red">{{title}}</span>',
 *              '任意名2' => function($val, $map){return $val;},
 *
 *          ],
 *      ]
 * ]
 *
 * @package helper
 */
class Tinyint
{
    
    use Traits\Singleton;
    
    /**
     * 值对应的文案
     */
    const TITLE = 'title';
    /**
     * 值对应数组的key
     */
    const VALUE = 'value';
    
    /**
     * 唯一key,可以通过反向查找到value
     */
    const KEY = 'key';
    
    
    /**
     * 值的下一个状态
     */
    const NEXT = 'next';
    
    
    protected $map = [];
    
    /**
     * 当前操作的field
     *
     * @var string
     */
    protected $field = '';


    /**
     * 获取多个数据
     *
     * @example 一维数组key和value模式
     * $model->getTinyint('cate_type')->getManyList([
     *      'key' => Tinyint::VALUE,
     *      'value' => Tinyint::TITLE,
     * ])
     *
     * @example 二维数组模式
     * $model->getTinyint('time_type')->getManyList([
     *      ['action' => Tinyint::VALUE, 'keyName' => 'key'],
     *      ['action' => Tinyint::TITLE, 'keyName' => 'value'],
     * ])
     *
     * @example return 返回格式
     * [
     *      {
     *          "key": 1,
     *          "value": "不推送"
     *      },
     *      {
     *          "key": 2,
     *          "value": "准时推送"
     *      }
     * ]
     *
     * @param array $manyData
     * @return array
     */
    public function getManyList(array $manyData)
    {
        $result = [];

        foreach ($this->getFieldMap() as $value => $fieldMap) {
            $temp = [];
            foreach ($manyData as $key => $manyDatum) {
                if (is_scalar($manyDatum)){ // 一维数组
                    if ((int)$key === $key){ // 索引数组
                        $getAction=$manyDatum;
                        $getKeyName=null;
                    }else{ // 非索引
                        $getAction = $manyDatum;
                        $getKeyName =$key;
                    }
                }else{ // 二维数组,指定返回的key名
                    $getAction = $manyDatum['action']; // 获取哪个action
                    $getKeyName = $manyDatum['keyName']; // 改名
                }

                $temp[$getKeyName ?? $getAction] = $this->get($value, '', $getAction);
            }

            $result[] = $temp;
        }

        return $result;
    }


    protected function handleActionValue($actionValue)
    {
        return $actionValue;
    }


    ///////////////////上面是新的,不在通过参数传递field和map//////////////////////////////



    
    /**
     * 设置文案,可改变action改变场景
     *
     * @param string $field 字段名
     * @param int    $value 字段名对应的值
     * @param mixed  $data $action参数对应的值
     * @param string $action 场景
     * @return $this
     */
    public function set($value, $data, $action = self::TITLE, $field = '')
    {
        $field = $this->getField($field);
        if (isset($this->map[$field])) {
            if (isset($this->map[$field][$value])) {
                $this->map[$field][$value][$action] = $data;
            }else{
                $this->map[$field][$value] = [$action => $data];
            }
        }else{
            $this->map[$field] = [
                $value => [$action => $data],
            ];
        }
        
        return $this;
    }


    /**
     * 获取设置的文案
     *
     * @param int $value 字段名对应的值
     * @param string $default 最终为空时的默认值
     * @param string $action 场景
     * @param string $field 字段名
     * @param null|array $map
     * @return string
     */
    public function get($value, $default = '', $action = self::TITLE, $field = '', $map = null)
    {
        $field = $this->getField($field);
        $map = $this->getMap($map);
        $result = null;
        $bool = $this->existsValue($value, $field);
        if ($bool && isset($map[$field][$value][$action])){
            $result = $map[$field][$value][$action];
            $data = $map[$field];
            
            if (is_callable($result)) {
                $result = $result($value, $data);
            }else if (is_string($action) && is_string($result)){ // 可通过{{}}来获取到其他值
                $result = preg_replace_callback('/{{([^_]+)}}/', function ($matches)use($value, $data){
                    return $this->replaceCallbackByGet($matches, $value, $data);
                }, $result);
                
            }
        }else if ($bool && $action === self::VALUE){ // 获取值
            $result = $value;
        }
    
        return isset($result) ? $result : $default;
    }
    
    
    /**
     * 根据action 获取actionValue列表
     *
     * @param string $action
     * @param string $key  指定返回值的key的值,默认为value
     * @param string $field
     * @param null   $map
     * @return array
     */
    public function getList($action = self::TITLE, $key = self::VALUE, $field = '', $map = null)
    {
        $field = $this->getField($field);
        $map = $this->getMap($map);
        
        $result = [];
        if (isset($map[$field]) && is_array($map[$field])){
            foreach ($map[$field] as $value => $item) {
                if (isset($item[$action])) {
                    $result[$this->get($value, '', $key, $field, $map)] = $this->get($value, '', $action, $field, $map);
                }else if ($action === self::VALUE){ // 获取值
                    if (empty($key)){
                        $result[] = $value;
                    }else{
                        $result[$this->get($value, '', $key, $field, $map)] = $value;
                    }
                }
            }
        }
        
        return $result;
    }

    /**
     * 获取所有value的列表
     *
     * @param string $field
     * @param null $map
     * @return array
     */
    public function getValues($field = '', $map = null)
    {
        return $this->getList(self::VALUE, null, $field, $map);
    }
    
    /**
     * 根据action 获取actionValue为true的列表,返回value
     *
     * @param string $action
     * @param        $field
     * @param null   $map
     * @return array
     */
    public function getListByActionValueTrue($action, $field = '', $map = null)
    {
        $field = $this->getField($field);
        $map = $this->getMap($map);
        
        $result = [];
        if (isset($map[$field]) && is_array($map[$field])){
            foreach ($map[$field] as $value => $item) {
                if (isset($item[$action]) && $item[$action] === true) {
                    $result[] = $value;
                }
            }
        }
        
        return $result;
    }
    
    
    
    /**
     * 不使用set,每次指定map
     *
     * @param array  $data 对应$this->map
     * @param array ...$args
     * @return string
     */
    public function getTemp(array $data, ...$args)
    {
        array_push($args, $data);
        return call_user_func_array([$this, 'get'], $args);
    }
    
    
    /**
     * get中的preg_replace_callback
     *
     * @param $matches
     * @param $value
     * @param $data
     * @return string
     */
    protected function replaceCallbackByGet($matches, $value, $data)
    {
        if (isset($data[$value][$matches[1]])) {
            return $data[$value][$matches[1]];
        }
        
        return '';
    }
    
    
    public function setMap($map, $value = null)
    {
        if (is_null($value) && is_array($map)) {
            $this->map = $map;
        }else if(isset($value) && is_string($map)){
            $this->map[$map] = $value;
        }
    
        return $this;
    }


    /**
     * 获取值
     *
     * @param string $field
     * @param null|array $map
     * @return array|mixed
     */
    public function getFieldMap($field = '', $map = null)
    {
        $map = $this->getMap($map);
        $field = $this->getField($field);

        return isset($map[$field]) ? $map[$field] : [];
    }

    public function getMap($map)
    {
        return $map ?: $this->map;
    }
    
    
    /**
     * 检测field是否存在
     *
     * @param $field
     * @return bool
     */
    public function existsField($field)
    {
        if (empty($field)) {
            return false;
        }
        
        return isset($this->map[$field]);
        
    }
    
    
    /**
     * 检测值是否存在
     *
     * @param $field
     * @param $value
     * @return bool
     */
    public function existsValue($value, $field = '')
    {
        $field = $this->getField($field);
        return $this->existsField($field) && isset($this->map[$field][$value]);
    }
    
    
    /**
     * 检测action里是否存在actionValue
     *
     * @param mixed  $actionValue
     * @param string $action
     * @param string $field
     * @param null   $map
     * @return bool
     */
    public function existsActionValue($actionValue, $action, $field = '', $map = null)
    {
        $fieldData = $this->getFieldMap($this->getField($field), $this->getMap($map));
        if (is_array($fieldData)){
            foreach ($fieldData as $item) {
                if (isset($item[$action]) && $item[$action] == $actionValue) {
                    return true;
                }
            }
        }
        
        return false;
    }
    
    
    /**
     * 检测指定action是否被设置
     *
     * @param $field
     * @param $action
     * @return bool
     */
    public function existsSetAction($action, $field = '')
    {
        $map = $this->getFieldMap($this->getField($field));
        if (is_array($map)){
            foreach ($map as $item) {
                if (is_array($item) && isset($item[$action])) {
                    return true;
                }
            }
        }
        
        return false;
    }
    
    
    
    /**
     * 判定对应action是否为true
     *
     * @param $field
     * @param $value
     * @param $action
     * @return bool
     */
    public function ifActionTrue($value, $action, $field = '')
    {
        $field = $this->getField($field);
        return $this->existsValue($value, $field) && isset($this->map[$field][$value][$action]) && $this->map[$field][$value][$action] === true;
    }
    
    
    
    
    /**
     * 获取$value里的$action的值,作为下一个值的$nextAction
     *
     * @param string $field
     * @param string $value
     * @param string $action  当前action
     * @param string $nextAction 下一个值的action
     * @param array  $flipArr 临时指定 值的映射关系,如:[1 => 2, 2 => 1]
     * @return string
     */
    public function getNextValueByValue($value, $action = self::NEXT, $nextAction = self::VALUE, $field = '', $flipArr = [])
    {
        $field = $this->getField($field);
        $result = '';
        if (!empty($flipArr) && isset($flipArr[$value])){ // 指定映射关系
            $result = $this->get($flipArr[$value], $result, $nextAction, $field);
        }else if ($this->existsValue($value, $field)) {
            $result = $this->get($this->get($value, $result, $action, $field), $result, $nextAction, $field);
        }
        
        return $result;
    }
    
    
    /**
     * 根据action和actionValue获取 值(map下的key)
     *
     * @param $field
     * @param $action
     * @param $actionValue
     * @return bool|int|string
     */
    public function getValueByActionValue($actionValue, $action = self::KEY, $field = '')
    {
        $field = $this->getField($field);
        if ($this->existsField($field)) {
            $map = $this->getFieldMap($field);
            foreach ($map as $value => $item) {
                if (isset($item[$action]) && $item[$action] == $actionValue) {
                    return $value;
                }
            }
        }
        
        return false;
    }
    
    
    /**
     * 获取action和actionValue为true的value
     *
     * @param        $action
     * @param string $field
     * @return bool|int|string
     */
    public function getValueByActionTrue($action, $field = '')
    {
        return $this->getValueByActionValue(true, $action, $field);
    }


    /**
     * @param string $field
     * @return string
     */
    public function getField($field = '')
    {
        return !empty($field) ? $field : $this->field;
    }
    
    /**
     * @param string $field
     * @return $this
     */
    public function setField($field)
    {
        $this->field = $field;
        
        return $this;
    }
    
    
}
<?php

namespace App\Repositories\Common\Traits;

use App\Repositories\Common\Tinyint;


/**
 * 封装Tinyint,提供便捷方法
 *
 * @property-read array $tinyintConfig
 */
trait TinyintTrait
{

//    protected $tinyintConfig = [];


    /**
     * @param string $field
     * @return Tinyint
     */
    public function getTinyint($field = '')
    {
        return Tinyint::instance([__CLASS__, $field])->setMap($this->tinyintConfig)->setField($field);
    }

}
<?php
/**
 * User: aozhuochao
 * Date: 2020/7/28
 */

namespace app\helpers\traits;

/**
 * laravel的bootTraits
 *
 * Trait bootTraits
 * @package app\helpers\traits
 */
trait bootTraits
{
    /**
     * Boot all of the bootable traits on the model.
     *
     * @return void
     */
    protected function bootObjectTraits()
    {
        $class = $this;

        foreach (class_uses_recursive($class) as $trait) {
            if (method_exists($this, $method = 'boot'.basename(str_replace('\\', '/', $trait)))) {
                call_user_func([$this, $method]);
            }
        }
    }
}
<?php
/**
 * User: aozhuochao
 * Date: 2020/3/11
 */

namespace app\helpers\traits;

/**
 * @method callChildren($name, ...$arguments)
 * @method callChildrenSelfSave($name, $parentClass, ...$arguments)
 */
trait callChildrenTrait
{
    protected $callSave = [];

    public function __call($name, $arguments)
    {
        static $num = 0;
        ++$num;

        if (method_exists($this, 'callChildren')) {
            return call_user_func_array([$this, 'callChildren'], array_merge([$name], $arguments));
        }else if (method_exists($this, 'callChildrenSelfSave')) {
            return $this->callSave[$name . '-' . $num] = call_user_func_array([clone $this, 'callChildrenSelfSave'], array_merge([$name, $this], $arguments));
        }
    }
}
<?php
/**
 * User: aozhuochao
 * Date: 2020/3/11
 */

namespace app\helpers\traits;

/**
 * @method callChildren($name, ...$arguments)
 * @method callChildrenSelfSave($name, $parentClass, ...$arguments)
 */
trait cloneSelfTrait
{
    /**
     * @var static[]
     */
    protected $callSave = [];

    protected $callArr = [
        'name' => '',
        'parentClass' => null,
        'arguments' => [],
    ];

    public function __call($name, $arguments)
    {
        static $num = 0;
        ++$num;

        return $this->callSave[$name . '-' . $num] = (clone $this)->setCallArr($num, $name, $this, $arguments);
    }

    /**
     * @param $num
     * @param string $callName
     * @param $parentClass
     * @param $arguments
     * @return $this
     */
    public function setCallArr($num, $callName, $parentClass, $arguments)
    {
        $this->callArr = [
            'num' => $num,
            'name' => $callName,
            'parentClass' => $parentClass,
            'arguments' => $arguments,
        ];

        return $this;
    }

    /**
     * 获取this,父类也获取自己的
     *
     * @return $this
     */
    public function getParentThis()
    {
        return empty($this->getCallName()) ? $this : $this->callArr['parentClass'];
    }

    public function getCallName()
    {
        return $this->callArr['name'];
    }
    public function getCallArguments()
    {
        return $this->callArr['arguments'];
    }
}
<?php

namespace system\helper;

/**
 * static缓存
 * 存在实例化open,过的,remember才走缓存
 */
class CacheStaticConditional
{
    protected static $instance;

    protected static $cache = [];

    public static function open()
    {
        return static::$instance = new static;
    }

    /**
     * 开启就存储$func的结果
     * 否则直接调用$func
     *
     * @param $cacheKey
     * @param $func
     * @param $dataArr
     * @return mixed
     */
    public static function remember($cacheKey, $func, $dataArr = [])
    {
        if (isset(static::$instance)) {
            $key = static::handleRememberCacheKey($cacheKey, $dataArr);
            return static::$cache[$key]?? (static::$cache[$key] = call_user_func_array($func, $dataArr));
        }

        return call_user_func_array($func, $dataArr);
    }

    /**
     * 根据缓存调用方法
     * 没缓存就不调用
     *
     * @param $cacheKey
     * @param $func
     * @param $default
     * @return mixed|null
     */
    public static function call($cacheKey, $func, $default = null)
    {
        if (is_null(static::$instance)) {
            return $default;
        }

        if (isset(static::$cache[$cacheKey])) {
            return call_user_func($func, static::$cache[$cacheKey]);
        }

        return  $default;
    }

    public static function handleRememberCacheKey($cacheKey, $dataArr = [])
    {
        $moreKey = $dataArr ? md5(serialize($dataArr)) : '';

        return $cacheKey . $moreKey;
    }
    
}
<?php

namespace system\helper;

/**
 * 将异常通过return返回
 */
class ThrowReturnException extends \RuntimeException
{
    protected $data;

    /**
     * @param $data
     * @noinspection PhpMissingParentConstructorInspection
     */
    public function __construct($data)
    {
        $this->data = $data;
    }


    public static function throw($data)
    {
        throw new static($data);
    }

    public static function run($func, $arr = [])
    {
        try{
            return call_user_func_array($func, $arr);
        }catch (ThrowReturnException $exception){
            return $exception->getData();
        }
    }

    /**
     * @return mixed
     */
    public function getData()
    {
        return $this->data;
    }
}
<?php

namespace system\engine;

/**
 * 代理重试,根据异常来
 */
class ProxyCallRetryException
{
    protected $exceptionFunc;
    protected $proxyObject;

    protected $retry = 2;


    public function __construct($proxyObject, $exceptionFunc, $retry = 2)
    {
        $this->exceptionFunc = $exceptionFunc;
        $this->proxyObject = $proxyObject;
        $this->retry = $retry;
    }

    public function __call($name, $arguments)
    {
        for ($a = 0; $a <= $this->retry; ++$a){
            try {
                return call_user_func_array([$this->proxyObject, $name], $arguments);

            }catch (\Exception | \Throwable $exception){
                if (!call_user_func($this->exceptionFunc, $exception, $this->proxyObject, $name, $arguments)) { // 返回false就中断
                    break;
                }

            }
        }


        return null;
    }

    

}
<?php

namespace App\Tools;

/**
 * 列表里详情的下一个数据
 */
class PageDetailNext
{

    /**
     * 详情获取下一个和上一个的id
     *
     * @see handleQueryOnNext($currentId, $requestArr, $action, $page, $setPageNextByIdLimit)
     *
     * @param int|string $currentId 可以整数或者字符串
     * @param callable $listFunc 每次加载一页
     * @param $uniKey
     * @param $redisPrefix
     * @return array|int[]
     */
    public static function getFuncId($currentId, $listFunc, $uniKey, $redisPrefix = 'page:next:')
    {

        $lastId = $nextId = 0;

        $inc = static::getInc(0, $uniKey, $redisPrefix);
        if (empty($inc)) { // 直接打开详情的
            return [
                'last' => $lastId,
                'next' => $nextId
            ];
        }

        $requestArr = \Illuminate\Support\Facades\Redis::get($redisPrefix . 'request:' . $inc . ':' . $uniKey)?:[];
        $requestArr = is_string($requestArr)?json_decode($requestArr, true):$requestArr;
        $setPageNextByIdLimit = 15;
        if (!empty($requestArr['setPageNextByIdLimit'])) {
            $setPageNextByIdLimit = $requestArr['setPageNextByIdLimit'];
        }

        $key = md5(json_encode($requestArr) . '--' . $inc . '--' . $uniKey);

        $pageKey = \Illuminate\Support\Facades\Redis::hGet($redisPrefix . 'hash:id:' . $key, $currentId);
        if (empty($pageKey)) { // 直接打开详情的
            return [
                'last' => $lastId,
                'next' => $nextId
            ];
        }

        $redisHashKey = $redisPrefix . 'hash:key:' . $key;
        $pageKeyLength = strlen($pageKey);
        $pageKeyLastThreeLimit = intval(substr($pageKey, -3)); // 获取后三位
        $pageKeyPrefixPage = substr($pageKey, 0, $pageKeyLength - 3); // 获取前面部分

//        $pageKeyArr = explode('_', $pageKey);
        $pageKeyArr = [$pageKeyPrefixPage, $pageKeyLastThreeLimit];

        $nextId = static::getNextId($redisHashKey, $pageKeyArr, $setPageNextByIdLimit);
        if (is_array($nextId)) {
            call_user_func($listFunc, $currentId, $requestArr, ...$nextId);
            $nextId = static::getNextId($redisHashKey, $pageKeyArr, $setPageNextByIdLimit);
            if (is_array($nextId)) {
                $nextId = 0;
            }
        }

        // 上一个
        $lastId = static::getLastId($redisHashKey, $pageKeyArr, $setPageNextByIdLimit);
        if (is_array($lastId)) {
            call_user_func($listFunc, $currentId, $requestArr, ...$lastId);
            $lastId = static::getLastId($redisHashKey, $pageKeyArr, $setPageNextByIdLimit);
            if (is_array($lastId)) {
                $lastId = 0;
            }
        }

        return [
            'last' => $lastId,
            'next' => $nextId
        ];
    }

    /**
     * 存储列表里详情的下一个数据
     *
     *
     * @param int[]|string[] $ids 整数或者字符串
     * @param int $limit 分页数量
     * @param $uniKey
     * @param $redisPrefix
     * @return int|string
     */
    public static function setPageNextById($ids, $page, $limit, $uniKey, $redisPrefix = 'page:next:', $moreRequestArr = [], $requestParamBool = true)
    {
        if (empty_a($ids)) {
            return '';
        }

        $arr = $requestParamBool?request()->input():[];
        $page = max(intval($page), 1);


        unset($arr['page']);
        $arr['setPageNextByIdLimit'] = $limit;
        $arr = array_merge($arr, $moreRequestArr);
        ksort($arr);
        $arrJson = json_encode($arr);

        $inc = static::getInc($page, $uniKey, $redisPrefix);

        $key = md5($arrJson . '--' . $inc . '--' . $uniKey);


        if ($page == 1) {
//        \Illuminate\Support\Facades\Redis::del($fullKey . ':request');
            \Illuminate\Support\Facades\Redis::set($redisPrefix . 'request:' . $inc . ':' . $uniKey, $arrJson, 86400 * 2);
        }

        $mArr = $mArrId = [];
        foreach ($ids as $idKey => $id) {
            $zKey = $page. str_pad($idKey, 3, '0', STR_PAD_LEFT);
            $mArr[$zKey] = $id;
            $mArrId[$id] = $zKey;
            \Illuminate\Support\Facades\Redis::zRem($redisPrefix . 'hash:key:' . $key, $zKey);
            \Illuminate\Support\Facades\Redis::zAdd($redisPrefix . 'hash:key:' . $key, $zKey, $id);
        }
        if (!empty($mArr)) {
            \Illuminate\Support\Facades\Redis::hMSet($redisPrefix . 'hash:id:' . $key, $mArrId);
            \Illuminate\Support\Facades\Redis::expire($redisPrefix . 'hash:key:' . $key, 86400 * 2);
            \Illuminate\Support\Facades\Redis::expire($redisPrefix . 'hash:id:' . $key, 86400 * 2);
        }


        return $inc;
    }



    protected static function getInc($page, $uniKey, $redisPrefix = 'page:next:')
    {

        if ($page == 1) {
            $inc = \Illuminate\Support\Facades\Redis::incr($redisPrefix . 'inc:' . $uniKey);
            \Illuminate\Support\Facades\Redis::expire($redisPrefix . 'inc:' . $uniKey, 86400 * 2);
        }else{
            $inc = \Illuminate\Support\Facades\Redis::get($redisPrefix . 'inc:' . $uniKey);
        }

        return $inc;
    }

    protected static function getNextId($redisHashKey, $pageKeyArr, $setPageNextByIdLimit)
    {
        if ($pageKeyArr[1] >= $setPageNextByIdLimit) { // 第二页
            $page = ($pageKeyArr[0] + 1);
            $hashKey = $page . ('000');
            $resultPage = $page;
        } else { // 当前页+1
            $page = ($pageKeyArr[0]);

            $hashKey = $page . (str_pad(($pageKeyArr[1] + 1), 3, '0', STR_PAD_LEFT));
            $resultPage = $page + 1;
        }

        $nextId = \Illuminate\Support\Facades\Redis::zRangeByScore($redisHashKey, $hashKey, '11111111' . '111', ['LIMIT' => [0, 1]]);

        if (empty($nextId)) { // 已有的没有,就查询
            return ['next', $resultPage, $setPageNextByIdLimit];
        }

        return current($nextId);
    }

    protected static function getLastId($redisHashKey, $pageKeyArr, $setPageNextByIdLimit)
    {
        if ($pageKeyArr[0] == 1 && empty($pageKeyArr[1])) { // 第一页第一个
            return 0;
        }

        if (empty($pageKeyArr[1])) { // 第一个
            $page = ($pageKeyArr[0] - 1);
            $hashKey = $page . (str_pad($setPageNextByIdLimit, 3, '0', STR_PAD_LEFT));
            $resultPage = $page;
        }else{ // 当前页-1
            $page = ($pageKeyArr[0]);
            $resultPage = $page - 1;

            $hashKey = $page . (str_pad(($pageKeyArr[1] - 1), 3, '0', STR_PAD_LEFT));
        }


        $lastId = \Illuminate\Support\Facades\Redis::zRevRangeByScore($redisHashKey, $hashKey, 0, ['LIMIT' => [0, 1]]);

        if (empty($lastId)) { // 已有的没有,就查询
            if ($resultPage < 2) { // 没有了
                return 0;
            }

            return ['last', $resultPage, $setPageNextByIdLimit];
        }

        return current($lastId);
    }
}
<?php

namespace app\utils;

/**
 * 代理返回null
 */
class ProxyNull
{
    public function __call($name, $arguments)
    {
        return null;
    }

    public static function __callStatic($name, $arguments)
    {
        return null;
    }


}
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think;

use Closure;
use Exception;
use Throwable;

class Pipeline
{
    protected $passable;

    protected $pipes = [];

    protected $exceptionHandler;

    /**
     * 初始数据
     * @param $passable
     * @return $this
     */
    public function send($passable)
    {
        $this->passable = $passable;
        return $this;
    }

    /**
     * 调用栈
     * @param $pipes
     * @return $this
     */
    public function through($pipes)
    {
        $this->pipes = is_array($pipes) ? $pipes : func_get_args();
        return $this;
    }

    /**
     * 执行
     * @param Closure $destination
     * @return mixed
     */
    public function then(Closure $destination)
    {
        $pipeline = array_reduce(
            array_reverse($this->pipes),
            $this->carry(),
            function ($passable) use ($destination) {
                try {
                    return $destination($passable);
                } catch (Throwable | Exception $e) {
                    return $this->handleException($passable, $e);
                }
            }
        );

        return $pipeline($this->passable);
    }

    /**
     * 设置异常处理器
     * @param callable $handler
     * @return $this
     */
    public function whenException($handler)
    {
        $this->exceptionHandler = $handler;
        return $this;
    }

    protected function carry()
    {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                try {
                    return $pipe($passable, $stack);
                } catch (Throwable | Exception $e) {
                    return $this->handleException($passable, $e);
                }
            };
        };
    }

    /**
     * 异常处理
     * @param $passable
     * @param $e
     * @return mixed
     */
    protected function handleException($passable, Throwable $e)
    {
        if ($this->exceptionHandler) {
            return call_user_func($this->exceptionHandler, $passable, $e);
        }
        throw $e;
    }

}
<?php

namespace App\Tools\Response;


class JsonFormat
{

    public static function format($code, $info, $data)
    {
        return compact('code', 'info', 'data');
    }

    public static function success($data = null)
    {
        return static::format(static::getCodeSuccess(), static::getCodeSuccessInfo(), $data);
    }

    public static function error($code = null, $info = null, $data = null)
    {
        return static::format($code??static::getCodeError(), $info??static::getCodeErrorInfo(), $data);
    }

    public static function errorMsg($info = null, $code = null, $data = null)
    {
        return static::format($code??static::getCodeError(), $info??static::getCodeErrorInfo(), $data);
    }


    public static function getCodeError()
    {
        return 400;
    }

    public static function getCodeErrorInfo()
    {
        return '错误';
    }



    public static function getCodeSuccess()
    {
        return 200;
    }

    public static function getCodeSuccessInfo()
    {
        return '成功';
    }

}
<?php

namespace app\services\promotion;

use app\model\Promotion;
use app\services\PromotionService;
use think\Collection;
use think\helper\Arr;
use think\helper\Str;

class EnvFilter
{
    public static function compare(array $env, array $condition, $comparators = null): bool
    {
        $compare = $comparators ? : self::comparators();
        foreach ($condition as $rule) {
            list($field, $operate, $value) = $rule;
            $dataValue = Arr::get($env, $field);
            $compareFunc = $compare[$operate] ?? function ($v1, $v2) { return false;};
            if (is_null($dataValue) || !$compareFunc($dataValue, $value)) {
                return false;
            }
        }
        return true;
    }

    public static function comparators(): array
    {
        return [
            "=" => function($v1, $v2) { return $v1 == $v2;},
            "!=" => function($v1, $v2) { return $v1 != $v2;},
            ">" => function ($v1, $v2) { return $v1 != $v2 && max($v1, $v2) == $v1;},
            ">=" => function ($v1, $v2) { return max($v1, $v2) == $v1;},
            "<" => function ($v1, $v2) { return $v1 != $v2 && max($v1, $v2) == $v2;},
            "<=" => function ($v1, $v2) { return max($v1, $v2) == $v2;},
            "in" => function ($v1, $v2) { return in_array($v1, explode(",", $v2));},
            "not in" => function ($v1, $v2) { return !in_array($v1, explode(",", $v2));},
            "^=" => function ($v1, $v2) { return Str::startsWith($v1, $v2);},
            "=$" => function ($v1, $v2) { return Str::endsWith($v1, $v2);},
            "contains" => function ($v1, $v2) { return Str::contains($v1, $v2);},
            "not contains" => function ($v1, $v2) { return !Str::contains($v1, $v2);},
            "regex" => function ($v1, $v2) { return preg_match($v2, $v1);},
            "before_time" => function ($v1, $v2) { return ($v1t = strtotime($v1)) && ($v2t = strtotime(sprintf("-%s", $v2))) && $v1t >= $v2t;},
            "after_time" => function ($v1, $v2) { return ($v1t = strtotime($v1)) && ($v2t = strtotime(sprintf("+%s", $v2))) && $v1t <= $v2t;},
            "before_out_time" => function ($v1, $v2) { return ($v1t = strtotime($v1)) && ($v2t = strtotime(sprintf("-%s", $v2))) && $v1t < $v2t;},
            "after_out_time" => function ($v1, $v2) { return ($v1t = strtotime($v1)) && ($v2t = strtotime(sprintf("+%s", $v2))) && $v1t > $v2t;},
        ];
    }
}
<?php

class DiffRunTime
{
    public static $arr = [];

    public static function start($key)
    {
        if (!isset(static::$arr[$key])) {
            static::$arr[$key] = [];
        }
        static::$arr[$key]['start'] = microtime(true);
    }

    public static function end($key){

        if (!isset(static::$arr[$key])) {
            static::$arr[$key] = [];
        }

        static::$arr[$key]['end'] = microtime(true);
        if (isset(static::$arr[$key]['start'])) {
//            $executionTime = ($endTime - $startTime) * 1000; // Convert to milliseconds
            static::$arr[$key]['diff'] = static::$arr[$key]['end'] - static::$arr[$key]['start'];
        }else{
            static::$arr[$key]['diff'] = false;
        }

    }

    public static function getArr()
    {
        return self::$arr;
    }

}
<?php


Class ClassMagicProxy{
    
    protected $that = null;
    protected $arr = [];
    protected $map = [];

    public static function new($that, $map, $arr)
    {
        return (new static())->setArr($arr)->setMap($map)->setThat($that);
    }

    public function setArr($arr)
    {
        $this->arr = $arr;
        return $this;
    }

    public function setMap($arr)
    {
        $this->map = $arr;
        return $this;
    }

    public function setThat($that)
    {
        $this->that = $that;
        return $this;
    }
    
    public function __get(string $name)
    {
        return call_user_func([$this->that, $this->map['__get']??'get'], $name, $this->arr);
    }

    public function __call(string $name, array $arguments)
    {
        return call_user_func([$this->that, $this->map['__call']??'call'], $name, $arguments, $this->arr);
    }

//    public static function __callStatic(string $name, array $arguments)
//    {
//        return call_user_func([$this->that, '__callStatic'], $name, $arguments, $this->arr);
//    }

}