Slim Framework:路由和控制器

问题描述 投票:3回答:1

最初,我的Slim Framework应用具有经典结构

(index.php)

<?php
$app = new \Slim\Slim();
$app->get('/hello/:name', function ($name) {
    echo "Hello, $name";
});
$app->run();

但是随着我添加更多路线和路线组,我转向了基于控制器的方法:

index.php

<?php
$app = new \Slim\Slim();
$app->get('/hello/:name', 'HelloController::hello');
$app->run();

HelloController.php

<?php
class HelloController {
    public static function hello($name) {
        echo "Hello, $name";
    }
}

这有效,对组织应用程序结构很有帮助,同时让我为每种控制方法建立单元测试。

但是,我不确定这是正确的方法。我觉得我是在特殊基础上嘲笑Silex的mount方法,那不是很好。在每个Controller方法中使用$ app上下文要求我使用\ Slim \ Slim :: getInstance(),这似乎比仅使用$ app像闭包一样效率低。

所以...有没有兼顾效率和订单的解决方案,或者效率是以路线/封锁的噩梦为代价的?

php slim
1个回答
6
投票

我想我可以和你们分享我的所作所为。我注意到Slim \ Slim中的每个route方法在某个时候都称为方法mapRoute

((为清楚起见,我更改了官方源代码的缩进)

Slim.php

 protected function mapRoute($args)
    {
        $pattern = array_shift($args);
        $callable = array_pop($args);

        $route = new \Slim\Route(
              $pattern, 
              $callable, 
              $this->settings['routes.case_sensitive']
        );
        $this->router->map($route);
        if (count($args) > 0) {
            $route->setMiddleware($args);
        }

        return $route;
    }

反过来,称为setCallable的Slim \ Route构造函数>

Route.php

public function setCallable($callable)
{
    $matches = [];
    $app = $this->app;
    if (
         is_string($callable) && 
         preg_match(
           '!^([^\:]+)\:([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$!', 
           $callable, 
           $matches
         )
       ) {
            $class = $matches[1];
            $method = $matches[2];
            $callable = function () use ($class, $method) {
                static $obj = null;
                if ($obj === null) {
                    $obj = new $class;
                }
                return call_user_func_array([$obj, $method], func_get_args());
            };
        }

        if (!is_callable($callable)) {
            throw new \InvalidArgumentException('Route callable must be callable');
        }

        $this->callable = $callable;
    }

基本上是哪个

  • 如果$callable是字符串,并且(注意单个冒号)的格式为ClassName method,则它是非静态的,因此Slim将实例化该类,然后调用该方法在它上面。
  • 如果无法调用,则引发异常(足够合理)
  • 否则,无论它是什么(ClassName :: staticMethod,闭包,函数名称),它都将按原样使用。
  • [ClassName应该是FQCN,所以更像\MyProject\Controllers\ClassName

实例化控制器(或其他对象)的点是注入App实例的好机会。因此,对于初学者来说,我overrode mapRoute

将应用实例注入到其中:

\Util\MySlim

 protected function mapRoute($args)
    {
        $pattern = array_shift($args);
        $callable = array_pop($args);

        $route = new \Util\MyRoute(
            $this, // <-- now my routes have a reference to the App
            $pattern, 
            $callable, 
            $this->settings['routes.case_sensitive']
        );
        $this->router->map($route);
        if (count($args) > 0) {
            $route->setMiddleware($args);
        }

        return $route;
    }

因此,基本上\Util\MyRoute\Slim\Route,在其构造函数中带有一个额外的参数,我将其存储为$this->app

此时,getCallable

可以将应用程序注入到每个需要实例化的控制器中

\Util\MyRoute.php

public function setCallable($callable)
{
    $matches = [];
    $app = $this->app;
    if (
       is_string($callable) && 
       preg_match(
          '!^([^\:]+)\:([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$!', 
          $callable, 
          $matches
          )
       ) {
        $class = $matches[1];
        $method = $matches[2];

        $callable = function () use ($app, $class, $method) {
            static $obj = null;
            if ($obj === null) {
                $obj = new $class($app); // <--- now they have the App too!!
            }
            return call_user_func_array([$obj, $method], func_get_args());
        };
    }

    if (!is_callable($callable)) {
        throw new \InvalidArgumentException('Route callable must be callable');
    }

    $this->callable = $callable;
}

就这样。使用这两个类,只要我使用单个冒号将控制器与方法分开,就可以将$app注入到在路由中声明的任何控制器中。使用paamayim nekudotayim

将把该方法称为静态方法,因此,如果我尝试访问其中的$this->app,将引发错误。

我使用blackfire.io进行了测试,...性能提升可忽略不计。

Pros

  • 这为我省去了在每个静态方法调用上调用$ app = \ Slim \ Slim :: getInstance()的痛苦,总共占大约100行文本。
  • 通过使每个控制器都继承于抽象控制器类,从而为进一步的优化开辟了道路,而后者又将app方法包装为便捷方法。
  • 这使我对Slim的请求和响应生命周期有了更好的了解。
  • 缺点:

  • 性能提升可忽略不计
  • 您必须将所有路由转换为使用单个冒号而不是paamayin,并且所有控制器方法都从静态转换为动态。
  • Slim基类的继承权在推出3.0.0版时可能会中断
  • 结束语 :( 4年后)

在Slim v3中,他们删除了静态访问器。反过来,如果您使用相同的约定FQCN\ClassName:method,则将使用应用程序的容器实例化控制器。而且,该方法从路由接收请求,响应和$args。这样的DI,很大的IoC。我非常喜欢。

回顾我对Slim 2的使用方法,它打破了替换掉的最基本原理(Liskov替代)。

class Route extends \Slim\Route
{
  protected $app;
  public function __construct($app, $pattern, $callable, $caseSensitive = true) {
   ...
   }
}

应该是

class Route extends \Slim\Route
{
  protected $app;
  public function __construct($pattern, $callable, $caseSensitive = true, $app = null) {
   ...
   }
}

因此它不会违反合同,可以透明地使用。

© www.soinside.com 2019 - 2024. All rights reserved.