<?php
namespace Li;

/**
 * Li framework application
 * it's what you want
 * @author seaice <haibing1458@163.com>
 * @link http://www.sunhaibing.com
 * @copyright 2015-2016 seaice (http://www.makeclean.net/php/li)
 * @license BSD license
 */
class App
{
    private static $_app;
    public $config = array(
        'debug'=>true,
        'controllerPath' => 'controller',
        'viewPath' => 'view',
        'defaultController' => 'site',
        'defaultAction' => 'index',
        'import'=>array(),
        'route'=>array(),
        'timezone'=>'Asia/Shanghai',
    );

    public $namespace; // 当前app的命名空间，必须和app的目录名一致
    public $route;
    public $controller;
    private static $_coreClasses = array(
        // 'Li\Config' => '/Config.php',
        'Li\Route' => '/Route.php',
        'Li\Controller' => '/Controller.php',
        'Li\Model' => '/Model.php',
        'Li\Form' => '/Form.php',
        'Li\Service' => '/Service.php',
        'Li\Expression' => '/Expression.php',
        'Li\HttpRequest' => '/HttpRequest.php',
        'Li\Exception' => '/Exception.php',
        'Li\Db' => '/Db.php',
        'Li\Log' => '/Log.php',
        'Li\File' => '/File.php',

        'Li\Mysql' => '/driver/Mysql.php',
        'Li\Redis' => '/driver/Redis.php',
        'Li\Iterator' => '/Iterator.php',

        'Li\GridView' => '/GridView.php',
        'Li\Pagination' => '/Pagination.php',
        'Li\Validate' => '/Validate.php',
        'Li\DataProvider' => '/DataProvider.php',

        'Li\Log' => '/Log.php',
        'Li\Session' => '/Session.php',

        // 'Li\Redis' => '/Redis.php',

        'Li\Captcha' => '/Captcha.php',
        'Li\Upload' => '/Upload.php',
    );

    public function __construct()
    {
        require(PATH_APP.'core/config/'.ENV.'/config.php');
        if (is_array($config)) {
            $this->config = array_merge($this->config, $config);
        }

        if($this->config['debug'] !== false) {
            error_reporting($this->config['debug']);
        } else {
            error_reporting(0);
        }
    }

    /**
     * init app
     * @return App the app instance
     */
    public static function init()
    {
        self::$_app = new self();
        self::$_app->_initConstant();

        require(PATH_LI . DIRECTORY_SEPARATOR . 'Function.php');
        return self::$_app;
    }

    private function _initRegister()
    {
        spl_autoload_register(array('Li\App', 'autoload'));
        register_shutdown_function(array('Li\App', 'shutdown'));
        // 设置一个用户定义的异常处理函数。
        set_exception_handler(array('Li\App', 'exceptionHandler'));
        // set_error_handler(array('Li\App', 'errorHandler'));

        Session::init();
    }

    /**
     * get app instance
     * @return App the app instance
     */
    public static function app()
    {
        return self::$_app;
    }

    /**
     * start the app
     */
    public function run($namespace)
    {
        $this->namespace = $namespace;
        $this->_initRegister();

        date_default_timezone_set($this->config['timezone']);

        if (false === IS_CLI) {
            $this->route = Route::instance()->init();
            $this->controller = Controller::create($this->route);
        }
    }

