This commit is contained in:
thinkphp
2017-11-20 16:02:55 +08:00
23 changed files with 944 additions and 722 deletions

View File

@@ -49,19 +49,26 @@ use think\db\Query;
*/
class Db
{
// 数据库连接实例
/**
* @var Connection[] 数据库连接实例
*/
private static $instance = [];
// 查询次数
/**
* @var int 查询次数
*/
public static $queryTimes = 0;
// 执行次数
/**
* @var int 执行次数
*/
public static $executeTimes = 0;
/**
* 数据库初始化 并取得数据库类实例
* @static
* 数据库初始化并取得数据库类实例
* @access public
* @param mixed $config 连接配置
* @param bool|string $name 连接标识 true 强制重新连接
* @param mixed $config 连接配置
* @param bool|string $name 连接标识 true 强制重新连接
* @return Connection
* @throws Exception
*/
@@ -70,34 +77,48 @@ class Db
if (false === $name) {
$name = md5(serialize($config));
}
if (true === $name || !isset(self::$instance[$name])) {
// 解析连接参数 支持数组和字符串
$options = self::parseConfig($config);
if (empty($options['type'])) {
throw new \InvalidArgumentException('Undefined db type');
}
$class = false !== strpos($options['type'], '\\') ? $options['type'] : '\\think\\db\\connector\\' . ucwords($options['type']);
$class = false !== strpos($options['type'], '\\') ?
$options['type'] :
'\\think\\db\\connector\\' . ucwords($options['type']);
// 记录初始化信息
if (App::$debug) {
Log::record('[ DB ] INIT ' . $options['type'], 'info');
}
if (true === $name) {
$name = md5(serialize($config));
}
self::$instance[$name] = new $class($options);
}
return self::$instance[$name];
}
public static function clear() {
self::$instance = null;
/**
* 清除连接实例
* @access public
* @return void
*/
public static function clear()
{
self::$instance = [];
}
/**
* 数据库连接参数解析
* @static
* @access private
* @param mixed $config
* @param mixed $config 连接参数
* @return array
*/
private static function parseConfig($config)
@@ -105,30 +126,27 @@ class Db
if (empty($config)) {
$config = Config::get('database');
} elseif (is_string($config) && false === strpos($config, '/')) {
// 支持读取配置参数
$config = Config::get($config);
}
if (is_string($config)) {
return self::parseDsn($config);
} else {
return $config;
$config = Config::get($config); // 支持读取配置参数
}
return is_string($config) ? self::parseDsn($config) : $config;
}
/**
* DSN解析
* DSN 解析
* 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1&param2=val2#utf8
* @static
* @access private
* @param string $dsnStr
* @param string $dsnStr 数据库 DSN 字符串解析
* @return array
*/
private static function parseDsn($dsnStr)
{
$info = parse_url($dsnStr);
if (!$info) {
return [];
}
$dsn = [
'type' => $info['scheme'],
'username' => isset($info['user']) ? $info['user'] : '',
@@ -144,13 +162,19 @@ class Db
} else {
$dsn['params'] = [];
}
return $dsn;
}
// 调用驱动类的方法
/**
* 调用驱动类的方法
* @access public
* @param string $method 方法名
* @param array $params 参数
* @return mixed
*/
public static function __callStatic($method, $params)
{
// 自动初始化数据库
return call_user_func_array([self::connect(), $method], $params);
}
}

View File

