You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 01:15:31