PHP MVC框架无法获取Query String及REQUEST_URI未定义警告求助
PHP MVC框架无法获取Query String问题
问题现象
- 访问
http://localhost/posts/index时,正常输出Hello from the index action in the Home controller - 访问
http://localhost/posts/index?page=1时,输出内容无变化,无法获取page=1参数,同时出现PHP Warning: Undefined array key 'REQUEST_URI'警告
相关代码
index.php
<?php /** * Front controller * * PHP version 5.4 */ /** * Autoloader */ spl_autoload_register(function ($class) { $root = dirname(__DIR__); // get the parent directory $file = $root . '/' . str_replace('\\', '/', $class) . '.php'; if (is_readable($file)) { require $root . '/' . str_replace('\\', '/', $class) . '.php'; } }); /** * Routing */ $router = new Core\Router(); // Add the routes $router->add('', ['controller' => 'Home', 'action' => 'index']); $router->add('{controller}/{action}'); $router->add('{controller}/{id:\d+}/{action}'); $router->dispatch($_SERVER['REQUEST_URI']);
.htaccess
# Remove index.php from the request but maintain the query string <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] #RewriteRule ^(.*)$ /index.php?$1 </IfModule> #---working-- #<IfModule mod_rewrite.c> # RewriteEngine On # Options +FollowSymLinks -Indexes # RewriteCond %{REQUEST_FILENAME} !-d # RewriteCond %{REQUEST_FILENAME} !-f # RewriteRule ^(.*)$ index.php [QSA,L] #</IfModule> #-also has no error--- #<IfModule mod_rewrite.c> # RewriteEngine On # RewriteBase / # RewriteCond %{REQUEST_FILENAME} !-f # RewriteCond %{REQUEST_FILENAME} !-d # RewriteRule . /index.php [L] #</IfModule>
Core\Router类
<?php namespace Core; /** * Router * * PHP version 5.4 */ class Router { /** * Associative array of routes (the routing table) * @var array */ protected $routes = []; /** * Parameters from the matched route * @var array */ protected $params = []; /** * Add a route to the routing table * * @param string $route The route URL * @param array $params Parameters (controller, action, etc.) * * @return void */ public function add($route, $params = []) { // Convert the route to a regular expression: escape forward slashes $route = preg_replace('/\//', '\\\\/', $route); // Convert variables e.g. {controller} $route = preg_replace('/\{([a-z]+)\}/', '(?P<\1>[a-z-]+)', $route); // Convert variables with custom regular expressions e.g. {id:\d+} $route = preg_replace('/\{([a-z]+):([^\}]+)\}/', '(?P<\1>\2)', $route); // Add start and end delimiters, and case insensitive flag $route = '/^' . $route . '$/i'; $this->routes[$route] = $params; } /** * Get all the routes from the routing table * * @return array */ public function getRoutes() { return $this->routes; } /** * Match the route to the routes in the routing table, setting the $params * property if a route is found. * * @param string $url The route URL * * @return boolean true if a match found, false otherwise */ public function match($url) { foreach ($this->routes as $route => $params) { if (preg_match($route, $url, $matches)) { // Get named capture group values foreach ($matches as $key => $match) { if (is_string($key)) { $params[$key] = $match; } } $this->params = $params; return true; } } return false; } /** * Get the currently matched parameters * * @return array */ public function getParams() { return $this->params; } /** * Dispatch the route, creating the controller object and running the * action method * * @param string $url The route URL * * @return void */ public function dispatch ($url) { $url = $this->removeQueryStringVariables($url); #$url = trim($url, "/"); no route found if ($this->match($url)) { $controller = $this->params['controller']; $controller = $this->convertToStudlyCaps($controller); $controller = "App\Controllers\$controller"; if (class_exists($controller)) { $controller_object = new $controller(); $action = $this->params['action']; $action = $this->convertToCamelCase($action); if (is_callable([$controller_object, $action])) { $controller_object->$action(); } else { echo "Method $action (in controller $controller) not found"; } } else { echo "Controller class $controller not found"; } } else { echo 'No route matched.'; } } /** * Convert the string with hyphens to StudlyCaps, * e.g. post-authors => PostAuthors * * @param string $string The string to convert * * @return string */ protected function convertToStudlyCaps($string) { return str_replace(' ', '', ucwords(str_replace('-', ' ', $string))); } /** * Convert the string with hyphens to camelCase, * e.g. add-new => addNew * * @param string $string The string to convert * * @return string */ protected function convertToCamelCase($string) { return lcfirst($this->convertToStudlyCaps($string)); } /** * Remove the query string variables from the URL (if any). As the full * query string is used for the route, any variables at the end will need * to be removed before the route is matched to the routing table. For * example: * * URL $_SERVER['QUERY_STRING'] Route * ------------------------------------------------------------------- * localhost '' '' * localhost/? '' '' * localhost/?page=1 page=1 '' * localhost/posts?page=1 posts&page=1 posts * localhost/posts/index posts/index posts/index * localhost/posts/index?page=1 posts/index&page=1 posts/index * * A URL of the format localhost/?page (one variable name, no value) won't * work however. (NB. The .htaccess file converts the first ? to a & when * it's passed through to the $_SERVER variable). * * @param string $url The full URL * * @return string The URL with the query string variables removed */ protected function removeQueryStringVariables($url) { if ($url != '') { $parts = explode('&', $url, 2); if (strpos($parts[0], '=') === false) { $url = $parts[0]; } else { $url = ''; } } return $url; } }
App\Controllers\Home控制器
<?php namespace App\Controllers; /* * Home controller * */ ;Class Home { /** * show the index page * @return void */ public function index() { echo 'Hello from the index action in the Home controller'; } }
解决方案
1. 修复Undefined array key 'REQUEST_URI'警告
$_SERVER['REQUEST_URI']并非在所有环境下都存在,替换为更可靠的获取方式:
// 修改index.php中的dispatch调用 $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : $_SERVER['PHP_SELF']; $router->dispatch($uri);
2. 让控制器获取Query String参数
当前removeQueryStringVariables仅用于路由匹配,并未传递查询参数给控制器,可通过两种方式解决:
方式一:直接在控制器中使用$_GET
修改Home控制器的index方法:
public function index() { $page = isset($_GET['page']) ? $_GET['page'] : 1; echo 'Hello from the index action in the Home controller, page: ' . $page; }
方式二:将查询参数注入控制器(符合MVC规范)
修改Router的dispatch方法,将$_GET作为参数传入控制器方法:
// 修改Router中调用action的代码 if (is_callable([$controller_object, $action])) { $controller_object->$action($_GET); } else { echo "Method $action (in controller $controller) not found"; } // 同步修改Home控制器的index方法 public function index($params) { $page = isset($params['page']) ? $params['page'] : 1; echo 'Hello from the index action in the Home controller, page: ' . $page; }
3. 确认.htaccess的QSA规则生效
确保启用的.htaccess规则包含[QSA,L],该规则会保留原始查询字符串,保证$_GET能正确获取参数。建议仅保留第一组生效的规则:
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </IfModule>
验证
修改后访问http://localhost/posts/index?page=1,应能正确输出包含page参数的内容,且不再出现REQUEST_URI警告。
内容的提问来源于stack exchange,提问作者Dodee
相关产品推荐
相关产品推荐

