From b2615d0de679fcc5696835bd8446632f87957522 Mon Sep 17 00:00:00 2001
From: Alejandro Celaya <alejandro@alejandrocelaya.com>
Date: Sat, 30 Apr 2016 18:59:03 +0200
Subject: [PATCH] Implemented routable redirect middleware

---
 composer.json                                 |  6 +-
 config/autoload/routes.global.php             | 13 ++--
 config/autoload/services.global.php           |  5 +-
 src/Middleware/CliParamsMiddleware.php        |  2 +-
 .../Factory/CliParamsMiddlewareFactory.php    |  2 +-
 .../Routable/RedirectMiddleware.php           | 75 +++++++++++++++++++
 templates/error/404.html.twig                 |  3 +-
 templates/layout/default.html.twig            | 36 +--------
 8 files changed, 92 insertions(+), 50 deletions(-)
 create mode 100644 src/Middleware/Routable/RedirectMiddleware.php

diff --git a/composer.json b/composer.json
index 652d81d1..d43d7fbb 100644
--- a/composer.json
+++ b/composer.json
@@ -14,10 +14,10 @@
         "php": "^5.5 || ^7.0",
         "zendframework/zend-expressive": "^1.0",
         "zendframework/zend-expressive-helpers": "^2.0",
-        "zendframework/zend-stdlib": "^2.7",
-        "zendframework/zend-expressive-aurarouter": "^1.0",
-        "zendframework/zend-servicemanager": "^3.0",
+        "zendframework/zend-expressive-fastroute": "^1.1",
         "zendframework/zend-expressive-twigrenderer": "^1.0",
+        "zendframework/zend-stdlib": "^2.7",
+        "zendframework/zend-servicemanager": "^3.0",
         "doctrine/orm": "^2.5",
         "guzzlehttp/guzzle": "^6.2",
         "acelaya/zsm-annotated-services": "^0.2.0"
diff --git a/config/autoload/routes.global.php b/config/autoload/routes.global.php
index a95711aa..40a3d20b 100644
--- a/config/autoload/routes.global.php
+++ b/config/autoload/routes.global.php
@@ -1,14 +1,15 @@
 <?php
+use Acelaya\UrlShortener\Middleware\Routable;
 
 return [
 
     'routes' => [
-//        [
-//            'name' => 'home',
-//            'path' => '/',
-//            'middleware' => '',
-//            'allowed_methods' => ['GET'],
-//        ],
+        [
+            'name' => 'long-url-redirect',
+            'path' => '/{shortCode}',
+            'middleware' => Routable\RedirectMiddleware::class,
+            'allowed_methods' => ['GET'],
+        ],
     ],
 
 ];
diff --git a/config/autoload/services.global.php b/config/autoload/services.global.php
index 421c8a00..414b2e56 100644
--- a/config/autoload/services.global.php
+++ b/config/autoload/services.global.php
@@ -25,7 +25,7 @@ return [
             Helper\ServerUrlMiddleware::class => Helper\ServerUrlMiddlewareFactory::class,
             Helper\UrlHelperMiddleware::class => Helper\UrlHelperMiddlewareFactory::class,
             Helper\ServerUrlHelper::class => InvokableFactory::class,
-            Router\AuraRouter::class => InvokableFactory::class,
+            Router\FastRouteRouter::class => InvokableFactory::class,
 
             // View
             'Zend\Expressive\FinalHandler' => Container\TemplatedErrorHandlerFactory::class,
@@ -39,12 +39,13 @@ return [
 
             // Middleware
             Middleware\CliRoutable\GenerateShortcodeMiddleware::class => AnnotatedFactory::class,
+            Middleware\Routable\RedirectMiddleware::class => AnnotatedFactory::class,
             Middleware\CliParamsMiddleware::class => Middleware\Factory\CliParamsMiddlewareFactory::class,
         ],
         'aliases' => [
             'em' => EntityManager::class,
             'httpClient' => GuzzleHttp\Client::class,
-            Router\RouterInterface::class => Router\AuraRouter::class,
+            Router\RouterInterface::class => Router\FastRouteRouter::class,
         ]
     ],
 
