ÓÅ·¢¹ú¼ÊÍøÕ¾¹ÙÍøADLab£ºThinkPHP5½¹µãÀàRequestÔ¶³Ì´úÂëÎó²îÆÊÎö
Ðû²¼Ê±¼ä 2019-01-122019Äê1ÔÂ11ÈÕ£¬ThinkPHPÍŶÓÐû²¼ÁËÒ»¸ö²¹¶¡¸üУ¬ÐÞ¸´ÁËÒ»´¦ÓÉÓÚ²»Çå¾²µÄ¶¯Ì¬º¯ÊýŲÓõ¼ÖµÄÔ¶³Ì´úÂëÖ´ÐÐÎó²î¡£¸ÃÎó²îΣº¦Ë®Æ½ºÜÊǸߣ¬Ä¬ÈÏÌõ¼þϼ´¿ÉÖ´ÐÐÔ¶³Ì´úÂë¡£ÓÅ·¢¹ú¼ÊÍøÕ¾¹ÙÍøADLabÇå¾²Ñо¿Ô±¶ÔThinkPHPµÄ¶à¸ö°æ±¾¾ÙÐÐÔ´ÂëÆÊÎöºÍÑéÖ¤ºó£¬È·ÈÏÏêϸÊÜÓ°ÏìµÄ°æ±¾ÎªThinkPHP5.0-5.0.23ÍêÕû°æ¡£
Îó²î¸´ÏÖ
ÍâµØÇéÐνÓÄÉThinkPHP 5.0.22ÍêÕû°æ+PHP5.5.38+Apache¾ÙÐи´ÏÖ¡£×°ÖÃÇéÐκóÖ´ÐÐPOC¼´¿ÉÖ´ÐÐϵͳÏÂÁÈçͼ£º
Îó²îÆÊÎö
ÒÔ¹ÙÍøÏÂÔصÄ5.0.22ÍêÕû°æ¾ÙÐÐÆÊÎö£¬Ê×Ïȶ¨Î»µ½Îó²îÒªº¦µã£º
thinkphp/library/think/Request.php:518
{
if (true === $method) {
// »ñÈ¡ÔʼÇëÇóÀàÐÍ
return $this->server('REQUEST_METHOD') ?: 'GET';
} elseif (!$this->method) {
if (isset($_POST[Config::get('var_method')])) {
$this->method = strtoupper($_POST[Config::get('var_method')]);
$this->{$this->method}($_POST);
} elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
$this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
} else {
$this->method = $this->server('REQUEST_METHOD') ?: 'GET';
}
}
return $this->method;
}
È¡µÃ$_POST[¡®_method¡¯]µÄÖµ²¢½«Æ丳ֵ¸ø$this->method£¬È»ºó¶¯Ì¬Å²ÓÃ$this->{$this->method}($_POST)¡£ÕâÒâζ׏¥»÷Õß¿ÉÒÔŲÓøÃÀàí§Ò⺯Êý²¢ÒÔ$_POST×÷ΪµÚÒ»¸ö²ÎÊý¡£ÈôÊǶ¯Ì¬Å²ÓÃ__constructº¯Êý£¬Ôò»áµ¼Ö´úÂëÖ´ÐС£
RequestÀàµÄ__constructº¯ÊýÈçÏ£º
protected function __construct($options = [])
{
foreach ($options as $name => $item) {
if (property_exists($this, $name)) {
$this->$name = $item;
}
}
if (is_null($this->filter)) {
$this->filter = Config::get('default_filter');
}
// ÉúÑÄ php://input
$this->input = file_get_contents('php://input');
}
ÓÉÓÚ$options²ÎÊý¿É¿Ø£¬¹¥»÷Õß¿ÉÒÔÁýÕÖ¸ÃÀàµÄfilterÊôÐÔ¡¢methodÊôÐÔÒÔ¼°getÊôÐÔµÄÖµ¡£¶øÔÚRequestÀàµÄparamº¯ÊýÖУº
{
if (empty($this->mergeParam)) {
$method = $this->method(true);
// ×Ô¶¯»ñÈ¡ÇëÇó±äÁ¿
switch ($method) {
case 'POST':
$vars = $this->post(false);
break;
case 'PUT':
case 'DELETE':
case 'PATCH':
$vars = $this->put(false);
break;
default:
$vars = [];
}
// Ä¿½ñÇëÇó²ÎÊýºÍURLµØµãÖеIJÎÊýºÏ²¢
$this->param = array_merge($this->param, $this->get(false), $vars, $this->route(false));
$this->mergeParam = true;
}
if (true === $name) {
// »ñÈ¡°üÀ¨ÎļþÉÏ´«ÐÅÏ¢µÄÊý×é
$file = $this->file();
$data = is_array($file) ? array_merge($this->param, $file) : $this->param;
return $this->input($data, '', $default, $filter);
}
return $this->input($this->param, $name, $default, $filter);
}
µ±$this->mergeParamΪ¿Õʱ£¬ÕâÀï»áŲÓÃ$this->get(false)¡£¸ú×Ù$this->getº¯Êý£º
{
if (empty($this->get)) {
$this->get = $_GET;
}
if (is_array($name)) {
$this->param = [];
return $this->get = array_merge($this->get, $name);
}
return $this->input($this->get, $name, $default, $filter);
}
¸Ãº¯ÊýĩβŲÓÃÁË$this->inputº¯Êý£¬²¢½«$this->get´«È룬¶ø$this->getµÄÖµÊǹ¥»÷Õ߿ɿصġ£¸ú×Ù$this->inputº¯Êý£º
{
if (false === $name) {
// »ñÈ¡ÔʼÊý¾Ý
return $data;
}
$name = (string) $name;
if ('' != $name) {
// ÆÊÎöname
if (strpos($name, '/')) {
list($name, $type) = explode('/', $name);
} else {
$type = 's';
}
// °´.²ð·Ö³É¶àάÊý×é¾ÙÐÐÅжÏ
foreach (explode('.', $name) as $val) {
if (isset($data[$val])) {
$data = $data[$val];
} else {
// ÎÞÊäÈëÊý¾Ý£¬·µ»ØĬÈÏÖµ
return $default;
}
}
if (is_object($data)) {
return $data;
}
}
// ÆÊÎö¹ýÂËÆ÷
$filter = $this->getFilter($filter, $default);
if (is_array($data)) {
array_walk_recursive($data, [$this, 'filterValue'], $filter);
reset($data);
} else {
$this->filterValue($data, $name, $filter);
}
if (isset($type) && $data !== $default) {
// Ç¿ÖÆÀàÐÍת»»
$this->typeCast($data, $type);
}
return $data;
}
¸Ãº¯ÊýŲÓÃÁË$this->getFileterÈ¡µÃ¹ýÂËÆ÷¡£º¯ÊýÌåÈçÏ£º
{
if (is_null($filter)) {
$filter = [];
} else {
$filter = $filter ?: $this->filter;
if (is_string($filter) && false === strpos($filter, '/')) {
$filter = explode(',', $filter);
} else {
$filter = (array) $filter;
}
}
$filter[] = $default;
return $filter;
}
$this->filterµÄÖµÊǹ¥»÷Õßͨ¹ýŲÓýṹº¯ÊýÁýÕÖ¿ØÖƵģ¬½«¸ÃÖµ·µ»Øºó½«½øÈëµ½inputº¯Êý:
array_walk_recursive($data, [$this, 'filterValue'], $filter);
reset($data);
}
Éó²éfilterValueº¯ÊýÈçÏ£º
{
$default = array_pop($filters);
foreach ($filters as $filter) {
if (is_callable($filter)) {
// ŲÓú¯Êý»òÕßÒªÁì¹ýÂË
$value = call_user_func($filter, $value);
} elseif (is_scalar($value)) {
if (false !== strpos($filter, '/')) {
// ÕýÔò¹ýÂË
if (!preg_match($filter, $value)) {
// Æ¥Åä²»Àֳɷµ»ØĬÈÏÖµ
$value = $default;
break;
}
} elseif (!empty($filter)) {
// filterº¯Êý²»±£´æʱ, ÔòʹÓÃfilter_var¾ÙÐйýÂË
// filterΪ·ÇÕûÐÎֵʱ, ŲÓÃfilter_idÈ¡µÃ¹ýÂËid
$value = filter_var($value, is_int($filter) ? $filter : filter_id($filter));
if (false === $value) {
$value = $default;
break;
}
}
}
}
return $this->filterExp($value);
}
ÔÚcall_user_funcº¯ÊýµÄŲÓÃÖУ¬$filter¿É¿Ø£¬$value¿É¿Ø¡£Òò´Ë£¬¿ÉÖ´úÂëÖ´ÐС£
´ÓThinkPHP5µÄÈë¿Úµã×îÏÈÆÊÎö£º
thinkphp/library/think/App.php:77
{
$request = is_null($request) ? Request::instance() : $request;
try {
$config = self::initCommon();
// Ä£¿é/¿ØÖÆÆ÷°ó¶¨
if (defined('BIND_MODULE')) {
BIND_MODULE && Route::bind(BIND_MODULE);
} elseif ($config['auto_bind_module']) {
// Èë¿Ú×Ô¶¯°ó¶¨
$name = pathinfo($request->baseFile(), PATHINFO_FILENAME);
if ($name && 'index' != $name && is_dir(APP_PATH . $name)) {
Route::bind($name);
}
}
$request->filter($config['default_filter']);
// ĬÈÏÓïÑÔ
Lang::range($config['default_lang']);
// ¿ªÆô¶àÓïÑÔ»úÖƼì²âÄ¿½ñÓïÑÔ
$config['lang_switch_on'] && Lang::detect();
$request->langset(Lang::range());
// ¼ÓÔØϵͳÓïÑÔ°ü
Lang::load([
THINK_PATH . 'lang' . DS . $request->langset() . EXT,
APP_PATH . 'lang' . DS . $request->langset() . EXT,
]);
// ¼àÌý app_dispatch
Hook::listen('app_dispatch', self::$dispatch);
// »ñÈ¡Ó¦Óõ÷ÀíÐÅÏ¢
$dispatch = self::$dispatch;
// δÉèÖõ÷ÀíÐÅÏ¢Ôò¾ÙÐÐ URL ·Óɼì²â
if (empty($dispatch)) {
$dispatch = self::routeCheck($request, $config);
}
// ¼Í¼Ŀ½ñµ÷ÀíÐÅÏ¢
$request->dispatch($dispatch);
// ¼Í¼·ÓɺÍÇëÇóÐÅÏ¢
if (self::$debug) {
Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info');
Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info');
Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info');
}
// ¼àÌý app_begin
Hook::listen('app_begin', $dispatch);
// ÇëÇ󻺴æ¼ì²é
$request->cache(
$config['request_cache'],
$config['request_cache_expire'],
$config['request_cache_except']
);
$data = self::exec($dispatch, $config);
runº¯ÊýµÚÒ»ÐбãʵÀý»¯ÁËÒ»¸öRequestÀ࣬²¢¸³Öµ¸øÁË$request¡£È»ºóŲÓÃrouteCheck($request,$config)£º
{
$path = $request->path();
$depr = $config['pathinfo_depr'];
$result = false;
// ·Óɼì²â
$check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on'];
if ($check) {
// ¿ªÆô·ÓÉ
if (is_file(RUNTIME_PATH . 'route.php')) {
// ¶Áȡ·ÓÉ»º´æ
$rules = include RUNTIME_PATH . 'route.php';
is_array($rules) && Route::rules($rules);
} else {
$files = $config['route_config_file'];
foreach ($files as $file) {
if (is_file(CONF_PATH . $file . CONF_EXT)) {
// µ¼Èë·ÓÉÉèÖÃ
$rules = include CONF_PATH . $file . CONF_EXT;
is_array($rules) && Route::import($rules);
}
}
}
// ·Óɼì²â£¨Æ¾Ö¤Â·Óɽç˵·µ»Ø²î±ðµÄURLµ÷Àí£©
$result = Route::check($request, $path, $depr, $config['url_domain_deploy']);
$must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must'];
if ($must && false === $result) {
// ·ÓÉÎÞЧ
throw new RouteNotFoundException();
}
}
// ·ÓÉÎÞЧÆÊÎöÄ£¿é/¿ØÖÆÆ÷/²Ù×÷/²ÎÊý... Ö§³Ö¿ØÖÆÆ÷×Ô¶¯ËÑË÷
if (false === $result) {
$result = Route::parseUrl($path, $depr, $config['controller_auto_search']);
}
return $result;
}
ÕâÀïŲÓÃRoute::check¾ÙÐзÓɼì²â¡£º¯ÊýÈçÏ£º
{
//¼ì²éÆÊÎö»º´æ
if (!App::$debug && Config::get('route_check_cache')) {
$key = self::getCheckCacheKey($request);
if (Cache::has($key)) {
list($rule, $route, $pathinfo, $option, $matches) = Cache::get($key);
return self::parseRule($rule, $route, $pathinfo, $option, $matches, true);
}
}
// ÍÑÀë·ûÌæ»» È·±£Â·Óɽç˵ʹÓÃͳһµÄÍÑÀë·û
$url = str_replace($depr, '|', $url);
if (isset(self::$rules['alias'][$url]) || isset(self::$rules['alias'][strstr($url, '|', true)])) {
// ¼ì²â·ÓÉÓÖÃû
$result = self::checkRouteAlias($request, $url, $depr);
if (false !== $result) {
return $result;
}
}
$method = strtolower($request->method());
// »ñÈ¡Ä¿½ñÇëÇóÀàÐ͵Ä·ÓɹæÔò
$rules = isset(self::$rules[$method]) ? self::$rules[$method] : [];
// ¼ì²âÓòÃû°²ÅÅ
if ($checkDomain) {
self::checkDomain($request, $rules, $method);
}
// ¼ì²âURL°ó¶¨
$return = self::checkUrlBind($url, $rules, $depr);
if (false !== $return) {
return $return;
}
if ('|' != $url) {
$url = rtrim($url, '|');
}
$item = str_replace('|', '/', $url);
if (isset($rules[$item])) {
// ¾²Ì¬Â·ÓɹæÔò¼ì²â
$rule = $rules[$item];
if (true === $rule) {
$rule = self::getRouteExpress($item);
}
if (!empty($rule['route']) && self::checkOption($rule['option'], $request)) {
self::setOption($rule['option']);
return self::parseRule($item, $rule['route'], $url, $rule['option']);
}
}
// ·ÓɹæÔò¼ì²â
if (!empty($rules)) {
return self::checkRoute($request, $rules, $url, $depr);
}
return false;
}
×¢ÖغìÉ«×ÖÌ岿·Ö¡£¶ÔÓ¦¿ªÍ·µÄµÚÒ»¸ö°ì·¨£¬Ò²¾ÍÊÇŲÓÃmethodº¯Êý¾ÙÐбäÁ¿ÁýÕÖ¡£ÕâÀïÐèÒªÁýÕÖµÄÊôÐÔÓÐ$this->filter,$this->method,$this->get¡£ÓÉÓÚ$request->method()µÄ·µ»ØֵΪ$this->method£¬ÒÔÊǸÃÖµÒ²ÐèÒª±»¿ØÖÆ¡£ÕâÀï·µ»ØÖµ¸³Öµ¸øÁË$method£¬È»ºóÈ¡³öself::$rules[$method]µÄÖµ¸ø$rules¡£ÕâÀïÐèҪעÖØ£ºThinkPHP5ÓÐ×Ô¶¯Àà¼ÓÔØ»úÖÆ£¬»á×Ô¶¯¼ÓÔØvendorĿ¼ÏµÄһЩÎļþ¡£¿ÉÊÇÍêÕû°æ¸ú½¹µã°æµÄvendorĿ¼½á¹¹ÊÇ·×ÆçÑùµÄ¡£
¿ÉÒÔ¿´µ½ÍêÕû°æ±È½¹µã°æ¶à³öÁ˼¸¸öÎļþ¼Ð¡£ÌØÊâÐèҪעÖصľÍÊÇthink-captcha/srcÕâ¸öÎļþ¼ÐÀïÓÐÒ»¸öhelper.phpÎļþ£º
ÕâÀïŲÓÃ\think\Route::getº¯Êý¾ÙÐзÓÉ×¢²áµÄ²Ù×÷¡£¶øÕâ²½²Ù×÷µÄÓ°Ïì¾ÍÊǸıäÁËÉÏÎÄÌáµ½µÄself::$rulesµÄÖµ¡£ÓÐÁËÕâ¸ö·ÓÉ£¬²Å»ª¾ÙÐÐRCE£¬²»È»²»Àֳɡ£ÕâÒ²¾ÍÊÇΪʲôֻӰÏìÍêÕû°æ£¬¶ø²»Ó°Ïì½¹µã°æµÄÔµ¹ÊÔÓÉ¡£´ËʱµÄself::$rulesµÄֵΪ:
ÄÇô£¬µ±¹¥»÷Õß¿ØÖÆ·µ»ØµÄ$methodµÄֵΪgetµÄʱ¼ä£¬$rulesµÄÖµ¾ÍÊÇÕâÌõ·ÓɵĹæÔò¡£È»ºó»Øµ½ÉÏÎÄÈ¡µ½$rulesÖ®ºó£¬Æ¾Ö¤´«ÈëµÄURLÈ¡µÃ$itemµÄÖµ£¬Ê¹µÃ$rules[$item]µÄֵΪcaptcha·ÓÉÊý×飬¾Í¿ÉÒÔ½øÒ»³ÌÐòÓõ½self::parseRuleº¯Êý¡£º¯ÊýÌåÂÔ³¤£¬ÕâÀïÈ¡Òªº¦µã£º
{
// ÆÊÎö·ÓɹæÔò
......
......
if ($route instanceof \Closure) {
// Ö´Ðбհü
$result = ['type' => 'function', 'function' => $route];
} elseif (0 === strpos($route, '/') || 0 === strpos($route, 'http')) {
// ·Óɵ½Öض¨ÏòµØµã
$result = ['type' => 'redirect', 'url' => $route, 'status' => isset($option['status']) ? $option['status'] : 301];
} elseif (0 === strpos($route, '\\')) {
// ·Óɵ½ÒªÁì
$method = strpos($route, '@') ? explode('@', $route) : $route;
$result = ['type' => 'method', 'method' => $method];
} elseif (0 === strpos($route, '@')) {
// ·Óɵ½¿ØÖÆÆ÷
$result = ['type' => 'controller', 'controller' => substr($route, 1)];
} else {
// ·Óɵ½Ä£¿é/¿ØÖÆÆ÷/²Ù×÷
$result = self::parseModule($route);
}
return $result;
}
´Ëʱת´ï½øÀ´µÄ$routeµÄֵΪ\think\captcha\CaptchaController@index¡£Òò´Ë½øÈëµÄÊDZê×¢ºìÉ«µÄif·ÖÖ§ÖС£ÔÚÕâ¸ö·ÖÖ§ÖУ¬$resultµÄ¡¯type¡¯¼ü¶ÔÓ¦µÄֵΪ¡®method¡¯¡£È»ºó½«$result²ã²ã·µ»Øµ½runº¯ÊýÖУ¬²¢¸³Öµ¸øÁË$dispatch¡£
if (empty($dispatch)) {
$dispatch = self::routeCheck($request, $config);
}
// ¼Í¼Ŀ½ñµ÷ÀíÐÅÏ¢
$request->dispatch($dispatch);
// ¼Í¼·ÓɺÍÇëÇóÐÅÏ¢
if (self::$debug) {
Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info');
Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info');
Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info');
}
// ¼àÌý app_begin
Hook::listen('app_begin', $dispatch);
// ÇëÇ󻺴æ¼ì²é
$request->cache(
$config['request_cache'],
$config['request_cache_expire'],
$config['request_cache_except']
);
$data = self::exec($dispatch, $config);
È»ºó½«$dispatch´øÈëµ½self::execº¯ÊýÖУº
{
switch ($dispatch['type']) {
case 'redirect': // Öض¨ÏòÌøת
$data = Response::create($dispatch['url'], 'redirect')
->code($dispatch['status']);
break;
case 'module': // Ä£¿é/¿ØÖÆÆ÷/²Ù×÷
$data = self::module(
$dispatch['module'],
$config,
isset($dispatch['convert']) ? $dispatch['convert'] : null
);
break;
case 'controller': // Ö´ÐпØÖÆÆ÷²Ù×÷
$vars = array_merge(Request::instance()->param(), $dispatch['var']);
$data = Loader::action(
$dispatch['controller'],
$vars,
$config['url_controller_layer'],
$config['controller_suffix']
);
break;
case 'method': // »Øµ÷ÒªÁì
$vars = array_merge(Request::instance()->param(), $dispatch['var']);
$data = self::invokeMethod($dispatch['method'], $vars);
break;
case 'function': // ±Õ°ü
$data = self::invokeFunction($dispatch['function']);
break;
case 'response': // Response ʵÀý
$data = $dispatch['response'];
break;
default:
throw new \InvalidArgumentException('dispatch type not support');
}
return $data;
}
½øÈëµ½ºìÉ«±ê×¢µÄ·ÖÖ§£¬¸Ã·Ö֧ŲÓÃRequestÀàµÄparamÒªÁì¡£Òò´Ë£¬Öª×ãÁËʹÓÃÁ´µÄµÚÈý²½£¬Ôì³ÉÏÂÁîÖ´ÐС£
ÓÅ·¢¹ú¼ÊÍøÕ¾¹ÙÍøADLabÇå¾²Ñо¿Ô±¶ÔThinkPHP5.0-5.0.23ÿ¸ö°æ±¾¶¼¾ÙÐÐÁËÆÊÎö£¬·¢Ã÷ThinkPHP5.0.2-5.0.23¿ÉÒÔʹÓÃͳһ¸öPOC£¬¶øThinkPHP5.0-5.0.1ÐèÒª¸ü¸ÄÒ»ÏÂPOC£¬Ôµ¹ÊÔÓÉÔÚÓÚRoute.phpµÄruleº¯ÊýµÄÒ»¸öʵÏÖС²î±ð¡£
ThinkPHP5.0-5.0.1°æ±¾µÄthinkphp/library/think/Route.php:235£¬½«$typeת»»³ÉÁË´óд£º

ÔÚThinkPHP5.0.2-5.0.23°æ±¾ÖУ¬ruleº¯ÊýÖÐÈ´½«$typeת»»³ÉÁËСд£º
²¹¶¡ÆÊÎö

½á ÂÛ
Ç¿ÁÒ½¨ÒéÓû§Éý¼¶µ½ThinkPHP5.0.24°æ±¾£¬²¢ÇÒ²»Òª¿ªÆôdebugģʽ£¬ÒÔÃâÔâÊܹ¥»÷¡£