diff --git a/composer.json b/composer.json index 3d645f895..fe874e8b6 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,8 @@ { "type": "project", "name": "zoujingli/thinkadmin", - "description": "ThinkAdmin Developer CMF", "homepage": "http://demo.thinkadmin.top", + "description": "ThinkAdmin Developer CMF", "keywords": [ "thinkphp", "thinkadmin" @@ -34,5 +34,11 @@ }, "config": { "preferred-install": "dist" + }, + "repositories": { + "packagist": { + "type": "composer", + "url": "https://packagist.laravel-china.org" + } } } diff --git a/thinkphp/helper.php b/thinkphp/helper.php index 372357f12..4f9e18c9a 100644 --- a/thinkphp/helper.php +++ b/thinkphp/helper.php @@ -304,6 +304,21 @@ if (!function_exists('debug')) { } } +if (!function_exists('download')) { + /** + * 获取\think\response\Download对象实例 + * @param string $filename 要下载的文件 + * @param string $name 显示文件名 + * @param bool $content 是否为内容 + * @param integer $expire 有效期(秒) + * @return \think\response\Download + */ + function download($filename, $name = '', $content = false, $expire = 180) + { + return Response::create($filename, 'download')->name($name)->isContent($content)->expire($expire); + } +} + if (!function_exists('dump')) { /** * 浏览器友好的变量输出 diff --git a/thinkphp/lang/zh-cn.php b/thinkphp/lang/zh-cn.php index d7c82f0c1..e2067a767 100644 --- a/thinkphp/lang/zh-cn.php +++ b/thinkphp/lang/zh-cn.php @@ -25,6 +25,7 @@ return [ 'method param miss' => '方法参数错误', 'method not exists' => '方法不存在', 'function not exists' => '函数不存在', + 'file not exists' => '文件不存在', 'module not exists' => '模块不存在', 'controller not exists' => '控制器不存在', 'class not exists' => '类不存在', diff --git a/thinkphp/library/think/App.php b/thinkphp/library/think/App.php index 235c4d856..8d4313e19 100644 --- a/thinkphp/library/think/App.php +++ b/thinkphp/library/think/App.php @@ -20,7 +20,7 @@ use think\route\Dispatch; */ class App extends Container { - const VERSION = '5.1.19'; + const VERSION = '5.1.21'; /** * 当前模块路径 @@ -557,7 +557,8 @@ class App extends Container if ($this->route->config('route_annotation')) { // 自动生成路由定义 if ($this->appDebug) { - $this->build->buildRoute($this->route->config('controller_suffix')); + $suffix = $this->route->config('controller_suffix') || $this->route->config('class_suffix'); + $this->build->buildRoute($suffix); } $filename = $this->runtimePath . 'build_route.php'; diff --git a/thinkphp/library/think/Build.php b/thinkphp/library/think/Build.php index f12c79093..c65936a85 100644 --- a/thinkphp/library/think/Build.php +++ b/thinkphp/library/think/Build.php @@ -232,13 +232,13 @@ class Build foreach ($controllers as $controller) { $controller = basename($controller, '.php'); + $class = new \ReflectionClass($namespace . '\\' . $module . '\\' . $layer . '\\' . $controller); + if ($suffix) { // 控制器后缀 $controller = substr($controller, 0, -10); } - $class = new \ReflectionClass($namespace . '\\' . $module . '\\' . $layer . '\\' . $controller); - if (strpos($layer, '\\')) { // 多级控制器 $level = str_replace(DIRECTORY_SEPARATOR, '.', substr($layer, 11)); diff --git a/thinkphp/library/think/Config.php b/thinkphp/library/think/Config.php index 1e60ac9d0..52ca8f0e9 100644 --- a/thinkphp/library/think/Config.php +++ b/thinkphp/library/think/Config.php @@ -91,7 +91,11 @@ class Config implements \ArrayAccess $name = $this->prefix . '.' . $name; } - return !is_null($this->get($name)) ? true : false; + if (class_exists('Yaconf')) { + return Yaconf::has($name); + } + + return !is_null($this->get($name)); } /** @@ -116,6 +120,14 @@ class Config implements \ArrayAccess */ public function get($name = null, $default = null) { + if (class_exists('Yaconf')) { + if ($name && !strpos($name, '.')) { + $name = $this->prefix . '.' . $name; + } + + return Yaconf::get($name, $default); + } + // 无参数时获取所有 if (empty($name)) { return $this->config; diff --git a/thinkphp/library/think/Console.php b/thinkphp/library/think/Console.php index 9757e4766..e73da7f15 100644 --- a/thinkphp/library/think/Console.php +++ b/thinkphp/library/think/Console.php @@ -48,6 +48,7 @@ class Console "think\\console\\command\\optimize\\Schema", "think\\console\\command\\optimize\\Route", "think\\console\\command\\RunServer", + "think\\console\\command\\Version", ]; /** @@ -792,4 +793,11 @@ class Console return $namespaces; } + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['commands'], $data['definition']); + + return $data; + } } diff --git a/thinkphp/library/think/Container.php b/thinkphp/library/think/Container.php index a8af9fa52..95d2b98a5 100644 --- a/thinkphp/library/think/Container.php +++ b/thinkphp/library/think/Container.php @@ -439,8 +439,9 @@ class Container implements ArrayAccess, IteratorAggregate, Countable $params = $reflect->getParameters(); foreach ($params as $param) { - $name = $param->getName(); - $class = $param->getClass(); + $name = $param->getName(); + $lowerName = Loader::parseName($name); + $class = $param->getClass(); if ($class) { $args[] = $this->getObjectParam($class->getName(), $vars); @@ -448,6 +449,8 @@ class Container implements ArrayAccess, IteratorAggregate, Countable $args[] = array_shift($vars); } elseif (0 == $type && isset($vars[$name])) { $args[] = $vars[$name]; + } elseif (0 == $type && isset($vars[$lowerName])) { + $args[] = $vars[$lowerName]; } elseif ($param->isDefaultValueAvailable()) { $args[] = $param->getDefaultValue(); } else { @@ -531,4 +534,12 @@ class Container implements ArrayAccess, IteratorAggregate, Countable { return new ArrayIterator($this->instances); } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['instances'], $data['instance']); + + return $data; + } } diff --git a/thinkphp/library/think/Controller.php b/thinkphp/library/think/Controller.php index 19fc8df6f..8edab8c67 100644 --- a/thinkphp/library/think/Controller.php +++ b/thinkphp/library/think/Controller.php @@ -251,4 +251,12 @@ class Controller return true; } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app'], $data['request']); + + return $data; + } } diff --git a/thinkphp/library/think/Db.php b/thinkphp/library/think/Db.php index f72c0f1d1..a21998c59 100644 --- a/thinkphp/library/think/Db.php +++ b/thinkphp/library/think/Db.php @@ -35,6 +35,7 @@ use think\db\Connection; * @method \think\db\Query order(mixed $field, string $order = null) static 查询ORDER * @method \think\db\Query orderRaw(string $field, array $bind = []) static 查询ORDER * @method \think\db\Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存 + * @method \think\db\Query withAttr(string $name = '',callable $callback) static 使用获取器获取数据 * @method mixed value(string $field) static 获取某个字段的值 * @method array column(string $field, string $key = '') static 获取某个列的值 * @method mixed find(mixed $data = null) static 查询单个记录 diff --git a/thinkphp/library/think/Debug.php b/thinkphp/library/think/Debug.php index 8aec3c3b4..776e17873 100644 --- a/thinkphp/library/think/Debug.php +++ b/thinkphp/library/think/Debug.php @@ -267,4 +267,12 @@ class Debug } } } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app']); + + return $data; + } } diff --git a/thinkphp/library/think/Hook.php b/thinkphp/library/think/Hook.php index 58791c815..b46684a58 100644 --- a/thinkphp/library/think/Hook.php +++ b/thinkphp/library/think/Hook.php @@ -218,4 +218,11 @@ class Hook return $result; } + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app']); + + return $data; + } } diff --git a/thinkphp/library/think/Log.php b/thinkphp/library/think/Log.php index b63233cd6..351aae741 100644 --- a/thinkphp/library/think/Log.php +++ b/thinkphp/library/think/Log.php @@ -378,4 +378,12 @@ class Log implements LoggerInterface { $this->log(__FUNCTION__, $message, $context); } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app']); + + return $data; + } } diff --git a/thinkphp/library/think/Middleware.php b/thinkphp/library/think/Middleware.php index 4814fdb99..aafe88314 100644 --- a/thinkphp/library/think/Middleware.php +++ b/thinkphp/library/think/Middleware.php @@ -110,6 +110,18 @@ class Middleware return $this->queue[$type] ?: []; } + /** + * 清除中间件 + * @access public + * @param string $type 中间件类型 + */ + public function clear($type = 'route') + { + if (isset($this->queue[$type])) { + $this->queue[$type] = []; + } + } + /** * 中间件调度 * @access public @@ -186,4 +198,11 @@ class Middleware }; } + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app']); + + return $data; + } } diff --git a/thinkphp/library/think/Model.php b/thinkphp/library/think/Model.php index 5b9ab3df7..7c3edbcbc 100644 --- a/thinkphp/library/think/Model.php +++ b/thinkphp/library/think/Model.php @@ -820,7 +820,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess try { // 删除当前模型数据 - $result = $db->where($where)->delete(); + $db->where($where)->delete(); // 关联删除 if (!empty($this->relationWrite)) { @@ -895,93 +895,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess return $model; } - /** - * 查找单条记录 - * @access public - * @param mixed $data 主键值或者查询条件(闭包) - * @param mixed $with 关联预查询 - * @param bool $cache 是否缓存 - * @param bool $failException 是否抛出异常 - * @return static|null - * @throws exception\DbException - */ - public static function get($data, $with = [], $cache = false, $failException = false) - { - if (is_null($data)) { - return; - } - - if (true === $with || is_int($with)) { - $cache = $with; - $with = []; - } - - $query = static::parseQuery($data, $with, $cache); - - return $query->failException($failException)->find($data); - } - - /** - * 查找单条记录 如果不存在直接抛出异常 - * @access public - * @param mixed $data 主键值或者查询条件(闭包) - * @param mixed $with 关联预查询 - * @param bool $cache 是否缓存 - * @return static|null - * @throws exception\DbException - */ - public static function getOrFail($data, $with = [], $cache = false) - { - return self::get($data, $with, $cache, true); - } - - /** - * 查找所有记录 - * @access public - * @param mixed $data 主键列表或者查询条件(闭包) - * @param array|string $with 关联预查询 - * @param bool $cache 是否缓存 - * @return static[]|false - * @throws exception\DbException - */ - public static function all($data = null, $with = [], $cache = false) - { - if (true === $with || is_int($with)) { - $cache = $with; - $with = []; - } - - $query = static::parseQuery($data, $with, $cache); - - return $query->select($data); - } - - /** - * 分析查询表达式 - * @access public - * @param mixed $data 主键列表或者查询条件(闭包) - * @param string $with 关联预查询 - * @param bool $cache 是否缓存 - * @return Query - */ - protected static function parseQuery(&$data, $with, $cache) - { - $result = self::with($with)->cache($cache); - - if (is_array($data) && key($data) !== 0) { - $result = $result->where($data); - $data = null; - } elseif ($data instanceof \Closure) { - $data($result); - $data = null; - } elseif ($data instanceof Query) { - $result = $data->with($with)->cache($cache); - $data = null; - } - - return $result; - } - /** * 删除记录 * @access public diff --git a/thinkphp/library/think/Request.php b/thinkphp/library/think/Request.php index 37760280b..ba468dace 100644 --- a/thinkphp/library/think/Request.php +++ b/thinkphp/library/think/Request.php @@ -521,6 +521,18 @@ class Request return $this->panDomain; } + /** + * 设置当前完整URL 包括QUERY_STRING + * @access public + * @param string $url URL + * @return $this + */ + public function setUrl($url) + { + $this->url = $url; + return $this; + } + /** * 获取当前完整URL 包括QUERY_STRING * @access public @@ -546,6 +558,18 @@ class Request return $complete ? $this->domain() . $this->url : $this->url; } + /** + * 设置当前完整URL 不包括QUERY_STRING + * @access public + * @param string $url URL + * @return $this + */ + public function setBaseUrl($url) + { + $this->baseUrl = $url; + return $this; + } + /** * 获取当前URL 不含QUERY_STRING * @access public @@ -1576,7 +1600,9 @@ class Request return $result; } - return $this->param($this->config['var_ajax']) ? true : $result; + $result = $this->param($this->config['var_ajax']) ? true : $result; + $this->mergeParam = false; + return $result; } /** @@ -1593,7 +1619,9 @@ class Request return $result; } - return $this->param($this->config['var_pjax']) ? true : $result; + $result = $this->param($this->config['var_pjax']) ? true : $result; + $this->mergeParam = false; + return $result; } /** @@ -2167,4 +2195,12 @@ class Request { return isset($this->param[$name]); } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['dispatch'], $data['config']); + + return $data; + } } diff --git a/thinkphp/library/think/Response.php b/thinkphp/library/think/Response.php index 09cb6a924..5fa5402ab 100644 --- a/thinkphp/library/think/Response.php +++ b/thinkphp/library/think/Response.php @@ -323,7 +323,7 @@ class Response /** * 页面缓存控制 * @access public - * @param string $cache 状态码 + * @param string $cache 缓存设置 * @return $this */ public function cacheControl($cache) @@ -333,6 +333,19 @@ class Response return $this; } + /** + * 设置页面不做任何缓存 + * @access public + * @return $this + */ + public function noCache() + { + $this->header['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'; + $this->header['Pragma'] = 'no-cache'; + + return $this; + } + /** * 页面输出类型 * @access public @@ -405,4 +418,12 @@ class Response { return $this->code; } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app']); + + return $data; + } } diff --git a/thinkphp/library/think/Route.php b/thinkphp/library/think/Route.php index 43b7614c5..353c0bf14 100644 --- a/thinkphp/library/think/Route.php +++ b/thinkphp/library/think/Route.php @@ -466,7 +466,9 @@ class Route // 检查路由别名 if (isset($rules['__alias__'])) { - $this->alias($rules['__alias__']); + foreach ($rules['__alias__'] as $key => $val) { + $this->alias($key, $val); + } unset($rules['__alias__']); } @@ -939,4 +941,12 @@ class Route { return call_user_func_array([$this->group, $method], $args); } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app'], $data['request']); + + return $data; + } } diff --git a/thinkphp/library/think/Session.php b/thinkphp/library/think/Session.php index f21c93b1e..63ee7a03e 100644 --- a/thinkphp/library/think/Session.php +++ b/thinkphp/library/think/Session.php @@ -197,8 +197,7 @@ class Session } if ($isDoStart) { - session_start(); - $this->init = true; + $this->start(); } else { $this->init = false; } @@ -219,7 +218,7 @@ class Session if (false === $this->init) { if (PHP_SESSION_ACTIVE != session_status()) { - session_start(); + $this->start(); } $this->init = true; } diff --git a/thinkphp/library/think/Template.php b/thinkphp/library/think/Template.php index 50dad097e..941032575 100644 --- a/thinkphp/library/think/Template.php +++ b/thinkphp/library/think/Template.php @@ -1307,4 +1307,12 @@ class Template return '/' . $regex . '/is'; } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app'], $data['storege']); + + return $data; + } } diff --git a/thinkphp/library/think/Url.php b/thinkphp/library/think/Url.php index e8159993b..6707bf289 100644 --- a/thinkphp/library/think/Url.php +++ b/thinkphp/library/think/Url.php @@ -286,7 +286,7 @@ class Url $rootDomain = $this->app['request']->rootDomain(); if (true === $domain) { // 自动判断域名 - $domain = $this->config['app_host'] ?: $this->app['request']->host(true); + $domain = $this->config['app_host'] ?: $this->app['request']->host(); $domains = $this->app['route']->getDomains(); @@ -389,4 +389,12 @@ class Url $this->root = $root; $this->app['request']->setRoot($root); } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app']); + + return $data; + } } diff --git a/thinkphp/library/think/console/command/Version.php b/thinkphp/library/think/console/command/Version.php new file mode 100644 index 000000000..ae7618c97 --- /dev/null +++ b/thinkphp/library/think/console/command/Version.php @@ -0,0 +1,32 @@ + +// +---------------------------------------------------------------------- +namespace think\console\command; + +use think\console\Command; +use think\console\Input; +use think\console\Output; +use think\facade\App; + +class Version extends Command +{ + protected function configure() + { + // 指令配置 + $this->setName('version') + ->setDescription('show thinkphp framework version'); + } + + protected function execute(Input $input, Output $output) + { + $output->writeln('v' . App::version()); + } + +} diff --git a/thinkphp/library/think/db/Query.php b/thinkphp/library/think/db/Query.php index d491453b0..bd5b02f6f 100644 --- a/thinkphp/library/think/db/Query.php +++ b/thinkphp/library/think/db/Query.php @@ -1489,6 +1489,11 @@ class Query $logic = strtoupper($logic); + if ($field instanceof Where) { + $this->options['where'][$logic] = $field->parse(); + return $this; + } + if (is_string($field) && !empty($this->options['via']) && !strpos($field, '.')) { $field = $this->options['via'] . '.' . $field; } @@ -2118,6 +2123,30 @@ class Query return $this; } + /** + * 设置数据字段获取器 + * @access public + * @param string|array $name 字段名 + * @param callable $callback 闭包获取器 + * @return $this + */ + public function withAttr($name, $callback = null) + { + if (is_array($name)) { + foreach ($name as $key => $val) { + $key = Loader::parseName($key); + + $this->options['with_attr'][$key] = $val; + } + } else { + $name = Loader::parseName($name); + + $this->options['with_attr'][$name] = $callback; + } + + return $this; + } + /** * 设置JSON字段信息 * @access public @@ -2813,14 +2842,28 @@ class Query if (!empty($this->model)) { // 生成模型对象 if (count($resultSet) > 0) { + // 检查动态获取器 + if (!empty($this->options['with_attr'])) { + foreach ($this->options['with_attr'] as $name => $val) { + if (strpos($name, '.')) { + list($relation, $field) = explode('.', $name); + + $withRelationAttr[$relation][$field] = $val; + unset($this->options['with_attr'][$name]); + } + } + } + + $withRelationAttr = isset($withRelationAttr) ? $withRelationAttr : []; + foreach ($resultSet as $key => &$result) { // 数据转换为模型对象 - $this->resultToModel($result, $this->options, true); + $this->resultToModel($result, $this->options, true, $withRelationAttr); } if (!empty($this->options['with'])) { // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with']); + $result->eagerlyResultSet($resultSet, $this->options['with'], $withRelationAttr); } // 模型数据集转换 @@ -2829,11 +2872,7 @@ class Query $resultSet = $this->model->toCollection($resultSet); } } else { - if (!empty($this->options['json'])) { - foreach ($resultSet as &$result) { - $this->jsonResult($result, $this->options['json'], true); - } - } + $this->resultSet($resultSet); if ('collection' == $this->connection->getConfig('resultset_type')) { // 返回Collection对象 @@ -2849,6 +2888,27 @@ class Query return $resultSet; } + /** + * 处理数据集 + * @access public + * @param array $resultSet + * @return void + */ + protected function resultSet(&$resultSet) + { + if (!empty($this->options['json'])) { + foreach ($resultSet as &$result) { + $this->jsonResult($result, $this->options['json'], true); + } + } + + if (!empty($this->options['with_attr'])) { + foreach ($resultSet as &$result) { + $this->getResultAttr($result, $this->options['with_attr']); + } + } + } + /** * 查找单条记录 * @access public @@ -2887,8 +2947,8 @@ class Query if (!empty($this->model)) { // 返回模型对象 $this->resultToModel($result, $this->options); - } elseif (!empty($this->options['json'])) { - $this->jsonResult($result, $this->options['json'], true); + } else { + $this->result($result); } } elseif (!empty($this->options['fail'])) { $this->throwNotFound($this->options); @@ -2898,18 +2958,151 @@ class Query } /** - * JSON字段数据转换 + * 查找单条记录 + * @access public + * @param mixed $data 主键值或者查询条件(闭包) + * @param mixed $with 关联预查询 + * @param bool $cache 是否缓存 + * @param bool $failException 是否抛出异常 + * @return static|null + * @throws exception\DbException + */ + public function get($data, $with = [], $cache = false, $failException = false) + { + if (is_null($data)) { + return; + } + + if (true === $with || is_int($with)) { + $cache = $with; + $with = []; + } + + return $this->parseQuery($data, $with, $cache) + ->failException($failException) + ->find($data); + } + + /** + * 查找单条记录 如果不存在直接抛出异常 + * @access public + * @param mixed $data 主键值或者查询条件(闭包) + * @param mixed $with 关联预查询 + * @param bool $cache 是否缓存 + * @return static|null + * @throws exception\DbException + */ + public function getOrFail($data, $with = [], $cache = false) + { + return $this->get($data, $with, $cache, true); + } + + /** + * 查找所有记录 + * @access public + * @param mixed $data 主键列表或者查询条件(闭包) + * @param array|string $with 关联预查询 + * @param bool $cache 是否缓存 + * @return static[]|false + * @throws exception\DbException + */ + public function all($data = null, $with = [], $cache = false) + { + if (true === $with || is_int($with)) { + $cache = $with; + $with = []; + } + + return $this->parseQuery($data, $with, $cache)->select($data); + } + + /** + * 分析查询表达式 + * @access public + * @param mixed $data 主键列表或者查询条件(闭包) + * @param string $with 关联预查询 + * @param bool $cache 是否缓存 + * @return Query + */ + protected function parseQuery(&$data, $with, $cache) + { + $result = $this->with($with)->cache($cache); + + if (is_array($data) && key($data) !== 0) { + $result = $result->where($data); + $data = null; + } elseif ($data instanceof \Closure) { + $data($result); + $data = null; + } elseif ($data instanceof Query) { + $result = $data->with($with)->cache($cache); + $data = null; + } + + return $result; + } + + /** + * 处理数据 * @access protected * @param array $result 查询数据 - * @param array $json JSON字段 - * @param bool $assoc 是否转换为数组 * @return void */ - protected function jsonResult(&$result, $json = [], $assoc = false) + protected function result(&$result) + { + if (!empty($this->options['json'])) { + $this->jsonResult($result, $this->options['json'], true); + } + + if (!empty($this->options['with_attr'])) { + $this->getResultAttr($result, $this->options['with_attr']); + } + } + + /** + * 使用获取器处理数据 + * @access protected + * @param array $result 查询数据 + * @param array $withAttr 字段获取器 + * @return void + */ + protected function getResultAttr(&$result, $withAttr = []) + { + foreach ($withAttr as $name => $closure) { + if (strpos($name, '.')) { + // 支持JSON字段 获取器定义 + list($key, $field) = explode('.', $name); + + if (isset($result[$key])) { + $result[$key][$field] = $closure(isset($result[$key][$field]) ? $result[$key][$field] : null, $result[$key]); + } + } else { + $result[$name] = $closure(isset($result[$name]) ? $result[$name] : null, $result); + } + } + } + + /** + * JSON字段数据转换 + * @access protected + * @param array $result 查询数据 + * @param array $json JSON字段 + * @param bool $assoc 是否转换为数组 + * @param array $withRelationAttr 关联获取器 + * @return void + */ + protected function jsonResult(&$result, $json = [], $assoc = false, $withRelationAttr = []) { foreach ($json as $name) { if (isset($result[$name])) { $result[$name] = json_decode($result[$name], $assoc); + + if (isset($withRelationAttr[$name])) { + foreach ($withRelationAttr[$name] as $key => $closure) { + $data = get_object_vars($result[$name]); + $result[$name]->$key = $closure(isset($result[$name]->$key) ? $result[$name]->$key : null, $data); + } + } } } } @@ -2917,28 +3110,46 @@ class Query /** * 查询数据转换为模型对象 * @access protected - * @param array $result 查询数据 - * @param array $options 查询参数 - * @param bool $resultSet 是否为数据集查询 + * @param array $result 查询数据 + * @param array $options 查询参数 + * @param bool $resultSet 是否为数据集查询 + * @param array $withRelationAttr 关联字段获取器 * @return void */ - protected function resultToModel(&$result, $options = [], $resultSet = false) + protected function resultToModel(&$result, $options = [], $resultSet = false, $withRelationAttr = []) { - if (!empty($options['json'])) { - $this->jsonResult($result, $options['json'], $options['json_assoc']); + // 动态获取器 + if (!empty($options['with_attr']) && empty($withRelationAttr)) { + foreach ($options['with_attr'] as $name => $val) { + if (strpos($name, '.')) { + list($relation, $field) = explode('.', $name); + + $withRelationAttr[$relation][$field] = $val; + unset($options['with_attr'][$name]); + } + } } - $condition = (!$resultSet && isset($options['where']['AND'])) ? $options['where']['AND'] : null; - $result = $this->model->newInstance($result, $condition); + // JSON 数据处理 + if (!empty($options['json'])) { + $this->jsonResult($result, $options['json'], $options['json_assoc'], $withRelationAttr); + } + + $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options)); + + // 动态获取器 + if (!empty($options['with_attr'])) { + $result->setModelAttrs($options['with_attr']); + } // 关联查询 if (!empty($options['relation'])) { - $result->relationQuery($options['relation']); + $result->relationQuery($options['relation'], $withRelationAttr); } // 预载入查询 if (!$resultSet && !empty($options['with'])) { - $result->eagerlyResult($result, $options['with']); + $result->eagerlyResult($result, $options['with'], $withRelationAttr); } // 关联统计 @@ -2949,6 +3160,16 @@ class Query } } + /** + * 获取模型的更新条件 + * @access protected + * @param array $options 查询参数 + */ + protected function getModelUpdateCondition(array $options) + { + return isset($options['where']['AND']) ? $options['where']['AND'] : null; + } + /** * 查询失败 抛出异常 * @access protected diff --git a/thinkphp/library/think/db/Where.php b/thinkphp/library/think/db/Where.php new file mode 100644 index 000000000..8db7c31eb --- /dev/null +++ b/thinkphp/library/think/db/Where.php @@ -0,0 +1,179 @@ + +// +---------------------------------------------------------------------- + +namespace think\db; + +use ArrayAccess; + +class Where implements ArrayAccess +{ + /** + * 查询表达式 + * @var array + */ + protected $where = []; + + /** + * 是否需要增加括号 + * @var bool + */ + protected $enclose = false; + + /** + * 创建一个查询表达式 + * + * @param array $where 查询条件数组 + * @param bool $enclose 是否增加括号 + */ + public function __construct(array $where = [], $enclose = false) + { + $this->where = $where; + $this->enclose = $enclose; + } + + /** + * 设置是否添加括号 + * @access public + * @param bool $enclose + * @return $this + */ + public function enclose($enclose = true) + { + $this->enclose = $enclose; + return $this; + } + + /** + * 解析为Query对象可识别的查询条件数组 + * @access public + * @return array + */ + public function parse() + { + $where = []; + + foreach ($this->where as $key => $val) { + if ($val instanceof Expression) { + $where[] = [$key, 'exp', $val]; + } elseif (is_null($val)) { + $where[] = [$key, 'NULL', '']; + } elseif (is_array($val)) { + $where[] = $this->parseItem($key, $val); + } else { + $where[] = [$key, '=', $val]; + } + } + + return $this->enclose ? [$where] : $where; + } + + /** + * 分析查询表达式 + * @access protected + * @param string $field 查询字段 + * @param string $op 查询表达式 + * @param mixed $condition 查询条件 + * @return array + */ + protected function parseItem($field, $where = []) + { + $op = $where[0]; + $condition = isset($where[1]) ? $where[1] : null; + + if (is_array($op)) { + // 同一字段多条件查询 + array_unshift($where, $field); + } elseif (is_null($condition)) { + if (in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) { + // null查询 + $where = [$field, $op, '']; + } elseif (in_array($op, ['=', 'eq', 'EQ', null], true)) { + $where = [$field, 'NULL', '']; + } elseif (in_array($op, ['<>', 'neq', 'NEQ'], true)) { + $where = [$field, 'NOTNULL', '']; + } else { + // 字段相等查询 + $where = [$field, '=', $op]; + } + } else { + $where = [$field, $op, $condition]; + } + + return $where; + } + + /** + * 修改器 设置数据对象的值 + * @access public + * @param string $name 名称 + * @param mixed $value 值 + * @return void + */ + public function __set($name, $value) + { + $this->where[$name] = $value; + } + + /** + * 获取器 获取数据对象的值 + * @access public + * @param string $name 名称 + * @return mixed + */ + public function __get($name) + { + return isset($this->where[$name]) ? $this->where[$name] : null; + } + + /** + * 检测数据对象的值 + * @access public + * @param string $name 名称 + * @return boolean + */ + public function __isset($name) + { + return isset($this->where[$name]); + } + + /** + * 销毁数据对象的值 + * @access public + * @param string $name 名称 + * @return void + */ + public function __unset($name) + { + unset($this->where[$name]); + } + + // ArrayAccess + public function offsetSet($name, $value) + { + $this->__set($name, $value); + } + + public function offsetExists($name) + { + return $this->__isset($name); + } + + public function offsetUnset($name) + { + $this->__unset($name); + } + + public function offsetGet($name) + { + return $this->__get($name); + } + +} diff --git a/thinkphp/library/think/debug/Console.php b/thinkphp/library/think/debug/Console.php index dd063c1c3..5cbaa0f2f 100644 --- a/thinkphp/library/think/debug/Console.php +++ b/thinkphp/library/think/debug/Console.php @@ -54,8 +54,8 @@ class Console $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; $mem = number_format((memory_get_usage() - Container::get('app')->getBeginMem()) / 1024, 2); - if (isset($_SERVER['HTTP_HOST'])) { - $uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + if ($request->host()) { + $uri = $request->protocol() . ' ' . $request->method() . ' : ' . $request->url(true); } else { $uri = 'cmd:' . implode(' ', $_SERVER['argv']); } diff --git a/thinkphp/library/think/debug/Html.php b/thinkphp/library/think/debug/Html.php index 85f354af0..a123762ee 100644 --- a/thinkphp/library/think/debug/Html.php +++ b/thinkphp/library/think/debug/Html.php @@ -54,8 +54,8 @@ class Html $mem = number_format((memory_get_usage() - Container::get('app')->getBeginMem()) / 1024, 2); // 页面Trace信息 - if (isset($_SERVER['HTTP_HOST'])) { - $uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + if ($request->host()) { + $uri = $request->protocol() . ' ' . $request->method() . ' : ' . $request->url(true); } else { $uri = 'cmd:' . implode(' ', $_SERVER['argv']); } diff --git a/thinkphp/library/think/model/Relation.php b/thinkphp/library/think/model/Relation.php index b969bca2a..b1434856a 100644 --- a/thinkphp/library/think/model/Relation.php +++ b/thinkphp/library/think/model/Relation.php @@ -119,10 +119,12 @@ abstract class Relation protected function getQueryWhere(&$where, $relation) { - foreach ($where as $key => $val) { + foreach ($where as $key => &$val) { if (is_string($key)) { $where[] = [false === strpos($key, '.') ? $relation . '.' . $key : $key, '=', $val]; unset($where[$key]); + } elseif (isset($val[0]) && false === strpos($val[0], '.')) { + $val[0] = $relation . '.' . $val[0]; } } } diff --git a/thinkphp/library/think/model/concern/Attribute.php b/thinkphp/library/think/model/concern/Attribute.php index c65c557bb..615758847 100644 --- a/thinkphp/library/think/model/concern/Attribute.php +++ b/thinkphp/library/think/model/concern/Attribute.php @@ -78,6 +78,12 @@ trait Attribute */ private $origin = []; + /** + * 动态获取器 + * @var array + */ + private $withAttr = []; + /** * 获取模型对象的主键 * @access public @@ -442,9 +448,18 @@ trait Attribute } // 检测属性获取器 - $method = 'get' . Loader::parseName($name, 1) . 'Attr'; + $fieldName = Loader::parseName($name); + $method = 'get' . Loader::parseName($name, 1) . 'Attr'; - if (method_exists($this, $method)) { + if (isset($this->withAttr[$fieldName])) { + if ($notFound && $relation = $this->isRelationAttr($name)) { + $modelRelation = $this->$relation(); + $value = $this->getRelationData($modelRelation); + } + + $closure = $this->withAttr[$fieldName]; + $value = $closure($value, $this->data); + } elseif (method_exists($this, $method)) { if ($notFound && $relation = $this->isRelationAttr($name)) { $modelRelation = $this->$relation(); $value = $this->getRelationData($modelRelation); @@ -583,4 +598,16 @@ trait Attribute return $value; } + /** + * 动态设置获取器 + * @access protected + * @param array $attrs 值 + * @return $this + */ + public function setModelAttrs(array $attrs = []) + { + $this->withAttr = $attrs; + + return $this; + } } diff --git a/thinkphp/library/think/model/concern/RelationShip.php b/thinkphp/library/think/model/concern/RelationShip.php index 2c993bd20..4379108b7 100644 --- a/thinkphp/library/think/model/concern/RelationShip.php +++ b/thinkphp/library/think/model/concern/RelationShip.php @@ -171,10 +171,11 @@ trait RelationShip /** * 查询当前模型的关联数据 * @access public - * @param string|array $relations 关联名 + * @param string|array $relations 关联名 + * @param array $withRelationAttr 关联获取器 * @return $this */ - public function relationQuery($relations) + public function relationQuery($relations, $withRelationAttr = []) { if (is_string($relations)) { $relations = explode(',', $relations); @@ -197,9 +198,16 @@ trait RelationShip list($relation, $subRelation) = explode('.', $relation, 2); } - $method = Loader::parseName($relation, 1, false); + $method = Loader::parseName($relation, 1, false); + $relationName = Loader::parseName($relation); - $this->relation[$relation] = $this->$method()->getRelation($subRelation, $closure); + $relationResult = $this->$method(); + + if (isset($withRelationAttr[$relationName])) { + $relationResult->withAttr($withRelationAttr[$relationName]); + } + + $this->relation[$relation] = $relationResult->getRelation($subRelation, $closure); } return $this; @@ -208,11 +216,12 @@ trait RelationShip /** * 预载入关联查询 返回数据集 * @access public - * @param array $resultSet 数据集 - * @param string $relation 关联名 + * @param array $resultSet 数据集 + * @param string $relation 关联名 + * @param array $withRelationAttr 关联获取器 * @return array */ - public function eagerlyResultSet(&$resultSet, $relation) + public function eagerlyResultSet(&$resultSet, $relation, $withRelationAttr = []) { $relations = is_string($relation) ? explode(',', $relation) : $relation; @@ -232,20 +241,28 @@ trait RelationShip list($relation, $subRelation) = explode('.', $relation, 2); } - $relation = Loader::parseName($relation, 1, false); + $relation = Loader::parseName($relation, 1, false); + $relationName = Loader::parseName($relation); - $this->$relation()->eagerlyResultSet($resultSet, $relation, $subRelation, $closure); + $relationResult = $this->$relation(); + + if (isset($withRelationAttr[$relationName])) { + $relationResult->withAttr($withRelationAttr[$relationName]); + } + + $relationResult->eagerlyResultSet($resultSet, $relation, $subRelation, $closure); } } /** * 预载入关联查询 返回模型对象 * @access public - * @param Model $result 数据对象 - * @param string $relation 关联名 + * @param Model $result 数据对象 + * @param string $relation 关联名 + * @param array $withRelationAttr 关联获取器 * @return Model */ - public function eagerlyResult(&$result, $relation) + public function eagerlyResult(&$result, $relation, $withRelationAttr = []) { $relations = is_string($relation) ? explode(',', $relation) : $relation; @@ -265,9 +282,16 @@ trait RelationShip list($relation, $subRelation) = explode('.', $relation, 2); } - $relation = Loader::parseName($relation, 1, false); + $relation = Loader::parseName($relation, 1, false); + $relationName = Loader::parseName($relation); - $this->$relation()->eagerlyResult($result, $relation, $subRelation, $closure); + $relationResult = $this->$relation(); + + if (isset($withRelationAttr[$relationName])) { + $relationResult->withAttr($withRelationAttr[$relationName]); + } + + $relationResult->eagerlyResult($result, $relation, $subRelation, $closure); } } @@ -533,7 +557,7 @@ trait RelationShip { $relation = Loader::parseName($attr, 1, false); - if (method_exists($this, $relation)) { + if (method_exists($this, $relation) && !method_exists('think\Model', $relation)) { return $relation; } diff --git a/thinkphp/library/think/model/relation/BelongsToMany.php b/thinkphp/library/think/model/relation/BelongsToMany.php index 02e666695..70d124437 100644 --- a/thinkphp/library/think/model/relation/BelongsToMany.php +++ b/thinkphp/library/think/model/relation/BelongsToMany.php @@ -298,7 +298,7 @@ class BelongsToMany extends Relation // 查询关联数据 $data = $this->eagerlyManyToMany([ ['pivot.' . $localKey, 'in', $range], - ], $relation, $subRelation); + ], $relation, $subRelation, $closure); // 关联属性名 $attr = Loader::parseName($relation); @@ -332,7 +332,7 @@ class BelongsToMany extends Relation // 查询管理数据 $data = $this->eagerlyManyToMany([ ['pivot.' . $this->localKey, '=', $pk], - ], $relation, $subRelation); + ], $relation, $subRelation, $closure); // 关联数据封装 if (!isset($data[$pk])) { @@ -387,15 +387,16 @@ class BelongsToMany extends Relation /** * 多对多 关联模型预查询 * @access protected - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param string $subRelation 子关联 + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param \Closure $closure 闭包 * @return array */ - protected function eagerlyManyToMany($where, $relation, $subRelation = '') + protected function eagerlyManyToMany($where, $relation, $subRelation = '', $closure = null) { // 预载入关联查询 支持嵌套预载入 - $list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where) + $list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where, $closure) ->with($subRelation) ->select(); @@ -424,13 +425,18 @@ class BelongsToMany extends Relation /** * BELONGS TO MANY 关联查询 * @access protected - * @param string $foreignKey 关联模型关联键 - * @param string $localKey 当前模型关联键 - * @param array $condition 关联查询条件 + * @param string $foreignKey 关联模型关联键 + * @param string $localKey 当前模型关联键 + * @param array $condition 关联查询条件 + * @param \Closure $closure 闭包 * @return Query */ - protected function belongsToManyQuery($foreignKey, $localKey, $condition = []) + protected function belongsToManyQuery($foreignKey, $localKey, $condition = [], $closure = null) { + if ($closure) { + $closure($this->query); + } + // 关联查询封装 $tableName = $this->query->getTable(); $table = $this->pivot->getTable(); diff --git a/thinkphp/library/think/model/relation/MorphOne.php b/thinkphp/library/think/model/relation/MorphOne.php index ede680c63..738040db4 100644 --- a/thinkphp/library/think/model/relation/MorphOne.php +++ b/thinkphp/library/think/model/relation/MorphOne.php @@ -190,7 +190,7 @@ class MorphOne extends Relation $closure($this->query); } - $list = $this->query->where($where)->with($subRelation)->find(); + $list = $this->query->where($where)->with($subRelation)->select(); $morphKey = $this->morphKey; // 组装模型数据 diff --git a/thinkphp/library/think/response/Download.php b/thinkphp/library/think/response/Download.php new file mode 100644 index 000000000..181129264 --- /dev/null +++ b/thinkphp/library/think/response/Download.php @@ -0,0 +1,137 @@ + +// +---------------------------------------------------------------------- + +namespace think\response; + +use think\Exception; +use think\Response; + +class Download extends Response +{ + protected $expire = 360; + protected $name; + protected $mimeType; + protected $isContent = false; + + /** + * 处理数据 + * @access protected + * @param mixed $data 要处理的数据 + * @return mixed + * @throws \Exception + */ + protected function output($data) + { + if (!$this->isContent && !is_file($data)) { + throw new Exception('file not exists:' . $data); + } + + ob_end_clean(); + + if (!empty($this->name)) { + $name = $this->name; + } else { + $name = !$this->isContent ? pathinfo($data, PATHINFO_BASENAME) : ''; + } + + if ($this->isContent) { + $mimeType = $this->mimeType; + $size = strlen($data); + } else { + $mimeType = $this->getMimeType($data); + $size = filesize($data); + } + + $this->header['Pragma'] = 'public'; + $this->header['Content-Type'] = $mimeType ?: 'application/octet-stream'; + $this->header['Cache-control'] = 'max-age=' . $this->expire; + $this->header['Content-Disposition'] = 'attachment; filename="' . $name . '"'; + $this->header['Content-Length'] = $size; + $this->header['Content-Transfer-Encoding'] = 'binary'; + $this->header['Expires'] = gmdate("D, d M Y H:i:s", time() + $this->expire) . ' GMT'; + + $this->lastModified(gmdate('D, d M Y H:i:s', time()) . ' GMT'); + + $data = $this->isContent ? $data : file_get_contents($data); + return $data; + } + + /** + * 设置是否为内容 必须配合mimeType方法使用 + * @access public + * @param bool $content + * @return $this + */ + public function isContent($content = true) + { + $this->isContent = $content; + return $this; + } + + /** + * 设置有效期 + * @access public + * @param integer $expire 有效期 + * @return $this + */ + public function expire($expire) + { + $this->expire = $expire; + return $this; + } + + /** + * 设置文件类型 + * @access public + * @param string $filename 文件名 + * @return $this + */ + public function mimeType($mimeType) + { + $this->mimeType = $mimeType; + return $this; + } + + /** + * 获取文件类型信息 + * @access public + * @param string $filename 文件名 + * @return string + */ + protected function getMimeType($filename) + { + if (!empty($this->mimeType)) { + return $this->mimeType; + } + + $finfo = finfo_open(FILEINFO_MIME_TYPE); + + return finfo_file($finfo, $filename); + } + + /** + * 设置下载文件的显示名称 + * @access public + * @param string $filename 文件名 + * @param bool $extension 后缀自动识别 + * @return $this + */ + public function name($filename, $extension = true) + { + $this->name = $filename; + + if ($extension && !strpos($filename, '.')) { + $this->name .= '.' . pathinfo($this->data, PATHINFO_EXTENSION); + } + + return $this; + } +} diff --git a/thinkphp/library/think/route/Dispatch.php b/thinkphp/library/think/route/Dispatch.php index 4b187d1e5..05d6b018a 100644 --- a/thinkphp/library/think/route/Dispatch.php +++ b/thinkphp/library/think/route/Dispatch.php @@ -352,4 +352,12 @@ abstract class Dispatch $this->app = Container::get('app'); $this->request = $this->app['request']; } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app'], $data['request'], $data['rule']); + + return $data; + } } diff --git a/thinkphp/library/think/route/Rule.php b/thinkphp/library/think/route/Rule.php index 5cdf42c81..9c26fb027 100644 --- a/thinkphp/library/think/route/Rule.php +++ b/thinkphp/library/think/route/Rule.php @@ -81,7 +81,7 @@ abstract class Rule * 需要和分组合并的路由参数 * @var array */ - protected $mergeOptions = ['after', 'before', 'model', 'header', 'response', 'append', 'middleware']; + protected $mergeOptions = ['after', 'model', 'header', 'response', 'append', 'middleware']; /** * 是否需要后置操作 @@ -89,6 +89,12 @@ abstract class Rule */ protected $doAfter; + /** + * 是否锁定参数 + * @var bool + */ + protected $lockOption = false; + abstract public function check($request, $url, $completeMatch = false); /** @@ -680,15 +686,18 @@ abstract class Rule */ protected function mergeGroupOptions() { - $parentOption = $this->parent->getOption(); - // 合并分组参数 - foreach ($this->mergeOptions as $item) { - if (isset($parentOption[$item]) && isset($this->option[$item])) { - $this->option[$item] = array_merge($parentOption[$item], $this->option[$item]); + if (!$this->lockOption) { + $parentOption = $this->parent->getOption(); + // 合并分组参数 + foreach ($this->mergeOptions as $item) { + if (isset($parentOption[$item]) && isset($this->option[$item])) { + $this->option[$item] = array_merge($parentOption[$item], $this->option[$item]); + } } - } - $this->option = array_merge($parentOption, $this->option); + $this->option = array_merge($parentOption, $this->option); + $this->lockOption = true; + } return $this->option; } @@ -727,7 +736,6 @@ abstract class Rule $url = array_slice(explode('|', $url), $count + 1); $this->parseUrlParams($request, implode('|', $url), $matches); - $this->route = $route; $this->vars = $matches; $this->option = $option; $this->doAfter = true; @@ -1098,4 +1106,12 @@ abstract class Rule { $this->router = Container::get('route'); } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['parent'], $data['router'], $data['route']); + + return $data; + } } diff --git a/thinkphp/library/think/route/RuleGroup.php b/thinkphp/library/think/route/RuleGroup.php index 576fe91b6..86eb686d1 100644 --- a/thinkphp/library/think/route/RuleGroup.php +++ b/thinkphp/library/think/route/RuleGroup.php @@ -124,6 +124,14 @@ class RuleGroup extends Rule return false; } + // 检查前置行为 + if (isset($this->option['before'])) { + if (false === $this->checkBefore($this->option['before'])) { + return false; + } + unset($this->option['before']); + } + // 解析分组路由 if ($this instanceof Resource) { $this->buildResourceRule($this->resource, $this->option); diff --git a/thinkphp/library/think/route/RuleItem.php b/thinkphp/library/think/route/RuleItem.php index 91c97ad43..d210d9222 100644 --- a/thinkphp/library/think/route/RuleItem.php +++ b/thinkphp/library/think/route/RuleItem.php @@ -156,11 +156,6 @@ class RuleItem extends Rule // 合并分组参数 $option = $this->mergeGroupOptions(); - // 检查前置行为 - if (isset($option['before']) && false === $this->checkBefore($option['before'])) { - return false; - } - $url = $this->urlSuffixCheck($request, $url, $option); if (is_null($match)) { @@ -168,6 +163,11 @@ class RuleItem extends Rule } if (false !== $match) { + // 检查前置行为 + if (isset($option['before']) && false === $this->checkBefore($option['before'])) { + return false; + } + return $this->parseRule($request, $this->rule, $this->route, $url, $option, $match); } diff --git a/thinkphp/library/think/view/driver/Php.php b/thinkphp/library/think/view/driver/Php.php index b6f6a3d07..7948dc053 100644 --- a/thinkphp/library/think/view/driver/Php.php +++ b/thinkphp/library/think/view/driver/Php.php @@ -176,4 +176,8 @@ class Php } } + public function __debugInfo() + { + return ['config' => $this->config]; + } } diff --git a/thinkphp/library/think/view/driver/Think.php b/thinkphp/library/think/view/driver/Think.php index 9ea068266..877aee85b 100644 --- a/thinkphp/library/think/view/driver/Think.php +++ b/thinkphp/library/think/view/driver/Think.php @@ -184,4 +184,9 @@ class Think { return call_user_func_array([$this->template, $method], $params); } + + public function __debugInfo() + { + return ['config' => $this->config]; + } } diff --git a/vendor/autoload.php b/vendor/autoload.php index 09be49190..ed8579556 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInit5a29168a045363ae5963661dddea68b7::getLoader(); +return ComposerAutoloaderInit2151fec9fdcca855dbe23b45bae24bbf::getLoader(); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index ca92c3687..bcd32ed30 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit5a29168a045363ae5963661dddea68b7 +class ComposerAutoloaderInit2151fec9fdcca855dbe23b45bae24bbf { private static $loader; @@ -19,15 +19,15 @@ class ComposerAutoloaderInit5a29168a045363ae5963661dddea68b7 return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit5a29168a045363ae5963661dddea68b7', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit2151fec9fdcca855dbe23b45bae24bbf', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit5a29168a045363ae5963661dddea68b7', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit2151fec9fdcca855dbe23b45bae24bbf', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { require_once __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit5a29168a045363ae5963661dddea68b7::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit2151fec9fdcca855dbe23b45bae24bbf::getInitializer($loader)); } else { $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { @@ -48,19 +48,19 @@ class ComposerAutoloaderInit5a29168a045363ae5963661dddea68b7 $loader->register(true); if ($useStaticLoader) { - $includeFiles = Composer\Autoload\ComposerStaticInit5a29168a045363ae5963661dddea68b7::$files; + $includeFiles = Composer\Autoload\ComposerStaticInit2151fec9fdcca855dbe23b45bae24bbf::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { - composerRequire5a29168a045363ae5963661dddea68b7($fileIdentifier, $file); + composerRequire2151fec9fdcca855dbe23b45bae24bbf($fileIdentifier, $file); } return $loader; } } -function composerRequire5a29168a045363ae5963661dddea68b7($fileIdentifier, $file) +function composerRequire2151fec9fdcca855dbe23b45bae24bbf($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { require $file; diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 9421ed94c..19b257e01 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInit5a29168a045363ae5963661dddea68b7 +class ComposerStaticInit2151fec9fdcca855dbe23b45bae24bbf { public static $files = array ( '1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php', @@ -303,9 +303,9 @@ class ComposerStaticInit5a29168a045363ae5963661dddea68b7 public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit5a29168a045363ae5963661dddea68b7::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit5a29168a045363ae5963661dddea68b7::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInit5a29168a045363ae5963661dddea68b7::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInit2151fec9fdcca855dbe23b45bae24bbf::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit2151fec9fdcca855dbe23b45bae24bbf::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit2151fec9fdcca855dbe23b45bae24bbf::$classMap; }, null, ClassLoader::class); } diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index cc09294b9..454d0a026 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -12,7 +12,13 @@ "type": "zip", "url": "https://api.github.com/repos/top-think/think-installer/zipball/f5400a12c60e513911aef41fe443fa6920952675", "reference": "f5400a12c60e513911aef41fe443fa6920952675", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://dl.laravel-china.org/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "composer-plugin-api": "^1.0" @@ -55,7 +61,13 @@ "type": "zip", "url": "https://api.github.com/repos/zoujingli/ip2region/zipball/0a55d6c1ab6b4cbaa204824557aa950aaaefda0e", "reference": "0a55d6c1ab6b4cbaa204824557aa950aaaefda0e", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://dl.laravel-china.org/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=5.3" @@ -98,7 +110,13 @@ "type": "zip", "url": "https://api.github.com/repos/zoujingli/WeChatDeveloper/zipball/39682a047e5c8f9c66107924dfed4e2080e97eb4", "reference": "39682a047e5c8f9c66107924dfed4e2080e97eb4", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://dl.laravel-china.org/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-curl": "*", @@ -152,7 +170,13 @@ "type": "zip", "url": "https://api.github.com/repos/zoujingli/WeOpenDeveloper/zipball/32aa04ad346790cd6ac7672c888e22e15cb007fe", "reference": "32aa04ad346790cd6ac7672c888e22e15cb007fe", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://dl.laravel-china.org/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-curl": "*", @@ -191,18 +215,24 @@ }, { "name": "topthink/framework", - "version": "v5.1.19", - "version_normalized": "5.1.19.0", + "version": "v5.1.21", + "version_normalized": "5.1.21.0", "source": { "type": "git", "url": "https://github.com/top-think/framework.git", - "reference": "3a0fea90ed2a99b181ce503090e08be1171ed091" + "reference": "e670467e24399c98581db1b1d39191848f3e6b4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/top-think/framework/zipball/3a0fea90ed2a99b181ce503090e08be1171ed091", - "reference": "3a0fea90ed2a99b181ce503090e08be1171ed091", - "shasum": "" + "url": "https://api.github.com/repos/top-think/framework/zipball/e670467e24399c98581db1b1d39191848f3e6b4d", + "reference": "e670467e24399c98581db1b1d39191848f3e6b4d", + "shasum": "", + "mirrors": [ + { + "url": "https://dl.laravel-china.org/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=5.6.0", @@ -217,7 +247,7 @@ "sebastian/phpcpd": "2.*", "squizlabs/php_codesniffer": "2.*" }, - "time": "2018-07-13T14:10:28+00:00", + "time": "2018-08-02T09:17:04+00:00", "type": "think-framework", "installation-source": "dist", "notification-url": "https://packagist.org/downloads/", @@ -255,7 +285,13 @@ "type": "zip", "url": "https://api.github.com/repos/top-think/think-captcha/zipball/54c8a51552f99ff9ea89ea9c272383a8f738ceee", "reference": "54c8a51552f99ff9ea89ea9c272383a8f738ceee", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://dl.laravel-china.org/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "topthink/framework": "5.1.*" @@ -296,7 +332,13 @@ "type": "zip", "url": "https://api.github.com/repos/qiniu/php-sdk/zipball/305ce1c1c0c71f794661fe45a96facf61ef96c5d", "reference": "305ce1c1c0c71f794661fe45a96facf61ef96c5d", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://dl.laravel-china.org/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=5.3.3" @@ -338,23 +380,29 @@ }, { "name": "symfony/options-resolver", - "version": "v3.4.12", - "version_normalized": "3.4.12.0", + "version": "v3.4.14", + "version_normalized": "3.4.14.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "cc5e98ed91688a22a7162a8800096356f9076b1d" + "reference": "6debc476953a45969ab39afe8dee0b825f356dc7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/cc5e98ed91688a22a7162a8800096356f9076b1d", - "reference": "cc5e98ed91688a22a7162a8800096356f9076b1d", - "shasum": "" + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/6debc476953a45969ab39afe8dee0b825f356dc7", + "reference": "6debc476953a45969ab39afe8dee0b825f356dc7", + "shasum": "", + "mirrors": [ + { + "url": "https://dl.laravel-china.org/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": "^5.5.9|>=7.0.8" }, - "time": "2018-05-30T04:26:49+00:00", + "time": "2018-07-26T08:45:46+00:00", "type": "library", "extra": { "branch-alias": { @@ -405,7 +453,13 @@ "type": "zip", "url": "https://api.github.com/repos/endroid/qr-code/zipball/c9644bec2a9cc9318e98d1437de3c628dcd1ef93", "reference": "c9644bec2a9cc9318e98d1437de3c628dcd1ef93", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://dl.laravel-china.org/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-gd": "*", @@ -467,7 +521,13 @@ "type": "zip", "url": "https://api.github.com/repos/aliyun/aliyun-oss-php-sdk/zipball/e69f57916678458642ac9d2fd341ae78a56996c8", "reference": "e69f57916678458642ac9d2fd341ae78a56996c8", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://dl.laravel-china.org/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=5.3" diff --git a/vendor/symfony/options-resolver/Debug/OptionsResolverIntrospector.php b/vendor/symfony/options-resolver/Debug/OptionsResolverIntrospector.php index 60317243e..146c18bf2 100644 --- a/vendor/symfony/options-resolver/Debug/OptionsResolverIntrospector.php +++ b/vendor/symfony/options-resolver/Debug/OptionsResolverIntrospector.php @@ -49,7 +49,7 @@ class OptionsResolverIntrospector */ public function getDefault($option) { - return call_user_func($this->get, 'defaults', $option, sprintf('No default value was set for the "%s" option.', $option)); + return \call_user_func($this->get, 'defaults', $option, sprintf('No default value was set for the "%s" option.', $option)); } /** @@ -61,7 +61,7 @@ class OptionsResolverIntrospector */ public function getLazyClosures($option) { - return call_user_func($this->get, 'lazy', $option, sprintf('No lazy closures were set for the "%s" option.', $option)); + return \call_user_func($this->get, 'lazy', $option, sprintf('No lazy closures were set for the "%s" option.', $option)); } /** @@ -73,7 +73,7 @@ class OptionsResolverIntrospector */ public function getAllowedTypes($option) { - return call_user_func($this->get, 'allowedTypes', $option, sprintf('No allowed types were set for the "%s" option.', $option)); + return \call_user_func($this->get, 'allowedTypes', $option, sprintf('No allowed types were set for the "%s" option.', $option)); } /** @@ -85,7 +85,7 @@ class OptionsResolverIntrospector */ public function getAllowedValues($option) { - return call_user_func($this->get, 'allowedValues', $option, sprintf('No allowed values were set for the "%s" option.', $option)); + return \call_user_func($this->get, 'allowedValues', $option, sprintf('No allowed values were set for the "%s" option.', $option)); } /** @@ -97,6 +97,6 @@ class OptionsResolverIntrospector */ public function getNormalizer($option) { - return call_user_func($this->get, 'normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option)); + return \call_user_func($this->get, 'normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option)); } } diff --git a/vendor/symfony/options-resolver/OptionsResolver.php b/vendor/symfony/options-resolver/OptionsResolver.php index 95a492de9..79a6c0897 100644 --- a/vendor/symfony/options-resolver/OptionsResolver.php +++ b/vendor/symfony/options-resolver/OptionsResolver.php @@ -433,7 +433,7 @@ class OptionsResolver implements Options )); } - $this->allowedValues[$option] = is_array($allowedValues) ? $allowedValues : array($allowedValues); + $this->allowedValues[$option] = \is_array($allowedValues) ? $allowedValues : array($allowedValues); // Make sure the option is processed unset($this->resolved[$option]); @@ -478,7 +478,7 @@ class OptionsResolver implements Options )); } - if (!is_array($allowedValues)) { + if (!\is_array($allowedValues)) { $allowedValues = array($allowedValues); } @@ -660,12 +660,12 @@ class OptionsResolver implements Options // Make sure that no unknown options are passed $diff = array_diff_key($options, $clone->defined); - if (count($diff) > 0) { + if (\count($diff) > 0) { ksort($clone->defined); ksort($diff); throw new UndefinedOptionsException(sprintf( - (count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Defined options are: "%s".', + (\count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Defined options are: "%s".', implode('", "', array_keys($diff)), implode('", "', array_keys($clone->defined)) )); @@ -680,11 +680,11 @@ class OptionsResolver implements Options // Check whether any required option is missing $diff = array_diff_key($clone->required, $clone->defaults); - if (count($diff) > 0) { + if (\count($diff) > 0) { ksort($diff); throw new MissingOptionsException(sprintf( - count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.', + \count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.', implode('", "', array_keys($diff)) )); } @@ -785,14 +785,13 @@ class OptionsResolver implements Options } if (!$valid) { - throw new InvalidOptionsException(sprintf( - 'The option "%s" with value %s is expected to be of type '. - '"%s", but is of type "%s".', - $option, - $this->formatValue($value), - implode('" or "', $this->allowedTypes[$option]), - implode('|', array_keys($invalidTypes)) - )); + $keys = array_keys($invalidTypes); + + if (1 === \count($keys) && '[]' === substr($keys[0], -2)) { + throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), $keys[0])); + } + + throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), implode('|', array_keys($invalidTypes)))); } } @@ -825,7 +824,7 @@ class OptionsResolver implements Options $this->formatValue($value) ); - if (count($printableAllowedValues) > 0) { + if (\count($printableAllowedValues) > 0) { $message .= sprintf( ' Accepted values are: %s.', $this->formatValues($printableAllowedValues) @@ -877,23 +876,8 @@ class OptionsResolver implements Options */ private function verifyTypes($type, $value, array &$invalidTypes) { - if ('[]' === substr($type, -2) && is_array($value)) { - $originalType = $type; - $type = substr($type, 0, -2); - $invalidValues = array_filter( // Filter out valid values, keeping invalid values in the resulting array - $value, - function ($value) use ($type) { - return !self::isValueValidType($type, $value); - } - ); - - if (!$invalidValues) { - return true; - } - - $invalidTypes[$this->formatTypeOf($value, $originalType)] = true; - - return false; + if (\is_array($value) && '[]' === substr($type, -2)) { + return $this->verifyArrayType($type, $value, $invalidTypes); } if (self::isValueValidType($type, $value)) { @@ -907,6 +891,46 @@ class OptionsResolver implements Options return false; } + /** + * @return bool + */ + private function verifyArrayType($type, array $value, array &$invalidTypes, $level = 0) + { + $type = substr($type, 0, -2); + + $suffix = '[]'; + while (\strlen($suffix) <= $level * 2) { + $suffix .= '[]'; + } + + if ('[]' === substr($type, -2)) { + $success = true; + foreach ($value as $item) { + if (!\is_array($item)) { + $invalidTypes[$this->formatTypeOf($item, null).$suffix] = true; + + return false; + } + + if (!$this->verifyArrayType($type, $item, $invalidTypes, $level + 1)) { + $success = false; + } + } + + return $success; + } + + foreach ($value as $item) { + if (!self::isValueValidType($type, $item)) { + $invalidTypes[$this->formatTypeOf($item, $type).$suffix] = $value; + + return false; + } + } + + return true; + } + /** * Returns whether a resolved option with the given name exists. * @@ -964,7 +988,7 @@ class OptionsResolver implements Options throw new AccessException('Counting is only supported within closures of lazy options and normalizers.'); } - return count($this->defaults); + return \count($this->defaults); } /** @@ -990,13 +1014,13 @@ class OptionsResolver implements Options while ('[]' === substr($type, -2)) { $type = substr($type, 0, -2); $value = array_shift($value); - if (!is_array($value)) { + if (!\is_array($value)) { break; } $suffix .= '[]'; } - if (is_array($value)) { + if (\is_array($value)) { $subTypes = array(); foreach ($value as $val) { $subTypes[$this->formatTypeOf($val, null)] = true; @@ -1006,7 +1030,7 @@ class OptionsResolver implements Options } } - return (is_object($value) ? get_class($value) : gettype($value)).$suffix; + return (\is_object($value) ? \get_class($value) : \gettype($value)).$suffix; } /** @@ -1022,19 +1046,19 @@ class OptionsResolver implements Options */ private function formatValue($value) { - if (is_object($value)) { - return get_class($value); + if (\is_object($value)) { + return \get_class($value); } - if (is_array($value)) { + if (\is_array($value)) { return 'array'; } - if (is_string($value)) { + if (\is_string($value)) { return '"'.$value.'"'; } - if (is_resource($value)) { + if (\is_resource($value)) { return 'resource'; } @@ -1076,6 +1100,22 @@ class OptionsResolver implements Options private static function isValueValidType($type, $value) { - return (function_exists($isFunction = 'is_'.$type) && $isFunction($value)) || $value instanceof $type; + return (\function_exists($isFunction = 'is_'.$type) && $isFunction($value)) || $value instanceof $type; + } + + /** + * @return array + */ + private function getInvalidValues(array $arrayValues, $type) + { + $invalidValues = array(); + + foreach ($arrayValues as $key => $value) { + if (!self::isValueValidType($type, $value)) { + $invalidValues[$key] = $value; + } + } + + return $invalidValues; } } diff --git a/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php b/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php index 12dc77b3c..449a53f6e 100644 --- a/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php +++ b/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php @@ -483,7 +483,7 @@ class OptionsResolverTest extends TestCase /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException - * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "DateTime[]". + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime[]". */ public function testResolveFailsIfInvalidTypedArray() { @@ -507,7 +507,7 @@ class OptionsResolverTest extends TestCase /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException - * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "integer|stdClass|array|DateTime[]". + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass[]". */ public function testResolveFailsIfTypedArrayContainsInvalidTypes() { @@ -524,7 +524,7 @@ class OptionsResolverTest extends TestCase /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException - * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but is of type "double[][]". + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "double[][]". */ public function testResolveFailsWithCorrectLevelsButWrongScalar() { @@ -1584,6 +1584,153 @@ class OptionsResolverTest extends TestCase $this->resolver->setDefined('bar'); $this->resolver->setDefault('lazy1', function () {}); - count($this->resolver); + \count($this->resolver); + } + + public function testNestedArrays() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + + $this->assertEquals(array( + 'foo' => array( + array( + 1, 2, + ), + ), + ), $this->resolver->resolve( + array( + 'foo' => array( + array(1, 2), + ), + ) + )); + } + + public function testNested2Arrays() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][][][]'); + + $this->assertEquals(array( + 'foo' => array( + array( + array( + array( + 1, 2, + ), + ), + ), + ), + ), $this->resolver->resolve( + array( + 'foo' => array( + array( + array( + array(1, 2), + ), + ), + ), + ) + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "integer[][][][]". + */ + public function testNestedArraysException() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'float[][][][]'); + + $this->resolver->resolve( + array( + 'foo' => array( + array( + array( + array(1, 2), + ), + ), + ), + ) + ); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]". + */ + public function testNestedArrayException1() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + $this->resolver->resolve(array( + 'foo' => array( + array(1, true, 'str', array(2, 3)), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]". + */ + public function testNestedArrayException2() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + $this->resolver->resolve(array( + 'foo' => array( + array(true, 'str', array(2, 3)), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string[][]". + */ + public function testNestedArrayException3() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[][][]'); + $this->resolver->resolve(array( + 'foo' => array( + array('str', array(1, 2)), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "integer[][][]". + */ + public function testNestedArrayException4() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[][][]'); + $this->resolver->resolve(array( + 'foo' => array( + array( + array('str'), array(1, 2), ), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array[]". + */ + public function testNestedArrayException5() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[]'); + $this->resolver->resolve(array( + 'foo' => array( + array( + array('str'), array(1, 2), ), + ), + )); } }