为我自己的PHP框架创建自己的参数解析器

时间:2015-08-03 23:24:28

标签: php web-services rest symfony symfony-http-foundation

我决定制作我自己的PHP迷你框架,我将用于现实生活中的工作,为社交应用程序创建Web服务。

我开始使用Fabien Potencier的指南,在Symfony的组件之上创建自己的框架 - http://symfony.com/doc/current/create_framework/index.html。 我真的很喜欢他的classLoader和http-foundation库,并决定将它们集成在一起。

我阅读了整个教程,但我决定停止将Symfony的组件集成到教程的第5部分,他将访问Symfony http内核,路由匹配器和控制器解析器(不包括那些)。

我的框架的前端控制器和路由映射器文件有问题。

front.php(前端控制器)

<?php 

        require 'config/config.php';
        require 'config/routing.php';
        require 'src/autoloader.php';

        use Symfony\Component\HttpFoundation\Request;
        use Symfony\Component\HttpFoundation\Response;

        $request = Request::createFromGlobals();

        $response = new Response();


        $path = $request->getPathInfo();

        if(isset($siteMap[$path])) {
            call_user_func_array('src\Controllers' .'\\' . $siteMap[$path]['controller'] . '::' . $siteMap[$path]['action'], array($siteMap[$path]['arguments']));
        } else {    
            $response->setStatusCode('404');
            $response->setContent('Page not found');
        }

    $response->send();
?>  

我的路由文件:

/*  
    Map of routes
*/

$siteMap = array('/' => array('controller' => 'IndexController', 'action' => 'indexAction', 'arguments' => ''),
                    '/categories' => array('controller' => 'CategoriesController', 'action' => 'indexAction', '

我现在想知道的是没有进一步使用Symfony的组件,我该怎么做这样的事情:

在我的路由文件中,我想添加一个像&#39; / hello&#39;使用Controller - Hello控制器和参数名称,年龄,性别,它们对应于浏览器中的请求GET www.base / hello / samuel / 11 / male。

在HelloController中有一个indexAction($ name,$ age,$ gender){...}。我已经尝试过查看Symfony的源代码,但到目前为止它还没有找到我(我花了很多时间去了解库的源代码)。我将模块化和分离前端控制器和控制器的功能进一步解决,但我想把它搞定。

啊,以及关于进一步构建我的框架的任何建议都会受到欢迎(我正在创建一个类似于REST的框架式Web服务,它需要可扩展且快速,并且每秒可处理数万个请求)。

2 个答案:

答案 0 :(得分:3)

如果您想知道Symfony如何转换自己的路由格式,您必须为路由定义正则表达式并将其与请求进行匹配,例如:&#39; / path / to / {id}&#39;到正则表达式,请参阅RouteCompiler :: compilePattern。这超出了这个问题的范围,我将不提供任何代码。

示例路由的正则表达式将类似^/hello/(\w*)/(\d*)/(\w*)/$,这将匹配&#39; / hello /任何字符串/任何数字/任何字符串/&#39;例如:/hello/samuel/11/male(不要忘记^$,它们会匹配字符串的开头和结尾。)

您必须为每个路由提供一个正则表达式,并对所有路由(直到一个匹配)执行preg_match(rawurldecode($request->getPathInfo(), $regex /* as created earlier*/, $matches),而不是isset($sitemap[$path])如果返回false,则路由不匹配。否则你必须得到你的控制器的参数:

preg_match(rawurldecode($request->getPathInfo(), $regex /* as created earlier*/, $matches)
$args = array();
// we omit the first element of $matches, since it's the full string for the match
for($i = 1; $i < count($matches); $i++){
  $args[] = $matches[$i];
}

现在您可以将$args与默认参数合并并调用控制器。

请参阅UrlMatcher :: matchCollection(),了解Symfony如何根据正则表达式匹配每条路由,并构造参数。

答案 1 :(得分:0)

我想补充一些其他信息。在Symfony中,您可以将请求的参数提取到方法参数中:

public function fooAction($bar);

在这种情况下,它会在$request->attributes中查找包含bar的密钥。如果值存在,它会将此值注入您的操作。在Symfony 3.0及更低版本中,这是通过ControllerResolver::getArguments()完成的。从Symfony 3.1开始,您将能够实现或扩展ArgumentResolver

使用ArgumentResolver意味着您可以轻松地将自己的功能添加到解析中。 The existing value resolvers are located here