    public function _initConstant()
    {
        define('CHAR_TRUE', '1');
        define('CHAR_FALSE', '0');

        define('NOW', isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time());

        define('IS_POST', isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'], 'POST'));
        define('IS_GET', isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'], 'GET'));
        define('IS_PUT', isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT'));
        define('IS_DELETE', isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'], 'DELETE'));
        define('IS_CLI', PHP_SAPI == 'cli');
        define('IS_AJAX', isset($_SERVER["HTTP_X_REQUESTED_WITH"]) && strtolower($_SERVER["HTTP_X_REQUESTED_WITH"]) == "xmlhttprequest");

        define('PATH_VENDOR', realpath(__DIR__ . "/../../") . '\\'); //基础目录(绝对)
        define('PATH_BASE', PATH_VENDOR . '../'); // 整个代码的根目录
        define('PATH_LI', dirname(__FILE__)); // 框架目录(绝对)
        define('PATH_APP_REL', dirname($_SERVER['SCRIPT_NAME'])); // 当前APP的相对路径
    }

    /**
     * get controller path
     * @return string the path of controller
     */
    public function getControllerPath()
    {
        return PATH_APP . 'core\\' . $this->config['controllerPath'];
    }

    public function getViewPath()
    {
        return BASE_PATH. 'view\\' . $this->config['viewPath'];
    }

    public static function autoload($className)
    {
        if (isset(self::$_coreClasses[$className])) {
            require_once PATH_LI . self::$_coreClasses[$className];
        } else {
            // 无命名空间
            if (strpos($className, '\\')===false) {  // class without namespace
                if (isset(App::app()->config['import'])
                    && is_array(App::app()->config['import'])) {
                    foreach (\Li\App::app()->config['import'] as $path) {
                        $classFile = PATH_APP . 'core' . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR . $className.'.php';
                        if (is_file($classFile)) {
                            include($classFile);
                        }
                    }
                }
            } else {
                // 命名空间
                $path = explode('\\', $className);

                $classFile = PATH_APP . '..' . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $path) . '.php';

                if (is_file($classFile)) {
                    include($classFile);
                }

            }
            return class_exists($className, false) || interface_exists($className, false);
        }
        return true;
    }

    /**
     * catch fatal error
     */
    public static function shutdown()
    {
        $error = error_get_last();

        if ($error !== null) {
            if (App::app()->config['debug'] == false) {
                Log::log()->fatal('Fatal Error:  ' .$error['message'] . ' in '. $error['file']. ' on '.$error['line']);
            }
        }
    }

    public static function exceptionHandler($exception)
    {
        if (App::app()->config['debug']) {
            echo "Exception: " . $exception->getMessage(). ' in ' . $exception->getFile() . ' on ' . $exception->getLine() . "
";
            echo 'trace    :' . '
';
            $trace = $exception->getTrace();
            foreach ($trace as $v) {
                echo '' . $v['file'] . '  in  '. $v['function'] .'  on  ' . $v['line'] . "
";
            }
        } else {
            Log::log()->error("Exception: " . $exception->getMessage(). ' in ' . $exception->getFile() . ' on ' . $exception->getLine());
        }
    }

    public static function errorHandler($errno, $errstr, $errfile, $errline, $errContext)
    {
        $errtype = array(
            E_ERROR              => 'E_ERROR',
            E_WARNING            => 'E_WARNING',
            E_PARSE              => 'E_PARSE',
            E_NOTICE             => 'E_NOTICE',
            E_CORE_ERROR         => 'E_CORE_ERROR',
            E_CORE_WARNING       => 'E_CORE_WARNING',
            E_COMPILE_ERROR      => 'E_COMPILE_ERROR',
            E_COMPILE_WARNING    => 'E_COMPILE_WARNING',
            E_USER_ERROR         => 'E_USER_ERROR',
            E_USER_WARNING       => 'E_USER_WARNING',
            E_USER_NOTICE        => 'E_USER_NOTICE',
            E_STRICT             => 'E_STRICT',
            E_RECOVERABLE_ERROR  => 'E_RECOVERABLE_ERROR',
            E_DEPRECATED         => 'E_DEPRECATED',
            E_USER_DEPRECATED    => 'E_USER_DEPRECATED',
            E_ALL                => 'E_ALL'
        );
        if (defined('E_STRICT')) {
            $errtype[E_STRICT] = 'runtime notice';
        }

        if ($errtype[$errno] != 'E_DEPRECATED') {
            echo "[" . date('Y-m-d H:i:s', NOW) . "] PHP {$errtype[$errno]}:  $errstr in $errfile on $errline
";
        }
    }

    /**
     * @return string the version of LiPHP framework
     */
    public function getVersion()
    {
        return '1.0';
    }

    /**
     * build url with controller action and url param
     *
     * @param $controller string the controller name
     * @param $action string the action name
     * @param $param array the url param
     *
     * @return string the url
     */
    public function url($controller, $action, $param)
    {
        $path = url();
        if (empty($param)) {
            // return PATH_APP_REL.'/'.$controller.'/'.$action;
            return $path . '/' . $controller.'/'.$action;
        } else {
            // return PATH_APP_REL.'/'.$controller.'/'.$action.'?'.http_build_query($param);
            return $path . '/' . $controller.'/'.$action.'?'.http_build_query($param);
        }
    }
    public static function setLocale($locale)
    {
    }

    public static function t($id, $parameters = [], $locale = null)
    {
        return Locale::locale()->trans($id, $parameters, $locale);
    }
}


<?php

namespace Reaction\Helpers\Request;

use Reaction;
use Reaction\Exceptions\InvalidArgumentException;

/**
 * Class UrlHelper
 * @package Reaction\Helpers\Request
 * @property Reaction\Web\RequestHelper $request
 */
class UrlHelper extends RequestAppHelperProxy
{
    public $helperClass = 'Reaction\Helpers\Url';

    /**
     * Creates a URL for the given route.
     *
     * This method will use [[\Reaction\Web\UrlManager]] to create a URL.
     *
     * You may specify the route as a string, e.g., `site/index`. You may also use an array
     * if you want to specify additional query parameters for the URL being created. The
     * array format must be:
     *
     * ```php
     * // generates: /index.php?r=site/index&param1=value1&param2=value2
     * ['site/index', 'param1' => 'value1', 'param2' => 'value2']
     * ```
     *
     * If you want to create a URL with an anchor, you can use the array format with a `#` parameter.
     * For example,
     *
     * ```php
     * // generates: /index.php?r=site/index&param1=value1#name
     * ['site/index', 'param1' => 'value1', '#' => 'name']
     * ```
     *
     * A route may be either absolute or relative. An absolute route has a leading slash (e.g. `/site/index`),
     * while a relative route has none (e.g. `site/index` or `index`). A relative route will be converted
     * into an absolute one by the following rules:
     *
     * - If the route is an empty string, the current [[\yii\web\Controller::route|route]] will be used;
     * - If the route contains no slashes at all (e.g. `index`), it is considered to be an action ID
     *   of the current controller and will be prepended with [[\yii\web\Controller::uniqueId]];
     * - If the route has no leading slash (e.g. `site/index`), it is considered to be a route relative
     *   to the current module and will be prepended with the module's [[\yii\base\Module::uniqueId|uniqueId]].
     *
     * A route can also be specified as an alias. In this case, the alias
     * will be converted into the actual route first before conducting the above transformation steps.
     *
     * Below are some examples of using this method:
     *
     * ```php
     * // /index.php?r=site%2Findex
     * echo Url::toRoute('site/index');
     *
     * // /index.php?r=site%2Findex&src=ref1#name
     * echo Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);
     *
     * // http://www.example.com/index.php?r=site%2Findex
     * echo Url::toRoute('site/index', true);
     *
     * // https://www.example.com/index.php?r=site%2Findex
     * echo Url::toRoute('site/index', 'https');
     *
     * // /index.php?r=post%2Findex     assume the alias "@posts" is defined as "post/index"
     * echo Url::toRoute('@posts');
     * ```
     *
     * @param string|array $route use a string to represent a route (e.g. `index`, `site/index`),
     * or an array to represent a route with query parameters (e.g. `['site/index', 'param1' => 'value1']`).
     * @param bool|string $scheme the URI scheme to use in the generated URL:
     *
     * - `false` (default): generating a relative URL.
     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
     * - string: generating an absolute URL with the specified scheme (either `http`, `https` or empty string
     *   for protocol-relative URL).
     *
     * @return string the generated URL
     * @throws InvalidArgumentException a relative route is given while there is no active controller
     * @see \Reaction\Helpers\Url::toRoute()
     */
    public function toRoute($route, $scheme = false)
    {
        $route = (array) $route;
        $route[0] = $this->normalizeRoutePath($route[0]);

        if ($scheme !== false) {
            return $this->getUrlManager()->createAbsoluteUrl($route, is_string($scheme) ? $scheme : null);
        }

        return $this->getUrlManager()->createUrl($route);
    }

    /**
     * Creates a URL based on the given parameters.
     *
     * This method is very similar to [[toRoute()]]. The only difference is that this method
     * requires a route to be specified as an array only. If a string is given, it will be treated as a URL.
     * In particular, if `$url` is
     *
     * - an array: [[toRoute()]] will be called to generate the URL. For example:
     *   `['site/index']`, `['post/index', 'page' => 2]`. Please refer to [[toRoute()]] for more details
     *   on how to specify a route.
     * - a string with a leading `@`: it is treated as an alias, and the corresponding aliased string
     *   will be returned.
     * - an empty string: the currently requested URL will be returned;
     * - a normal string: it will be returned as is.
     *
     * When `$scheme` is specified (either a string or `true`), an absolute URL with host info (obtained from
     * [[\yii\web\UrlManager::$hostInfo]]) will be returned. If `$url` is already an absolute URL, its scheme
     * will be replaced with the specified one.
     *
     * Below are some examples of using this method:
     *
     * ```php
     * // /index.php?r=site%2Findex
     * echo Url::to(['site/index']);
     *
     * // /index.php?r=site%2Findex&src=ref1#name
     * echo Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);
     *
     * // /index.php?r=post%2Findex     assume the alias "@posts" is defined as "/post/index"
     * echo Url::to(['@posts']);
     *
     * // the currently requested URL
     * echo Url::to();
     *
     * // /images/logo.gif
     * echo Url::to('@web/images/logo.gif');
     *
     * // images/logo.gif
     * echo Url::to('images/logo.gif');
     *
     * // http://www.example.com/images/logo.gif
     * echo Url::to('@web/images/logo.gif', true);
     *
     * // https://www.example.com/images/logo.gif
     * echo Url::to('@web/images/logo.gif', 'https');
     *
     * // //www.example.com/images/logo.gif
     * echo Url::to('@web/images/logo.gif', '');
     * ```
     *
     *
     * @param array|string $url the parameter to be used to generate a valid URL
     * @param bool|string $scheme the URI scheme to use in the generated URL:
     *
     * - `false` (default): generating a relative URL.
     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
     * - string: generating an absolute URL with the specified scheme (either `http`, `https` or empty string
     *   for protocol-relative URL).
     *
     * @return string the generated URL
     * @throws InvalidArgumentException a relative route is given while there is no active controller
     * @see \Reaction\Helpers\Url::to()
     */
    public function to($url = '', $scheme = false)
    {
        if (is_array($url)) {
            return $this->toRoute($url, $scheme);
        }

        $url = Reaction::$app->getAlias($url);
        if ($url === '') {
            $url = $this->request->getUrl();
        }

        if ($scheme === false) {
            return $url;
        }

        if ($this->isRelative($url)) {
            // turn relative URL into absolute
            $url = $this->getUrlManager()->getHostInfo() . '/' . ltrim($url, '/');
        }

        return $this->ensureScheme($url, $scheme);
    }

    /**
     * Normalize URL by ensuring that it use specified scheme.
     *
     * If URL is relative or scheme is not string, normalization is skipped.
     *
     * @param string $url the URL to process
     * @param string $scheme the URI scheme used in URL (e.g. `http` or `https`). Use empty string to
     * create protocol-relative URL (e.g. `//example.com/path`)
     * @return string the processed URL
     * @see \Reaction\Helpers\Url::ensureScheme()
     */
    public function ensureScheme($url, $scheme)
    {
        if ($this->isRelative($url) || !is_string($scheme)) {
            return $url;
        }

        if (substr($url, 0, 2) === '//') {
            // e.g. //example.com/path/to/resource
            return $scheme === '' ? $url : "$scheme:$url";
        }

        if (($pos = strpos($url, '://')) !== false) {
            if ($scheme === '') {
                $url = substr($url, $pos + 1);
            } else {
                $url = $scheme . substr($url, $pos);
            }
        }

        return $url;
    }

    /**
     * Returns the base URL of the current request.
     * @param bool|string $scheme the URI scheme to use in the returned base URL:
     *
     * - `false` (default): returning the base URL without host info.
     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
     * - string: returning an absolute base URL with the specified scheme (either `http`, `https` or empty string
     *   for protocol-relative URL).
     * @return string
     * @see \Reaction\Helpers\Url::base()
     */
    public function base($scheme = false)
    {
        $url = $this->getUrlManager()->getBaseUrl();
        if ($scheme !== false) {
            $url = $this->getUrlManager()->getHostInfo() . $url;
            $url = $this->ensureScheme($url, $scheme);
        }

        return $url;
    }

    /**
     * Remembers the specified URL so that it can be later fetched back by [[previous()]].
     *
     * @param string|array $url the URL to remember. Please refer to [[to()]] for acceptable formats.
     * If this parameter is not specified, the currently requested URL will be used.
     * @param string $name the name associated with the URL to be remembered. This can be used
     * later by [[previous()]]. If not set, [[\Reaction\Web\User::setReturnUrl()]] will be used with passed URL.
     * @see previous()
     * @see \Reaction\Web\User::setReturnUrl()
     * @see \Reaction\Helpers\Url::remember()
     */
    public function remember($url = '', $name = null)
    {
        $url = $this->to($url);

        if ($name === null) {
            $this->app->user->setReturnUrl($url);
        } else {
            $this->app->session->set($name, $url);
        }
    }

    /**
     * Returns the URL previously [[remember()|remembered]].
     *
     * @param string $name the named associated with the URL that was remembered previously.
     * If not set, [[\Reaction\Web\User::getReturnUrl()]] will be used to obtain remembered URL.
     * @return string|null the URL previously remembered. Null is returned if no URL was remembered with the given name
     * and `$name` is not specified.
     * @see remember()
     * @see \Reaction\Web\User::getReturnUrl()
     * @see \Reaction\Helpers\Url::previous()
     */
    public function previous($name = null)
    {
        if ($name === null) {
            return $this->app->user->getReturnUrl();
        }

        return $this->app->session->get($name);
    }

    /**
     * Returns the canonical URL of the currently requested page.
     *
     * The canonical URL is constructed using the current controller's [[\yii\web\Controller::route]] and
     * [[\yii\web\Controller::actionParams]]. You may use the following code in the layout view to add a link tag
     * about canonical URL:
     *
     * ```php
     * $this->registerLinkTag(['rel' => 'canonical', 'href' => Url::canonical()]);
     * ```
     *
     * @return string the canonical URL of the currently requested page
     * @see \Reaction\Helpers\Url::canonical()
     */
    public function canonical()
    {
        $routePath = $this->getCurrentPath(true);
        if ($routePath === null) {
            return null;
        }
        $params = $this->app->getRoute()->getRouteParams();
        $params[0] = $routePath;

        return $this->getUrlManager()->createAbsoluteUrl($params);
    }

    /**
     * Returns the home URL.
     *
     * @param bool|string $scheme the URI scheme to use for the returned URL:
     *
     * - `false` (default): returning a relative URL.
     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
     * - string: returning an absolute URL with the specified scheme (either `http`, `https` or empty string
     *   for protocol-relative URL).
     *
     * @return string home URL
     * @see \Reaction\Helpers\Url::home()
     */
    public function home($scheme = false)
    {
        $url = $this->app->getHomeUrl();

        if ($scheme !== false) {
            $url = $this->getUrlManager()->getHostInfo() . $url;
            $url = $this->ensureScheme($url, $scheme);
        }
        if (strpos($url, '/') !== 0) {
            $url = '/' . $url;
        }

        return $url;
    }

    /**
     * Returns a value indicating whether a URL is relative.
     * A relative URL does not have host info part.
     * @param string $url the URL to be checked
     * @return bool whether the URL is relative
     */
    public function isRelative($url)
    {
        return strncmp($url, '//', 2) && strpos($url, '://') === false;
    }

    /**
     * Creates a URL by using the current route and the GET parameters.
     *
     * You may modify or remove some of the GET parameters, or add additional query parameters through
     * the `$params` parameter. In particular, if you specify a parameter to be null, then this parameter
     * will be removed from the existing GET parameters; all other parameters specified in `$params` will
     * be merged with the existing GET parameters. For example,
     *
     * ```php
     * // assume $_GET = ['id' => 123, 'src' => 'google'], current route is "post/view"
     *
     * // /index.php?r=post%2Fview&id=123&src=google
     * echo Url::current();
     *
     * // /index.php?r=post%2Fview&id=123
     * echo Url::current(['src' => null]);
     *
     * // /index.php?r=post%2Fview&id=100&src=google
     * echo Url::current(['id' => 100]);
     * ```
     *
     * Note that if you're replacing array parameters with `[]` at the end you should specify `$params` as nested arrays.
     * For a `PostSearchForm` model where parameter names are `PostSearchForm[id]` and `PostSearchForm[src]` the syntax
     * would be the following:
     *
     * ```php
     * // index.php?r=post%2Findex&PostSearchForm%5Bid%5D=100&PostSearchForm%5Bsrc%5D=google
     * echo Url::current([
     *     $postSearch->formName() => ['id' => 100, 'src' => 'google'],
     * ]);
     * ```
     *
     * @param array $params an associative array of parameters that will be merged with the current GET parameters.
     * If a parameter value is null, the corresponding GET parameter will be removed.
     * @param bool|string $scheme the URI scheme to use in the generated URL:
     *
     * - `false` (default): generating a relative URL.
     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
     * - string: generating an absolute URL with the specified scheme (either `http`, `https` or empty string
     *   for protocol-relative URL).
     *
     * @return string the generated URL
     * @see \Reaction\Helpers\Url::current()
     */
    public function current(array $params = [], $scheme = false)
    {
        $routePath = $this->getCurrentPath(true);
        if ($routePath === null) {
            return null;
        }
        $currentParams = $this->request->getQueryParams();
        $currentParams[0] = $routePath;
        $route = array_replace_recursive($currentParams, $params);
        return $this->toRoute($route, $scheme);
    }

    /**
     * Normalizes route and makes it suitable for UrlManager. Absolute routes are staying as is
     * while relative routes are converted to absolute ones.
     *
     * A relative route is a route without a leading slash, such as "view", "post/view".
     *
     * - If the route is an empty string, the current [[\yii\web\Controller::route|route]] will be used;
     * - If the route contains no slashes at all, it is considered to be an action ID
     *   of the current controller and will be prepended with [[\yii\web\Controller::uniqueId]];
     * - If the route has no leading slash, it is considered to be a route relative
     *   to the current module and will be prepended with the module's uniqueId.
     *
     * A route can also be specified as an alias. In this case, the alias
     * will be converted into the actual route first before conducting the above transformation steps.
     *
     * @param string              $routePath the route. This can be either an absolute route or a relative route.
     * @return string normalized  route suitable for UrlManager
     */
    protected function normalizeRoutePath($routePath)
    {
        $routePath = Reaction::$app->getAlias((string) $routePath);
        if (strncmp($routePath, '/', 1) === 0) {
            // absolute route
            return $routePath;
        }

        $route = $this->app->getRoute();

        // relative route
        if ($route === null || $route->controller === null) {
            throw new InvalidArgumentException("Unable to resolve the relative route: $routePath. No active controller is available.");
        }

        return $routePath === '' ? $route->getRoutePath(true) : $route->controller->group() . '/' . $routePath;
    }

    /**
     * Get current path from request & current controller
     * @param bool $onlyStaticPart
     * @return null|string
     */
    protected function getCurrentPath($onlyStaticPart = false) {
        $route = $this->app->getRoute();
        return $route ? $route->getRoutePath($onlyStaticPart) : null;
    }

    /**
     * Get RequestHelper
     * @return Reaction\Web\RequestHelper
     */
    protected function getRequest() {
        return $this->app->reqHelper;
    }
    /**
     * @return \Reaction\Web\UrlManager URL manager used to create URLs
     */
    protected function getUrlManager()
    {
        return $this->app->getUrlManager();
    }
}


<?php

declare(strict_types=1);

namespace Psi\Component\Description\Schema\Extension;

use Psi\Component\Description\Schema\ExtensionInterface;
use Psi\Component\Description\Schema\Builder;
use Psi\Component\Description\Descriptor\BooleanDescriptor;
use Psi\Component\Description\Descriptor\ArrayDescriptor;
use Psi\Component\Description\Descriptor\UriCollectionDescriptor;

/**
 * Standard extension for providing hierarchical descriptors.
 *
 * Can be applied to file systems, hierarchical databases, etc.
 */
class HierarchyExtension implements ExtensionInterface
{
    /**
     * {@inheritdoc}
     */
    public function buildSchema(Builder $builder)
    {
        $builder->add('allow_children', BooleanDescriptor::class, 'This class is allowed to have children');
        $builder->add('children_types', ArrayDescriptor::class, 'Valid children types');
        $builder->add('uris.create_child', UriCollectionDescriptor::class, 'Collection of URLs where children can be created (keys are the child types)');
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'hierarchy';
    }
}


<?php

/*
 * This file is part of the Snide monitoring package.
 *
 * (c) Pascal DENIS <pascal.denis.75@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Snide\Monitoring\Model;

/**
 * Class Application
 *
 * @author Pascal DENIS <pascal.denis.75@gmail.com>
 */
class Application
{
    /**
     * App ID
     *
     * @var string
     */
    protected $id;
    /**
     * App name
     *
     * @var string
     */
    protected $name;
    /**
     * App url
     *
     * @var string
     */
    protected $url;
    /**
     * List of tests
     *
     * @var array
     */
    protected $tests;

    /**
     * Down exception
     *
     * @var \Exception
     */
    protected $exception;

    /**
     * Getter exception
     *
     * @return \Exception
     */
    public function getException()
    {
        return $this->exception;
    }

    /**
     * Setter exception
     *
     * @param \Exception $exception Down exception
     */
    public function setException(\Exception $exception)
    {
        $this->exception = $exception;
    }

    /**
     * Getter ID
     *
     * @return string
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Setter ID
     *
     * @param $id App ID
     */
    public function setId($id)
    {
        $this->id = $id;
    }

    /**
     * Getter name
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Setter name
     *
     * @param $name App name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Getter url
     *
     * @return string
     */
    public function getUrl()
    {
        return $this->url;
    }

    /**
     * Setter url
     *
     * @param $url App url
     */
    public function setUrl($url)
    {
        $this->url = $url;
    }

    /**
     * Add a test to the list
     *
     * @param Test $test A test
     */
    public function addTest(Test $test)
    {
        $this->tests[] = $test;
    }

    /**
     * Getter tests
     *
     * @return array
     */
    public function getTests()
    {
        if (!is_array($this->tests)) {
            $this->tests = array();
        }
        return $this->tests;
    }

    /**
     * Setter tests
     *
     * @param array $tests List of tests
     */
    public function setTests(array $tests = array())
    {
        $this->tests = array();
        foreach ($tests as $test) {
            $this->addTest($test);
        }
    }

    /**
     * Application is working if all tests are OK & no down exception is set
     *
     * @return boolean
     */
    public function isWorking()
    {
        if (null != $this->exception) {
            return false;
        }

        foreach ($this->getTests() as $test) {
            if ($test->hasFailed()) {
                $this->exception = new \Exception('Tests failed');
                return false;
            }
        }

        return true;
    }
}


<?php

namespace Reaction\Db;

use Reaction\Promise\ExtendedPromiseInterface;
use function Reaction\Promise\resolve;

/**
 * ViewFinderTrait implements the method getViewNames for finding views in a database.
 */
trait ViewFinderTrait
{
    /**
     * @var array list of ALL view names in the database
     */
    private $_viewNames = [];

    /**
     * Returns all views names in the database.
     * @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema.
     * @return ExtendedPromiseInterface with array all views names in the database. The names have NO schema name prefix.
     */
    abstract protected function findViewNames($schema = '');

    /**
     * Returns all view names in the database.
     * @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema name.
     * If not empty, the returned view names will be prefixed with the schema name.
     * @param bool $refresh whether to fetch the latest available view names. If this is false,
     * view names fetched previously (if available) will be returned.
     * @return ExtendedPromiseInterface with string[] all view names in the database.
     */
    public function getViewNames($schema = '', $refresh = false)
    {
        if (!isset($this->_viewNames[$schema]) || $refresh) {
            return $this->findViewNames($schema)->then(
                function($names) use($schema) {
                    $this->_viewNames[$schema] = $names;
                    return $names;
                }
            );
        }

        return resolve($this->_viewNames[$schema]);
    }
}


<?php

namespace Sinpe\Cache;

use Psr\Log\LoggerInterface;
use Psr\SimpleCache\CacheInterface;
use Sinpe\Support\Traits\MacroAware as MacroTrait;
use Illuminate\Support\Collection;

/**
 * Psr16Manager class
 *  *
 * 缓存处理器封装类.
 *
 * @author    wupinglong <18222544@qq.com>
 * @copyright 2018 Sinpe, Inc.
 *
 * @see      http://www.sinpe.com/
 */
class Psr16Manager implements ManagerInterface
{
    use MacroTrait;

    /**
     * The cache handler.
     *
     * @var \Psr\SimpleCache\CacheInterface
     */
    protected static $handler;

    /**
     * 是否处于调试.
     *
     * @var bool
     */
    private $_debug = false;

    /**
     * 日志处理器.
     *
     * @var \Psr\Log\LoggerInterface
     */
    private $_logger;

    /**
     * The cache tag assistant.
     *
     * @var \Sinpe\Cache\TagAssistant
     */
    protected $assistant;

    /**
     * The default number of seconds to store items.
     *
     * @var int
     */
    protected $ttl = 60;

    /**
     * 类全局设置.
     *
     * @param \Psr\SimpleCache\CacheInterface $handler 缓存处理器
     */
    public static function setHandler(CacheInterface $handler)
    {
        static::$handler = $handler;
    }

    /**
     * Create a new cache repository instance.
     *
     * @param \Psr\SimpleCache\CacheInterface $handler 缓存处理器
     */
    public function __construct(CacheInterface $handler = null)
    {
        // 对象单独设置
        if (!is_null($handler)) {
            static::setHandler($handler);
        }
    }

    /**
     * 设置调试状态
     *
     * @param bool $debug 是否调试
     *
     * @return bool
     */
    public function debug($debug = null)
    {
        if (!is_null($debug)) {
            $this->_debug = (bool) $debug;
        }

        return $this->_debug;
    }

    /**
     * 日志.
     *
     * @param \Psr\Log\LoggerInterface $logger 日志处理器
     *
     * @return \Psr\Log\LoggerInterface
     */
    public function logger(LoggerInterface $logger = null)
    {
        if (!is_null($logger)) {
            $this->_logger = $logger;
        }

        return $this->_logger;
    }

    /**
     * Return an instance with the tags.
     *
     * @param string $tags 键
     *
     * @return static
     */
    public function withTags($tags)
    {
        $clone = clone $this;

        $clone->assistant = new TagAssistant(
            $clone,
            is_array($tags) ? $tags : func_get_args()
        );

        return $clone;
    }

    /**
     * Return 缓存助手，缓存key管理.
     *
     * @return Assistant
     */
    public function getAssistant()
    {
        return $this->assistant;
    }

    /**
     * Return an instance with the ttl.
     *
     * @param int $ttl 生命值
     *
     * @return static
     */
    public function withTTL(int $ttl)
    {
        $clone = clone $this;

        $clone->ttl = $ttl;

        return $clone;
    }

    /**
     * Retrieve an item from the cache by key.
     *
     * @param string $key     键
     * @param mixed  $default 默认值
     *
     * @return mixed
     */
    public function get(string $key, $default = null)
    {
        $value = static::$handler->get($this->normalized($key));
        // If we could not find the cache value, we will get
        // the default value for this cache value. This default could be a callback
        // so we will execute the value function which will resolve it if needed.
        if (is_null($value)) {
            $value = is_callable($default) ? $default() : $default;
        }

        if (static::hasMacro()) {
            $value = $this->runMacro('getAfter', [$value, $key, false]);
        }

        return $value;
    }

    /**
     * Obtains multiple cache items by their unique keys.
     *
     * @param array $keys    a list of keys that can obtained in a single operation
     * @param mixed $default default value to return for keys that do not exist
     *
     * @return iterable A list of key => value pairs.
     *                  Cache keys that do not exist or are stale will have $default as value.
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     *                                                   MUST be thrown if $keys is neither an array nor a Traversable,
     *                                                   or if any of the $keys are not a legal value
     */
    public function getMultiple(array $keys, $default = null)
    {
        $keyMaps = [];

        $values = static::$handler->getMultiple(
            (new Collection($keys))->map(
                // 回调
                function ($value, $key) use ($keyMaps) {
                    $keyOri = is_string($key) ? $key : $value;
                    $keyNormalized = $this->normalized($keyOri);

                    $keyMaps[$keyNormalized] = $keyOri;

                    return $keyNormalized;
                }
            )->values()->all()
        );

        $values = (new Collection($values))->mapWithKeys(
            function ($value, $key) use ($keys, $default, $keyMaps) {
                $keyOri = $keyMaps[$key];
                // If we could not find the cache value, we will get
                // the default value for this cache value. This default could be a callback
                // so we will execute the value function which will resolve it if needed.
                if (is_null($value)) {
                    // 如果keys自带有默认值
                    if (isset($keys[$keyOri])) {
                        $keyDefault = $keys[$keyOri];

                        $value = is_callable($keyDefault)
                            ? $keyDefault() : $keyDefault;
                    }

                    if (!is_null($default)) {
                        $value = is_callable($default)
                            ? $default($keyOri) : $default;
                    }
                }

                return [$keyOri => $value];
            }
        )->all();

        if (static::hasMacro()) {
            $values = $this->runMacro('getAfter', [$values, $keys, true]);
        }

        return $values;
    }

    /**
     * Store an item in the cache.
     *
     * @param string                               $key   键
     * @param mixed                                $value 值
     * @param \DateTimeInterface|\DateInterval|int $ttl   生命值，秒
     *
     * @return bool
     */
    public function set(string $key, $value, int $ttl = null)
    {
        $keyNormalized = $this->normalized($key);

        $result = static::$handler->set($keyNormalized, $value, $ttl ?? $this->ttl);

        if ($result) {
            if ($this->assistant) {
                $this->assistant->add($keyNormalized);
                $this->assistant->save();
            }
        }

        return $result;
    }

    /**
     * Store multiple items in the cache for a given number of minutes.
     *
     * @param array                                $values key、value表
     * @param \DateTimeInterface|\DateInterval|int $ttl    生命值，秒
     */
    public function setMultiple(array $values, int $ttl = null)
    {
        $valuesNormalized = [];

        foreach ($values as $key => $value) {
            $valuesNormalized[$this->normalized($key)] = $value;
        }

        $result = static::$handler->setMultiple(
            $valuesNormalized,
            $ttl ?? $this->ttl
        );

        if ($result) {
            if ($this->assistant) {
                foreach (array_keys($valuesNormalized) as $key) {
                    $this->assistant->add($key);
                }
                $this->assistant->save();
            }
        }

        return $result;
    }

    /**
     * Retrieve an item from the cache and delete it.
     *
     * @param string $key     键
     * @param mixed  $default 默认值
     *
     * @return mixed|null
     */
    public function pull(string $key, $default = null)
    {
        $result = $this->get($key, $default);

        $this->delete($key);

        return $result;
    }

    /**
     * Store an item in the cache if the key does not exist.
     *
     * @param string                               $key   键
     * @param mixed                                $value 值
     * @param \DateTimeInterface|\DateInterval|int $ttl   生命值，秒
     *
     * @return bool
     */
    public function add(string $key, $value, int $ttl = null)
    {
        // If the store has an "add" method we will call the method on the store so it
        // has a chance to override this logic. Some drivers better support the way
        // this operation should work with a total "atomic" implementation of it.
        if (method_exists(static::$handler, 'add')) {
            return static::$handler->add(
                $this->normalized($key),
                $value,
                $ttl ?? $this->ttl
            );
        }

        // If the value did not exist in the cache, we will put the value in the cache
        // so it exists for subsequent requests. Then, we will return true so it is
        // easy to know if the value gets added. Otherwise, we will return false.
        if (is_null($this->get($key))) {
            return $this->set($key, $value, $ttl ?? $this->ttl);
        }

        return false;
    }

    /**
     * Get an item from the cache, or store the default value.
     *
     * @param string                               $key      键
     * @param \Closure                             $callback 获取不到时的取值
     * @param \DateTimeInterface|\DateInterval|int $ttl      生命值
     *
     * @return mixed
     */
    public function remember(string $key, \Closure $callback, $ttl = null)
    {
        $value = $this->get($key);
        // If the item exists in the cache we will just return this immediately and if
        // not we will execute the given Closure and cache the result of that for a
        // given number of seconds so it's available for all subsequent requests.
        if (!is_null($value)) {
            return $value;
        }

        $this->set($key, $value = $callback(), $ttl ?? $this->ttl);

        return $value;
    }

    /**
     * Remove an item from the cache.
     *
     * @param string $key 缓存键
     *
     * @return bool
     */
    public function delete(string $key)
    {
        $keyNormalized = $this->normalized($key);

        $result = static::$handler->delete($keyNormalized);

        if ($result) {
            if ($this->assistant) {
                $this->assistant->remove($keyNormalized);
                $this->assistant->save();
            }
        }

        return $result;
    }

    /**
     * {@inheritdoc}
     */
    public function deleteMultiple(array $keys)
    {
        $keysNormalized = [];

        foreach ($keys as $key) {
            $keysNormalized[] = $this->normalized($key);
        }

        $result = static::$handler->deleteMultiple($keysNormalized);

        if ($result) {
            if ($this->assistant) {
                foreach ($keysNormalized as $key) {
                    $this->assistant->remove($key);
                }
                $this->assistant->save();
            }
        }

        return $result;
    }

    /**
     * Increment/Decrement the value of an item in the cache.
     *
     * @param string $key  键
     * @param mixed  $step 改变量
     *
     * @return int|bool
     */
    public function indecrement(string $key, $step = 1)
    {
        $value = $this->get($key, 0);

        $value += $step;

        $this->set($key, $value);

        return $value;
    }

    /**
     * 对存储到缓存系统的key进行格式化，通过子类覆盖来自定义你自己的规则。
     *
     * @param string $key 键
     *
     * @return string
     */
    protected function normalized($key)
    {
        return $key;
    }

    /**
     * Call a method.
     *
     * @param string $method    调用方法
     * @param array  $arguments 调用参数
     *
     * @return mixed
     */
    public function __call($method, $arguments)
    {
        if (method_exists(static::$handler, $method)) {
            return static::$handler->$method(...$parameters);
        }

        throw new \BadMethodCallException(
            sprintf(
                'Method "%s::%s" does not exist.',
                get_class($this),
                $method
            )
        );
    }

    /**
     * Clone cache repository instance.
     */
    public function __clone()
    {
        $this->assistant = null;
    }
}


<?php

namespace Think\Template\TagLib;

use Think\Template\TagLib;

/**
 * Html标签库驱动
 */
final class Html extends TagLib
{
    // 标签定义
    protected $tags = [
        // 标签定义： attr 属性列表 close 是否闭合（0 或者1 默认1） alias 标签别名 level 嵌套层次
        'editor'   => ['attr' => 'id,name,style,width,height,type', 'close' => 1],
        'select'   => [
            'attr'  => 'name,options,values,output,multiple,id,size,first,change,selected,dblclick',
            'close' => 0,
        ],
        'grid'     => ['attr' => 'id,pk,style,action,actionlist,show,datasource', 'close' => 0],
        'list'     => ['attr' => 'id,pk,style,action,actionlist,show,datasource,checkbox', 'close' => 0],
        'imagebtn' => ['attr' => 'id,name,value,type,style,click', 'close' => 0],
        'checkbox' => ['attr' => 'name,checkboxes,checked,separator', 'close' => 0],
        'radio'    => ['attr' => 'name,radios,checked,separator', 'close' => 0],
    ];

    /**
     * editor标签解析 插入可视化编辑器
     * 格式： {$vo.remark}

     *
     * @param array $tag 标签属性
     * @param   string $content
     *
     * @return string
     */
    public function _editor(array $tag = [], $content = '')
    {
        $id = !empty($tag['id']) ? $tag['id'] : '_editor';
        $name = $tag['name'];
        $style = !empty($tag['style']) ? $tag['style'] : '';
        $width = !empty($tag['width']) ? $tag['width'] : '100%';
        $height = !empty($tag['height']) ? $tag['height'] : '320px';
        //   $content    =   $tag['content'];
        $type = $tag['type'];
        switch (strtoupper($type)) {
            case 'FCKEDITOR':
                $parseStr = '<textarea id="'.$id.'" name="'.$name.'">'.$content.'</textarea> ';
                break;
            case 'FCKMINI':
                $parseStr = '<textarea id="'.$id.'" name="'.$name.'">'.$content.'</textarea> ';
                break;
            case 'EWEBEDITOR':
                $parseStr = "<input type='hidden'  id='{$id}' name='{$name}'  value='{$content}'>";
                break;
            case 'NETEASE':
                $parseStr = '<textarea id="'.$id.'" name="'.$name.'" style="display:none">'.$content.'</textarea>';
                break;
            case 'UBB':
                $parseStr = '

<TEXTAREA id="UBBEditor" name="'.$name.'"  style="clear:both;float:none;width:'.$width.';height:'.$height.'" >'.$content.'</TEXTAREA>

';
                break;
            case 'KINDEDITOR':
                $parseStr = '<textarea id="'.$id.'" style="'.$style.'" name="'.$name.'" >'.$content.'</textarea>';
                break;
            default :
                $parseStr = '<textarea id="'.$id.'" style="'.$style.'" name="'.$name.'" >'.$content.'</textarea>';
        }

        return $parseStr;
    }

    /**
     * imageBtn标签解析
     * 格式：

     *
     * @param array $tag 标签属性
     *
     * @return string
     */
    public function _imageBtn(array $tag = [])
    {
        //input name
        $name = $tag['name'];
        //input value
        $value = $tag['value'];
        //html id
        $id = isset($tag['id']) ? $tag['id'] : '';
        //html class
        $style = isset($tag['style']) ? $tag['style'] : '';
        //html input onclick
        $click = isset($tag['click']) ? $tag['click'] : '';
        //input type
        $type = empty($tag['type']) ? 'button' : $tag['type'];

        if (!empty($name)) {
            $parseStr = '
<input type="'.$type.'" id="'.$id.'" name="'.$name.'" value="'.$value.'" onclick="'.$click.'" class="'.$name.' imgButton">
';
        } else {
            $parseStr = '
<input type="'.$type.'" id="'.$id.'"  name="'.$name.'" value="'.$value.'" onclick="'.$click.'" class="button">
';
        }

        return $parseStr;
    }

    /**
     * imageLink标签解析
     * 格式：

     *
     * @param array $tag 标签属性
     *
     * @return string
     */
    public function _imgLink(array $tag = [])
    {
        //input name
        $name = $tag['name'];
        //alt文字
        $alt = $tag['alt'];
        //html ID
        $id = $tag['id'];
        //Html class
        $style = $tag['style'];
        //html input onclick
        $click = $tag['click'];
        //input type
        $type = $tag['type'];
        if (empty($type)) {
            $type = 'button';
        }

        return '<input title="'.$alt.'" type="'.$type.'" id="'.$id.'"  name="'.$name.'" onmouseover="this.style.filter=\'alpha(opacity=100)\'" onmouseout="this.style.filter=\'alpha(opacity=80)\'" onclick="'.$click.'" align="absmiddle" class="'.$name.' imgLink">';
    }

    /**
     * select标签解析
     * 格式：

     *
     * @param array $tag 标签属性
     *
     * @return string
     */
    public function _select(array $tag = [])
    {
        $name = $tag['name'];
        $options = $tag['options'];
        $values = $tag['values'];
        $output = $tag['output'];
        $multiple = $tag['multiple'];
        $id = $tag['id'];
        $size = $tag['size'];
        $first = $tag['first'];
        $selected = $tag['selected'];
        $style = $tag['style'];
        $ondblclick = $tag['dblclick'];
        $onchange = $tag['change'];

        if (!empty($multiple)) {
            $parseStr = '<select id="'.$id.'" name="'.$name.'" ondblclick="'.$ondblclick.'" onchange="'.$onchange.'" multiple="multiple" class="'.$style.'" size="'.$size.'" >';
        } else {
            $parseStr = '<select id="'.$id.'" name="'.$name.'" onchange="'.$onchange.'" ondblclick="'.$ondblclick.'" class="'.$style.'" >';
        }
        if (!empty($first)) {
            $parseStr .= '<option value="" >'.$first.'</option>';
        }
        if (!empty($options)) {
            $parseStr .= '<?php  foreach($'.$options.' as $key=>$val) { ?>';
            if (!empty($selected)) {
                $parseStr .= '<?php if(!empty($'.$selected.') && ($'.$selected.' == $key || in_array($key,$'.$selected.'))) { ?>';
                $parseStr .= '<option selected="selected" value="<?php echo $key ?>"><?php echo $val ?></option>';
                $parseStr .= '<?php }else { ?><option value="<?php echo $key ?>"><?php echo $val ?></option>';
                $parseStr .= '<?php } ?>';
            } else {
                $parseStr .= '<option value="<?php echo $key ?>"><?php echo $val ?></option>';
            }
            $parseStr .= '<?php } ?>';
        } elseif (!empty($values)) {
            $parseStr .= '<?php  for($i=0;$i<count($'.$values.');$i++) { ?>';
            if (!empty($selected)) {
                $parseStr .= '<?php if(isset($'.$selected.') && ((is_string($'.$selected.') && $'.$selected.' == $'.$values.'[$i]) || (is_array($'.$selected.') && in_array($'.$values.'[$i],$'.$selected.')))) { ?>';
                $parseStr .= '<option selected="selected" value="<?php echo $'.$values.'[$i] ?>"><?php echo $'.$output.'[$i] ?></option>';
                $parseStr .= '<?php }else { ?><option value="<?php echo $'.$values.'[$i] ?>"><?php echo $'.$output.'[$i] ?></option>';
                $parseStr .= '<?php } ?>';
            } else {
                $parseStr .= '<option value="<?php echo $'.$values.'[$i] ?>"><?php echo $'.$output.'[$i] ?></option>';
            }
            $parseStr .= '<?php } ?>';
        }

        return $parseStr.'</select>';
    }

    /**
     * checkbox标签解析
     * 格式：

     *
     * @param array $tag 标签属性
     *
     * @return string
     */
    public function _checkbox(array $tag = [])
    {
        $name = $tag['name'];
        $checkboxes = $tag['checkboxes'];
        $checked = $tag['checked'];
        $separator = $tag['separator'];
        $checkboxes = $this->tpl->get($checkboxes);
        $checked = $this->tpl->get($checked) ? $this->tpl->get($checked) : $checked;
        $parseStr = '';
        foreach ($checkboxes as $key => $val) {
            if ($checked == $key || in_array($key, $checked)) {
                $parseStr .= '<input type="checkbox" checked="checked" name="'.$name.'[]" value="'.$key.'">'.$val.$separator;
            } else {
                $parseStr .= '<input type="checkbox" name="'.$name.'[]" value="'.$key.'">'.$val.$separator;
            }
        }

        return $parseStr;
    }

    /**
     * radio标签解析
     * 格式：

     *
     * @param array $tag 标签属性
     *
     * @return string
     */
    public function _radio(array $tag = [])
    {
        $name = $tag['name'];
        $radios = $tag['radios'];
        $checked = $tag['checked'];
        $separator = $tag['separator'];
        $radios = $this->tpl->get($radios);
        $checked = $this->tpl->get($checked) ? $this->tpl->get($checked) : $checked;
        $parseStr = '';
        foreach ($radios as $key => $val) {
            if ($checked == $key) {
                $parseStr .= '<input type="radio" checked="checked" name="'.$name.'[]" value="'.$key.'">'.$val.$separator;
            } else {
                $parseStr .= '<input type="radio" name="'.$name.'[]" value="'.$key.'">'.$val.$separator;
            }

        }

        return $parseStr;
    }

    /**
     * list标签解析
     * 格式：

     *
     * @param array $tag 标签属性
     *
     * @return string
     */
    public function _grid(array $tag = [])
    {
        //表格ID
        $id = $tag['id'];
        //列表显示的数据源VoList名称
        $datasource = $tag['datasource'];
        //主键名，默认为id
        $pk = empty($tag['pk']) ? 'id' : $tag['pk'];
        //样式名
        $style = $tag['style'];
        //Vo对象名
        $name = !empty($tag['name']) ? $tag['name'] : 'vo';
        //是否显示功能操作
        $action = !empty($tag['action']) ? $tag['action'] : false;
        $key = !empty($tag['key']);
        if (isset($tag['actionlist'])) {
            //指定功能列表
            $actionlist = explode(',', trim($tag['actionlist']));
        }

        if (substr($tag['show'], 0, 1) == '$') {
            $show = $this->tpl->get(substr($tag['show'], 1));
        } else {
            $show = $tag['show'];
        }
        //列表显示字段列表
        $show = explode(',', $show);

        //计算表格的列数
        $colNum = count($show);
        if (!empty($action)) {
            $colNum++;
        }
        if (!empty($key)) {
            $colNum++;
        }

        //显示开始
        $parseStr = "\n";
        $parseStr .= '
';
        $parseStr .= '

';
        $parseStr .= '
';
        //列表需要显示的字段
        $fields = [];
        foreach ($show as $val) {
            $fields[] = explode(':', $val);
        }

        if (!empty($key)) {
            $parseStr .= 'No	';
        }
        //显示指定的字段
        foreach ($fields as $field) {
//                $property = explode('|', $field[0]);
            $showname = explode('|', $field[1]);
            if (isset($showname[1])) {
                $parseStr .= '';
            } else {
                $parseStr .= '';
            }
            $parseStr .= $showname[0].'	';
        }
        //如果指定显示操作功能列
        if (!empty($action)) {
            $parseStr .= '操作	';
        }
        $parseStr .= '
';
        $parseStr .= '<volist name="'.$datasource.'" id="'.$name.'" >
';    //支持鼠标移动单元行颜色变化 具体方法在js中定义

        if (!empty($key)) {
            $parseStr .= '{$i}	';
        }
        foreach ($fields as $field) {
            //显示定义的列表字段
            $parseStr .= '';
            if (!empty($field[2])) {
                // 支持列表字段链接功能 具体方法由JS函数实现
                $href = explode('|', $field[2]);
                if (count($href) > 1) {
                    //指定链接传的字段值
                    // 支持多个字段传递
                    $array = explode('^', $href[1]);
                    if (count($array) > 1) {
                        foreach ($array as $a) {
                            $temp[] = '\'{$'.$name.'.'.$a.'|addslashes}\'';
                        }
                        $parseStr .= '';
                    } else {
                        $parseStr .= '';
                    }
                } else {
                    //如果没有指定默认传编号值
                    $parseStr .= '';
                }
            }
            if (strpos($field[0], '^')) {
                $property = explode('^', $field[0]);
                foreach ($property as $p) {
                    $unit = explode('|', $p);
                    if (count($unit) > 1) {
                        $parseStr .= '{$'.$name.'.'.$unit[0].'|'.$unit[1].'} ';
                    } else {
                        $parseStr .= '{$'.$name.'.'.$p.'} ';
                    }
                }
            } else {
                $property = explode('|', $field[0]);
                if (count($property) > 1) {
                    $parseStr .= '{$'.$name.'.'.$property[0].'|'.$property[1].'}';
                } else {
                    $parseStr .= '{$'.$name.'.'.$field[0].'}';
                }
            }
            if (!empty($field[2])) {
                $parseStr .= '';
            }
            $parseStr .= '	';

        }
        //显示功能操作
        if (!empty($action)) {
            //显示指定的功能项
            if (!empty($actionlist[0])) {
                $parseStr .= '';
                foreach ($actionlist as $val) {
                    if (strpos($val, ':')) {
                        $a = explode(':', $val);
                        if (count($a) > 2) {
                            $parseStr .= ''.$a[1].' ';
                        } else {
                            $parseStr .= ''.$a[1].' ';
                        }
                    } else {
                        $array = explode('|', $val);
                        if (count($array) > 2) {
                            $parseStr .= ' '.$array[2].' ';
                        } else {
                            $parseStr .= ' {$'.$name.'.'.$val.'} ';
                        }
                    }
                }
                $parseStr .= '	';
            }
        }
        $parseStr .= '
</volist>

';

        return $parseStr."\n\n";
    }

    /**
     * list标签解析
     * 格式：

     *
     * @param array $tag 标签属性
     *
     * @return string
     */
    public function _list(array $tag = [])
    {
        //表格ID
        $id = $tag['id'];
        //列表显示的数据源VoList名称
        $datasource = $tag['datasource'];
        //主键名，默认为id
        $pk = empty($tag['pk']) ? 'id' : $tag['pk'];
        //样式名
        $style = $tag['style'];
        //Vo对象名
        $name = !empty($tag['name']) ? $tag['name'] : 'vo';
        //是否显示功能操作
        $action = $tag['action'] == 'true';
        $key = !empty($tag['key']);
        $sort = $tag['sort'] == 'false' ? false : true;
        //是否显示Checkbox
        $checkbox = $tag['checkbox'];
        if (isset($tag['actionlist'])) {
            if (substr($tag['actionlist'], 0, 1) == '$') {
                $actionlist = $this->tpl->get(substr($tag['actionlist'], 1));
            } else {
                $actionlist = $tag['actionlist'];
            }
            //指定功能列表
            $actionlist = explode(',', trim($actionlist));
        }

        if (substr($tag['show'], 0, 1) == '$') {
            $show = $this->tpl->get(substr($tag['show'], 1));
        } else {
            $show = $tag['show'];
        }
        //列表显示字段列表
        $show = explode(',', $show);

        //计算表格的列数
        $colNum = count($show);
        if (!empty($checkbox)) {
            $colNum++;
        }
        if (!empty($action)) {
            $colNum++;
        }
        if (!empty($key)) {
            $colNum++;
        }

        //显示开始
        $parseStr = "\n";
        $parseStr .= '
';
        $parseStr .= '

';
        $parseStr .= '
';
        //列表需要显示的字段
        $fields = [];
        foreach ($show as $val) {
            $fields[] = explode(':', $val);
        }
        //如果指定需要显示checkbox列
        if (!empty($checkbox) && 'true' == strtolower($checkbox)) {
            $parseStr .= '<input type="checkbox" id="check" onclick="CheckAll(\''.$id.'\')">	';
        }
        if (!empty($key)) {
            $parseStr .= 'No	';
        }
        //显示指定的字段
        foreach ($fields as $field) {
            $property = explode('|', $field[0]);
            $showname = explode('|', $field[1]);
            if (isset($showname[1])) {
                $parseStr .= '';
            } else {
                $parseStr .= '';
            }
            $showname[2] = isset($showname[2]) ? $showname[2] : $showname[0];
            if ($sort) {
                $parseStr .= ''.$showname[0].'<eq name="order" value="'.$property[0].'" ></eq>	';
            } else {
                $parseStr .= $showname[0].'	';
            }

        }
        //如果指定显示操作功能列
        if (!empty($action)) {
            $parseStr .= '操作	';
        }

        $parseStr .= '
';
        //支持鼠标移动单元行颜色变化 具体方法在js中定义
        $parseStr .= '<volist name="'.$datasource.'" id="'.$name.'" >
';
        //如果需要显示checkbox 则在每行开头显示checkbox
        if (!empty($checkbox)) {
            $parseStr .= '<input type="checkbox" name="key"	value="{$'.$name.'.'.$pk.'}">	';
        }
        if (!empty($key)) {
            $parseStr .= '{$i}	';
        }
        foreach ($fields as $field) {
            //显示定义的列表字段
            $parseStr .= '';
            if (!empty($field[2])) {
                // 支持列表字段链接功能 具体方法由JS函数实现
                $href = explode('|', $field[2]);
                if (count($href) > 1) {
                    //指定链接传的字段值
                    // 支持多个字段传递
                    $array = explode('^', $href[1]);
                    if (count($array) > 1) {
                        foreach ($array as $a) {
                            $temp[] = '\'{$'.$name.'.'.$a.'|addslashes}\'';
                        }
                        $parseStr .= '';
                    } else {
                        $parseStr .= '';
                    }
                } else {
                    //如果没有指定默认传编号值
                    $parseStr .= '';
                }
            }
            if (strpos($field[0], '^')) {
                $property = explode('^', $field[0]);
                foreach ($property as $p) {
                    $unit = explode('|', $p);
                    if (count($unit) > 1) {
                        $parseStr .= '{$'.$name.'.'.$unit[0].'|'.$unit[1].'} ';
                    } else {
                        $parseStr .= '{$'.$name.'.'.$p.'} ';
                    }
                }
            } else {
                $property = explode('|', $field[0]);
                if (count($property) > 1) {
                    $parseStr .= '{$'.$name.'.'.$property[0].'|'.$property[1].'}';
                } else {
                    $parseStr .= '{$'.$name.'.'.$field[0].'}';
                }
            }
            if (!empty($field[2])) {
                $parseStr .= '';
            }
            $parseStr .= '	';

        }
        //显示功能操作
        if (!empty($action)) {
            //显示指定的功能项
            if (!empty($actionlist[0])) {
                $parseStr .= '';
                foreach ($actionlist as $val) {
                    if (strpos($val, ':')) {
                        $a = explode(':', $val);
                        if (count($a) > 2) {
                            $parseStr .= ''.$a[1].' ';
                        } else {
                            $parseStr .= ''.$a[1].' ';
                        }
                    } else {
                        $array = explode('|', $val);
                        if (count($array) > 2) {
                            $parseStr .= ' '.$array[2].' ';
                        } else {
                            $parseStr .= ' {$'.$name.'.'.$val.'} ';
                        }
                    }
                }
                $parseStr .= '	';
            }
        }
        $parseStr .= '
</volist>

';

        return $parseStr."\n\n";
    }
}


<?php

    namespace KsUtils\StrFilter;

/**
 * Filter for the phone number
 */
class Phone implements \KsUtils\StrFilter
{
    /**
     * Returns only digits from source string. If string is
     * 11 chars length and first char is 7 then that char is stripped
     * @param  string $str Source string
     * @return string Filtered string
     */
    public function filter($str)
    {
        $str = preg_replace("|[\D+]|", "", $str);

        if (strlen($str) == 11 && in_array(substr($str, 0, 1), array("7", "8"))) {
            $str = substr($str, 1);
        }

        $len2 = strlen($str);

        return $len2 == 10 ? $str : "";
    }
}


<?php

namespace Reaction\Run\Commands;

use Reaction\Base\ConfigReader;
use Reaction\Helpers\ArrayHelper;
use Reaction\StaticApplicationInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Trait ConfigTrait
 * @package Reaction\Run\Commands
 */
trait ConfigTrait
{
    /**
     * @var ConfigReader
     */
    private $_reader;
    /**
     * @var string
     */
    protected $confDir = 'Config';

    /**
     * Config application's wide options
     * @param Command $command
     */
    protected function configDefaultOptions(Command $command)
    {
        $defaultConfPath = getcwd() . '/' . $this->confDir;
        $command->addArgument('working-directory', InputOption::VALUE_REQUIRED, 'Working directory', getcwd());
        $command
            ->addOption('app-type', null, InputOption::VALUE_REQUIRED, 'Application type', StaticApplicationInterface::APP_TYPE_WEB)
            ->addOption('app-host', null, InputOption::VALUE_REQUIRED, 'Host to run', '127.0.0.1')
            ->addOption('app-port', null, InputOption::VALUE_REQUIRED, 'Port to run', 4000)
            ->addOption('app-debug', null, InputOption::VALUE_REQUIRED, 'Enable debug', true)
            ->addOption('app-config', null, InputOption::VALUE_REQUIRED, 'Configs dir path', $defaultConfPath);
    }

    /**
     * Get option from input or config array
     * @param InputInterface $input
     * @param array          $config
     * @param string         $optionName
     * @param string         $configName
     * @return mixed
     */
    protected function optionOrConfigValue(InputInterface $input, $config, $optionName, $configName = null)
    {
        $optionNameRaw = $this->normalizeConfigArrayPath($optionName);
        if ($input->hasParameterOption('--' . $optionNameRaw)) {
            return $this->getOptionWithTypeCast($input, $optionName);
        }
        if (!isset($configName)) {
            $configName = $optionNameRaw;
        }

        $configValue = ArrayHelper::getValue($config, $configName, null);

        return isset($configValue) ? $configValue : $input->getOption($optionName);
    }

    /**
     * Get option with typecast
     * @param InputInterface $input
     * @param string         $optionName
     * @return mixed
     */
    protected function getOptionWithTypeCast(InputInterface $input, $optionName)
    {
        $optionNameExp = explode(':', $optionName);
        if (count($optionNameExp) > 1) {
            $optionName = $optionNameExp[0];
            $optionType = $optionNameExp[1];
        }
        $optionValue = $input->getOption($optionName);
        if (isset($optionType) && function_exists($optionType . 'val')) {
            $typeCastFunc = $optionType . 'val';
            $optionValue = $typeCastFunc($optionValue);
        }
        return $optionValue;
    }

    /**
     * Load config data from file
     * @param string $path
     * @param string $appType
     * @return array
     */
    protected function loadConfigFromFile($path, $appType = StaticApplicationInterface::APP_TYPE_WEB)
    {
        if (!isset($this->_reader)) {
            $reader = new ConfigReader([
                'path' => $path,
                'appType' => $appType,
            ]);
            $this->_reader = $reader;
        }
        return $this->_reader->data;
    }

    /**
     * Get path of config file
     * @param InputInterface $input
     * @return string
     * @throws \Exception
     */
    protected function getConfigPath(InputInterface $input)
    {
        $configOption = $input->getOption('app-config');
        if ($configOption && !file_exists($configOption)) {
            throw new \Exception(sprintf('Config file not found: "%s"', $configOption));
        }
        $possiblePaths = [
            $configOption,
            $this->confDir,
            sprintf('%s/%s', dirname($GLOBALS['argv'][0]), $this->confDir)
        ];

        foreach ($possiblePaths as $path) {
            if (file_exists($path) && is_dir($path)) {
                return realpath($path);
            }
        }
        return '';
    }

    /**
     * Load config data
     * @param InputInterface  $input
     * @param OutputInterface $output
     * @param bool            $saveConfig
     * @return array|mixed
     * @throws \Exception
     */
    protected function loadConfig(InputInterface $input, OutputInterface $output, $saveConfig = true)
    {
        $configsPath = $this->getConfigPath($input);
        $appType = $input->getOption('app-type');
        $config = $this->loadConfigFromFile($configsPath, $appType);
        /**
         * Array format
         * [ 'OPTION_NAME:TYPE' => 'CONFIG_ARRAY_PATH', ]
         */
        $configOptions = [
            'app-host' => 'appStatic.hostname',
            'app-port:int' => 'appStatic.port',
            'app-debug:bool' => 'appStatic.debug',
        ];

        foreach ($configOptions as $optionName => $configName) {
            $value = $this->optionOrConfigValue($input, $config, $optionName, $configName);
            ArrayHelper::setValue($config, $configName, $value);
        }

        if ($saveConfig) {
            $this->_reader->data = $config;
            \Reaction::$config = $this->_reader;
        }

        return $config;
    }

    /**
     * Init config.
     * Locate file, load it and check values
     * @param InputInterface  $input
     * @param OutputInterface $output
     * @param bool            $render
     * @return array
     */
    protected function initializeConfig(InputInterface $input, OutputInterface $output, $render = true)
    {
        if ($workingDir = $input->getArgument('working-directory')) {
            chdir($workingDir);
        }
        $config = $this->loadConfig($input, $output);

        if ($render) {
            $this->renderConfig($output, $config);
        }
        return $config;
    }

    /**
     * Render config as table in console
     * @param OutputInterface $output
     * @param array           $config
     */
    protected function renderConfig(OutputInterface $output, array $config)
    {
        $table = new Table($output);
        $rows = $this->renderConfigRows($config);
        $table->addRows($rows);
        $table->render();
    }

    /**
     * Normalize config rows for render in table
     * @param array $rows
     * @param int   $cardinality
     * @return array
     */
    private function renderConfigRows($rows, $cardinality = 0) {
        if ($cardinality > 0) {
            $prefix = str_repeat('  ', $cardinality - 1) . '↳ ';
        } else {
            $prefix = '';
        }
        $rowsNew = [];
        foreach ($rows as $key => $value) {
            $keyStr = !is_numeric($key) ? $prefix . $key : $prefix . $key;
            $valueArray = null;
            if (is_array($value)) {
                $isSimpleArray = false;
                if (ArrayHelper::isIndexed($value)) {
                    $isSimpleArray = true;
                    foreach ($value as $valueChild) {
                        if (!is_scalar($valueChild)) {
                            $isSimpleArray = false;
                            break;
                        }
                    }
                }
                if ($isSimpleArray) {
                    foreach ($value as &$valueChild) {
                        $valueChild = $this->renderValue($valueChild);
                    }
                    $valueStr = '[' . implode(', ', $value) . ']';
                } else {
                    $valueStr = '↓ []';
                    $valueArray = $this->renderConfigRows($value, $cardinality + 1);
                }
            } else {
                $valueStr = $this->renderValue($value);
            }
            $valueStr = '<comment>' . $valueStr . '</comment>';
            $rowsNew[] = [$keyStr, $valueStr];
            if (isset($valueArray)) {
                $rowsNew = ArrayHelper::merge($rowsNew, $valueArray);
            }
        }
        return $rowsNew;
    }

    /**
     * Get string representation of value
     * @param mixed $value
     * @return string
     */
    private function renderValue($value)
    {
        $type = gettype($value);
        $valueStr = null;
        switch ($type) {
            case 'object':
                $valueStr = 'object(' . get_class($value) . ')';
                break;
            case 'array':
                $valueStr = 'array(' . count($value) . ')';
                break;
            case 'boolean':
                $valueStr = $value ? 'TRUE' : 'FALSE';
                break;
            case 'string':
                $valueStr = '"' . $value . '"';
                break;
            default: $valueStr = $value;
        }
        return $valueStr;
    }

    /**
     * Normalize config array key path
     * @param string $configPath
     * @return string
     */
    private function normalizeConfigArrayPath($configPath)
    {
        if (!strpos($configPath, ':')) {
            return $configPath;
        }
        $configPathExp = explode(':', $configPath);
        return reset($configPathExp);
    }
}