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 @@
+
./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/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/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/Connect/ConnectManager.php b/src/Viserio/Connect/ConnectManager.php
index 0c77829f8..4ee1d4320 100644
--- a/src/Viserio/Connect/ConnectManager.php
+++ b/src/Viserio/Connect/ConnectManager.php
@@ -106,9 +106,9 @@ protected function createMariadbConnection(array $config): PDO
*
* @param array $config
*
- * @return \Mongo|\MongoClient|\MongoDB\Client
+ * @return \Mongo
*/
- protected function createMongoConnection(array $config): PDO
+ protected function createMongoConnection(array $config)
{
return (new MongoConnector())->connect($config);
}
diff --git a/src/Viserio/Console/Application.php b/src/Viserio/Console/Application.php
index a1d217b4d..3aacdde75 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\Support\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);
}
/**
@@ -86,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)
{
@@ -105,10 +111,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
+ * @return \Symfony\Component\Console\Command\Command
*/
- 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(
@@ -120,18 +127,23 @@ public function command(string $expression, $callable): SymfonyCommand
$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);
}
};
$command = $this->createCommand($expression, $commandFunction);
+ $command->setAliases($aliases);
$this->add($command);
@@ -208,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
{
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/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 b254f556b..93e679f0a 100644
--- a/src/Viserio/Console/Tests/ApplicationTest.php
+++ b/src/Viserio/Console/Tests/ApplicationTest.php
@@ -4,12 +4,16 @@
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 StdClass;
+use Symfony\Component\Console\{
+ Input\StringInput,
+ Output\OutputInterface
+};
+use Viserio\Console\{
+ Application,
+ Tests\Fixture\SpyOutput,
+ Tests\Fixture\ViserioCommand
+};
class ApplicationTest extends \PHPUnit_Framework_TestCase
{
@@ -20,9 +24,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 +143,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 +153,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 +178,7 @@ public function testItCanInjectUsingParameterNames()
$this->application->command('greet', function (OutputInterface $output, $stdClass) {
$output->write($stdClass->foo);
});
+
$this->assertOutputIs('greet', 'hello');
}
@@ -182,9 +190,31 @@ public function testItShouldThrowIfAParameterCannotBeResolved()
{
$this->application->command('greet', function ($fbo) {
});
+
$this->assertOutputIs('greet', '');
}
+ public function testRunsACommandViaItsAliasAndReturnsExitCode()
+ {
+ $this->application->command('foo', function ($output) {
+ $output->write(1);
+ }, ['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);
+ }
+
/**
* Fixture method.
*
@@ -204,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/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/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/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 @@
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/Http/Exceptions/ByteCountingStreamException.php b/src/Viserio/Contracts/Http/Exceptions/ByteCountingStreamException.php
new file mode 100644
index 000000000..80188b2c4
--- /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/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/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/DataGenerator.php b/src/Viserio/Contracts/Routing/DataGenerator.php
deleted file mode 100644
index 85c169260..000000000
--- a/src/Viserio/Contracts/Routing/DataGenerator.php
+++ /dev/null
@@ -1,13 +0,0 @@
- 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/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(),
+ $route->getUri()
+ ));
+ }
+}
diff --git a/src/Viserio/Contracts/Routing/Pattern.php b/src/Viserio/Contracts/Routing/Pattern.php
new file mode 100644
index 000000000..31ea23c1f
--- /dev/null
+++ b/src/Viserio/Contracts/Routing/Pattern.php
@@ -0,0 +1,28 @@
+ 'user' },
+ * ParameterSegment{ $name => 'id', $match => '[0-9]+' },
+ * StaticSegment{ $value => 'create' },
+ * ]
+ *
+ * @param string $route
+ * @param string[] $conditions
+ *
+ * @return \Viserio\Contracts\Routing\RouteSegment[]
+ *
+ * @throws \Viserio\Contracts\Routing\Exception\InvalidRoutePatternException
+ */
+ public function parse(string $route, array $conditions): array;
+}
diff --git a/src/Viserio/Contracts/Routing/RouteStrategy.php b/src/Viserio/Contracts/Routing/RouteStrategy.php
deleted file mode 100644
index 8112f9ef6..000000000
--- a/src/Viserio/Contracts/Routing/RouteStrategy.php
+++ /dev/null
@@ -1,13 +0,0 @@
-assertEquals($msg, $exception->getMessage());
+ $this->assertSame($prev, $exception->getPrevious());
+ }
+
+ public function getTestCases()
+ {
+ return [[7, 5], [5, 0]];
+ }
+}
diff --git a/src/Viserio/Contracts/View/Traits/ViewAwareTrait.php b/src/Viserio/Contracts/View/Traits/ViewAwareTrait.php
index 6c058d1e9..65a8ea4a3 100644
--- a/src/Viserio/Contracts/View/Traits/ViewAwareTrait.php
+++ b/src/Viserio/Contracts/View/Traits/ViewAwareTrait.php
@@ -10,7 +10,7 @@ trait ViewAwareTrait
/**
* View factory instance.
*
- * @var \Interop\Container\ContainerInterface|null
+ * @var \Viserio\Contracts\View\Factory
*/
protected $views;
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/Contracts/composer.json b/src/Viserio/Contracts/composer.json
index 1f3890502..52af450b6 100644
--- a/src/Viserio/Contracts/composer.json
+++ b/src/Viserio/Contracts/composer.json
@@ -7,7 +7,8 @@
"narrowspark",
"contracts",
"interfaces",
- "message", "psr-7",
+ "message",
+ "psr-7",
"log",
"psr-3",
"RFC 2616",
@@ -28,8 +29,12 @@
}
],
"require": {
- "php" : "7.0.0 - 7.0.5 || ^7.0.7",
- "psr/cache" : "^1.0",
+ "php" : "7.0.0 - 7.0.5 || ^7.0.7"
+ },
+ "require-dev": {
+ "phpunit/phpunit" : "^5.1",
+ "narrowspark/php-cs-fixer-config" : "^1.1",
+ "narrowspark/testing-helper" : "^1.5",
"container-interop/container-interop" : "^1.1",
"psr/http-message" : "^1.0",
"psr/log" : "^1.0"
@@ -40,6 +45,11 @@
},
"exclude-from-classmap" : ["/Tests/"]
},
+ "autoload-dev": {
+ "psr-4": {
+ "Viserio\\Contracts\\Tests\\" : "Tests/"
+ }
+ },
"extra": {
"branch-alias": {
"dev-master" : "1.0-dev"
@@ -50,6 +60,11 @@
"psr/http-message-implementation" : "^1.0",
"psr/log-implementation" : "^1.0"
},
+ "suggest": {
+ "psr/log" : "Required to use log contracts (^1.0).",
+ "psr/http-message" : "Required to use Http, Middleware, Routing contracts (^1.0).",
+ "container-interop/container-interop" : "Required to use Container contract (^1.0)."
+ },
"minimum-stability" : "dev",
"prefer-stable" : true
}
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/Events/Dispatcher.php b/src/Viserio/Events/Dispatcher.php
index 4cb01cb99..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\Support\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/Request.php b/src/Viserio/Http/Request.php
index 9e9684318..8c9fb91c7 100644
--- a/src/Viserio/Http/Request.php
+++ b/src/Viserio/Http/Request.php
@@ -159,7 +159,7 @@ public function getUri()
*/
public function withUri(UriInterface $uri, $preserveHost = false)
{
- if ($uri === $this->uri) {
+ if ($this->uri === $uri) {
return $this;
}
diff --git a/src/Viserio/Http/Stream/AbstractStreamDecorator.php b/src/Viserio/Http/Stream/AbstractStreamDecorator.php
index b735f46f4..cfdef4578 100644
--- a/src/Viserio/Http/Stream/AbstractStreamDecorator.php
+++ b/src/Viserio/Http/Stream/AbstractStreamDecorator.php
@@ -2,39 +2,25 @@
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;
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;
}
/**
@@ -48,7 +34,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);
@@ -121,6 +107,9 @@ public function eof()
return $this->stream->eof();
}
+ /**
+ * {@inheritdoc}
+ */
public function tell()
{
return $this->stream->tell();
@@ -181,16 +170,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/ByteCountingStream.php b/src/Viserio/Http/Stream/ByteCountingStream.php
new file mode 100644
index 000000000..5ed73b752
--- /dev/null
+++ b/src/Viserio/Http/Stream/ByteCountingStream.php
@@ -0,0 +1,78 @@
+stream = $stream;
+
+ if (!is_int($bytesToRead) || $bytesToRead < 0) {
+ $msg = 'Bytes to read should be a non-negative integer for '
+ . sprintf('ByteCountingStream, got %s.', $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/Stream/LazyOpenStream.php b/src/Viserio/Http/Stream/LazyOpenStream.php
index 0fb0048ae..7e0edccd9 100644
--- a/src/Viserio/Http/Stream/LazyOpenStream.php
+++ b/src/Viserio/Http/Stream/LazyOpenStream.php
@@ -3,9 +3,11 @@
namespace Viserio\Http\Stream;
use Psr\Http\Message\StreamInterface;
+use Throwable;
+use UnexpectedValueException;
use Viserio\Http\Util;
-class LazyOpenStream extends AbstractStreamDecorator
+class LazyOpenStream implements StreamInterface
{
/**
* @var string
@@ -21,16 +23,167 @@ 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;
}
+ /**
+ * Magic method used to create a new stream if streams are not added in
+ * the constructor of LazyOpenStream.
+ *
+ * @param string $name Name of the property (allows "stream" only).
+ *
+ * @return \Psr\Http\Message\StreamInterface
+ */
+ public function __get($name)
+ {
+ if ($name == 'stream') {
+ $this->stream = $this->createStream();
+
+ return $this->stream;
+ }
+
+ throw new UnexpectedValueException(sprintf('%s not found on class', $name));
+ }
+
+ /**
+ * {@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);
+ }
+
/**
* Creates the underlying stream lazily when required.
*
- * @return StreamInterface
+ * @return \Psr\Http\Message\StreamInterface
*/
protected function createStream(): StreamInterface
{
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/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/QueueMailer.php b/src/Viserio/Mail/QueueMailer.php
index 8631f5b01..01bc51f69 100644
--- a/src/Viserio/Mail/QueueMailer.php
+++ b/src/Viserio/Mail/QueueMailer.php
@@ -7,6 +7,7 @@
use SuperClosure\Serializer;
use Swift_Mailer;
use Viserio\Contracts\{
+ Container\Traits\ContainerAwareTrait,
Mail\QueueMailer as QueueMailerContract,
Queue\Queue as QueueContract,
Queue\Job as JobContract,
@@ -14,7 +15,6 @@
};
use Viserio\Support\{
Invoker,
- Traits\ContainerAwareTrait,
Str
};
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/Middleware/Dispatcher.php b/src/Viserio/Middleware/Dispatcher.php
index f9768c0dc..dd435b463 100644
--- a/src/Viserio/Middleware/Dispatcher.php
+++ b/src/Viserio/Middleware/Dispatcher.php
@@ -13,7 +13,7 @@
Middleware\Middleware as MiddlewareContract,
Middleware\Stack as StackContract
};
-use Viserio\Support\Traits\ContainerAwareTrait;
+use Viserio\Contracts\Container\Traits\ContainerAwareTrait;
class Dispatcher implements StackContract
{
@@ -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/Pipeline/Pipeline.php b/src/Viserio/Pipeline/Pipeline.php
index d93985ed5..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\Support\Traits\ContainerAwareTrait;
class Pipeline implements PipelineContract
{
@@ -71,15 +73,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 +89,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 +97,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 +122,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);
};
}
diff --git a/src/Viserio/Queue/Connectors/AbstractQueue.php b/src/Viserio/Queue/Connectors/AbstractQueue.php
index 81ba034dd..776b4cc21 100644
--- a/src/Viserio/Queue/Connectors/AbstractQueue.php
+++ b/src/Viserio/Queue/Connectors/AbstractQueue.php
@@ -14,7 +14,7 @@
CallQueuedHandler,
QueueClosure
};
-use Viserio\Support\Traits\ContainerAwareTrait;
+use Viserio\Contracts\Container\Traits\ContainerAwareTrait;
abstract class AbstractQueue implements QueueConnectorContract
{
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/Queue/Jobs/AbstractJob.php b/src/Viserio/Queue/Jobs/AbstractJob.php
index 9914ab703..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\Support\Traits\ContainerAwareTrait;
abstract class AbstractJob implements JobContract
{
diff --git a/src/Viserio/Queue/Tests/Fixture/TestQueue.php b/src/Viserio/Queue/Tests/Fixture/TestQueue.php
index 8227e4815..6a7f35d0b 100644
--- a/src/Viserio/Queue/Tests/Fixture/TestQueue.php
+++ b/src/Viserio/Queue/Tests/Fixture/TestQueue.php
@@ -3,7 +3,7 @@
namespace Viserio\Queue\Tests\Fixture;
use Viserio\Contracts\Encryption\Encrypter as EncrypterContract;
-use Viserio\Support\Traits\ContainerAwareTrait;
+use Viserio\Contracts\Container\Traits\ContainerAwareTrait;
class TestQueue
{
diff --git a/src/Viserio/Routing/AbstractController.php b/src/Viserio/Routing/AbstractController.php
new file mode 100644
index 000000000..f85a6e6c1
--- /dev/null
+++ b/src/Viserio/Routing/AbstractController.php
@@ -0,0 +1,8 @@
+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 = parent::dispatch($method, $uri);
+ $match = $this->dispatch(
+ $request->getMethod(),
+ $request->getUri()->getPath()
+ );
switch ($match[0]) {
- case FastDispatcher::NOT_FOUND:
- return $this->handleNotFound();
-
- case FastDispatcher::METHOD_NOT_ALLOWED:
- $allowed = (array) $match[1];
-
- return $this->handleNotAllowed($allowed);
-
- case FastDispatcher::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);
+ case DispatcherContract::NOT_FOUND:
+ // 404 Not Found...
break;
- case RouteStrategyContract::RESTFUL_STRATEGY:
- $response = $this->handleRestfulStrategy($controller, $vars);
+ case DispatcherContract::HTTP_METHOD_NOT_ALLOWED:
+ // 405 Method Not Allowed...
break;
- case RouteStrategyContract::REQUEST_RESPONSE_STRATEGY:
- default:
- $response = $this->handleRequestResponseStrategy($controller, $vars);
+ case DispatcherContract::FOUND:
+ // Matched route, dispatch to associated handler...
break;
}
-
- return $response;
- }
-
- /**
- * Handles response to Request -> Response Strategy based routes.
- *
- * @param ResponseContract $controller
- * @param array $vars
- *
- * @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.
- *
- * @throws HttpException\NotFoundException
- *
- * @return JsonResponse
+ * {@inheritdoc}
*/
- protected function handleNotFound()
+ public function dispatch(string $httpMethod, string $uri): array
{
- $exception = new HttpException\NotFoundException();
-
- if ($this->getStrategy() === RouteStrategyContract::RESTFUL_STRATEGY) {
- return $exception->getJsonResponse();
- }
- throw $exception;
}
- /**
- * 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();
- }
-
- throw $exception;
}
}
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 @@
+parameterKeys;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMatchedParameterExpressions(string $segmentVariable, string $uniqueKey = null): array
+ {
+ return array_fill_keys($this->parameterKeys, $segmentVariable);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ 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.',
+ $matcher->getHash(),
+ $this->getHash()
+ )
+ );
+ }
+
+ $this->parameterKeys = array_unique(
+ array_merge($this->parameterKeys, $matcher->getParameterKeys()),
+ SORT_NUMERIC
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getHash(): string
+ {
+ 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/AnyMatcher.php b/src/Viserio/Routing/Matchers/AnyMatcher.php
new file mode 100644
index 000000000..47678cec2
--- /dev/null
+++ b/src/Viserio/Routing/Matchers/AnyMatcher.php
@@ -0,0 +1,32 @@
+parameterKeys = $parameterKeys;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getConditionExpression(string $segmentVariable, string $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
new file mode 100644
index 000000000..7aa127e87
--- /dev/null
+++ b/src/Viserio/Routing/Matchers/CompoundMatcher.php
@@ -0,0 +1,79 @@
+getParameterKeys());
+ }
+
+ $this->parameterKeys = $parameterKeys;
+ $this->matchers = $matchers;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getConditionExpression(string $segmentVariable, string $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, string $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(): string
+ {
+ $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..2788f1a4b
--- /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, string $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/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
new file mode 100644
index 000000000..a844ba169
--- /dev/null
+++ b/src/Viserio/Routing/Matchers/RegexMatcher.php
@@ -0,0 +1,117 @@
+regex = $regex;
+
+ $map = [$parameterKeyGroupMap => 0];
+
+ $this->parameterKeyGroupMap = $map;
+ $this->parameterKeys = array_keys($map);
+ }
+
+ /**
+ * 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, string $uniqueKey = null): string
+ {
+ return 'preg_match('
+ . VarExporter::export($this->regex)
+ . ', '
+ . $segmentVariable
+ . ', '
+ . '$matches' . $uniqueKey
+ . ')';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMatchedParameterExpressions(string $segmentVariable, string $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();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getMatchHash(): string
+ {
+ return $this->regex;
+ }
+}
diff --git a/src/Viserio/Routing/Matchers/StaticMatcher.php b/src/Viserio/Routing/Matchers/StaticMatcher.php
new file mode 100644
index 000000000..0e2369ef5
--- /dev/null
+++ b/src/Viserio/Routing/Matchers/StaticMatcher.php
@@ -0,0 +1,64 @@
+parameterKeys = $parameterKeys ?? [];
+ $this->segment = $segment;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getConditionExpression(string $segmentVariable, string $uniqueKey = null): string
+ {
+ return $segmentVariable . ' === ' . VarExporter::export($this->segment);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMatchedParameterExpressions(string $segmentVariable, string $uniqueKey = null): array
+ {
+ $keys = $this->parameterKeys;
+
+ if (count($keys) > 0) {
+ return [$keys[0] => $segmentVariable];
+ }
+
+ return [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getMatchHash(): string
+ {
+ return $this->segment;
+ }
+}
diff --git a/src/Viserio/Routing/Redirect.php b/src/Viserio/Routing/Redirect.php
index f6eeafa1e..c93031e0d 100644
--- a/src/Viserio/Routing/Redirect.php
+++ b/src/Viserio/Routing/Redirect.php
@@ -40,129 +40,19 @@ class Redirect
protected $action;
/**
- * RouteCollection instance.
+ * The URL generator instance.
*
- * @var \Viserio\Routing\RouteCollection
+ * @var \Viserio\Routing\UrlGenerator
*/
- protected $route;
-
- public function __construct(RouteCollection $route)
- {
- $this->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
new file mode 100644
index 000000000..8fddcb397
--- /dev/null
+++ b/src/Viserio/Routing/Route.php
@@ -0,0 +1,377 @@
+uri = $uri;
+ // 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)) {
+ $this->httpMethods[] = 'HEAD';
+ }
+
+ if (isset($this->action['prefix'])) {
+ $this->addPrefix($this->action['prefix']);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDomain()
+ {
+ return $this->action['domain'] ?? null;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getUri(): string
+ {
+ return $this->uri;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setUri(string $uri): RouteContract
+ {
+ $this->uri = $uri;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->action['as'] ?? null;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setName(string $name): RouteContract
+ {
+ $this->action['as'] = isset($this->action['as']) ? $this->action['as'] . $name : $name;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMethods(): array
+ {
+ return $this->httpMethods;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isHttpOnly(): bool
+ {
+ return in_array('http', $this->action, true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isHttpsOnly(): bool
+ {
+ return in_array('https', $this->action, true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getActionName(): string
+ {
+ return $this->action['controller'] ?? 'Closure';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAction(): array
+ {
+ return $this->action;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setAction(array $action): RouteContract
+ {
+ $this->action = $action;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addPrefix(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[$name] = $value;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getParameter(string $name, $default = null)
+ {
+ return Arr::get($this->getParameters(), $name, $default);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasParameter(string $name): bool
+ {
+ return Arr::has($this->getParameters(), $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->getParameters();
+
+ unset($this->parameters[$name]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isStatic(): bool
+ {
+ $this->getParameters();
+
+ foreach($this->parameters as $parameter) {
+ if ($parameter instanceof ParameterMatcher) {
+ 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 mixed
+ */
+ public function __get($key)
+ {
+ return $this->getParameter($key);
+ }
+
+ /**
+ * Set configured invoker.
+ *
+ * @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 array
+ *
+ * @throws \UnexpectedValueException
+ */
+ protected function parseAction($action): array
+ {
+ // 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(sprintf('Route for [%s] has no action.', $this->uri));
+ }];
+ }
+
+ // 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']) && strpos($action['uses'], '::') === false) {
+ 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 ea6cc42df..8c276c73d 100644
--- a/src/Viserio/Routing/RouteCollection.php
+++ b/src/Viserio/Routing/RouteCollection.php
@@ -2,36 +2,30 @@
declare(strict_types=1);
namespace Viserio\Routing;
-use Closure;
-use FastRoute\DataGenerator;
-use FastRoute\RouteCollector;
-use FastRoute\RouteParser as FastRouteParser;
-use Interop\Container\ContainerInterface as ContainerContract;
-use InvalidArgumentException;
-use LogicException;
-use RuntimeException;
-use Viserio\Contracts\Routing\RouteCollector as RouteCollectorContract;
-use Viserio\Contracts\Routing\RouteStrategy as RouteStrategyContract;
-use Viserio\Routing\RouteParser as ViserioRouteParser;
+use Viserio\Contracts\{
+ Container\Traits\ContainerAwareTrait,
+ Routing\Route as RouteContract
+};
-class RouteCollection extends RouteCollector implements RouteStrategyContract, RouteCollectorContract
+class RouteCollection
{
- /*
- * Route strategy functionality
- */
- use RouteStrategyTrait;
+ 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
*/
protected $namedRoutes = [];
@@ -42,327 +36,81 @@ class RouteCollection extends RouteCollector implements RouteStrategyContract, R
protected $filters = [];
/**
- * Constructor.
- *
- * @param ContainerContract $container
- * @param \FastRoute\RouteParser $parser
- * @param \FastRoute\DataGenerator $generator
+ * @var \Viserio\Routing\RouteGroup[]
*/
- public function __construct(
- ContainerContract $container,
- FastRouteParser $parser,
- DataGenerator $generator
- ) {
- $this->container = $container;
-
- parent::__construct($parser, $generator);
- }
+ protected $groups = [];
/**
- * 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);
-
- //Check for a route alias starting with @
- $matches = [];
+ $this->addToCollections($route);
- 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;
- }
-
- /**
- * Builds a dispatcher based on the routes attached to this collection.
- *
- * @return \Viserio\Routing\Dispatcher
- */
- public function getDispatcher()
- {
- $dispatcher = new Dispatcher($this->container, $this->routes, $this->getData());
-
- if ($this->strategy !== null) {
- $dispatcher->setStrategy($this->strategy);
- }
-
- return $dispatcher;
+ return $route;
}
/**
- * Map a handler to the given methods and route.
- *
- * @param string $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', $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
+ * Get all of the routes in the collection.
*
- * @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
+ * @return array
*/
- public function onBefore($name, $handler, $priority = 0)
+ public function getRoutes(): array
{
- $this->addEventListener($name, $handler, 'before', $priority);
+ return array_values($this->allRoutes);
}
/**
- * Add an "after" event listener.
+ * Add the given route to the arrays of routes.
*
- * @param string $name
- * @param callable $handler
- * @param int $priority
+ * @param \Viserio\Contracts\Routing\Route $route
*/
- public function onAfter($name, $handler, $priority = 0)
+ protected function addToCollections(RouteContract $route)
{
- $this->addEventListener($name, $handler, 'after', $priority);
- }
+ $domainAndUri = $route->getDomain() . $route->getUri();
- /**
- * Add a global "before" event listener.
- *
- * @param callable $handler
- * @param int $priority
- */
- public function globalOnBefore($handler, $priority = 0)
- {
- $this->addEventListener(null, $handler, 'before', $priority);
- }
+ foreach ($route->getMethods() as $method) {
+ $this->routes[$method][$domainAndUri] = $route;
+ }
- /**
- * Add a global "after" event listener.
- *
- * @param callable $handler
- * @param int $priority
- */
- public function globalOnAfter($handler, $priority = 0)
- {
- $this->addEventListener(null, $handler, 'after', $priority);
+ $this->allRoutes[$method.$domainAndUri] = $route;
}
/**
- * Redirect instance.
*
- * @return \Viserio\Routing\Redirect
- */
- public function redirect()
- {
- return new Redirect($this);
- }
-
- /**
- * Returns the array of registered named routes (starting with @).
+ * @param string $pattern [description]
*
* @return array
*/
- public function getNamedRoutes()
+ protected function parseRoutingPattern(string $pattern): array
{
- return $this->namedRoutes;
- }
+ if (is_string($pattern)) {
+ return [$pattern, []];
+ }
- /**
- * @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));
+ 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'
+ ));
}
- $this->filters[$name] = $name;
- }
-
- $name = $name ? sprintf('route%s%s', $when, $name) : sprintf('route%s', $when);
+ $patternString = $pattern[0];
+ $parameterConditions = $pattern;
- $this->container['events']->addListener($name, $handler, $priority);
- }
+ unset($parameterConditions[0]);
- /**
- * 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 [$patternString, $parameterConditions];
}
- 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);
+ throw new InvalidRoutePatternException(sprintf(
+ 'Cannot add route: route pattern must be a pattern string, %s given',
+ gettype($pattern)
+ ));
}
}
diff --git a/src/Viserio/Routing/RouteGroup.php b/src/Viserio/Routing/RouteGroup.php
new file mode 100644
index 000000000..3f1390e34
--- /dev/null
+++ b/src/Viserio/Routing/RouteGroup.php
@@ -0,0 +1,67 @@
+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]);
+ }
+
+ /**
+ * {@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/RouteParser.php b/src/Viserio/Routing/RouteParser.php
index 3fcae90ce..0d0cca471 100644
--- a/src/Viserio/Routing/RouteParser.php
+++ b/src/Viserio/Routing/RouteParser.php
@@ -2,30 +2,153 @@
declare(strict_types=1);
namespace Viserio\Routing;
-use FastRoute\RouteParser as FastRouteParser;
-use FastRoute\RouteParser\Std;
+use Viserio\Routing\Matchers\{
+ StaticMatcher,
+ ParameterMatcher
+};
+use Viserio\Contracts\Routing\{
+ Exceptions\InvalidRoutePatternException,
+ RouteParser as RouteParserContract,
+ RouteSegment as RouteSegmentContract,
+ Pattern
+};
-class RouteParser extends Std implements FastRouteParser
+class RouteParser implements RouteParserContract
{
/**
- * Regex to find the route alias.
+ * {@inheritdoc}
*/
- const ALIAS_REGEX = '/^(@[a-zA-Z0-9-_\.]+)/';
+ public function parse(string $route, array $conditions): array
+ {
+ if (strlen($route) > 1 && $route[0] !== '/') {
+ throw new InvalidRoutePatternException(sprintf(
+ 'Invalid route pattern: non-root route must be prefixed with \'/\', \'%s\' given',
+ $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;
+ }
/**
- * Parses the string into an array of segments.
- *
- * "/user/{name}/{id:[0-9]+}"
+ * Validate and match uri paramters.
*
* @param string $route
+ * @param string $patternSegment
+ * @param array &$conditions
+ * @param array &$matches
+ * @param array &$names
*
- * @return array
+ * @return bool
*/
- public function parse($route)
+ protected function matchRouteParameters(
+ string $route,
+ 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',
+ $route
+ ));
+ }
+ } else {
+ if ($character === '{') {
+ $matches[] = [self::STATIC_PART, $current];
+ $current = '';
+ $inParameter = true;
+
+ continue;
+ } elseif ($character === '}') {
+ throw new InvalidRoutePatternException(sprintf(
+ 'Invalid route uri: cannot contain \'}\' before opening \'{\', \'%s\' given',
+ $route
+ ));
+ }
+ }
+
+ $current .= $character;
+ }
+
+ if ($inParameter) {
+ throw new InvalidRoutePatternException(sprintf(
+ 'Invalid route uri: cannot contain \'{\' without closing \'}\', \'%s\' given',
+ $route
+ ));
+ } elseif ($current !== '') {
+ $matches[] = [self::STATIC_PART, $current];
+ }
+
+ return $matchedParameter;
+ }
+
+ /**
+ * Generate a segment regex.
+ *
+ * @param array $matches
+ * @param array $parameterPatterns
+ *
+ * @return string
+ */
+ protected function generateRegex(array $matches, array $parameterPatterns): string
{
- //Remove possible name in route
- $route = preg_replace(self::ALIAS_REGEX, '', $route);
+ $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) . ')';
+ }
+ }
+
+ $regex .= '$/';
- return parent::parse($route);
+ return $regex;
}
}
diff --git a/src/Viserio/Routing/RouteStrategyTrait.php b/src/Viserio/Routing/RouteStrategyTrait.php
deleted file mode 100644
index 6b39d8fcc..000000000
--- a/src/Viserio/Routing/RouteStrategyTrait.php
+++ /dev/null
@@ -1,43 +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..d1b8ba822
--- /dev/null
+++ b/src/Viserio/Routing/Router.php
@@ -0,0 +1,203 @@
+container = $container;
+ $this->parser = $parser;
+ }
+
+ /**
+ * {@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;
+ }
+
+ /**
+ * Dispatch router for HTTP request.
+ *
+ * @param \Psr\Http\Message\ServerRequestInterface $request The current HTTP request object
+ *
+ * @return array
+ */
+ public function dispatch(ServerRequestInterface $request)
+ {
+
+ }
+
+ /**
+ * 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[] = $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
+ {
+ $pattern = $this->parser->parse($uri, ['TODO']);
+
+ $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);
+ }
+
+ /**
+ * 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 0019dcf62..3c1df5cd5 100644
--- a/src/Viserio/Routing/Tests/DispatcherTest.php
+++ b/src/Viserio/Routing/Tests/DispatcherTest.php
@@ -2,19 +2,21 @@
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
{
- private function getRouteCollection()
+ public function testMatch()
{
- return new RouteCollection(
- new Container(),
- new RouteParser(),
- new GroupCountBased()
- );
+ $route = new Route('GET', 'test', null);
+
+ $collection = new RouteCollection();
+ $collection->addRoute($route);
+
+ $dispatcher = new Dispatcher($collection);
}
}
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 @@
+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
new file mode 100644
index 000000000..e2df5f019
--- /dev/null
+++ b/src/Viserio/Routing/Tests/Matchers/CompoundMatcherTest.php
@@ -0,0 +1,55 @@
+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());
+ }
+
+ 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
new file mode 100644
index 000000000..26eac0b74
--- /dev/null
+++ b/src/Viserio/Routing/Tests/Matchers/ExpressionMatcherTest.php
@@ -0,0 +1,36 @@
+assertSame('ctype_digit({segment})', $matcher->getExpression());
+ }
+
+ public function testGetConditionExpression()
+ {
+ $matcher = new ExpressionMatcher('ctype_digit({segment})', [1]);
+
+ $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);
+ }
+}
diff --git a/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php
new file mode 100644
index 000000000..e9f953c10
--- /dev/null
+++ b/src/Viserio/Routing/Tests/Matchers/RegexMatcherTest.php
@@ -0,0 +1,54 @@
+assertSame(1, $matcher->getGroupCount());
+ }
+
+ public function testGetRegex()
+ {
+ $matcher = new RegexMatcher('/^(' . Pattern::ALPHA . ')$/', 12);
+
+ $this->assertSame('/^(' . 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'));
+ }
+
+ 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/Tests/Matchers/StaticMatcherTest.php b/src/Viserio/Routing/Tests/Matchers/StaticMatcherTest.php
new file mode 100644
index 000000000..80f880c9f
--- /dev/null
+++ b/src/Viserio/Routing/Tests/Matchers/StaticMatcherTest.php
@@ -0,0 +1,43 @@
+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'));
+ }
+
+ public function testMergeParameterKeys()
+ {
+ $matcher = new StaticMatcher('two', [2]);
+ $matcher2 = new StaticMatcher('two', [3]);
+ $matcher->mergeParameterKeys($matcher2);
+
+ $this->assertSame([2 => 'two'], $matcher->getMatchedParameterExpressions('two'));
+ }
+}
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
index 3c0cffc50..b45b409d3 100644
--- a/src/Viserio/Routing/Tests/RouteParserTest.php
+++ b/src/Viserio/Routing/Tests/RouteParserTest.php
@@ -2,17 +2,173 @@
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
{
/**
- * Asserts that the @ sign is correctly added when missing.
+ * @dataProvider routeParsingProvider
*/
- public function testNamedRouteAreHandledTheSameAsNotNamedRoute()
+ public function testRouteParser($pattern, array $conditions, array $expectedSegments)
{
$parser = new RouteParser();
- $this->assertEquals($parser->parse('@bundle.named_route/my-route'), $parser->parse('/my-route'));
+ $this->assertEquals($expectedSegments, $parser->parse($pattern, $conditions));
+ }
+
+ public function routeParsingProvider()
+ {
+ return [
+ [
+ // Empty route
+ '',
+ [],
+ []
+ ],
+ [
+ // Empty route
+ '/',
+ [],
+ [new StaticMatcher('')]
+ ],
+ [
+ '/user',
+ [],
+ [new StaticMatcher('user')]
+ ],
+ [
+ '/user/',
+ [],
+ [new StaticMatcher('user'), new StaticMatcher('')]
+ ],
+ [
+ '/user/profile',
+ [],
+ [new StaticMatcher('user'), new StaticMatcher('profile')]
+ ],
+ [
+ '/{parameter}',
+ [],
+ [new ParameterMatcher('parameter', '/^(' . Pattern::ANY. ')$/')]
+ ],
+ [
+ '/{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
new file mode 100644
index 000000000..3206ae1b5
--- /dev/null
+++ b/src/Viserio/Routing/Tests/RouteTest.php
@@ -0,0 +1,110 @@
+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']);
+
+ $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->isHttpOnly());
+
+ $route = new Route('GET', 'test', ['https']);
+
+ $this->assertTrue($route->isHttpsOnly());
+ }
+
+ 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());
+ }
+
+ protected function getRouter()
+ {
+ return new Router($this->mock(ContainerInterface::class), new RouteParser());
+ }
+}
diff --git a/src/Viserio/Routing/Tests/UrlGenerator/CachedDataGeneratorTest.php b/src/Viserio/Routing/Tests/UrlGenerator/CachedDataGeneratorTest.php
deleted file mode 100644
index dede2e289..000000000
--- a/src/Viserio/Routing/Tests/UrlGenerator/CachedDataGeneratorTest.php
+++ /dev/null
@@ -1,63 +0,0 @@
-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/Tests/VarExporterTest.php b/src/Viserio/Routing/Tests/VarExporterTest.php
new file mode 100644
index 000000000..59267c4f0
--- /dev/null
+++ b/src/Viserio/Routing/Tests/VarExporterTest.php
@@ -0,0 +1,48 @@
+ 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(), '(object)[]'],
+ [(object) ['foo' => 'bar'], '(object)[\'foo\' => \'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/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/VarExporter.php b/src/Viserio/Routing/VarExporter.php
new file mode 100644
index 000000000..a8e1b268f
--- /dev/null
+++ b/src/Viserio/Routing/VarExporter.php
@@ -0,0 +1,62 @@
+ ' . self::export(current($value)) . ']';
+ }
+
+ $code = '[';
+
+ foreach ($value as $key => $element) {
+ $code .= self::export($key);
+ $code .= ' => ';
+ $code .= self::export($element);
+ $code .= ',';
+ }
+
+ $code .= ']';
+
+ return $code;
+ } elseif (is_object($value) && $value instanceof StdClass) {
+ return '(object)' . self::export((array) $value);
+ }
+
+ if (is_scalar($value)) {
+ return var_export($value, true);
+ }
+
+ return 'unserialize(' . var_export(serialize($value), true) . ')';
+ }
+
+ /**
+ * Don't instantiate this class.
+ *
+ * @codeCoverageIgnore
+ */
+ private function __construct() {
+ //
+ }
+}
diff --git a/src/Viserio/Routing/composer.json b/src/Viserio/Routing/composer.json
index 56bd1cb9d..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", "FastRoute", "dispatcher"],
+ "keywords" : ["viserio", "narrowspark", "route", "dispatcher", "router"],
"license" : "MIT",
"homepage" : "http://github.com/narrowspark/framework",
"support" : {
@@ -19,17 +19,19 @@
],
"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"
+ "narrowspark/arr" : "^1.1",
+ "php-di/invoker" : "^1.3",
+ "psr/http-message" : "^1.0",
+ "viserio/contracts" : "self.version",
+ "viserio/middleware" : "self.version",
+ "viserio/support" : "self.version"
},
"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": {
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()
{
diff --git a/src/Viserio/Support/AbstractConnectionManager.php b/src/Viserio/Support/AbstractConnectionManager.php
index ffe881105..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\Support\Traits\ContainerAwareTrait;
abstract class AbstractConnectionManager
{
diff --git a/src/Viserio/Support/AbstractManager.php b/src/Viserio/Support/AbstractManager.php
index 6a72c916e..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\Support\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 5e51036ad..441f4e2a9 100644
--- a/src/Viserio/Support/Invoker.php
+++ b/src/Viserio/Support/Invoker.php
@@ -4,13 +4,15 @@
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 Viserio\Support\Traits\ContainerAwareTrait;
+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 cc4ca87fd..1bd801830 100644
--- a/src/Viserio/Support/composer.json
+++ b/src/Viserio/Support/composer.json
@@ -18,42 +18,38 @@
}
],
"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",
+ "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"
- },
"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;