Slim Framework: rutas y controladores

Originalmente, mi aplicación Slim Framework tenía la estructura clásica

(index.php)

get('/hello/:name', function ($name) { echo "Hello, $name"; }); $app->run(); 

Pero a medida que agregué más rutas y grupos de rutas, pasé a un enfoque basado en controlador:

index.php

 get('/hello/:name', 'HelloController::hello'); $app->run(); 

HelloController.php

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

Esto funciona, y ha sido útil organizar la estructura de mi aplicación, al mismo tiempo que me permite construir pruebas unitarias para cada método de control.

Sin embargo, no estoy seguro de que este sea el camino correcto. Siento que me estoy burlando del método de mount de Silex en una base sui generis, y eso no puede ser bueno. Usar el contexto $ app dentro de cada método del Controlador requiere que use \ Slim \ Slim :: getInstance (), que parece menos eficiente que simplemente usar $ app como una lata de cierre.

Entonces … ¿hay una solución que permita tanto la eficiencia como el orden, o la eficiencia se consigue a costa de una pesadilla de ruta / cierre?

Creo que puedo compartir lo que hice con ustedes. Me di cuenta de que cada método de ruta en Slim \ Slim en algún momento llamado el método 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; } 

A su vez, el constructor Slim \ Route llamado setCallable

Route.php

 public function setCallable($callable) { $matches = []; 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]; //\Util\Helpers::prdie('Matches are', $matches); $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; } 

Y esto significa que las rutas declaradas con un invocable en la forma Controlador: el método ( tenga en cuenta los dos puntos ) se interpretaron como métodos no estáticos y, por lo tanto, cuando se instanciaron en el cierre invocable. Así que extendí Slim \ Slim y Slim \ Route. En el primero, superé mapRoute

\ Util \ MySlim

  protected function mapRoute($args) { $pattern = array_shift($args); $callable = array_pop($args); $route = new \Util\MyRoute($this, $pattern, $callable, $this->settings['routes.case_sensitive']); $this->router->map($route); if (count($args) > 0) { $route->setMiddleware($args); } return $route; } 

Cuando instalas \ Util \ MyRoute, también transfiero $ app, porque en MyRoute hay una propiedad protegida y el método getCallable lo usa para instanciar el controlador, lo que permite utilizar métodos no estáticos con la propiedad $ app ya en ellos.

\ 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); } 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; } 

Entonces ahí está. Usando estas dos clases, puedo inyectar $ app en cualquier controlador que declare en la ruta, siempre que use un solo punto para separar el controlador del método. Usar paamayim nekudotayim llamará al método como estático y por lo tanto arrojará un error si bash acceder a $ this-> app dentro de él.

Ejecuté pruebas usando blackfire.io y … la ganancia de rendimiento es insignificante.

Pros :

  • Esto me ahorra la molestia de llamar a $ app = \ Slim \ Slim :: getInstance () en cada método estático de contabilidad de llamadas para alrededor de 100 líneas de texto en general.
  • abre el camino para una mayor optimización haciendo que cada controlador herede de una clase abstracta de controlador, que a su vez envuelve los métodos de la aplicación en métodos de conveniencia.
  • me hizo entender el ciclo de vida de solicitud y respuesta de Slim un poco mejor.

Contras:

  • las ganancias de rendimiento son insignificantes
  • tienes que convertir todas tus rutas para usar un solo punto y coma en lugar de paamayin, y todos tus métodos de control de estático a dynamic.
  • la herencia de las clases base Slim podría romperse cuando se desplieguen v 3.0.0