【问题标题】:Replace matches from one string in another string替换另一个字符串中的一个字符串的匹配项
【发布时间】:2015-08-17 23:47:54
【问题描述】:

我正在为我的 PHP MVC 应用程序编写一个路由器,我目前需要找到一种方法来使用路由中的匹配项作为控制器和操作的变量。

例如,如果我有以下路线:/users/qub1/home

我想使用类似这样的正则表达式:\/users\/(?!/).*\/(?!/).*

然后我想指定这样的操作:$2(在示例中,这将是 home)

传递给动作的参数如下:$1(在示例中,这将是 qub1)。

这将执行类似于以下的代码:

$controller = new UsersController();
$controller->$2($1);

配置的路线是这样存储的:

public function setRoute($route, $regex = false, $controller = 'Index', $action = 'index', $parameters = array()) {
        if(!$regex) {
            $route = preg_quote($route, '/');
        }

        $this->routes[] = [
                'route' => $route,
                'controller' => $controller,
                'action' => $action,
                'parameters' => $parameters
        ];
    }

上述示例的存储位置如下:$router->setRoute('\/users\/(?!/).*\/(?!/).*', true, 'User', '$2', [$1]);

所以本质上,我想使用一个正则表达式中的匹配组作为变量来替换另一个正则表达式(如果这有意义的话)。

我希望我已经足够准确地描述了我的问题。感谢您的帮助。

编辑:

我目前用来解析路由的代码(它不起作用,但它应该说明我想要实现的目标):

public function executeRoute($route) {
        // Loop over available routes
        foreach($this->routes as $currentRoute) {
            // Check if the current route matches the provided route
            if(preg_match('/^' . $currentRoute['route'] . '$/', '/' . $route, $matches)) {
                // If it matches, perform the current route's action
                // Define names
                $controllerClass = preg_replace('\$.*\d', $matches[str_replace('$', '', '$1')], ucfirst($currentRoute['controller'] . 'Controller'));
                $actionMethod = preg_replace('\$.*\d', $matches[str_replace('$', '', '$1')], strtolower($currentRoute['action']) . 'Action');
                $parameters = preg_replace('\$.*\d', $matches[str_replace('$', '', '$1')], join(', ', $currentRoute['parameters']));

                // Create the controller
                $controller = new $controllerClass();
                $controller->$actionMethod($parameters);

                // Return
                return;
            }
        }
}

【问题讨论】:

  • 嗨,你可以试试 strstr();在 php php.net/strstr
  • 路由的重点是隐藏内部实现,这样可以很容易地添加新的路由。你真的让它变得更复杂,它需要是
  • @KarthikKeyan 我用过 strtr 并且它有效。我首先必须将匹配数组转换为关联数组(在索引前加上'$'),然后我将其用作针头参数。我现在可以使用以下方式轻松设置路由:$router->setRoute('\/users\/([^\/]*)', true, 'Users', 'index', ['$1']);,它将在幕后执行类似的操作:UsersController->indexAction($1) 其中 $1 将是用户名。

标签: php regex model-view-controller router


【解决方案1】:

虽然我不确定这是一种设计良好的方法,但它是可行的。这是在if 中替换您的代码:

// you already specify the controller name, so no need for replacing
$controllerClass = ucfirst($currentRoute['controller'] . 'Controller');

// also here, no need to replace. You just need to get the right element from the array
$actionMethod = strtolower($matches[ltrim($currentRoute['action'], '$')] . 'Action';

// here I make the assumption that this parameter is an array. You might want to add a check here
$parameters = array();
foreach ($currentRoute['parameters'] as $parameter) {
    $parameters[] = $matches[ltrim($parameter, '$')];
}

// check before instantiating
if (!class_exists($controllerClass)) {
    die('invalid controller');
}
$controller = new $controllerClass();

// also check before invoking the method
if (!method_exists($controller, $actionMethod)) {
    die('invalid method');
}
// this PHP function allows to call the function with a variable number of parameters
call_user_func_array(array($controller, $actionMethod), $parameters);

你的方法不是很受欢迎的一个原因是你做了很多假设:

  • 正则表达式的组数必须与您在其他参数中使用的组数一样多
  • 如果您对正则表达式不精确,则可以在代码中调用任何方法

也许这对你的项目来说已经足够了,但如果你想创建一些不是为了教育目的的东西,你应该考虑使用一个成熟的路由器。

【讨论】:

  • 感谢您的代码,它有帮助!是的,这主要是作为一种学习体验(为了更熟悉 MVC 模式),所以我不会在实际网站上使用它。至于您所说的关于用户能够调用任何方法的内容,一旦应用了正则表达式,该字符串始终会附加“Action”,因此他们应该只能调用任何以 action 结尾的方法,这是他们应该使用的所有方法无论如何都能访问。还是我忽略了那里的东西?
  • 它应该没问题,但我总是犹豫是否将用户提供的字符串付诸实践。您永远无法猜测攻击者可能会想到什么。
猜你喜欢
  • 1970-01-01
  • 2021-07-11
  • 1970-01-01
  • 1970-01-01
  • 2021-06-15
  • 1970-01-01
  • 2023-04-10
  • 2013-12-03
  • 1970-01-01
相关资源
最近更新 更多