diff --git a/src/Middleware/CliParamsMiddleware.php b/src/Middleware/CliParamsMiddleware.php
index c750ff9b..da243a15 100644
--- a/src/Middleware/CliParamsMiddleware.php
+++ b/src/Middleware/CliParamsMiddleware.php
@@ -46,7 +46,7 @@ class CliParamsMiddleware implements MiddlewareInterface
     public function __invoke(Request $request, Response $response, callable $out = null)
     {
         // When not in CLI, just call next middleware
-        if (! php_sapi_name() === 'cli') {
+        if (php_sapi_name() !== 'cli') {
             return $out($request, $response);
         }
 
diff --git a/src/Middleware/Factory/CliParamsMiddlewareFactory.php b/src/Middleware/Factory/CliParamsMiddlewareFactory.php
index 73e365ac..b806cbeb 100644
--- a/src/Middleware/Factory/CliParamsMiddlewareFactory.php
+++ b/src/Middleware/Factory/CliParamsMiddlewareFactory.php
@@ -24,6 +24,6 @@ class CliParamsMiddlewareFactory implements FactoryInterface
      */
     public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
     {
-        return new CliParamsMiddleware($_SERVER['argv']);
+        return new CliParamsMiddleware(isset($_SERVER['argv']) ? $_SERVER['argv'] : []);
     }
 }
diff --git a/src/Middleware/Routable/RedirectMiddleware.php b/src/Middleware/Routable/RedirectMiddleware.php
new file mode 100644
index 00000000..29870d46
--- /dev/null
+++ b/src/Middleware/Routable/RedirectMiddleware.php
@@ -0,0 +1,75 @@
+<?php
+namespace Acelaya\UrlShortener\Middleware\Routable;
+
+use Acelaya\UrlShortener\Service\UrlShortener;
+use Acelaya\UrlShortener\Service\UrlShortenerInterface;
+use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+use Zend\Diactoros\Response\RedirectResponse;
+use Zend\Stratigility\MiddlewareInterface;
+
+class RedirectMiddleware implements MiddlewareInterface
+{
+    /**
+     * @var UrlShortenerInterface
+     */
+    private $urlShortener;
+
+    /**
+     * RedirectMiddleware constructor.
+     * @param UrlShortenerInterface|UrlShortener $urlShortener
+     *
+     * @Inject({UrlShortener::class})
+     */
+    public function __construct(UrlShortenerInterface $urlShortener)
+    {
+        $this->urlShortener = $urlShortener;
+    }
+
+    /**
+     * Process an incoming request and/or response.
+     *
+     * Accepts a server-side request and a response instance, and does
+     * something with them.
+     *
+     * If the response is not complete and/or further processing would not
+     * interfere with the work done in the middleware, or if the middleware
+     * wants to delegate to another process, it can use the `$out` callable
+     * if present.
+     *
+     * If the middleware does not return a value, execution of the current
+     * request is considered complete, and the response instance provided will
+     * be considered the response to return.
+     *
+     * Alternately, the middleware may return a response instance.
+     *
+     * Often, middleware will `return $out();`, with the assumption that a
+     * later middleware will return a response.
+     *
+     * @param Request $request
+     * @param Response $response
+     * @param null|callable $out
+     * @return null|Response
+     */
+    public function __invoke(Request $request, Response $response, callable $out = null)
+    {
+        $shortCode = $request->getAttribute('shortCode', '');
+        
+        try {
+            $longUrl = $this->urlShortener->shortCodeToUrl($shortCode);
+
+            // If provided shortCode does not belong to a valid long URL, dispatch next middleware, which is 404
+            // middleware
+            if (! isset($longUrl)) {
+                return $out($request, $response);
+            }
+
+            // Return a redirect response to the long URL
+            return new RedirectResponse($longUrl, 301);
+        } catch (\Exception $e) {
+            // In case of error, dispatch 404 error
+            return $out($request, $response);
+        }
+    }
+}
diff --git a/templates/error/404.html.twig b/templates/error/404.html.twig
index 7b4e363e..8e3f3a7a 100644
--- a/templates/error/404.html.twig
+++ b/templates/error/404.html.twig
@@ -7,7 +7,6 @@
     <h2>This is awkward.</h2>
     <p>We encountered a 404 Not Found error.</p>
     <p>
-        You are looking for something that doesn't exist or may have moved. Check out one of the links on this page
-        or head back to <a href="{{ path('home') }}">Home</a>.
+        You are looking for something that doesn't exist or may have moved.
     </p>
 {% endblock %}
diff --git a/templates/layout/default.html.twig b/templates/layout/default.html.twig
index b3664d0e..ac34990a 100644
--- a/templates/layout/default.html.twig
+++ b/templates/layout/default.html.twig
@@ -17,40 +17,6 @@
     {% block stylesheets %}{% endblock %}
 </head>
 <body class="app">
-    <header class="app-header">
-        <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
-            <div class="container">
-                <div class="navbar-header">
-                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
-                        <span class="icon-bar"></span>
-                        <span class="icon-bar"></span>
-                        <span class="icon-bar"></span>
-                    </button>
-                    <a class="navbar-brand" href="{{ path('home') }}">
-                        <img src="{{ asset('zf-logo.png') }}" alt="Zend Expressive" />
-                    </a>
-                </div>
-                <div class="collapse navbar-collapse">
-                    <ul class="nav navbar-nav">
-                        <li>
-                            <a href="https://zendframework.github.io/zend-expressive/" target="_blank">
-                                <i class="fa fa-book"></i> Docs
-                            </a>
-                        </li>
-                        <li>
-                            <a href="https://github.com/zendframework/zend-expressive" target="_blank">
-                                <i class="fa fa-wrench"></i> Contribute
-                            </a>
-                        </li>
-                        <li>
-
-                        </li>
-                    </ul>
-                </div>
-            </div>
-        </nav>
-    </header>
-
     <div class="app-content">
         <main class="container">
             {% block content %}{% endblock %}
@@ -62,7 +28,7 @@
             <hr />
             {% block footer %}
                 <p>
-                    &copy; 2005 - {{ "now"|date("Y") }} by Zend Technologies Ltd. All rights reserved.
+                    &copy; {{ "now" | date("Y") }} by Alejandro Celaya.
                 </p>
             {% endblock %}
         </div>