Browse Source

Route middleware.

Frederic G. MARAND 6 years ago
parent
commit
837f761afb

+ 14 - 14
.idea/silex-book.iml

@@ -85,20 +85,6 @@
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="module-library">
-      <library name="PHARS">
-        <CLASSES>
-          <root url="phar://$MODULE_DIR$/vendor/phpunit/phpunit/tests/_files/phpunit-example-extension/tools/phpunit.d/phpunit-example-extension-1.0.1.phar/" />
-          <root url="phar://$MODULE_DIR$/vendor/phar-io/manifest/tests/_fixture/test.phar/" />
-          <root url="phar://$MODULE_DIR$/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/phar/phar-sample.phar/" />
-        </CLASSES>
-        <SOURCES>
-          <root url="phar://$MODULE_DIR$/vendor/phpunit/phpunit/tests/_files/phpunit-example-extension/tools/phpunit.d/phpunit-example-extension-1.0.1.phar/" />
-          <root url="phar://$MODULE_DIR$/vendor/phar-io/manifest/tests/_fixture/test.phar/" />
-          <root url="phar://$MODULE_DIR$/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/phar/phar-sample.phar/" />
-        </SOURCES>
-      </library>
-    </orderEntry>
     <orderEntry type="module-library">
       <library name="PHP" type="php">
         <CLASSES>
@@ -265,5 +251,19 @@
         </SOURCES>
       </library>
     </orderEntry>
+    <orderEntry type="module-library">
+      <library name="PHARS">
+        <CLASSES>
+          <root url="phar://$MODULE_DIR$/vendor/phar-io/manifest/tests/_fixture/test.phar/" />
+          <root url="phar://$MODULE_DIR$/vendor/phpunit/phpunit/tests/_files/phpunit-example-extension/tools/phpunit.d/phpunit-example-extension-1.0.1.phar/" />
+          <root url="phar://$MODULE_DIR$/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/phar/phar-sample.phar/" />
+        </CLASSES>
+        <SOURCES>
+          <root url="phar://$MODULE_DIR$/vendor/phar-io/manifest/tests/_fixture/test.phar/" />
+          <root url="phar://$MODULE_DIR$/vendor/phpunit/phpunit/tests/_files/phpunit-example-extension/tools/phpunit.d/phpunit-example-extension-1.0.1.phar/" />
+          <root url="phar://$MODULE_DIR$/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/phar/phar-sample.phar/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
   </component>
 </module>

+ 7 - 0
src/app.php

@@ -2,6 +2,7 @@
 
 use demo\AppRoute;
 use demo\Logger;
+use demo\Services\Timer;
 use Demo\UserConverter;
 use Silex\Application;
 use Silex\Provider\AssetServiceProvider;
@@ -12,7 +13,13 @@ use Symfony\Component\HttpFoundation\Request;
 
 $app = new Application();
 
+// Non-closure classes are used as parameters, not Pimple services, but may
+// nonetheless be used as such.
+$app['timer'] = Timer::create();
+
+// Closures are the basic form of Pimple services.
 $app['logger'] = function () { return new Logger(); };
+
 $app->register(new ServiceControllerServiceProvider());
 $app->register(new AssetServiceProvider());
 $app->register(new TwigServiceProvider());

+ 37 - 4
src/controllers.php

@@ -29,9 +29,14 @@ use demo\Controllers\UserController;
 use demo\Errors\GenericErrorHandler;
 use demo\Errors\HttpErrorHandler;
 use demo\Middleware\AfterAll;
+use demo\Middleware\AfterRoute;
 use demo\Middleware\BeforeAll;
+use demo\Middleware\BeforeRoute;
 use demo\Middleware\Finish;
 use demo\Views\JsonView;
+use Silex\Application;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
 
 //Request::setTrustedProxies(array('127.0.0.1'));
 
@@ -59,12 +64,36 @@ $app->before('mwBeforeAll:next', $priority = 0);
 
 // Callable service example.
 // "After": triggered on KernelEvents::RESPONSE.
-$app->after('mwAfterAll');
+$app->after('mwAfterAll', $priority = 0);
 
 // Plain callable example.
 // "Finish": triggered on KernelEvents::TERMINATE.
 $app->finish([Finish::class, 'handle']);
 
+/* ---- Route middleware -------------------------------------------------------
+Middleware is invoked as event subscribers on KernelEvents with fixed priority.
+
+- After() middleware is called on KernelEvents::Response.
+  - Fixed priority 128 means before the Application after() default 0.
+  - App middleware EARLY_EVENT == 512 > 0 => app MW before route MW
+  - App middleware 0 or LATE_EVENT == 512 > 0 => app MW after route MW
+  - It receives Request, Response, Application.
+- Before() middleware is called on KernelEvents::Request.
+  - App middleware EARLY_EVENT / 0 / LATE_EVENT are all > -1024 means global
+    before() middleware is /always/ invoked before any before() route
+    middleware.
+  - Using manual values < -1024 allow running route middleware earlier, but with
+    undefined consequences since Silex expectations are no longer abided by.
+  - It receives Request, Application.
+
+All route middlewares for a route are called in order. Since route middleware
+have no priority of they own, making them come outside the normal order means
+changing the priority of the global middleware.
+
+Route middleware is resolved by the callback resolved, so suffers the same
+limitations as view or error handlers, but since they receive $app, this does
+not matter much. */
+
 // ---- Routing ----------------------------------------------------------------
 // Demo Twig.
 $app->get('/',                  HomeController::class       . '::home')
