From 35b0ea118222f9eab191ca9ac4c208ac49f9146c Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sun, 7 Aug 2016 01:03:35 +0200 Subject: [PATCH 01/39] fixes 16 --- composer.json | 6 +- phpunit.xml.dist | 4 +- src/Viserio/Bus/Dispatcher.php | 2 +- src/Viserio/Console/Application.php | 2 +- src/Viserio/Console/Command/Command.php | 8 +- .../Container/ContextualBindingBuilder.php | 2 +- src/Viserio/Container/Inflector.php | 2 +- .../Container}/Traits/ContainerAwareTrait.php | 2 +- .../Contracts/Routing/DataGenerator.php | 13 --- .../Exceptions/UrlGenerationException.php | 20 ++++ .../Contracts/Routing/RouteCollector.php | 7 +- .../Traits/ContainerAwareTraitTest.php | 4 +- .../Contracts/View/Traits/ViewAwareTrait.php | 2 +- src/Viserio/Contracts/composer.json | 21 +++- src/Viserio/Events/Dispatcher.php | 2 +- src/Viserio/Mail/QueueMailer.php | 2 +- src/Viserio/Middleware/Dispatcher.php | 2 +- src/Viserio/Pipeline/Pipeline.php | 2 +- .../Queue/Connectors/AbstractQueue.php | 2 +- src/Viserio/Queue/Jobs/AbstractJob.php | 2 +- src/Viserio/Queue/Tests/Fixture/TestQueue.php | 2 +- src/Viserio/Routing/Controller.php | 7 ++ src/Viserio/Routing/ControllerDispatcher.php | 7 ++ src/Viserio/Routing/Dispatcher.php | 13 +-- src/Viserio/Routing/Route.php | 7 ++ src/Viserio/Routing/RouteCollection.php | 51 +++++---- src/Viserio/Routing/RouteGroup.php | 43 +++++++ src/Viserio/Routing/RouteParser.php | 31 ------ src/Viserio/Routing/RouteStrategyTrait.php | 43 ------- src/Viserio/Routing/Router.php | 7 ++ .../UrlGenerator/CachedDataGeneratorTest.php | 63 ----------- .../GroupCountBasedDataGeneratorTest.php | 105 ------------------ .../UrlGenerator/SimpleUrlGeneratorTest.php | 89 --------------- src/Viserio/Routing/UrlGenerator.php | 28 +++++ .../UrlGenerator/CachedDataGenerator.php | 71 ------------ .../GroupCountBasedDataGenerator.php | 105 ------------------ .../UrlGenerator/SimpleUrlGenerator.php | 96 ---------------- src/Viserio/Routing/composer.json | 10 +- .../Support/AbstractConnectionManager.php | 2 +- src/Viserio/Support/AbstractManager.php | 2 +- src/Viserio/Support/Invoker.php | 2 +- src/Viserio/Support/composer.json | 38 +++---- src/Viserio/View/Virtuoso.php | 4 +- 43 files changed, 224 insertions(+), 709 deletions(-) rename src/Viserio/{Support => Contracts/Container}/Traits/ContainerAwareTrait.php (95%) delete mode 100644 src/Viserio/Contracts/Routing/DataGenerator.php create mode 100644 src/Viserio/Contracts/Routing/Exceptions/UrlGenerationException.php rename src/Viserio/{Support/Tests => Contracts/Tests/Container}/Traits/ContainerAwareTraitTest.php (84%) create mode 100644 src/Viserio/Routing/Controller.php create mode 100644 src/Viserio/Routing/ControllerDispatcher.php create mode 100644 src/Viserio/Routing/Route.php create mode 100644 src/Viserio/Routing/RouteGroup.php delete mode 100644 src/Viserio/Routing/RouteParser.php delete mode 100644 src/Viserio/Routing/RouteStrategyTrait.php create mode 100644 src/Viserio/Routing/Router.php delete mode 100644 src/Viserio/Routing/Tests/UrlGenerator/CachedDataGeneratorTest.php delete mode 100644 src/Viserio/Routing/Tests/UrlGenerator/GroupCountBasedDataGeneratorTest.php delete mode 100644 src/Viserio/Routing/Tests/UrlGenerator/SimpleUrlGeneratorTest.php create mode 100644 src/Viserio/Routing/UrlGenerator.php delete mode 100644 src/Viserio/Routing/UrlGenerator/CachedDataGenerator.php delete mode 100644 src/Viserio/Routing/UrlGenerator/GroupCountBasedDataGenerator.php delete mode 100644 src/Viserio/Routing/UrlGenerator/SimpleUrlGenerator.php diff --git a/composer.json b/composer.json index fcb480837..5d9356182 100644 --- a/composer.json +++ b/composer.json @@ -47,14 +47,13 @@ "php-di/invoker" : "^1.3", "phpseclib/phpseclib" : "^2.0", "nesbot/carbon" : "^1.21", - "nikic/fast-route" : "^0.6", - "ramsey/uuid" : "^3.4", "paragonie/constant_time_encoding" : "^2.0", "paragonie/password_lock" : "^3.0", "psr/cache" : "^1.0", "psr/log" : "^1.0", "psr/http-message" : "^1.0", "psy/psysh" : "^0.6", + "ramsey/uuid" : "^3.4", "roave/security-advisories" : "dev-master", "schnittstabil/csrf-tokenservice" : "^2.0", "stecman/symfony-console-completion" : "^0.6", @@ -65,7 +64,8 @@ "symfony/polyfill-intl-icu" : "^1.0", "symfony/polyfill-mbstring" : "^1.0", "symfony/var-dumper" : "^3.1", - "swiftmailer/swiftmailer" : "^5.4" + "swiftmailer/swiftmailer" : "^5.4", + "timetoogo/rapid-route" : "^2.0" }, "replace": { "viserio/cache" : "self.version", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index cbc51d17d..4279064dc 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -90,9 +90,9 @@ ./src/Viserio/Queue/Tests - + ./src/Viserio/Session/Tests diff --git a/src/Viserio/Bus/Dispatcher.php b/src/Viserio/Bus/Dispatcher.php index 66cb6ee70..f284f5d18 100644 --- a/src/Viserio/Bus/Dispatcher.php +++ b/src/Viserio/Bus/Dispatcher.php @@ -8,7 +8,7 @@ use Viserio\Contracts\Bus\Dispatcher as DispatcherContract; use Viserio\Pipeline\Pipeline; use Viserio\Support\Invoker; -use Viserio\Support\Traits\ContainerAwareTrait; +use Viserio\Contracts\Container\Traits\ContainerAwareTrait; class Dispatcher implements DispatcherContract { diff --git a/src/Viserio/Console/Application.php b/src/Viserio/Console/Application.php index a1d217b4d..ce7df474e 100644 --- a/src/Viserio/Console/Application.php +++ b/src/Viserio/Console/Application.php @@ -15,7 +15,7 @@ use Viserio\Console\Input\InputOption; use Viserio\Contracts\Console\Application as ApplicationContract; use Viserio\Support\Invoker; -use Viserio\Support\Traits\ContainerAwareTrait; +use Viserio\Contracts\Container\Traits\ContainerAwareTrait; class Application extends SymfonyConsole implements ApplicationContract { diff --git a/src/Viserio/Console/Command/Command.php b/src/Viserio/Console/Command/Command.php index bfba1392a..c5ee58e49 100644 --- a/src/Viserio/Console/Command/Command.php +++ b/src/Viserio/Console/Command/Command.php @@ -18,11 +18,11 @@ Question\Question }; use Viserio\Console\Style\NarrowsparkStyle; -use Viserio\Contracts\Support\Arrayable; -use Viserio\Support\{ - Invoker, - Traits\ContainerAwareTrait +use Viserio\Contracts\{ + Support\Arrayable, + Container\Traits\ContainerAwareTrait }; +use Viserio\Support\Invoker; abstract class Command extends BaseCommand implements CompletionAwareInterface { diff --git a/src/Viserio/Container/ContextualBindingBuilder.php b/src/Viserio/Container/ContextualBindingBuilder.php index ee6a3ec78..6224beef5 100644 --- a/src/Viserio/Container/ContextualBindingBuilder.php +++ b/src/Viserio/Container/ContextualBindingBuilder.php @@ -3,7 +3,7 @@ namespace Viserio\Container; use Viserio\Contracts\Container\ContextualBindingBuilder as ContextualBindingBuilderContract; -use Viserio\Support\Traits\ContainerAwareTrait; +use Viserio\Contracts\Container\Traits\ContainerAwareTrait; /** * ContextualBindingBuilder. diff --git a/src/Viserio/Container/Inflector.php b/src/Viserio/Container/Inflector.php index 14827daf5..84ddd2b6a 100644 --- a/src/Viserio/Container/Inflector.php +++ b/src/Viserio/Container/Inflector.php @@ -2,7 +2,7 @@ declare(strict_types=1); namespace Viserio\Container; -use Viserio\Support\Traits\ContainerAwareTrait; +use Viserio\Contracts\Container\Traits\ContainerAwareTrait; class Inflector { diff --git a/src/Viserio/Support/Traits/ContainerAwareTrait.php b/src/Viserio/Contracts/Container/Traits/ContainerAwareTrait.php similarity index 95% rename from src/Viserio/Support/Traits/ContainerAwareTrait.php rename to src/Viserio/Contracts/Container/Traits/ContainerAwareTrait.php index b26cc4578..e40aada4d 100644 --- a/src/Viserio/Support/Traits/ContainerAwareTrait.php +++ b/src/Viserio/Contracts/Container/Traits/ContainerAwareTrait.php @@ -1,6 +1,6 @@ getName()}] [URI: {$route->getPath()}]."); + } +} diff --git a/src/Viserio/Contracts/Routing/RouteCollector.php b/src/Viserio/Contracts/Routing/RouteCollector.php index 1dd7b7cf1..6572605e8 100644 --- a/src/Viserio/Contracts/Routing/RouteCollector.php +++ b/src/Viserio/Contracts/Routing/RouteCollector.php @@ -4,10 +4,5 @@ interface RouteCollector { - /** - * Returns the collected route data. - * - * @return array - */ - public function getData(); + } diff --git a/src/Viserio/Support/Tests/Traits/ContainerAwareTraitTest.php b/src/Viserio/Contracts/Tests/Container/Traits/ContainerAwareTraitTest.php similarity index 84% rename from src/Viserio/Support/Tests/Traits/ContainerAwareTraitTest.php rename to src/Viserio/Contracts/Tests/Container/Traits/ContainerAwareTraitTest.php index 51208eee2..adb5c9190 100644 --- a/src/Viserio/Support/Tests/Traits/ContainerAwareTraitTest.php +++ b/src/Viserio/Contracts/Tests/Container/Traits/ContainerAwareTraitTest.php @@ -1,9 +1,9 @@ handleNotFound(); - case FastDispatcher::METHOD_NOT_ALLOWED: + case MatchResult::HTTP_METHOD_NOT_ALLOWED: $allowed = (array) $match[1]; return $this->handleNotAllowed($allowed); - case FastDispatcher::FOUND: + case MatchResult::FOUND: default: $handler = (isset($this->routes[$match[1]]['callback'])) ? $this->routes[$match[1]]['callback'] : diff --git a/src/Viserio/Routing/Route.php b/src/Viserio/Routing/Route.php new file mode 100644 index 000000000..ec4314e89 --- /dev/null +++ b/src/Viserio/Routing/Route.php @@ -0,0 +1,7 @@ + '{$1:[0-9]+}', + '/{(.+?):word}/' => '{$1:[a-zA-Z]+}', + '/{(.+?):alphanum_dash}/' => '{$1:[a-zA-Z0-9-_]+}', + '/{(.+?):slug}/' => '{$1:[a-z0-9-]+}', + '/{(.+?):uuid}/' => '{$1:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}+}' + ]; + /** * Constructor. * - * @param ContainerContract $container - * @param \FastRoute\RouteParser $parser - * @param \FastRoute\DataGenerator $generator + * @param \Interop\Container\ContainerInterface $container */ public function __construct( - ContainerContract $container, - FastRouteParser $parser, - DataGenerator $generator + ContainerInterface $container ) { $this->container = $container; - - parent::__construct($parser, $generator); } /** @@ -125,7 +132,7 @@ public function getDispatcher() /** * Map a handler to the given methods and route. * - * @param string $route The route to match against + * @param string|array $route The route to match against * @param string|callable $handler The handler for the route * @param string|string[] $methods The HTTP methods for this handler * @param int $strategy @@ -146,7 +153,7 @@ public function map($route, $handler, $methods = 'GET', $strategy = self::REQUES */ public function get($route, $handler, $strategy = self::REQUEST_RESPONSE_STRATEGY) { - return $this->addRoute('GET', $route, $handler, $strategy); + return $this->addRoute(['GET', 'HEAD'], $route, $handler, $strategy); } /** @@ -298,7 +305,7 @@ public function globalOnAfter($handler, $priority = 0) * * @return \Viserio\Routing\Redirect */ - public function redirect() + public function redirect(): Redirect { return new Redirect($this); } diff --git a/src/Viserio/Routing/RouteGroup.php b/src/Viserio/Routing/RouteGroup.php new file mode 100644 index 000000000..377d04c51 --- /dev/null +++ b/src/Viserio/Routing/RouteGroup.php @@ -0,0 +1,43 @@ +callback = $callback; + $this->collection = $collection; + $this->prefix = sprintf('/%s', ltrim($prefix, '/')); + } + + /** + * Process the group and ensure routes are added to the collection. + */ + public function __invoke() + { + call_user_func_array($this->callback, [$this]); + } +} diff --git a/src/Viserio/Routing/RouteParser.php b/src/Viserio/Routing/RouteParser.php deleted file mode 100644 index 3fcae90ce..000000000 --- a/src/Viserio/Routing/RouteParser.php +++ /dev/null @@ -1,31 +0,0 @@ -strategy = $strategy; - - return; - } - - throw new InvalidArgumentException( - 'Provided strategy must be an integer or an instance of [\Viserio\Contracts\Routing\CustomStrategy]' - ); - } - - /** - * Gets global strategy. - * - * @return int - */ - public function getStrategy() - { - return $this->strategy; - } -} diff --git a/src/Viserio/Routing/Router.php b/src/Viserio/Routing/Router.php new file mode 100644 index 000000000..9c8000e4d --- /dev/null +++ b/src/Viserio/Routing/Router.php @@ -0,0 +1,7 @@ +getGenerator($filename, $this->once()); - $data = $generator->getData(); - $this->assertNotEmpty(file_get_contents($filename)); - $this->assertEquals([], $data); - - return $filename; - } - - /** - * Test create with a fresh cache. - * - * @depends testCreateWritesCache - */ - public function testCreateWithFreshCache($filename) - { - $generator = $this->getGenerator($filename, $this->never()); - $generator->getData(); - } - - /** - * Test an unwriteable file. - * - * @todo This relies on something outside of Narrowspark throwing the exception - */ - public function testUnableToWriteCache() - { - $generator = $this->getGenerator('/some/unwriteable/path'); - $this->setExpectedException('RuntimeException', 'Failed to create'); - $generator->getData(); - } - - /** - * @param string $filename - * @param null $expects - * @param array $routes - * - * @return CachedDataGenerator - */ - protected function getGenerator($filename, $expects = null, $routes = []) - { - $generator = $this->getMockForAbstractClass('Viserio\Contracts\Routing\DataGenerator'); - $generator->expects($expects ?: $this->any()) - ->method('getData') - ->will($this->returnValue($routes)); - - return new CachedDataGenerator(new Filesystem(), $generator, $filename, false); - } -} diff --git a/src/Viserio/Routing/Tests/UrlGenerator/GroupCountBasedDataGeneratorTest.php b/src/Viserio/Routing/Tests/UrlGenerator/GroupCountBasedDataGeneratorTest.php deleted file mode 100644 index cd6fa6db2..000000000 --- a/src/Viserio/Routing/Tests/UrlGenerator/GroupCountBasedDataGeneratorTest.php +++ /dev/null @@ -1,105 +0,0 @@ -getMockForAbstractClass('Viserio\Contracts\Routing\RouteCollector'); - $collector->expects($this->once()) - ->method('getData') - ->will($this->returnValue([ - // Static routes - [ - '/' => [ - 'GET' => [ - 'name' => 'home', - 'controller' => 'handler1', - ], - ], - ], - // Dynamic routes - [ - 'GET' => [ - [ - 'regex' => '~^(?|/user/([^/]+)/show)$~', - 'routeMap' => [ - 2 => [ - [ - 'name' => 'user_show', - 'handler' => 'handler2', - ], - [ - 'id' => 'id', - ], - ], - ], - ], - ], - ], - ])); - - $generator = new GroupCountBasedDataGenerator($collector); - $data = $generator->getData(); - $this->assertEquals([ - 'home' => '/', - 'user_show' => [ - 'path' => '/user/{id}/show', - 'params' => [ - 'id' => 'id', - ], - ], - ], $data); - } - - /** - * Test invalid data handling. - */ - public function testInvalidData() - { - $collector = $this->getMockForAbstractClass('Viserio\Contracts\Routing\RouteCollector'); - $collector->expects($this->once()) - ->method('getData') - ->will($this->returnValue([ - [], - [ - 'GET' => [ - [ - 'regex' => '~^(?|/user/([^/]+)/show|/user/([^/]+)/edit)$~', - 'routeMap' => [ - // Invalid index - 0 => [ - [ - 'name' => 'user_show', - 'handler' => 'handler2', - ], - [ - 'id' => 'id', - ], - ], - // Valid, but No "name" attribute - 3 => [ - [ - 'handler' => 'handler2', - ], - [ - 'id' => 'id', - ], - ], - ], - ], - ], - ], - ])); - - $generator = new GroupCountBasedDataGenerator($collector); - $data = $generator->getData(); - $this->assertEquals([], $data); - } -} diff --git a/src/Viserio/Routing/Tests/UrlGenerator/SimpleUrlGeneratorTest.php b/src/Viserio/Routing/Tests/UrlGenerator/SimpleUrlGeneratorTest.php deleted file mode 100644 index ae6e8717a..000000000 --- a/src/Viserio/Routing/Tests/UrlGenerator/SimpleUrlGeneratorTest.php +++ /dev/null @@ -1,89 +0,0 @@ -getGenerator(); - $this->assertEquals('/', $generator->generate('home')); - } - - /** - * Test base URL functionality. - */ - public function testBaseUrl() - { - $generator = $this->getGenerator(); - $request = Request::create('https://www.example.com/subdirectory/somepage', 'GET', [], [], [], [ - 'SCRIPT_FILENAME' => 'index.php', - 'PHP_SELF' => '/subdirectory/index.php', - ]); - $generator->setRequest($request); - $this->assertEquals('/subdirectory/', $generator->generate('home')); - } - - /** - * Test absolute URL functionality. - */ - public function testAbsoluteUrl() - { - $generator = $this->getGenerator(); - $request = Request::create('https://www.example.com/subdirectory/somepage', 'GET', [], [], [], [ - 'SCRIPT_FILENAME' => 'index.php', - 'PHP_SELF' => '/subdirectory/index.php', - ]); - $generator->setRequest($request); - $this->assertEquals('https://www.example.com/subdirectory/', $generator->generate('home', [], true)); - } - - /** - * Test a dynamic route. - */ - public function testDynamicRoute() - { - $generator = $this->getGenerator([ - 'user_edit' => [ - 'params' => [ - 'id', - ], - 'path' => '/user/{id}/edit', - ], - ]); - $this->assertEquals('/user/123/edit', $generator->generate('user_edit', ['id' => 123])); - } - - /** - * Test a dynamic route with a missing parameter. - */ - public function testDynamicRouteWithMissingParameter() - { - $generator = $this->getGenerator([ - 'user_edit' => [ - 'params' => [ - 'id', - ], - 'path' => '/user/{id}/edit', - ], - ]); - $this->setExpectedException('RuntimeException', 'Missing required parameter'); - $this->assertEquals('/user/123/edit', $generator->generate('user_edit')); - } - - private function getGenerator(array $routes = ['home' => '/']) - { - $dataGenerator = $this->getMockForAbstractClass('Viserio\Contracts\Routing\DataGenerator'); - $dataGenerator->expects($this->once()) - ->method('getData') - ->will($this->returnValue($routes)); - - return new SimpleUrlGenerator($dataGenerator); - } -} diff --git a/src/Viserio/Routing/UrlGenerator.php b/src/Viserio/Routing/UrlGenerator.php new file mode 100644 index 000000000..ab604a0c2 --- /dev/null +++ b/src/Viserio/Routing/UrlGenerator.php @@ -0,0 +1,28 @@ + '/', + '%40' => '@', + '%3A' => ':', + '%3B' => ';', + '%2C' => ',', + '%3D' => '=', + '%2B' => '+', + '%21' => '!', + '%2A' => '*', + '%7C' => '|', + '%3F' => '?', + '%26' => '&', + '%23' => '#', + '%25' => '%', + ]; +} diff --git a/src/Viserio/Routing/UrlGenerator/CachedDataGenerator.php b/src/Viserio/Routing/UrlGenerator/CachedDataGenerator.php deleted file mode 100644 index 7a3af4561..000000000 --- a/src/Viserio/Routing/UrlGenerator/CachedDataGenerator.php +++ /dev/null @@ -1,71 +0,0 @@ -wrappedGenerator = $wrappedGenerator; - $this->cacheFile = $cacheFile; - $this->debug = $debug; - - $this->files = $files; - } - - /** - * Get formatted route data for use by a URL generator. - * - * @return array - */ - public function getData(): array - { - $files = $this->files; - $cache = $this->cacheFile; - - if (! $files->exists($cache) || ! $this->debug) { - $routes = $this->wrappedGenerator->getData(); - $files->write($cache, 'getRequire($this->cacheFile); - } -} diff --git a/src/Viserio/Routing/UrlGenerator/GroupCountBasedDataGenerator.php b/src/Viserio/Routing/UrlGenerator/GroupCountBasedDataGenerator.php deleted file mode 100644 index 7c0a84e93..000000000 --- a/src/Viserio/Routing/UrlGenerator/GroupCountBasedDataGenerator.php +++ /dev/null @@ -1,105 +0,0 @@ -routeCollector = $routeCollector; - } - - /** - * Get formatted route data for use by a URL generator. - * - * @return array - */ - public function getData(): array - { - $routes = $this->routeCollector->getData(); - $data = []; - - foreach ($routes[0] as $path => $methods) { - $handler = reset($methods); - if (is_array($handler) && isset($handler['name'])) { - $data[$handler['name']] = $path; - } - } - - foreach ($routes[1] as $method) { - foreach ($method as $group) { - $data = array_merge($data, $this->parseDynamicGroup($group)); - } - } - - return $data; - } - - /** - * Parse a group of dynamic routes. - * - * @param $group - * - * @return array - */ - private function parseDynamicGroup($group) - { - $regex = $group['regex']; - $parts = explode('|', $regex); - $data = []; - - foreach ($group['routeMap'] as $matchIndex => $routeData) { - if (! is_array($routeData[0]) || ! isset($routeData[0]['name']) || ! isset($parts[$matchIndex - 1])) { - continue; - } - - $parameters = $routeData[1]; - $path = $parts[$matchIndex - 1]; - - foreach ($parameters as $parameter) { - $path = $this->replaceOnce('([^/]+)', '{' . $parameter . '}', $path); - } - - $path = rtrim($path, '()$~'); - $data[$routeData[0]['name']] = [ - 'path' => $path, - 'params' => $parameters, - ]; - } - - return $data; - } - - /** - * Replace the first occurrence of a string. - * - * @param string $search - * @param string $replace - * @param string $subject - * - * @return mixed - */ - private function replaceOnce($search, $replace, $subject) - { - $pos = strpos($subject, $search); - - if ($pos !== false) { - $subject = substr_replace($subject, $replace, $pos, strlen($search)); - } - - return $subject; - } -} diff --git a/src/Viserio/Routing/UrlGenerator/SimpleUrlGenerator.php b/src/Viserio/Routing/UrlGenerator/SimpleUrlGenerator.php deleted file mode 100644 index f45e7b9c4..000000000 --- a/src/Viserio/Routing/UrlGenerator/SimpleUrlGenerator.php +++ /dev/null @@ -1,96 +0,0 @@ -dataGenerator = $dataGenerator; - } - - /** - * Generate a URL for the given route. - * - * @param string $name The name of the route to generate a url for - * @param array $parameters Parameters to pass to the route - * @param bool $absolute If true, the generated route should be absolute - * - * @return string - */ - public function generate(string $name, array $parameters = [], bool $absolute = false): string - { - if (! $this->initialized) { - $this->initialize(); - } - - $alias = strpos($name, '@') === false ? '@' . $name : $name; - - $path = $this->routes[$alias]; - - if (is_array($path)) { - $params = $path['params']; - $path = $path['path']; - - foreach ($params as $param) { - if (! isset($parameters[$param])) { - throw new RuntimeException( - 'Missing required parameter "' . $param . '". Optional parameters not currently supported' - ); - } - - $path = str_replace('{' . $param . '}', $parameters[$param], $path); - } - } - - if ($this->request) { - $path = $this->request->getBaseUrl() . $path; - if ($absolute) { - $path = $this->request->getSchemeAndHttpHost() . $path; - } - } - - return $path; - } - - /** - * @param null|\Symfony\Component\HttpFoundation\Request $request - */ - public function setRequest(SymfonyRequest $request = null) - { - $this->request = $request; - } - - /** - * Initialize the generator. - */ - protected function initialize() - { - $this->routes = $this->dataGenerator->getData(); - $this->initialized = true; - } -} diff --git a/src/Viserio/Routing/composer.json b/src/Viserio/Routing/composer.json index 56bd1cb9d..b7e21034e 100644 --- a/src/Viserio/Routing/composer.json +++ b/src/Viserio/Routing/composer.json @@ -2,7 +2,7 @@ "name" : "viserio/routing", "type" : "library", "description": "The Viserio Routing package.", - "keywords" : ["viserio", "narrowspark", "route", "FastRoute", "dispatcher"], + "keywords" : ["viserio", "narrowspark", "route", "rapid-route", "dispatcher"], "license" : "MIT", "homepage" : "http://github.com/narrowspark/framework", "support" : { @@ -19,12 +19,10 @@ ], "require": { "php" : "7.0.0 - 7.0.5 || ^7.0.7", - "viserio/container" : "self.version", - "viserio/contracts" : "self.version", - "viserio/http" : "self.version", "container-interop/container-interop" : "^1.0", - "nikic/fast-route" : "^0.5", - "symfony/http-kernel" : "^3.1" + "timetoogo/rapid-route" : "^0.5", + "psr/http-message" : "^1.0", + "viserio/contracts" : "self.version" }, "require-dev": { "narrowspark/php-cs-fixer-config" : "^1.1", diff --git a/src/Viserio/Support/AbstractConnectionManager.php b/src/Viserio/Support/AbstractConnectionManager.php index ffe881105..16451b7bb 100644 --- a/src/Viserio/Support/AbstractConnectionManager.php +++ b/src/Viserio/Support/AbstractConnectionManager.php @@ -9,7 +9,7 @@ Config\Manager as ConfigContract, Support\Connector as ConnectorContract }; -use Viserio\Support\Traits\ContainerAwareTrait; +use Viserio\Contracts\Container\Traits\ContainerAwareTrait; abstract class AbstractConnectionManager { diff --git a/src/Viserio/Support/AbstractManager.php b/src/Viserio/Support/AbstractManager.php index 6a72c916e..979a98a98 100644 --- a/src/Viserio/Support/AbstractManager.php +++ b/src/Viserio/Support/AbstractManager.php @@ -5,7 +5,7 @@ use Closure; use InvalidArgumentException; use Viserio\Contracts\Config\Manager as ConfigContract; -use Viserio\Support\Traits\ContainerAwareTrait; +use Viserio\Contracts\Container\Traits\ContainerAwareTrait; abstract class AbstractManager { diff --git a/src/Viserio/Support/Invoker.php b/src/Viserio/Support/Invoker.php index 5e51036ad..05c2386bb 100644 --- a/src/Viserio/Support/Invoker.php +++ b/src/Viserio/Support/Invoker.php @@ -10,7 +10,7 @@ use Invoker\ParameterResolver\DefaultValueResolver; use Invoker\ParameterResolver\NumericArrayResolver; use Invoker\ParameterResolver\ResolverChain; -use Viserio\Support\Traits\ContainerAwareTrait; +use Viserio\Contracts\Container\Traits\ContainerAwareTrait; class Invoker implements InvokerInterface { diff --git a/src/Viserio/Support/composer.json b/src/Viserio/Support/composer.json index cc4ca87fd..51dd1a913 100644 --- a/src/Viserio/Support/composer.json +++ b/src/Viserio/Support/composer.json @@ -18,42 +18,42 @@ } ], "require": { - "php" : "7.0.0 - 7.0.5 || ^7.0.7", - "viserio/contracts" : "self.version" + "php" : "7.0.0 - 7.0.5 || ^7.0.7", + "psr/log" : "^1.0", + "viserio/contracts" : "self.version" }, "require-dev": { - "danielstjules/stringy" : "^2.3", - "narrowspark/php-cs-fixer-config" : "^1.1", - "narrowspark/testing-helper" : "^1.5", - "php-di/invoker" : "^1.3", - "phpunit/phpunit" : "^5.1", - "symfony/var-dumper" : "^3.1" + "danielstjules/stringy" : "^2.3", + "narrowspark/php-cs-fixer-config" : "^1.1", + "narrowspark/testing-helper" : "^1.5", + "php-di/invoker" : "^1.3", + "phpunit/phpunit" : "^5.1", + "symfony/var-dumper" : "^3.1" }, "autoload": { "psr-4": { - "Viserio\\Support\\" : "" + "Viserio\\Support\\" : "" }, - "exclude-from-classmap" : ["/Tests/"] + "exclude-from-classmap" : ["/Tests/"] }, "autoload-dev": { "psr-4": { - "Viserio\\Support\\Tests\\" : "Tests/" + "Viserio\\Support\\Tests\\" : "Tests/" } }, "provide": { - "container-interop/container-interop-implementation" : "~1.1", - "psr/log-implementation" : "~1.0" + "psr/log-implementation" : "~1.0" }, "extra": { "branch-alias": { - "dev-master" : "1.0-dev" + "dev-master" : "1.0-dev" } }, "suggest": { - "danielstjules/stringy" : "Required to use the Stringy Class in Str (^2.3)", - "php-di/invoker" : "Required to use the Invoker Class (^1.3)", - "symfony/var-dumper" : "Improves the Dumper::dump() (^3.1)." + "danielstjules/stringy" : "Required to use the Stringy Class in Str (^2.3)", + "php-di/invoker" : "Required to use the Invoker Class (^1.3)", + "symfony/var-dumper" : "Improves the Dumper::dump() (^3.1)." }, - "minimum-stability" : "dev", - "prefer-stable" : true + "minimum-stability" : "dev", + "prefer-stable" : true } diff --git a/src/Viserio/View/Virtuoso.php b/src/Viserio/View/Virtuoso.php index a8a706acf..922f09329 100644 --- a/src/Viserio/View/Virtuoso.php +++ b/src/Viserio/View/Virtuoso.php @@ -6,14 +6,14 @@ use Interop\Container\ContainerInterface; use InvalidArgumentException; use Viserio\Contracts\{ + Container\Traits\ContainerAwareTrait, Events\Dispatcher as DispatcherContract, View\View as ViewContract, View\Virtuoso as VirtuosoContract }; use Viserio\Support\{ Invoker, - Str, - Traits\ContainerAwareTrait + Str }; use Viserio\View\Traits\NormalizeNameTrait; From e74caa5e9b4f39e8973d77809c4148849c71c9a5 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sun, 7 Aug 2016 01:12:17 +0200 Subject: [PATCH 02/39] micro-optimization --- src/Viserio/Pipeline/Pipeline.php | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/Viserio/Pipeline/Pipeline.php b/src/Viserio/Pipeline/Pipeline.php index c449759af..d0f1e78a9 100644 --- a/src/Viserio/Pipeline/Pipeline.php +++ b/src/Viserio/Pipeline/Pipeline.php @@ -71,15 +71,9 @@ public function then(Closure $destination) $firstSlice = $this->getInitialSlice($destination); $stages = array_reverse($this->stages); + $callable = array_reduce($stages, $this->getSlice(), $firstSlice); - return call_user_func( - array_reduce( - $stages, - $this->getSlice(), - $firstSlice - ), - $this->traveler - ); + return $callable($this->traveler); } /** @@ -93,7 +87,7 @@ protected function getSlice(): Closure return function ($traveler) use ($stack, $stage) { // If the $stage is an instance of a Closure, we will just call it directly. if ($stage instanceof Closure) { - return call_user_func($stage, $traveler, $stack); + return $stage($traveler, $stack); // Otherwise we'll resolve the stages out of the container and call it with // the appropriate method and arguments, returning the results back out. @@ -101,17 +95,17 @@ protected function getSlice(): Closure return $this->sliceThroughContainer($traveler, $stack, $stage); } elseif (is_array($stage)) { $reflectionClass = new ReflectionClass(array_shift($stage)); + $parameters = [$traveler, $stack]; - return call_user_func_array( - $reflectionClass->newInstanceArgs($stage), - [$traveler, $stack] - ); + return $reflectionClass->newInstanceArgs($stage)(...$parameters); } // If the pipe is already an object we'll just make a callable and pass it to // the pipe as-is. There is no need to do any extra parsing and formatting // since the object we're given was already a fully instantiated object. - return call_user_func_array([$stage, $this->method], [$traveler, $stack]); + $parameters = [$traveler, $stack]; + + return $stage->{$this->method}(...$parameters); }; }; } @@ -126,7 +120,7 @@ protected function getSlice(): Closure protected function getInitialSlice(Closure $destination): Closure { return function ($traveler) use ($destination) { - return call_user_func($destination, $traveler); + return $destination($traveler); }; } From 7a32f98809e0f13f99dcadc2c92d6a937f9b4561 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sun, 7 Aug 2016 01:43:18 +0200 Subject: [PATCH 03/39] working on route --- src/Viserio/Contracts/Routing/Route.php | 8 ++++ src/Viserio/Routing/Route.php | 60 ++++++++++++++++++++++++- src/Viserio/Routing/Router.php | 1 + 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/Viserio/Contracts/Routing/Route.php diff --git a/src/Viserio/Contracts/Routing/Route.php b/src/Viserio/Contracts/Routing/Route.php new file mode 100644 index 000000000..7b12b6ca3 --- /dev/null +++ b/src/Viserio/Contracts/Routing/Route.php @@ -0,0 +1,8 @@ + Date: Sun, 7 Aug 2016 14:02:49 +0200 Subject: [PATCH 04/39] remove old tests --- src/Viserio/Contracts/Routing/Route.php | 44 ++++ src/Viserio/Middleware/Dispatcher.php | 5 + src/Viserio/Routing/RouteCollection.php | 56 ++++- src/Viserio/Routing/RouteGroup.php | 24 +++ src/Viserio/Routing/Tests/DispatcherTest.php | 9 +- .../Routing/Tests/RouteCollectionTest.php | 204 ------------------ src/Viserio/Routing/Tests/RouteParserTest.php | 18 -- src/Viserio/Routing/composer.json | 3 +- 8 files changed, 127 insertions(+), 236 deletions(-) delete mode 100644 src/Viserio/Routing/Tests/RouteParserTest.php diff --git a/src/Viserio/Contracts/Routing/Route.php b/src/Viserio/Contracts/Routing/Route.php index 7b12b6ca3..f23d4030b 100644 --- a/src/Viserio/Contracts/Routing/Route.php +++ b/src/Viserio/Contracts/Routing/Route.php @@ -4,5 +4,49 @@ interface Route { + /** + * Get the domain defined for the route. + * + * @return string|null + */ + public function domain(); + /** + * Get the URI that the route responds to. + * + * @return string + */ + public function getUri(): string; + + /** + * Set the URI that the route responds to. + * + * @param string $uri + * + * @return $this + */ + public function setUri(string $uri); + + /** + * Get the prefix of the route instance. + * + * @return string + */ + public function getPrefix(): string; + + /** + * Get the name of the route instance. + * + * @return string + */ + public function getName(); + + /** + * Add or change the route name. + * + * @param string $name + * + * @return $this + */ + public function name(string $name); } diff --git a/src/Viserio/Middleware/Dispatcher.php b/src/Viserio/Middleware/Dispatcher.php index 9b8fcb506..dd435b463 100644 --- a/src/Viserio/Middleware/Dispatcher.php +++ b/src/Viserio/Middleware/Dispatcher.php @@ -33,6 +33,11 @@ class Dispatcher implements StackContract */ protected $response; + /** + * Create a new middleware instance. + * + * @param \Psr\Http\Message\ResponseInterface $response + */ public function __construct(ResponseInterface $response) { $this->response = $response; diff --git a/src/Viserio/Routing/RouteCollection.php b/src/Viserio/Routing/RouteCollection.php index 9f0256a32..276cf9ba9 100644 --- a/src/Viserio/Routing/RouteCollection.php +++ b/src/Viserio/Routing/RouteCollection.php @@ -2,11 +2,15 @@ declare(strict_types=1); namespace Viserio\Routing; +use ArrayIterator; use Closure; +use Countable; use Interop\Container\ContainerInterface; use InvalidArgumentException; +use IteratorAggregate; use LogicException; use RuntimeException; +use RapidRoute\RouteParser; use Viserio\Contracts\{ Container\Traits\ContainerAwareTrait, Routing\RouteCollector as RouteCollectorContract, @@ -14,19 +18,23 @@ }; use Viserio\Routing\RouteParser as ViserioRouteParser; -class RouteCollection implements RouteStrategyContract, RouteCollectorContract +class RouteCollection implements RouteStrategyContract, RouteCollectorContract, Countable, IteratorAggregate { use ContainerAwareTrait; - /** - * @var \Interop\Container\ContainerInterface + /** + * An array of the routes keyed by method. + * + * @var array */ - protected $container; + protected $routes = []; /** + * An flattened array of all of the routes. + * * @var array */ - protected $routes = []; + protected $allRoutes = []; /** * @var array @@ -43,6 +51,11 @@ class RouteCollection implements RouteStrategyContract, RouteCollectorContract */ protected $groups = []; + /** + * @var \RapidRoute\RouteParser + */ + protected $parser = []; + /** * @var array */ @@ -57,11 +70,14 @@ class RouteCollection implements RouteStrategyContract, RouteCollectorContract /** * Constructor. * + * @param \RapidRoute\RouteParser $parser * @param \Interop\Container\ContainerInterface $container */ public function __construct( + RouteParser $parser, ContainerInterface $container ) { + $this->parser = $parser; $this->container = $container; } @@ -320,6 +336,36 @@ public function getNamedRoutes() return $this->namedRoutes; } + /** + * Get all of the routes in the collection. + * + * @return array + */ + public function getRoutes() + { + return array_values($this->allRoutes); + } + + /** + * Get an iterator for the items. + * + * @return \ArrayIterator + */ + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->getRoutes()); + } + + /** + * Count the number of items in the collection. + * + * @return int + */ + public function count(): int + { + return count($this->getRoutes()); + } + /** * @param string|null $name * @param callable $handler diff --git a/src/Viserio/Routing/RouteGroup.php b/src/Viserio/Routing/RouteGroup.php index 377d04c51..2378f6d46 100644 --- a/src/Viserio/Routing/RouteGroup.php +++ b/src/Viserio/Routing/RouteGroup.php @@ -40,4 +40,28 @@ public function __invoke() { call_user_func_array($this->callback, [$this]); } + + /** + * {@inheritdoc} + */ + public function map($method, $path, $handler) + { + $path = ($path === '/') ? $this->prefix : $this->prefix . sprintf('/%s', ltrim($path, '/')); + $route = $this->collection->map($method, $path, $handler); + $route->setParentGroup($this); + + if ($host = $this->getHost()) { + $route->setHost($host); + } + + if ($scheme = $this->getScheme()) { + $route->setScheme($scheme); + } + + foreach ($this->getMiddlewareStack() as $middleware) { + $route->middleware($middleware); + } + + return $route; + } } diff --git a/src/Viserio/Routing/Tests/DispatcherTest.php b/src/Viserio/Routing/Tests/DispatcherTest.php index 0019dcf62..990ee5bde 100644 --- a/src/Viserio/Routing/Tests/DispatcherTest.php +++ b/src/Viserio/Routing/Tests/DispatcherTest.php @@ -9,12 +9,5 @@ class DispatcherTest extends \PHPUnit_Framework_TestCase { - private function getRouteCollection() - { - return new RouteCollection( - new Container(), - new RouteParser(), - new GroupCountBased() - ); - } + } diff --git a/src/Viserio/Routing/Tests/RouteCollectionTest.php b/src/Viserio/Routing/Tests/RouteCollectionTest.php index c9a5679dc..53739f283 100644 --- a/src/Viserio/Routing/Tests/RouteCollectionTest.php +++ b/src/Viserio/Routing/Tests/RouteCollectionTest.php @@ -2,214 +2,10 @@ declare(strict_types=1); namespace Viserio\Routing\Tests; -use FastRoute\DataGenerator\GroupCountBased; -use Viserio\Container\Container; use Viserio\Routing\RouteCollection; use Viserio\Routing\RouteParser; class RouteCollectionTest extends \PHPUnit_Framework_TestCase { - /** - * Asserts that routes are set via convenience methods. - */ - public function testSetsRoutesViaConvenienceMethods() - { - $router = $this->getRouteCollection(); - $router->get('/route/{wildcard}', 'handler_get', RouteCollection::RESTFUL_STRATEGY); - $router->post('/route/{wildcard}', 'handler_post', RouteCollection::URI_STRATEGY); - $router->put('/route/{wildcard}', 'handler_put', RouteCollection::REQUEST_RESPONSE_STRATEGY); - $router->patch('/route/{wildcard}', 'handler_patch'); - $router->delete('/route/{wildcard}', 'handler_delete'); - $router->head('/route/{wildcard}', 'handler_head'); - $router->options('/route/{wildcard}', 'handler_options'); - - $routes = (new \ReflectionClass($router))->getProperty('routes'); - $routes->setAccessible(true); - $routes = $routes->getValue($router); - - $this->assertCount(7, $routes); - - $this->assertSame($routes['handler_get'], ['strategy' => 1]); - $this->assertSame($routes['handler_post'], ['strategy' => 2]); - $this->assertSame($routes['handler_put'], ['strategy' => 0]); - $this->assertSame($routes['handler_patch'], ['strategy' => 0]); - $this->assertSame($routes['handler_delete'], ['strategy' => 0]); - $this->assertSame($routes['handler_head'], ['strategy' => 0]); - $this->assertSame($routes['handler_options'], ['strategy' => 0]); - } - - /** - * Asserts that routes are set via convenience methods with Closures. - */ - public function testSetsRoutesViaConvenienceMethodsWithClosures() - { - $router = $this->getRouteCollection(); - - $router->get('/route/{wildcard}', function () { - return 'get'; - }); - $router->post('/route/{wildcard}', function () { - return 'post'; - }); - $router->put('/route/{wildcard}', function () { - return 'put'; - }); - $router->patch('/route/{wildcard}', function () { - return 'patch'; - }); - $router->delete('/route/{wildcard}', function () { - return 'delete'; - }); - $router->head('/route/{wildcard}', function () { - return 'head'; - }); - $router->options('/route/{wildcard}', function () { - return 'options'; - }); - $router->any('/route/{wildcard}', function () { - return 'any'; - }); - - $routes = (new \ReflectionClass($router))->getProperty('routes'); - $routes->setAccessible(true); - $routes = $routes->getValue($router); - - $this->assertCount(8, $routes); - - foreach ($routes as $route) { - $this->assertArrayHasKey('callback', $route); - $this->assertArrayHasKey('strategy', $route); - } - } - - /** - * Asserts that global strategy is used when set. - */ - public function testGlobalStrategyIsUsedWhenSet() - { - $router = $this->getRouteCollection(); - - $router->setStrategy(RouteCollection::URI_STRATEGY); - $router->get('/route/{wildcard}', 'handler_get', RouteCollection::RESTFUL_STRATEGY); - $router->post('/route/{wildcard}', 'handler_post', RouteCollection::URI_STRATEGY); - $router->put('/route/{wildcard}', 'handler_put', RouteCollection::REQUEST_RESPONSE_STRATEGY); - $router->patch('/route/{wildcard}', 'handler_patch'); - $router->delete('/route/{wildcard}', 'handler_delete'); - $router->head('/route/{wildcard}', 'handler_head'); - $router->options('/route/{wildcard}', 'handler_options'); - $routes = (new \ReflectionClass($router))->getProperty('routes'); - $routes->setAccessible(true); - $routes = $routes->getValue($router); - - $this->assertCount(7, $routes); - - $this->assertSame($routes['handler_get'], ['strategy' => 2]); - $this->assertSame($routes['handler_post'], ['strategy' => 2]); - $this->assertSame($routes['handler_put'], ['strategy' => 2]); - $this->assertSame($routes['handler_patch'], ['strategy' => 2]); - $this->assertSame($routes['handler_delete'], ['strategy' => 2]); - $this->assertSame($routes['handler_head'], ['strategy' => 2]); - $this->assertSame($routes['handler_options'], ['strategy' => 2]); - } - - /** - * Asserts that an exception is thrown when an incorrect strategy type is provided. - */ - public function testExceptionIsThrownWhenWrongStrategyTypeProvided() - { - $this->setExpectedException('InvalidArgumentException'); - $router = $this->getRouteCollection(); - $router->setStrategy('hello'); - } - - /** - * Asserts that `getDispatcher` method returns correct instance. - */ - public function testCollectionReturnsDispatcher() - { - $router = $this->getRouteCollection(); - $this->assertInstanceOf('Viserio\Routing\Dispatcher', $router->getDispatcher()); - $this->assertInstanceOf('FastRoute\Dispatcher\GroupCountBased', $router->getDispatcher()); - } - - /** - * Asserts named routes are put in namedRoute array. - */ - public function testNamedRoutesAreProperlyHandled() - { - $router = $this->getRouteCollection(); - - $router->addRoute('GET', 'noname', function () { - }); - $router->addRoute('GET', '@name/named-route', function () { - }); - $router->addRoute('GET', '@another-name/another-named-route', function () { - }); - - $data = $router->getData(); - - $this->assertCount(2, $router->getNamedRoutes()); - $this->assertCount(3, $data); - $this->assertEquals(['GET', '/', ['handler' => 'handler0', 'name' => 'name']], $data[1]); - $this->assertEquals(['GET', '/', ['handler' => 'handler0', 'name' => 'another-name']], $data[2]); - } - - public function testCallableControllers() - { - $router = $this->getRouteCollection(); - - $router->get('/', new CallableController()); - - $routes = (new \ReflectionClass($router))->getProperty('routes'); - $routes->setAccessible(true); - $routes = $routes->getValue($router); - - $this->assertCount(1, $routes); - } - - /** - * @expectedException \RuntimeException - */ - public function testNonCallbleObjectControllersError() - { - $router = $this->getRouteCollection(); - - $router->get('/', new \stdClass()); - - $routes = (new \ReflectionClass($router))->getProperty('routes'); - $routes->setAccessible(true); - $routes = $routes->getValue($router); - - $this->assertCount(0, $routes); - } - - public function testRedirect() - { - $router = $this->getRouteCollection(); - - $this->assertSame($router->redirect(), new \Viserio\Routing\Redirect($router)); - } - - private function getRouteCollection() - { - return new RouteCollection( - new Container(), - new RouteParser(), - new GroupCountBased() - ); - } -} - -class CallableController -{ - /** - * @param Symfony\Component\HttpFoundation\Request $request - * @param Symfony\Component\HttpFoundation\Response $response - */ - public function __invoke( - Symfony\Component\HttpFoundation\Request $request, - Symfony\Component\HttpFoundation\Response $response - ) { - } } diff --git a/src/Viserio/Routing/Tests/RouteParserTest.php b/src/Viserio/Routing/Tests/RouteParserTest.php deleted file mode 100644 index 3c0cffc50..000000000 --- a/src/Viserio/Routing/Tests/RouteParserTest.php +++ /dev/null @@ -1,18 +0,0 @@ -assertEquals($parser->parse('@bundle.named_route/my-route'), $parser->parse('/my-route')); - } -} diff --git a/src/Viserio/Routing/composer.json b/src/Viserio/Routing/composer.json index b7e21034e..f82ae66c6 100644 --- a/src/Viserio/Routing/composer.json +++ b/src/Viserio/Routing/composer.json @@ -20,8 +20,9 @@ "require": { "php" : "7.0.0 - 7.0.5 || ^7.0.7", "container-interop/container-interop" : "^1.0", - "timetoogo/rapid-route" : "^0.5", "psr/http-message" : "^1.0", + "timetoogo/rapid-route" : "^2.0", + "viserio/middleware" : "self.version", "viserio/contracts" : "self.version" }, "require-dev": { From 182978d39e466679fc5ea5509f9d6ed7dd024da8 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sun, 7 Aug 2016 20:23:10 +0200 Subject: [PATCH 05/39] added a ByteCountingStream --- .../ByteCountingStreamException.php | 62 +++++++++ .../ByteCountingStreamExceptionTest.php | 29 ++++ src/Viserio/Contracts/phpunit.xml.dist | 40 ++++++ .../Http/Stream/ByteCountingStream.php | 77 +++++++++++ .../Tests/Stream/ByteCountingStreamTest.php | 67 ++++++++++ src/Viserio/Routing/Controller.php | 7 - src/Viserio/Routing/ControllerDispatcher.php | 7 - src/Viserio/Routing/Dispatcher.php | 5 - src/Viserio/Routing/Redirect.php | 124 +----------------- src/Viserio/Routing/Route.php | 2 +- 10 files changed, 283 insertions(+), 137 deletions(-) create mode 100644 src/Viserio/Contracts/Http/Exceptions/ByteCountingStreamException.php create mode 100644 src/Viserio/Contracts/Tests/Http/Exception/ByteCountingStreamExceptionTest.php create mode 100644 src/Viserio/Contracts/phpunit.xml.dist create mode 100644 src/Viserio/Http/Stream/ByteCountingStream.php create mode 100644 src/Viserio/Http/Tests/Stream/ByteCountingStreamTest.php delete mode 100644 src/Viserio/Routing/Controller.php delete mode 100644 src/Viserio/Routing/ControllerDispatcher.php diff --git a/src/Viserio/Contracts/Http/Exceptions/ByteCountingStreamException.php b/src/Viserio/Contracts/Http/Exceptions/ByteCountingStreamException.php new file mode 100644 index 000000000..ffc4d4c54 --- /dev/null +++ b/src/Viserio/Contracts/Http/Exceptions/ByteCountingStreamException.php @@ -0,0 +1,62 @@ +expectBytes = $expect; + $this->actualBytes = $actual; + + parent::__construct($msg, 0, $previous); + } + + /** + * Get expected bytes to be read. + * + * @return int + */ + public function getExpectBytes(): int + { + return $this->expectBytes; + } + + /** + * Get remaining bytes available for read. + * + * @return int + */ + public function getRemainingBytes(): int + { + return $this->actualBytes; + } +} diff --git a/src/Viserio/Contracts/Tests/Http/Exception/ByteCountingStreamExceptionTest.php b/src/Viserio/Contracts/Tests/Http/Exception/ByteCountingStreamExceptionTest.php new file mode 100644 index 000000000..63beaf150 --- /dev/null +++ b/src/Viserio/Contracts/Tests/Http/Exception/ByteCountingStreamExceptionTest.php @@ -0,0 +1,29 @@ +assertEquals($msg, $exception->getMessage()); + $this->assertSame($prev, $exception->getPrevious()); + } + + public function getTestCases() + { + return [[7, 5], [5, 0]]; + } +} diff --git a/src/Viserio/Contracts/phpunit.xml.dist b/src/Viserio/Contracts/phpunit.xml.dist new file mode 100644 index 000000000..c5e4bab3a --- /dev/null +++ b/src/Viserio/Contracts/phpunit.xml.dist @@ -0,0 +1,40 @@ + + + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./vendor + ./Tests + + + + + diff --git a/src/Viserio/Http/Stream/ByteCountingStream.php b/src/Viserio/Http/Stream/ByteCountingStream.php new file mode 100644 index 000000000..e85185a30 --- /dev/null +++ b/src/Viserio/Http/Stream/ByteCountingStream.php @@ -0,0 +1,77 @@ +stream = $stream; + if (!is_int($bytesToRead) || $bytesToRead < 0) { + $msg = "Bytes to read should be a non-negative integer for " + . "ByteCountingStream, got {$bytesToRead}."; + throw new InvalidArgumentException($msg); + } + + if ($this->stream->getSize() !== null && + $bytesToRead > $this->stream->getSize() + ) { + throw new ByteCountingStreamException( + $bytesToRead, + $this->stream->getSize() + ); + } + + $this->remaining = $bytesToRead; + } + + /** + * {@inheritdoc} + * + * @throws \Viserio\Contracts\Http\Exception\ByteCountingStreamException + */ + public function read($length) + { + if ($this->remaining === 0) { + return ''; + } + + $offset = $this->tell(); + $bytesToRead = min($length, $this->remaining); + $data = $this->stream->read($bytesToRead); + + $this->remaining -= strlen($data); + + if ((!$data || $data === '') && $this->remaining !== 0) { + // hits EOF + $provide = $this->tell() - $offset; + + throw new ByteCountingStreamException($this->remaining, $provide); + } + + return $data; + } +} diff --git a/src/Viserio/Http/Tests/Stream/ByteCountingStreamTest.php b/src/Viserio/Http/Tests/Stream/ByteCountingStreamTest.php new file mode 100644 index 000000000..d89c06c77 --- /dev/null +++ b/src/Viserio/Http/Tests/Stream/ByteCountingStreamTest.php @@ -0,0 +1,67 @@ +assertEquals('foo ', $testStream->read(4)); + $this->assertEquals('bar ', $testStream->read(4)); + $this->assertEquals('', $testStream->read(4)); + + $testStream->close(); + $testStream = new ByteCountingStream(Util::getStream('testing'), 5); + $testStream->seek(4); + + $this->assertEquals('ing', $testStream->read(5)); + + $testStream->close(); + } + + /** + * @expectedException \Viserio\Contracts\Http\Exceptions\ByteCountingStreamException + * @expectedExceptionMessage The ByteCountingStream decorator expects to be able to read + */ + public function testEnsureStopReadWhenHitEof() + { + $testStream = new ByteCountingStream(Util::getStream('abc'), 3); + $testStream->seek(3); + $testStream->read(3); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage The stream is detached + */ + public function testEnsureReadUnclosedStream() + { + $body = Util::getStream("closed"); + $closedStream = new ByteCountingStream($body, 5); + $body->close(); + $closedStream->read(3); + } +} diff --git a/src/Viserio/Routing/Controller.php b/src/Viserio/Routing/Controller.php deleted file mode 100644 index 5970ba372..000000000 --- a/src/Viserio/Routing/Controller.php +++ /dev/null @@ -1,7 +0,0 @@ -route = $route; - } - - /** - * [to description]. - * - * @param string $location - * - * @return $this - */ - public function to($location) - { - $this->mode = 'redirect'; - $this->location = $location; - - return $this; - } - - /** - * [toRoute description]. - * - * @param string $routeName [description] - * @param array $parameters - * - * @return $this - */ - public function toRoute($routeName, array $parameters = []) - { - $this->mode = 'named'; - $this->routeName = $routeName; - $this->parameters = $parameters; - - return $this; - } - - /** - * [toAction description]. - * - * @param [type] $action [description] - * @param array $parameters - * - * @return $this - */ - public function toAction($action, array $parameters = []) - { - $this->mode = 'action'; - $this->action = $action; - $this->parameters = $parameters; - - return $this; - } + protected $generator; /** - * [with description]. - * - * @param $key - * @param $value - * - * @internal param $ [type] $key [description] - * @internal param $ [type] $value [description] + * Create a new Redirector instance. * - * @return $this [type] [description] + * @param \Viserio\Routing\UrlGenerator */ - public function with($key, $value) + public function __construct(UrlGenerator $generator) { - //session - - return $this; - } - - /** - * [execute description]. - * - * @return bool - */ - public function execute() - { - switch ($this->mode) { - case 'redirect': - header('Location: ' . $this->location); - break; - - case 'named': - $this->route->runNamed($this->routeName, $this->parameters); - break; - - case 'action': - if (is_string($this->action)) { - $this->stringToCallback($this->action); - } - - $this->route->execute($this->action, $this->parameters); - break; - } - - return false; - } - - /** - * [stringToCallback description]. - * - * @param string $callback - * - * @throws \Exception - * - * @return bool - */ - protected function stringToCallback(&$callback) - { - if (substr_count($callback, '::') === 1) { - $callback = explode('::', $callback); - - return true; - } - - throw new \Exception('Invalid callback: ' . $callback); + $this->generator = $generator; } } diff --git a/src/Viserio/Routing/Route.php b/src/Viserio/Routing/Route.php index f9d6b7bae..c787db233 100644 --- a/src/Viserio/Routing/Route.php +++ b/src/Viserio/Routing/Route.php @@ -5,7 +5,7 @@ use RapidRoute\Route as BaseRoute; use Viserio\Contracts\Routing\Route as RouteContract; -class Route extends BaseRoute implements RouteContract +class Route extends BaseRoute { /** * The URI pattern the route responds to. From 52a6f5ff7f2e968ffdee63eec310fe1eeb8411be Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sun, 7 Aug 2016 23:47:51 +0200 Subject: [PATCH 06/39] update interfaces --- src/Viserio/Contracts/Routing/Route.php | 73 ++++++-- src/Viserio/Contracts/Routing/RouteGroup.php | 7 + src/Viserio/Routing/AbstractController.php | 8 + src/Viserio/Routing/Route.php | 171 ++++++++++++++++++- 4 files changed, 246 insertions(+), 13 deletions(-) create mode 100644 src/Viserio/Contracts/Routing/RouteGroup.php create mode 100644 src/Viserio/Routing/AbstractController.php diff --git a/src/Viserio/Contracts/Routing/Route.php b/src/Viserio/Contracts/Routing/Route.php index f23d4030b..7bc0b5544 100644 --- a/src/Viserio/Contracts/Routing/Route.php +++ b/src/Viserio/Contracts/Routing/Route.php @@ -9,7 +9,7 @@ interface Route * * @return string|null */ - public function domain(); + public function getDomain(); /** * Get the URI that the route responds to. @@ -25,28 +25,81 @@ public function getUri(): string; * * @return $this */ - public function setUri(string $uri); + public function setUri(string $uri): Route; /** - * Get the prefix of the route instance. + * Get the name of the route instance. * - * @return string + * @return string|null */ - public function getPrefix(): string; + public function getName(); /** - * Get the name of the route instance. + * Add or change the route name. + * + * @param string $name + * + * @return $this + */ + public function setName(string $name): Route; + + /** + * Get the HTTP verbs the route responds to. + * + * @return array + */ + public function getMethods(): array; + + /** + * Determine if the route only responds to HTTP requests. + * + * @return bool + */ + public function httpOnly(): bool; + + /** + * Determine if the route only responds to HTTPS requests. + * + * @return bool + */ + public function httpsOnly(): bool; + + /** + * Get the action name for the route. * * @return string */ - public function getName(); + public function getActionName(): string; /** - * Add or change the route name. + * Get the action array for the route. * - * @param string $name + * @return array + */ + public function getAction(): array; + + /** + * Set the action array for the route. + * + * @param array $action * * @return $this */ - public function name(string $name); + public function setAction(array $action): Route; + + /** + * Get the parent group. + * + * @return \Viserio\Contracts\Routing\RouteGroup + */ + public function getParentGroup(): RouteGroup; + + /** + * Set the parent group. + * + * @param \Viserio\Contracts\Routing\RouteGroup $group + * + * @return \Viserio\Contracts\Routing\Route + */ + public function setParentGroup(RouteGroup $group): Route; } diff --git a/src/Viserio/Contracts/Routing/RouteGroup.php b/src/Viserio/Contracts/Routing/RouteGroup.php new file mode 100644 index 000000000..fa44d57cc --- /dev/null +++ b/src/Viserio/Contracts/Routing/RouteGroup.php @@ -0,0 +1,7 @@ +action['domain'] ?? null; + } + + /** + * Get the URI associated with the route. + * + * @return string + */ + public function getUri(): string + { + return $this->uri; + } + + /** + * Set the URI that the route responds to. + * + * @param string $uri + * + * @return $this + */ + public function setUri(string $uri): RouteContract + { + $this->uri = $uri; + + return $this; + } + + /** + * Get the name of the route instance. + * + * @return string + */ + public function getName() + { + return $this->action['as'] ?? null; + } + + /** + * Add or change the route name. + * + * @param string $name + * + * @return $this + */ + public function setName(string $name): RouteContract + { + $this->action['as'] = isset($this->action['as']) ? $this->action['as'] . $name : $name; + + return $this; + } + + /** + * Get the HTTP verbs the route responds to. + * + * @return array + */ + public function getMethods(): array + { + return $this->httpMethods; + } + + /** + * Determine if the route only responds to HTTP requests. + * + * @return bool + */ + public function httpOnly(): bool + { + return in_array('http', $this->action, true); + } + + /** + * Determine if the route only responds to HTTPS requests. + * + * @return bool + */ + public function httpsOnly(): bool + { + return in_array('https', $this->action, true); + } + + /** + * Get the action name for the route. + * + * @return string + */ + public function getActionName(): string + { + return $this->action['controller'] ?? 'Closure'; + } + + /** + * Get the action array for the route. + * + * @return array + */ + public function getAction(): array + { + return $this->action; + } + + /** + * Set the action array for the route. + * + * @param array $action + * + * @return $this + */ + public function setAction(array $action): RouteContract + { + $this->action = $action; + + return $this; + } + + /** + * Get the parent group. + * + * @return \Viserio\Contracts\Routing\RouteGroup + */ + public function getParentGroup(): RouteGroupContract + { + return $this->group; + } + + /** + * Set the parent group. + * + * @param \Viserio\Contracts\Routing\RouteGroup $group + * + * @return \Viserio\Contracts\Routing\Route + */ + public function setParentGroup(RouteGroupContract $group): RouteContract + { + $this->group = $group; + + return $this; + } } From 362398a2a59a0dbcd9a8be2e73528071ad1355a9 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Tue, 9 Aug 2016 00:09:03 +0200 Subject: [PATCH 07/39] added more functions to route --- composer.json | 4 +- src/Viserio/Cache/composer.json | 10 +- src/Viserio/Contracts/Routing/Route.php | 89 ++++- .../Contracts/Routing/RouteCollector.php | 8 - .../Contracts/Routing/RouteStrategy.php | 13 - src/Viserio/Contracts/Routing/Router.php | 106 ++++++ src/Viserio/Http/Request.php | 2 +- src/Viserio/Routing/Route.php | 276 +++++++++++--- src/Viserio/Routing/RouteCollection.php | 354 +----------------- src/Viserio/Routing/Router.php | 198 ++++++++++ src/Viserio/Routing/Tests/RouteTest.php | 7 + src/Viserio/Routing/composer.json | 6 +- 12 files changed, 646 insertions(+), 427 deletions(-) delete mode 100644 src/Viserio/Contracts/Routing/RouteCollector.php delete mode 100644 src/Viserio/Contracts/Routing/RouteStrategy.php create mode 100644 src/Viserio/Contracts/Routing/Router.php create mode 100644 src/Viserio/Routing/Tests/RouteTest.php diff --git a/composer.json b/composer.json index 5d9356182..7e1c83ab9 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "php" : "7.0.0 - 7.0.5 || ^7.0.7", "ext-openssl" : "*", "ext-fileinfo" : "*", - "cache/chain-adapter" : "^0.3", + "cache/chain-adapter" : "^0.4", "cache/namespaced-cache" : "^0.1", "classpreloader/classpreloader" : "^3.0", "container-interop/container-interop" : "^1.1", @@ -104,7 +104,7 @@ "aws/aws-sdk-php" : "^3.18", "cache/array-adapter" : "^0.4", "cache/filesystem-adapter" : "^0.3", - "cache/session-handler" : "^0.1", + "cache/session-handler" : "^0.2", "cache/void-adapter" : "^0.3", "fzaninotto/faker" : "^1.6", "guzzlehttp/guzzle" : "^6.0", diff --git a/src/Viserio/Cache/composer.json b/src/Viserio/Cache/composer.json index 16aae3b90..7f18388b5 100644 --- a/src/Viserio/Cache/composer.json +++ b/src/Viserio/Cache/composer.json @@ -39,16 +39,16 @@ ], "require": { "php" : "7.0.0 - 7.0.5 || ^7.0.7", - "cache/chain-adapter" : "^0.3", + "cache/chain-adapter" : "^0.4", "cache/namespaced-cache" : "^0.1", "psr/cache" : "^1.0", "viserio/config" : "self.version", "viserio/cotracts" : "self.version" }, "require-dev": { - "cache/array-adapter" : "^0.2", + "cache/array-adapter" : "^0.4", "cache/filesystem-adapter" : "^0.3", - "cache/session-handler" : "^0.1", + "cache/session-handler" : "^0.2", "cache/void-adapter" : "^0.3", "narrowspark/php-cs-fixer-config" : "^1.1", "narrowspark/testing-helper" : "^1.5", @@ -76,13 +76,13 @@ "suggest": { "cache/apc-adapter" : "Required to use the Apc cache (^0.3).", "cache/apcu-adapter" : "Required to use the Apcu cache (^0.3).", - "cache/array-adapter" : "Required to use the Array cache (^0.2)", + "cache/array-adapter" : "Required to use the Array cache (^0.4)", "cache/filesystem-adapter" : "Required to use the Filesystem cache (^0.3).", "cache/memcache-adapter" : "Required to use the Memcache cache (^0.3).", "cache/memcached-adapter" : "Required to use the Memcached cache (^0.3).", "cache/mongodb-adapter" : "Required to use the Mongodb cache (^0.2).", "cache/predis-adapter" : "Required to use the Predis cache (^0.4).", - "cache/session-handler" : "Required to use the Session cache (^0.1).", + "cache/session-handler" : "Required to use the Session cache (^0.2).", "cache/void-adapter" : "Required to use the Void cache (^0.3)." }, "minimum-stability" : "dev", diff --git a/src/Viserio/Contracts/Routing/Route.php b/src/Viserio/Contracts/Routing/Route.php index 7bc0b5544..b249172b5 100644 --- a/src/Viserio/Contracts/Routing/Route.php +++ b/src/Viserio/Contracts/Routing/Route.php @@ -87,19 +87,94 @@ public function getAction(): array; */ public function setAction(array $action): Route; + /** + * Add a prefix to the route URI. + * + * @param string $prefix + * + * @return $this + */ + public function prefix(string $prefix): Route; + + /** + * Get the prefix of the route instance. + * + * @return string + */ + public function getPrefix(): string; + /** - * Get the parent group. + * Set a parameter to the given value. * - * @return \Viserio\Contracts\Routing\RouteGroup + * @param string $name + * @param mixed $value + * + * @return $this */ - public function getParentGroup(): RouteGroup; + public function setParameter($name, $value): Route; /** - * Set the parent group. + * Get a given parameter from the route. * - * @param \Viserio\Contracts\Routing\RouteGroup $group + * @param string $name + * @param mixed $default + * + * @return string|object + */ + public function getParameter(string $name, $default = null); + + /** + * Determine a given parameter exists from the route. + * + * @param string $name * - * @return \Viserio\Contracts\Routing\Route + * @return bool + */ + public function hasParameter(string $name): bool; + + /** + * Get the key / value list of parameters for the route. + * + * @return array + * + * @throws \LogicException + */ + public function getParameters(): array; + + /** + * Determine if the route has parameters. + * + * @return bool + */ + public function hasParameters(): bool; + + /** + * Unset a parameter on the route if it is set. + * + * @param string $name + */ + public function forgetParameter(string $name); + + /** + * Check if route is a static route. + * + * @return bool + */ + public function isStatic(): bool; + + /** + * Run the route action and return the response. + * + * @return mixed + */ + public function run(); + + /** + * Set the router instance on the route. + * + * @param \Viserio\Contracts\Routing\Router $router + * + * @return $this */ - public function setParentGroup(RouteGroup $group): Route; + public function setRouter(Router $router): Route; } diff --git a/src/Viserio/Contracts/Routing/RouteCollector.php b/src/Viserio/Contracts/Routing/RouteCollector.php deleted file mode 100644 index 6572605e8..000000000 --- a/src/Viserio/Contracts/Routing/RouteCollector.php +++ /dev/null @@ -1,8 +0,0 @@ -uri) { + if ($this->uri === $uri) { return $this; } diff --git a/src/Viserio/Routing/Route.php b/src/Viserio/Routing/Route.php index a611255bb..b4d532ce8 100644 --- a/src/Viserio/Routing/Route.php +++ b/src/Viserio/Routing/Route.php @@ -2,10 +2,18 @@ declare(strict_types=1); namespace Viserio\Routing; +use LogicException; +use Narrowspark\Arr\StaticArr as Arr; +use RapidRoute\RouteSegments\ParameterSegment; +use UnexpectedValueException; use Viserio\Contracts\{ Container\Traits\ContainerAwareTrait, Routing\Route as RouteContract, - Routing\RouteGroup as RouteGroupContract + Routing\Router as RouterContract +}; +use Viserio\Support\{ + Invoker, + Str }; class Route implements RouteContract @@ -75,17 +83,37 @@ class Route implements RouteContract */ protected $router; + /** + * Invoker instance. + * + * @var \Viserio\Support\Invoker + */ + protected $invoker; + /** - * The route group instance used by the route. + * Create a new Route instance. * - * @var \Viserio\Contracts\Routing\RouteGroup + * @param array|string $methods + * @param string $uri + * @param \Closure|array|null $action */ - protected $group; + public function __construct($methods, $uri, $action) + { + $this->uri = $uri; + $this->methods = (array) $methods; + $this->action = $this->parseAction($action); + + if (in_array('GET', $this->methods) && ! in_array('HEAD', $this->methods)) { + $this->methods[] = 'HEAD'; + } + + if (isset($this->action['prefix'])) { + $this->prefix($this->action['prefix']); + } + } /** - * Get the domain defined for the route. - * - * @return string|null + * {@inheritdoc} */ public function getDomain() { @@ -93,9 +121,7 @@ public function getDomain() } /** - * Get the URI associated with the route. - * - * @return string + * {@inheritdoc} */ public function getUri(): string { @@ -103,11 +129,7 @@ public function getUri(): string } /** - * Set the URI that the route responds to. - * - * @param string $uri - * - * @return $this + * {@inheritdoc} */ public function setUri(string $uri): RouteContract { @@ -117,9 +139,7 @@ public function setUri(string $uri): RouteContract } /** - * Get the name of the route instance. - * - * @return string + * {@inheritdoc} */ public function getName() { @@ -127,11 +147,7 @@ public function getName() } /** - * Add or change the route name. - * - * @param string $name - * - * @return $this + * {@inheritdoc} */ public function setName(string $name): RouteContract { @@ -141,9 +157,7 @@ public function setName(string $name): RouteContract } /** - * Get the HTTP verbs the route responds to. - * - * @return array + * {@inheritdoc} */ public function getMethods(): array { @@ -151,9 +165,7 @@ public function getMethods(): array } /** - * Determine if the route only responds to HTTP requests. - * - * @return bool + * {@inheritdoc} */ public function httpOnly(): bool { @@ -161,9 +173,7 @@ public function httpOnly(): bool } /** - * Determine if the route only responds to HTTPS requests. - * - * @return bool + * {@inheritdoc} */ public function httpsOnly(): bool { @@ -171,9 +181,7 @@ public function httpsOnly(): bool } /** - * Get the action name for the route. - * - * @return string + * {@inheritdoc} */ public function getActionName(): string { @@ -181,9 +189,7 @@ public function getActionName(): string } /** - * Get the action array for the route. - * - * @return array + * {@inheritdoc} */ public function getAction(): array { @@ -191,11 +197,7 @@ public function getAction(): array } /** - * Set the action array for the route. - * - * @param array $action - * - * @return $this + * {@inheritdoc} */ public function setAction(array $action): RouteContract { @@ -205,26 +207,194 @@ public function setAction(array $action): RouteContract } /** - * Get the parent group. + * {@inheritdoc} + */ + public function prefix(string $prefix): RouteContract + { + $uri = rtrim($prefix, '/').'/'.ltrim($this->uri, '/'); + + $this->uri = trim($uri, '/'); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getPrefix(): string + { + return $this->action['prefix'] ?? ''; + } + + /** + * {@inheritdoc} + */ + public function setParameter($name, $value): RouteContract + { + $this->parameters(); + + $this->parameters[$name] = $value; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getParameter(string $name, $default = null) + { + return Arr::get($this->parameters(), $name, $default); + } + + /** + * {@inheritdoc} + */ + public function hasParameter(string $name): bool + { + return Arr::has($this->parameters(), $name); + } + + /** + * {@inheritdoc} + */ + public function getParameters(): array + { + if (isset($this->parameters)) { + return $this->parameters; + } + + throw new LogicException('Route is not bound.'); + } + + /** + * {@inheritdoc} + */ + public function hasParameters(): bool + { + return isset($this->parameters); + } + + /** + * {@inheritdoc} + */ + public function forgetParameter(string $name) + { + $this->parameters(); + + unset($this->parameters[$name]); + } + + /** + * {@inheritdoc} + */ + public function isStatic(): bool + { + foreach($this->parameters as $parameter) { + if ($parameter instanceof ParameterSegment) { + return false; + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function run() + { + $this->initInvoker(); + + return $this->invoker->call( + $this->action['uses'], + array_values($this->getParameters()) + ); + } + + /** + * {@inheritdoc} + */ + public function setRouter(RouterContract $router): RouteContract + { + $this->router = $router; + + return $this; + } + + /** + * Dynamically access route parameters. + * + * @param string $key * - * @return \Viserio\Contracts\Routing\RouteGroup + * @return mixed */ - public function getParentGroup(): RouteGroupContract + public function __get($key) { - return $this->group; + return $this->parameter($key); } /** - * Set the parent group. + * Set configured invoker. * - * @param \Viserio\Contracts\Routing\RouteGroup $group + * @return \Viserio\Support\Invoker; + */ + protected function initInvoker(): Invoker + { + if ($this->invoker === null) { + $this->invoker = (new Invoker()) + ->injectByTypeHint(true) + ->injectByParameterName(true) + ->setContainer($this->getContainer()); + } + + return $this->invoker; + } + + /** + * Parse the route action into a standard array. + * + * @param callable|array|null $action * - * @return \Viserio\Contracts\Routing\Route + * @return array + * + * @throws \UnexpectedValueException */ - public function setParentGroup(RouteGroupContract $group): RouteContract + protected function parseAction($action): array { - $this->group = $group; + // If no action is passed in right away, we assume the user will make use of + // fluent routing. In that case, we set a default closure, to be executed + // if the user never explicitly sets an action to handle the given uri. + if (is_null($action)) { + return ['uses' => function () { + throw new LogicException("Route for [{$this->uri}] has no action."); + }]; + } - return $this; + // If the action is already a Closure instance, we will just set that instance + // as the "uses" property. + if (is_callable($action)) { + return ['uses' => $action]; + } + + // If no "uses" property has been set, we will dig through the array to find a + // Closure instance within this list. We will set the first Closure we come across. + if (! isset($action['uses'])) { + $action['uses'] = Arr::first($action, function ($value, $key) { + return is_callable($value) && is_numeric($key); + }); + } + + if (is_string($action['uses']) && ! Str::contains($action['uses'], '::')) { + if (! method_exists($action, '__invoke')) { + throw new UnexpectedValueException(sprintf( + 'Invalid route action: [%s]', + $action + )); + } + + $action['uses'] = $action.'::__invoke'; + } + + return $action; } } diff --git a/src/Viserio/Routing/RouteCollection.php b/src/Viserio/Routing/RouteCollection.php index 276cf9ba9..1d242a5d7 100644 --- a/src/Viserio/Routing/RouteCollection.php +++ b/src/Viserio/Routing/RouteCollection.php @@ -3,22 +3,10 @@ namespace Viserio\Routing; use ArrayIterator; -use Closure; use Countable; -use Interop\Container\ContainerInterface; -use InvalidArgumentException; -use IteratorAggregate; -use LogicException; -use RuntimeException; -use RapidRoute\RouteParser; -use Viserio\Contracts\{ - Container\Traits\ContainerAwareTrait, - Routing\RouteCollector as RouteCollectorContract, - Routing\RouteStrategy as RouteStrategyContract -}; -use Viserio\Routing\RouteParser as ViserioRouteParser; +use Viserio\Contracts\Routing\Route as RouteContract; -class RouteCollection implements RouteStrategyContract, RouteCollectorContract, Countable, IteratorAggregate +class RouteCollection implements Countable, IteratorAggregate { use ContainerAwareTrait; @@ -51,289 +39,34 @@ class RouteCollection implements RouteStrategyContract, RouteCollectorContract, */ protected $groups = []; - /** - * @var \RapidRoute\RouteParser - */ - protected $parser = []; - - /** - * @var array - */ - protected $patternMatchers = [ - '/{(.+?):number}/' => '{$1:[0-9]+}', - '/{(.+?):word}/' => '{$1:[a-zA-Z]+}', - '/{(.+?):alphanum_dash}/' => '{$1:[a-zA-Z0-9-_]+}', - '/{(.+?):slug}/' => '{$1:[a-z0-9-]+}', - '/{(.+?):uuid}/' => '{$1:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}+}' - ]; - - /** - * Constructor. - * - * @param \RapidRoute\RouteParser $parser - * @param \Interop\Container\ContainerInterface $container - */ - public function __construct( - RouteParser $parser, - ContainerInterface $container - ) { - $this->parser = $parser; - $this->container = $container; - } - - /** - * Add a route to the collection. + /** + * Add a Route instance to the collection. * - * @param string|string[] $method - * @param string $route - * @param callable $handler - * @param int $strategy + * @param Viserio\Contracts\Routing\Route $route * - * @return \Viserio\Routing\RouteCollection + * @return Viserio\Contracts\Routing\Route */ - public function addRoute($method, $route, $handler, $strategy = self::REQUEST_RESPONSE_STRATEGY) + public function addRoute(RouteContract $route): RouteContract { - // are we running a single strategy for the collection? - $strategy = (isset($this->strategy)) ? $this->strategy : $strategy; - - // if the handler is an anonymous function, we need to store it for later use - // by the dispatcher, otherwise we just throw the handler string at FastRoute - if ($handler instanceof Closure || (is_object($handler) && is_callable($handler))) { - $callback = $handler; - $handler = uniqid('Viserio::route::', true); - - $this->routes[$handler]['callback'] = $callback; - } elseif (is_object($handler)) { - throw new RuntimeException('Object controllers must be callable.'); - } - - $this->routes[$handler]['strategy'] = $strategy; - - $route = $this->parseRouteString($route); + $this->addToCollections($route); - //Check for a route alias starting with @ - $matches = []; - - if (preg_match(ViserioRouteParser::ALIAS_REGEX, $route, $matches)) { - $route = preg_replace(ViserioRouteParser::ALIAS_REGEX, '', $route); - $this->namedRoutes[$matches[0]] = $route; - - $handler = [ - 'name' => $matches[0], - 'handler' => $handler, - ]; - } - - parent::addRoute($method, $route, $handler); - - return $this; + return $route; } /** - * Builds a dispatcher based on the routes attached to this collection. + * Add the given route to the arrays of routes. * - * @return \Viserio\Routing\Dispatcher + * @param Viserio\Contracts\Routing\Route $route */ - public function getDispatcher() + protected function addToCollections(RouteContract $route) { - $dispatcher = new Dispatcher($this->container, $this->routes, $this->getData()); + $domainAndUri = $route->domain() . $route->getUri(); - if ($this->strategy !== null) { - $dispatcher->setStrategy($this->strategy); + foreach ($route->methods() as $method) { + $this->routes[$method][$domainAndUri] = $route; } - return $dispatcher; - } - - /** - * Map a handler to the given methods and route. - * - * @param string|array $route The route to match against - * @param string|callable $handler The handler for the route - * @param string|string[] $methods The HTTP methods for this handler - * @param int $strategy - */ - public function map($route, $handler, $methods = 'GET', $strategy = self::REQUEST_RESPONSE_STRATEGY) - { - $this->addRoute($methods, $route, $handler, $strategy); - } - - /** - * Add a route that responds to GET HTTP method. - * - * @param string $route - * @param string|\Closure $handler - * @param int $strategy - * - * @return \Viserio\Routing\RouteCollection - */ - public function get($route, $handler, $strategy = self::REQUEST_RESPONSE_STRATEGY) - { - return $this->addRoute(['GET', 'HEAD'], $route, $handler, $strategy); - } - - /** - * Add a route that responds to POST HTTP method. - * - * @param string $route - * @param string|\Closure $handler - * @param int $strategy - * - * @return \Viserio\Routing\RouteCollection - */ - public function post($route, $handler, $strategy = self::REQUEST_RESPONSE_STRATEGY) - { - return $this->addRoute('POST', $route, $handler, $strategy); - } - - /** - * Add a route that responds to PUT HTTP method. - * - * @param string $route - * @param string|\Closure $handler - * @param int $strategy - * - * @return \Viserio\Routing\RouteCollection - */ - public function put($route, $handler, $strategy = self::REQUEST_RESPONSE_STRATEGY) - { - return $this->addRoute('PUT', $route, $handler, $strategy); - } - - /** - * Add a route that responds to PATCH HTTP method. - * - * @param string $route - * @param string|\Closure $handler - * @param int $strategy - * - * @return \Viserio\Routing\RouteCollection - */ - public function patch($route, $handler, $strategy = self::REQUEST_RESPONSE_STRATEGY) - { - return $this->addRoute('PATCH', $route, $handler, $strategy); - } - - /** - * Add a route that responds to DELETE HTTP method. - * - * @param string $route - * @param string|\Closure $handler - * @param int $strategy - * - * @return \Viserio\Routing\RouteCollection - */ - public function delete($route, $handler, $strategy = self::REQUEST_RESPONSE_STRATEGY) - { - return $this->addRoute('DELETE', $route, $handler, $strategy); - } - - /** - * Add a route that responds to HEAD HTTP method. - * - * @param string $route - * @param string|\Closure $handler - * @param int $strategy - * - * @return \Viserio\Routing\RouteCollection - */ - public function head($route, $handler, $strategy = self::REQUEST_RESPONSE_STRATEGY) - { - return $this->addRoute('HEAD', $route, $handler, $strategy); - } - - /** - * Add a route that responds to OPTIONS HTTP method. - * - * @param string $route - * @param string|\Closure $handler - * @param int $strategy - * - * @return \Viserio\Routing\RouteCollection - */ - public function options($route, $handler, $strategy = self::REQUEST_RESPONSE_STRATEGY) - { - return $this->addRoute('OPTIONS', $route, $handler, $strategy); - } - - /** - * Add a route that responds to ANY HTTP method. - * - * @param string $route - * @param string|\Closure $handler - * @param int $strategy - * - * @return \Viserio\Routing\RouteCollection - */ - public function any($route, $handler, $strategy = self::REQUEST_RESPONSE_STRATEGY) - { - return $this->addRoute('ANY', $route, $handler, $strategy); - } - - /** - * Add a "before" event listener. - * - * @param string $name - * @param callable $handler - * @param int $priority - */ - public function onBefore($name, $handler, $priority = 0) - { - $this->addEventListener($name, $handler, 'before', $priority); - } - - /** - * Add an "after" event listener. - * - * @param string $name - * @param callable $handler - * @param int $priority - */ - public function onAfter($name, $handler, $priority = 0) - { - $this->addEventListener($name, $handler, 'after', $priority); - } - - /** - * Add a global "before" event listener. - * - * @param callable $handler - * @param int $priority - */ - public function globalOnBefore($handler, $priority = 0) - { - $this->addEventListener(null, $handler, 'before', $priority); - } - - /** - * Add a global "after" event listener. - * - * @param callable $handler - * @param int $priority - */ - public function globalOnAfter($handler, $priority = 0) - { - $this->addEventListener(null, $handler, 'after', $priority); - } - - /** - * Redirect instance. - * - * @return \Viserio\Routing\Redirect - */ - public function redirect(): Redirect - { - return new Redirect($this); - } - - /** - * Returns the array of registered named routes (starting with @). - * - * @return array - */ - public function getNamedRoutes() - { - return $this->namedRoutes; + $this->allRoutes[$method.$domainAndUri] = $route; } /** @@ -341,7 +74,7 @@ public function getNamedRoutes() * * @return array */ - public function getRoutes() + public function getRoutes(): array { return array_values($this->allRoutes); } @@ -365,57 +98,4 @@ public function count(): int { return count($this->getRoutes()); } - - /** - * @param string|null $name - * @param callable $handler - * @param string $when - * @param int $priority - */ - protected function addEventListener($name, $handler, $when, $priority) - { - if ($name) { - if (array_key_exists($name, $this->filters)) { - throw new LogicException(sprintf('Filter with name %s already defined', $name)); - } - - $this->filters[$name] = $name; - } - - $name = $name ? sprintf('route%s%s', $when, $name) : sprintf('route%s', $when); - - $this->container['events']->addListener($name, $handler, $priority); - } - - /** - * Get filter. - * - * @param string $name - */ - protected function getFilter($name) - { - if (! array_key_exists($name, $this->filters)) { - throw new InvalidArgumentException(sprintf('Filter with name %s is not defined', $name)); - } - - return $this->filters[$name]; - } - - /** - * Convenience method to convert pre-defined key words in to regex strings. - * - * @param string $route - * - * @return string - */ - protected function parseRouteString($route) - { - $wildcards = [ - '/{(.+?):number}/' => '{$1:[0-9]+}', - '/{(.+?):word}/' => '{$1:[a-zA-Z]+}', - '/{(.+?):alphanum_dash}/' => '{$1:[a-zA-Z0-9-_]+}', - ]; - - return preg_replace(array_keys($wildcards), array_values($wildcards), $route); - } } diff --git a/src/Viserio/Routing/Router.php b/src/Viserio/Routing/Router.php index a4bdb9db7..f4f83a43a 100644 --- a/src/Viserio/Routing/Router.php +++ b/src/Viserio/Routing/Router.php @@ -2,7 +2,205 @@ declare(strict_types=1); namespace Viserio\Routing; +use Interop\Container\ContainerInterface; +use Viserio\Contracts\{ + Container\Traits\ContainerAwareTrait, + Events\Traits\EventsAwareTrait +}; + class Router { + use ContainerAwareTrait; + use EventsAwareTrait; + + /** + * The route collection instance. + * + * @var \Viserio\Routing\RouteCollection + */ + protected $routes; + + /** + * Create a new Router instance. + * + * @param \Interop\Container\ContainerInterface $container + */ + public function __construct(ContainerInterface $container) + { + $this->routes = new RouteCollection; + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function get(string $uri, $action = null): RouteContract + { + return $this->addRoute(['GET', 'HEAD'], $uri, $action); + } + + /** + * {@inheritdoc} + */ + public function post(string $uri, $action = null): RouteContract + { + return $this->addRoute('POST', $uri, $action); + } + + /** + * {@inheritdoc} + */ + public function put(string $uri, $action = null): RouteContract + { + return $this->addRoute('PUT', $uri, $action); + } + + /** + * {@inheritdoc} + */ + public function patch(string $uri, $action = null): RouteContract + { + return $this->addRoute('PATCH', $uri, $action); + } + + /** + * {@inheritdoc} + */ + public function delete(string $uri, $action = null): RouteContract + { + return $this->addRoute('DELETE', $uri, $action); + } + + /** + * {@inheritdoc} + */ + public function options(string $uri, $action = null): RouteContract + { + return $this->addRoute('OPTIONS', $uri, $action); + } + + /** + * {@inheritdoc} + */ + public function any(string $uri, $action = null): RouteContract + { + $verbs = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE']; + + return $this->addRoute($verbs, $uri, $action); + } + + /** + * {@inheritdoc} + */ + public function match($methods, $uri, $action = null): RouteContract + { + return $this->addRoute(array_map('strtoupper', (array) $methods), $uri, $action); + } + + /** + * {@inheritdoc} + */ + public function getGroup(): RouteGroupContract + { + return $this->group; + } + + /** + * {@inheritdoc} + */ + public function group(array $attributes, Closure $callback): RouterContract + { + $this->group = $group; + + return $this; + } + + /** + * Add a route to the underlying route collection. + * + * @param array|string $methods + * @param string $uri + * @param \Closure|array|string|null $action + * + * @return \Viserio\Contracts\Routing\Route + */ + protected function addRoute($methods, string $uri, $action): RouteContract + { + return $this->routes->addRoute($this->createRoute($methods, $uri, $action)); + } + + /** + * Create a new route instance. + * + * @param array|string $methods + * @param string $uri + * @param mixed $action + * + * @return \Viserio\Contracts\Routing\Route + */ + protected function createRoute($methods, string $uri, $action): RouteContract + { + list($patternString, $conditions) = $this->parseRoutingPattern($pattern); + + $pattern = $this->parser->parse( + $patternString, + $conditions + $this->globalParameterConditions + ); + + $route = $this->newRoute( + $methods, + $this->prefix($uri), + $action + ); + + foreach ($pattern as $key => $value) { + $route->setParameter($key, $value); + } + + return $route; + } + + /** + * Create a new Route object. + * + * @param array|string $methods + * @param string $uri + * @param mixed $action + * + * @return \Viserio\Contracts\Routing\Route + */ + protected function newRoute($methods, string $uri, $action): Route + { + return (new Route($methods, $uri, $action)) + ->setRouter($this) + ->setContainer($this->container); + } + + protected function parseRoutingPattern($pattern) + { + if(is_string($pattern)) { + return [$pattern, []]; + } + + if(is_array($pattern)) { + if(!isset($pattern[0]) || !is_string($pattern[0])) { + throw new InvalidRoutePatternException(sprintf( + 'Cannot add route: route pattern array must have the first element containing the pattern string, %s given', + isset($pattern[0]) ? gettype($pattern[0]) : 'none' + )); + } + + $patternString = $pattern[0]; + $parameterConditions = $pattern; + + unset($parameterConditions[0]); + + return [$patternString, $parameterConditions]; + } + throw new InvalidRoutePatternException(sprintf( + 'Cannot add route: route pattern must be a pattern string or array, %s given', + gettype($pattern) + )); + } } diff --git a/src/Viserio/Routing/Tests/RouteTest.php b/src/Viserio/Routing/Tests/RouteTest.php new file mode 100644 index 000000000..2d4493038 --- /dev/null +++ b/src/Viserio/Routing/Tests/RouteTest.php @@ -0,0 +1,7 @@ + Date: Tue, 9 Aug 2016 00:19:24 +0200 Subject: [PATCH 08/39] remove interface --- src/Viserio/Routing/Dispatcher.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Viserio/Routing/Dispatcher.php b/src/Viserio/Routing/Dispatcher.php index 11b07b945..126d1eed3 100644 --- a/src/Viserio/Routing/Dispatcher.php +++ b/src/Viserio/Routing/Dispatcher.php @@ -9,11 +9,10 @@ use RuntimeException; use Symfony\Component\HttpKernel\Exception\HttpException; use Viserio\Contracts\Http\Response as ResponseContract; -use Viserio\Contracts\Routing\RouteStrategy as RouteStrategyContract; use Viserio\Http\JsonResponse; use Viserio\Http\Response; -class Dispatcher implements RouteStrategyContract +class Dispatcher { /** * Container instance. From dc162440b761f442e4bad2af2b73034d7df19c0d Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Tue, 9 Aug 2016 00:24:42 +0200 Subject: [PATCH 09/39] added Container\Traits\ContainerAwareTrait, --- src/Viserio/Routing/RouteCollection.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Viserio/Routing/RouteCollection.php b/src/Viserio/Routing/RouteCollection.php index 1d242a5d7..9a2439f67 100644 --- a/src/Viserio/Routing/RouteCollection.php +++ b/src/Viserio/Routing/RouteCollection.php @@ -4,7 +4,10 @@ use ArrayIterator; use Countable; -use Viserio\Contracts\Routing\Route as RouteContract; +use Viserio\Contracts\{ + Container\Traits\ContainerAwareTrait, + Routing\Route as RouteContract +}; class RouteCollection implements Countable, IteratorAggregate { From ac762be3428008c0e75b20a9c5b24ff17eb8d227 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Tue, 9 Aug 2016 19:57:28 +0200 Subject: [PATCH 10/39] fix --- src/Viserio/Routing/RouteCollection.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Viserio/Routing/RouteCollection.php b/src/Viserio/Routing/RouteCollection.php index 9a2439f67..c6cd17681 100644 --- a/src/Viserio/Routing/RouteCollection.php +++ b/src/Viserio/Routing/RouteCollection.php @@ -4,6 +4,7 @@ use ArrayIterator; use Countable; +use IteratorAggregate; use Viserio\Contracts\{ Container\Traits\ContainerAwareTrait, Routing\Route as RouteContract From 91d2421a393bb3790ce7fc716d77461d5d6b6098 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Tue, 9 Aug 2016 23:22:32 +0200 Subject: [PATCH 11/39] Added some tests --- src/Viserio/Contracts/Routing/Route.php | 2 +- src/Viserio/Routing/Route.php | 10 +-- .../Routing/Tests/Fixture/Controller.php | 13 +++ src/Viserio/Routing/Tests/RouteTest.php | 79 +++++++++++++++++++ src/Viserio/Routing/composer.json | 3 +- 5 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 src/Viserio/Routing/Tests/Fixture/Controller.php diff --git a/src/Viserio/Contracts/Routing/Route.php b/src/Viserio/Contracts/Routing/Route.php index b249172b5..e682531d8 100644 --- a/src/Viserio/Contracts/Routing/Route.php +++ b/src/Viserio/Contracts/Routing/Route.php @@ -94,7 +94,7 @@ public function setAction(array $action): Route; * * @return $this */ - public function prefix(string $prefix): Route; + public function addPrefix(string $prefix): Route; /** * Get the prefix of the route instance. diff --git a/src/Viserio/Routing/Route.php b/src/Viserio/Routing/Route.php index b4d532ce8..ee2318d8e 100644 --- a/src/Viserio/Routing/Route.php +++ b/src/Viserio/Routing/Route.php @@ -100,15 +100,15 @@ class Route implements RouteContract public function __construct($methods, $uri, $action) { $this->uri = $uri; - $this->methods = (array) $methods; + $this->httpMethods = (array) $methods; $this->action = $this->parseAction($action); - if (in_array('GET', $this->methods) && ! in_array('HEAD', $this->methods)) { - $this->methods[] = 'HEAD'; + if (in_array('GET', $this->httpMethods) && ! in_array('HEAD', $this->httpMethods)) { + $this->httpMethods[] = 'HEAD'; } if (isset($this->action['prefix'])) { - $this->prefix($this->action['prefix']); + $this->addPrefix($this->action['prefix']); } } @@ -209,7 +209,7 @@ public function setAction(array $action): RouteContract /** * {@inheritdoc} */ - public function prefix(string $prefix): RouteContract + public function addPrefix(string $prefix): RouteContract { $uri = rtrim($prefix, '/').'/'.ltrim($this->uri, '/'); diff --git a/src/Viserio/Routing/Tests/Fixture/Controller.php b/src/Viserio/Routing/Tests/Fixture/Controller.php new file mode 100644 index 000000000..e7087feb5 --- /dev/null +++ b/src/Viserio/Routing/Tests/Fixture/Controller.php @@ -0,0 +1,13 @@ + Controller::class.'::string']); + + $this->assertSame(['GET', 'HEAD'], $route->getMethods()); + + $route = new Route('PUT', 'test', ['uses' => Controller::class.'::string']); + + $this->assertSame(['PUT'], $route->getMethods()); + + $route = new Route(['GET', 'POST'], 'test', ['uses' => Controller::class.'::string']); + + $this->assertSame(['GET', 'POST', 'HEAD'], $route->getMethods()); + } + + public function testGetDomain() + { + $route = new Route('GET', 'test', ['domain' => 'test.com']); + + $this->assertSame('test.com', $route->getDomain()); + } + + public function testGetAndSetUri() + { + $route = new Route('GET', 'test', ['domain' => 'test.com']); + + $this->assertSame('test', $route->getUri()); + + $route->setUri('/foo/bar'); + + $this->assertSame('/foo/bar', $route->getUri()); + } + + public function testGetAndSetName() + { + $route = new Route('GET', 'test', ['as' => 'test']); + + $this->assertSame('test', $route->getName()); + + $route->setName('foo'); + + $this->assertSame('testfoo', $route->getName()); + + $route = new Route('GET', 'test', null); + $route->setName('test'); + + $this->assertSame('test', $route->getName()); + } + + public function testHttpAndHttps() + { + $route = new Route('GET', 'test', ['http']); + + $this->assertTrue($route->httpOnly()); + + $route = new Route('GET', 'test', ['https']); + + $this->assertTrue($route->httpsOnly()); + } + + public function testSetAndGetPrefix() + { + $route = new Route('GET', 'test', ['prefix' => 'test']); + + $this->assertSame('test', $route->getPrefix()); + $this->assertSame('test/test', $route->getUri()); + + $route = new Route('GET', 'test', null); + $route->addPrefix('foo'); + + $this->assertSame('foo/test', $route->getUri()); + + $route->addPrefix('test'); + + $this->assertSame('test/foo/test', $route->getUri()); + } } diff --git a/src/Viserio/Routing/composer.json b/src/Viserio/Routing/composer.json index ddba53661..7fd62e585 100644 --- a/src/Viserio/Routing/composer.json +++ b/src/Viserio/Routing/composer.json @@ -32,7 +32,8 @@ "require-dev": { "narrowspark/php-cs-fixer-config" : "^1.1", "narrowspark/testing-helper" : "^1.5", - "phpunit/phpunit" : "^5.1" + "phpunit/phpunit" : "^5.1", + "viserio/http" : "self.version" }, "autoload": { "psr-4": { From cd173e39e28f405e5e9c2f47377488f1e305351e Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Wed, 10 Aug 2016 00:38:27 +0200 Subject: [PATCH 12/39] remove rapid route --- composer.json | 3 +- src/Viserio/Routing/Dispatcher.php | 331 +++---------------- src/Viserio/Routing/Route.php | 11 +- src/Viserio/Routing/RouteCollection.php | 42 ++- src/Viserio/Routing/Router.php | 44 ++- src/Viserio/Routing/Tests/DispatcherTest.php | 19 +- src/Viserio/Routing/Tests/RouteTest.php | 28 +- src/Viserio/Routing/composer.json | 1 - 8 files changed, 150 insertions(+), 329 deletions(-) diff --git a/composer.json b/composer.json index 7e1c83ab9..1a6aa5573 100644 --- a/composer.json +++ b/composer.json @@ -64,8 +64,7 @@ "symfony/polyfill-intl-icu" : "^1.0", "symfony/polyfill-mbstring" : "^1.0", "symfony/var-dumper" : "^3.1", - "swiftmailer/swiftmailer" : "^5.4", - "timetoogo/rapid-route" : "^2.0" + "swiftmailer/swiftmailer" : "^5.4" }, "replace": { "viserio/cache" : "self.version", diff --git a/src/Viserio/Routing/Dispatcher.php b/src/Viserio/Routing/Dispatcher.php index 126d1eed3..9540c4102 100644 --- a/src/Viserio/Routing/Dispatcher.php +++ b/src/Viserio/Routing/Dispatcher.php @@ -2,334 +2,81 @@ declare(strict_types=1); namespace Viserio\Routing; -use Closure; -use Exception; -use RapidRoute\MatchResult; -use Interop\Container\ContainerInterface as ContainerContract; -use RuntimeException; -use Symfony\Component\HttpKernel\Exception\HttpException; -use Viserio\Contracts\Http\Response as ResponseContract; -use Viserio\Http\JsonResponse; -use Viserio\Http\Response; +use RapidRoute\{ + MatchResult, + InvalidRouteDataException, + Compilation\TreeBasedRouterCompiler +}; +use Psr\Http\Message\ServerRequestInterface; class Dispatcher { /** - * Container instance. + * The route collection instance. * - * @var \Interop\Container\ContainerInterface + * @var \Viserio\Routing\RouteCollection */ - protected $container; + protected $routes; /** - * All routes. + * Create a new dispatcher instance. * - * @var array + * @param \Viserio\Routing\RouteCollection $routes */ - protected $routes = []; - - /** - * [$cache description]. - * - * @var bool - */ - protected $cache; - - /** - * Cache folder path for cached routes. - * - * @var string - */ - protected $cachePath; - - /** - * Cached routes. - * - * @var array - */ - protected $cahcedRoutes = []; - - /** - * Constructor. - * - * @param ContainerContract $container - * @param array $routes - * @param array $data - */ - public function __construct(ContainerContract $container, array $routes, array $data) + public function __construct(RouteCollection $routes) { - $this->container = $container; - $this->routes = $routes; - - parent::__construct($data); + $this->routes = new RouteCollection; } /** - * Match and dispatch a route matching the given http method and uri. + * Match and dispatch a route matching the given http method and + * uri, retruning an execution chain. * - * @param string $method - * @param string $uri + * @param \Psr\Http\Message\ServerRequestInterface $request * - * @return ResponseContract + * @return mixed */ - public function dispatch($method, $uri) + public function handle(ServerRequestInterface $request) { + $match = $this->match( + $request->getMethod(), + $request->getUri()->getPath() + ); + switch ($match[0]) { case MatchResult::NOT_FOUND: - return $this->handleNotFound(); - - case MatchResult::HTTP_METHOD_NOT_ALLOWED: - $allowed = (array) $match[1]; - - return $this->handleNotAllowed($allowed); - - case MatchResult::FOUND: - default: - $handler = (isset($this->routes[$match[1]]['callback'])) ? - $this->routes[$match[1]]['callback'] : - $match[1]; - - $strategy = $this->routes[$match[1]]['strategy']; - $vars = (array) $match[2]; - - return $this->handleFound($handler, $strategy, $vars); - } - } - - /** - * Invoke a controller action. - * - * @param ResponseContract $controller - * @param array $vars - * - * @return ResponseContract - */ - public function invokeController($controller, array $vars = []) - { - if (is_array($controller)) { - $controller = [ - $this->container[$controller[0]], - $controller[1], - ]; - } - - return call_user_func_array($controller, array_values($vars)); - } - - /** - * Handle dispatching of a found route. - * - * @param string|\Closure $handler - * @param int|\Viserio\Contracts\Routing\CustomStrategy $strategy - * @param array $vars - * - * @throws \RuntimeException - * - * @return ResponseContract - */ - protected function handleFound($handler, $strategy, array $vars = []) - { - if ($this->getStrategy() === null) { - $this->setStrategy($strategy); - } - - $controller = $this->isController($handler); - - // handle getting of response based on strategy - if (is_int($strategy)) { - return $this->getResponseOnStrategy($controller, $strategy, $vars); - } - - $traits = class_uses($strategy, true); - - // dispatch via strategy - if (isset($traits['Viserio\Container\ContainerAwareTrait'])) { - $strategy->setContainer($this->container); - } - - // we must be using a custom strategy - return $strategy->dispatch($controller, $vars); - } - - /** - * Check if handler is a controller. - * - * @param string|\Closure $handler - * - * @throws \RuntimeException - * - * @return \Closure|string|array - */ - protected function isController($handler) - { - $controller = null; - - // figure out what the controller is - if (($handler instanceof Closure) || is_callable($handler)) { - $controller = $handler; - } - - if (is_string($handler) && strpos($handler, '::') !== false) { - $controller = explode('::', $handler); - } - - // if controller method wasn't specified, throw exception. - if (! $controller) { - throw new RuntimeException('A class method must be provided as a controller. ClassName::methodName'); - } - - return $controller; - } - - /** - * Handle getting of response based on strategy. - * - * @param \Viserio\Contracts\Http\Response $controller - * @param int $strategy - * @param array $vars - * - * @return ResponseContract - */ - protected function getResponseOnStrategy($controller, $strategy, $vars) - { - switch ($strategy) { - case RouteStrategyContract::URI_STRATEGY: - $response = $this->handleUriStrategy($controller, $vars); + // 404 Not Found... break; - case RouteStrategyContract::RESTFUL_STRATEGY: - $response = $this->handleRestfulStrategy($controller, $vars); + case MatchResult::HTTP_METHOD_NOT_ALLOWED: + // 405 Method Not Allowed... break; - case RouteStrategyContract::REQUEST_RESPONSE_STRATEGY: - default: - $response = $this->handleRequestResponseStrategy($controller, $vars); + case MatchResult::FOUND: + // Matched route, dispatch to associated handler... break; } - - return $response; } /** - * Handles response to Request -> Response Strategy based routes. - * - * @param ResponseContract $controller - * @param array $vars + * Get Route for given method and uri. * - * @return ResponseContract - */ - protected function handleRequestResponseStrategy($controller, array $vars = []) - { - $response = $this->invokeController($controller, [ - $this->container->get('request'), - $this->container->get('response'), - $vars, - ]); - - if ($response instanceof ResponseContract) { - return $response; - } - - throw new RuntimeException( - 'When using the Request -> Response Strategy your controller must return an instance of [Viserio\Contracts\Http\Response]' - ); - } - - /** - * Handles response to Restful Strategy based routes. - * - * @param ResponseContract $controller - * @param array $vars - * - * @return JsonResponse - */ - protected function handleRestfulStrategy($controller, array $vars = []) - { - try { - $response = $this->invokeController($controller, [ - $this->container['request'], - $vars, - ]); - - if ($response instanceof JsonResponse) { - return $response; - } - - if (is_array($response) || $response instanceof \ArrayObject) { - return new JsonResponse($response); - } - - throw new RuntimeException( - 'Your controller action must return a valid response for the Restful Strategy Acceptable responses are of type: [Array], [ArrayObject] and [Viserio\Http\JsonResponse]' - ); - } catch (HttpException $exception) { - $body = [ - 'status_code' => $exception->getStatusCode(), - 'message' => $exception->getMessage(), - ]; - - return new JsonResponse($body, $exception->getStatusCode(), $exception->getHeaders()); - } - } - - /** - * Handles response to URI Strategy based routes. - * - * @param ResponseContract $controller - * @param array $vars - * - * @return ResponseContract - */ - protected function handleUriStrategy($controller, array $vars) - { - $response = $this->invokeController($controller, $vars); - - if ($response instanceof ResponseContract) { - return $response; - } - - try { - $response = new Response($response); - } catch (Exception $exception) { - throw new RuntimeException('Unable to build Response from controller return value', 0, $exception); - } - - return $response; - } - - /** - * Handle a not found route. + * @param string $httpMethod + * @param string $uri * - * @throws HttpException\NotFoundException + * @return \RapidRoute\MatchResult * - * @return JsonResponse + * @throws \RapidRoute\InvalidRouteDataException */ - protected function handleNotFound() + public function match(string $httpMethod, string $uri) { - $exception = new HttpException\NotFoundException(); - - if ($this->getStrategy() === RouteStrategyContract::RESTFUL_STRATEGY) { - return $exception->getJsonResponse(); - } + $compiledRouter = $this->generate(); - throw $exception; + return MatchResult::fromArray($compiledRouter($httpMethod, $uri)); } - /** - * Handles a not allowed route. - * - * @param array $allowed - * - * @throws HttpException\MethodNotAllowedException - * - * @return JsonResponse - */ - protected function handleNotAllowed(array $allowed) + protected function generate() { - $exception = new HttpException\MethodNotAllowedException($allowed); - - if ($this->getStrategy() === RouteStrategyContract::RESTFUL_STRATEGY) { - return $exception->getJsonResponse(); - } + $routerCompiler = new TreeBasedRouterCompiler(); - throw $exception; + return $routerCompiler->compileRouter($this->routes); } } diff --git a/src/Viserio/Routing/Route.php b/src/Viserio/Routing/Route.php index ee2318d8e..a873091d7 100644 --- a/src/Viserio/Routing/Route.php +++ b/src/Viserio/Routing/Route.php @@ -100,7 +100,8 @@ class Route implements RouteContract public function __construct($methods, $uri, $action) { $this->uri = $uri; - $this->httpMethods = (array) $methods; + // According to RFC methods are defined in uppercase (See RFC 7231) + $this->httpMethods = array_map("strtoupper",(array) $methods); $this->action = $this->parseAction($action); if (in_array('GET', $this->httpMethods) && ! in_array('HEAD', $this->httpMethods)) { @@ -231,8 +232,6 @@ public function getPrefix(): string */ public function setParameter($name, $value): RouteContract { - $this->parameters(); - $this->parameters[$name] = $value; return $this; @@ -243,7 +242,7 @@ public function setParameter($name, $value): RouteContract */ public function getParameter(string $name, $default = null) { - return Arr::get($this->parameters(), $name, $default); + return Arr::get($this->getParameters(), $name, $default); } /** @@ -251,7 +250,7 @@ public function getParameter(string $name, $default = null) */ public function hasParameter(string $name): bool { - return Arr::has($this->parameters(), $name); + return Arr::has($this->getParameters(), $name); } /** @@ -279,7 +278,7 @@ public function hasParameters(): bool */ public function forgetParameter(string $name) { - $this->parameters(); + $this->getParameters(); unset($this->parameters[$name]); } diff --git a/src/Viserio/Routing/RouteCollection.php b/src/Viserio/Routing/RouteCollection.php index c6cd17681..8f6809612 100644 --- a/src/Viserio/Routing/RouteCollection.php +++ b/src/Viserio/Routing/RouteCollection.php @@ -43,7 +43,7 @@ class RouteCollection implements Countable, IteratorAggregate */ protected $groups = []; - /** + /** * Add a Route instance to the collection. * * @param Viserio\Contracts\Routing\Route $route @@ -57,22 +57,6 @@ public function addRoute(RouteContract $route): RouteContract return $route; } - /** - * Add the given route to the arrays of routes. - * - * @param Viserio\Contracts\Routing\Route $route - */ - protected function addToCollections(RouteContract $route) - { - $domainAndUri = $route->domain() . $route->getUri(); - - foreach ($route->methods() as $method) { - $this->routes[$method][$domainAndUri] = $route; - } - - $this->allRoutes[$method.$domainAndUri] = $route; - } - /** * Get all of the routes in the collection. * @@ -102,4 +86,28 @@ public function count(): int { return count($this->getRoutes()); } + + /** + * @return Route[] + */ + public function asArray(): array + { + return array_values($this->allRoutes); + } + + /** + * Add the given route to the arrays of routes. + * + * @param Viserio\Contracts\Routing\Route $route + */ + protected function addToCollections(RouteContract $route) + { + $domainAndUri = $route->getDomain() . $route->getUri(); + + foreach ($route->getMethods() as $method) { + $this->routes[$method][$domainAndUri] = $route; + } + + $this->allRoutes[$method.$domainAndUri] = $route; + } } diff --git a/src/Viserio/Routing/Router.php b/src/Viserio/Routing/Router.php index f4f83a43a..ee0473c73 100644 --- a/src/Viserio/Routing/Router.php +++ b/src/Viserio/Routing/Router.php @@ -2,13 +2,22 @@ declare(strict_types=1); namespace Viserio\Routing; +use Closure; use Interop\Container\ContainerInterface; +use Psr\Http\Message\ServerRequestInterface; +use RapidRoute\{ + InvalidRoutePatternException, + RouteParser +}; use Viserio\Contracts\{ Container\Traits\ContainerAwareTrait, - Events\Traits\EventsAwareTrait + Events\Traits\EventsAwareTrait, + Routing\Route as RouteContract, + Routing\Router as RouterContract, + Routing\RouteGroup as RouteGroupContract }; -class Router +class Router implements RouterContract { use ContainerAwareTrait; use EventsAwareTrait; @@ -24,11 +33,13 @@ class Router * Create a new Router instance. * * @param \Interop\Container\ContainerInterface $container + * @param \RapidRoute\RouteParser $parser */ - public function __construct(ContainerInterface $container) + public function __construct(ContainerInterface $container, RouteParser $parser) { $this->routes = new RouteCollection; $this->container = $container; + $this->parser = $parser; } /** @@ -113,6 +124,18 @@ public function group(array $attributes, Closure $callback): RouterContract $this->group = $group; return $this; + } + + /** + * Dispatch router for HTTP request. + * + * @param \Psr\Http\Message\ServerRequestInterface $request The current HTTP request object + * + * @return array + */ + public function dispatch(ServerRequestInterface $request) + { + } /** @@ -140,11 +163,11 @@ protected function addRoute($methods, string $uri, $action): RouteContract */ protected function createRoute($methods, string $uri, $action): RouteContract { - list($patternString, $conditions) = $this->parseRoutingPattern($pattern); + list($patternString, $conditions) = $this->parseRoutingPattern($uri); $pattern = $this->parser->parse( $patternString, - $conditions + $this->globalParameterConditions + $conditions ); $route = $this->newRoute( @@ -203,4 +226,15 @@ protected function parseRoutingPattern($pattern) gettype($pattern) )); } + + /** + * Prefix the given URI with the last prefix. + * + * @param string $uri + * @return string + */ + protected function prefix($uri) + { + return trim('/'.trim($uri, '/'), '/') ?: '/'; + } } diff --git a/src/Viserio/Routing/Tests/DispatcherTest.php b/src/Viserio/Routing/Tests/DispatcherTest.php index 990ee5bde..f1bd8a2cb 100644 --- a/src/Viserio/Routing/Tests/DispatcherTest.php +++ b/src/Viserio/Routing/Tests/DispatcherTest.php @@ -2,12 +2,23 @@ declare(strict_types=1); namespace Viserio\Routing\Tests; -use FastRoute\DataGenerator\GroupCountBased; -use Viserio\Container\Container; -use Viserio\Routing\RouteCollection; -use Viserio\Routing\RouteParser; +use Viserio\Routing\{ + Dispatcher, + Route, + RouteCollection +}; class DispatcherTest extends \PHPUnit_Framework_TestCase { + public function testMatch() + { + $route = new Route('GET', 'test', null); + $collection = new RouteCollection(); + $collection->addRoute($route); + + $dispatcher = new Dispatcher($collection); + + var_dump($dispatcher->match('GET', 'test')); + } } diff --git a/src/Viserio/Routing/Tests/RouteTest.php b/src/Viserio/Routing/Tests/RouteTest.php index 7bb7380f0..bdd39076d 100644 --- a/src/Viserio/Routing/Tests/RouteTest.php +++ b/src/Viserio/Routing/Tests/RouteTest.php @@ -2,11 +2,30 @@ declare(strict_types=1); namespace Viserio\Routing\Tests; -use Viserio\Routing\Route; -use Viserio\Routing\Tests\Fixture\Controller; +use Narrowspark\TestingHelper\Traits\MockeryTrait; +use Interop\Container\ContainerInterface; +use RapidRoute\RouteParser; +use Viserio\Routing\{ + Route, + Router, + Tests\Fixture\Controller +}; class RouteTest extends \PHPUnit_Framework_TestCase { + use MockeryTrait; + + public function testBasicDispatchingOfRoutes() + { + $router = $this->getRouter(); + $router->get('/hello/{name}', function (Request $request, Response $response) { + $name = $request->getAttribute('name'); + $response->getBody()->write("Hello, $name"); + + return $response; + }); + } + public function testGetMethods() { $route = new Route('GET', 'test', ['uses' => Controller::class.'::string']); @@ -83,4 +102,9 @@ public function testSetAndGetPrefix() $this->assertSame('test/foo/test', $route->getUri()); } + + protected function getRouter() + { + return new Router($this->mock(ContainerInterface::class), new RouteParser()); + } } diff --git a/src/Viserio/Routing/composer.json b/src/Viserio/Routing/composer.json index 7fd62e585..001c1d47a 100644 --- a/src/Viserio/Routing/composer.json +++ b/src/Viserio/Routing/composer.json @@ -24,7 +24,6 @@ "narrowspark/arr" : "^1.1", "php-di/invoker" : "^1.3", "psr/http-message" : "^1.0", - "timetoogo/rapid-route" : "^2.0", "viserio/contracts" : "self.version", "viserio/middleware" : "self.version", "viserio/support" : "self.version" From df45b96b88faa7ab271f556dd42b47bbfbb244c8 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Thu, 11 Aug 2016 00:15:03 +0200 Subject: [PATCH 13/39] More work on routing --- .../Contracts/Routing/CustomStrategy.php | 24 -------- .../Exceptions/InvalidRouteDataException.php | 18 ++++++ .../InvalidRoutePatternException.php | 9 +++ .../Exceptions/UrlGenerationException.php | 6 +- src/Viserio/Contracts/Routing/RouteParser.php | 24 ++++++++ .../Contracts/Routing/RouteSegment.php | 15 +++++ .../Contracts/Routing/SegmentMatcher.php | 7 +++ src/Viserio/Routing/Dispatcher.php | 10 ---- src/Viserio/Routing/Pattern.php | 47 +++++++++++++++ src/Viserio/Routing/Route.php | 23 +------- src/Viserio/Routing/RouteCollection.php | 35 +---------- src/Viserio/Routing/RouteParser.php | 59 +++++++++++++++++++ src/Viserio/Routing/Router.php | 55 +++-------------- .../Routing/Segments/ParameterSegment.php | 8 +++ src/Viserio/Routing/Tests/DispatcherTest.php | 2 - src/Viserio/Routing/Tests/RouteTest.php | 2 +- src/Viserio/Routing/composer.json | 1 - 17 files changed, 205 insertions(+), 140 deletions(-) delete mode 100644 src/Viserio/Contracts/Routing/CustomStrategy.php create mode 100644 src/Viserio/Contracts/Routing/Exceptions/InvalidRouteDataException.php create mode 100644 src/Viserio/Contracts/Routing/Exceptions/InvalidRoutePatternException.php create mode 100644 src/Viserio/Contracts/Routing/RouteParser.php create mode 100644 src/Viserio/Contracts/Routing/RouteSegment.php create mode 100644 src/Viserio/Contracts/Routing/SegmentMatcher.php create mode 100644 src/Viserio/Routing/Pattern.php create mode 100644 src/Viserio/Routing/RouteParser.php create mode 100644 src/Viserio/Routing/Segments/ParameterSegment.php diff --git a/src/Viserio/Contracts/Routing/CustomStrategy.php b/src/Viserio/Contracts/Routing/CustomStrategy.php deleted file mode 100644 index eed7ec2a5..000000000 --- a/src/Viserio/Contracts/Routing/CustomStrategy.php +++ /dev/null @@ -1,24 +0,0 @@ - ClassName, 1 => MethodName]) - * - \Closure (controller is an anonymous function) - * - * @param string|array|\Closure $controller - * @param array $vars - named wildcard segments of the matched route - * - * @return mixed - */ - public function dispatch($controller, array $vars); -} diff --git a/src/Viserio/Contracts/Routing/Exceptions/InvalidRouteDataException.php b/src/Viserio/Contracts/Routing/Exceptions/InvalidRouteDataException.php new file mode 100644 index 000000000..0cc97844f --- /dev/null +++ b/src/Viserio/Contracts/Routing/Exceptions/InvalidRouteDataException.php @@ -0,0 +1,18 @@ +getName()}] [URI: {$route->getPath()}]."); + return new static(sprintf( + 'Missing required parameters for [Route: %s] [URI: %s].', + $route->getName(), + $route->getPath() + )); } } diff --git a/src/Viserio/Contracts/Routing/RouteParser.php b/src/Viserio/Contracts/Routing/RouteParser.php new file mode 100644 index 000000000..605b120b0 --- /dev/null +++ b/src/Viserio/Contracts/Routing/RouteParser.php @@ -0,0 +1,24 @@ + 'user' }, + * ParameterSegment{ $name => 'id', $match => '[0-9]+' }, + * StaticSegment{ $value => 'create' }, + * ] + * + * @param string $route + * + * @return \Viserio\Contracts\Routing\RouteSegment[] + * + * @throws \Viserio\Contracts\Routing\Exception\InvalidRoutePatternException + */ + public function parse(string $route): array; +} diff --git a/src/Viserio/Contracts/Routing/RouteSegment.php b/src/Viserio/Contracts/Routing/RouteSegment.php new file mode 100644 index 000000000..f85aa5e99 --- /dev/null +++ b/src/Viserio/Contracts/Routing/RouteSegment.php @@ -0,0 +1,15 @@ +generate(); - return MatchResult::fromArray($compiledRouter($httpMethod, $uri)); } protected function generate() { - $routerCompiler = new TreeBasedRouterCompiler(); - - return $routerCompiler->compileRouter($this->routes); } } diff --git a/src/Viserio/Routing/Pattern.php b/src/Viserio/Routing/Pattern.php new file mode 100644 index 000000000..2ec8b270c --- /dev/null +++ b/src/Viserio/Routing/Pattern.php @@ -0,0 +1,47 @@ +allRoutes); } - /** - * Get an iterator for the items. - * - * @return \ArrayIterator - */ - public function getIterator(): ArrayIterator - { - return new ArrayIterator($this->getRoutes()); - } - - /** - * Count the number of items in the collection. - * - * @return int - */ - public function count(): int - { - return count($this->getRoutes()); - } - - /** - * @return Route[] - */ - public function asArray(): array - { - return array_values($this->allRoutes); - } - /** * Add the given route to the arrays of routes. * diff --git a/src/Viserio/Routing/RouteParser.php b/src/Viserio/Routing/RouteParser.php new file mode 100644 index 000000000..7d4dbbd5f --- /dev/null +++ b/src/Viserio/Routing/RouteParser.php @@ -0,0 +1,59 @@ + 1 && $route[0] !== '/') { + throw new InvalidRoutePatternException(sprintf( + 'Invalid route pattern: non-root route must be prefixed with \'/\', \'%s\' given', + $route + )); + } + + list($patternString, $conditions) = $this->parseRoutingPattern($route); + + $segments = []; + + return $segments; + } + + protected function parseRoutingPattern($pattern) + { + if (is_string($pattern)) { + return [$pattern, []]; + } + + if (is_array($pattern)) { + if (!isset($pattern[0]) || !is_string($pattern[0])) { + throw new InvalidRoutePatternException(sprintf( + 'Cannot add route: route pattern array must have the first element containing the pattern string, %s given', + isset($pattern[0]) ? gettype($pattern[0]) : 'none' + )); + } + + $patternString = $pattern[0]; + $parameterConditions = $pattern; + + unset($parameterConditions[0]); + + return [$patternString, $parameterConditions]; + } + + throw new InvalidRoutePatternException(sprintf( + 'Cannot add route: route pattern must be a pattern string or array, %s given', + gettype($pattern) + )); + } +} diff --git a/src/Viserio/Routing/Router.php b/src/Viserio/Routing/Router.php index ee0473c73..7e77cfe2d 100644 --- a/src/Viserio/Routing/Router.php +++ b/src/Viserio/Routing/Router.php @@ -5,16 +5,13 @@ use Closure; use Interop\Container\ContainerInterface; use Psr\Http\Message\ServerRequestInterface; -use RapidRoute\{ - InvalidRoutePatternException, - RouteParser -}; use Viserio\Contracts\{ Container\Traits\ContainerAwareTrait, Events\Traits\EventsAwareTrait, Routing\Route as RouteContract, Routing\Router as RouterContract, - Routing\RouteGroup as RouteGroupContract + Routing\RouteGroup as RouteGroupContract, + Routing\RouteParser as RouteParserContract }; class Router implements RouterContract @@ -25,19 +22,18 @@ class Router implements RouterContract /** * The route collection instance. * - * @var \Viserio\Routing\RouteCollection + * @var array */ - protected $routes; + protected $routes = []; /** * Create a new Router instance. * - * @param \Interop\Container\ContainerInterface $container - * @param \RapidRoute\RouteParser $parser + * @param \Interop\Container\ContainerInterface $container + * @param \Viserio\Contracts\Routing\RouteParser $parser */ - public function __construct(ContainerInterface $container, RouteParser $parser) + public function __construct(ContainerInterface $container, RouteParserContract $parser) { - $this->routes = new RouteCollection; $this->container = $container; $this->parser = $parser; } @@ -149,7 +145,7 @@ public function dispatch(ServerRequestInterface $request) */ protected function addRoute($methods, string $uri, $action): RouteContract { - return $this->routes->addRoute($this->createRoute($methods, $uri, $action)); + return $this->routes[] = $this->createRoute($methods, $uri, $action); } /** @@ -163,12 +159,7 @@ protected function addRoute($methods, string $uri, $action): RouteContract */ protected function createRoute($methods, string $uri, $action): RouteContract { - list($patternString, $conditions) = $this->parseRoutingPattern($uri); - - $pattern = $this->parser->parse( - $patternString, - $conditions - ); + $pattern = $this->parser->parse($uri); $route = $this->newRoute( $methods, @@ -199,34 +190,6 @@ protected function newRoute($methods, string $uri, $action): Route ->setContainer($this->container); } - protected function parseRoutingPattern($pattern) - { - if(is_string($pattern)) { - return [$pattern, []]; - } - - if(is_array($pattern)) { - if(!isset($pattern[0]) || !is_string($pattern[0])) { - throw new InvalidRoutePatternException(sprintf( - 'Cannot add route: route pattern array must have the first element containing the pattern string, %s given', - isset($pattern[0]) ? gettype($pattern[0]) : 'none' - )); - } - - $patternString = $pattern[0]; - $parameterConditions = $pattern; - - unset($parameterConditions[0]); - - return [$patternString, $parameterConditions]; - } - - throw new InvalidRoutePatternException(sprintf( - 'Cannot add route: route pattern must be a pattern string or array, %s given', - gettype($pattern) - )); - } - /** * Prefix the given URI with the last prefix. * diff --git a/src/Viserio/Routing/Segments/ParameterSegment.php b/src/Viserio/Routing/Segments/ParameterSegment.php new file mode 100644 index 000000000..b2b6b954a --- /dev/null +++ b/src/Viserio/Routing/Segments/ParameterSegment.php @@ -0,0 +1,8 @@ +addRoute($route); $dispatcher = new Dispatcher($collection); - - var_dump($dispatcher->match('GET', 'test')); } } diff --git a/src/Viserio/Routing/Tests/RouteTest.php b/src/Viserio/Routing/Tests/RouteTest.php index bdd39076d..aebb0516b 100644 --- a/src/Viserio/Routing/Tests/RouteTest.php +++ b/src/Viserio/Routing/Tests/RouteTest.php @@ -4,10 +4,10 @@ use Narrowspark\TestingHelper\Traits\MockeryTrait; use Interop\Container\ContainerInterface; -use RapidRoute\RouteParser; use Viserio\Routing\{ Route, Router, + RouteParser, Tests\Fixture\Controller }; diff --git a/src/Viserio/Routing/composer.json b/src/Viserio/Routing/composer.json index 001c1d47a..ecf5ae8db 100644 --- a/src/Viserio/Routing/composer.json +++ b/src/Viserio/Routing/composer.json @@ -20,7 +20,6 @@ "require": { "php" : "7.0.0 - 7.0.5 || ^7.0.7", "container-interop/container-interop" : "^1.0", - "danielstjules/stringy" : "^2.3", "narrowspark/arr" : "^1.1", "php-di/invoker" : "^1.3", "psr/http-message" : "^1.0", From e82fcf47cfce3625b37fdcca0cdbacee9493dd07 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sat, 13 Aug 2016 00:50:23 +0200 Subject: [PATCH 14/39] added more tests | start adding benchmarks | adding matcher for uri segments --- benchmarks/Routing/RouteParserBench.php | 10 ++++ composer.json | 5 +- phpbench.json | 5 ++ .../Contracts/Routing/RouteSegment.php | 15 ----- .../Contracts/Routing/SegmentMatcher.php | 40 +++++++++++++ .../Matchers/AbstractSegmentMatcher.php | 57 +++++++++++++++++++ .../Routing/Matchers/StaticMatcher.php | 49 ++++++++++++++++ src/Viserio/Routing/Pattern.php | 2 +- src/Viserio/Routing/RouteParser.php | 4 +- .../Routing/Segments/ParameterSegment.php | 8 --- src/Viserio/Routing/Tests/RouteParserTest.php | 38 +++++++++++++ src/Viserio/Routing/Tests/VarExporterTest.php | 47 +++++++++++++++ src/Viserio/Routing/VarExporter.php | 54 ++++++++++++++++++ 13 files changed, 306 insertions(+), 28 deletions(-) create mode 100644 benchmarks/Routing/RouteParserBench.php create mode 100644 phpbench.json delete mode 100644 src/Viserio/Contracts/Routing/RouteSegment.php create mode 100644 src/Viserio/Routing/Matchers/AbstractSegmentMatcher.php create mode 100644 src/Viserio/Routing/Matchers/StaticMatcher.php delete mode 100644 src/Viserio/Routing/Segments/ParameterSegment.php create mode 100644 src/Viserio/Routing/Tests/RouteParserTest.php create mode 100644 src/Viserio/Routing/Tests/VarExporterTest.php create mode 100644 src/Viserio/Routing/VarExporter.php diff --git a/benchmarks/Routing/RouteParserBench.php b/benchmarks/Routing/RouteParserBench.php new file mode 100644 index 000000000..e6c72149a --- /dev/null +++ b/benchmarks/Routing/RouteParserBench.php @@ -0,0 +1,10 @@ +parameterKeys; + } + + /** + * {@inheritdoc} + */ + public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey): array + { + return array_fill_keys($this->parameterKeys, $segmentVariable); + } + + /** + * {@inheritdoc} + */ + public function mergeParameterKeys(SegmentMatcherContract $matcher) + { + if($matcher->getMatchHash() !== $this->getMatchHash()) { + throw new RuntimeException( + sprintf('Cannot merge parameters: matchers must be equivalent, \'%s\' expected, \'%s\' given', get_class($matcher), $this->getMatchHash()) + ); + } + + $this->parameterKeys = array_unique( + array_merge($this->parameterKeys, $matcher->parameterKeys), + SORT_NUMERIC + ); + } + + /** + * {@inheritdoc} + */ + public function getMatchHash(): string + { + return uniqid(get_called_class(), true); + } +} diff --git a/src/Viserio/Routing/Matchers/StaticMatcher.php b/src/Viserio/Routing/Matchers/StaticMatcher.php new file mode 100644 index 000000000..cecf7fda5 --- /dev/null +++ b/src/Viserio/Routing/Matchers/StaticMatcher.php @@ -0,0 +1,49 @@ +parameterKeys = $parameterKey === null ? [] : [$parameterKey]; + $this->segment = $segment; + } + + /** + * {@inheritdoc} + */ + public function getConditionExpression($segmentVariable, $uniqueKey): string + { + return $segmentVariable . ' === ' . VarExporter::export($this->segment); + } + + /** + * {@inheritdoc} + */ + public function getMatchedParameterExpressions($segmentVariable, $uniqueKey): array + { + return $this->parameterKeys ? [$this->parameterKeys[0] => $segmentVariable] : []; + } +} diff --git a/src/Viserio/Routing/Pattern.php b/src/Viserio/Routing/Pattern.php index 2ec8b270c..44b5188ce 100644 --- a/src/Viserio/Routing/Pattern.php +++ b/src/Viserio/Routing/Pattern.php @@ -39,7 +39,7 @@ public static function asRegex(string $pattern): string } /** - * Don't Instantiate the class. + * Don't instantiate this class. */ private function __construct() { // diff --git a/src/Viserio/Routing/RouteParser.php b/src/Viserio/Routing/RouteParser.php index 7d4dbbd5f..ff1a64462 100644 --- a/src/Viserio/Routing/RouteParser.php +++ b/src/Viserio/Routing/RouteParser.php @@ -17,8 +17,8 @@ public function parse(string $route): array { if (strlen($route) > 1 && $route[0] !== '/') { throw new InvalidRoutePatternException(sprintf( - 'Invalid route pattern: non-root route must be prefixed with \'/\', \'%s\' given', - $route + 'Invalid route pattern: non-root route must be prefixed with \'/\', \'%s\' given', + $route )); } diff --git a/src/Viserio/Routing/Segments/ParameterSegment.php b/src/Viserio/Routing/Segments/ParameterSegment.php deleted file mode 100644 index b2b6b954a..000000000 --- a/src/Viserio/Routing/Segments/ParameterSegment.php +++ /dev/null @@ -1,8 +0,0 @@ -assertEquals($expectedSegments, $parser->parse($uri)); + // } + + public function parsingExamples() + { + return [ + [ + // Empty route + '', + [], + [] + ], + [ + // Empty route + '/', + [], + [new StaticSegment('')] + ], + [ + '/user', + [], + [new StaticSegment('user')] + ], + ]; + } +} diff --git a/src/Viserio/Routing/Tests/VarExporterTest.php b/src/Viserio/Routing/Tests/VarExporterTest.php new file mode 100644 index 000000000..13af657a0 --- /dev/null +++ b/src/Viserio/Routing/Tests/VarExporterTest.php @@ -0,0 +1,47 @@ + 1]'], + [[1, 2, 3], '[0 => 1,1 => 2,2 => 3,]'], + [[1, '2', 3], '[0 => 1,1 => \'2\',2 => 3,]'], + [['foo' => 1, [2, 3]], '[\'foo\' => 1,0 => [0 => 2,1 => 3,],]'], + [new StdClass(), 'unserialize(\'O:8:"stdClass":0:{}\')'], + [(object) ['foo' => 'bar'], 'unserialize(\'O:8:"stdClass":1:{s:3:"foo";s:3:"bar";}\')'], + [new Controller(), 'unserialize(\'O:40:"Viserio\\\\Routing\\\\Tests\\\\Fixture\\\\Controller":0:{}\')'], + ]; + } + /** + * @dataProvider exportCases + */ + public function testConvertsValueToValidPhp($value, $code) + { + $exported = VarExporter::export($value); + $evaluated = eval('return ' . $exported . ';'); + + $this->assertSame($code, $exported, ''); + $this->assertEquals($value, $evaluated); + } +} diff --git a/src/Viserio/Routing/VarExporter.php b/src/Viserio/Routing/VarExporter.php new file mode 100644 index 000000000..6f7b6b8cb --- /dev/null +++ b/src/Viserio/Routing/VarExporter.php @@ -0,0 +1,54 @@ + ' . self::export(current($value)) . ']'; + } + + $code = '['; + $indent = ' '; + + foreach ($value as $key => $element) { + $code .= self::export($key); + $code .= ' => '; + $code .= self::export($element); + $code .= ','; + } + + $code .= ']'; + + return $code; + } elseif (is_object($value) && get_class($value) === StdClass::class) { + return '(object)' . self::export((array) $value); + } + + if (is_scalar($value)) { + return var_export($value, true); + } + + return 'unserialize(' . var_export(serialize($value), true) . ')'; + } +} From 7d1cda07e2c7912570d57310d4d0d38b4ab9da71 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sat, 13 Aug 2016 00:59:02 +0200 Subject: [PATCH 15/39] cs fix --- src/Viserio/Routing/Matchers/StaticMatcher.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Viserio/Routing/Matchers/StaticMatcher.php b/src/Viserio/Routing/Matchers/StaticMatcher.php index cecf7fda5..d7def85a7 100644 --- a/src/Viserio/Routing/Matchers/StaticMatcher.php +++ b/src/Viserio/Routing/Matchers/StaticMatcher.php @@ -34,7 +34,7 @@ public function __construct(string $segment, $parameterKey = null) /** * {@inheritdoc} */ - public function getConditionExpression($segmentVariable, $uniqueKey): string + public function getConditionExpression(string $segmentVariable, int $uniqueKey): string { return $segmentVariable . ' === ' . VarExporter::export($this->segment); } @@ -42,7 +42,7 @@ public function getConditionExpression($segmentVariable, $uniqueKey): string /** * {@inheritdoc} */ - public function getMatchedParameterExpressions($segmentVariable, $uniqueKey): array + public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey): array { return $this->parameterKeys ? [$this->parameterKeys[0] => $segmentVariable] : []; } From a64168ece01a8f283d85e51b5c4e5f4e38dd1b10 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sat, 13 Aug 2016 01:07:19 +0200 Subject: [PATCH 16/39] fix call for a stdclass VarExporter --- src/Viserio/Routing/Tests/VarExporterTest.php | 4 ++-- src/Viserio/Routing/VarExporter.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Viserio/Routing/Tests/VarExporterTest.php b/src/Viserio/Routing/Tests/VarExporterTest.php index 13af657a0..9cca02d99 100644 --- a/src/Viserio/Routing/Tests/VarExporterTest.php +++ b/src/Viserio/Routing/Tests/VarExporterTest.php @@ -28,8 +28,8 @@ public function exportCases() [[1, 2, 3], '[0 => 1,1 => 2,2 => 3,]'], [[1, '2', 3], '[0 => 1,1 => \'2\',2 => 3,]'], [['foo' => 1, [2, 3]], '[\'foo\' => 1,0 => [0 => 2,1 => 3,],]'], - [new StdClass(), 'unserialize(\'O:8:"stdClass":0:{}\')'], - [(object) ['foo' => 'bar'], 'unserialize(\'O:8:"stdClass":1:{s:3:"foo";s:3:"bar";}\')'], + [new StdClass(), '(object)[]'], + [(object) ['foo' => 'bar'], '(object)[\'foo\' => \'bar\']'], [new Controller(), 'unserialize(\'O:40:"Viserio\\\\Routing\\\\Tests\\\\Fixture\\\\Controller":0:{}\')'], ]; } diff --git a/src/Viserio/Routing/VarExporter.php b/src/Viserio/Routing/VarExporter.php index 6f7b6b8cb..f6c7b60e8 100644 --- a/src/Viserio/Routing/VarExporter.php +++ b/src/Viserio/Routing/VarExporter.php @@ -41,7 +41,7 @@ public static function export($value): string $code .= ']'; return $code; - } elseif (is_object($value) && get_class($value) === StdClass::class) { + } elseif (is_object($value) && $value instanceof StdClass) { return '(object)' . self::export((array) $value); } From 8356efbbc6925cb64b07ac7479c4c5c9e7a612bf Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sat, 13 Aug 2016 02:31:30 +0200 Subject: [PATCH 17/39] cs fixes | remove some magic --- .../ByteCountingStreamException.php | 14 ++++----- .../Exceptions/UrlGenerationException.php | 2 +- src/Viserio/Contracts/Routing/Route.php | 6 ++-- .../Contracts/Routing/SegmentMatcher.php | 2 ++ src/Viserio/Events/Dispatcher.php | 12 +++++--- src/Viserio/Events/composer.json | 1 + .../Http/Stream/AbstractStreamDecorator.php | 29 +++++++------------ .../Http/Stream/ByteCountingStream.php | 7 +++-- src/Viserio/Http/Stream/LazyOpenStream.php | 3 +- src/Viserio/Pipeline/Pipeline.php | 6 ++-- src/Viserio/Queue/Jobs/AbstractJob.php | 6 ++-- .../Matchers/AbstractSegmentMatcher.php | 2 +- .../Routing/Matchers/StaticMatcher.php | 2 +- src/Viserio/Routing/Route.php | 14 +++++---- src/Viserio/Routing/RouteCollection.php | 6 ++-- src/Viserio/Routing/Tests/RouteTest.php | 4 +-- src/Viserio/Routing/VarExporter.php | 1 - 17 files changed, 62 insertions(+), 55 deletions(-) diff --git a/src/Viserio/Contracts/Http/Exceptions/ByteCountingStreamException.php b/src/Viserio/Contracts/Http/Exceptions/ByteCountingStreamException.php index ffc4d4c54..80188b2c4 100644 --- a/src/Viserio/Contracts/Http/Exceptions/ByteCountingStreamException.php +++ b/src/Viserio/Contracts/Http/Exceptions/ByteCountingStreamException.php @@ -2,8 +2,8 @@ declare(strict_types=1); namespace Viserio\Contracts\Http\Exceptions; -use Throwable; use RuntimeException; +use Throwable; class ByteCountingStreamException extends RuntimeException { @@ -24,15 +24,15 @@ class ByteCountingStreamException extends RuntimeException /** * ByteCountingStreamException constructor. * - * @param int $expect expected bytes to be read - * @param int $actual actual available bytes to read - * @param \Throwable $previous Exception being thrown + * @param int $expect expected bytes to be read + * @param int $actual actual available bytes to read + * @param \Throwable|null $previous Exception being thrown */ public function __construct(int $expect, int $actual, Throwable $previous = null) { - $msg = "The ByteCountingStream decorator expects to be able to " - . "read {$expect} bytes from a stream, but the stream being decorated " - . "only contains {$actual} bytes."; + $msg = 'The ByteCountingStream decorator expects to be able to ' + . sprintf('read %s bytes from a stream, but the stream being decorated ', $expect) + . sprintf('only contains %s bytes.', $actual); $this->expectBytes = $expect; $this->actualBytes = $actual; diff --git a/src/Viserio/Contracts/Routing/Exceptions/UrlGenerationException.php b/src/Viserio/Contracts/Routing/Exceptions/UrlGenerationException.php index 42e3ca5fa..9d2efdfa6 100644 --- a/src/Viserio/Contracts/Routing/Exceptions/UrlGenerationException.php +++ b/src/Viserio/Contracts/Routing/Exceptions/UrlGenerationException.php @@ -18,7 +18,7 @@ public static function forMissingParameters($route) return new static(sprintf( 'Missing required parameters for [Route: %s] [URI: %s].', $route->getName(), - $route->getPath() + $route->getUri() )); } } diff --git a/src/Viserio/Contracts/Routing/Route.php b/src/Viserio/Contracts/Routing/Route.php index e682531d8..9e0bf653c 100644 --- a/src/Viserio/Contracts/Routing/Route.php +++ b/src/Viserio/Contracts/Routing/Route.php @@ -55,14 +55,14 @@ public function getMethods(): array; * * @return bool */ - public function httpOnly(): bool; + public function isHttpOnly(): bool; /** * Determine if the route only responds to HTTPS requests. * * @return bool */ - public function httpsOnly(): bool; + public function isHttpsOnly(): bool; /** * Get the action name for the route. @@ -152,6 +152,8 @@ public function hasParameters(): bool; * Unset a parameter on the route if it is set. * * @param string $name + * + * @return void */ public function forgetParameter(string $name); diff --git a/src/Viserio/Contracts/Routing/SegmentMatcher.php b/src/Viserio/Contracts/Routing/SegmentMatcher.php index eb30781ff..20d49989d 100644 --- a/src/Viserio/Contracts/Routing/SegmentMatcher.php +++ b/src/Viserio/Contracts/Routing/SegmentMatcher.php @@ -25,6 +25,8 @@ public function getMatchedParameterExpressions(string $segmentVariable, int $uni * Merge parameters keys from same matcher. * * @param \Viserio\Contracts\Routing\SegmentMatcher $matcher + * + * @return void */ public function mergeParameterKeys(SegmentMatcher $matcher); diff --git a/src/Viserio/Events/Dispatcher.php b/src/Viserio/Events/Dispatcher.php index af3418fe3..1a1b09226 100644 --- a/src/Viserio/Events/Dispatcher.php +++ b/src/Viserio/Events/Dispatcher.php @@ -3,10 +3,14 @@ namespace Viserio\Events; use Interop\Container\ContainerInterface as ContainerContract; -use Viserio\Contracts\Events\Dispatcher as DispatcherContract; -use Viserio\Support\Invoker; -use Viserio\Support\Str; -use Viserio\Contracts\Container\Traits\ContainerAwareTrait; +use Viserio\Contracts\{ + Container\Traits\ContainerAwareTrait, + Events\Dispatcher as DispatcherContract +}; +use Viserio\Support\{ + Invoker, + Str +}; class Dispatcher implements DispatcherContract { diff --git a/src/Viserio/Events/composer.json b/src/Viserio/Events/composer.json index 981590b56..b5de64cf1 100644 --- a/src/Viserio/Events/composer.json +++ b/src/Viserio/Events/composer.json @@ -19,6 +19,7 @@ ], "require": { "php" : "7.0.0 - 7.0.5 || ^7.0.7", + "danielstjules/stringy" : "^2.3", "viserio/cotracts" : "self.version", "viserio/support" : "self.version" }, diff --git a/src/Viserio/Http/Stream/AbstractStreamDecorator.php b/src/Viserio/Http/Stream/AbstractStreamDecorator.php index b735f46f4..bbffaa0eb 100644 --- a/src/Viserio/Http/Stream/AbstractStreamDecorator.php +++ b/src/Viserio/Http/Stream/AbstractStreamDecorator.php @@ -11,30 +11,18 @@ abstract class AbstractStreamDecorator implements StreamInterface { /** - * @param StreamInterface $stream Stream to decorate + * Stream instance. + * + * @var \Psr\Http\Message\StreamInterface */ - public function __construct(StreamInterface $stream) - { - $this->stream = $stream; - } + protected $stream; /** - * Magic method used to create a new stream if streams are not added in - * the constructor of a decorator (e.g., LazyOpenStream). - * - * @param string $name Name of the property (allows "stream" only). - * - * @return StreamInterface + * @param StreamInterface $stream Stream to decorate */ - public function __get($name) + public function __construct(StreamInterface $stream) { - if ($name == 'stream') { - $this->stream = $this->createStream(); - - return $this->stream; - } - - throw new UnexpectedValueException("$name not found on class"); + $this->stream = $stream; } /** @@ -121,6 +109,9 @@ public function eof() return $this->stream->eof(); } + /** + * {@inheritdoc} + */ public function tell() { return $this->stream->tell(); diff --git a/src/Viserio/Http/Stream/ByteCountingStream.php b/src/Viserio/Http/Stream/ByteCountingStream.php index e85185a30..5ed73b752 100644 --- a/src/Viserio/Http/Stream/ByteCountingStream.php +++ b/src/Viserio/Http/Stream/ByteCountingStream.php @@ -2,8 +2,8 @@ declare(strict_types=1); namespace Viserio\Http\Stream; -use Psr\Http\Message\StreamInterface; use InvalidArgumentException; +use Psr\Http\Message\StreamInterface; use Viserio\Contracts\Http\Exceptions\ByteCountingStreamException; /** @@ -30,9 +30,10 @@ class ByteCountingStream extends AbstractStreamDecorator public function __construct(StreamInterface $stream, int $bytesToRead) { $this->stream = $stream; + if (!is_int($bytesToRead) || $bytesToRead < 0) { - $msg = "Bytes to read should be a non-negative integer for " - . "ByteCountingStream, got {$bytesToRead}."; + $msg = 'Bytes to read should be a non-negative integer for ' + . sprintf('ByteCountingStream, got %s.', $bytesToRead); throw new InvalidArgumentException($msg); } diff --git a/src/Viserio/Http/Stream/LazyOpenStream.php b/src/Viserio/Http/Stream/LazyOpenStream.php index 0fb0048ae..134730208 100644 --- a/src/Viserio/Http/Stream/LazyOpenStream.php +++ b/src/Viserio/Http/Stream/LazyOpenStream.php @@ -25,12 +25,13 @@ public function __construct($filename, $mode) { $this->filename = $filename; $this->mode = $mode; + $this->stream = $this->createStream(); } /** * Creates the underlying stream lazily when required. * - * @return StreamInterface + * @return \Psr\Http\Message\StreamInterface */ protected function createStream(): StreamInterface { diff --git a/src/Viserio/Pipeline/Pipeline.php b/src/Viserio/Pipeline/Pipeline.php index d0f1e78a9..efeebb1e6 100644 --- a/src/Viserio/Pipeline/Pipeline.php +++ b/src/Viserio/Pipeline/Pipeline.php @@ -4,9 +4,11 @@ use Closure; use ReflectionClass; -use Viserio\Contracts\Pipeline\Pipeline as PipelineContract; +use Viserio\Contracts\{ + Container\Traits\ContainerAwareTrait, + Pipeline\Pipeline as PipelineContract +}; use Viserio\Support\Invoker; -use Viserio\Contracts\Container\Traits\ContainerAwareTrait; class Pipeline implements PipelineContract { diff --git a/src/Viserio/Queue/Jobs/AbstractJob.php b/src/Viserio/Queue/Jobs/AbstractJob.php index 2788b2f44..6ce7d4bea 100644 --- a/src/Viserio/Queue/Jobs/AbstractJob.php +++ b/src/Viserio/Queue/Jobs/AbstractJob.php @@ -4,9 +4,11 @@ use DateTime; use Narrowspark\Arr\StaticArr as Arr; -use Viserio\Contracts\Queue\Job as JobContract; +use Viserio\Contracts\{ + Container\Traits\ContainerAwareTrait, + Queue\Job as JobContract +}; use Viserio\Queue\CallQueuedHandler; -use Viserio\Contracts\Container\Traits\ContainerAwareTrait; abstract class AbstractJob implements JobContract { diff --git a/src/Viserio/Routing/Matchers/AbstractSegmentMatcher.php b/src/Viserio/Routing/Matchers/AbstractSegmentMatcher.php index ad3bba4a1..8abd8fbc1 100644 --- a/src/Viserio/Routing/Matchers/AbstractSegmentMatcher.php +++ b/src/Viserio/Routing/Matchers/AbstractSegmentMatcher.php @@ -42,7 +42,7 @@ public function mergeParameterKeys(SegmentMatcherContract $matcher) } $this->parameterKeys = array_unique( - array_merge($this->parameterKeys, $matcher->parameterKeys), + array_merge($this->parameterKeys, $matcher->getParameterKeys()), SORT_NUMERIC ); } diff --git a/src/Viserio/Routing/Matchers/StaticMatcher.php b/src/Viserio/Routing/Matchers/StaticMatcher.php index d7def85a7..1fdd906ae 100644 --- a/src/Viserio/Routing/Matchers/StaticMatcher.php +++ b/src/Viserio/Routing/Matchers/StaticMatcher.php @@ -16,7 +16,7 @@ class StaticMatcher extends AbstractSegmentMatcher /** * Create a new satic segment matcher instance. * - * @param string $value + * @param string $segment * @param array|null $parameterKey */ public function __construct(string $segment, $parameterKey = null) diff --git a/src/Viserio/Routing/Route.php b/src/Viserio/Routing/Route.php index c37a20c66..b86775bb2 100644 --- a/src/Viserio/Routing/Route.php +++ b/src/Viserio/Routing/Route.php @@ -80,7 +80,7 @@ public function __construct($methods, $uri, $action) { $this->uri = $uri; // According to RFC methods are defined in uppercase (See RFC 7231) - $this->httpMethods = array_map("strtoupper",(array) $methods); + $this->httpMethods = array_map('strtoupper',(array) $methods); $this->action = $this->parseAction($action); if (in_array('GET', $this->httpMethods) && ! in_array('HEAD', $this->httpMethods)) { @@ -147,7 +147,7 @@ public function getMethods(): array /** * {@inheritdoc} */ - public function httpOnly(): bool + public function isHttpOnly(): bool { return in_array('http', $this->action, true); } @@ -155,7 +155,7 @@ public function httpOnly(): bool /** * {@inheritdoc} */ - public function httpsOnly(): bool + public function isHttpsOnly(): bool { return in_array('https', $this->action, true); } @@ -267,6 +267,8 @@ public function forgetParameter(string $name) */ public function isStatic(): bool { + $this->getParameters(); + foreach($this->parameters as $parameter) { if ($parameter instanceof ParameterSegment) { return false; @@ -308,13 +310,13 @@ public function setRouter(RouterContract $router): RouteContract */ public function __get($key) { - return $this->parameter($key); + return $this->getParameter($key); } /** * Set configured invoker. * - * @return \Viserio\Support\Invoker; + * @return \Viserio\Support\Invoker */ protected function initInvoker(): Invoker { @@ -344,7 +346,7 @@ protected function parseAction($action): array // if the user never explicitly sets an action to handle the given uri. if (is_null($action)) { return ['uses' => function () { - throw new LogicException("Route for [{$this->uri}] has no action."); + throw new LogicException(sprintf('Route for [%s] has no action.', $this->uri)); }]; } diff --git a/src/Viserio/Routing/RouteCollection.php b/src/Viserio/Routing/RouteCollection.php index 0ac046cce..4542209fc 100644 --- a/src/Viserio/Routing/RouteCollection.php +++ b/src/Viserio/Routing/RouteCollection.php @@ -43,9 +43,9 @@ class RouteCollection /** * Add a Route instance to the collection. * - * @param Viserio\Contracts\Routing\Route $route + * @param \Viserio\Contracts\Routing\Route $route * - * @return Viserio\Contracts\Routing\Route + * @return \Viserio\Contracts\Routing\Route */ public function addRoute(RouteContract $route): RouteContract { @@ -67,7 +67,7 @@ public function getRoutes(): array /** * Add the given route to the arrays of routes. * - * @param Viserio\Contracts\Routing\Route $route + * @param \Viserio\Contracts\Routing\Route $route */ protected function addToCollections(RouteContract $route) { diff --git a/src/Viserio/Routing/Tests/RouteTest.php b/src/Viserio/Routing/Tests/RouteTest.php index aebb0516b..fa4eef170 100644 --- a/src/Viserio/Routing/Tests/RouteTest.php +++ b/src/Viserio/Routing/Tests/RouteTest.php @@ -79,11 +79,11 @@ public function testHttpAndHttps() { $route = new Route('GET', 'test', ['http']); - $this->assertTrue($route->httpOnly()); + $this->assertTrue($route->isHttpOnly()); $route = new Route('GET', 'test', ['https']); - $this->assertTrue($route->httpsOnly()); + $this->assertTrue($route->isHttpsOnly()); } public function testSetAndGetPrefix() diff --git a/src/Viserio/Routing/VarExporter.php b/src/Viserio/Routing/VarExporter.php index f6c7b60e8..bd45a2c82 100644 --- a/src/Viserio/Routing/VarExporter.php +++ b/src/Viserio/Routing/VarExporter.php @@ -29,7 +29,6 @@ public static function export($value): string } $code = '['; - $indent = ' '; foreach ($value as $key => $element) { $code .= self::export($key); From afd1e058df1f2379f22054dd456c19a019bdb968 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sat, 13 Aug 2016 02:31:56 +0200 Subject: [PATCH 18/39] remove some magic --- .../Http/Stream/AbstractStreamDecorator.php | 17 +- src/Viserio/Http/Stream/LazyOpenStream.php | 156 +++++++++++++++++- 2 files changed, 155 insertions(+), 18 deletions(-) diff --git a/src/Viserio/Http/Stream/AbstractStreamDecorator.php b/src/Viserio/Http/Stream/AbstractStreamDecorator.php index bbffaa0eb..af4ee52c8 100644 --- a/src/Viserio/Http/Stream/AbstractStreamDecorator.php +++ b/src/Viserio/Http/Stream/AbstractStreamDecorator.php @@ -2,8 +2,7 @@ declare(strict_types=1); namespace Viserio\Http\Stream; -use BadMethodCallException; -use Exception; +use Throwable; use Psr\Http\Message\StreamInterface; use UnexpectedValueException; use Viserio\Http\Util; @@ -36,7 +35,7 @@ public function __toString() } return $this->getContents(); - } catch (Exception $e) { + } catch (Throwable $e) { // Really, PHP? https://bugs.php.net/bug.php?id=53648 trigger_error('StreamDecorator::__toString exception: ' . (string) $e, E_USER_ERROR); @@ -172,16 +171,4 @@ public function write($string) { return $this->stream->write($string); } - - /** - * Implement in subclasses to dynamically create streams when requested. - * - * @throws \BadMethodCallException - * - * @return StreamInterface - */ - protected function createStream(): StreamInterface - { - throw new BadMethodCallException('Not implemented'); - } } diff --git a/src/Viserio/Http/Stream/LazyOpenStream.php b/src/Viserio/Http/Stream/LazyOpenStream.php index 134730208..9f5c48fc3 100644 --- a/src/Viserio/Http/Stream/LazyOpenStream.php +++ b/src/Viserio/Http/Stream/LazyOpenStream.php @@ -5,7 +5,7 @@ use Psr\Http\Message\StreamInterface; use Viserio\Http\Util; -class LazyOpenStream extends AbstractStreamDecorator +class LazyOpenStream implements StreamInterface { /** * @var string @@ -21,11 +21,161 @@ class LazyOpenStream extends AbstractStreamDecorator * @param string $filename File to lazily open * @param string $mode fopen mode to use when opening the stream */ - public function __construct($filename, $mode) + public function __construct(string $filename, string $mode) { $this->filename = $filename; $this->mode = $mode; - $this->stream = $this->createStream(); + } + + /** + * Magic method used to create a new stream if streams are not added in + * the constructor of a decorator (e.g., LazyOpenStream). + * + * @param string $name Name of the property (allows "stream" only). + * + * @return StreamInterface + */ + public function __get($name) + { + if ($name == 'stream') { + $this->stream = $this->createStream(); + + return $this->stream; + } + + throw new UnexpectedValueException("$name not found on class"); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + try { + if ($this->isSeekable()) { + $this->seek(0); + } + + return $this->getContents(); + } catch (Throwable $e) { + // Really, PHP? https://bugs.php.net/bug.php?id=53648 + trigger_error('StreamDecorator::__toString exception: ' + . (string) $e, E_USER_ERROR); + + return ''; + } + } + + /** + * {@inheritdoc} + */ + public function getContents() + { + return Util::copyToString($this); + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->stream->close(); + } + + /** + * {@inheritdoc} + */ + public function getMetadata($key = null) + { + return $this->stream->getMetadata($key); + } + + /** + * {@inheritdoc} + */ + public function detach() + { + return $this->stream->detach(); + } + + /** + * {@inheritdoc} + */ + public function getSize() + { + return $this->stream->getSize(); + } + + /** + * {@inheritdoc} + */ + public function eof() + { + return $this->stream->eof(); + } + + /** + * {@inheritdoc} + */ + public function tell() + { + return $this->stream->tell(); + } + + /** + * {@inheritdoc} + */ + public function isReadable() + { + return $this->stream->isReadable(); + } + + /** + * {@inheritdoc} + */ + public function isWritable() + { + return $this->stream->isWritable(); + } + + /** + * {@inheritdoc} + */ + public function isSeekable() + { + return $this->stream->isSeekable(); + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + $this->seek(0); + } + + /** + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + $this->stream->seek($offset, $whence); + } + + /** + * {@inheritdoc} + */ + public function read($length) + { + return $this->stream->read($length); + } + + /** + * {@inheritdoc} + */ + public function write($string) + { + return $this->stream->write($string); } /** From b247d7ae93ca80d760dc424e512d0e8cb8624540 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sat, 13 Aug 2016 02:34:29 +0200 Subject: [PATCH 19/39] cs fixes --- src/Viserio/Http/Stream/AbstractStreamDecorator.php | 1 - src/Viserio/Http/Stream/LazyOpenStream.php | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Viserio/Http/Stream/AbstractStreamDecorator.php b/src/Viserio/Http/Stream/AbstractStreamDecorator.php index af4ee52c8..cfdef4578 100644 --- a/src/Viserio/Http/Stream/AbstractStreamDecorator.php +++ b/src/Viserio/Http/Stream/AbstractStreamDecorator.php @@ -4,7 +4,6 @@ use Throwable; use Psr\Http\Message\StreamInterface; -use UnexpectedValueException; use Viserio\Http\Util; abstract class AbstractStreamDecorator implements StreamInterface diff --git a/src/Viserio/Http/Stream/LazyOpenStream.php b/src/Viserio/Http/Stream/LazyOpenStream.php index 9f5c48fc3..7929a5202 100644 --- a/src/Viserio/Http/Stream/LazyOpenStream.php +++ b/src/Viserio/Http/Stream/LazyOpenStream.php @@ -3,6 +3,7 @@ namespace Viserio\Http\Stream; use Psr\Http\Message\StreamInterface; +use UnexpectedValueException; use Viserio\Http\Util; class LazyOpenStream implements StreamInterface @@ -29,11 +30,11 @@ public function __construct(string $filename, string $mode) /** * Magic method used to create a new stream if streams are not added in - * the constructor of a decorator (e.g., LazyOpenStream). + * the constructor of LazyOpenStream. * * @param string $name Name of the property (allows "stream" only). * - * @return StreamInterface + * @return \Psr\Http\Message\StreamInterface */ public function __get($name) { From b05559ed334ff952408627228d7fad38f77cec34 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sat, 13 Aug 2016 11:23:55 +0200 Subject: [PATCH 20/39] added new matcher --- .../Contracts/Routing/SegmentMatcher.php | 12 +- src/Viserio/Http/Stream/LazyOpenStream.php | 3 +- ...SegmentMatcher.php => AbstractMatcher.php} | 4 +- .../Routing/Matchers/ParameterMatcher.php | 52 +++++++++ src/Viserio/Routing/Matchers/RegexMatcher.php | 108 ++++++++++++++++++ .../Routing/Matchers/StaticMatcher.php | 19 ++- .../Tests/Matchers/StaticMatcherTest.php | 34 ++++++ 7 files changed, 217 insertions(+), 15 deletions(-) rename src/Viserio/Routing/Matchers/{AbstractSegmentMatcher.php => AbstractMatcher.php} (92%) create mode 100644 src/Viserio/Routing/Matchers/ParameterMatcher.php create mode 100644 src/Viserio/Routing/Matchers/RegexMatcher.php create mode 100644 src/Viserio/Routing/Tests/Matchers/StaticMatcherTest.php diff --git a/src/Viserio/Contracts/Routing/SegmentMatcher.php b/src/Viserio/Contracts/Routing/SegmentMatcher.php index 20d49989d..5d6d9bc37 100644 --- a/src/Viserio/Contracts/Routing/SegmentMatcher.php +++ b/src/Viserio/Contracts/Routing/SegmentMatcher.php @@ -14,12 +14,12 @@ public function getParameterKeys(): array; /** * Get a metched parameter key back, * - * @param string $segmentVariable - * @param int $uniqueKey + * @param string $segmentVariable + * @param int|null $uniqueKey * * @return string[] */ - public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey): array; + public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey = null): array; /** * Merge parameters keys from same matcher. @@ -33,12 +33,12 @@ public function mergeParameterKeys(SegmentMatcher $matcher); /** * Get a ready to use condition expression from a segment. * - * @param string $segmentVariable - * @param int $uniqueKey + * @param string $segmentVariable + * @param int|null $uniqueKey * * @return string */ - public function getConditionExpression(string $segmentVariable, int $uniqueKey): string; + public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string; /** * Returns a unique hash for the matching criteria of the segment. diff --git a/src/Viserio/Http/Stream/LazyOpenStream.php b/src/Viserio/Http/Stream/LazyOpenStream.php index 7929a5202..a27a151e1 100644 --- a/src/Viserio/Http/Stream/LazyOpenStream.php +++ b/src/Viserio/Http/Stream/LazyOpenStream.php @@ -4,6 +4,7 @@ use Psr\Http\Message\StreamInterface; use UnexpectedValueException; +use Throwable; use Viserio\Http\Util; class LazyOpenStream implements StreamInterface @@ -44,7 +45,7 @@ public function __get($name) return $this->stream; } - throw new UnexpectedValueException("$name not found on class"); + throw new UnexpectedValueException(sprintf('%s not found on class', $name)); } /** diff --git a/src/Viserio/Routing/Matchers/AbstractSegmentMatcher.php b/src/Viserio/Routing/Matchers/AbstractMatcher.php similarity index 92% rename from src/Viserio/Routing/Matchers/AbstractSegmentMatcher.php rename to src/Viserio/Routing/Matchers/AbstractMatcher.php index 8abd8fbc1..a42f08ff7 100644 --- a/src/Viserio/Routing/Matchers/AbstractSegmentMatcher.php +++ b/src/Viserio/Routing/Matchers/AbstractMatcher.php @@ -5,7 +5,7 @@ use RuntimeException; use Viserio\Contracts\Routing\SegmentMatcher as SegmentMatcherContract; -abstract class AbstractSegmentMatcher implements SegmentMatcherContract +abstract class AbstractMatcher implements SegmentMatcherContract { /** * Stores all parameters keys. @@ -25,7 +25,7 @@ public function getParameterKeys(): array /** * {@inheritdoc} */ - public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey): array + public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey = null): array { return array_fill_keys($this->parameterKeys, $segmentVariable); } diff --git a/src/Viserio/Routing/Matchers/ParameterMatcher.php b/src/Viserio/Routing/Matchers/ParameterMatcher.php new file mode 100644 index 000000000..308da0e00 --- /dev/null +++ b/src/Viserio/Routing/Matchers/ParameterMatcher.php @@ -0,0 +1,52 @@ +names = is_array($names) ? $names : [$names]; + + parent::__construct($regex, $this->getKeyGroupedMap()); + } + + /** + * [getKeyGroupMap description] + * + * @param array $parameterIndexNameMap + * + * @return array + */ + protected function getKeyGroupedMap(array $parameterIndexNameMap): array + { + $parameterKey = empty($parameterIndexNameMap) ? 0 : max(array_keys($parameterIndexNameMap)) + 1; + $parameterKeyGroupMap = []; + $group = 0; + + foreach($this->names as $name) { + $parameterKeyGroupMap[$parameterKey] = $group++; + $parameterKey++; + } + + return $parameterKeyGroupMap; + } +} diff --git a/src/Viserio/Routing/Matchers/RegexMatcher.php b/src/Viserio/Routing/Matchers/RegexMatcher.php new file mode 100644 index 000000000..5820c8b38 --- /dev/null +++ b/src/Viserio/Routing/Matchers/RegexMatcher.php @@ -0,0 +1,108 @@ +regex = Pattern::asRegex($regex); + $this->parameterKeyGroupMap = $parameterKeyGroupMap; + $this->parameterKeys = array_keys($parameterKeyGroupMap); + } + + /** + * Counted parameters keys. + * + * @return int + */ + public function getGroupCount(): int + { + return count(array_unique($this->parameterKeyGroupMap, SORT_NUMERIC)); + } + + /** + * Retruns the parameters key group array. + * + * @return array + */ + public function getParameterKeyGroupMap(): array + { + return $this->parameterKeyGroupMap; + } + + /** + * Retruns the used regex. + * + * @return string + */ + public function getRegex(): string + { + return $this->regex; + } + + /** + * {@inheritdoc} + */ + public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string + { + return 'preg_match(' + . VarExporter::export($this->regex) + . ', ' + . $segmentVariable + . ', ' + . '$matches' . $uniqueKey + . ')'; + } + + /** + * {@inheritdoc} + */ + public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey = null): array + { + $matches = []; + + foreach($this->parameterKeyGroupMap as $parameterKey => $group) { + // Use $group + 1 as the first $matches element is the full text that matched, + // we want the groups + $matches[$parameterKey] = '$matches' . $uniqueKey . '[' . ($group + 1) . ']'; + } + + return $matches; + } + + /** + * {@inheritdoc} + */ + public function mergeParameterKeys(SegmentMatcherContract $matcher) + { + parent::mergeParameterKeys($matcher); + + $this->parameterKeyGroupMap += $matcher->getParameterKeyGroupMap(); + } +} diff --git a/src/Viserio/Routing/Matchers/StaticMatcher.php b/src/Viserio/Routing/Matchers/StaticMatcher.php index 1fdd906ae..f5e74f764 100644 --- a/src/Viserio/Routing/Matchers/StaticMatcher.php +++ b/src/Viserio/Routing/Matchers/StaticMatcher.php @@ -3,8 +3,9 @@ namespace Viserio\Routing\Matchers; use RuntimeException; +use Viserio\Routing\VarExporter; -class StaticMatcher extends AbstractSegmentMatcher +class StaticMatcher extends AbstractMatcher { /** * The static string @@ -19,7 +20,7 @@ class StaticMatcher extends AbstractSegmentMatcher * @param string $segment * @param array|null $parameterKey */ - public function __construct(string $segment, $parameterKey = null) + public function __construct(string $segment, array $parameterKey = null) { if (strpos($segment, '/') !== false) { throw new RuntimeException( @@ -27,14 +28,14 @@ public function __construct(string $segment, $parameterKey = null) ); } - $this->parameterKeys = $parameterKey === null ? [] : [$parameterKey]; + $this->parameterKeys = $parameterKey ?? []; $this->segment = $segment; } /** * {@inheritdoc} */ - public function getConditionExpression(string $segmentVariable, int $uniqueKey): string + public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string { return $segmentVariable . ' === ' . VarExporter::export($this->segment); } @@ -42,8 +43,14 @@ public function getConditionExpression(string $segmentVariable, int $uniqueKey): /** * {@inheritdoc} */ - public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey): array + public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey = null): array { - return $this->parameterKeys ? [$this->parameterKeys[0] => $segmentVariable] : []; + $keys = $this->parameterKeys; + + if (count($keys) > 0) { + return [$keys[0] => $segmentVariable]; + } + + return []; } } diff --git a/src/Viserio/Routing/Tests/Matchers/StaticMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/StaticMatcherTest.php new file mode 100644 index 000000000..d29f2f2f8 --- /dev/null +++ b/src/Viserio/Routing/Tests/Matchers/StaticMatcherTest.php @@ -0,0 +1,34 @@ +assertSame('one === \'one\'', $matcher->getConditionExpression('one')); + } + + public function testGetMatchedParameterExpressions() + { + $matcher = new StaticMatcher('two', [1]); + + $this->assertSame([1 => 'two'], $matcher->getMatchedParameterExpressions('two')); + + $matcher = new StaticMatcher('three'); + + $this->assertSame([], $matcher->getMatchedParameterExpressions('three')); + } +} From 5acb59228e644acdc82be9bee3001c29424c4dfa Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sat, 13 Aug 2016 15:19:46 +0200 Subject: [PATCH 21/39] added all matcher --- .../Contracts/Routing/SegmentMatcher.php | 8 +- .../Routing/Matchers/AbstractMatcher.php | 15 +++- .../Routing/Matchers/CompoundMatcher.php | 80 +++++++++++++++++++ .../Routing/Matchers/ExpressionMatcher.php | 51 ++++++++++++ src/Viserio/Routing/Matchers/RegexMatcher.php | 9 +++ .../Routing/Matchers/StaticMatcher.php | 14 +++- .../Tests/Matchers/StaticMatcherTest.php | 9 +++ 7 files changed, 176 insertions(+), 10 deletions(-) create mode 100644 src/Viserio/Routing/Matchers/CompoundMatcher.php create mode 100644 src/Viserio/Routing/Matchers/ExpressionMatcher.php diff --git a/src/Viserio/Contracts/Routing/SegmentMatcher.php b/src/Viserio/Contracts/Routing/SegmentMatcher.php index 5d6d9bc37..62b453e81 100644 --- a/src/Viserio/Contracts/Routing/SegmentMatcher.php +++ b/src/Viserio/Contracts/Routing/SegmentMatcher.php @@ -4,7 +4,9 @@ interface SegmentMatcher { - /** + const SEGMENT_PLACEHOLDER = '{segment}'; + + /** * Return all set parameters keys. * * @return int[] @@ -41,9 +43,9 @@ public function mergeParameterKeys(SegmentMatcher $matcher); public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string; /** - * Returns a unique hash for the matching criteria of the segment. + * Returns a unique hash for the segment matcher. * * @return string */ - public function getMatchHash(): string; + public function getHash(): string; } diff --git a/src/Viserio/Routing/Matchers/AbstractMatcher.php b/src/Viserio/Routing/Matchers/AbstractMatcher.php index a42f08ff7..a52f50bc2 100644 --- a/src/Viserio/Routing/Matchers/AbstractMatcher.php +++ b/src/Viserio/Routing/Matchers/AbstractMatcher.php @@ -35,9 +35,9 @@ public function getMatchedParameterExpressions(string $segmentVariable, int $uni */ public function mergeParameterKeys(SegmentMatcherContract $matcher) { - if($matcher->getMatchHash() !== $this->getMatchHash()) { + if($matcher->getHash() !== $this->getHash()) { throw new RuntimeException( - sprintf('Cannot merge parameters: matchers must be equivalent, \'%s\' expected, \'%s\' given', get_class($matcher), $this->getMatchHash()) + sprintf('Cannot merge parameters: matchers must be equivalent, \'%s\' expected, \'%s\' given', $matcher->getHash(), $this->getHash()) ); } @@ -50,8 +50,15 @@ public function mergeParameterKeys(SegmentMatcherContract $matcher) /** * {@inheritdoc} */ - public function getMatchHash(): string + public function getHash(): string { - return uniqid(get_called_class(), true); + return get_class($this) . ':' . $this->getMatchHash(); } + + /** + * Returns a unique hash for the matching criteria of the segment. + * + * @return string + */ + abstract protected function getMatchHash(): string; } diff --git a/src/Viserio/Routing/Matchers/CompoundMatcher.php b/src/Viserio/Routing/Matchers/CompoundMatcher.php new file mode 100644 index 000000000..85c4b245a --- /dev/null +++ b/src/Viserio/Routing/Matchers/CompoundMatcher.php @@ -0,0 +1,80 @@ +getParameterKeys()); + } + + $this->parameterKeys = $parameterKeys; + $this->matchers = $matchers; + } + + /** + * {@inheritdoc} + */ + public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string + { + $conditions = []; + + foreach($this->matchers as $key => $matcher) { + $conditions[] = $matcher->getConditionExpression($segmentVariable, $uniqueKey . '_' . $key); + } + + return implode(' && ', $conditions); + } + + /** + * {@inheritdoc} + */ + public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey = null): array + { + $expressions = []; + + foreach($this->matchers as $key => $matcher) { + $matchedParameterExpressions = $matcher->getMatchedParameterExpressions( + $segmentVariable, + $uniqueKey . '_' . $key + ); + + foreach($matchedParameterExpressions as $parameter => $expression) { + $expressions[$parameter] = $expression; + } + } + + return $expressions; + } + + /** + * {@inheritdoc} + */ + protected function getMatchHash() + { + $hashes = []; + + foreach($this->matchers as $matcher) { + $hashes[] = $matcher->getHash(); + } + + return implode('::', $hashes); + } +} diff --git a/src/Viserio/Routing/Matchers/ExpressionMatcher.php b/src/Viserio/Routing/Matchers/ExpressionMatcher.php new file mode 100644 index 000000000..4858af492 --- /dev/null +++ b/src/Viserio/Routing/Matchers/ExpressionMatcher.php @@ -0,0 +1,51 @@ +expression = $expression; + $this->parameterKeys = $parameterKeys; + } + + /** + * Returns the used expression. + * + * @return string + */ + public function getExpression(): string + { + return $this->expression; + } + + /** + * {@inheritdoc} + */ + public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string + { + return str_replace(self::SEGMENT_PLACEHOLDER, $segmentVariable, $this->expression); + } + + /** + * {@inheritdoc} + */ + protected function getMatchHash(): string + { + return $this->expression; + } +} diff --git a/src/Viserio/Routing/Matchers/RegexMatcher.php b/src/Viserio/Routing/Matchers/RegexMatcher.php index 5820c8b38..df7087ab5 100644 --- a/src/Viserio/Routing/Matchers/RegexMatcher.php +++ b/src/Viserio/Routing/Matchers/RegexMatcher.php @@ -6,6 +6,7 @@ Pattern, VarExporter }; +use Viserio\Contracts\Routing\SegmentMatcher as SegmentMatcherContract; class RegexMatcher extends AbstractMatcher { @@ -105,4 +106,12 @@ public function mergeParameterKeys(SegmentMatcherContract $matcher) $this->parameterKeyGroupMap += $matcher->getParameterKeyGroupMap(); } + + /** + * {@inheritdoc} + */ + protected function getMatchHash(): string + { + return $this->regex; + } } diff --git a/src/Viserio/Routing/Matchers/StaticMatcher.php b/src/Viserio/Routing/Matchers/StaticMatcher.php index f5e74f764..02b2fe507 100644 --- a/src/Viserio/Routing/Matchers/StaticMatcher.php +++ b/src/Viserio/Routing/Matchers/StaticMatcher.php @@ -18,9 +18,9 @@ class StaticMatcher extends AbstractMatcher * Create a new satic segment matcher instance. * * @param string $segment - * @param array|null $parameterKey + * @param array|null $parameterKeys */ - public function __construct(string $segment, array $parameterKey = null) + public function __construct(string $segment, array $parameterKeys = null) { if (strpos($segment, '/') !== false) { throw new RuntimeException( @@ -28,7 +28,7 @@ public function __construct(string $segment, array $parameterKey = null) ); } - $this->parameterKeys = $parameterKey ?? []; + $this->parameterKeys = $parameterKeys ?? []; $this->segment = $segment; } @@ -53,4 +53,12 @@ public function getMatchedParameterExpressions(string $segmentVariable, int $uni return []; } + + /** + * {@inheritdoc} + */ + protected function getMatchHash(): string + { + return $this->segment; + } } diff --git a/src/Viserio/Routing/Tests/Matchers/StaticMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/StaticMatcherTest.php index d29f2f2f8..80f880c9f 100644 --- a/src/Viserio/Routing/Tests/Matchers/StaticMatcherTest.php +++ b/src/Viserio/Routing/Tests/Matchers/StaticMatcherTest.php @@ -31,4 +31,13 @@ public function testGetMatchedParameterExpressions() $this->assertSame([], $matcher->getMatchedParameterExpressions('three')); } + + public function testMergeParameterKeys() + { + $matcher = new StaticMatcher('two', [2]); + $matcher2 = new StaticMatcher('two', [3]); + $matcher->mergeParameterKeys($matcher2); + + $this->assertSame([2 => 'two'], $matcher->getMatchedParameterExpressions('two')); + } } From 59a4058ee90257794552cce20b84d35b72b1dfde Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sun, 14 Aug 2016 20:45:17 +0200 Subject: [PATCH 22/39] remove ParameterMatcher | start adding tests --- .../Routing/Matchers/AbstractMatcher.php | 8 ++- .../Routing/Matchers/CompoundMatcher.php | 12 ++--- .../Routing/Matchers/ParameterMatcher.php | 52 ------------------- src/Viserio/Routing/Matchers/RegexMatcher.php | 13 +++-- .../Tests/Matchers/RegexMatcherTest.php | 46 ++++++++++++++++ 5 files changed, 66 insertions(+), 65 deletions(-) delete mode 100644 src/Viserio/Routing/Matchers/ParameterMatcher.php create mode 100644 src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php diff --git a/src/Viserio/Routing/Matchers/AbstractMatcher.php b/src/Viserio/Routing/Matchers/AbstractMatcher.php index a52f50bc2..078f83718 100644 --- a/src/Viserio/Routing/Matchers/AbstractMatcher.php +++ b/src/Viserio/Routing/Matchers/AbstractMatcher.php @@ -35,9 +35,13 @@ public function getMatchedParameterExpressions(string $segmentVariable, int $uni */ public function mergeParameterKeys(SegmentMatcherContract $matcher) { - if($matcher->getHash() !== $this->getHash()) { + if ($matcher->getHash() !== $this->getHash()) { throw new RuntimeException( - sprintf('Cannot merge parameters: matchers must be equivalent, \'%s\' expected, \'%s\' given', $matcher->getHash(), $this->getHash()) + sprintf( + 'Cannot merge parameters: matchers must be equivalent, \'%s\' expected, \'%s\' given', + $matcher->getHash(), + $this->getHash() + ) ); } diff --git a/src/Viserio/Routing/Matchers/CompoundMatcher.php b/src/Viserio/Routing/Matchers/CompoundMatcher.php index 85c4b245a..5ee9af400 100644 --- a/src/Viserio/Routing/Matchers/CompoundMatcher.php +++ b/src/Viserio/Routing/Matchers/CompoundMatcher.php @@ -21,7 +21,7 @@ public function __construct(array $matchers) { $parameterKeys = []; - foreach($matchers as $matcher) { + foreach ($matchers as $matcher) { $parameterKeys = array_merge($parameterKeys, $matcher->getParameterKeys()); } @@ -36,7 +36,7 @@ public function getConditionExpression(string $segmentVariable, int $uniqueKey = { $conditions = []; - foreach($this->matchers as $key => $matcher) { + foreach ($this->matchers as $key => $matcher) { $conditions[] = $matcher->getConditionExpression($segmentVariable, $uniqueKey . '_' . $key); } @@ -50,13 +50,13 @@ public function getMatchedParameterExpressions(string $segmentVariable, int $uni { $expressions = []; - foreach($this->matchers as $key => $matcher) { + foreach ($this->matchers as $key => $matcher) { $matchedParameterExpressions = $matcher->getMatchedParameterExpressions( $segmentVariable, $uniqueKey . '_' . $key ); - foreach($matchedParameterExpressions as $parameter => $expression) { + foreach ($matchedParameterExpressions as $parameter => $expression) { $expressions[$parameter] = $expression; } } @@ -67,11 +67,11 @@ public function getMatchedParameterExpressions(string $segmentVariable, int $uni /** * {@inheritdoc} */ - protected function getMatchHash() + protected function getMatchHash(): string { $hashes = []; - foreach($this->matchers as $matcher) { + foreach ($this->matchers as $matcher) { $hashes[] = $matcher->getHash(); } diff --git a/src/Viserio/Routing/Matchers/ParameterMatcher.php b/src/Viserio/Routing/Matchers/ParameterMatcher.php deleted file mode 100644 index 308da0e00..000000000 --- a/src/Viserio/Routing/Matchers/ParameterMatcher.php +++ /dev/null @@ -1,52 +0,0 @@ -names = is_array($names) ? $names : [$names]; - - parent::__construct($regex, $this->getKeyGroupedMap()); - } - - /** - * [getKeyGroupMap description] - * - * @param array $parameterIndexNameMap - * - * @return array - */ - protected function getKeyGroupedMap(array $parameterIndexNameMap): array - { - $parameterKey = empty($parameterIndexNameMap) ? 0 : max(array_keys($parameterIndexNameMap)) + 1; - $parameterKeyGroupMap = []; - $group = 0; - - foreach($this->names as $name) { - $parameterKeyGroupMap[$parameterKey] = $group++; - $parameterKey++; - } - - return $parameterKeyGroupMap; - } -} diff --git a/src/Viserio/Routing/Matchers/RegexMatcher.php b/src/Viserio/Routing/Matchers/RegexMatcher.php index df7087ab5..32dd963e1 100644 --- a/src/Viserio/Routing/Matchers/RegexMatcher.php +++ b/src/Viserio/Routing/Matchers/RegexMatcher.php @@ -28,13 +28,16 @@ class RegexMatcher extends AbstractMatcher * Create a new regex segment matcher instance. * * @param string $segment - * @param array $parameterKeyGroupMap + * @param int $parameterKeyGroupMap */ - public function __construct(string $regex, array $parameterKeyGroupMap) + public function __construct(string $regex, int $parameterKeyGroupMap) { $this->regex = Pattern::asRegex($regex); - $this->parameterKeyGroupMap = $parameterKeyGroupMap; - $this->parameterKeys = array_keys($parameterKeyGroupMap); + + $map = [$parameterKeyGroupMap => 0]; + + $this->parameterKeyGroupMap = $map; + $this->parameterKeys = array_keys($map); } /** @@ -88,7 +91,7 @@ public function getMatchedParameterExpressions(string $segmentVariable, int $uni { $matches = []; - foreach($this->parameterKeyGroupMap as $parameterKey => $group) { + foreach ($this->parameterKeyGroupMap as $parameterKey => $group) { // Use $group + 1 as the first $matches element is the full text that matched, // we want the groups $matches[$parameterKey] = '$matches' . $uniqueKey . '[' . ($group + 1) . ']'; diff --git a/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php new file mode 100644 index 000000000..822e538a2 --- /dev/null +++ b/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php @@ -0,0 +1,46 @@ +assertSame(1, $matcher->getGroupCount()); + } + + public function testGetRegex() + { + $matcher = new RegexMatcher(Pattern::ALPHA, 12); + + $this->assertSame(Pattern::asRegex(Pattern::ALPHA), $matcher->getRegex()); + } + + public function testGetParameterKeyGroupMap() + { + $matcher = new RegexMatcher(Pattern::ALPHA, 12); + + $this->assertSame([12 => 0], $matcher->getParameterKeyGroupMap()); + } + + public function testGetConditionExpression() + { + $matcher = new RegexMatcher(Pattern::ALPHA, 12); + + $this->assertSame('preg_match(\'/^([a-zA-Z]+)$/\', test, $matches)', $matcher->getConditionExpression('test')); + } + + public function testGetMatchedParameterExpressions() + { + $matcher = new RegexMatcher(Pattern::ALPHA, 12); + + $this->assertSame([12 => '$matches[1]'], $matcher->getMatchedParameterExpressions('test')); + } +} From 32aebd737a9826a44bab52ac0f1a30d51ec878db Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sun, 14 Aug 2016 21:52:00 +0200 Subject: [PATCH 23/39] added more tests --- src/Viserio/Routing/Matchers/AnyMatcher.php | 32 +++++++++++++++++++ .../Routing/Matchers/CompoundMatcher.php | 3 +- .../Routing/Tests/Matchers/AnyMatcherTest.php | 15 +++++++++ .../Tests/Matchers/CompoundMatcherTest.php | 9 ++++++ .../Tests/Matchers/ExpressionMatcherTest.php | 22 +++++++++++++ 5 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/Viserio/Routing/Matchers/AnyMatcher.php create mode 100644 src/Viserio/Routing/Tests/Matchers/AnyMatcherTest.php create mode 100644 src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php create mode 100644 src/Viserio/Routing/Tests/Matchers/ExpressionMatcherTest.php diff --git a/src/Viserio/Routing/Matchers/AnyMatcher.php b/src/Viserio/Routing/Matchers/AnyMatcher.php new file mode 100644 index 000000000..4b09c9ff7 --- /dev/null +++ b/src/Viserio/Routing/Matchers/AnyMatcher.php @@ -0,0 +1,32 @@ +parameterKeys = $parameterKeys; + } + + /** + * {@inheritdoc} + */ + public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string + { + return $segmentVariable . ' !== \'\''; + } + + /** + * {@inheritdoc} + */ + protected function getMatchHash(): string + { + return ''; + } +} diff --git a/src/Viserio/Routing/Matchers/CompoundMatcher.php b/src/Viserio/Routing/Matchers/CompoundMatcher.php index 5ee9af400..2d3e03648 100644 --- a/src/Viserio/Routing/Matchers/CompoundMatcher.php +++ b/src/Viserio/Routing/Matchers/CompoundMatcher.php @@ -14,8 +14,7 @@ class CompoundMatcher extends AbstractMatcher /** * Create a new compound matcher instance. * - * @param string $expression - * @param array $parameterKeys + * @param array $matchers */ public function __construct(array $matchers) { diff --git a/src/Viserio/Routing/Tests/Matchers/AnyMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/AnyMatcherTest.php new file mode 100644 index 000000000..c9c00cb9c --- /dev/null +++ b/src/Viserio/Routing/Tests/Matchers/AnyMatcherTest.php @@ -0,0 +1,15 @@ +assertSame('segment/[test] !== \'\'', $matcher->getConditionExpression('segment/[test]')); + } +} diff --git a/src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php new file mode 100644 index 000000000..c5823c794 --- /dev/null +++ b/src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php @@ -0,0 +1,9 @@ +assertSame('ctype_digit({segment})', $matcher->getExpression()); + } + + public function testGetConditionExpression() + { + $matcher = new ExpressionMatcher('ctype_digit({segment})', [1]); + + $this->assertSame('ctype_digit(test)', $matcher->getConditionExpression('test')); + } +} From 299740ddf1e25602195f9006f7d402ce499d776e Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sun, 14 Aug 2016 23:08:35 +0200 Subject: [PATCH 24/39] more tests --- .../Contracts/Routing/SegmentMatcher.php | 12 +++---- .../Routing/Matchers/AbstractMatcher.php | 2 +- src/Viserio/Routing/Matchers/AnyMatcher.php | 2 +- .../Routing/Matchers/CompoundMatcher.php | 4 +-- .../Routing/Matchers/ExpressionMatcher.php | 2 +- src/Viserio/Routing/Matchers/RegexMatcher.php | 4 +-- .../Routing/Matchers/StaticMatcher.php | 4 +-- .../Tests/Matchers/CompoundMatcherTest.php | 35 ++++++++++++++++++- 8 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/Viserio/Contracts/Routing/SegmentMatcher.php b/src/Viserio/Contracts/Routing/SegmentMatcher.php index 62b453e81..af55abbbd 100644 --- a/src/Viserio/Contracts/Routing/SegmentMatcher.php +++ b/src/Viserio/Contracts/Routing/SegmentMatcher.php @@ -16,12 +16,12 @@ public function getParameterKeys(): array; /** * Get a metched parameter key back, * - * @param string $segmentVariable - * @param int|null $uniqueKey + * @param string $segmentVariable + * @param string|null $uniqueKey * * @return string[] */ - public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey = null): array; + public function getMatchedParameterExpressions(string $segmentVariable, string $uniqueKey = null): array; /** * Merge parameters keys from same matcher. @@ -35,12 +35,12 @@ public function mergeParameterKeys(SegmentMatcher $matcher); /** * Get a ready to use condition expression from a segment. * - * @param string $segmentVariable - * @param int|null $uniqueKey + * @param string $segmentVariable + * @param string|null $uniqueKey * * @return string */ - public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string; + public function getConditionExpression(string $segmentVariable, string $uniqueKey = null): string; /** * Returns a unique hash for the segment matcher. diff --git a/src/Viserio/Routing/Matchers/AbstractMatcher.php b/src/Viserio/Routing/Matchers/AbstractMatcher.php index 078f83718..b362851a2 100644 --- a/src/Viserio/Routing/Matchers/AbstractMatcher.php +++ b/src/Viserio/Routing/Matchers/AbstractMatcher.php @@ -25,7 +25,7 @@ public function getParameterKeys(): array /** * {@inheritdoc} */ - public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey = null): array + public function getMatchedParameterExpressions(string $segmentVariable, string $uniqueKey = null): array { return array_fill_keys($this->parameterKeys, $segmentVariable); } diff --git a/src/Viserio/Routing/Matchers/AnyMatcher.php b/src/Viserio/Routing/Matchers/AnyMatcher.php index 4b09c9ff7..2621df44f 100644 --- a/src/Viserio/Routing/Matchers/AnyMatcher.php +++ b/src/Viserio/Routing/Matchers/AnyMatcher.php @@ -17,7 +17,7 @@ public function __construct(array $parameterKeys) /** * {@inheritdoc} */ - public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string + public function getConditionExpression(string $segmentVariable, string $uniqueKey = null): string { return $segmentVariable . ' !== \'\''; } diff --git a/src/Viserio/Routing/Matchers/CompoundMatcher.php b/src/Viserio/Routing/Matchers/CompoundMatcher.php index 2d3e03648..7aa127e87 100644 --- a/src/Viserio/Routing/Matchers/CompoundMatcher.php +++ b/src/Viserio/Routing/Matchers/CompoundMatcher.php @@ -31,7 +31,7 @@ public function __construct(array $matchers) /** * {@inheritdoc} */ - public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string + public function getConditionExpression(string $segmentVariable, string $uniqueKey = null): string { $conditions = []; @@ -45,7 +45,7 @@ public function getConditionExpression(string $segmentVariable, int $uniqueKey = /** * {@inheritdoc} */ - public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey = null): array + public function getMatchedParameterExpressions(string $segmentVariable, string $uniqueKey = null): array { $expressions = []; diff --git a/src/Viserio/Routing/Matchers/ExpressionMatcher.php b/src/Viserio/Routing/Matchers/ExpressionMatcher.php index 4858af492..2788f1a4b 100644 --- a/src/Viserio/Routing/Matchers/ExpressionMatcher.php +++ b/src/Viserio/Routing/Matchers/ExpressionMatcher.php @@ -36,7 +36,7 @@ public function getExpression(): string /** * {@inheritdoc} */ - public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string + public function getConditionExpression(string $segmentVariable, string $uniqueKey = null): string { return str_replace(self::SEGMENT_PLACEHOLDER, $segmentVariable, $this->expression); } diff --git a/src/Viserio/Routing/Matchers/RegexMatcher.php b/src/Viserio/Routing/Matchers/RegexMatcher.php index 32dd963e1..1a5424f81 100644 --- a/src/Viserio/Routing/Matchers/RegexMatcher.php +++ b/src/Viserio/Routing/Matchers/RegexMatcher.php @@ -73,7 +73,7 @@ public function getRegex(): string /** * {@inheritdoc} */ - public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string + public function getConditionExpression(string $segmentVariable, string $uniqueKey = null): string { return 'preg_match(' . VarExporter::export($this->regex) @@ -87,7 +87,7 @@ public function getConditionExpression(string $segmentVariable, int $uniqueKey = /** * {@inheritdoc} */ - public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey = null): array + public function getMatchedParameterExpressions(string $segmentVariable, string $uniqueKey = null): array { $matches = []; diff --git a/src/Viserio/Routing/Matchers/StaticMatcher.php b/src/Viserio/Routing/Matchers/StaticMatcher.php index 02b2fe507..0e2369ef5 100644 --- a/src/Viserio/Routing/Matchers/StaticMatcher.php +++ b/src/Viserio/Routing/Matchers/StaticMatcher.php @@ -35,7 +35,7 @@ public function __construct(string $segment, array $parameterKeys = null) /** * {@inheritdoc} */ - public function getConditionExpression(string $segmentVariable, int $uniqueKey = null): string + public function getConditionExpression(string $segmentVariable, string $uniqueKey = null): string { return $segmentVariable . ' === ' . VarExporter::export($this->segment); } @@ -43,7 +43,7 @@ public function getConditionExpression(string $segmentVariable, int $uniqueKey = /** * {@inheritdoc} */ - public function getMatchedParameterExpressions(string $segmentVariable, int $uniqueKey = null): array + public function getMatchedParameterExpressions(string $segmentVariable, string $uniqueKey = null): array { $keys = $this->parameterKeys; diff --git a/src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php index c5823c794..f7bb13a8f 100644 --- a/src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php +++ b/src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php @@ -2,8 +2,41 @@ declare(strict_types=1); namespace Viserio\Routing\Tests\Matchers; -use Viserio\Routing\Matchers\ExpressionMatcher; +use Viserio\Routing\Matchers\{ + AnyMatcher, + CompoundMatcher, + StaticMatcher +}; class CompoundMatcherTest extends \PHPUnit_Framework_TestCase { + public function testGetConditionExpression() + { + $matcher = new CompoundMatcher([ + new StaticMatcher('test', [1]), + new AnyMatcher([0]) + ]); + + $this->assertSame('test === \'test\' && test !== \'\'', $matcher->getConditionExpression('test', '2')); + } + + public function testGetMatchedParameterExpressions() + { + $matcher = new CompoundMatcher([ + new StaticMatcher('test', [1]), + new AnyMatcher([0]) + ]); + + $this->assertSame([1 => 'test', 0 => 'test'], $matcher->getMatchedParameterExpressions('test', '2')); + } + + public function testGetHash() + { + $matcher = new CompoundMatcher([ + new StaticMatcher('test', [1]), + new AnyMatcher([0]) + ]); + + $this->assertSame('Viserio\Routing\Matchers\CompoundMatcher:Viserio\Routing\Matchers\StaticMatcher:test::Viserio\Routing\Matchers\AnyMatcher:', $matcher->getHash()); + } } From d4f81453acc607a45b7862d24655a94ec2bf8f2e Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Sun, 14 Aug 2016 23:18:47 +0200 Subject: [PATCH 25/39] more tests --- .../Routing/Matchers/AbstractMatcher.php | 2 +- .../Tests/Matchers/ExpressionMatcherTest.php | 18 +++++++++++++++++- src/Viserio/Routing/Tests/VarExporterTest.php | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Viserio/Routing/Matchers/AbstractMatcher.php b/src/Viserio/Routing/Matchers/AbstractMatcher.php index b362851a2..0925ec14f 100644 --- a/src/Viserio/Routing/Matchers/AbstractMatcher.php +++ b/src/Viserio/Routing/Matchers/AbstractMatcher.php @@ -38,7 +38,7 @@ public function mergeParameterKeys(SegmentMatcherContract $matcher) if ($matcher->getHash() !== $this->getHash()) { throw new RuntimeException( sprintf( - 'Cannot merge parameters: matchers must be equivalent, \'%s\' expected, \'%s\' given', + 'Cannot merge parameters: matchers must be equivalent, \'%s\' expected, \'%s\' given.', $matcher->getHash(), $this->getHash() ) diff --git a/src/Viserio/Routing/Tests/Matchers/ExpressionMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/ExpressionMatcherTest.php index 1b1e5f5f8..8283491f6 100644 --- a/src/Viserio/Routing/Tests/Matchers/ExpressionMatcherTest.php +++ b/src/Viserio/Routing/Tests/Matchers/ExpressionMatcherTest.php @@ -2,7 +2,10 @@ declare(strict_types=1); namespace Viserio\Routing\Tests\Matchers; -use Viserio\Routing\Matchers\ExpressionMatcher; +use Viserio\Routing\Matchers\{ + ExpressionMatcher, + StaticMatcher +}; class ExpressionMatcherTest extends \PHPUnit_Framework_TestCase { @@ -19,4 +22,17 @@ public function testGetConditionExpression() $this->assertSame('ctype_digit(test)', $matcher->getConditionExpression('test')); } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage Cannot merge parameters: matchers must be equivalent, 'Viserio\Routing\Matchers\StaticMatcher:two' expected, 'Viserio\Routing\Matchers\ExpressionMatcher:ctype_digit({segment})' given. + */ + public function testMergeParameterKeys() + { + $matcher = new ExpressionMatcher('ctype_digit({segment})', [1]); + $matcher2 = new StaticMatcher('two', [3]); + $matcher->mergeParameterKeys($matcher2); + + $this->assertSame([2 => 'two'], $matcher->getMatchedParameterExpressions('two')); + } } diff --git a/src/Viserio/Routing/Tests/VarExporterTest.php b/src/Viserio/Routing/Tests/VarExporterTest.php index 9cca02d99..59267c4f0 100644 --- a/src/Viserio/Routing/Tests/VarExporterTest.php +++ b/src/Viserio/Routing/Tests/VarExporterTest.php @@ -33,6 +33,7 @@ public function exportCases() [new Controller(), 'unserialize(\'O:40:"Viserio\\\\Routing\\\\Tests\\\\Fixture\\\\Controller":0:{}\')'], ]; } + /** * @dataProvider exportCases */ From 4c364b26e7084ca67ed9398f1d53fd066f5abef6 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Mon, 15 Aug 2016 00:12:00 +0200 Subject: [PATCH 26/39] added more tests --- src/Viserio/Routing/Generator/RouteTreeBuilder.php | 8 ++++++++ .../Routing/Tests/Matchers/AnyMatcherTest.php | 9 +++++++++ .../Routing/Tests/Matchers/CompoundMatcherTest.php | 13 +++++++++++++ .../Tests/Matchers/ExpressionMatcherTest.php | 2 -- .../Routing/Tests/Matchers/RegexMatcherTest.php | 10 ++++++++++ src/Viserio/Routing/VarExporter.php | 7 +++++++ src/Viserio/Routing/composer.json | 2 +- 7 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/Viserio/Routing/Generator/RouteTreeBuilder.php diff --git a/src/Viserio/Routing/Generator/RouteTreeBuilder.php b/src/Viserio/Routing/Generator/RouteTreeBuilder.php new file mode 100644 index 000000000..b116ea967 --- /dev/null +++ b/src/Viserio/Routing/Generator/RouteTreeBuilder.php @@ -0,0 +1,8 @@ +assertSame('segment/[test] !== \'\'', $matcher->getConditionExpression('segment/[test]')); } + + public function testAnyMergingParameterKeys() + { + $matcher1 = new AnyMatcher([123]); + $matcher2 = new AnyMatcher([12, 3]); + $matcher1->mergeParameterKeys($matcher2); + + $this->assertSame([123, 12, 3], $matcher1->getParameterKeys()); + } } diff --git a/src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php index f7bb13a8f..e2df5f019 100644 --- a/src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php +++ b/src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php @@ -39,4 +39,17 @@ public function testGetHash() $this->assertSame('Viserio\Routing\Matchers\CompoundMatcher:Viserio\Routing\Matchers\StaticMatcher:test::Viserio\Routing\Matchers\AnyMatcher:', $matcher->getHash()); } + + public function testCompoundSegmentMatcher() + { + $matcher1 = new CompoundMatcher([new StaticMatcher('a'), new StaticMatcher('b', [0])]); + $matcher2 = new CompoundMatcher([new StaticMatcher('a', [0]), new StaticMatcher('c', [1])]); + + $this->assertSame([0], $matcher1->getParameterKeys()); + $this->assertNotEquals($matcher2->getHash(), $matcher1->getHash()); + $this->assertSame('$segment === \'a\' && $segment === \'b\'', $matcher1->getConditionExpression('$segment', '0')); + $this->assertSame([0 => '$segment'], $matcher1->getMatchedParameterExpressions('$segment', '0')); + $this->assertSame('$segment === \'a\' && $segment === \'c\'', $matcher2->getConditionExpression('$segment', '0')); + $this->assertSame([0 => '$segment', 1 => '$segment'], $matcher2->getMatchedParameterExpressions('$segment', '0')); + } } diff --git a/src/Viserio/Routing/Tests/Matchers/ExpressionMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/ExpressionMatcherTest.php index 8283491f6..26eac0b74 100644 --- a/src/Viserio/Routing/Tests/Matchers/ExpressionMatcherTest.php +++ b/src/Viserio/Routing/Tests/Matchers/ExpressionMatcherTest.php @@ -32,7 +32,5 @@ public function testMergeParameterKeys() $matcher = new ExpressionMatcher('ctype_digit({segment})', [1]); $matcher2 = new StaticMatcher('two', [3]); $matcher->mergeParameterKeys($matcher2); - - $this->assertSame([2 => 'two'], $matcher->getMatchedParameterExpressions('two')); } } diff --git a/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php index 822e538a2..a7379cba2 100644 --- a/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php +++ b/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php @@ -43,4 +43,14 @@ public function testGetMatchedParameterExpressions() $this->assertSame([12 => '$matches[1]'], $matcher->getMatchedParameterExpressions('test')); } + + public function testRegexMergingParameterKeys() + { + $matcher1 = new RegexMatcher(Pattern::ANY, 12); + $matcher2 = new RegexMatcher(Pattern::ANY, 11); + $matcher1->mergeParameterKeys($matcher2); + + $this->assertSame([12, 11], $matcher1->getParameterKeys()); + $this->assertSame([12 => 0, 11 => 0], $matcher1->getParameterKeyGroupMap()); + } } diff --git a/src/Viserio/Routing/VarExporter.php b/src/Viserio/Routing/VarExporter.php index bd45a2c82..71cef7b6a 100644 --- a/src/Viserio/Routing/VarExporter.php +++ b/src/Viserio/Routing/VarExporter.php @@ -50,4 +50,11 @@ public static function export($value): string return 'unserialize(' . var_export(serialize($value), true) . ')'; } + + /** + * Don't instantiate this class. + */ + private function __construct() { + // + } } diff --git a/src/Viserio/Routing/composer.json b/src/Viserio/Routing/composer.json index ecf5ae8db..7ed8f39e6 100644 --- a/src/Viserio/Routing/composer.json +++ b/src/Viserio/Routing/composer.json @@ -2,7 +2,7 @@ "name" : "viserio/routing", "type" : "library", "description": "The Viserio Routing package.", - "keywords" : ["viserio", "narrowspark", "route", "rapid-route", "dispatcher"], + "keywords" : ["viserio", "narrowspark", "route", "dispatcher", "router"], "license" : "MIT", "homepage" : "http://github.com/narrowspark/framework", "support" : { From 63bab5ef223920b32dfcee125c10cd63f26fd147 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Mon, 15 Aug 2016 21:58:54 +0200 Subject: [PATCH 27/39] added more tests --- src/Viserio/Contracts/Routing/Dispatcher.php | 31 ++++ .../InvalidRoutePatternException.php | 4 +- .../{ => Contracts}/Routing/Pattern.php | 23 +-- src/Viserio/Contracts/Routing/RouteParser.php | 9 +- src/Viserio/Routing/Dispatcher.php | 16 +- src/Viserio/Routing/Matchers/AnyMatcher.php | 2 +- .../Routing/Matchers/ParameterMatcher.php | 28 ++++ src/Viserio/Routing/Matchers/RegexMatcher.php | 7 +- src/Viserio/Routing/RouteCollection.php | 34 ++++ src/Viserio/Routing/RouteParser.php | 143 +++++++++++++--- src/Viserio/Routing/Router.php | 2 +- .../Tests/Matchers/RegexMatcherTest.php | 22 ++- src/Viserio/Routing/Tests/RouteParserTest.php | 154 +++++++++++++++++- src/Viserio/Routing/Tests/RouteTest.php | 10 +- src/Viserio/Routing/VarExporter.php | 2 + 15 files changed, 394 insertions(+), 93 deletions(-) create mode 100644 src/Viserio/Contracts/Routing/Dispatcher.php rename src/Viserio/{ => Contracts}/Routing/Pattern.php (53%) create mode 100644 src/Viserio/Routing/Matchers/ParameterMatcher.php diff --git a/src/Viserio/Contracts/Routing/Dispatcher.php b/src/Viserio/Contracts/Routing/Dispatcher.php new file mode 100644 index 000000000..9a43f2e13 --- /dev/null +++ b/src/Viserio/Contracts/Routing/Dispatcher.php @@ -0,0 +1,31 @@ + MatchResult::FOUND, 1 => , 2 => ] + * + * [0 => MatchResult::HTTP_METHOD_NOT_ALLOWED, 1 => ] + * + * [0 => MatchResult::NOT_FOUND] + * + * @param string $httpMethod + * @param string $uri + * + * @return array + * + * @throws \RuntimeException + */ + public function dispatch(string $httpMethod, string $uri): array; +} diff --git a/src/Viserio/Contracts/Routing/Exceptions/InvalidRoutePatternException.php b/src/Viserio/Contracts/Routing/Exceptions/InvalidRoutePatternException.php index 7fc1b0512..5df454650 100644 --- a/src/Viserio/Contracts/Routing/Exceptions/InvalidRoutePatternException.php +++ b/src/Viserio/Contracts/Routing/Exceptions/InvalidRoutePatternException.php @@ -2,8 +2,8 @@ declare(strict_types=1); namespace Viserio\Contracts\Routing\Exceptions; -use Exception; +use InvalidArgumentException; -class InvalidRoutePatternException extends Exception +class InvalidRoutePatternException extends InvalidArgumentException { } diff --git a/src/Viserio/Routing/Pattern.php b/src/Viserio/Contracts/Routing/Pattern.php similarity index 53% rename from src/Viserio/Routing/Pattern.php rename to src/Viserio/Contracts/Routing/Pattern.php index 44b5188ce..31ea23c1f 100644 --- a/src/Viserio/Routing/Pattern.php +++ b/src/Viserio/Contracts/Routing/Pattern.php @@ -1,8 +1,8 @@ 'create' }, * ] * - * @param string $route + * @param string $route + * @param string[] $conditions * * @return \Viserio\Contracts\Routing\RouteSegment[] * * @throws \Viserio\Contracts\Routing\Exception\InvalidRoutePatternException */ - public function parse(string $route): array; + public function parse(string $route, array $conditions): array; } diff --git a/src/Viserio/Routing/Dispatcher.php b/src/Viserio/Routing/Dispatcher.php index 16e044508..967d3cfac 100644 --- a/src/Viserio/Routing/Dispatcher.php +++ b/src/Viserio/Routing/Dispatcher.php @@ -3,8 +3,9 @@ namespace Viserio\Routing; use Psr\Http\Message\ServerRequestInterface; +use Viserio\Contracts\Routing\Dispatcher as DispatcherContract; -class Dispatcher +class Dispatcher implements DispatcherContract { /** * The route collection instance. @@ -33,7 +34,7 @@ public function __construct(RouteCollection $routes) */ public function handle(ServerRequestInterface $request) { - $match = $this->match( + $match = $this->dispatch( $request->getMethod(), $request->getUri()->getPath() ); @@ -52,16 +53,9 @@ public function handle(ServerRequestInterface $request) } /** - * Get Route for given method and uri. - * - * @param string $httpMethod - * @param string $uri - * - * @return \RapidRoute\MatchResult - * - * @throws \RapidRoute\InvalidRouteDataException + * {@inheritdoc} */ - public function match(string $httpMethod, string $uri) + public function dispatch(string $httpMethod, string $uri): array { } diff --git a/src/Viserio/Routing/Matchers/AnyMatcher.php b/src/Viserio/Routing/Matchers/AnyMatcher.php index 2621df44f..47678cec2 100644 --- a/src/Viserio/Routing/Matchers/AnyMatcher.php +++ b/src/Viserio/Routing/Matchers/AnyMatcher.php @@ -7,7 +7,7 @@ class AnyMatcher extends AbstractMatcher /** * Create a new any matcher instance. * - * @param array $parameterKeys + * @param array $parameterKeys */ public function __construct(array $parameterKeys) { diff --git a/src/Viserio/Routing/Matchers/ParameterMatcher.php b/src/Viserio/Routing/Matchers/ParameterMatcher.php new file mode 100644 index 000000000..4bb4084ca --- /dev/null +++ b/src/Viserio/Routing/Matchers/ParameterMatcher.php @@ -0,0 +1,28 @@ +names = is_array($names) ? $names : [$names]; + $this->regex = $regex; + } +} diff --git a/src/Viserio/Routing/Matchers/RegexMatcher.php b/src/Viserio/Routing/Matchers/RegexMatcher.php index 1a5424f81..d495c400b 100644 --- a/src/Viserio/Routing/Matchers/RegexMatcher.php +++ b/src/Viserio/Routing/Matchers/RegexMatcher.php @@ -2,10 +2,7 @@ declare(strict_types=1); namespace Viserio\Routing\Matchers; -use Viserio\Routing\{ - Pattern, - VarExporter -}; +use Viserio\Routing\VarExporter; use Viserio\Contracts\Routing\SegmentMatcher as SegmentMatcherContract; class RegexMatcher extends AbstractMatcher @@ -32,7 +29,7 @@ class RegexMatcher extends AbstractMatcher */ public function __construct(string $regex, int $parameterKeyGroupMap) { - $this->regex = Pattern::asRegex($regex); + $this->regex = $regex; $map = [$parameterKeyGroupMap => 0]; diff --git a/src/Viserio/Routing/RouteCollection.php b/src/Viserio/Routing/RouteCollection.php index 4542209fc..8c276c73d 100644 --- a/src/Viserio/Routing/RouteCollection.php +++ b/src/Viserio/Routing/RouteCollection.php @@ -79,4 +79,38 @@ protected function addToCollections(RouteContract $route) $this->allRoutes[$method.$domainAndUri] = $route; } + + /** + * + * @param string $pattern [description] + * + * @return array + */ + protected function parseRoutingPattern(string $pattern): array + { + if (is_string($pattern)) { + return [$pattern, []]; + } + + if (is_array($pattern)) { + if (!isset($pattern[0]) || !is_string($pattern[0])) { + throw new InvalidRoutePatternException(sprintf( + 'Cannot add route: route pattern array must have the first element containing the pattern string, %s given', + isset($pattern[0]) ? gettype($pattern[0]) : 'none' + )); + } + + $patternString = $pattern[0]; + $parameterConditions = $pattern; + + unset($parameterConditions[0]); + + return [$patternString, $parameterConditions]; + } + + throw new InvalidRoutePatternException(sprintf( + 'Cannot add route: route pattern must be a pattern string, %s given', + gettype($pattern) + )); + } } diff --git a/src/Viserio/Routing/RouteParser.php b/src/Viserio/Routing/RouteParser.php index ff1a64462..e5b363d40 100644 --- a/src/Viserio/Routing/RouteParser.php +++ b/src/Viserio/Routing/RouteParser.php @@ -2,10 +2,15 @@ declare(strict_types=1); namespace Viserio\Routing; +use Viserio\Routing\Matchers\{ + StaticMatcher, + ParameterMatcher +}; use Viserio\Contracts\Routing\{ - Exception\InvalidRoutePatternException, + Exceptions\InvalidRoutePatternException, RouteParser as RouteParserContract, - RouteSegment as RouteSegmentContract + RouteSegment as RouteSegmentContract, + Pattern }; class RouteParser implements RouteParserContract @@ -13,7 +18,7 @@ class RouteParser implements RouteParserContract /** * {@inheritdoc} */ - public function parse(string $route): array + public function parse(string $route, array $conditions): array { if (strlen($route) > 1 && $route[0] !== '/') { throw new InvalidRoutePatternException(sprintf( @@ -22,38 +27,128 @@ public function parse(string $route): array )); } - list($patternString, $conditions) = $this->parseRoutingPattern($route); - $segments = []; + $matches = []; + $names = []; + $patternSegments = explode('/', $route); + + array_shift($patternSegments); + + foreach ($patternSegments as $key => $patternSegment) { + if ($this->matchRouteParameters($route, $patternSegment, $conditions, $matches, $names)) { + $segments[] = new ParameterMatcher( + $names, + $this->generateRegex($matches, $conditions) + ); + } else { + $segments[] = new StaticMatcher($patternSegment); + } + } return $segments; } - protected function parseRoutingPattern($pattern) - { - if (is_string($pattern)) { - return [$pattern, []]; - } + /** + * Validate and match uri paramters. + * + * @param string $pattern + * @param string $patternSegment + * @param array &$conditions + * @param array &$matches + * @param array &$names + * + * @return bool + */ + protected function matchRouteParameters( + string $uri, + string $patternSegment, + array &$conditions, + array &$matches, + array &$names + ): bool { + $matchedParameter = false; + $names = []; + $matches = []; + $current = ''; + $inParameter = false; + + foreach (str_split($patternSegment) as $character) { + if ($inParameter) { + if ($character === '}') { + if (strpos($current, ':') !== false) { + $regex = substr($current, strpos($current, ':') + 1); + $current = substr($current, 0, strpos($current, ':')); + $conditions[$current] = $regex; + } + + $matches[] = [self::PARAMETER_PART, $current]; + $names[] = $current; + $current = ''; + $inParameter = false; + $matchedParameter = true; + + continue; + } elseif ($character === '{') { + throw new InvalidRoutePatternException(sprintf( + 'Invalid route uri: cannot contain nested \'{\', \'%s\' given', + $uri + )); + } + } else { + if ($character === '{') { + $matches[] = [self::STATIC_PART, $current]; + $current = ''; + $inParameter = true; - if (is_array($pattern)) { - if (!isset($pattern[0]) || !is_string($pattern[0])) { - throw new InvalidRoutePatternException(sprintf( - 'Cannot add route: route pattern array must have the first element containing the pattern string, %s given', - isset($pattern[0]) ? gettype($pattern[0]) : 'none' - )); + continue; + } elseif ($character === '}') { + throw new InvalidRoutePatternException(sprintf( + 'Invalid route uri: cannot contain \'}\' before opening \'{\', \'%s\' given', + $uri + )); + } } - $patternString = $pattern[0]; - $parameterConditions = $pattern; + $current .= $character; + } - unset($parameterConditions[0]); + if ($inParameter) { + throw new InvalidRoutePatternException(sprintf( + 'Invalid route uri: cannot contain \'{\' without closing \'}\', \'%s\' given', + $uri + )); + } elseif ($current !== '') { + $matches[] = [self::STATIC_PART, $current]; + } - return [$patternString, $parameterConditions]; + return $matchedParameter; + } + + /** + * Generate a segment regex. + * + * @param array $matches + * @param array $parameterPatterns + * + * @return string + */ + protected function generateRegex(array $matches, array $parameterPatterns): string + { + $regex = '/^'; + + foreach ($matches as $match) { + list($type, $part) = $match; + + if ($type === self::STATIC_PART) { + $regex .= preg_quote($part, '/'); + } else { + // Parameter, $part is the parameter name + $regex .= '(' . $parameterPatterns[$part] ?? Pattern::ANY . ')'; + } } - throw new InvalidRoutePatternException(sprintf( - 'Cannot add route: route pattern must be a pattern string or array, %s given', - gettype($pattern) - )); + $regex .= '$/'; + + return $regex; } } diff --git a/src/Viserio/Routing/Router.php b/src/Viserio/Routing/Router.php index 7e77cfe2d..d1b8ba822 100644 --- a/src/Viserio/Routing/Router.php +++ b/src/Viserio/Routing/Router.php @@ -159,7 +159,7 @@ protected function addRoute($methods, string $uri, $action): RouteContract */ protected function createRoute($methods, string $uri, $action): RouteContract { - $pattern = $this->parser->parse($uri); + $pattern = $this->parser->parse($uri, ['TODO']); $route = $this->newRoute( $methods, diff --git a/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php index a7379cba2..e9f953c10 100644 --- a/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php +++ b/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php @@ -2,52 +2,50 @@ declare(strict_types=1); namespace Viserio\Routing\Tests\Matchers; -use Viserio\Routing\{ - Pattern, - Matchers\RegexMatcher -}; +use Viserio\Routing\Matchers\RegexMatcher; +use Viserio\Contracts\Routing\Pattern; class RegexMatcherTest extends \PHPUnit_Framework_TestCase { public function testGetGroupCount() { - $matcher = new RegexMatcher(Pattern::ALPHA, 12); + $matcher = new RegexMatcher('/^(' . Pattern::ALPHA . ')$/', 12); $this->assertSame(1, $matcher->getGroupCount()); } public function testGetRegex() { - $matcher = new RegexMatcher(Pattern::ALPHA, 12); + $matcher = new RegexMatcher('/^(' . Pattern::ALPHA . ')$/', 12); - $this->assertSame(Pattern::asRegex(Pattern::ALPHA), $matcher->getRegex()); + $this->assertSame('/^(' . Pattern::ALPHA . ')$/', $matcher->getRegex()); } public function testGetParameterKeyGroupMap() { - $matcher = new RegexMatcher(Pattern::ALPHA, 12); + $matcher = new RegexMatcher('/^(' . Pattern::ALPHA . ')$/', 12); $this->assertSame([12 => 0], $matcher->getParameterKeyGroupMap()); } public function testGetConditionExpression() { - $matcher = new RegexMatcher(Pattern::ALPHA, 12); + $matcher = new RegexMatcher('/^(' . Pattern::ALPHA . ')$/', 12); $this->assertSame('preg_match(\'/^([a-zA-Z]+)$/\', test, $matches)', $matcher->getConditionExpression('test')); } public function testGetMatchedParameterExpressions() { - $matcher = new RegexMatcher(Pattern::ALPHA, 12); + $matcher = new RegexMatcher('/^(' . Pattern::ALPHA . ')$/', 12); $this->assertSame([12 => '$matches[1]'], $matcher->getMatchedParameterExpressions('test')); } public function testRegexMergingParameterKeys() { - $matcher1 = new RegexMatcher(Pattern::ANY, 12); - $matcher2 = new RegexMatcher(Pattern::ANY, 11); + $matcher1 = new RegexMatcher('/^(' . Pattern::ANY . ')$/', 12); + $matcher2 = new RegexMatcher('/^(' . Pattern::ANY . ')$/', 11); $matcher1->mergeParameterKeys($matcher2); $this->assertSame([12, 11], $matcher1->getParameterKeys()); diff --git a/src/Viserio/Routing/Tests/RouteParserTest.php b/src/Viserio/Routing/Tests/RouteParserTest.php index 2b18514d5..61875f3fe 100644 --- a/src/Viserio/Routing/Tests/RouteParserTest.php +++ b/src/Viserio/Routing/Tests/RouteParserTest.php @@ -2,18 +2,30 @@ declare(strict_types=1); namespace Viserio\Routing\Tests; -use Viserio\Routing\RouteParser; +use RuntimeException; +use Viserio\Routing\{ + RouteParser, + Matchers\StaticMatcher, + Matchers\ParameterMatcher +}; +use Viserio\Contracts\Routing\{ + Exceptions\InvalidRoutePatternException, + Pattern +}; class RouteParserTest extends \PHPUnit_Framework_TestCase { - // public function testParse($uri, array $expectedSegments) - // { - // $parser = new RouteParser(); + /** + * @dataProvider parsingProvider + */ + public function testParse(string $pattern, array $conditions, array $expectedSegments) + { + $parser = new RouteParser(); - // $this->assertEquals($expectedSegments, $parser->parse($uri)); - // } + $this->assertEquals($expectedSegments, $parser->parse($pattern, $conditions)); + } - public function parsingExamples() + public function parsingProvider() { return [ [ @@ -26,12 +38,136 @@ public function parsingExamples() // Empty route '/', [], - [new StaticSegment('')] + [new StaticMatcher('')] ], [ '/user', [], - [new StaticSegment('user')] + [new StaticMatcher('user')] + ], + [ + '/user/', + [], + [new StaticMatcher('user'), new StaticMatcher('')] + ], + [ + '/user/profile', + [], + [new StaticMatcher('user'), new StaticMatcher('profile')] + ], + [ + '/{parameter}', + [], + [new ParameterMatcher('parameter', RouteParser::DEFAULT_PARAMETER_PATTERN)] + ], + [ + '/{param}', + ['param' => Pattern::ALPHA_NUM], + [new ParameterMatcher('param', Pattern::ALPHA_NUM)] + ], + [ + '/user/{id}/profile/{type}', + ['id' => Pattern::DIGITS, 'type' => Pattern::ALPHA_LOWER], + [ + new StaticMatcher('user'), + new ParameterMatcher('id', Pattern::DIGITS), + new StaticMatcher('profile'), + new ParameterMatcher('type', Pattern::ALPHA_LOWER), + ] + ], + [ + '/prefix{param}', + ['param' => Pattern::ALPHA_NUM], + [new ParameterMatcher(['param'], '/^prefix(' . Pattern::ALPHA_NUM . ')$/')] + ], + [ + '/{param}suffix', + ['param' => Pattern::ALPHA_NUM], + [new ParameterMatcher(['param'], '/^(' . Pattern::ALPHA_NUM . ')suffix$/')] + ], + [ + '/abc{param1}:{param2}', + ['param1' => Pattern::ANY, 'param2' => Pattern::ALPHA], + [new ParameterMatcher(['param1', 'param2'], '/^abc(' . Pattern::ANY . ')\:(' . Pattern::ALPHA . ')$/')] + ], + [ + '/shop/{category}:{product}/buy/quantity:{quantity}', + ['category' => Pattern::ALPHA, 'product' => Pattern::ALPHA, 'quantity' => Pattern::DIGITS], + [ + new StaticMatcher('shop'), + new ParameterMatcher(['category', 'product'], '/^(' . Pattern::ALPHA . ')\:(' . Pattern::ALPHA . ')$/'), + new StaticMatcher('buy'), + new ParameterMatcher(['quantity'], '/^quantity\:(' . Pattern::DIGITS . ')$/'), + ] + ], + [ + '/{param:[0-9]+}', + [], + [new ParameterMatcher(['param'], '/^([0-9]+)$/'),] + ], + [ + '/{param:[\:]+}', + [], + [new ParameterMatcher(['param'], '/^([\:]+)$/'),] + ], + [ + // Inline regexps take precedence + '/{param:[a-z]+}', + ['param' => Pattern::ALPHA_UPPER], + [new ParameterMatcher(['param'], '/^([a-z]+)$/'),] + ], + [ + '/abc{param1:.+}:{param2:.+}', + [], + [new ParameterMatcher(['param1', 'param2'], '/^abc(.+)\:(.+)$/')] + ], + [ + '/shop/{category:[\w]+}:{product:[\w]+}/buy/quantity:{quantity:[0-9]+}', + [], + [ + new StaticMatcher('shop'), + new ParameterMatcher(['category', 'product'], '/^([\w]+)\:([\w]+)$/'), + new StaticMatcher('buy'), + new ParameterMatcher(['quantity'], '/^quantity\:([0-9]+)$/'), + ] + ], + ]; + } + + /** + * @dataProvider invalidParsingProvider + */ + public function testInvalidRouteParsing($uri, $expectedExceptionType) { + $this->setExpectedExceptionRegExp( + $expectedExceptionType ?: RuntimeException::class, + '/.*/' + ); + + (new RouteParser())->parse($uri, []); + } + + public function invalidParsingProvider() + { + return [ + [ + 'abc', + InvalidRoutePatternException::class, + ], + [ + '/test/{a/bc}', + InvalidRoutePatternException::class, + ], + [ + '/test/{a{bc}', + InvalidRoutePatternException::class, + ], + [ + '/test/{abc}}', + InvalidRoutePatternException::class, + ], + [ + '/test/{a{bc}}', + InvalidRoutePatternException::class, ], ]; } diff --git a/src/Viserio/Routing/Tests/RouteTest.php b/src/Viserio/Routing/Tests/RouteTest.php index fa4eef170..3206ae1b5 100644 --- a/src/Viserio/Routing/Tests/RouteTest.php +++ b/src/Viserio/Routing/Tests/RouteTest.php @@ -18,12 +18,12 @@ class RouteTest extends \PHPUnit_Framework_TestCase public function testBasicDispatchingOfRoutes() { $router = $this->getRouter(); - $router->get('/hello/{name}', function (Request $request, Response $response) { - $name = $request->getAttribute('name'); - $response->getBody()->write("Hello, $name"); + // $router->get('/hello/{name}', function (Request $request, Response $response) { + // $name = $request->getAttribute('name'); + // $response->getBody()->write("Hello, $name"); - return $response; - }); + // return $response; + // }); } public function testGetMethods() diff --git a/src/Viserio/Routing/VarExporter.php b/src/Viserio/Routing/VarExporter.php index 71cef7b6a..de6ae395c 100644 --- a/src/Viserio/Routing/VarExporter.php +++ b/src/Viserio/Routing/VarExporter.php @@ -53,6 +53,8 @@ public static function export($value): string /** * Don't instantiate this class. + * + * @codeCoverageIgnore */ private function __construct() { // From 7c5cab81e8f038dca10d1310a61b648e61eaab17 Mon Sep 17 00:00:00 2001 From: Scrutinizer Auto-Fixer Date: Mon, 15 Aug 2016 22:13:55 +0200 Subject: [PATCH 28/39] Scrutinizer Auto-Fixes (#339) This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com --- src/Viserio/Http/Stream/LazyOpenStream.php | 2 +- src/Viserio/Routing/Matchers/RegexMatcher.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Viserio/Http/Stream/LazyOpenStream.php b/src/Viserio/Http/Stream/LazyOpenStream.php index a27a151e1..7e0edccd9 100644 --- a/src/Viserio/Http/Stream/LazyOpenStream.php +++ b/src/Viserio/Http/Stream/LazyOpenStream.php @@ -3,8 +3,8 @@ namespace Viserio\Http\Stream; use Psr\Http\Message\StreamInterface; -use UnexpectedValueException; use Throwable; +use UnexpectedValueException; use Viserio\Http\Util; class LazyOpenStream implements StreamInterface diff --git a/src/Viserio/Routing/Matchers/RegexMatcher.php b/src/Viserio/Routing/Matchers/RegexMatcher.php index d495c400b..16d2c4bf1 100644 --- a/src/Viserio/Routing/Matchers/RegexMatcher.php +++ b/src/Viserio/Routing/Matchers/RegexMatcher.php @@ -2,8 +2,8 @@ declare(strict_types=1); namespace Viserio\Routing\Matchers; -use Viserio\Routing\VarExporter; use Viserio\Contracts\Routing\SegmentMatcher as SegmentMatcherContract; +use Viserio\Routing\VarExporter; class RegexMatcher extends AbstractMatcher { From a677e60c7f0d26b4ce164e001fff17599d9ea3a0 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Mon, 15 Aug 2016 22:17:45 +0200 Subject: [PATCH 29/39] cs fixes --- src/Viserio/Routing/Matchers/RegexMatcher.php | 2 +- src/Viserio/Routing/VarExporter.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Viserio/Routing/Matchers/RegexMatcher.php b/src/Viserio/Routing/Matchers/RegexMatcher.php index 16d2c4bf1..a844ba169 100644 --- a/src/Viserio/Routing/Matchers/RegexMatcher.php +++ b/src/Viserio/Routing/Matchers/RegexMatcher.php @@ -24,7 +24,7 @@ class RegexMatcher extends AbstractMatcher /** * Create a new regex segment matcher instance. * - * @param string $segment + * @param string $regex * @param int $parameterKeyGroupMap */ public function __construct(string $regex, int $parameterKeyGroupMap) diff --git a/src/Viserio/Routing/VarExporter.php b/src/Viserio/Routing/VarExporter.php index de6ae395c..a8e1b268f 100644 --- a/src/Viserio/Routing/VarExporter.php +++ b/src/Viserio/Routing/VarExporter.php @@ -9,7 +9,7 @@ class VarExporter /** * Converts the supplied value into a valid PHP representation. * - * @param mixed $value + * @param string|array|null|object $value * * @return string */ From 1d8abad5795049985d22a8dfe431ac81be5bc476 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Mon, 15 Aug 2016 23:42:29 +0200 Subject: [PATCH 30/39] added all tests for parser --- src/Viserio/Routing/RouteParser.php | 2 +- src/Viserio/Routing/Tests/RouteParserTest.php | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Viserio/Routing/RouteParser.php b/src/Viserio/Routing/RouteParser.php index e5b363d40..d43d93bfd 100644 --- a/src/Viserio/Routing/RouteParser.php +++ b/src/Viserio/Routing/RouteParser.php @@ -143,7 +143,7 @@ protected function generateRegex(array $matches, array $parameterPatterns): stri $regex .= preg_quote($part, '/'); } else { // Parameter, $part is the parameter name - $regex .= '(' . $parameterPatterns[$part] ?? Pattern::ANY . ')'; + $regex .= '(' . ($parameterPatterns[$part] ?? Pattern::ANY) . ')'; } } diff --git a/src/Viserio/Routing/Tests/RouteParserTest.php b/src/Viserio/Routing/Tests/RouteParserTest.php index 61875f3fe..b45b409d3 100644 --- a/src/Viserio/Routing/Tests/RouteParserTest.php +++ b/src/Viserio/Routing/Tests/RouteParserTest.php @@ -16,16 +16,16 @@ class RouteParserTest extends \PHPUnit_Framework_TestCase { /** - * @dataProvider parsingProvider + * @dataProvider routeParsingProvider */ - public function testParse(string $pattern, array $conditions, array $expectedSegments) + public function testRouteParser($pattern, array $conditions, array $expectedSegments) { $parser = new RouteParser(); $this->assertEquals($expectedSegments, $parser->parse($pattern, $conditions)); } - public function parsingProvider() + public function routeParsingProvider() { return [ [ @@ -58,21 +58,21 @@ public function parsingProvider() [ '/{parameter}', [], - [new ParameterMatcher('parameter', RouteParser::DEFAULT_PARAMETER_PATTERN)] + [new ParameterMatcher('parameter', '/^(' . Pattern::ANY. ')$/')] ], [ '/{param}', ['param' => Pattern::ALPHA_NUM], - [new ParameterMatcher('param', Pattern::ALPHA_NUM)] + [new ParameterMatcher('param', '/^(' . Pattern::ALPHA_NUM . ')$/')] ], [ '/user/{id}/profile/{type}', ['id' => Pattern::DIGITS, 'type' => Pattern::ALPHA_LOWER], [ new StaticMatcher('user'), - new ParameterMatcher('id', Pattern::DIGITS), + new ParameterMatcher('id', '/^(' . Pattern::DIGITS . ')$/'), new StaticMatcher('profile'), - new ParameterMatcher('type', Pattern::ALPHA_LOWER), + new ParameterMatcher('type', '/^(' . Pattern::ALPHA_LOWER . ')$/'), ] ], [ From 10c51d6d374afd73f641a54fec59f68e9ca60778 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Mon, 15 Aug 2016 23:54:06 +0200 Subject: [PATCH 31/39] Cs fixes --- src/Viserio/Routing/Route.php | 4 ++-- src/Viserio/Routing/RouteGroup.php | 4 ++-- src/Viserio/Routing/RouteParser.php | 10 +++++----- src/Viserio/Support/AbstractConnectionManager.php | 2 +- src/Viserio/Support/AbstractManager.php | 6 ++++-- src/Viserio/Support/Debug/Dumper.php | 6 ++++-- src/Viserio/Support/Invoker.php | 14 ++++++++------ src/Viserio/Support/composer.json | 4 ---- 8 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/Viserio/Routing/Route.php b/src/Viserio/Routing/Route.php index b86775bb2..ec7334154 100644 --- a/src/Viserio/Routing/Route.php +++ b/src/Viserio/Routing/Route.php @@ -10,7 +10,7 @@ Routing\Route as RouteContract, Routing\Router as RouterContract }; -use Viserio\Routing\Segments\ParameterSegment; +use Viserio\Routing\Matchers\ParameterMatcher; use Viserio\Support\{ Invoker, Str @@ -270,7 +270,7 @@ public function isStatic(): bool $this->getParameters(); foreach($this->parameters as $parameter) { - if ($parameter instanceof ParameterSegment) { + if ($parameter instanceof ParameterMatcher) { return false; } } diff --git a/src/Viserio/Routing/RouteGroup.php b/src/Viserio/Routing/RouteGroup.php index 2378f6d46..3f1390e34 100644 --- a/src/Viserio/Routing/RouteGroup.php +++ b/src/Viserio/Routing/RouteGroup.php @@ -28,9 +28,9 @@ class RouteGroup */ public function __construct(string $prefix, callable $callback, RouteCollection $collection) { - $this->callback = $callback; + $this->callback = $callback; $this->collection = $collection; - $this->prefix = sprintf('/%s', ltrim($prefix, '/')); + $this->prefix = sprintf('/%s', ltrim($prefix, '/')); } /** diff --git a/src/Viserio/Routing/RouteParser.php b/src/Viserio/Routing/RouteParser.php index d43d93bfd..0d0cca471 100644 --- a/src/Viserio/Routing/RouteParser.php +++ b/src/Viserio/Routing/RouteParser.php @@ -51,7 +51,7 @@ public function parse(string $route, array $conditions): array /** * Validate and match uri paramters. * - * @param string $pattern + * @param string $route * @param string $patternSegment * @param array &$conditions * @param array &$matches @@ -60,7 +60,7 @@ public function parse(string $route, array $conditions): array * @return bool */ protected function matchRouteParameters( - string $uri, + string $route, string $patternSegment, array &$conditions, array &$matches, @@ -91,7 +91,7 @@ protected function matchRouteParameters( } elseif ($character === '{') { throw new InvalidRoutePatternException(sprintf( 'Invalid route uri: cannot contain nested \'{\', \'%s\' given', - $uri + $route )); } } else { @@ -104,7 +104,7 @@ protected function matchRouteParameters( } elseif ($character === '}') { throw new InvalidRoutePatternException(sprintf( 'Invalid route uri: cannot contain \'}\' before opening \'{\', \'%s\' given', - $uri + $route )); } } @@ -115,7 +115,7 @@ protected function matchRouteParameters( if ($inParameter) { throw new InvalidRoutePatternException(sprintf( 'Invalid route uri: cannot contain \'{\' without closing \'}\', \'%s\' given', - $uri + $route )); } elseif ($current !== '') { $matches[] = [self::STATIC_PART, $current]; diff --git a/src/Viserio/Support/AbstractConnectionManager.php b/src/Viserio/Support/AbstractConnectionManager.php index 16451b7bb..06cfa0214 100644 --- a/src/Viserio/Support/AbstractConnectionManager.php +++ b/src/Viserio/Support/AbstractConnectionManager.php @@ -7,9 +7,9 @@ use InvalidArgumentException; use Viserio\Contracts\{ Config\Manager as ConfigContract, + Container\Traits\ContainerAwareTrait, Support\Connector as ConnectorContract }; -use Viserio\Contracts\Container\Traits\ContainerAwareTrait; abstract class AbstractConnectionManager { diff --git a/src/Viserio/Support/AbstractManager.php b/src/Viserio/Support/AbstractManager.php index 979a98a98..6e39def21 100644 --- a/src/Viserio/Support/AbstractManager.php +++ b/src/Viserio/Support/AbstractManager.php @@ -4,8 +4,10 @@ use Closure; use InvalidArgumentException; -use Viserio\Contracts\Config\Manager as ConfigContract; -use Viserio\Contracts\Container\Traits\ContainerAwareTrait; +use Viserio\Contracts\{ + Config\Manager as ConfigContract, + Container\Traits\ContainerAwareTrait +}; abstract class AbstractManager { diff --git a/src/Viserio/Support/Debug/Dumper.php b/src/Viserio/Support/Debug/Dumper.php index 73c2d93c4..b70fbdd3e 100644 --- a/src/Viserio/Support/Debug/Dumper.php +++ b/src/Viserio/Support/Debug/Dumper.php @@ -2,8 +2,10 @@ declare(strict_types=1); namespace Viserio\Support\Debug; -use Symfony\Component\VarDumper\Cloner\VarCloner; -use Symfony\Component\VarDumper\Dumper\CliDumper; +use Symfony\Component\VarDumper\{ + Cloner\VarCloner, + Dumper\CliDumper +}; /** * @codeCoverageIgnore diff --git a/src/Viserio/Support/Invoker.php b/src/Viserio/Support/Invoker.php index 05c2386bb..441f4e2a9 100644 --- a/src/Viserio/Support/Invoker.php +++ b/src/Viserio/Support/Invoker.php @@ -4,12 +4,14 @@ use Invoker\Invoker as DiInvoker; use Invoker\InvokerInterface; -use Invoker\ParameterResolver\AssociativeArrayResolver; -use Invoker\ParameterResolver\Container\ParameterNameContainerResolver; -use Invoker\ParameterResolver\Container\TypeHintContainerResolver; -use Invoker\ParameterResolver\DefaultValueResolver; -use Invoker\ParameterResolver\NumericArrayResolver; -use Invoker\ParameterResolver\ResolverChain; +use Invoker\ParameterResolver\{ + AssociativeArrayResolver, + Container\ParameterNameContainerResolver, + Container\TypeHintContainerResolver, + DefaultValueResolver, + NumericArrayResolver, + ResolverChain +}; use Viserio\Contracts\Container\Traits\ContainerAwareTrait; class Invoker implements InvokerInterface diff --git a/src/Viserio/Support/composer.json b/src/Viserio/Support/composer.json index 51dd1a913..1bd801830 100644 --- a/src/Viserio/Support/composer.json +++ b/src/Viserio/Support/composer.json @@ -19,7 +19,6 @@ ], "require": { "php" : "7.0.0 - 7.0.5 || ^7.0.7", - "psr/log" : "^1.0", "viserio/contracts" : "self.version" }, "require-dev": { @@ -41,9 +40,6 @@ "Viserio\\Support\\Tests\\" : "Tests/" } }, - "provide": { - "psr/log-implementation" : "~1.0" - }, "extra": { "branch-alias": { "dev-master" : "1.0-dev" From 16bfc95f02d3fd1ee2d8fedd5bdeb664df515bda Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Mon, 15 Aug 2016 23:56:44 +0200 Subject: [PATCH 32/39] remove need for str class --- src/Viserio/Routing/Route.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Viserio/Routing/Route.php b/src/Viserio/Routing/Route.php index ec7334154..8fddcb397 100644 --- a/src/Viserio/Routing/Route.php +++ b/src/Viserio/Routing/Route.php @@ -11,10 +11,7 @@ Routing\Router as RouterContract }; use Viserio\Routing\Matchers\ParameterMatcher; -use Viserio\Support\{ - Invoker, - Str -}; +use Viserio\Support\Invoker; class Route implements RouteContract { @@ -364,7 +361,7 @@ protected function parseAction($action): array }); } - if (is_string($action['uses']) && ! Str::contains($action['uses'], '::')) { + if (is_string($action['uses']) && strpos($action['uses'], '::') === false) { if (! method_exists($action, '__invoke')) { throw new UnexpectedValueException(sprintf( 'Invalid route action: [%s]', From db9c7b56e6d32693e62724bb16dba104b02b3299 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Tue, 16 Aug 2016 00:00:24 +0200 Subject: [PATCH 33/39] Support command aliases --- src/Viserio/Console/Application.php | 4 +++- src/Viserio/Console/Tests/ApplicationTest.php | 24 +++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Viserio/Console/Application.php b/src/Viserio/Console/Application.php index ce7df474e..11f63c167 100644 --- a/src/Viserio/Console/Application.php +++ b/src/Viserio/Console/Application.php @@ -105,10 +105,11 @@ public function add(SymfonyCommand $command) * @param callable|string|array $callable Called when the command is called. * When using a container, this can be a "pseudo-callable" * i.e. the name of the container entry to invoke. + * @param array $aliases An array of aliases for the command. * * @return SymfonyCommand */ - public function command(string $expression, $callable): SymfonyCommand + public function command(string $expression, $callable, array $aliases = []): SymfonyCommand { $commandFunction = function (InputInterface $input, OutputInterface $output) use ($callable) { $parameters = array_merge( @@ -132,6 +133,7 @@ public function command(string $expression, $callable): SymfonyCommand }; $command = $this->createCommand($expression, $commandFunction); + $command->setAliases($aliases); $this->add($command); diff --git a/src/Viserio/Console/Tests/ApplicationTest.php b/src/Viserio/Console/Tests/ApplicationTest.php index b254f556b..0b0d6869d 100644 --- a/src/Viserio/Console/Tests/ApplicationTest.php +++ b/src/Viserio/Console/Tests/ApplicationTest.php @@ -4,7 +4,7 @@ use Mockery as Mock; use Narrowspark\TestingHelper\ArrayContainer; -use stdClass; +use StdClass; use Symfony\Component\Console\Input\StringInput; use Symfony\Component\Console\Output\OutputInterface; use Viserio\Console\Application; @@ -20,9 +20,9 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase public function setUp() { - $stdClass = new stdClass(); + $stdClass = new StdClass(); $stdClass->foo = 'hello'; - $stdClass2 = new stdClass(); + $stdClass2 = new StdClass(); $stdClass2->foo = 'nope!'; $container = new ArrayContainer([ @@ -139,7 +139,7 @@ public function testItShouldRunACommandWitMultipleOptions() public function testItShouldInjectTypeHintInPriority() { - $this->application->command('greet', function (OutputInterface $output, stdClass $param) { + $this->application->command('greet', function (OutputInterface $output, StdClass $param) { $output->write($param->foo); }); @@ -149,20 +149,23 @@ public function testItShouldInjectTypeHintInPriority() public function testItCanResolveCallableStringFromContainer() { $this->application->command('greet', 'command.greet'); + $this->assertOutputIs('greet', 'hello'); } public function testItCanResolveCallableArrayFromContainer() { $this->application->command('greet', 'command.arr.greet'); + $this->assertOutputIs('greet', 'hello'); } public function testItcanInjectUsingTypeHints() { - $this->application->command('greet', function (OutputInterface $output, stdClass $stdClass) { + $this->application->command('greet', function (OutputInterface $output, StdClass $stdClass) { $output->write($stdClass->foo); }); + $this->assertOutputIs('greet', 'hello'); } @@ -171,6 +174,7 @@ public function testItCanInjectUsingParameterNames() $this->application->command('greet', function (OutputInterface $output, $stdClass) { $output->write($stdClass->foo); }); + $this->assertOutputIs('greet', 'hello'); } @@ -182,9 +186,19 @@ public function testItShouldThrowIfAParameterCannotBeResolved() { $this->application->command('greet', function ($fbo) { }); + $this->assertOutputIs('greet', ''); } + public function testRunsACommandViaItsAliasAndReturnsExitCode() + { + $this->application->command('foo', function () { + return 1; + }, ['bar']); + + $this->assertSame(1, $this->application->runCommand('bar')); + } + /** * Fixture method. * From 781aca4f0ff88fc34a2db52f941d93423ce8bb26 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Tue, 16 Aug 2016 00:04:39 +0200 Subject: [PATCH 34/39] update dispatcher --- src/Viserio/Contracts/Routing/Dispatcher.php | 12 ++++++++++++ src/Viserio/Routing/Dispatcher.php | 6 +++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Viserio/Contracts/Routing/Dispatcher.php b/src/Viserio/Contracts/Routing/Dispatcher.php index 9a43f2e13..7c7bb5f85 100644 --- a/src/Viserio/Contracts/Routing/Dispatcher.php +++ b/src/Viserio/Contracts/Routing/Dispatcher.php @@ -2,6 +2,8 @@ declare(strict_types=1); namespace Viserio\Contracts\Routing; +use Psr\Http\Message\ServerRequestInterface; + interface Dispatcher { const NOT_FOUND = 0; @@ -10,6 +12,16 @@ interface Dispatcher const METHOD_NOT_ALLOWED = 2; + /** + * Match and dispatch a route matching the given http method and + * uri, retruning an execution chain. + * + * @param \Psr\Http\Message\ServerRequestInterface $request + * + * @return mixed + */ + public function handle(ServerRequestInterface $request); + /** * Constructs a match results object from the supplied array. * The expected format is one of: diff --git a/src/Viserio/Routing/Dispatcher.php b/src/Viserio/Routing/Dispatcher.php index 967d3cfac..b88cf5090 100644 --- a/src/Viserio/Routing/Dispatcher.php +++ b/src/Viserio/Routing/Dispatcher.php @@ -40,13 +40,13 @@ public function handle(ServerRequestInterface $request) ); switch ($match[0]) { - case MatchResult::NOT_FOUND: + case DispatcherContract::NOT_FOUND: // 404 Not Found... break; - case MatchResult::HTTP_METHOD_NOT_ALLOWED: + case DispatcherContract::HTTP_METHOD_NOT_ALLOWED: // 405 Method Not Allowed... break; - case MatchResult::FOUND: + case DispatcherContract::FOUND: // Matched route, dispatch to associated handler... break; } From 8695267b064972cdc6e3ae14fc776bdcd3dc28f1 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Tue, 16 Aug 2016 19:04:30 +0200 Subject: [PATCH 35/39] improve Console --- src/Viserio/Console/Application.php | 50 ++++++++------ .../Console/Command/ExpressionParser.php | 8 ++- src/Viserio/Console/Tests/ApplicationTest.php | 33 ++++++--- .../Console/Tests/Command/CommandTest.php | 68 +++++++------------ .../Tests/Command/ExpressionParserTest.php | 10 +-- .../Console/Tests/Fixture/SpyOutput.php | 6 +- .../Console/Tests/Fixture/ViserioCommand.php | 6 +- .../Tests/Fixture/ViserioSecCommand.php | 6 +- src/Viserio/Console/composer.json | 1 + .../InvalidCommandExpression.php | 2 +- 10 files changed, 104 insertions(+), 86 deletions(-) rename src/Viserio/Contracts/Console/{ => Exceptions}/InvalidCommandExpression.php (73%) diff --git a/src/Viserio/Console/Application.php b/src/Viserio/Console/Application.php index 11f63c167..8f2b14a11 100644 --- a/src/Viserio/Console/Application.php +++ b/src/Viserio/Console/Application.php @@ -2,20 +2,27 @@ declare(strict_types=1); namespace Viserio\Console; +use Closure; use Interop\Container\ContainerInterface as ContainerContract; use Invoker\Exception\InvocationException; use RuntimeException; -use Symfony\Component\Console\Application as SymfonyConsole; -use Symfony\Component\Console\Command\Command as SymfonyCommand; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Viserio\Console\Command\Command as ViserioCommand; -use Viserio\Console\Command\ExpressionParser as Parser; -use Viserio\Console\Input\InputOption; -use Viserio\Contracts\Console\Application as ApplicationContract; +use Symfony\Component\Console\{ + Application as SymfonyConsole, + Command\Command as SymfonyCommand, + Input\InputDefinition, + Input\InputInterface, + Output\OutputInterface +}; +use Viserio\Console\{ + Command\Command as ViserioCommand, + Command\ExpressionParser as Parser, + Input\InputOption +}; +use Viserio\Contracts\{ + Console\Application as ApplicationContract, + Container\Traits\ContainerAwareTrait +}; use Viserio\Support\Invoker; -use Viserio\Contracts\Container\Traits\ContainerAwareTrait; class Application extends SymfonyConsole implements ApplicationContract { @@ -59,9 +66,9 @@ class Application extends SymfonyConsole implements ApplicationContract /** * Create a new Cerebro console application. * - * @param ContainerContract $container - * @param string $version - * @param string $name + * @param \Interop\Container\ContainerInterface $container + * @param string $version + * @param string $name */ public function __construct( ContainerContract $container, @@ -70,15 +77,14 @@ public function __construct( ) { $this->name = $name; $this->version = $version; - - $this->setContainer($container); + $this->container = $container; $this->expressionParser = new Parser(); - $this->initInvoker(); + $this->initInvoker(); $this->setAutoExit(false); $this->setCatchExceptions(false); - parent::__construct($this->getName(), $this->getVersion()); + parent::__construct($name, $version); } /** @@ -121,14 +127,18 @@ public function command(string $expression, $callable, array $aliases = []): Sym $input->getOptions() ); + if ($callable instanceof Closure) { + $callable = $callable->bindTo($this, $this); + } + try { $this->getInvoker()->call($callable, $parameters); - } catch (InvocationException $e) { + } catch (InvocationException $exception) { throw new RuntimeException(sprintf( "Impossible to call the '%s' command: %s", $input->getFirstArgument(), - $e->getMessage() - ), 0, $e); + $exception->getMessage() + ), 0, $exception); } }; diff --git a/src/Viserio/Console/Command/ExpressionParser.php b/src/Viserio/Console/Command/ExpressionParser.php index cae7939a2..dc6713865 100644 --- a/src/Viserio/Console/Command/ExpressionParser.php +++ b/src/Viserio/Console/Command/ExpressionParser.php @@ -2,9 +2,11 @@ declare(strict_types=1); namespace Viserio\Console\Command; -use Viserio\Console\Input\InputArgument; -use Viserio\Console\Input\InputOption; -use Viserio\Contracts\Console\InvalidCommandExpression; +use Viserio\Console\Input\{ + InputArgument, + InputOption +}; +use Viserio\Contracts\Console\Exceptions\InvalidCommandExpression; use Viserio\Support\Str; class ExpressionParser diff --git a/src/Viserio/Console/Tests/ApplicationTest.php b/src/Viserio/Console/Tests/ApplicationTest.php index 0b0d6869d..93e679f0a 100644 --- a/src/Viserio/Console/Tests/ApplicationTest.php +++ b/src/Viserio/Console/Tests/ApplicationTest.php @@ -5,11 +5,15 @@ use Mockery as Mock; use Narrowspark\TestingHelper\ArrayContainer; use StdClass; -use Symfony\Component\Console\Input\StringInput; -use Symfony\Component\Console\Output\OutputInterface; -use Viserio\Console\Application; -use Viserio\Console\Tests\Fixture\SpyOutput; -use Viserio\Console\Tests\Fixture\ViserioCommand; +use Symfony\Component\Console\{ + Input\StringInput, + Output\OutputInterface +}; +use Viserio\Console\{ + Application, + Tests\Fixture\SpyOutput, + Tests\Fixture\ViserioCommand +}; class ApplicationTest extends \PHPUnit_Framework_TestCase { @@ -192,11 +196,23 @@ public function testItShouldThrowIfAParameterCannotBeResolved() public function testRunsACommandViaItsAliasAndReturnsExitCode() { - $this->application->command('foo', function () { - return 1; + $this->application->command('foo', function ($output) { + $output->write(1); }, ['bar']); - $this->assertSame(1, $this->application->runCommand('bar')); + $this->assertOutputIs('bar', 1); + } + + public function testitShouldRunACommandInTheScopeOfTheApplication() + { + $whatIsThis = null; + + $this->application->command('foo', function () use (&$whatIsThis) { + $whatIsThis = $this; + }); + + $this->assertOutputIs('foo', ''); + $this->assertSame($this->application, $whatIsThis); } /** @@ -218,6 +234,7 @@ private function assertOutputIs($command, $expected) $output = new SpyOutput(); $this->application->run(new StringInput($command), $output); + $this->assertEquals($expected, $output->output); } } diff --git a/src/Viserio/Console/Tests/Command/CommandTest.php b/src/Viserio/Console/Tests/Command/CommandTest.php index a55209607..a7c4b7de8 100644 --- a/src/Viserio/Console/Tests/Command/CommandTest.php +++ b/src/Viserio/Console/Tests/Command/CommandTest.php @@ -2,17 +2,26 @@ declare(strict_types=1); namespace Viserio\Console\Tests\Command; -use Mockery as Mock; -use Narrowspark\TestingHelper\ArrayContainer; -use Symfony\Component\Console\Input\StringInput; -use Symfony\Component\Console\Output\NullOutput; -use Symfony\Component\Console\Output\OutputInterface; -use Viserio\Console\Application; -use Viserio\Console\Tests\Fixture\ViserioSecCommand as ViserioCommand; +use Narrowspark\TestingHelper\{ + ArrayContainer, + Traits\MockeryTrait +}; +use Symfony\Component\Console\{ + Input\StringInput, + Output\NullOutput, + Output\OutputInterface +}; +use Viserio\Console\{ + Application, + Tests\Fixture\ViserioCommand, + Tests\Fixture\ViserioSecCommand +}; use Viserio\Support\Invoker; class CommandTest extends \PHPUnit_Framework_TestCase { + use MockeryTrait; + /** * @var Application */ @@ -25,6 +34,8 @@ class CommandTest extends \PHPUnit_Framework_TestCase public function setUp() { + parent::setUp(); + $container = new ArrayContainer([ 'foo' => function (OutputInterface $output) { $output->write('hello'); @@ -39,62 +50,31 @@ public function setUp() ->setContainer($this->application->getContainer()); } - public function tearDown() - { - Mock::close(); - } - public function testGetNormalVerbosity() { - $command = new ViserioCommand(); + $command = new ViserioSecCommand(); $this->assertSame(32, $command->getVerbosity()); } public function testGetVerbosityLevelFromCommand() { - $command = new ViserioCommand(); + $command = new ViserioSecCommand(); $this->assertSame(128, $command->getVerbosity(128)); - $command = new ViserioCommand(); + $command = new ViserioSecCommand(); $this->assertSame(128, $command->getVerbosity('vv')); } public function testSetVerbosityLevelToCommand() { - $command = new ViserioCommand(); + $command = new ViserioSecCommand(); $command->setVerbosity(256); $this->assertSame(256, $command->getVerbosity()); } - // @TODO finish test. - // public function testCallAnotherConsoleCommand() - // { - // $container = new MockContainer(); - // $events = Mock::mock('Viserio\Contracts\Events\Dispatcher', ['addListener' => null]); - // - // $application = new Application($container, $events, '1.0.0'); - // $application->command('foo', function (OutputInterface $output) { - // $output->write('hello'); - // }); - // - // $command = new ViserioCommand(); - // $command->setApplication($application); - // $command->setInvoker( - // (new Invoker()) - // ->injectByTypeHint(true) - // ->injectByParameterName(true) - // ->setContainer($application->getContainer()) - // ); - // $command->run(new StringInput(''), new NullOutput()); - // - // $tester = new CommandTester($command); - // - // $this->assertSame($application->get('foo'), $command->call('foo')); - // } - public function testGetOptionFromCommand() { - $command = new ViserioCommand(); + $command = new ViserioSecCommand(); $command->setApplication($this->application); $command->setInvoker($this->invoker); @@ -106,7 +86,7 @@ public function testGetOptionFromCommand() public function testGetArgumentFromCommand() { - $command = new ViserioCommand(); + $command = new ViserioSecCommand(); $command->setApplication($this->application); $command->setInvoker($this->invoker); diff --git a/src/Viserio/Console/Tests/Command/ExpressionParserTest.php b/src/Viserio/Console/Tests/Command/ExpressionParserTest.php index 367d83a49..1317c0460 100644 --- a/src/Viserio/Console/Tests/Command/ExpressionParserTest.php +++ b/src/Viserio/Console/Tests/Command/ExpressionParserTest.php @@ -3,8 +3,10 @@ namespace Viserio\Console\Tests\Command; use Viserio\Console\Command\ExpressionParser; -use Viserio\Console\Input\InputArgument; -use Viserio\Console\Input\InputOption; +use Viserio\Console\Input\{ + InputArgument, + InputOption +}; class ExpressionParserTest extends \PHPUnit_Framework_TestCase { @@ -119,7 +121,7 @@ public function testItParsesOptionsWithShortcuts() } /** - * @expectedException \Viserio\Contracts\Console\InvalidCommandExpression + * @expectedException \Viserio\Contracts\Console\Exceptions\InvalidCommandExpression * @expectedExceptionMessage An option must be enclosed by brackets: [--option] */ public function testItProvidesAnErrorMessageOnOptionsMissingBrackets() @@ -129,7 +131,7 @@ public function testItProvidesAnErrorMessageOnOptionsMissingBrackets() } /** - * @expectedException \Viserio\Contracts\Console\InvalidCommandExpression + * @expectedException \Viserio\Contracts\Console\Exceptions\InvalidCommandExpression * @expectedExceptionMessage The expression was empty */ public function testItProvidesAnErrorMessageOnEmpty() diff --git a/src/Viserio/Console/Tests/Fixture/SpyOutput.php b/src/Viserio/Console/Tests/Fixture/SpyOutput.php index f4c358fe1..8e75a342e 100644 --- a/src/Viserio/Console/Tests/Fixture/SpyOutput.php +++ b/src/Viserio/Console/Tests/Fixture/SpyOutput.php @@ -2,8 +2,10 @@ declare(strict_types=1); namespace Viserio\Console\Tests\Fixture; -use Symfony\Component\Console\Output\Output; -use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\{ + Output, + OutputInterface +}; class SpyOutput extends Output implements OutputInterface { diff --git a/src/Viserio/Console/Tests/Fixture/ViserioCommand.php b/src/Viserio/Console/Tests/Fixture/ViserioCommand.php index 3c4280442..46bda1ba9 100644 --- a/src/Viserio/Console/Tests/Fixture/ViserioCommand.php +++ b/src/Viserio/Console/Tests/Fixture/ViserioCommand.php @@ -2,8 +2,10 @@ declare(strict_types=1); namespace Viserio\Console\Tests\Fixture; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\{ + InputArgument, + InputOption +}; use Viserio\Console\Command\Command; class ViserioCommand extends Command diff --git a/src/Viserio/Console/Tests/Fixture/ViserioSecCommand.php b/src/Viserio/Console/Tests/Fixture/ViserioSecCommand.php index 09df1c75f..8ef17aae1 100644 --- a/src/Viserio/Console/Tests/Fixture/ViserioSecCommand.php +++ b/src/Viserio/Console/Tests/Fixture/ViserioSecCommand.php @@ -2,8 +2,10 @@ declare(strict_types=1); namespace Viserio\Console\Tests\Fixture; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\{ + InputArgument, + InputOption +}; use Viserio\Console\Command\Command; class ViserioSecCommand extends Command diff --git a/src/Viserio/Console/composer.json b/src/Viserio/Console/composer.json index 1a51afdda..8014e36cd 100644 --- a/src/Viserio/Console/composer.json +++ b/src/Viserio/Console/composer.json @@ -20,6 +20,7 @@ "require": { "php" : "7.0.0 - 7.0.5 || ^7.0.7", "container-interop/container-interop" : "^1.1", + "php-di/invoker" : "^1.3", "stecman/symfony-console-completion" : "^0.6", "symfony/console" : "^3.1", "viserio/cotracts" : "self.version", diff --git a/src/Viserio/Contracts/Console/InvalidCommandExpression.php b/src/Viserio/Contracts/Console/Exceptions/InvalidCommandExpression.php similarity index 73% rename from src/Viserio/Contracts/Console/InvalidCommandExpression.php rename to src/Viserio/Contracts/Console/Exceptions/InvalidCommandExpression.php index 6b95fd735..ef0867e0f 100644 --- a/src/Viserio/Contracts/Console/InvalidCommandExpression.php +++ b/src/Viserio/Contracts/Console/Exceptions/InvalidCommandExpression.php @@ -1,6 +1,6 @@ Date: Tue, 16 Aug 2016 19:17:30 +0200 Subject: [PATCH 36/39] Scrutinizer Auto-Fixes (#342) This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com --- src/Viserio/Console/Application.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Viserio/Console/Application.php b/src/Viserio/Console/Application.php index 8f2b14a11..3aacdde75 100644 --- a/src/Viserio/Console/Application.php +++ b/src/Viserio/Console/Application.php @@ -92,7 +92,7 @@ public function __construct( * * @param \Symfony\Component\Console\Command\Command $command * - * @return SymfonyCommand|null + * @return null|\Symfony\Component\Console\Command\Command */ public function add(SymfonyCommand $command) { @@ -113,7 +113,7 @@ public function add(SymfonyCommand $command) * i.e. the name of the container entry to invoke. * @param array $aliases An array of aliases for the command. * - * @return SymfonyCommand + * @return \Symfony\Component\Console\Command\Command */ public function command(string $expression, $callable, array $aliases = []): SymfonyCommand { @@ -220,7 +220,7 @@ protected function getEnvironmentOption(): InputOption * @param string $expression * @param callable $callable * - * @return SymfonyCommand + * @return \Symfony\Component\Console\Command\Command */ protected function createCommand(string $expression, callable $callable): SymfonyCommand { From e246d3209ee1c4cc5595da1d0905c4a424e8dd20 Mon Sep 17 00:00:00 2001 From: Scrutinizer Auto-Fixer Date: Tue, 16 Aug 2016 19:40:45 +0200 Subject: [PATCH 37/39] Scrutinizer Auto-Fixes (#343) This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com --- src/Viserio/Cache/CacheManager.php | 1 - src/Viserio/Connect/ConnectManager.php | 2 +- src/Viserio/Container/Container.php | 1 - .../Contracts/Exception/Exception/FlattenException.php | 2 +- src/Viserio/Contracts/Exception/Handler.php | 5 +++++ src/Viserio/Contracts/Mail/GPGMailer.php | 2 +- src/Viserio/Contracts/StaticalProxy/AliasLoader.php | 8 ++++++++ src/Viserio/Contracts/View/Virtuoso.php | 6 ++++++ src/Viserio/Mail/Mailer.php | 3 +-- src/Viserio/Mail/Transport/Mandrill.php | 2 +- src/Viserio/Mail/Transport/Postmark.php | 6 +++--- src/Viserio/Mail/Transport/Ses.php | 2 +- src/Viserio/Mail/Transport/SparkPost.php | 2 +- src/Viserio/Mail/TransportManager.php | 7 +++---- src/Viserio/Queue/Connectors/SyncQueue.php | 2 +- src/Viserio/StaticalProxy/StaticalProxy.php | 4 ++-- 16 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/Viserio/Cache/CacheManager.php b/src/Viserio/Cache/CacheManager.php index 42c78afad..2a20f1070 100644 --- a/src/Viserio/Cache/CacheManager.php +++ b/src/Viserio/Cache/CacheManager.php @@ -23,7 +23,6 @@ use Memcached; use MongoDB\Driver\Manager as MongoDBManager; use Predis\Client as PredisClient; -use Psr\Cache\CacheItemPoolInterface; use Redis; use Viserio\{ Filesystem\FilesystemManager, diff --git a/src/Viserio/Connect/ConnectManager.php b/src/Viserio/Connect/ConnectManager.php index 0c77829f8..537028658 100644 --- a/src/Viserio/Connect/ConnectManager.php +++ b/src/Viserio/Connect/ConnectManager.php @@ -106,7 +106,7 @@ protected function createMariadbConnection(array $config): PDO * * @param array $config * - * @return \Mongo|\MongoClient|\MongoDB\Client + * @return PDO */ protected function createMongoConnection(array $config): PDO { diff --git a/src/Viserio/Container/Container.php b/src/Viserio/Container/Container.php index 42d1e9e27..179aa8977 100644 --- a/src/Viserio/Container/Container.php +++ b/src/Viserio/Container/Container.php @@ -3,7 +3,6 @@ namespace Viserio\Container; use Interop\Container\ContainerInterface as ContainerInteropInterface; -use Nucleus\Invoker\Invoker; use Viserio\Container\Exception\BindingResolutionException; use Viserio\Container\Exception\ContainerException; use Viserio\Container\Exception\NotFoundException; diff --git a/src/Viserio/Contracts/Exception/Exception/FlattenException.php b/src/Viserio/Contracts/Exception/Exception/FlattenException.php index 0053fb8c9..5117ec760 100644 --- a/src/Viserio/Contracts/Exception/Exception/FlattenException.php +++ b/src/Viserio/Contracts/Exception/Exception/FlattenException.php @@ -2,9 +2,9 @@ declare(strict_types=1); namespace Viserio\Contracts\Exception\Exception; -use __PHP_Incomplete_Class; use ArrayObject; use Throwable; +use __PHP_Incomplete_Class; /** * @author Fabien Potencier diff --git a/src/Viserio/Contracts/Exception/Handler.php b/src/Viserio/Contracts/Exception/Handler.php index ff962dc00..6e3cc6c8c 100644 --- a/src/Viserio/Contracts/Exception/Handler.php +++ b/src/Viserio/Contracts/Exception/Handler.php @@ -74,11 +74,13 @@ public function addShouldntReport(Throwable $exception): Handler; /** * Register the exception / Error handlers for the application. + * @return void */ public function register(); /** * Unregister the PHP error handler. + * @return void */ public function unregister(); @@ -96,6 +98,7 @@ public function unregister(); * @param null $context * * @throws \ErrorException + * @return void */ public function handleError( int $level, @@ -113,11 +116,13 @@ public function handleError( * be handled differently since they are not normal exceptions. * * @param \Throwable $exception + * @return null|string */ public function handleException(Throwable $exception); /** * Handle the PHP shutdown event. + * @return void */ public function handleShutdown(); } diff --git a/src/Viserio/Contracts/Mail/GPGMailer.php b/src/Viserio/Contracts/Mail/GPGMailer.php index 64fed0573..3cab9304d 100644 --- a/src/Viserio/Contracts/Mail/GPGMailer.php +++ b/src/Viserio/Contracts/Mail/GPGMailer.php @@ -81,7 +81,7 @@ public function sign($text): string; * * @throws \Exception * - * @return string + * @return boolean */ public function verify($text, string $fingerprint): bool; } diff --git a/src/Viserio/Contracts/StaticalProxy/AliasLoader.php b/src/Viserio/Contracts/StaticalProxy/AliasLoader.php index d9db0b9c4..e2ac46886 100644 --- a/src/Viserio/Contracts/StaticalProxy/AliasLoader.php +++ b/src/Viserio/Contracts/StaticalProxy/AliasLoader.php @@ -25,6 +25,7 @@ public function load(string $alias): bool; /** * Removes an alias. + * @return void */ public function removeAlias(); @@ -42,6 +43,7 @@ public function resolveAlias(string $alias); * * @param string|string[] $patterns * @param string|null $translation + * @return void */ public function aliasPattern($patterns, string $translation = null); @@ -50,6 +52,7 @@ public function aliasPattern($patterns, string $translation = null); * * @param string $pattern * @param string|null $translation + * @return void */ public function removeAliasPattern(string $pattern, string $translation = null); @@ -58,6 +61,7 @@ public function removeAliasPattern(string $pattern, string $translation = null); * * @param string $class * @param string $alias + * @return void */ public function aliasNamespace(string $class, string $alias); @@ -72,11 +76,13 @@ public function resolveNamespaceAlias(string $alias); /** * Removes a namespace alias. + * @return void */ public function removeNamespaceAlias(); /** * Register the loader on the auto-loader stack. + * @return void */ public function register(); @@ -89,6 +95,7 @@ public function isRegistered(): bool; /** * Unregisters the autoloader function. + * @return void */ public function unregister(); @@ -96,6 +103,7 @@ public function unregister(); * Set the registered aliases. * * @param array $aliases + * @return void */ public function setAliases(array $aliases); diff --git a/src/Viserio/Contracts/View/Virtuoso.php b/src/Viserio/Contracts/View/Virtuoso.php index 9dd2e3060..cf7e5181a 100644 --- a/src/Viserio/Contracts/View/Virtuoso.php +++ b/src/Viserio/Contracts/View/Virtuoso.php @@ -83,6 +83,7 @@ public function yieldContent(string $section, string $default = ''): string; * * @param string $section * @param string $content + * @return void */ public function startSection(string $section, string $content = ''); @@ -91,6 +92,7 @@ public function startSection(string $section, string $content = ''); * * @param string $section * @param string $content + * @return void */ public function inject(string $section, string $content); @@ -116,21 +118,25 @@ public function appendSection(): string; /** * Clear all of the section contents. + * @return void */ public function clearSections(); /** * Clear all of the section contents if done rendering. + * @return void */ public function clearSectionsIfDoneRendering(); /** * Increment the rendering counter. + * @return void */ public function incrementRender(); /** * Decrement the rendering counter. + * @return void */ public function decrementRender(); diff --git a/src/Viserio/Mail/Mailer.php b/src/Viserio/Mail/Mailer.php index 978d4722c..c76e70253 100644 --- a/src/Viserio/Mail/Mailer.php +++ b/src/Viserio/Mail/Mailer.php @@ -3,12 +3,11 @@ namespace Viserio\Mail; use Closure; -use Exception; use InvalidArgumentException; use Narrowspark\Arr\StaticArr as Arr; use Swift_Mailer; -use Swift_Mime_Message; use Swift_Message; +use Swift_Mime_Message; use Viserio\Contracts\{ Events\Traits\EventsAwareTrait, Mail\Mailer as MailerContract, diff --git a/src/Viserio/Mail/Transport/Mandrill.php b/src/Viserio/Mail/Transport/Mandrill.php index 3ea434c93..37ca3a90a 100644 --- a/src/Viserio/Mail/Transport/Mandrill.php +++ b/src/Viserio/Mail/Transport/Mandrill.php @@ -69,7 +69,7 @@ public function getKey(): string * * @param string $key * - * @return string + * @return Mandrill */ public function setKey(string $key): Mandrill { diff --git a/src/Viserio/Mail/Transport/Postmark.php b/src/Viserio/Mail/Transport/Postmark.php index fd57a7b45..6eb364c86 100644 --- a/src/Viserio/Mail/Transport/Postmark.php +++ b/src/Viserio/Mail/Transport/Postmark.php @@ -77,7 +77,7 @@ public function getServerToken(): string * * @param string $serverToken * - * @return string + * @return Postmark */ public function setServerToken(string $serverToken): Postmark { @@ -90,7 +90,7 @@ public function setServerToken(string $serverToken): Postmark * Convert email dictionary with emails and names * to array of emails with names. * - * @param array $emails + * @param string[] $emails * * @return array */ @@ -115,7 +115,7 @@ protected function convertEmailsArray(array $emails): array * @param Swift_Mime_Message $message * @param string $mimeType * - * @return \Swift_Mime_MimePart|null + * @return \Swift_Mime_MimeEntity|null */ protected function getMIMEPart(Swift_Mime_Message $message, $mimeType) { diff --git a/src/Viserio/Mail/Transport/Ses.php b/src/Viserio/Mail/Transport/Ses.php index ccfa49145..45aa34628 100644 --- a/src/Viserio/Mail/Transport/Ses.php +++ b/src/Viserio/Mail/Transport/Ses.php @@ -30,7 +30,7 @@ public function __construct(SesClient $ses) * @param \Swift_Mime_Message $message * @param string[]|null $failedRecipients * - * @return Log|null + * @return integer */ public function send(Swift_Mime_Message $message, &$failedRecipients = null) { diff --git a/src/Viserio/Mail/Transport/SparkPost.php b/src/Viserio/Mail/Transport/SparkPost.php index e47ef6ca6..e88a76fa5 100644 --- a/src/Viserio/Mail/Transport/SparkPost.php +++ b/src/Viserio/Mail/Transport/SparkPost.php @@ -89,7 +89,7 @@ public function getKey(): string * * @param string $key * - * @return string + * @return SparkPost */ public function setKey(string $key): SparkPost { diff --git a/src/Viserio/Mail/TransportManager.php b/src/Viserio/Mail/TransportManager.php index b7ffa281d..8dec4a772 100644 --- a/src/Viserio/Mail/TransportManager.php +++ b/src/Viserio/Mail/TransportManager.php @@ -3,12 +3,11 @@ namespace Viserio\Mail; use Aws\Ses\SesClient; -use Interop\Container\ContainerInterface; -use Swift_SmtpTransport as SmtpTransport; -use Swift_MailTransport as MailTransport; -use Narrowspark\Arr\StaticArr as Arr; use GuzzleHttp\Client as HttpClient; +use Narrowspark\Arr\StaticArr as Arr; use Psr\Log\LoggerInterface; +use Swift_MailTransport as MailTransport; +use Swift_SmtpTransport as SmtpTransport; use Viserio\Support\AbstractManager; use Viserio\Mail\Transport\{ Log as LogTransport, diff --git a/src/Viserio/Queue/Connectors/SyncQueue.php b/src/Viserio/Queue/Connectors/SyncQueue.php index 32272c73c..0bf1f6ac1 100644 --- a/src/Viserio/Queue/Connectors/SyncQueue.php +++ b/src/Viserio/Queue/Connectors/SyncQueue.php @@ -3,8 +3,8 @@ namespace Viserio\Queue\Connectors; use Throwable; -use Viserio\Contracts\Queue\Job as JobContract; use Viserio\Contracts\Exception\Exception\FatalThrowableError; +use Viserio\Contracts\Queue\Job as JobContract; use Viserio\Queue\Jobs\SyncJob; class SyncQueue extends AbstractQueue diff --git a/src/Viserio/StaticalProxy/StaticalProxy.php b/src/Viserio/StaticalProxy/StaticalProxy.php index 587cfb15f..4514bde6f 100644 --- a/src/Viserio/StaticalProxy/StaticalProxy.php +++ b/src/Viserio/StaticalProxy/StaticalProxy.php @@ -154,7 +154,7 @@ protected static function resolveStaticalProxyInstance($name) * * @param string $name * - * @return object + * @return MockInterface */ protected static function createFreshMockInstance(string $name) { @@ -168,7 +168,7 @@ protected static function createFreshMockInstance(string $name) /** * Create a fresh mock instance for the given class. * - * @return object + * @return MockInterface */ protected static function createMock() { From e6d6baa34c6cfff726aafb2b525427023527f96d Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Tue, 16 Aug 2016 19:33:08 +0200 Subject: [PATCH 38/39] cs fixes --- src/Viserio/Connect/ConnectManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Viserio/Connect/ConnectManager.php b/src/Viserio/Connect/ConnectManager.php index 537028658..1b30c11ff 100644 --- a/src/Viserio/Connect/ConnectManager.php +++ b/src/Viserio/Connect/ConnectManager.php @@ -108,7 +108,7 @@ protected function createMariadbConnection(array $config): PDO * * @return PDO */ - protected function createMongoConnection(array $config): PDO + protected function createMongoConnection(array $config) { return (new MongoConnector())->connect($config); } From 3371e877c0fa514d8dbc7fea5c98da4ea4ce7f87 Mon Sep 17 00:00:00 2001 From: Scrutinizer Auto-Fixer Date: Tue, 16 Aug 2016 19:55:48 +0200 Subject: [PATCH 39/39] Scrutinizer Auto-Fixes (#344) This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com --- src/Viserio/Connect/ConnectManager.php | 2 +- src/Viserio/Contracts/Session/Store.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Viserio/Connect/ConnectManager.php b/src/Viserio/Connect/ConnectManager.php index 1b30c11ff..4ee1d4320 100644 --- a/src/Viserio/Connect/ConnectManager.php +++ b/src/Viserio/Connect/ConnectManager.php @@ -106,7 +106,7 @@ protected function createMariadbConnection(array $config): PDO * * @param array $config * - * @return PDO + * @return \Mongo */ protected function createMongoConnection(array $config) { diff --git a/src/Viserio/Contracts/Session/Store.php b/src/Viserio/Contracts/Session/Store.php index 7e093a34b..da587dd89 100644 --- a/src/Viserio/Contracts/Session/Store.php +++ b/src/Viserio/Contracts/Session/Store.php @@ -33,6 +33,7 @@ public function open(): bool; * Sets the session ID. * * @param string $id + * @return void */ public function setId(string $id); @@ -47,6 +48,7 @@ public function getId(): string; * Sets the session name. * * @param string $name + * @return void */ public function setName(string $name); @@ -83,6 +85,7 @@ public function migrate(bool $destroy = false): bool; * This method is generally not required for real sessions as * the session will be automatically saved at the end of * code execution. + * @return void */ public function save(); @@ -110,6 +113,7 @@ public function get(string $name, $default = null); * * @param string $name * @param mixed $value + * @return void */ public function set(string $name, $value); @@ -118,6 +122,7 @@ public function set(string $name, $value); * * @param string $key * @param mixed $value + * @return void */ public function push(string $key, $value); @@ -139,6 +144,7 @@ public function all(): array; /** * Clears all attributes. + * @return void */ public function clear(); @@ -153,6 +159,7 @@ public function isStarted(): bool; * Set the request limit for a session. * * @param int $limit + * @return void */ public function setIdRequestsLimit(int $limit); @@ -168,6 +175,7 @@ public function getRequestsCount(): int; * will be automatically expired. * * @param int $ttl + * @return void */ public function setIdLiveTime(int $ttl); @@ -194,6 +202,7 @@ public function getRegenerationTrace(): int; /** * Age the flash data for the session. + * @return void */ public function ageFlashData(); @@ -202,6 +211,7 @@ public function ageFlashData(); * * @param string $key * @param mixed $value + * @return void */ public function flash(string $key, $value); @@ -211,11 +221,13 @@ public function flash(string $key, $value); * * @param string $key * @param mixed $value + * @return void */ public function now(string $key, $value); /** * Reflash all of the session flash data. + * @return void */ public function reflash(); @@ -223,6 +235,7 @@ public function reflash(); * Reflash a subset of the current flash data. * * @param array|mixed $keys + * @return void */ public function keep($keys = null); @@ -230,6 +243,7 @@ public function keep($keys = null); * Add a new Fingerprint generator. * * @param Fingerprint $fingerprintGenerator + * @return void */ public function addFingerprintGenerator(Fingerprint $fingerprintGenerator); @@ -251,6 +265,7 @@ public function handlerNeedsRequest(): bool; * Set the request on the handler instance. * * @param ServerRequestInterface $request + * @return void */ public function setRequestOnHandler(ServerRequestInterface $request);