From 577ad146a4623afe8cdf435bfde125f378bae4a7 Mon Sep 17 00:00:00 2001
From: Alejandro Celaya <alejandro@alejandrocelaya.com>
Date: Sat, 30 Apr 2016 18:14:43 +0200
Subject: [PATCH] Implemented shortcode generation

---
 .../autoload/middleware-pipeline.global.php   |  2 +
 config/autoload/services.global.php           |  1 +
 src/Middleware/CliParamsMiddleware.php        | 66 ++++++++++++++
 .../GenerateShortcodeMiddleware.php           | 91 +++++++++++++++++++
 .../Factory/CliParamsMiddlewareFactory.php    | 29 ++++++
 5 files changed, 189 insertions(+)
 create mode 100644 src/Middleware/CliParamsMiddleware.php
 create mode 100644 src/Middleware/CliRoutable/GenerateShortcodeMiddleware.php
 create mode 100644 src/Middleware/Factory/CliParamsMiddlewareFactory.php

diff --git a/config/autoload/middleware-pipeline.global.php b/config/autoload/middleware-pipeline.global.php
index 8a393ce1..d033f9b2 100644
--- a/config/autoload/middleware-pipeline.global.php
+++ b/config/autoload/middleware-pipeline.global.php
@@ -1,4 +1,5 @@
 <?php
+use Acelaya\UrlShortener\Middleware\CliParamsMiddleware;
 use Zend\Expressive\Container\ApplicationFactory;
 use Zend\Expressive\Helper;
 
@@ -15,6 +16,7 @@ return [
         'routing' => [
             'middleware' => [
                 ApplicationFactory::ROUTING_MIDDLEWARE,
+                CliParamsMiddleware::class,
                 Helper\UrlHelperMiddleware::class,
                 ApplicationFactory::DISPATCH_MIDDLEWARE,
             ],
diff --git a/config/autoload/services.global.php b/config/autoload/services.global.php
index 5d47160a..421c8a00 100644
--- a/config/autoload/services.global.php
+++ b/config/autoload/services.global.php
@@ -39,6 +39,7 @@ return [
 
             // Middleware
             Middleware\CliRoutable\GenerateShortcodeMiddleware::class => AnnotatedFactory::class,
+            Middleware\CliParamsMiddleware::class => Middleware\Factory\CliParamsMiddlewareFactory::class,
         ],
         'aliases' => [
             'em' => EntityManager::class,
diff --git a/src/Middleware/CliParamsMiddleware.php b/src/Middleware/CliParamsMiddleware.php
new file mode 100644
index 00000000..c750ff9b
--- /dev/null
+++ b/src/Middleware/CliParamsMiddleware.php
@@ -0,0 +1,66 @@
+<?php
+namespace Acelaya\UrlShortener\Middleware;
+
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+use Zend\Expressive\Router\RouteResult;
+use Zend\Stratigility\MiddlewareInterface;
+
+class CliParamsMiddleware implements MiddlewareInterface
+{
+    /**
+     * @var array
+     */
+    private $argv;
+
+    public function __construct(array $argv)
+    {
+        $this->argv = $argv;
+    }
+
+    /**
+     * 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)
+    {
+        // When not in CLI, just call next middleware
+        if (! php_sapi_name() === 'cli') {
+            return $out($request, $response);
+        }
+
+        /** @var RouteResult $routeResult */
+        $routeResult = $request->getAttribute(RouteResult::class);
+        if (! $routeResult->isSuccess()) {
+            return $out($request, $response);
+        }
+
+        // Inject ARGV params as request attributes
+        if ($routeResult->getMatchedRouteName() === 'cli-generate-shortcode') {
+            $request = $request->withAttribute('longUrl', isset($this->argv[2]) ? $this->argv[2] : null);
+        }
+
+        return $out($request, $response);
+    }
+}
diff --git a/src/Middleware/CliRoutable/GenerateShortcodeMiddleware.php b/src/Middleware/CliRoutable/GenerateShortcodeMiddleware.php
new file mode 100644
index 00000000..44fa3bf8
--- /dev/null
+++ b/src/Middleware/CliRoutable/GenerateShortcodeMiddleware.php
@@ -0,0 +1,91 @@
+<?php
+namespace Acelaya\UrlShortener\Middleware\CliRoutable;
+
+use Acelaya\UrlShortener\Exception\InvalidUrlException;
+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\Uri;
+use Zend\Stratigility\MiddlewareInterface;
+
+class GenerateShortcodeMiddleware implements MiddlewareInterface
+{
+    /**
+     * @var UrlShortenerInterface
+     */
+    private $urlShortener;
+    /**
+     * @var array
+     */
+    private $config;
+
+    /**
+     * GenerateShortcodeMiddleware constructor.
+     *
+     * @param UrlShortenerInterface|UrlShortener $urlShortener
+     * @param array $config
+     *
+     * @Inject({UrlShortener::class, "config.url-shortener"})
+     */
+    public function __construct(UrlShortenerInterface $urlShortener, array $config)
+    {
+        $this->urlShortener = $urlShortener;
+        $this->config = $config;
+    }
+
+    /**
+     * 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)
+    {
+        $longUrl = $request->getAttribute('longUrl');
+
+        try {
+            if (! isset($longUrl)) {
+                $response->getBody()->write('A URL was not provided!' . PHP_EOL);
+                return;
+            }
+
+            $shortcode = $this->urlShortener->urlToShortCode(new Uri($longUrl));
+            $shortUrl = (new Uri())->withPath($shortcode)
+                                   ->withScheme($this->config['schema'])
+                                   ->withHost($this->config['hostname']);
+
+            $response->getBody()->write(
+                sprintf('Processed URL "%s".%sGenerated short URL "%s"', $longUrl, PHP_EOL, $shortUrl) . PHP_EOL
+            );
+        } catch (InvalidUrlException $e) {
+            $response->getBody()->write(
+                sprintf('Provided URL "%s" is invalid. Try with a different one.', $longUrl) . PHP_EOL
+            );
+        } catch (\Exception $e) {
+            $response->getBody()->write($e);
+        } finally {
+            return is_callable($out) ? $out($request, $response) : $response;
+        }
+    }
+}
diff --git a/src/Middleware/Factory/CliParamsMiddlewareFactory.php b/src/Middleware/Factory/CliParamsMiddlewareFactory.php
new file mode 100644
index 00000000..73e365ac
--- /dev/null
+++ b/src/Middleware/Factory/CliParamsMiddlewareFactory.php
@@ -0,0 +1,29 @@
+<?php
+namespace Acelaya\UrlShortener\Middleware\Factory;
+
+use Acelaya\UrlShortener\Middleware\CliParamsMiddleware;
+use Interop\Container\ContainerInterface;
+use Interop\Container\Exception\ContainerException;
+use Zend\ServiceManager\Exception\ServiceNotCreatedException;
+use Zend\ServiceManager\Exception\ServiceNotFoundException;
+use Zend\ServiceManager\Factory\FactoryInterface;
+
+class CliParamsMiddlewareFactory implements FactoryInterface
+{
+    /**
+     * Create an object
+     *
+     * @param  ContainerInterface $container
+     * @param  string $requestedName
+     * @param  null|array $options
+     * @return object
+     * @throws ServiceNotFoundException if unable to resolve the service.
+     * @throws ServiceNotCreatedException if an exception is raised when
+     *     creating a service.
+     * @throws ContainerException if any other error occurs
+     */
+    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
+    {
+        return new CliParamsMiddleware($_SERVER['argv']);
+    }
+}