@@ -98,6 +127,9 @@ $app->get('/hello-json/{name}', EscapeController::class     . '::json');
 // Demo streaming.
 $app->get('/noise',             StreamController::class     . '::customStream');
 $app->get('/pass',              StreamController::class     . '::fileStream');
+$app->get('/skip',                  function () {})
+  ->after([AfterRoute::class, 'next'])
+  ->before([BeforeRoute::class, 'next']);
 
 // Demo error handling.
 $app->get('err/http',           ErrorController::class      . '::errorHttp');
@@ -108,8 +140,9 @@ $app->get('/user/{user}',       UserController::class       . '::itemAction')
   ->convert('user', 'converter.user:convert');
 
 /* ---- View handlers ----------------------------------------------------------
-View handlers can also receive Request $request as 2nd arg e.g. for basic
-content negotiation. But the callback_resolver service:
+View handlers receive the controller result, and can also receive Request
+$request as 2nd arg e.g. for basic content negotiation. But the
+callback_resolver service:
  - can not receive $app
  - unlike the controller_resolver, can only use standard callables/services
  - unlike the exception listener, can not use _invoke-able class names.
@@ -127,5 +160,5 @@ They are triggered on KernelEvents::EXCEPTION.
  */
 $app->error([HttpErrorHandler::class, 'handle']);
 // Handlers do not receive $app, so they have to be closures or instantiated
-// here if they need it (or use the global $app like view handlers.
+// here if they need it (or use the global $app) like view handlers.
 $app->error(new GenericErrorHandler($app));

+ 5 - 1
src/demo/Middleware/AfterAll.php

@@ -19,7 +19,10 @@ class AfterAll {
    * @return \Symfony\Component\HttpFoundation\Response|void
    */
   public function __invoke(Request $request, Response $response, Application $app) {
-    $MESSAGE = "<p>In aAM</p>\n";
+    $MESSAGE = "<p>" . $app['timer']->delay() .": In aAM</p>\n";
+    /* Note how content added to the response like this looks as if it had been
+      added after the echo'ed content in the global middleware. Compare the
+      timestamps for sequence */
     if ($response instanceof JsonResponse) {
       $content = $response->getContent();
       $raw = json_decode($content, TRUE);
@@ -35,6 +38,7 @@ class AfterAll {
       return;
     }
     echo $MESSAGE;
+    flush();
   }
 
 }

+ 28 - 0
src/demo/Middleware/AfterRoute.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace demo\Middleware;
+
+use Silex\Application;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+class AfterRoute {
+  public static function next(Request $request, Response $response, Application $app) {
+    $MESSAGE = "<p>" . $app['timer']->delay() .": in aR</p>\n";
+    $content = $response->getContent();
+    if ($content !== FALSE) {
+      $content .= $MESSAGE;
+      $response->setContent($content);
+    }
+    else {
+      // Only echo info on text responses.
+      $ct = $response->headers->get('Content-Type');
+      if (isset($ct) && strpos($ct, 'text') !== 0) {
+        return;
+      }
+      echo $MESSAGE;
+      flush();
+    }
+  }
+
+}

+ 2 - 1
src/demo/Middleware/BeforeAll.php

@@ -16,7 +16,8 @@ class BeforeAll {
   public function next(Request $request, Application $app) {
     // Only add info on requests URIs containing "json".
     if (!preg_match('/json/', $request->getRequestUri())) {
-      echo "<p>In bAM</p>\n";
+      echo "<p>" . $app['timer']->delay() .": In bAM</p>\n";
+      flush();
     }
   }
 

+ 18 - 0
src/demo/Middleware/BeforeRoute.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace demo\Middleware;
+
+
+use Silex\Application;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+class BeforeRoute {
+
+  public static function next(Request $request, Application $app) {
+    echo "<p>" . $app['timer']->delay() .": In bR</p>\n";
+    flush();
+    return new Response('Controller bypassed', Response::HTTP_OK);
+  }
+
+}

+ 23 - 0
src/demo/Services/Timer.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace demo\Services;
+
+
+class Timer {
+  const SCALE = 1E4;
+
+  protected $t0;
+
+  protected function __construct($t0) {
+    $this->t0 = $t0;
+  }
+
+  public static function create() {
+    return new static(microtime(true));
+  }
+
+  public function delay() {
+    $diff = microtime(true) - $this->t0;
+    return round($diff * static::SCALE);
+  }
+}

+ 0 - 2
var/cache/.gitignore

@@ -1,2 +0,0 @@
-*
-!.gitignore