diff --git a/README.md b/README.md index ad85157d4..5cd6d1597 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -## 简介 +**3.2版本已经过了维护生命周期,官方已经不再维护,请及时更新至5.0或者5.1版本** + +## 简介 ThinkPHP 是一个免费开源的,快速、简单的面向对象的 轻量级PHP开发框架 ,创立于2006年初,遵循Apache2开源协议发布,是为了敏捷WEB应用开发和简化企业应用开发而诞生的。ThinkPHP从诞生以来一直秉承简洁实用的设计原则,在保持出色的性能和至简的代码的同时,也注重易用性。并且拥有众多的原创功能和特性,在社区团队的积极参与下,在易用性、扩展性和性能方面不断优化和改进,已经成长为国内最领先和最具影响力的WEB应用开发框架,众多的典型案例确保可以稳定用于商业以及门户级的开发。 @@ -34,4 +36,4 @@ ThinkPHP从诞生以来一直秉承大道至简的开发理念,无论从底层 ## 商业友好的开源协议 -ThinkPHP遵循Apache2开源协议发布。Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再作为开源或商业软件发布。 \ No newline at end of file +ThinkPHP遵循Apache2开源协议发布。Apache Licence是著名的非盈利开源组织Apache采用的协议,该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再作为开源或商业软件发布。 diff --git a/ThinkPHP/Common/functions.php b/ThinkPHP/Common/functions.php index f91d29445..790f3a9e5 100644 --- a/ThinkPHP/Common/functions.php +++ b/ThinkPHP/Common/functions.php @@ -1068,7 +1068,17 @@ function U($url = '', $vars = '', $suffix = true, $domain = false) if (!empty($path)) { $var[$varModule] = implode($depr, $path); } else { - if (C('MULTI_MODULE')) { + // 如果为插件,自动转换路径 + if (CONTROLLER_PATH) { + $var[$varModule] = MODULE_NAME; + $varAddon = C('VAR_ADDON'); + if (MODULE_NAME != C('DEFAULT_MODULE')) { + $var[$varController] = MODULE_NAME; + } + + $vars = array_merge(array($varAddon => CONTROLLER_PATH), $vars); + + } elseif (C('MULTI_MODULE')) { if (MODULE_NAME != C('DEFAULT_MODULE') || !C('MODULE_ALLOW_LIST')) { $var[$varModule] = MODULE_NAME; } @@ -1080,14 +1090,14 @@ function U($url = '', $vars = '', $suffix = true, $domain = false) } } if (isset($var[$varModule])) { - $module = $var[$varModule]; + $module = defined('BIND_MODULE') && BIND_MODULE == $var[$varModule] ? '' : $var[$varModule]; unset($var[$varModule]); } } } - if (C('URL_MODEL') == 0) { + if (0 == C('URL_MODEL')) { // 普通模式URL转换 $url = __APP__ . '?' . C('VAR_MODULE') . "={$module}&" . http_build_query(array_reverse($var)); if ($urlCase) { @@ -1102,8 +1112,16 @@ function U($url = '', $vars = '', $suffix = true, $domain = false) if (isset($route)) { $url = __APP__ . '/' . rtrim($url, $depr); } else { - $module = (defined('BIND_MODULE') && BIND_MODULE == $module) ? '' : $module; - $url = __APP__ . '/' . ($module ? $module . MODULE_PATHINFO_DEPR : '') . implode($depr, array_reverse($var)); + $path = implode($depr, array_reverse($var)); + if (C('URL_ROUTER_ON')) { + $url = Think\Route::reverse($path, $vars, $depr, $suffix); + if (!$url) { + $url = $path; + } + } else { + $url = $path; + } + $url = __APP__ . '/' . ($module ? $module . MODULE_PATHINFO_DEPR : '') . $url; } if ($urlCase) { $url = strtolower($url); @@ -1114,7 +1132,6 @@ function U($url = '', $vars = '', $suffix = true, $domain = false) if ('' !== trim($val)) { $url .= $depr . $var . $depr . urlencode($val); } - } } if ($suffix) { @@ -1127,7 +1144,7 @@ function U($url = '', $vars = '', $suffix = true, $domain = false) } } } - if (isset($anchor)) { + if (!empty($anchor)) { $url .= '#' . $anchor; } if ($domain) { @@ -1732,7 +1749,7 @@ function think_filter(&$value) // TODO 其他安全过滤 // 过滤查询特殊字符 - if (preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i', $value)) { + if (preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN|BIND)$/i', $value)) { $value .= ' '; } } diff --git a/ThinkPHP/Conf/convention.php b/ThinkPHP/Conf/convention.php index eedfd7ffb..8da40fcca 100644 --- a/ThinkPHP/Conf/convention.php +++ b/ThinkPHP/Conf/convention.php @@ -96,7 +96,7 @@ /* SESSION设置 */ 'SESSION_AUTO_START' => true, // 是否自动开启Session 'SESSION_OPTIONS' => array(), // session 配置数组 支持type name id path expire domain 等参数 - 'SESSION_TYPE' => '', // session hander类型 默认无需设置 除非扩展了session hander驱动 + 'SESSION_TYPE' => '', // session handler类型 默认无需设置 除非扩展了session handler驱动 'SESSION_PREFIX' => '', // session 前缀 //'VAR_SESSION_ID' => 'session_id', //sessionID的提交变量 diff --git a/ThinkPHP/Library/Behavior/BuildLiteBehavior.class.php b/ThinkPHP/Library/Behavior/BuildLiteBehavior.class.php index 294c27d38..145b4f747 100644 --- a/ThinkPHP/Library/Behavior/BuildLiteBehavior.class.php +++ b/ThinkPHP/Library/Behavior/BuildLiteBehavior.class.php @@ -18,7 +18,7 @@ class BuildLiteBehavior { public function run(&$params) { - if (!defined('BUILD_LITE_FILE')) { + if (!defined('BUILD_LITE_FILE') || BUILD_LITE_FILE == false) { return; } diff --git a/ThinkPHP/Library/Org/Net/Http.class.php b/ThinkPHP/Library/Org/Net/Http.class.php index 94b509715..d6a6d8989 100644 --- a/ThinkPHP/Library/Org/Net/Http.class.php +++ b/ThinkPHP/Library/Org/Net/Http.class.php @@ -164,7 +164,7 @@ public static function download($filename, $showname = '', $content = '', $expir if (empty($showname)) { $showname = $filename; } - $showname = basename($showname); + $showname = self::get_basename($showname);; if (!empty($filename)) { $finfo = new \finfo(FILEINFO_MIME); $type = $finfo->file($filename); @@ -182,6 +182,9 @@ public static function download($filename, $showname = '', $content = '', $expir header("Content-type: " . $type); header('Content-Encoding: none'); header("Content-Transfer-Encoding: binary"); + // 清空文件的头部信息,解决文件下载无法打开问题 + ob_clean(); // 清空缓冲区 + flush(); // 刷新输出缓冲 if ('' == $content) { readfile($filename); } else { @@ -190,6 +193,14 @@ public static function download($filename, $showname = '', $content = '', $expir exit(); } + /** + * 获取文件的名称,兼容中文名 + * @return string + */ + static public function get_basename($filename){ + return preg_replace('/^.+[\\\\\\/]/', '', $filename); + } + /** * 显示HTTP Header 信息 * @return string diff --git a/ThinkPHP/Library/Org/Net/IpLocation.class.php b/ThinkPHP/Library/Org/Net/IpLocation.class.php index bf7c70347..8d8562e63 100644 --- a/ThinkPHP/Library/Org/Net/IpLocation.class.php +++ b/ThinkPHP/Library/Org/Net/IpLocation.class.php @@ -54,7 +54,10 @@ class IpLocation public function __construct($filename = "UTFWry.dat") { $this->fp = 0; - if (($this->fp = fopen(dirname(__FILE__) . '/' . $filename, 'rb')) !== false) { + if(!is_file($filename)) { + $filename = dirname(__FILE__) . '/' . $filename; + } + if (($this->fp = fopen($filename, 'rb')) !== false) { $this->firstip = $this->getlong(); $this->lastip = $this->getlong(); $this->totalip = ($this->lastip - $this->firstip) / 7; diff --git a/ThinkPHP/Library/Think/App.class.php b/ThinkPHP/Library/Think/App.class.php index 60e09d0a2..d9a87663f 100644 --- a/ThinkPHP/Library/Think/App.class.php +++ b/ThinkPHP/Library/Think/App.class.php @@ -163,8 +163,8 @@ public static function invokeAction($module, $action) } } // 开启绑定参数过滤机制 - if (C('URL_PARAMS_SAFE')) { - $filters = C('URL_PARAMS_FILTER') ?: C('DEFAULT_FILTER'); + if (C('URL_PARAMS_FILTER')) { + $filters = C('URL_PARAMS_FILTER_TYPE') ?: C('DEFAULT_FILTER'); if ($filters) { $filters = explode(',', $filters); foreach ($filters as $filter) { diff --git a/ThinkPHP/Library/Think/Cache/Driver/Memcached.class.php b/ThinkPHP/Library/Think/Cache/Driver/Memcached.class.php index 5cf08faf1..82b38a5dd 100644 --- a/ThinkPHP/Library/Think/Cache/Driver/Memcached.class.php +++ b/ThinkPHP/Library/Think/Cache/Driver/Memcached.class.php @@ -72,7 +72,8 @@ public function set($name, $value, $expire = null) $expire = $this->options['expire']; } $name = $this->options['prefix'] . $name; - if ($this->handler->set($name, $value, time() + $expire)) { + $expire = $expire == 0 ? 0 : time() + $expire; + if ($this->handler->set($name, $value, $expire)) { if ($this->options['length'] > 0) { // 记录缓存队列 $this->queue($name); diff --git a/ThinkPHP/Library/Think/Controller/RestController.class.php b/ThinkPHP/Library/Think/Controller/RestController.class.php index 63233ebc8..99298f2e5 100644 --- a/ThinkPHP/Library/Think/Controller/RestController.class.php +++ b/ThinkPHP/Library/Think/Controller/RestController.class.php @@ -104,6 +104,7 @@ public function __call($method, $args) protected function getAcceptType() { $type = array( + 'html' => 'text/html,application/xhtml+xml,*/*', 'xml' => 'application/xml,text/xml,application/x-xml', 'json' => 'application/json,text/x-json,application/jsonrequest,text/json', 'js' => 'text/javascript,application/javascript,application/x-javascript', @@ -117,15 +118,15 @@ protected function getAcceptType() 'jpg' => 'image/jpg,image/jpeg,image/pjpeg', 'gif' => 'image/gif', 'csv' => 'text/csv', - 'html' => 'text/html,application/xhtml+xml,*/*', ); foreach ($type as $key => $val) { $array = explode(',', $val); foreach ($array as $k => $v) { - if (stristr($_SERVER['HTTP_ACCEPT'], $v)) { - return $key; - } + if(array_key_exists('HTTP_ACCEPT',$_SERVER)) + if (stristr($_SERVER['HTTP_ACCEPT'], $v)) { + return $key; + } } } return false; @@ -203,9 +204,14 @@ protected function encodeData($data, $type = '') return ''; } - if ('json' == $type) { + if('json' == $type) { // 返回JSON数据格式到客户端 包含状态信息 - $data = json_encode($data); + if(version_compare(PHP_VERSION,'5.4.0','<')) { + $this->arrayRecursive($data, 'urlencode', true); + $data = urldecode(json_encode($data)); + } else { + $data = json_encode($data,JSON_UNESCAPED_UNICODE); + } } elseif ('xml' == $type) { // 返回xml格式数据 $data = xml_encode($data); @@ -217,6 +223,39 @@ protected function encodeData($data, $type = '') return $data; } + /************************************************************** + * + * 使用特定function对数组中所有元素做处理 + * @param string|array &$array 要处理的字符串或者数组 + * @param string $function 要执行的函数 + * @return boolean $apply_to_keys_also 是否也应用到key上 + * @access protected + * + *************************************************************/ + protected function arrayRecursive(&$array, $function, $apply_to_keys_also = false) + { + static $recursive_counter = 0; + if (++$recursive_counter > 1000) { + die('possible deep recursion attack'); + } + foreach ($array as $key => $value) { + if (is_array($value)) { + $this->arrayRecursive($array[$key], $function, $apply_to_keys_also); + } elseif(is_string($value)) { + $array[$key] = $function($value); + } + + if ($apply_to_keys_also && is_string($key)) { + $new_key = $function($key); + if ($new_key != $key) { + $array[$new_key] = $array[$key]; + unset($array[$key]); + } + } + } + $recursive_counter--; + } + /** * 设置页面输出的CONTENT_TYPE和编码 * @access public diff --git a/ThinkPHP/Library/Think/Db.class.php b/ThinkPHP/Library/Think/Db.class.php index 39f12b965..0a7117600 100644 --- a/ThinkPHP/Library/Think/Db.class.php +++ b/ThinkPHP/Library/Think/Db.class.php @@ -125,7 +125,7 @@ private static function parseDsn($dsnStr) 'password' => isset($info['pass']) ? $info['pass'] : '', 'hostname' => isset($info['host']) ? $info['host'] : '', 'hostport' => isset($info['port']) ? $info['port'] : '', - 'database' => isset($info['path']) ? substr($info['path'], 1) : '', + 'database' => isset($info['path']) ? ltrim($info['path'], '/') : '', 'charset' => isset($info['fragment']) ? $info['fragment'] : 'utf8', ); diff --git a/ThinkPHP/Library/Think/Db/Driver.class.php b/ThinkPHP/Library/Think/Db/Driver.class.php index 09b251fbd..a5048fe8f 100644 --- a/ThinkPHP/Library/Think/Db/Driver.class.php +++ b/ThinkPHP/Library/Think/Db/Driver.class.php @@ -28,6 +28,8 @@ abstract class Driver protected $lastInsID = null; // 返回或者影响记录数 protected $numRows = 0; + // 事物操作PDO实例 + protected $transPDO = null; // 事务指令数 protected $transTimes = 0; // 错误信息 @@ -276,6 +278,8 @@ public function startTrans() //数据rollback 支持 if (0 == $this->transTimes) { + // 记录当前操作PDO + $this->transPdo = $this->_linkID; $this->_linkID->beginTransaction(); } $this->transTimes++; @@ -289,13 +293,17 @@ public function startTrans() */ public function commit() { - if ($this->transTimes > 0) { + if (1 == $this->transTimes) { + // 由嵌套事物的最外层进行提交 $result = $this->_linkID->commit(); $this->transTimes = 0; + $this->transPdo = null; if (!$result) { $this->error(); return false; } + } else { + $this->transTimes = $this->transTimes <= 0 ? 0 : $this->transTimes - 1; } return true; } @@ -310,6 +318,7 @@ public function rollback() if ($this->transTimes > 0) { $result = $this->_linkID->rollback(); $this->transTimes = 0; + $this->transPdo = null; if (!$result) { $this->error(); return false; @@ -407,18 +416,18 @@ protected function parseLock($lock = false) protected function parseSet($data) { foreach ($data as $key => $val) { - if (is_array($val) && 'exp' == $val[0]) { + if (isset($val[0]) && 'exp' == $val[0]) { $set[] = $this->parseKey($key) . '=' . $val[1]; } elseif (is_null($val)) { $set[] = $this->parseKey($key) . '=NULL'; } elseif (is_scalar($val)) { // 过滤非标量数据 if (0 === strpos($val, ':') && in_array($val, array_keys($this->bind))) { - $set[] = $this->parseKey($key) . '=' . $this->escapeString($val); + $set[] = $this->parseKey($key) . '=' . $val; } else { $name = count($this->bind); - $set[] = $this->parseKey($key) . '=:' . $name; - $this->bindParam($name, $val); + $set[] = $this->parseKey($key) . '=:' . $key . '_' . $name; + $this->bindParam($key . '_' . $name, $val); } } } @@ -438,12 +447,13 @@ protected function bindParam($name, $value) } /** - * 字段名分析 - * @access protected + * 字段和表名处理 + * @access public * @param string $key + * @param bool $strict * @return string */ - protected function parseKey(&$key) + public function parseKey($key, $strict = false) { return $key; } @@ -522,8 +532,7 @@ protected function parseTable($tables) } $tables = $array; } elseif (is_string($tables)) { - $tables = explode(',', $tables); - array_walk($tables, array(&$this, 'parseKey')); + $tables = array_map(array($this, 'parseKey'), explode(',', $tables)); } return implode(',', $tables); } @@ -749,29 +758,34 @@ protected function parseOrder($order) return ''; } $array = array(); + if (is_string($order) && '[RAND]' != $order) { + $order = array_map('trim', explode(',', $order)); + } + if (is_array($order)) { foreach ($order as $key => $val) { if (is_numeric($key)) { - if (false === strpos($val, '(')) { - $array[] = $this->parseKey($val); - } + list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' '); } else { - $sort = in_array(strtolower($val), array('asc', 'desc')) ? ' ' . $val : ''; - $array[] = $this->parseKey($key) . $sort; + $sort = $val; + } + + if (preg_match('/^[\w\.]+$/', $key)) { + $sort = strtoupper($sort); + $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : ''; + if (strpos($key, '.')) { + list($alias, $key) = explode('.', $key); + $array[] = $this->parseKey($alias, true) . '.' . $this->parseKey($key, true) . $sort; + } else { + $array[] = $this->parseKey($key, true) . $sort; + } } } } elseif ('[RAND]' == $order) { // 随机排序 $array[] = $this->parseRand(); - } else { - foreach (explode(',', $order) as $val) { - if (preg_match('/\s+(ASC|DESC)$/i', rtrim($val), $match, PREG_OFFSET_CAPTURE)) { - $array[] = $this->parseKey(ltrim(substr($val, 0, $match[0][1]))) . ' ' . $match[1][0]; - } elseif (false === strpos($val, '(')) { - $array[] = $this->parseKey($val); - } - } } + $order = implode(',', $array); return !empty($order) ? ' ORDER BY ' . $order : ''; } @@ -899,7 +913,7 @@ public function insert($data, $options = array(), $replace = false) $this->model = $options['model']; $this->parseBind(!empty($options['bind']) ? $options['bind'] : array()); foreach ($data as $key => $val) { - if (is_array($val) && 'exp' == $val[0]) { + if (isset($val[0]) && 'exp' == $val[0]) { $fields[] = $this->parseKey($key); $values[] = $val[1]; } elseif (is_null($val)) { @@ -909,11 +923,11 @@ public function insert($data, $options = array(), $replace = false) // 过滤非标量数据 $fields[] = $this->parseKey($key); if (0 === strpos($val, ':') && in_array($val, array_keys($this->bind))) { - $values[] = $this->parseValue($val); + $values[] = $val; } else { $name = count($this->bind); - $values[] = ':' . $name; - $this->bindParam($name, $val); + $values[] = ':' . $key . '_' . $name; + $this->bindParam($key . '_' . $name, $val); } } } @@ -951,11 +965,11 @@ public function insertAll($dataSet, $options = array(), $replace = false) $value[] = 'NULL'; } elseif (is_scalar($val)) { if (0 === strpos($val, ':') && in_array($val, array_keys($this->bind))) { - $value[] = $this->parseValue($val); + $value[] = $val; } else { $name = count($this->bind); - $value[] = ':' . $name; - $this->bindParam($name, $val); + $value[] = ':' . $key . '_' . $name; + $this->bindParam($key . '_' . $name, $val); } } } @@ -982,8 +996,8 @@ public function selectInsert($fields, $table, $options = array()) $fields = explode(',', $fields); } - array_walk($fields, array($this, 'parseKey')); - $sql = 'INSERT INTO ' . $this->parseTable($table) . ' (' . implode(',', $fields) . ') '; + $fields = array_map(array($this, 'parseKey'), $fields); + $sql = 'INSERT INTO ' . $this->parseTable($table) . ' (' . implode(',', $fields) . ') '; $sql .= $this->buildSelectSql($options); return $this->execute($sql, !empty($options['fetch_sql']) ? true : false); } @@ -1189,6 +1203,11 @@ protected function debug($start) */ protected function initConnect($master = true) { + // 开启事物时用同一个连接进行操作 + if ($this->transPDO) { + return $this->transPDO; + } + if (!empty($this->config['deploy'])) // 采用分布式数据库 { diff --git a/ThinkPHP/Library/Think/Db/Driver/Mongo.class.php b/ThinkPHP/Library/Think/Db/Driver/Mongo.class.php index 36e0c64b3..5b7cb6dd9 100644 --- a/ThinkPHP/Library/Think/Db/Driver/Mongo.class.php +++ b/ThinkPHP/Library/Think/Db/Driver/Mongo.class.php @@ -270,8 +270,11 @@ public function insertAll($dataList, $options = array()) * @param string $pk 主键名 * @return integer */ - public function getMongoNextId($pk) + public function getMongoNextId($pk,$options=array()) { + if (isset($options['table'])) { + $this->switchCollection($options['table']); + } if ($this->config['debug']) { $this->queryStr = $this->_dbName . '.' . $this->_collectionName . '.find({},{' . $pk . ':1}).sort({' . $pk . ':-1}).limit(1)'; } @@ -517,8 +520,12 @@ public function group($keys, $initial, $reduce, $options = array()) } try { $this->debug(true); - $option = array('condition' => $options['condition'], 'finalize' => $options['finalize'], 'maxTimeMS' => $options['maxTimeMS']); - $group = $this->_collection->group($keys, $initial, $reduce, $options); + + $option = array(); + isset($options['condition'])&&$option['condition']=$options['condition']; + isset($options['finalize'])&&$option['finalize']=$options['condition']; + isset($options['maxTimeMS'])&&$option['maxTimeMS']=$options['condition']; + $group = $this->_collection->group($keys,$initial,$reduce,$option); $this->debug(false); if ($cache && $group['ok']) { @@ -622,7 +629,7 @@ protected function parseSet($data) if (is_array($val)) { switch ($val[0]) { case 'inc': - $result['$inc'][$key] = (int) $val[1]; + $result['$inc'][$key] = (float) $val[1]; break; case 'set': case 'unset': diff --git a/ThinkPHP/Library/Think/Db/Driver/Mysql.class.php b/ThinkPHP/Library/Think/Db/Driver/Mysql.class.php index 975d6c933..e67575385 100644 --- a/ThinkPHP/Library/Think/Db/Driver/Mysql.class.php +++ b/ThinkPHP/Library/Think/Db/Driver/Mysql.class.php @@ -94,14 +94,24 @@ public function getTables($dbName = '') /** * 字段和表名处理 - * @access protected + * @access public * @param string $key + * @param bool $strict * @return string */ - protected function parseKey(&$key) + public function parseKey($key, $strict = false) { + if (is_int($key)) { + return $key; + } + $key = trim($key); - if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) { + + if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) { + E('not support data:' . $key); + } + + if ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) { $key = '`' . $key . '`'; } return $key; @@ -192,7 +202,7 @@ protected function parseDuplicate($duplicate) $val = array('value', $val); } - if (!isset($val[1])) { + if (!isset($val[1]) && !is_null($val[1])) { continue; } diff --git a/ThinkPHP/Library/Think/Db/Driver/Sqlsrv.class.php b/ThinkPHP/Library/Think/Db/Driver/Sqlsrv.class.php index fadf0a2c9..5d4e52cdc 100644 --- a/ThinkPHP/Library/Think/Db/Driver/Sqlsrv.class.php +++ b/ThinkPHP/Library/Think/Db/Driver/Sqlsrv.class.php @@ -104,15 +104,21 @@ protected function parseOrder($order) } /** - * 字段名分析 - * @access protected + * 字段和表名处理 + * @access public * @param string $key + * @param bool $strict * @return string */ - protected function parseKey(&$key) + public function parseKey($key, $strict = false) { $key = trim($key); - if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) { + + if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) { + E('not support data:' . $key); + } + + if ($strict || (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) { $key = '[' . $key . ']'; } return $key; diff --git a/ThinkPHP/Library/Think/Db/Lite.class.php b/ThinkPHP/Library/Think/Db/Lite.class.php index ad6ef16ed..8afc496c6 100644 --- a/ThinkPHP/Library/Think/Db/Lite.class.php +++ b/ThinkPHP/Library/Think/Db/Lite.class.php @@ -28,6 +28,8 @@ class Lite protected $lastInsID = null; // 返回或者影响记录数 protected $numRows = 0; + // 事物操作PDO实例 + protected $transPDO = null; // 事务指令数 protected $transTimes = 0; // 错误信息 @@ -248,6 +250,8 @@ public function startTrans() //数据rollback 支持 if (0 == $this->transTimes) { + // 记录当前操作PDO + $this->transPdo = $this->_linkID; $this->_linkID->beginTransaction(); } $this->transTimes++; @@ -261,13 +265,17 @@ public function startTrans() */ public function commit() { - if ($this->transTimes > 0) { - $result = $this->_linkID->commit(); + if ($this->transTimes == 1) { + // 由嵌套事物的最外层进行提交 + $result = $this->_linkID->commit(); $this->transTimes = 0; + $this->transPdo = null; if (!$result) { $this->error(); return false; } + } else { + $this->transTimes--; } return true; } @@ -280,8 +288,9 @@ public function commit() public function rollback() { if ($this->transTimes > 0) { - $result = $this->_linkID->rollback(); + $result = $this->_linkID->rollback(); $this->transTimes = 0; + $this->transPdo = null; if (!$result) { $this->error(); return false; @@ -353,7 +362,7 @@ public function error() // 记录错误日志 trace($this->error, '', 'ERR'); if ($this->config['debug']) { -// 开启数据库调试模式 + // 开启数据库调试模式 E($this->error); } else { return $this->error; @@ -421,7 +430,7 @@ public function setModel($model) protected function debug($start) { if ($this->config['debug']) { -// 开启数据库调试模式 + // 开启数据库调试模式 if ($start) { G('queryStartTime'); } else { @@ -442,6 +451,11 @@ protected function debug($start) */ protected function initConnect($master = true) { + // 开启事物时用同一个连接进行操作 + if ($this->transPDO) { + return $this->transPDO; + } + if (!empty($this->config['deploy'])) // 采用分布式数据库 { diff --git a/ThinkPHP/Library/Think/Dispatcher.class.php b/ThinkPHP/Library/Think/Dispatcher.class.php index ffb5b901f..9ef34c342 100644 --- a/ThinkPHP/Library/Think/Dispatcher.class.php +++ b/ThinkPHP/Library/Think/Dispatcher.class.php @@ -14,7 +14,6 @@ * ThinkPHP内置的Dispatcher类 * 完成URL解析、路由和调度 */ - class Dispatcher { @@ -25,31 +24,23 @@ class Dispatcher */ public static function dispatch() { - $varPath = C('VAR_PATHINFO'); - $varAddon = C('VAR_ADDON'); - $varModule = C('VAR_MODULE'); - $varController = C('VAR_CONTROLLER'); - $varAction = C('VAR_ACTION'); - $urlCase = C('URL_CASE_INSENSITIVE'); - if (isset($_GET[$varPath])) { - // 判断URL里面是否有兼容模式参数 + $varPath = C('VAR_PATHINFO'); + $urlCase = C('URL_CASE_INSENSITIVE'); + if (isset($_GET[$varPath])) { // 判断URL里面是否有兼容模式参数 $_SERVER['PATH_INFO'] = $_GET[$varPath]; unset($_GET[$varPath]); - } elseif (IS_CLI) { - // CLI模式下 index.php module/controller/action/params/... + } elseif (IS_CLI) { // CLI模式下 index.php module/controller/action/params/... $_SERVER['PATH_INFO'] = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : ''; } // 开启子域名部署 if (C('APP_SUB_DOMAIN_DEPLOY')) { $rules = C('APP_SUB_DOMAIN_RULES'); - if (isset($rules[$_SERVER['HTTP_HOST']])) { - // 完整域名或者IP配置 + if (isset($rules[$_SERVER['HTTP_HOST']])) { // 完整域名或者IP配置 define('APP_DOMAIN', $_SERVER['HTTP_HOST']); // 当前完整域名 $rule = $rules[APP_DOMAIN]; } else { - if (strpos(C('APP_DOMAIN_SUFFIX'), '.')) { - // com.cn net.cn + if (strpos(C('APP_DOMAIN_SUFFIX'), '.')) { // com.cn net.cn $domain = array_slice(explode('.', $_SERVER['HTTP_HOST']), 0, -3); } else { $domain = array_slice(explode('.', $_SERVER['HTTP_HOST']), 0, -2); @@ -67,11 +58,11 @@ public static function dispatch() $rule = $rules[$subDomain]; } elseif (isset($rules['*.' . $domain2]) && !empty($domain3)) { // 泛三级域名 - $rule = $rules['*.' . $domain2]; + $rule = $rules['*.' . $domain2]; $panDomain = $domain3; } elseif (isset($rules['*']) && !empty($domain2) && 'www' != $domain2) { // 泛二级域名 - $rule = $rules['*']; + $rule = $rules['*']; $panDomain = $domain2; } } @@ -84,11 +75,11 @@ public static function dispatch() } $array = explode('/', $rule); // 模块绑定 - defined('BIND_MODULE') or define('BIND_MODULE', array_shift($array)); + define('BIND_MODULE', array_shift($array)); // 控制器绑定 if (!empty($array)) { $controller = array_shift($array); - if ($controller && !defined('BIND_MODULE')) { + if ($controller) { define('BIND_CONTROLLER', $controller); } } @@ -116,7 +107,7 @@ public static function dispatch() break; } elseif (!empty($_SERVER[$type])) { $_SERVER['PATH_INFO'] = (0 === strpos($_SERVER[$type], $_SERVER['SCRIPT_NAME'])) ? - substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type]; + substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type]; break; } } @@ -129,29 +120,31 @@ public static function dispatch() $_SERVER['PATH_INFO'] = ''; define('__INFO__', ''); define('__EXT__', ''); + $paths = array(); } else { - define('__INFO__', trim($_SERVER['PATH_INFO'], '/')); // URL后缀 define('__EXT__', strtolower(pathinfo($_SERVER['PATH_INFO'], PATHINFO_EXTENSION))); - $_SERVER['PATH_INFO'] = __INFO__; - if (!defined('BIND_MODULE') && (!C('URL_ROUTER_ON') || !Route::check())) { - if (__INFO__ && C('MULTI_MODULE')) { - // 获取模块名 - $paths = explode($depr, __INFO__, 2); - $_GET[$varModule] = preg_replace('/\.' . __EXT__ . '$/i', '', $paths[0]); - $_SERVER['PATH_INFO'] = isset($paths[1]) ? $paths[1] : ''; + // 检查禁止访问的URL后缀 + if ($denySuffix = C('URL_DENY_SUFFIX')) { + if (in_array(__EXT__, explode('|', strtolower(str_replace('.', '', $denySuffix))))) { + send_http_status(404); + exit; } } + define('__INFO__', trim($_SERVER['PATH_INFO'], '/')); + // 去除URL后缀 + $_SERVER['PATH_INFO'] = preg_replace('/\.' . __EXT__ . '$/i', '', __INFO__); + $paths = explode($depr, trim($_SERVER['PATH_INFO'], $depr)); } // URL常量 define('__SELF__', strip_tags($_SERVER[C('URL_REQUEST_URI')])); // 获取模块名称 - define('MODULE_NAME', defined('BIND_MODULE') ? BIND_MODULE : self::getModule($varModule)); + define('MODULE_NAME', self::getModule($paths)); // 检测模块是否存在 - if (MODULE_NAME && !in_array_case(MODULE_NAME, C('MODULE_DENY_LIST')) && is_dir(APP_PATH . MODULE_NAME)) { + if (MODULE_NAME && is_dir(APP_PATH . MODULE_NAME)) { // 定义当前模块路径 define('MODULE_PATH', APP_PATH . MODULE_NAME . '/'); // 定义当前模块的模版缓存路径 @@ -159,7 +152,7 @@ public static function dispatch() // 定义当前模块的日志目录 C('LOG_PATH', realpath(LOG_PATH) . '/' . MODULE_NAME . '/'); - // 模块检测 + // 模块配置文件开始载入检测位 Hook::listen('module_check'); // 加载模块配置文件 @@ -188,13 +181,13 @@ public static function dispatch() } // 加载模块函数文件 - if (is_file(MODULE_PATH . 'Common/function.php')) { + if (is_file(MODULE_PATH . 'Common/function.php')) include MODULE_PATH . 'Common/function.php'; - } - - $urlCase = C('URL_CASE_INSENSITIVE'); // 加载模块的扩展配置文件 load_ext_file(MODULE_PATH); + + // 模块配置文件加载完成检测位 + Hook::listen('module_config'); } else { E(L('_MODULE_NOT_EXIST_') . ':' . MODULE_NAME); } @@ -220,51 +213,25 @@ public static function dispatch() // 模块URL地址 $moduleName = defined('MODULE_ALIAS') ? MODULE_ALIAS : MODULE_NAME; define('__MODULE__', (defined('BIND_MODULE') || !C('MULTI_MODULE')) ? __APP__ : __APP__ . '/' . ($urlCase ? strtolower($moduleName) : $moduleName)); + // 获取控制器和操作名 + define('CONTROLLER_NAME', self::getController($paths, $urlCase)); + define('ACTION_NAME', self::getAction($paths, $urlCase)); - if ('' != $_SERVER['PATH_INFO'] && (!C('URL_ROUTER_ON') || !Route::check())) { - // 检测路由规则 如果没有则按默认规则调度URL - Hook::listen('path_info'); - // 检查禁止访问的URL后缀 - if (C('URL_DENY_SUFFIX') && preg_match('/\.(' . trim(C('URL_DENY_SUFFIX'), '.') . ')$/i', $_SERVER['PATH_INFO'])) { - send_http_status(404); - exit; - } - - // 去除URL后缀 - $_SERVER['PATH_INFO'] = preg_replace(C('URL_HTML_SUFFIX') ? '/\.(' . trim(C('URL_HTML_SUFFIX'), '.') . ')$/i' : '/\.' . __EXT__ . '$/i', '', $_SERVER['PATH_INFO']); - - $depr = C('URL_PATHINFO_DEPR'); - $paths = explode($depr, trim($_SERVER['PATH_INFO'], $depr)); - - if (!defined('BIND_CONTROLLER')) { - // 获取控制器 - if (C('CONTROLLER_LEVEL') > 1) { - // 控制器层次 - $_GET[$varController] = implode('/', array_slice($paths, 0, C('CONTROLLER_LEVEL'))); - $paths = array_slice($paths, C('CONTROLLER_LEVEL')); - } else { - $_GET[$varController] = array_shift($paths); - } - } - // 获取操作 - if (!defined('BIND_ACTION')) { - $_GET[$varAction] = array_shift($paths); - } + if ($paths) { // 解析剩余的URL参数 $var = array(); if (C('URL_PARAMS_BIND') && 1 == C('URL_PARAMS_BIND_TYPE')) { // URL参数按顺序绑定变量 $var = $paths; } else { - preg_replace_callback('/(\w+)\/([^\/]+)/', function ($match) use (&$var) {$var[$match[1]] = strip_tags($match[2]);}, implode('/', $paths)); + preg_replace_callback('/(\w+)\/([^\/]+)/', function ($match) use (&$var) { + $var[$match[1]] = strip_tags($match[2]); + }, implode('/', $paths)); } $_GET = array_merge($var, $_GET); } // 获取控制器的命名空间(路径) - define('CONTROLLER_PATH', self::getSpace($varAddon, $urlCase)); - // 获取控制器和操作名 - define('CONTROLLER_NAME', defined('BIND_CONTROLLER') ? BIND_CONTROLLER : self::getController($varController, $urlCase)); - define('ACTION_NAME', defined('BIND_ACTION') ? BIND_ACTION : self::getAction($varAction, $urlCase)); + define('CONTROLLER_PATH', self::getSpace($urlCase)); // 当前控制器的UR地址 $controllerName = defined('CONTROLLER_ALIAS') ? CONTROLLER_ALIAS : CONTROLLER_NAME; @@ -274,14 +241,17 @@ public static function dispatch() define('__ACTION__', __CONTROLLER__ . $depr . (defined('ACTION_ALIAS') ? ACTION_ALIAS : ACTION_NAME)); //保证$_REQUEST正常取值 - $_REQUEST = array_merge($_POST, $_GET, $_COOKIE); // -- 加了$_COOKIE. 保证哦.. + $_REQUEST = array_merge($_POST, $_GET); } /** * 获得控制器的命名空间路径 便于插件机制访问 + * @param boolean $urlCase 是否转换成小写 + * @return string */ - private static function getSpace($var, $urlCase) + private static function getSpace($urlCase) { + $var = C('VAR_ADDON'); $space = !empty($_GET[$var]) ? strip_tags($_GET[$var]) : ''; unset($_GET[$var]); return $space; @@ -289,11 +259,34 @@ private static function getSpace($var, $urlCase) /** * 获得实际的控制器名称 + * @param array $paths path_info数组 + * @param boolean $urlCase 是否转换成小写 + * @return string */ - private static function getController($var, $urlCase) + private static function getController(&$paths, $urlCase) { - $controller = (!empty($_GET[$var]) ? $_GET[$var] : C('DEFAULT_CONTROLLER')); - unset($_GET[$var]); + if (defined('BIND_CONTROLLER')) { + return BIND_CONTROLLER; + } else { + if ($paths) { + // PATH_INFO检测标签位 + Hook::listen('path_info'); + if (C('CONTROLLER_LEVEL') > 1) {// 控制器层次 + $controller = implode('/', array_slice($paths, 0, C('CONTROLLER_LEVEL'))); + $paths = array_slice($paths, C('CONTROLLER_LEVEL')); + } else { + $controller = array_shift($paths); + } + } else { + $var = C('VAR_CONTROLLER'); + if (!empty($_GET[$var])) { + $controller = $_GET[$var]; + unset($_GET[$var]); + } else { + $controller = C('DEFAULT_CONTROLLER'); + } + } + } if ($maps = C('URL_CONTROLLER_MAP')) { if (isset($maps[strtolower($controller)])) { // 记录当前别名 @@ -315,13 +308,30 @@ private static function getController($var, $urlCase) /** * 获得实际的操作名称 + * @param array $paths path_info数组 + * @param boolean $urlCase 是否转换成小写 + * @return string */ - private static function getAction($var, $urlCase) + private static function getAction(&$paths, $urlCase) { - $action = !empty($_POST[$var]) ? - $_POST[$var] : - (!empty($_GET[$var]) ? $_GET[$var] : C('DEFAULT_ACTION')); - unset($_POST[$var], $_GET[$var]); + if (defined('BIND_ACTION')) { + return BIND_ACTION; + } else { + if ($paths) { + $action = array_shift($paths); + } else { + $var = C('VAR_ACTION'); + if (!empty($_GET[$var])) { + $action = $_GET[$var]; + unset($_GET[$var]); + } elseif (!empty($_POST[$var])) { + $action = $_POST[$var]; + unset($_POST[$var]); + } else { + $action = C('DEFAULT_ACTION'); + } + } + } if ($maps = C('URL_ACTION_MAP')) { if (isset($maps[strtolower(CONTROLLER_NAME)])) { $maps = $maps[strtolower(CONTROLLER_NAME)]; @@ -348,14 +358,34 @@ private static function getAction($var, $urlCase) /** * 获得实际的模块名称 + * @param array $paths path_info数组 + * @return string */ - private static function getModule($var) + private static function getModule(&$paths) { - $module = (!empty($_GET[$var]) ? $_GET[$var] : C('DEFAULT_MODULE')); - unset($_GET[$var]); - $allowList = C('MODULE_ALLOW_LIST'); // 允许的模块列表 - if (!empty($allowList) && is_array($allowList) && !in_array_case($module, $allowList)) { - E(L('_MODULE_NOT_EXIST_') . ':' . strip_tags(ucfirst($module))); + if (defined('BIND_MODULE')) { + return BIND_MODULE; + } else { + // 检查路由 + if ($paths && C('URL_ROUTER_ON') && Route::check($paths)) { + $paths = explode(MODULE_PATHINFO_DEPR, trim($_SERVER['PATH_INFO'], MODULE_PATHINFO_DEPR)); + } + if ($paths && C('MULTI_MODULE')) { // 获取模块名 + $allowList = C('MODULE_ALLOW_LIST'); // 允许的模块列表 + if (empty($allowList) || (is_array($allowList) && in_array_case($paths[0], $allowList))) { + $module = array_shift($paths); + $_SERVER['PATH_INFO'] = implode(MODULE_PATHINFO_DEPR, $paths); + } + } else { + $var = C('VAR_MODULE'); + if (!empty($_GET[$var])) { + $module = $_GET[$var]; + unset($_GET[$var]); + } + } + if (empty($module)) { + $module = C('DEFAULT_MODULE'); + } } if ($maps = C('URL_MODULE_MAP')) { if (isset($maps[strtolower($module)])) { @@ -363,7 +393,7 @@ private static function getModule($var) define('MODULE_ALIAS', strtolower($module)); // 获取实际的模块名 return ucfirst($maps[MODULE_ALIAS]); - } elseif (array_search(strtolower($module), $maps)) { + } elseif (array_search(strtolower($module), $maps) || in_array_case($module, C('MODULE_DENY_LIST'))) { // 禁止访问原始模块 return ''; } diff --git a/ThinkPHP/Library/Think/Image/Driver/Imagick.class.php b/ThinkPHP/Library/Think/Image/Driver/Imagick.class.php index 20c6a1274..294775964 100644 --- a/ThinkPHP/Library/Think/Image/Driver/Imagick.class.php +++ b/ThinkPHP/Library/Think/Image/Driver/Imagick.class.php @@ -84,9 +84,9 @@ public function save($imgname, $type = null, $quality = 80, $interlace = true) $this->img->setImageFormat($type); } - //JPEG图像设置隔行扫描 - if ('jpeg' == $type || 'jpg' == $type) { - $this->img->setImageInterlaceScheme(1); + //JPEG\PNG 图像进行渐进渲染 + if ('jpeg' == $type || 'jpg' == $type || 'png' == $type) { + $this->img->setImageInterlaceScheme(\Imagick::INTERLACE_PLANE); } // 设置图像质量 diff --git a/ThinkPHP/Library/Think/Model.class.php b/ThinkPHP/Library/Think/Model.class.php index 897c72d7e..c1c6ebbd6 100644 --- a/ThinkPHP/Library/Think/Model.class.php +++ b/ThinkPHP/Library/Think/Model.class.php @@ -93,7 +93,7 @@ public function __construct($name = '', $tablePrefix = '', $connection = '') } elseif ('' != $tablePrefix) { $this->tablePrefix = $tablePrefix; } elseif (!isset($this->tablePrefix)) { - $this->tablePrefix = C('DB_PREFIX'); + $this->tablePrefix = !empty($this->connection) && !is_null(C($this->connection . '.DB_PREFIX')) ? C($this->connection . '.DB_PREFIX') : C('DB_PREFIX'); } // 数据库初始化操作 @@ -239,7 +239,7 @@ public function __call($method, $args) } elseif (in_array(strtolower($method), array('count', 'sum', 'min', 'max', 'avg'), true)) { // 统计查询的实现 $field = isset($args[0]) ? $args[0] : '*'; - return $this->getField(strtoupper($method) . '(' . $field . ') AS tp_' . $method); + return $this->getField(strtoupper($method) . '(' . $this->db->parseKey($field, true) . ') AS tp_' . $method); } elseif (strtolower(substr($method, 0, 5)) == 'getby') { // 根据某个字段获取记录 $field = parse_name(substr($method, 5)); @@ -518,8 +518,8 @@ public function delete($options = array()) } else { $where[$pk] = $options; } - $options = array(); - $options['where'] = $where; + + $this->options['where'] = $where; } // 根据复合主键删除记录 if (is_array($options) && (count($options) > 0) && is_array($pk)) { @@ -536,13 +536,13 @@ public function delete($options = array()) $where[$field] = $options[$i]; unset($options[$i++]); } - $options['where'] = $where; + $this->options['where'] = $where; } else { return false; } } // 分析表达式 - $options = $this->_parseOptions($options); + $options = $this->_parseOptions(); if (empty($options['where'])) { // 如果条件为空 不进行删除操作 除非设置 1=1 return false; @@ -589,8 +589,8 @@ public function select($options = array()) } else { $where[$pk] = $options; } - $options = array(); - $options['where'] = $where; + + $this->options['where'] = $where; } elseif (is_array($options) && (count($options) > 0) && is_array($pk)) { // 根据复合主键查询 $count = 0; @@ -606,16 +606,16 @@ public function select($options = array()) $where[$field] = $options[$i]; unset($options[$i++]); } - $options['where'] = $where; + $this->options['where'] = $where; } else { return false; } } elseif (false === $options) { // 用于子查询 不查询只返回SQL - $options['fetch_sql'] = true; + $this->options['fetch_sql'] = true; } // 分析表达式 - $options = $this->_parseOptions($options); + $options = $this->_parseOptions(); // 判断查询缓存 if (isset($options['cache'])) { $cache = $options['cache']; @@ -774,8 +774,8 @@ public function find($options = array()) { if (is_numeric($options) || is_string($options)) { $where[$this->getPk()] = $options; - $options = array(); - $options['where'] = $where; + + $this->options['where'] = $where; } // 根据复合主键查找记录 $pk = $this->getPk(); @@ -794,15 +794,15 @@ public function find($options = array()) $where[$field] = $options[$i]; unset($options[$i++]); } - $options['where'] = $where; + $this->options['where'] = $where; } else { return false; } } // 总是查找一条记录 - $options['limit'] = 1; + $this->options['limit'] = 1; // 分析表达式 - $options = $this->_parseOptions($options); + $options = $this->_parseOptions(); // 判断查询缓存 if (isset($options['cache'])) { $cache = $options['cache']; @@ -828,8 +828,8 @@ public function find($options = array()) // 读取数据后的处理 $data = $this->_read_data($resultSet[0]); $this->_after_find($data, $options); - if (!empty($this->options['result'])) { - return $this->returnResult($data, $this->options['result']); + if (!empty($options['result'])) { + return $this->returnResult($data, $options['result']); } $this->data = $data; if (isset($cache)) { @@ -1101,6 +1101,15 @@ public function create($data = '', $type = '') $fields = $this->insertFields; } elseif (self::MODEL_UPDATE == $type && isset($this->updateFields)) { $fields = $this->updateFields; + $pk = $this->getPk(); + if (is_string($pk)) { + array_push($fields, $pk); + } + if (is_array($pk)) { + foreach ($pk as $pkTemp) { + array_push($fields, $pkTemp); + } + } } if (isset($fields)) { if (is_string($fields)) { @@ -1245,8 +1254,8 @@ private function autoOperation(&$data, $type) } switch (trim($auto[3])) { - case 'function':// 使用函数进行填充 字段的值作为参数 - case 'callback': // 使用回调方法 + case 'function': // 使用函数进行填充 字段的值作为参数 + case 'callback': // 使用回调方法 $args = isset($auto[4]) ? (array) $auto[4] : array(); if (isset($data[$auto[0]])) { array_unshift($args, $data[$auto[0]]); @@ -1257,17 +1266,17 @@ private function autoOperation(&$data, $type) $data[$auto[0]] = call_user_func_array(array(&$this, $auto[1]), $args); } break; - case 'field': // 用其它字段的值进行填充 + case 'field': // 用其它字段的值进行填充 $data[$auto[0]] = $data[$auto[1]]; break; - case 'ignore': // 为空忽略 + case 'ignore': // 为空忽略 if ($auto[1] === $data[$auto[0]]) { unset($data[$auto[0]]); } break; case 'string': - default: // 默认作为字符串填充 + default: // 默认作为字符串填充 $data[$auto[0]] = $auto[1]; } if (isset($data[$auto[0]]) && false === $data[$auto[0]]) { @@ -1321,13 +1330,13 @@ protected function autoValidation($data, $type) $val[4] = isset($val[4]) ? $val[4] : 'regex'; // 判断验证条件 switch ($val[3]) { - case self::MUST_VALIDATE: // 必须验证 不管表单是否有设置该字段 + case self::MUST_VALIDATE: // 必须验证 不管表单是否有设置该字段 if (false === $this->_validationField($data, $val)) { return false; } break; - case self::VALUE_VALIDATE: // 值不为空的时候才验证 + case self::VALUE_VALIDATE: // 值不为空的时候才验证 if ('' != trim($data[$val[0]])) { if (false === $this->_validationField($data, $val)) { return false; @@ -1335,7 +1344,7 @@ protected function autoValidation($data, $type) } break; - default: // 默认表单存在该字段就验证 + default: // 默认表单存在该字段就验证 if (isset($data[$val[0]])) { if (false === $this->_validationField($data, $val)) { return false; @@ -1390,8 +1399,8 @@ protected function _validationField($data, $val) protected function _validationFieldItem($data, $val) { switch (strtolower(trim($val[4]))) { - case 'function':// 使用函数进行验证 - case 'callback': // 调用方法进行验证 + case 'function': // 使用函数进行验证 + case 'callback': // 调用方法进行验证 $args = isset($val[6]) ? (array) $val[6] : array(); if (is_string($val[0]) && strpos($val[0], ',')) { $val[0] = explode(',', $val[0]); @@ -1412,9 +1421,9 @@ protected function _validationFieldItem($data, $val) } else { return call_user_func_array(array(&$this, $val[1]), $args); } - case 'confirm': // 验证两个字段是否相同 + case 'confirm': // 验证两个字段是否相同 return $data[$val[0]] == $data[$val[1]]; - case 'unique': // 验证某个值是否唯一 + case 'unique': // 验证某个值是否唯一 if (is_string($val[0]) && strpos($val[0], ',')) { $val[0] = explode(',', $val[0]); } @@ -1441,7 +1450,7 @@ protected function _validationFieldItem($data, $val) $this->options = $options; return true; - default: // 检查附加规则 + default: // 检查附加规则 return $this->check($data[$val[0]], $val[1], $val[4]); } } @@ -1458,12 +1467,12 @@ public function check($value, $rule, $type = 'regex') { $type = strtolower(trim($type)); switch ($type) { - case 'in':// 验证是否在某个指定范围之内 逗号分隔字符串或者数组 + case 'in': // 验证是否在某个指定范围之内 逗号分隔字符串或者数组 case 'notin': $range = is_array($rule) ? $rule : explode(',', $rule); return 'in' == $type ? in_array($value, $range) : !in_array($value, $range); - case 'between':// 验证是否在某个范围 - case 'notbetween': // 验证是否不在某个范围 + case 'between': // 验证是否在某个范围 + case 'notbetween': // 验证是否不在某个范围 if (is_array($rule)) { $min = $rule[0]; $max = $rule[1]; @@ -1471,11 +1480,11 @@ public function check($value, $rule, $type = 'regex') list($min, $max) = explode(',', $rule); } return 'between' == $type ? $value >= $min && $value <= $max : $value < $min || $value > $max; - case 'equal':// 验证是否等于某个值 - case 'notequal': // 验证是否等于某个值 + case 'equal': // 验证是否等于某个值 + case 'notequal': // 验证是否等于某个值 return 'equal' == $type ? $value == $rule : $value != $rule; - case 'length': // 验证长度 - $length = mb_strlen($value, 'utf-8'); // 当前数据长度 + case 'length': // 验证长度 + $length = mb_strlen($value, 'utf-8'); // 当前数据长度 if (strpos($rule, ',')) { // 长度区间 list($min, $max) = explode(',', $rule); @@ -1495,12 +1504,12 @@ public function check($value, $rule, $type = 'regex') } return NOW_TIME >= $start && NOW_TIME <= $end; - case 'ip_allow': // IP 操作许可验证 + case 'ip_allow': // IP 操作许可验证 return in_array(get_client_ip(), explode(',', $rule)); - case 'ip_deny': // IP 操作禁止验证 + case 'ip_deny': // IP 操作禁止验证 return !in_array(get_client_ip(), explode(',', $rule)); case 'regex': - default: // 默认使用正则验证 可以使用验证类中定义的验证名称 + default: // 默认使用正则验证 可以使用验证类中定义的验证名称 // 检查附加规则 return $this->regex($value, $rule); } diff --git a/ThinkPHP/Library/Think/Model/MongoModel.class.php b/ThinkPHP/Library/Think/Model/MongoModel.class.php index 67afdb481..1c3b3b4a9 100644 --- a/ThinkPHP/Library/Think/Model/MongoModel.class.php +++ b/ThinkPHP/Library/Think/Model/MongoModel.class.php @@ -148,7 +148,8 @@ public function getMongoNextId($pk = '') if (empty($pk)) { $pk = $this->getPk(); } - return $this->db->getMongoNextId($pk); + $options = $this->_parseOptions(); + return $this->db->getMongoNextId($pk,$options); } /** @@ -198,7 +199,7 @@ protected function _before_insert(&$data, $options) // 主键自动增长 $pk = $this->getPk(); if (!isset($data[$pk])) { - $data[$pk] = $this->db->getMongoNextId($pk); + $data[$pk] = $this->db->getMongoNextId($pk, $options); } } } diff --git a/ThinkPHP/Library/Think/Model/ViewModel.class.php b/ThinkPHP/Library/Think/Model/ViewModel.class.php index fb455740f..a91d27e40 100644 --- a/ThinkPHP/Library/Think/Model/ViewModel.class.php +++ b/ThinkPHP/Library/Think/Model/ViewModel.class.php @@ -111,32 +111,87 @@ private function _checkFields($name, $fields) /** * 检查条件中的视图字段 - * @access protected - * @param mixed $data 条件表达式 + * @param $where 条件表达式 * @return array */ protected function checkCondition($where) { if (is_array($where)) { - $view = array(); - // 检查视图字段 + $fields = $field_map_table = array(); foreach ($this->viewFields as $key => $val) { - $k = isset($val['_as']) ? $val['_as'] : $key; - $val = $this->_checkFields($key, $val); - foreach ($where as $name => $value) { - if (false !== $field = array_search($name, $val, true)) { - // 存在视图字段 - $_key = is_numeric($field) ? $k . '.' . $name : $k . '.' . $field; - $view[$_key] = $value; - unset($where[$name]); + $table_alias = isset($val['_as']) ? $val['_as'] : $key; + $val = $this->_checkFields($key, $val); + foreach ($val as $as_name => $v) { + if (is_numeric($as_name)) { + $fields[] = $v; //所有表字段集合 + $field_map_table[] = $table_alias; //所有表字段对应表名集合 + } else { + $fields[$as_name] = $v; + $field_map_table[$as_name] = $table_alias; } } } - $where = array_merge($view, array_diff_key(array_merge($where, $view), $view)); + $where = $this->_parseWhere($where, $fields, $field_map_table); } + return $where; } + /** + * 解析where表达式 + * @param $where + * @param $fields + * @param $field_map_table + * @return array + */ + private function _parseWhere($where, $fields, $field_map_table) + { + $view = array(); + foreach ($where as $name => $val) { + if ('_complex' == $name) { + //复合查询 + foreach ($val as $k => $v) { + if (false === strpos(substr($k, 0, 1), '_')) { + if (false !== $field = array_search($k, $fields, true)) { // 存在视图字段 + $k = is_numeric($field) ? $field_map_table[$field] . '.' . $k : $field_map_table[$field] . '.' . $field; //字段别名 + } + } else if (is_array($v)) { + //数组复合查询 + $v = $this->_parseWhere($val[$k], $fields, $field_map_table); + } + $view[$name][$k] = $v; + } + } else { + if (strpos($name, '|')) { + //name|title快捷查询 + $arr = explode('|', $name); + foreach ($arr as $k => $v) { + if (false !== $field = array_search($v, $fields, true)) { + $arr[$k] = is_numeric($field) ? $field_map_table[$field] . '.' . $v : $field_map_table[$field] . '.' . $field; + } + } + $view[implode('|', $arr)] = $val; + } else if (strpos($name, '&')) { + //name&title快捷查询 + $arr = explode('&', $name); + foreach ($arr as $k => $v) { + if (false !== $field = array_search($v, $fields, true)) { + $arr[$k] = is_numeric($field) ? $field_map_table[$field] . '.' . $v : $field_map_table[$field] . '.' . $field; + } + } + $view[implode('&', $arr)] = $val; + } else { + if (false !== $field = array_search($name, $fields, true)) { + $name = is_numeric($field) ? $field_map_table[$field] . '.' . $name : $field_map_table[$field] . '.' . $field; + } + $view[$name] = $val; + } + } + } + + return $view; + } + /** * 检查Order表达式中的视图字段 * @access protected diff --git a/ThinkPHP/Library/Think/Route.class.php b/ThinkPHP/Library/Think/Route.class.php index 80bdec8cf..19b76213f 100644 --- a/ThinkPHP/Library/Think/Route.class.php +++ b/ThinkPHP/Library/Think/Route.class.php @@ -16,81 +16,100 @@ class Route { - // 路由检测 - public static function check() + /** + * 路由检测 + * @param array $paths path_info数组 + * @return boolean + */ + public static function check($paths = array()) { - $depr = C('URL_PATHINFO_DEPR'); - $regx = preg_replace('/\.' . __EXT__ . '$/i', '', trim($_SERVER['PATH_INFO'], $depr)); - // 分隔符替换 确保路由定义使用统一的分隔符 - if ('/' != $depr) { - $regx = str_replace($depr, '/', $regx); + $rules = self::ruleCache(); + if (!empty($paths)) { + $regx = implode('/', $paths); + } else { + $depr = C('URL_PATHINFO_DEPR'); + $regx = preg_replace('/\.' . __EXT__ . '$/i', '', trim($_SERVER['PATH_INFO'], $depr)); + if (!$regx) { + return false; + } + // 分隔符替换 确保路由定义使用统一的分隔符 + if ('/' != $depr) { + $regx = str_replace($depr, '/', $regx); + } } - // URL映射定义(静态路由) - $maps = C('URL_MAP_RULES'); - if (isset($maps[$regx])) { - $var = self::parseUrl($maps[$regx]); - $_GET = array_merge($var, $_GET); + // 静态路由检查 + if (isset($rules[0][$regx])) { + $route = $rules[0][$regx]; + $_SERVER['PATH_INFO'] = $route[0]; + $args = array_pop($route); + if (!empty($route[1])) { + $args = array_merge($args, $route[1]); + } + $_GET = array_merge($args, $_GET); return true; } - // 动态路由处理 - $routes = C('URL_ROUTE_RULES'); - if (!empty($routes)) { - foreach ($routes as $rule => $route) { + // 动态路由检查 + if (!empty($rules[1])) { + foreach ($rules[1] as $rule => $route) { if (is_numeric($rule)) { - // 支持 array('rule','adddress',...) 定义路由 $rule = array_shift($route); } - if (is_array($route) && isset($route[2])) { - // 路由参数 - $options = $route[2]; - if (isset($options['ext']) && __EXT__ != $options['ext']) { - // URL后缀检测 - continue; - } - if (isset($options['method']) && REQUEST_METHOD != strtoupper($options['method'])) { - // 请求类型检测 + + $args = array_pop($route); + if (isset($route[2])) { + // 路由参数检查 + if (!self::checkOption($route[2], __EXT__)) { continue; } - // 自定义检测 - if (!empty($options['callback']) && is_callable($options['callback'])) { - if (false === call_user_func($options['callback'])) { - continue; - } - } } - if (0 === strpos($rule, '/') && preg_match($rule, $regx, $matches)) { - // 正则路由 - if ($route instanceof \Closure) { + $matches = self::checkUrlMatch($rule, $args, $regx); + if ($matches !== false) { + if ($route[0] instanceof \Closure) { // 执行闭包 - $result = self::invokeRegx($route, $matches); + $result = self::invoke($route[0], $matches); // 如果返回布尔值 则继续执行 return is_bool($result) ? $result : exit; } else { - return self::parseRegex($matches, $route, $regx); - } - } else { - // 规则路由 - $len1 = substr_count($regx, '/'); - $len2 = substr_count($rule, '/'); - if ($len1 >= $len2 || strpos($rule, '[')) { - if ('$' == substr($rule, -1, 1)) { - // 完整匹配 - if ($len1 != $len2) { - continue; - } else { - $rule = substr($rule, 0, -1); + // 存在动态变量 + if (strpos($route[0], ':')) { + $matches = array_values($matches); + $route[0] = preg_replace_callback('/:(\d+)/', function ($match) use (&$matches) { + return $matches[$match[1] - 1]; + }, $route[0]); + } + // 路由参数关联$matches + if ('/' == substr($rule, 0, 1)) { + $rule_params = array(); + foreach($route[1] as $param_key => $param) + { + list($param_name,$param_value) = explode('=', $param,2); + if(!is_null($param_value)) + { + if(preg_match('/^:(\d*)$/',$param_value, $match_index)) + { + $match_index = $match_index[1]-1; + $param_value = $matches[$match_index]; + } + $rule_params[$param_name] = $param_value; + unset($route[1][$param_key]); + } } + $route[1] = $rule_params; } - $match = self::checkUrlMatch($regx, $rule); - if (false !== $match) { - if ($route instanceof \Closure) { - // 执行闭包 - $result = self::invokeRule($route, $match); - // 如果返回布尔值 则继续执行 - return is_bool($result) ? $result : exit; - } else { - return self::parseRule($rule, $route, $regx); + // 重定向 + if ('/' == substr($route[0], 0, 1)) { + header("Location: $route[0]", true, $route[1]); + exit; + } else { + $depr = C('URL_PATHINFO_DEPR'); + if ('/' != $depr) { + $route[0] = str_replace('/', $depr, $route[0]); + } + $_SERVER['PATH_INFO'] = $route[0]; + if (!empty($route[1])) { + $_GET = array_merge($route[1], $_GET); } + return true; } } } @@ -99,80 +118,119 @@ public static function check() return false; } - // 检测URL和规则路由是否匹配 - private static function checkUrlMatch($regx, $rule) + /** + * 路由反向解析 + * @param string $path 控制器/方法 + * @param array $vars url参数 + * @param string $depr 分隔符 + * @param string|true $suffix url后缀 + * @return string|false + */ + public static function reverse($path, &$vars, $depr, $suffix = true) { - $m1 = explode('/', $regx); - $m2 = explode('/', $rule); - $var = array(); - foreach ($m2 as $key => $val) { - if (0 === strpos($val, '[:')) { - $val = substr($val, 1, -1); + static $_rules; + if (is_null($_rules)) { + if ($rules = self::ruleCache()) { + foreach ($rules as $i => $rules2) { + foreach ($rules2 as $rule => $route) { + if (is_array($route) && is_string($route[0]) && '/' != substr($route[0], 0, 1)) { + $_rules[$i][$route[0]][$rule] = $route; + } + } + } } + } + // 静态路由 + if (isset($_rules[0][$path])) { + foreach ($_rules[0][$path] as $rule => $route) { + $args = array_pop($route); + if (count($vars) == count($args) && !empty($vars) && !array_diff($vars, $args)) { + return str_replace('/', $depr, $rule); + } + } + } + if (isset($_rules[1][$path])) { + foreach ($_rules[1][$path] as $rule => $route) { + if (is_numeric($rule)) { + $rule = array_shift($route); + } - if (':' == substr($val, 0, 1)) { -// 动态变量 - if ($pos = strpos($val, '|')) { - // 使用函数过滤 - $val = substr($val, 1, $pos - 1); + $args = array_pop($route); + $array = array(); + if (isset($route[2])) { + // 路由参数检查 + if (!self::checkOption($route[2], $suffix)) { + continue; + } } - if (strpos($val, '\\')) { - $type = substr($val, -1); - if ('d' == $type) { - if (isset($m1[$key]) && !is_numeric($m1[$key])) { - return false; + if ('/' != substr($rule, 0, 1)) { + // 规则路由 + foreach ($args as $key => $val) { + $flag = false; + if ($val[0] == 0) { + // 静态变量值 + $array[$key] = $key; + continue; + } + if (isset($vars[$key])) { + // 是否有过滤条件 + if (!empty($val[2])) { + if ($val[2] == 'int') { + // 是否为数字 + if (!is_numeric($vars[$key]) || !preg_match('/^\d*$/',$vars[$key])) { + break; + } + } else { + // 排除的名称 + if (in_array($vars[$key], $val[2])) { + break; + } + } + } + $flag = true; + $array[$key] = $vars[$key]; + } elseif ($val[0] == 1) { + // 如果是必选项 + break; } - } - $name = substr($val, 1, -2); - } elseif ($pos = strpos($val, '^')) { - $array = explode('-', substr(strstr($val, '^'), 1)); - if (in_array($m1[$key], $array)) { - return false; + // 匹配成功 + if (!empty($flag)) { + foreach (array_keys($array) as $key) { + $array[$key] = urlencode($array[$key]); + unset($vars[$key]); + } + return implode($depr, $array); } - $name = substr($val, 1, $pos - 1); } else { - $name = substr($val, 1); + // 正则路由 + $keys = !empty($args) ? array_keys($args) : array_keys($vars); + $temp_vars = $vars; + $str = preg_replace_callback('/\(.*?\)/', function ($match) use (&$temp_vars, &$keys) { + $k = array_shift($keys); + $re_var = ''; + if(isset($temp_vars[$k])) + { + $re_var = $temp_vars[$k]; + unset($temp_vars[$k]); + } + return urlencode($re_var); + }, $rule); + $str = substr($str, 1, -1); + $str = rtrim(ltrim($str, '^'), '$'); + $str = str_replace('\\', '', $str); + if (preg_match($rule, $str, $matches)) { + // 匹配成功 + $vars = $temp_vars; + return str_replace('/', $depr, $str); + } } - $var[$name] = isset($m1[$key]) ? $m1[$key] : ''; - } elseif (0 !== strcasecmp($val, $m1[$key])) { - return false; - } - } - // 成功匹配后返回URL中的动态变量数组 - return $var; - } - - // 解析规范的路由地址 - // 地址格式 [控制器/操作?]参数1=值1&参数2=值2... - private static function parseUrl($url) - { - $var = array(); - if (false !== strpos($url, '?')) { - // [控制器/操作?]参数1=值1&参数2=值2... - $info = parse_url($url); - $path = explode('/', $info['path']); - parse_str($info['query'], $var); - } elseif (strpos($url, '/')) { - // [控制器/操作] - $path = explode('/', $url); - } else { - // 参数1=值1&参数2=值2... - parse_str($url, $var); - } - if (isset($path)) { - $var[C('VAR_ACTION')] = array_pop($path); - if (!empty($path)) { - $var[C('VAR_CONTROLLER')] = array_pop($path); - } - if (!empty($path)) { - $var[C('VAR_MODULE')] = array_pop($path); } } - return $var; + return false; } - // 解析规则路由 + // 规则路由定义方法: // '路由规则'=>'[控制器/操作]?额外参数1=值1&额外参数2=值2...' // '路由规则'=>array('[控制器/操作]','额外参数1=值1&额外参数2=值2...') // '路由规则'=>'外部地址' @@ -181,153 +239,267 @@ private static function parseUrl($url) // 外部地址中可以用动态变量 采用 :1 :2 的方式 // 'news/:month/:day/:id'=>array('News/read?cate=1','status=1'), // 'new/:id'=>array('/new.php?id=:1',301), 重定向 - private static function parseRule($rule, $route, $regx) + // 正则路由定义方法: + // '路由正则'=>'[控制器/操作]?参数1=值1&参数2=值2...' + // '路由正则'=>array('[控制器/操作]?参数1=值1&参数2=值2...','额外参数1=值1&额外参数2=值2...') + // '路由正则'=>'外部地址' + // '路由正则'=>array('外部地址','重定向代码') + // 参数值和外部地址中可以用动态变量 采用 :1 :2 的方式 + // '/new\/(\d+)\/(\d+)/'=>array('News/read?id=:1&page=:2&cate=1','status=1'), + // '/new\/(\d+)/'=>array('/new.php?id=:1&page=:2&status=1','301'), 重定向 + /** + * 读取规则缓存 + * @param boolean $update 是否更新 + * @return array + */ + public static function ruleCache($update = false) { - // 获取路由地址规则 - $url = is_array($route) ? $route[0] : $route; - // 获取URL地址中的参数 - $paths = explode('/', $regx); - // 解析路由规则 - $matches = array(); - $rule = explode('/', $rule); - foreach ($rule as $item) { - $fun = ''; - if (0 === strpos($item, '[:')) { - $item = substr($item, 1, -1); - } - if (0 === strpos($item, ':')) { - // 动态变量获取 - if ($pos = strpos($item, '|')) { - // 支持函数过滤 - $fun = substr($item, $pos + 1); - $item = substr($item, 0, $pos); + $result = array(); + $module = defined('MODULE_NAME') ? '_' . MODULE_NAME : ''; + if (APP_DEBUG || $update || !$result = S('url_route_rules' . $module)) { + // 静态路由 + $result[0] = C('URL_MAP_RULES'); + if (!empty($result[0])) { + foreach ($result[0] as $rule => $route) { + if (!is_array($route)) { + $route = array($route); + } + if (strpos($route[0], '?')) { + // 分离参数 + list($route[0], $args) = explode('?', $route[0], 2); + parse_str($args, $args); + } else { + $args = array(); + } + if (!empty($route[1]) && is_string($route[1])) { + // 额外参数 + parse_str($route[1], $route[1]); + } + $route[] = $args; + $result[0][$rule] = $route; } - if ($pos = strpos($item, '^')) { - $var = substr($item, 1, $pos - 1); - } elseif (strpos($item, '\\')) { - $var = substr($item, 1, -2); - } else { - $var = substr($item, 1); + } + // 动态路由 + $result[1] = []; + $dynamicRoutes = C('URL_ROUTE_RULES'); + if (!empty($dynamicRoutes)) { + foreach ($dynamicRoutes as $key => $value) { + $rule = $key; + $route = $value; + + if (!is_array($route)) { + $route = array($route); + } elseif (is_numeric($rule)) { + // 支持 array('rule','adddress',...) 定义路由 + $rule = array_shift($route); + } + if (!empty($route)) { + $args = array(); + if (is_string($route[0])) { + if (0 === strpos($route[0], '/') || 0 === strpos($route[0], 'http')) { + // 重定向 + if (!isset($route[1])) { + $route[1] = 301; + } + } else { + if (!empty($route[1]) && is_string($route[1])) { + // 额外参数 + parse_str($route[1], $route[1]); + } + if (strpos($route[0], '?')) { + // 分离参数 + list($route[0], $params) = explode('?', $route[0], 2); + if (!empty($params)) { + foreach (explode('&', $params) as $key => $val) { + if (0 === strpos($val, ':')) { + // 动态参数 + $val = substr($val, 1); + $args[$key] = strpos($val, '|') ? explode('|', $val, 2) : array($val); + } else { + $route[1][$key] = $val; + } + } + } + } + } + } + if ('/' != substr($rule, 0, 1)) { + // 规则路由 + foreach (explode('/', rtrim($rule, '$')) as $item) { + $filter = $fun = ''; + $type = 0; + if (0 === strpos($item, '[:')) { + // 可选变量 + $type = 2; + $item = substr($item, 1, -1); + } + if (0 === strpos($item, ':')) { + // 动态变量获取 + $type = $type ?: 1; + if ($pos = strpos($item, '|')) { + // 支持函数过滤 + $fun = substr($item, $pos + 1); + $item = substr($item, 1, $pos - 1); + } + if ($pos = strpos($item, '^')) { + // 排除项 + $filter = explode('-', substr($item, $pos + 1)); + $item = substr($item, 1, $pos - 1); + } elseif (strpos($item, '\\')) { + // \d表示限制为数字 + if ('d' == substr($item, -1)) { + $filter = 'int'; + } + $item = substr($item, 1, -2); + } else { + $item = substr($item, 1); + } + } + $args[$item] = array($type, $fun, $filter); + } + } + $route[] = $args; + + // 保持配置中路由定义的键的类型,以支持多个路由的路由表达式相同而路由参数不同的情况 + if (is_numeric($key)) { + array_unshift($route, $rule); + $rule = $key; + } + + $result[1][$rule] = $route; + } } - $matches[$var] = !empty($fun) ? $fun(array_shift($paths)) : array_shift($paths); - } else { - // 过滤URL中的静态变量 - array_shift($paths); } + S('url_route_rules' . $module, $result); } + return $result; + } - if (0 === strpos($url, '/') || 0 === strpos($url, 'http')) { - // 路由重定向跳转 - if (strpos($url, ':')) { // 传递动态参数 - $values = array_values($matches); - $url = preg_replace_callback('/:(\d+)/', function ($match) use ($values) {return $values[$match[1] - 1];}, $url); - } - header("Location: $url", true, (is_array($route) && isset($route[1])) ? $route[1] : 301); - exit; - } else { - // 解析路由地址 - $var = self::parseUrl($url); - // 解析路由地址里面的动态参数 - $values = array_values($matches); - foreach ($var as $key => $val) { - if (0 === strpos($val, ':')) { - $var[$key] = $values[substr($val, 1) - 1]; + /** + * 路由参数检测 + * @param array $options 路由参数 + * @param string|true $suffix URL后缀 + * @return boolean + */ + private static function checkOption($options, $suffix = true) + { + // URL后缀检测 + if (isset($options['ext'])) { + if ($suffix) { + $suffix = $suffix === true ? C('URL_HTML_SUFFIX') : $suffix; + if ($pos = strpos($suffix, '|')) { + $suffix = substr($suffix, 0, $pos); } } - $var = array_merge($matches, $var); - // 解析剩余的URL参数 - if (!empty($paths)) { - preg_replace_callback('/(\w+)\/([^\/]+)/', function ($match) use (&$var) {$var[strtolower($match[1])] = strip_tags($match[2]);}, implode('/', $paths)); + if ($suffix != $options['ext']) { + return false; } - // 解析路由自动传入参数 - if (is_array($route) && isset($route[1])) { - if (is_array($route[1])) { - $params = $route[1]; - } else { - parse_str($route[1], $params); - } - $var = array_merge($var, $params); + } + if (isset($options['method']) && REQUEST_METHOD != strtoupper($options['method'])) { + // 请求类型检测 + return false; + } + // 自定义检测 + if (!empty($options['callback']) && is_callable($options['callback'])) { + if (false === call_user_func($options['callback'])) { + return false; } - $_GET = array_merge($var, $_GET); } return true; } - // 解析正则路由 - // '路由正则'=>'[控制器/操作]?参数1=值1&参数2=值2...' - // '路由正则'=>array('[控制器/操作]?参数1=值1&参数2=值2...','额外参数1=值1&额外参数2=值2...') - // '路由正则'=>'外部地址' - // '路由正则'=>array('外部地址','重定向代码') - // 参数值和外部地址中可以用动态变量 采用 :1 :2 的方式 - // '/new\/(\d+)\/(\d+)/'=>array('News/read?id=:1&page=:2&cate=1','status=1'), - // '/new\/(\d+)/'=>array('/new.php?id=:1&page=:2&status=1','301'), 重定向 - private static function parseRegex($matches, $route, $regx) + /** + * 检测URL和路由规则是否匹配 + * @param string $rule 路由规则 + * @param array $args 路由动态变量 + * @param string $regx URL地址 + * @return array|false + */ + private static function checkUrlMatch(&$rule, &$args, &$regx) { - // 获取路由地址规则 - $url = is_array($route) ? $route[0] : $route; - $url = preg_replace_callback('/:(\d+)/', function ($match) use ($matches) {return $matches[$match[1]];}, $url); - if (0 === strpos($url, '/') || 0 === strpos($url, 'http')) { - // 路由重定向跳转 - header("Location: $url", true, (is_array($route) && isset($route[1])) ? $route[1] : 301); - exit; - } else { - // 解析路由地址 - $var = self::parseUrl($url); - // 处理函数 - foreach ($var as $key => $val) { - if (strpos($val, '|')) { - list($val, $fun) = explode('|', $val); - $var[$key] = $fun($val); + $params = array(); + if ('/' == substr($rule, 0, 1)) { + // 正则路由 + if (preg_match($rule, $regx, $matches)) { + if ($args) { // 存在动态变量 + foreach ($args as $key => $val) { + $params[$key] = isset($val[1]) ? $val[1]($matches[$val[0]]) : $matches[$val[0]]; + } + $regx = substr_replace($regx, '', 0, strlen($matches[0])); } + array_shift($matches); + return $matches; + } else { + return false; } - // 解析剩余的URL参数 - $regx = substr_replace($regx, '', 0, strlen($matches[0])); - if ($regx) { - preg_replace_callback('/(\w+)\/([^\/]+)/', function ($match) use (&$var) { - $var[strtolower($match[1])] = strip_tags($match[2]); - }, $regx); + } else { + $paths = explode('/', $regx); + // $结尾则要求完整匹配 + if ('$' == substr($rule, -1) && count($args) != count($paths)) { + return false; } - // 解析路由自动传入参数 - if (is_array($route) && isset($route[1])) { - if (is_array($route[1])) { - $params = $route[1]; + foreach ($args as $key => $val) { + $var = array_shift($paths) ?: ''; + if ($val[0] == 0) { + // 静态变量 + if (0 !== strcasecmp($key, $var)) { + return false; + } } else { - parse_str($route[1], $params); + if (isset($val[2])) { + // 设置了过滤条件 + if ($val[2] == 'int') { + // 如果值不为整数 + if (!preg_match('/^\d*$/',$var)) { + return false; + } + } else { + // 如果值在排除的名单里 + if (is_array($val[2]) && in_array($var, $val[2])) { + return false; + } + } + } + if (!empty($var)) { + $params[$key] = !empty($val[1]) ? $val[1]($var) : $var; + } elseif ($val[0] == 1) { + // 不是可选的 + return false; + } } - $var = array_merge($var, $params); } - $_GET = array_merge($var, $_GET); + $matches = $params; + $regx = implode('/', $paths); } - return true; - } - - // 执行正则匹配下的闭包方法 支持参数调用 - private static function invokeRegx($closure, $var = array()) - { - $reflect = new \ReflectionFunction($closure); - $params = $reflect->getParameters(); - $args = array(); - array_shift($var); - foreach ($params as $param) { - if (!empty($var)) { - $args[] = array_shift($var); - } elseif ($param->isDefaultValueAvailable()) { - $args[] = $param->getDefaultValue(); - } + // 解析剩余的URL参数 + if ($regx) { + preg_replace_callback('/(\w+)\/([^\/]+)/', function ($match) use (&$params) { + $params[strtolower($match[1])] = strip_tags($match[2]); + }, $regx); } - return $reflect->invokeArgs($args); + $_GET = array_merge($params, $_GET); + + // 成功匹配后返回URL中的动态变量数组 + return $matches; } - // 执行规则匹配下的闭包方法 支持参数调用 - private static function invokeRule($closure, $var = array()) + /** + * 执行闭包方法 支持参数调用 + * @param function $closure 闭包函数 + * @param array $var 传给闭包的参数 + * @return boolean + */ + private static function invoke($closure, $var = array()) { $reflect = new \ReflectionFunction($closure); - $params = $reflect->getParameters(); - $args = array(); - foreach ($params as $param) { + $params = $reflect->getParameters(); + $args = array(); + foreach ($params as $i => $param) { $name = $param->getName(); if (isset($var[$name])) { $args[] = $var[$name]; + } elseif (isset($var[$i])) { + $args[] = $var[$i]; } elseif ($param->isDefaultValueAvailable()) { $args[] = $param->getDefaultValue(); } @@ -335,4 +507,4 @@ private static function invokeRule($closure, $var = array()) return $reflect->invokeArgs($args); } -} +} \ No newline at end of file diff --git a/ThinkPHP/Library/Think/Storage.class.php b/ThinkPHP/Library/Think/Storage.class.php index df4bdabb5..7e5140066 100644 --- a/ThinkPHP/Library/Think/Storage.class.php +++ b/ThinkPHP/Library/Think/Storage.class.php @@ -34,7 +34,7 @@ public static function connect($type = 'File', $options = array()) self::$handler = new $class($options); } - public static function __callstatic($method, $args) + public static function __callStatic($method, $args) { //调用缓存驱动的方法 if (method_exists(self::$handler, $method)) { diff --git a/ThinkPHP/Library/Think/Think.class.php b/ThinkPHP/Library/Think/Think.class.php index 8ae9fca82..c63a9d2f7 100644 --- a/ThinkPHP/Library/Think/Think.class.php +++ b/ThinkPHP/Library/Think/Think.class.php @@ -44,9 +44,10 @@ public static function start() if (!APP_DEBUG && Storage::has($runtimefile)) { Storage::load($runtimefile); } else { - if (Storage::has($runtimefile)) { + //在高并发状态下,这里容易引起死锁,建议去掉这里的删除和重建,毕竟后面已经有一个删除和重建操作了 + /*if (Storage::has($runtimefile)) { Storage::unlink($runtimefile); - } + }*/ $content = ''; // 读取应用模式 diff --git a/ThinkPHP/Library/Think/View.class.php b/ThinkPHP/Library/Think/View.class.php index f6c5836c6..506831b4a 100644 --- a/ThinkPHP/Library/Think/View.class.php +++ b/ThinkPHP/Library/Think/View.class.php @@ -132,11 +132,23 @@ public function fetch($templateFile = '', $content = '', $prefix = '') ob_implicit_flush(0); if ('php' == strtolower(C('TMPL_ENGINE_TYPE'))) { // 使用PHP原生模板 - $_content = $content; - // 模板阵列变量分解成为独立变量 - extract($this->tVar, EXTR_OVERWRITE); - // 直接载入PHP模板 - empty($_content) ? include $templateFile : eval('?>' . $_content); + if (empty($content)) { + if (isset($this->tVar['templateFile'])) { + $__template__ = $templateFile; + extract($this->tVar, EXTR_OVERWRITE); + include $__template__; + } else { + extract($this->tVar, EXTR_OVERWRITE); + include $templateFile; + } + } elseif (isset($this->tVar['content'])) { + $__content__ = $content; + extract($this->tVar, EXTR_OVERWRITE); + eval('?>' . $__content__); + } else { + extract($this->tVar, EXTR_OVERWRITE); + eval('?>' . $content); + } } else { // 视图解析标签 $params = array('var' => $this->tVar, 'file' => $templateFile, 'content' => $content, 'prefix' => $prefix); @@ -146,9 +158,10 @@ public function fetch($templateFile = '', $content = '', $prefix = '') $content = ob_get_clean(); // 内容过滤标签 Hook::listen('view_filter', $content); - if(APP_DEBUG){ // debug模式时,将后台分配变量输出到浏览器控制台 + if (APP_DEBUG && C('PARSE_VAR')) { + // debug模式时,将后台分配变量输出到浏览器控制台 $parseVar = empty($this->tVar) ? json_encode(array()) : json_encode($this->tVar); - $content = $content.''; + $content = $content . ''; } // 输出模板文件 return $content; diff --git a/ThinkPHP/ThinkPHP.php b/ThinkPHP/ThinkPHP.php index c66d876b4..6e165683a 100644 --- a/ThinkPHP/ThinkPHP.php +++ b/ThinkPHP/ThinkPHP.php @@ -22,7 +22,7 @@ } // 版本信息 -const THINK_VERSION = '3.2.3'; +const THINK_VERSION = '3.2.5'; // URL 模式定义 const URL_COMMON = 0; //普通模式 diff --git a/ThinkPHP/Tpl/think_exception.tpl b/ThinkPHP/Tpl/think_exception.tpl index 61fd00717..e4afe4fae 100644 --- a/ThinkPHP/Tpl/think_exception.tpl +++ b/ThinkPHP/Tpl/think_exception.tpl @@ -1,8 +1,3 @@ -
diff --git a/composer.json b/composer.json index 5154e2e0e..c8c33f74e 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "type": "framework", "keywords": ["framework","thinkphp","ORM"], "homepage": "http://thinkphp.cn/", - "license": "Apache2", + "license": "Apache-2.0", "authors": [ { "name": "liu21st",