@@ -16,21 +16,27 @@ use think\response\Redirect;
class Debug
{
// 区间时间信息
/**
* @var array 区间时间信息
*/
protected static $info = [];
// 区间内存信息
/**
* @var array 区间内存信息
*/
protected static $mem = [];
/**
* 记录时间(微秒)和内存使用情况
* @param string $name 标记位置
* @param mixed $value 标记值 留空则取当前 time 表示仅记录时间 否则同时记录时间和内存
* @return mixed
* @access public
* @param string $name 标记位置
* @param mixed $value 标记值(留空则取当前 time 表示仅记录时间 否则同时记录时间和内存)
* @return void
*/
public static function remark($name, $value = '')
{
// 记录时间和内存使用
self::$info[$name] = is_float($value) ? $value : microtime(true);
if ('time' != $value) {
self::$mem['mem'][$name] = is_float($value) ? $value : memory_get_usage();
self::$mem['peak'][$name] = memory_get_peak_usage();
@@ -39,23 +45,26 @@ class Debug
/**
* 统计某个区间的时间(微秒)使用情况 返回值以秒为单位
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer|string $dec 小数位
* @return integer
* @access public
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer $dec 小数位
* @return string
*/
public static function getRangeTime($start, $end, $dec = 6)
{
if (!isset(self::$info[$end])) {
self::$info[$end] = microtime(true);
}
return number_format((self::$info[$end] - self::$info[$start]), $dec);
}
/**
* 统计从开始到统计时的时间(微秒)使用情况 返回值以秒为单位
* @param integer|string $dec 小数位
* @return integer
* @access public
* @param integer $dec 小数位
* @return string
*/
public static function getUseTime($dec = 6)
{
@@ -64,6 +73,7 @@ class Debug
/**
* 获取当前访问的吞吐率情况
* @access public
* @return string
*/
public static function getThroughputRate()
@@ -73,9 +83,10 @@ class Debug
/**
* 记录区间的内存使用情况
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer|string $dec 小数位
* @access public
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer $dec 小数位
* @return string
*/
public static function getRangeMem($start, $end, $dec = 2)
@@ -83,19 +94,23 @@ class Debug
if (!isset(self::$mem['mem'][$end])) {
self::$mem['mem'][$end] = memory_get_usage();
}
$size = self::$mem['mem'][$end] - self::$mem['mem'][$start];
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
$pos = 0;
while ($size >= 1024) {
$size /= 1024;
$pos++;
}
return round($size, $dec) . " " . $a[$pos];
}
/**
* 统计从开始到统计时的内存使用情况
* @param integer|string $dec 小数位
* @access public
* @param integer $dec 小数位
* @return string
*/
public static function getUseMem($dec = 2)
@@ -103,103 +118,128 @@ class Debug
$size = memory_get_usage() - THINK_START_MEM;
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
$pos = 0;
while ($size >= 1024) {
$size /= 1024;
$pos++;
}
return round($size, $dec) . " " . $a[$pos];
}
/**
* 统计区间的内存峰值情况
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer|string $dec 小数位
* @return mixed
* @access public
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer $dec 小数位
* @return string
*/
public static function getMemPeak($start, $end, $dec = 2)
{
if (!isset(self::$mem['peak'][$end])) {
self::$mem['peak'][$end] = memory_get_peak_usage();
}
$size = self::$mem['peak'][$end] - self::$mem['peak'][$start];
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
$pos = 0;
while ($size >= 1024) {
$size /= 1024;
$pos++;
}
return round($size, $dec) . " " . $a[$pos];
}
/**
* 获取文件加载信息
* @param bool $detail 是否显示详细
* @access public
* @param bool $detail 是否显示详细
* @return integer|array
*/
public static function getFile($detail = false)
{
$files = get_included_files();
if ($detail) {
$files = get_included_files();
$info = [];
foreach ($files as $key => $file) {
$info = [];
foreach ($files as $file) {
$info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )';
}
return $info;
}
return count(get_included_files());
return count($files);
}
/**
* 浏览器友好的变量输出
* @param mixed $var 变量
* @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串
* @param string $label 标签 默认为空
* @param integer $flags htmlspecialchars flags
* @return void|string
* @access public
* @param mixed $var 变量
* @param boolean $echo 是否输出(默认为 true为 false 则返回输出字符串)
* @param string|null $label 标签(默认为空)
* @param integer $flags htmlspecialchars 的标志
* @return null|string
*/
public static function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE)
{
$label = (null === $label) ? '' : rtrim($label) . ':';
ob_start();
var_dump($var);
$output = ob_get_clean();
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', ob_get_clean());
if (IS_CLI) {
$output = PHP_EOL . $label . $output . PHP_EOL;
} else {
if (!extension_loaded('xdebug')) {
$output = htmlspecialchars($output, $flags);
}
$output = '<pre>' . $label . $output . '</pre>';
}
if ($echo) {
echo($output);
return;
} else {
return $output;
}
return $output;
}
/**
* 调试信息注入到响应中
* @access public
* @param Response $response 响应实例
* @param string $content 返回的字符串
* @return void
*/
public static function inject(Response $response, &$content)
{
$config = Config::get('trace');
$type = isset($config['type']) ? $config['type'] : 'Html';
$request = Request::instance();
$class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type);
$config = Config::get('trace');
$type = isset($config['type']) ? $config['type'] : 'Html';
$class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type);
unset($config['type']);
if (class_exists($class)) {
$trace = new $class($config);
} else {
if (!class_exists($class)) {
throw new ClassNotFoundException('class not exists:' . $class, $class);
}
/** @var \think\debug\Console|\think\debug\Html $trace */
$trace = new $class($config);
if ($response instanceof Redirect) {
//TODO 记录
// TODO 记录
} else {
$output = $trace->output($response, Log::getLog());
if (is_string($output)) {
// trace调试信息注入
// trace 调试信息注入
$pos = strripos($content, '</body>');
if (false !== $pos) {
$content = substr($content, 0, $pos) . $output . substr($content, $pos);

View File

@@ -15,22 +15,25 @@ class Env
{
/**
* 获取环境变量值
* @param string $name 环境变量名(支持二级 .号分割)
* @param string $default 默认值
* @access public
* @param string $name 环境变量名(支持二级 . 号分割)
* @param string $default 默认值
* @return mixed
*/
public static function get($name, $default = null)
{
$result = getenv(ENV_PREFIX . strtoupper(str_replace('.', '_', $name)));
if (false !== $result) {
if ('false' === $result) {
$result = false;
} elseif ('true' === $result) {
$result = true;
}
return $result;
} else {
return $default;
}
return $default;
}
}

View File

@@ -20,6 +20,7 @@ class Error
{
/**
* 注册异常处理
* @access public
* @return void
*/
public static function register()
@@ -32,12 +33,15 @@ class Error
/**
* 异常处理
* @param \Exception|\Throwable $e 异常
* @access public
* @param \Exception|\Throwable $e 异常
* @return void
*/
public static function appException($e)
{
if (!$e instanceof \Exception) $e = new ThrowableError($e);
if (!$e instanceof \Exception) {
$e = new ThrowableError($e);
}
$handler = self::getExceptionHandler();
$handler->report($e);
@@ -51,11 +55,12 @@ class Error
/**
* 错误处理
* @access public
* @param integer $errno 错误编号
* @param integer $errstr 详细错误信息
* @param string $errfile 出错的文件
* @param integer $errline 出错行号
* @param array $errcontext 出错上下文
* @param array $errcontext 出错上下文
* @return void
* @throws ErrorException
*/
@@ -64,13 +69,16 @@ class Error
$exception = new ErrorException($errno, $errstr, $errfile, $errline, $errcontext);
// 符合异常处理的则将错误信息托管至 think\exception\ErrorException
if (error_reporting() & $errno) throw $exception;
if (error_reporting() & $errno) {
throw $exception;
}
self::getExceptionHandler()->report($exception);
}
/**
* 异常中止处理
* @access public
* @return void
*/
public static function appShutdown()
@@ -88,7 +96,8 @@ class Error
/**
* 确定错误类型是否致命
* @param int $type
* @access protected
* @param int $type 错误类型
* @return bool
*/
protected static function isFatal($type)
@@ -98,6 +107,7 @@ class Error
/**
* 获取异常处理的实例
* @access public
* @return Handle
*/
public static function getExceptionHandler()
@@ -115,7 +125,10 @@ class Error
} else {
$handle = new Handle;
if ($class instanceof \Closure) $handle->setRender($class);
if ($class instanceof \Closure) {
$handle->setRender($class);
}
}
}

View File

@@ -13,15 +13,13 @@ namespace think;
class Exception extends \Exception
{
/**
* 保存异常页面显示的额外Debug数据
* @var array
* @var array 保存异常页面显示的额外 Debug 数据
*/
protected $data = [];
/**
* 设置异常额外的Debug数据
* 设置异常额外的 Debug 数据
* 数据将会显示为下面的格式
*
* Exception Data
@@ -33,8 +31,10 @@ class Exception extends \Exception
* key1 value1
* key2 value2
*
* @param string $label 数据分类,用于异常页面显示
* @param array $data 需要显示的数据,必须为关联数组
* @access protected
* @param string $label 数据分类,用于异常页面显示
* @param array $data 需要显示的数据,必须为关联数组
* @return void
*/
final protected function setData($label, array $data)
{
@@ -42,13 +42,14 @@ class Exception extends \Exception
}
/**
* 获取异常额外Debug数据
* 获取异常额外 Debug 数据
* 主要用于输出到异常页面便于调试
* @return array 由setData设置的Debug数据
* @access public
* @return array
*/
final public function getData()
{
return $this->data;
}
}

View File

@@ -16,25 +16,51 @@ use SplFileObject;
class File extends SplFileObject
{
/**
* 错误信息
* @var string
* @var string 错误信息
*/
private $error = '';
// 当前完整文件名
/**
* @var string 当前完整文件名
*/
protected $filename;
// 上传文件名
/**
* @var string 上传文件名
*/
protected $saveName;
// 文件上传命名规则
/**
* @var string 文件上传命名规则
*/
protected $rule = 'date';
// 文件上传验证规则
/**
* @var array 文件上传验证规则
*/
protected $validate = [];
// 单元测试
/**
* @var bool 单元测试
*/
protected $isTest;
// 上传文件信息
/**
* @var array 上传文件信息
*/
protected $info;
// 文件hash信息
/**
* @var array 文件 hash 信息
*/
protected $hash = [];
/**
* File constructor.
* @access public
* @param string $filename 文件名称
* @param string $mode 访问模式
*/
public function __construct($filename, $mode = 'r')
{
parent::__construct($filename, $mode);
@@ -42,30 +68,35 @@ class File extends SplFileObject
}
/**
* 是否测试
* @param bool $test 是否测试
* 设置是否是单元测试
* @access public
* @param bool $test 是否是测试
* @return $this
*/
public function isTest($test = false)
{
$this->isTest = $test;
return $this;
}
/**
* 设置上传信息
* @param array $info 上传文件信息
* @access public
* @param array $info 上传文件信息
* @return $this
*/
public function setUploadInfo($info)
{
$this->info = $info;
return $this;
}
/**
* 获取上传文件的信息
* @param string $name
* @access public
* @param string $name 信息名称
* @return array|string
*/
public function getInfo($name = '')
@@ -75,6 +106,7 @@ class File extends SplFileObject
/**
* 获取上传文件的文件名
* @access public
* @return string
*/
public function getSaveName()
@@ -84,18 +116,21 @@ class File extends SplFileObject
/**
* 设置上传文件的保存文件名
* @param string $saveName
* @access public
* @param string $saveName 保存名称
* @return $this
*/
public function setSaveName($saveName)
{
$this->saveName = $saveName;
return $this;
}
/**
* 获取文件的哈希散列值
* @param string $type
* @access public
* @param string $type 类型
* @return string
*/
public function hash($type = 'sha1')
@@ -103,75 +138,79 @@ class File extends SplFileObject
if (!isset($this->hash[$type])) {
$this->hash[$type] = hash_file($type, $this->filename);
}
return $this->hash[$type];
}
/**
* 检查目录是否可写
* @param string $path 目录
* @access protected
* @param string $path 目录
* @return boolean
*/
protected function checkPath($path)
{
if (is_dir($path)) {
if (is_dir($path) || mkdir($path, 0755, true)) {
return true;
}
if (mkdir($path, 0755, true)) {
return true;
} else {
$this->error = ['directory {:path} creation failed', ['path' => $path]];
return false;
}
$this->error = ['directory {:path} creation failed', ['path' => $path]];
return false;
}
/**
* 获取文件类型信息
* @access public
* @return string
*/
public function getMime()
{
$finfo = finfo_open(FILEINFO_MIME_TYPE);
return finfo_file($finfo, $this->filename);
}
/**
* 设置文件的命名规则
* @param string $rule 文件命名规则
* @access public
* @param string $rule 文件命名规则
* @return $this
*/
public function rule($rule)
{
$this->rule = $rule;
return $this;
}
/**
* 设置上传文件的验证规则
* @param array $rule 验证规则
* @access public
* @param array $rule 验证规则
* @return $this
*/
public function validate($rule = [])
public function validate(array $rule = [])
{
$this->validate = $rule;
return $this;
}
/**
* 检测是否合法的上传文件
* @access public
* @return bool
*/
public function isValid()
{
if ($this->isTest) {
return is_file($this->filename);
}
return is_uploaded_file($this->filename);
return $this->isTest ? is_file($this->filename) : is_uploaded_file($this->filename);
}
/**
* 检测上传文件
* @param array $rule 验证规则
* @access public
* @param array $rule 验证规则
* @return bool
*/
public function check($rule = [])
@@ -184,7 +223,7 @@ class File extends SplFileObject
return false;
}
/* 检查文件Mime类型 */
/* 检查文件 Mime 类型 */
if (isset($rule['type']) && !$this->checkMime($rule['type'])) {
$this->error = 'mimetype to upload is not allowed';
return false;
@@ -201,12 +240,14 @@ class File extends SplFileObject
$this->error = 'illegal image files';
return false;
}
return true;
}
/**
* 检测上传文件后缀
* @param array|string $ext 允许后缀
* @access public
* @param array|string $ext 允许后缀
* @return bool
*/
public function checkExt($ext)
@@ -217,79 +258,73 @@ class File extends SplFileObject
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
if (!in_array($extension, $ext)) {
return false;
}
return true;
return in_array($extension, $ext);
}
/**
* 检测图像文件
* @access public
* @return bool
*/
public function checkImg()
{
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
/* 对图像文件进行严格检测 */
if (in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) && !in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13])) {
return false;
}
return true;
// 如果上传的不是图片,或者是图片而且后缀确实符合图片类型则返回 true
return !in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) || in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13]);
}
// 判断图像类型
/**
* 判断图像类型
* @access protected
* @param string $image 图片名称
* @return bool|int
*/
protected function getImageType($image)
{
if (function_exists('exif_imagetype')) {
return exif_imagetype($image);
} else {
try {
$info = getimagesize($image);
return $info ? $info[2] : false;
} catch (\Exception $e) {
return false;
}
}
try {
$info = getimagesize($image);
return $info ? $info[2] : false;
} catch (\Exception $e) {
return false;
}
}
/**
* 检测上传文件大小
* @param integer $size 最大大小
* @access public
* @param integer $size 最大大小
* @return bool
*/
public function checkSize($size)
{
if ($this->getSize() > $size) {
return false;
}
return true;
return $this->getSize() <= $size;
}
/**
* 检测上传文件类型
* @param array|string $mime 允许类型
* @access public
* @param array|string $mime 允许类型
* @return bool
*/
public function checkMime($mime)
{
if (is_string($mime)) {
$mime = explode(',', $mime);
}
$mime = is_string($mime) ? explode(',', $mime) : $mime;
if (!in_array(strtolower($this->getMime()), $mime)) {
return false;
}
return true;
return in_array(strtolower($this->getMime()), $mime);
}
/**
* 移动文件
* @param string $path 保存路径
* @param string|bool $savename 保存的文件名 默认自动生成
* @param boolean $replace 同名文件是否覆盖
* @return false|File false-失败 否则返回File实例
* @access public
* @param string $path 保存路径
* @param string|bool $savename 保存的文件名 默认自动生成
* @param boolean $replace 同名文件是否覆盖
* @return false|File
*/
public function move($path, $savename = true, $replace = true)
{
@@ -320,7 +355,7 @@ class File extends SplFileObject
return false;
}
/* 不覆盖同名文件 */
// 不覆盖同名文件
if (!$replace && is_file($filename)) {
$this->error = ['has the same filename: {:filename}', ['filename' => $filename]];
return false;
@@ -334,23 +369,23 @@ class File extends SplFileObject
return false;
}
// 返回 File对象实例
// 返回 File 对象实例
$file = new self($filename);
$file->setSaveName($saveName);
$file->setUploadInfo($this->info);
$file->setSaveName($saveName)->setUploadInfo($this->info);
return $file;
}
/**
* 获取保存文件名
* @param string|bool $savename 保存的文件名 默认自动生成
* @access protected
* @param string|bool $savename 保存的文件名 默认自动生成
* @return string
*/
protected function buildSaveName($savename)
{
// 自动生成文件名
if (true === $savename) {
// 自动生成文件名
if ($this->rule instanceof \Closure) {
$savename = call_user_func_array($this->rule, [$this]);
} else {
@@ -382,7 +417,9 @@ class File extends SplFileObject
/**
* 获取错误代码信息
* @param int $errorNo 错误号
* @access private
* @param int $errorNo 错误号
* @return $this
*/
private function error($errorNo)
{
@@ -406,10 +443,13 @@ class File extends SplFileObject
default:
$this->error = 'unknown upload error';
}
return $this;
}
/**
* 获取错误信息(支持多语言)
* @access public
* @return string
*/
public function getError()
@@ -424,6 +464,13 @@ class File extends SplFileObject
return Lang::has($msg) ? Lang::get($msg, $vars) : $msg;
}
/**
* 魔法方法,获取文件的 hash 值
* @access public
* @param string $method 方法名
* @param mixed $args 调用参数
* @return string
*/
public function __call($method, $args)
{
return $this->hash($method);

View File

@@ -13,19 +13,23 @@ namespace think;
class Hook
{
/**
* @var array 标签
*/
private static $tags = [];
/**
* 动态添加行为扩展到某个标签
* @param string $tag 标签名称
* @param mixed $behavior 行为名称
* @param bool $first 是否放到开头执行
* @access public
* @param string $tag 标签名称
* @param mixed $behavior 行为名称
* @param bool $first 是否放到开头执行
* @return void
*/
public static function add($tag, $behavior, $first = false)
{
isset(self::$tags[$tag]) || self::$tags[$tag] = [];
if (is_array($behavior) && !is_callable($behavior)) {
if (!array_key_exists('_overlay', $behavior) || !$behavior['_overlay']) {
unset($behavior['_overlay']);
@@ -43,8 +47,10 @@ class Hook
/**
* 批量导入插件
* @param array $tags 插件信息
* @param boolean $recursive 是否递归合并
* @access public
* @param array $tags 插件信息
* @param boolean $recursive 是否递归合并
* @return void
*/
public static function import(array $tags, $recursive = true)
{
@@ -59,55 +65,59 @@ class Hook
/**
* 获取插件信息
* @param string $tag 插件位置 留空获取全部
* @access public
* @param string $tag 插件位置(留空获取全部)
* @return array
*/
public static function get($tag = '')
{
if (empty($tag)) {
//获取全部的插件信息
return self::$tags;
} else {
return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : [];
}
return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : [];
}
/**
* 监听标签的行为
* @param string $tag 标签名称
* @param mixed $params 传入参数
* @param mixed $extra 额外参数
* @param bool $once 只获取一个有效返回值
* @access public
* @param string $tag 标签名称
* @param mixed $params 传入参数
* @param mixed $extra 额外参数
* @param bool $once 只获取一个有效返回值
* @return mixed
*/
public static function listen($tag, &$params = null, $extra = null, $once = false)
{
$results = [];
$tags = static::get($tag);
foreach ($tags as $key => $name) {
foreach (static::get($tag) as $key => $name) {
$results[$key] = self::exec($name, $tag, $params, $extra);
if (false === $results[$key]) {
// 如果返回false 则中断行为执行
break;
} elseif (!is_null($results[$key]) && $once) {
// 如果返回 false,或者仅获取一个有效返回则中断行为执行
if (false === $results[$key] || (!is_null($results[$key]) && $once)) {
break;
}
}
return $once ? end($results) : $results;
}
/**
* 执行某个行为
* @param mixed $class 要执行的行为
* @param string $tag 方法名(标签名)
* @param Mixed $params 传人的参数
* @param mixed $extra 额外参数
* @access public
* @param mixed $class 要执行的行为
* @param string $tag 方法名(标签名)
* @param mixed $params 传人的参数
* @param mixed $extra 额外参数
* @return mixed
*/
public static function exec($class, $tag = '', &$params = null, $extra = null)
{
App::$debug && Debug::remark('behavior_start', 'time');
$method = Loader::parseName($tag, 1, false);
if ($class instanceof \Closure) {
$result = call_user_func_array($class, [ & $params, $extra]);
$class = 'Closure';
@@ -126,10 +136,12 @@ class Hook
$method = ($tag && is_callable([$obj, $method])) ? $method : 'run';
$result = $obj->$method($params, $extra);
}
if (App::$debug) {
Debug::remark('behavior_end', 'time');
Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info');
}
return $result;
}

View File

@@ -13,114 +13,148 @@ namespace think;
class Lang
{
// 语言数据
/**
* @var array 语言数据
*/
private static $lang = [];
// 语言作用域
private static $range = 'zh-cn';
// 语言自动侦测的变量
protected static $langDetectVar = 'lang';
// 语言Cookie变量
protected static $langCookieVar = 'think_var';
// 语言Cookie的过期时间
protected static $langCookieExpire = 3600;
// 允许语言列表
protected static $allowLangList = [];
// Accept-Language转义为对应语言包名称 系统默认配置
protected static $acceptLanguage = [
'zh-hans-cn' => 'zh-cn',
];
// 设定当前的语言
/**
* @var string 语言作用域
*/
private static $range = 'zh-cn';
/**
* @var string 语言自动侦测的变量
*/
protected static $langDetectVar = 'lang';
/**
* @var string 语言 Cookie 变量
*/
protected static $langCookieVar = 'think_var';
/**
* @var int 语言 Cookie 的过期时间
*/
protected static $langCookieExpire = 3600;
/**
* @var array 允许语言列表
*/
protected static $allowLangList = [];
/**
* @var array Accept-Language 转义为对应语言包名称 系统默认配置
*/
protected static $acceptLanguage = ['zh-hans-cn' => 'zh-cn'];
/**
* 设定当前的语言
* @access public
* @param string $range 语言作用域
* @return string
*/
public static function range($range = '')
{
if ('' == $range) {
return self::$range;
} else {
if ($range) {
self::$range = $range;
}
return self::$range;
}
/**
* 设置语言定义(不区分大小写)
* @param string|array $name 语言变量
* @param string $value 语言
* @param string $range 语言作用域
* @access public
* @param string|array $name 语言变量
* @param string $value 语言
* @param string $range 语言作用域
* @return mixed
*/
public static function set($name, $value = null, $range = '')
{
$range = $range ?: self::$range;
// 批量定义
if (!isset(self::$lang[$range])) {
self::$lang[$range] = [];
}
if (is_array($name)) {
return self::$lang[$range] = array_change_key_case($name) + self::$lang[$range];
} else {
return self::$lang[$range][strtolower($name)] = $value;
}
return self::$lang[$range][strtolower($name)] = $value;
}
/**
* 加载语言定义(不区分大小写)
* @param array|string $file 语言文件
* @param string $range 语言作用域
* @access public
* @param array|string $file 语言文件
* @param string $range 语言作用域
* @return mixed
*/
public static function load($file, $range = '')
{
$range = $range ?: self::$range;
$file = is_string($file) ? [$file] : $file;
if (!isset(self::$lang[$range])) {
self::$lang[$range] = [];
}
// 批量定义
if (is_string($file)) {
$file = [$file];
}
$lang = [];
foreach ($file as $_file) {
if (is_file($_file)) {
// 记录加载信息
App::$debug && Log::record('[ LANG ] ' . $_file, 'info');
$_lang = include $_file;
if (is_array($_lang)) {
$lang = array_change_key_case($_lang) + $lang;
}
}
}
if (!empty($lang)) {
self::$lang[$range] = $lang + self::$lang[$range];
}
return self::$lang[$range];
}
/**
* 获取语言定义(不区分大小写)
* @param string|null $name 语言变量
* @param string $range 语言作用域
* @access public
* @param string|null $name 语言变量
* @param string $range 语言作用域
* @return mixed
*/
public static function has($name, $range = '')
{
$range = $range ?: self::$range;
return isset(self::$lang[$range][strtolower($name)]);
}
/**
* 获取语言定义(不区分大小写)
* @param string|null $name 语言变量
* @param array $vars 变量替换
* @param string $range 语言作用域
* @access public
* @param string|null $name 语言变量
* @param array $vars 变量替换
* @param string $range 语言作用域
* @return mixed
*/
public static function get($name = null, $vars = [], $range = '')
{
$range = $range ?: self::$range;
// 空参数返回所有定义
if (empty($name)) {
return self::$lang[$range];
}
$key = strtolower($name);
$value = isset(self::$lang[$range][$key]) ? self::$lang[$range][$key] : $name;
@@ -145,45 +179,50 @@ class Lang
}
}
return $value;
}
/**
* 自动侦测设置获取语言选择
* @access public
* @return string
*/
public static function detect()
{
// 自动侦测设置获取语言选择
$langSet = '';
if (isset($_GET[self::$langDetectVar])) {
// url中设置了语言变量
// url 中设置了语言变量
$langSet = strtolower($_GET[self::$langDetectVar]);
} elseif (isset($_COOKIE[self::$langCookieVar])) {
// Cookie中设置了语言变量
// Cookie 中设置了语言变量
$langSet = strtolower($_COOKIE[self::$langCookieVar]);
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
// 自动侦测浏览器语言
preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
$langSet = strtolower($matches[1]);
$acceptLangs = Config::get('header_accept_lang');
if (isset($acceptLangs[$langSet])) {
$langSet = $acceptLangs[$langSet];
} elseif (isset(self::$acceptLanguage[$langSet])) {
$langSet = self::$acceptLanguage[$langSet];
}
}
// 合法的语言
if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) {
// 合法的语言
self::$range = $langSet ?: self::$range;
}
return self::$range;
}
/**
* 设置语言自动侦测的变量
* @param string $var 变量名称
* @access public
* @param string $var 变量名称
* @return void
*/
public static function setLangDetectVar($var)
@@ -192,8 +231,9 @@ class Lang
}
/**
* 设置语言的cookie保存变量
* @param string $var 变量名称
* 设置语言的 cookie 保存变量
* @access public
* @param string $var 变量名称
* @return void
*/
public static function setLangCookieVar($var)
@@ -202,8 +242,9 @@ class Lang
}
/**
* 设置语言的cookie的过期时间
* @param string $expire 过期时间
* 设置语言的 cookie 的过期时间
* @access public
* @param string $expire 过期时间
* @return void
*/
public static function setLangCookieExpire($expire)
@@ -213,7 +254,8 @@ class Lang
/**
* 设置允许的语言列表
* @param array $list 语言列表
* @access public
* @param array $list 语言列表
* @return void
*/
public static function setAllowLangList($list)

View File

@@ -15,31 +15,55 @@ use think\exception\ClassNotFoundException;
class Loader
{
// 实例
/**
* @var array 实例数组
*/
protected static $instance = [];
// 类名映射
/**
* @var array 类名映射
*/
protected static $map = [];
// 命名空间别名
/**
* @var array 命名空间别名
*/
protected static $namespaceAlias = [];
// PSR-4
/**
* @var array PSR-4 命名空间前缀长度映射
*/
private static $prefixLengthsPsr4 = [];
private static $prefixDirsPsr4 = [];
private static $fallbackDirsPsr4 = [];
// PSR-0
private static $prefixesPsr0 = [];
/**
* @var array PSR-4 的加载目录
*/
private static $prefixDirsPsr4 = [];
/**
* @var array PSR-4 加载失败的回退目录
*/
private static $fallbackDirsPsr4 = [];
/**
* @var array PSR-0 命名空间前缀映射
*/
private static $prefixesPsr0 = [];
/**
* @var array PSR-0 加载失败的回退目录
*/
private static $fallbackDirsPsr0 = [];
// 自动加载的文件
/**
* @var array 自动加载的文件
*/
private static $autoloadFiles = [];
/**
* 自动加载
* @access public
* @param string $class 类名
* @param string $class 类名
* @return bool
*/
public static function autoload($class)
@@ -55,14 +79,12 @@ class Loader
}
}
$file = self::findFile($class);
$path = pathinfo($file, PATHINFO_FILENAME);
$realPath = pathinfo(realpath($file), PATHINFO_FILENAME);
// 非 Win 环境不严格区分大小写
if ($file && (!IS_WIN || $path == $realPath)) {
__include_file($file);
return true;
if ($file = self::findFile($class)) {
// 非 Win 环境不严格区分大小写
if (!IS_WIN || pathinfo($file, PATHINFO_FILENAME) == pathinfo(realpath($file), PATHINFO_FILENAME)) {
__include_file($file);
return true;
}
}
return false;
@@ -71,8 +93,8 @@ class Loader
/**
* 查找文件
* @access private
* @param string $class 类名
* @return bool
* @param string $class 类名
* @return bool|string
*/
private static function findFile($class)
{
@@ -133,14 +155,15 @@ class Loader
}
}
// 找不到则设置映射为 false 并返回
return self::$map[$class] = false;
}
/**
* 注册 classmap
* @access public
* @param string|array $class 类名
* @param string $map 映射
* @param string|array $class 类名
* @param string $map 映射
* @return void
*/
public static function addClassMap($class, $map = '')
@@ -155,8 +178,8 @@ class Loader
/**
* 注册命名空间
* @access public
* @param string|array $namespace 命名空间
* @param string $path 路径
* @param string|array $namespace 命名空间
* @param string $path 路径
* @return void
*/
public static function addNamespace($namespace, $path = '')
@@ -171,11 +194,11 @@ class Loader
}
/**
* 添加 Ps0 空间
* 添加 PSR-0 命名空间
* @access private
* @param array|null $prefix 空间前缀
* @param array $paths 路径
* @param bool $prepend 预先设置的优先级更高
* @param array|string $prefix 空间前缀
* @param array $paths 路径
* @param bool $prepend 预先设置的优先级更高
* @return void
*/
private static function addPsr0($prefix, $paths, $prepend = false)
@@ -198,11 +221,11 @@ class Loader
}
/**
* 添加 Ps4 空间
* 添加 PSR-4 空间
* @access private
* @param array|string $prefix 空间前缀
* @param string $paths 路径
* @param bool $prepend 预先设置的优先级更高
* @param array|string $prefix 空间前缀
* @param string $paths 路径
* @param bool $prepend 预先设置的优先级更高
* @return void
*/
private static function addPsr4($prefix, $paths, $prepend = false)
@@ -237,8 +260,8 @@ class Loader
/**
* 注册命名空间别名
* @access public
* @param array|string $namespace 命名空间
* @param string $original 源文件
* @param array|string $namespace 命名空间
* @param string $original 源文件
* @return void
*/
public static function addNamespaceAlias($namespace, $original = '')
@@ -253,7 +276,7 @@ class Loader
/**
* 注册自动加载机制
* @access public
* @param callable $autoload 自动加载处理方法
* @param callable $autoload 自动加载处理方法
* @return void
*/
public static function register($autoload = null)
@@ -325,9 +348,9 @@ class Loader
/**
* 导入所需的类库 同 Java 的 Import 本函数有缓存功能
* @access public
* @param string $class 类库命名空间字符串
* @param string $baseUrl 起始路径
* @param string $ext 导入的文件扩展名
* @param string $class 类库命名空间字符串
* @param string $baseUrl 起始路径
* @param string $ext 导入的文件扩展名
* @return bool
*/
public static function import($class, $baseUrl = '', $ext = EXT)
@@ -362,12 +385,9 @@ class Loader
// 如果类存在则导入类库文件
if (is_array($baseUrl)) {
foreach ($baseUrl as $path) {
$filename = $path . DS . $class . $ext;
if (is_file($filename)) {
if (is_file($filename = $path . DS . $class . $ext)) {
break;
}
}
} else {
$filename = $baseUrl . $class . $ext;
@@ -389,10 +409,10 @@ class Loader
/**
* 实例化(分层)模型
* @access public
* @param string $name Model名称
* @param string $layer 业务层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $common 公共模块名
* @param string $name Model名称
* @param string $layer 业务层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $common 公共模块名
* @return object
* @throws ClassNotFoundException
*/
@@ -424,10 +444,10 @@ class Loader
/**
* 实例化(分层)控制器 格式:[模块名/]控制器名
* @access public
* @param string $name 资源地址
* @param string $layer 控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $empty 空控制器名称
* @param string $name 资源地址
* @param string $layer 控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $empty 空控制器名称
* @return object
* @throws ClassNotFoundException
*/
@@ -453,16 +473,17 @@ class Loader
/**
* 实例化验证类 格式:[模块名/]验证器名
* @access public
* @param string $name 资源地址
* @param string $layer 验证层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $common 公共模块名
* @param string $name 资源地址
* @param string $layer 验证层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $common 公共模块名
* @return object|false
* @throws ClassNotFoundException
*/
public static function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common')
{
$name = $name ?: Config::get('default_validate');
if (empty($name)) {
return new Validate;
}
@@ -492,9 +513,9 @@ class Loader
/**
* 解析模块和类名
* @access protected
* @param string $name 资源地址
* @param string $layer 验证层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $name 资源地址
* @param string $layer 验证层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return array
*/
protected static function getModuleAndClass($name, $layer, $appendSuffix)
@@ -518,8 +539,8 @@ class Loader
/**
* 数据库初始化 并取得数据库类实例
* @access public
* @param mixed $config 数据库配置
* @param bool|string $name 连接标识 true 强制重新连接
* @param mixed $config 数据库配置
* @param bool|string $name 连接标识 true 强制重新连接
* @return \think\db\Connection
*/
public static function db($config = [], $name = false)
@@ -530,10 +551,10 @@ class Loader
/**
* 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
* @access public
* @param string $url 调用地址
* @param string|array $vars 调用参数 支持字符串和数组
* @param string $layer 要调用的控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $url 调用地址
* @param string|array $vars 调用参数 支持字符串和数组
* @param string $layer 要调用的控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return mixed
*/
public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
@@ -562,9 +583,9 @@ class Loader
* 字符串命名风格转换
* type 0 将 Java 风格转换为 C 的风格 1 将 C 风格转换为 Java 的风格
* @access public
* @param string $name 字符串
* @param integer $type 转换类型
* @param bool $ucfirst 首字母是否大写(驼峰规则)
* @param string $name 字符串
* @param integer $type 转换类型
* @param bool $ucfirst 首字母是否大写(驼峰规则)
* @return string
*/
public static function parseName($name, $type = 0, $ucfirst = true)
@@ -583,10 +604,10 @@ class Loader
/**
* 解析应用类的类名
* @access public
* @param string $module 模块名
* @param string $layer 层名 controller model ...
* @param string $name 类名
* @param bool $appendSuffix 是否添加类名后缀
* @param string $module 模块名
* @param string $layer 层名 controller model ...
* @param string $name 类名
* @param bool $appendSuffix 是否添加类名后缀
* @return string
*/
public static function parseClass($module, $layer, $name, $appendSuffix = false)
@@ -617,7 +638,7 @@ class Loader
/**
* include
* @param string $file 文件路径
* @param string $file 文件路径
* @return mixed
*/
function __include_file($file)
@@ -627,7 +648,7 @@ function __include_file($file)
/**
* require
* @param string $file 文件路径
* @param string $file 文件路径
* @return mixed
*/
function __require_file($file)

View File

@@ -17,12 +17,12 @@ use think\exception\ClassNotFoundException;
* Class Log
* @package think
*
* @method void log($msg) static
* @method void error($msg) static
* @method void info($msg) static
* @method void sql($msg) static
* @method void notice($msg) static
* @method void alert($msg) static
* @method void log($msg) static 记录一般日志
* @method void error($msg) static 记录错误日志
* @method void info($msg) static 记录一般信息日志
* @method void sql($msg) static 记录 SQL 查询日志
* @method void notice($msg) static 记录提示日志
* @method void alert($msg) static 记录报警日志
*/
class Log
{
@@ -34,41 +34,60 @@ class Log
const ALERT = 'alert';
const DEBUG = 'debug';
// 日志信息
/**
* @var array 日志信息
*/
protected static $log = [];
// 配置参数
/**
* @var array 配置参数
*/
protected static $config = [];
// 日志类型
/**
* @var array 日志类型
*/
protected static $type = ['log', 'error', 'info', 'sql', 'notice', 'alert', 'debug'];
// 日志写入驱动
/**
* @var log\driver\File|log\driver\Test|log\driver\Socket 日志写入驱动
*/
protected static $driver;
// 当前日志授权key
/**
* @var string 当前日志授权 key
*/
protected static $key;
/**
* 日志初始化
* @param array $config
* @access public
* @param array $config 配置参数
* @return void
*/
public static function init($config = [])
{
$type = isset($config['type']) ? $config['type'] : 'File';
$class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type);
self::$config = $config;
unset($config['type']);
$type = isset($config['type']) ? $config['type'] : 'File';
$class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type);
if (class_exists($class)) {
self::$driver = new $class($config);
} else {
throw new ClassNotFoundException('class not exists:' . $class, $class);
}
// 记录初始化信息
App::$debug && Log::record('[ LOG ] INIT ' . $type, 'info');
}
/**
* 获取日志信息
* @param string $type 信息类型
* @return array
* @access public
* @param string $type 信息类型
* @return array|string
*/
public static function getLog($type = '')
{
@@ -77,21 +96,22 @@ class Log
/**
* 记录调试信息
* @param mixed $msg 调试信息
* @param string $type 信息类型
* @access public
* @param mixed $msg 调试信息
* @param string $type 信息类型
* @return void
*/
public static function record($msg, $type = 'log')
{
self::$log[$type][] = $msg;
if (IS_CLI) {
// 命令行下面日志写入改进
self::save();
}
// 命令行下面日志写入改进
IS_CLI && self::save();
}
/**
* 清空日志信息
* @access public
* @return void
*/
public static function clear()
@@ -100,8 +120,9 @@ class Log
}
/**
* 当前日志记录的授权key
* @param string $key 授权key
* 设置当前日志记录的授权 key
* @access public
* @param string $key 授权 key
* @return void
*/
public static function key($key)
@@ -111,102 +132,105 @@ class Log
/**
* 检查日志写入权限
* @param array $config 当前日志配置参数
* @access public
* @param array $config 当前日志配置参数
* @return bool
*/
public static function check($config)
{
if (self::$key && !empty($config['allow_key']) && !in_array(self::$key, $config['allow_key'])) {
return false;
}
return true;
return !self::$key || empty($config['allow_key']) || in_array(self::$key, $config['allow_key']);
}
/**
* 保存调试信息
* @access public
* @return bool
*/
public static function save()
{
if (!empty(self::$log)) {
if (is_null(self::$driver)) {
self::init(Config::get('log'));
}
if (!self::check(self::$config)) {
// 检测日志写入权限
return false;
}
if (empty(self::$config['level'])) {
// 获取全部日志
$log = self::$log;
if (!App::$debug && isset($log['debug'])) {
unset($log['debug']);
}
} else {
// 记录允许级别
$log = [];
foreach (self::$config['level'] as $level) {
if (isset(self::$log[$level])) {
$log[$level] = self::$log[$level];
}
}
}
$result = self::$driver->save($log);
if ($result) {
self::$log = [];
}
Hook::listen('log_write_done', $log);
return $result;
// 没有需要保存的记录则直接返回
if (empty(self::$log)) {
return true;
}
return true;
is_null(self::$driver) && self::init(Config::get('log'));
// 检测日志写入权限
if (!self::check(self::$config)) {
return false;
}
if (empty(self::$config['level'])) {
// 获取全部日志
$log = self::$log;
if (!App::$debug && isset($log['debug'])) {
unset($log['debug']);
}
} else {
// 记录允许级别
$log = [];
foreach (self::$config['level'] as $level) {
if (isset(self::$log[$level])) {
$log[$level] = self::$log[$level];
}
}
}
if ($result = self::$driver->save($log)) {
self::$log = [];
}
Hook::listen('log_write_done', $log);
return $result;
}
/**
* 实时写入日志信息 并支持行为
* @param mixed $msg 调试信息
* @param string $type 信息类型
* @param bool $force 是否强制写入
* @access public
* @param mixed $msg 调试信息
* @param string $type 信息类型
* @param bool $force 是否强制写入
* @return bool
*/
public static function write($msg, $type = 'log', $force = false)
{
$log = self::$log;
// 封装日志信息
if (true === $force || empty(self::$config['level'])) {
$log[$type][] = $msg;
} elseif (in_array($type, self::$config['level'])) {
$log[$type][] = $msg;
} else {
// 如果不是强制写入,而且信息类型不在可记录的类别中则直接返回 false 不做记录
if (true !== $force && !empty(self::$config['level']) && !in_array($type, self::$config['level'])) {
return false;
}
// 监听log_write
// 封装日志信息
$log[$type][] = $msg;
// 监听 log_write
Hook::listen('log_write', $log);
if (is_null(self::$driver)) {
self::init(Config::get('log'));
}
is_null(self::$driver) && self::init(Config::get('log'));
// 写入日志
$result = self::$driver->save($log);
if ($result) {
if ($result = self::$driver->save($log)) {
self::$log = [];
}
return $result;
}
/**
* 静态调用
* @param $method
* @param $args
* @return mixed
* 静态方法调用
* @access public
* @param string $method 调用方法
* @param mixed $args 参数
* @return void
*/
public static function __callStatic($method, $args)
{
if (in_array($method, self::$type)) {
array_push($args, $method);
return call_user_func_array('\\think\\Log::record', $args);
call_user_func_array('\\think\\Log::record', $args);
}
}

View File

@@ -1297,7 +1297,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$auto = true;
}
foreach ($dataSet as $key => $data) {
if (!empty($auto) && isset($data[$pk])) {
if ($this->isUpdate || (!empty($auto) && isset($data[$pk]))) {
$result[$key] = self::update($data, [], $this->field);
} else {
$result[$key] = self::create($data, $this->field);

View File

@@ -12,6 +12,7 @@
namespace think;
use think\exception\TemplateNotFoundException;
use think\template\TagLib;
/**
* ThinkPHP分离出来的模板引擎
@@ -121,7 +122,7 @@ class Template
* 模板引擎配置项
* @access public
* @param array|string $config
* @return void|array
* @return string|void|array
*/
public function config($config)
{
@@ -235,7 +236,7 @@ class Template
* @access public
* @param mixed $name 布局模板名称 false 则关闭布局
* @param string $replace 布局模板内容替换标识
* @return object
* @return Template
*/
public function layout($name, $replace = '')
{
@@ -689,6 +690,7 @@ class Template
} else {
$className = '\\think\\template\\taglib\\' . ucwords($tagLib);
}
/** @var Taglib $tLib */
$tLib = new $className($this);
$tLib->parseTag($content, $hide ? '' : $tagLib);
return;
@@ -1071,7 +1073,7 @@ class Template
} else {
$path = isset($module) ? APP_PATH . $module . DS . basename($this->config['view_path']) . DS : $this->config['view_path'];
}
$template = $path . $template . '.' . ltrim($this->config['view_suffix'], '.');
$template = realpath($path . $template . '.' . ltrim($this->config['view_suffix'], '.'));
}
if (is_file($template)) {

View File

@@ -681,21 +681,17 @@ class Validate
*/
protected function fileExt($file, $rule)
{
if (!($file instanceof File)) {
return false;
}
if (is_string($rule)) {
$rule = explode(',', $rule);
}
if (is_array($file)) {
foreach ($file as $item) {
if (!$item->checkExt($rule)) {
if (!($item instanceof File) || !$item->checkExt($rule)) {
return false;
}
}
return true;
} else {
} elseif ($file instanceof File) {
return $file->checkExt($rule);
} else {
return false;
}
}
@@ -708,21 +704,17 @@ class Validate
*/
protected function fileMime($file, $rule)
{
if (!($file instanceof File)) {
return false;
}
if (is_string($rule)) {
$rule = explode(',', $rule);
}
if (is_array($file)) {
foreach ($file as $item) {
if (!$item->checkMime($rule)) {
if (!($item instanceof File) || !$item->checkMime($rule)) {
return false;
}
}
return true;
} else {
} elseif ($file instanceof File) {
return $file->checkMime($rule);
} else {
return false;
}
}
@@ -735,18 +727,17 @@ class Validate
*/
protected function fileSize($file, $rule)
{
if (!($file instanceof File)) {
return false;
}
if (is_array($file)) {
foreach ($file as $item) {
if (!$item->checkSize($rule)) {
if (!($item instanceof File) || !$item->checkSize($rule)) {
return false;
}
}
return true;
} else {
} elseif ($file instanceof File) {
return $file->checkSize($rule);
} else {
return false;
}
}

View File

@@ -0,0 +1 @@
{$name??'default'}

View File

@@ -40,13 +40,13 @@ class Foo extends Controller
public function fetchTest()
{
$template = dirname(__FILE__) . '/display.html';
$template = APP_PATH . 'views' . DS .'display.html';
return $this->fetch($template, ['name' => 'ThinkPHP']);
}
public function displayTest()
{
$template = dirname(__FILE__) . '/display.html';
$template = APP_PATH . 'views' . DS .'display.html';
return $this->display($template, ['name' => 'ThinkPHP']);
}
public function test()
@@ -161,7 +161,7 @@ class controllerTest extends \PHPUnit_Framework_TestCase
{
$controller = new Foo(Request::instance());
$view = $this->getView($controller);
$template = dirname(__FILE__) . '/display.html';
$template = APP_PATH . 'views' . DS .'display.html';
$viewFetch = $view->fetch($template, ['name' => 'ThinkPHP']);
$this->assertEquals($controller->fetchTest(), $viewFetch);
}
@@ -170,7 +170,7 @@ class controllerTest extends \PHPUnit_Framework_TestCase
{
$controller = new Foo;
$view = $this->getView($controller);
$template = dirname(__FILE__) . '/display.html';
$template = APP_PATH . 'views' . DS .'display.html';
$viewFetch = $view->display($template, ['name' => 'ThinkPHP']);
$this->assertEquals($controller->displayTest(), $viewFetch);

View File

@@ -16,331 +16,160 @@
namespace tests\thinkphp\library\think;
use think\Cache;
use think\Template;
class templateTest extends \PHPUnit_Framework_TestCase
{
public function testVar()
/**
* @var Template
*/
protected $template;
public function setUp()
{
$template = new Template();
$content = <<<EOF
{\$name.a.b}
EOF;
$data = <<<EOF
<?php echo \$name['a']['b']; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{\$name.a??'test'}
EOF;
$data = <<<EOF
<?php echo isset(\$name['a'])?\$name['a']:'test'; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{\$name.a?='test'}
EOF;
$data = <<<EOF
<?php if(!empty(\$name['a'])) echo 'test'; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{\$name.a?:'test'}
EOF;
$data = <<<EOF
<?php echo !empty(\$name['a'])?\$name['a']:'test'; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{\$name.a?\$name.b:'no'}
EOF;
$data = <<<EOF
<?php echo !empty(\$name['a'])?\$name['b']:'no'; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{\$name.a==\$name.b?='test'}
EOF;
$data = <<<EOF
<?php if(\$name['a']==\$name['b']) echo 'test'; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{\$name.a==\$name.b?'a':'b'}
EOF;
$data = <<<EOF
<?php echo \$name['a']==\$name['b']?'a':'b'; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{\$name.a|default='test'==\$name.b?'a':'b'}
EOF;
$data = <<<EOF
<?php echo (isset(\$name['a']) && (\$name['a'] !== '')?\$name['a']:'test')==\$name['b']?'a':'b'; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{\$name.a|trim==\$name.b?='eq'}
EOF;
$data = <<<EOF
<?php if(trim(\$name['a'])==\$name['b']) echo 'eq'; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{:ltrim(rtrim(\$name.a))}
EOF;
$data = <<<EOF
<?php echo ltrim(rtrim(\$name['a'])); ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{~echo(trim(\$name.a))}
EOF;
$data = <<<EOF
<?php echo(trim(\$name['a'])); ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{++\$name.a}
EOF;
$data = <<<EOF
<?php echo ++\$name['a']; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{/*\$name*/}
EOF;
$data = '';
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{\$0a}
EOF;
$data = '{$0a}';
$template->parse($content);
$this->assertEquals($data, $content);
$this->template = new Template();
}
public function testVarFunction()
public function testAssign()
{
$template = new Template();
$reflectProperty = new \ReflectionProperty(get_class($this->template), 'data');
$reflectProperty->setAccessible(true);
$content = <<<EOF
{\$name.a.b|default='test'}
EOF;
$data = <<<EOF
<?php echo (isset(\$name['a']['b']) && (\$name['a']['b'] !== '')?\$name['a']['b']:'test'); ?>
EOF;
$this->template->assign('version', 'ThinkPHP3.2');
$data = $reflectProperty->getValue($this->template);
$this->assertEquals('ThinkPHP3.2', $data['version']);
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{\$create_time|date="y-m-d",###}
EOF;
$data = <<<EOF
<?php echo date("y-m-d",\$create_time); ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
{\$name}
{\$name|trim|substr=0,3}
EOF;
$data = <<<EOF
<?php echo \$name; ?>
<?php echo substr(trim(\$name),0,3); ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$this->template->assign(['name' => 'Gao', 'version' => 'ThinkPHP5']);
$data = $reflectProperty->getValue($this->template);
$this->assertEquals('Gao', $data['name']);
$this->assertEquals('ThinkPHP5', $data['version']);
}
public function testVarIdentify()
public function testGet()
{
$config['tpl_begin'] = '<#';
$config['tpl_end'] = '#>';
$config['tpl_var_identify'] = '';
$template = new Template($config);
$this->template = new Template();
$data = [
'project' => 'ThinkPHP',
'version' => [
'ThinkPHP5' => ['Think5.0', 'Think5.1']
]
];
$this->template->assign($data);
$content = <<<EOF
<#\$info.a??'test'#>
EOF;
$data = <<<EOF
<?php echo ((is_array(\$info)?\$info['a']:\$info->a)) ? (is_array(\$info)?\$info['a']:\$info->a) : 'test'; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
<#\$info.a?='test'#>
EOF;
$data = <<<EOF
<?php if((is_array(\$info)?\$info['a']:\$info->a)) echo 'test'; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
<#\$info.a==\$info.b?='test'#>
EOF;
$data = <<<EOF
<?php if((is_array(\$info)?\$info['a']:\$info->a)==(is_array(\$info)?\$info['b']:\$info->b)) echo 'test'; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$content = <<<EOF
<#\$info.a|default='test'?'yes':'no'#>
EOF;
$data = <<<EOF
<?php echo ((is_array(\$info)?\$info['a']:\$info->a) ?: 'test')?'yes':'no'; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$template2 = new Template();
$template2->tpl_var_identify = 'obj';
$content = <<<EOF
{\$info2.b|trim?'yes':'no'}
EOF;
$data = <<<EOF
<?php echo trim(\$info2->b)?'yes':'no'; ?>
EOF;
$template2->parse($content);
$this->assertEquals($data, $content);
$this->assertSame($data, $this->template->get());
$this->assertSame('ThinkPHP', $this->template->get('project'));
$this->assertSame(['Think5.0', 'Think5.1'], $this->template->get('version.ThinkPHP5'));
$this->assertNull($this->template->get('version.ThinkPHP3.2'));
}
public function testThinkVar()
/**
* @dataProvider provideTestParseWithVar
*/
public function testParseWithVar($content, $expected)
{
$this->template = new Template();
$this->template->parse($content);
$this->assertEquals($expected, $content);
}
/**
* @dataProvider provideTestParseWithVarFunction
*/
public function testParseWithVarFunction($content, $expected)
{
$this->template = new Template();
$this->template->parse($content);
$this->assertEquals($expected, $content);
}
/**
* @dataProvider provideTestParseWithVarIdentify
*/
public function testParseWithVarIdentify($content, $expected, $config)
{
$this->template = new Template($config);
$this->template->parse($content);
$this->assertEquals($expected, $content);
}
/**
* @dataProvider provideTestParseWithThinkVar
*/
public function testParseWithThinkVar($content, $expected)
{
$config['tpl_begin'] = '{';
$config['tpl_end'] = '}';
$template = new Template($config);
$this->template = new Template($config);
$_SERVER['SERVER_NAME'] = 'server_name';
$_GET['action'] = 'action';
$_POST['action'] = 'action';
$_COOKIE['name'] = 'name';
$_SESSION['action'] = ['name' => 'name'];
define('SITE_NAME', 'site_name');
$content = <<<EOF
{\$Think.SERVER.SERVER_NAME}<br/>
{\$Think.GET.action}<br/>
{\$Think.POST.action}<br/>
{\$Think.COOKIE.action}<br/>
{\$Think.COOKIE.action.name}<br/>
{\$Think.SESSION.action}<br/>
{\$Think.SESSION.action.name}<br/>
{\$Think.ENV.OS}<br/>
{\$Think.REQUEST.action}<br/>
{\$Think.CONST.SITE_NAME}<br/>
{\$Think.LANG.action}<br/>
{\$Think.CONFIG.action.name}<br/>
{\$Think.NOW}<br/>
{\$Think.VERSION}<br/>
{\$Think.LDELIM}<br/>
{\$Think.RDELIM}<br/>
{\$Think.SITE_NAME}<br/>
{\$Think.SITE.URL}
EOF;
$data = <<<EOF
<?php echo \\think\\Request::instance()->server('SERVER_NAME'); ?><br/>
<?php echo \\think\\Request::instance()->get('action'); ?><br/>
<?php echo \\think\\Request::instance()->post('action'); ?><br/>
<?php echo \\think\\Cookie::get('action'); ?><br/>
<?php echo \\think\\Cookie::get('action.name'); ?><br/>
<?php echo \\think\\Session::get('action'); ?><br/>
<?php echo \\think\\Session::get('action.name'); ?><br/>
<?php echo \\think\\Request::instance()->env('OS'); ?><br/>
<?php echo \\think\\Request::instance()->request('action'); ?><br/>
<?php echo SITE_NAME; ?><br/>
<?php echo \\think\\Lang::get('action'); ?><br/>
<?php echo \\think\\Config::get('action.name'); ?><br/>
<?php echo date('Y-m-d g:i a',time()); ?><br/>
<?php echo THINK_VERSION; ?><br/>
<?php echo '{'; ?><br/>
<?php echo '}'; ?><br/>
<?php echo SITE_NAME; ?><br/>
<?php echo ''; ?>
EOF;
$template->parse($content);
$this->assertEquals($data, $content);
$this->template->parse($content);
$this->assertEquals($expected, $content);
}
public function testFetch()
/**
* @expectedException \think\exception\TemplateNotFoundException
*/
public function testFetchWithEmptyTemplate()
{
$template = new Template();
$template->assign('name', 'name');
$this->template = new Template();
$this->template->fetch('Foo');
}
/**
* @dataProvider provideTestFetchWithNoCache
*/
public function testFetchWithNoCache($data, $expected)
{
$this->template = new Template();
$this->template->fetch($data['template'], $data['vars'], $data['config']);
$this->expectOutputString($expected);
}
public function testFetchWithCache()
{
$this->template = new Template();
$data = [
'name' => 'value'
];
$config = [
'strip_space' => true,
'view_path' => dirname(__FILE__) . DS,
'cache_id' => '__CACHE_ID__',
'cache_id' => 'TEST_FETCH_WITH_CACHE',
'display_cache' => true,
];
$data = ['name' => 'value'];
$template->layout('layout')->fetch('display', $data, $config);
$this->template->fetch(APP_PATH . 'views' . DS .'display.html', $data, $config);
$this->expectOutputString('value');
$this->assertEquals('value', Cache::get($config['cache_id']));
}
public function testDisplay()
{
$config['view_path'] = dirname(__FILE__) . DS;
$config['view_suffix'] = '.html';
$config['layout_on'] = true;
$config['layout_name'] = 'layout';
$template = new Template($config);
$files = ['extend' => 'extend', 'include' => 'include'];
$template->assign('files', $files);
$template->assign('user', ['name' => 'name', 'account' => 100]);
$template->assign('message', 'message');
$template->assign('info', ['value' => 'value']);
$config = [
'view_path' => APP_PATH . DS . 'views' . DS,
'view_suffix' => '.html',
'layout_on' => true,
'layout_name' => 'layout'
];
$this->template = new Template($config);
$this->template->assign('files', ['extend' => 'extend', 'include' => 'include']);
$this->template->assign('user', ['name' => 'name', 'account' => 100]);
$this->template->assign('message', 'message');
$this->template->assign('info', ['value' => 'value']);
$content = <<<EOF
{extend name="\$files.extend" /}
@@ -356,7 +185,7 @@ main
{/block}
{/block}
EOF;
$content2 = <<<EOF
$expected = <<<EOF
<nav>
header
<div id="wrap">
@@ -382,33 +211,205 @@ value:
php code</div>
</nav>
EOF;
$template->display($content);
$this->expectOutputString($content2);
// $template->parse($content);
// var_dump($content);
$this->template->display($content);
$this->expectOutputString($expected);
}
public function testVarAssign()
/**
* @dataProvider provideTestLayout
*/
public function testLayout($data, $expected)
{
$template = new Template();
$template->assign('name', 'value');
$value = $template->get('name');
$this->assertEquals('value', $value);
$this->template = new Template();
$this->template->layout($data['name'], $data['replace']);
$this->assertSame($expected['layout_on'], $this->template->config('layout_on'));
$this->assertSame($expected['layout_name'], $this->template->config('layout_name'));
$this->assertSame($expected['layout_item'], $this->template->config('layout_item'));
}
public function testVarGet()
public function testParseAttr()
{
$template = new Template();
$data = ['a' => 'a', 'b' => 'b'];
$template->assign($data);
$this->assertEquals($data, $template->get());
$attributes = $this->template->parseAttr("<name version='ThinkPHP' name=\"Gao\"></name>");
$this->assertSame(['version' => 'ThinkPHP', 'name' => 'Gao'], $attributes);
$attributes = $this->template->parseAttr("<name version='ThinkPHP' name=\"Gao\">TestCase</name>", 'version');
$this->assertSame('ThinkPHP', $attributes);
}
public function testIsCache()
{
$template = new Template(['cache_id' => '__CACHE_ID__', 'display_cache' => true]);
$this->assertTrue($template->isCache('__CACHE_ID__'));
$template->display_cache = false;
$this->assertTrue(!$template->isCache('__CACHE_ID__'));
$this->template = new Template();
$config = [
'cache_id' => rand(0, 10000) . rand(0, 10000) . time(),
'display_cache' => true
];
$this->assertFalse($this->template->isCache($config['cache_id']));
$this->template->fetch(APP_PATH . 'views' . DS .'display.html', [], $config);
$this->assertTrue($this->template->isCache($config['cache_id']));
}
public function provideTestParseWithVar()
{
return [
["{\$name.a.b}", "<?php echo \$name['a']['b']; ?>"],
["{\$name.a??'test'}", "<?php echo isset(\$name['a'])?\$name['a']:'test'; ?>"],
["{\$name.a?='test'}", "<?php if(!empty(\$name['a'])) echo 'test'; ?>"],
["{\$name.a?:'test'}", "<?php echo !empty(\$name['a'])?\$name['a']:'test'; ?>"],
["{\$name.a?\$name.b:'no'}", "<?php echo !empty(\$name['a'])?\$name['b']:'no'; ?>"],
["{\$name.a==\$name.b?='test'}", "<?php if(\$name['a']==\$name['b']) echo 'test'; ?>"],
["{\$name.a==\$name.b?'a':'b'}", "<?php echo \$name['a']==\$name['b']?'a':'b'; ?>"],
["{\$name.a|default='test'==\$name.b?'a':'b'}", "<?php echo (isset(\$name['a']) && (\$name['a'] !== '')?\$name['a']:'test')==\$name['b']?'a':'b'; ?>"],
["{\$name.a|trim==\$name.b?='eq'}", "<?php if(trim(\$name['a'])==\$name['b']) echo 'eq'; ?>"],
["{:ltrim(rtrim(\$name.a))}", "<?php echo ltrim(rtrim(\$name['a'])); ?>"],
["{~echo(trim(\$name.a))}", "<?php echo(trim(\$name['a'])); ?>"],
["{++\$name.a}", "<?php echo ++\$name['a']; ?>"],
["{/*\$name*/}", ""],
["{\$0a}", "{\$0a}"]
];
}
public function provideTestParseWithVarFunction()
{
return [
["{\$name.a.b|default='test'}", "<?php echo (isset(\$name['a']['b']) && (\$name['a']['b'] !== '')?\$name['a']['b']:'test'); ?>"],
["{\$create_time|date=\"y-m-d\",###}", "<?php echo date(\"y-m-d\",\$create_time); ?>"],
["{\$name}\n{\$name|trim|substr=0,3}", "<?php echo \$name; ?>\n<?php echo substr(trim(\$name),0,3); ?>"]
];
}
public function provideTestParseWithVarIdentify()
{
$config['tpl_begin'] = '<#';
$config['tpl_end'] = '#>';
$config['tpl_var_identify'] = '';
return [
[
"<#\$info.a??'test'#>",
"<?php echo ((is_array(\$info)?\$info['a']:\$info->a)) ? (is_array(\$info)?\$info['a']:\$info->a) : 'test'; ?>",
$config
],
[
"<#\$info.a?='test'#>",
"<?php if((is_array(\$info)?\$info['a']:\$info->a)) echo 'test'; ?>",
$config
],
[
"<#\$info.a==\$info.b?='test'#>",
"<?php if((is_array(\$info)?\$info['a']:\$info->a)==(is_array(\$info)?\$info['b']:\$info->b)) echo 'test'; ?>",
$config
],
[
"<#\$info.a|default='test'?'yes':'no'#>",
"<?php echo ((is_array(\$info)?\$info['a']:\$info->a) ?: 'test')?'yes':'no'; ?>",
$config
],
[
"{\$info2.b|trim?'yes':'no'}",
"<?php echo trim(\$info2->b)?'yes':'no'; ?>",
array_merge(['tpl_var_identify' => 'obj'])
]
];
}
public function provideTestParseWithThinkVar()
{
return [
["{\$Think.SERVER.SERVER_NAME}<br/>", "<?php echo \\think\\Request::instance()->server('SERVER_NAME'); ?><br/>"],
["{\$Think.GET.action}<br/>", "<?php echo \\think\\Request::instance()->get('action'); ?><br/>"],
["{\$Think.POST.action}<br/>", "<?php echo \\think\\Request::instance()->post('action'); ?><br/>"],
["{\$Think.COOKIE.action}<br/>", "<?php echo \\think\\Cookie::get('action'); ?><br/>"],
["{\$Think.COOKIE.action.name}<br/>", "<?php echo \\think\\Cookie::get('action.name'); ?><br/>"],
["{\$Think.SESSION.action}<br/>", "<?php echo \\think\\Session::get('action'); ?><br/>"],
["{\$Think.SESSION.action.name}<br/>", "<?php echo \\think\\Session::get('action.name'); ?><br/>"],
["{\$Think.ENV.OS}<br/>", "<?php echo \\think\\Request::instance()->env('OS'); ?><br/>"],
["{\$Think.REQUEST.action}<br/>", "<?php echo \\think\\Request::instance()->request('action'); ?><br/>"],
["{\$Think.CONST.THINK_VERSION}<br/>", "<?php echo THINK_VERSION; ?><br/>"],
["{\$Think.LANG.action}<br/>", "<?php echo \\think\\Lang::get('action'); ?><br/>"],
["{\$Think.CONFIG.action.name}<br/>", "<?php echo \\think\\Config::get('action.name'); ?><br/>"],
["{\$Think.NOW}<br/>", "<?php echo date('Y-m-d g:i a',time()); ?><br/>"],
["{\$Think.VERSION}<br/>", "<?php echo THINK_VERSION; ?><br/>"],
["{\$Think.LDELIM}<br/>", "<?php echo '{'; ?><br/>"],
["{\$Think.RDELIM}<br/>", "<?php echo '}'; ?><br/>"],
["{\$Think.THINK_VERSION}<br/>", "<?php echo THINK_VERSION; ?><br/>"],
["{\$Think.SITE.URL}", "<?php echo ''; ?>"]
];
}
public function provideTestFetchWithNoCache()
{
$provideData = [];
$this->template = [
'template' => APP_PATH . 'views' . DS .'display.html',
'vars' => [],
'config' => []
];
$expected = 'default';
$provideData[] = [$this->template, $expected];
$this->template = [
'template' => APP_PATH . 'views' . DS .'display.html',
'vars' => ['name' => 'ThinkPHP5'],
'config' => []
];
$expected = 'ThinkPHP5';
$provideData[] = [$this->template, $expected];
$this->template = [
'template' => 'views@display',
'vars' => [],
'config' => [
'view_suffix' => 'html'
]
];
$expected = 'default';
$provideData[] = [$this->template, $expected];
$this->template = [
'template' => 'views@/display',
'vars' => ['name' => 'ThinkPHP5'],
'config' => [
'view_suffix' => 'phtml'
]
];
$expected = 'ThinkPHP5';
$provideData[] = [$this->template, $expected];
$this->template = [
'template' => 'display',
'vars' => ['name' => 'ThinkPHP5'],
'config' => [
'view_suffix' => 'html',
'view_base' => APP_PATH . 'views' . DS
]
];
$expected = 'ThinkPHP5';
$provideData[] = [$this->template, $expected];
return $provideData;
}
public function provideTestLayout()
{
$provideData = [];
$data = ['name' => false, 'replace' => ''];
$expected = ['layout_on' => false, 'layout_name' => 'layout', 'layout_item' => '{__CONTENT__}'];
$provideData[] = [$data, $expected];
$data = ['name' => null, 'replace' => ''];
$expected = ['layout_on' => true, 'layout_name' => 'layout', 'layout_item' => '{__CONTENT__}'];
$provideData[] = [$data, $expected];
$data = ['name' => 'ThinkName', 'replace' => 'ThinkReplace'];
$expected = ['layout_on' => true, 'layout_name' => 'ThinkName', 'layout_item' => 'ThinkReplace'];
$provideData[] = [$data, $expected];
return $provideData;
}
}