[更新]ComposerUpdate

This commit is contained in:
Anyon 2018-08-06 14:54:56 +08:00
parent 27ac17f224
commit 40df6c4f50
45 changed files with 1276 additions and 260 deletions

View File

@ -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"
}
}
}

View File

@ -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')) {
/**
* 浏览器友好的变量输出

View File

@ -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' => '类不存在',

View File

@ -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';

View File

@ -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));

View File

@ -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;

View File

@ -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;
}
}

View File

@ -440,6 +440,7 @@ class Container implements ArrayAccess, IteratorAggregate, Countable
foreach ($params as $param) {
$name = $param->getName();
$lowerName = Loader::parseName($name);
$class = $param->getClass();
if ($class) {
@ -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;
}
}

View File

@ -251,4 +251,12 @@ class Controller
return true;
}
public function __debugInfo()
{
$data = get_object_vars($this);
unset($data['app'], $data['request']);
return $data;
}
}

View File

@ -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 查询单个记录

View File

@ -267,4 +267,12 @@ class Debug
}
}
}
public function __debugInfo()
{
$data = get_object_vars($this);
unset($data['app']);
return $data;
}
}

View File

@ -218,4 +218,11 @@ class Hook
return $result;
}
public function __debugInfo()
{
$data = get_object_vars($this);
unset($data['app']);
return $data;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -1307,4 +1307,12 @@ class Template
return '/' . $regex . '/is';
}
public function __debugInfo()
{
$data = get_object_vars($this);
unset($data['app'], $data['storege']);
return $data;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,32 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
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());
}
}

View File

@ -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);
@ -2897,19 +2957,152 @@ class Query
return $result;
}
/**
* 查找单条记录
* @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 查询数据
* @return void
*/
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)
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);
}
}
}
}
}
@ -2920,25 +3113,43 @@ class Query
* @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

View File

@ -0,0 +1,179 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
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);
}
}

View File

@ -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']);
}

View File

@ -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']);
}

View File

@ -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];
}
}
}

View File

@ -78,6 +78,12 @@ trait Attribute
*/
private $origin = [];
/**
* 动态获取器
* @var array
*/
private $withAttr = [];
/**
* 获取模型对象的主键
* @access public
@ -442,9 +448,18 @@ trait Attribute
}
// 检测属性获取器
$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;
}
}

View File

@ -172,9 +172,10 @@ trait RelationShip
* 查询当前模型的关联数据
* @access public
* @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);
@ -198,8 +199,15 @@ trait RelationShip
}
$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;
@ -210,9 +218,10 @@ trait RelationShip
* @access public
* @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;
@ -233,8 +242,15 @@ trait RelationShip
}
$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);
}
}
@ -243,9 +259,10 @@ trait RelationShip
* @access public
* @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;
@ -266,8 +283,15 @@ trait RelationShip
}
$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;
}

View File

@ -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])) {
@ -390,12 +390,13 @@ class BelongsToMany extends Relation
* @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();
@ -427,10 +428,15 @@ class BelongsToMany extends Relation
* @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();

View File

@ -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;
// 组装模型数据

View File

@ -0,0 +1,137 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
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;
}
}

View File

@ -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;
}
}

View File

@ -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,6 +686,7 @@ abstract class Rule
*/
protected function mergeGroupOptions()
{
if (!$this->lockOption) {
$parentOption = $this->parent->getOption();
// 合并分组参数
foreach ($this->mergeOptions as $item) {
@ -689,6 +696,8 @@ abstract class Rule
}
$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;
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -176,4 +176,8 @@ class Php
}
}
public function __debugInfo()
{
return ['config' => $this->config];
}
}

View File

@ -184,4 +184,9 @@ class Think
{
return call_user_func_array([$this->template, $method], $params);
}
public function __debugInfo()
{
return ['config' => $this->config];
}
}

2
vendor/autoload.php vendored
View File

@ -4,4 +4,4 @@
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit5a29168a045363ae5963661dddea68b7::getLoader();
return ComposerAutoloaderInit2151fec9fdcca855dbe23b45bae24bbf::getLoader();

View File

@ -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;

View File

@ -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);
}

View File

@ -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"

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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), ),
),
));
}
}