Skip to content
This repository has been archived by the owner on Jan 21, 2020. It is now read-only.

Commit

Permalink
Merge branch 'feature/factory-creation' into release-1.0.0
Browse files Browse the repository at this point in the history
Close #52
Fixes #42
  • Loading branch information
weierophinney committed Feb 12, 2018
2 parents 4360bd3 + 1f61691 commit 1d9bdf0
Show file tree
Hide file tree
Showing 34 changed files with 1,420 additions and 2 deletions.
20 changes: 18 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,27 @@ All notable changes to this project will be documented in this file, in reverse

### Added

- Nothing.
- [#52](https://github.com/zendframework/zend-expressive-tooling/pull/52) adds
the command `factory:create`. The command expects a fully-qualified,
resolvable, class name; it then generates a factory class for it as a sibling
class file, using reflection. By default, it also registers the class and
factory with the container, in the file `config/autoload/zend-expressive-tooling-factories.global.php`.
Pass the option `--no-register` to disable this auto-registration.

### Changed

- Nothing.
- [#52](https://github.com/zendframework/zend-expressive-tooling/pull/52)
modifies the `middleware:create` command to invoke `factory:create` once it
has successfully created the new middleware. You may disable this feature by
passing the option `--no-factory`; if you want to generate the factory, but
not auto-register the middleware service, pass the option `--no-register`.

- [#52](https://github.com/zendframework/zend-expressive-tooling/pull/52)
modifies the `handler:create` command to invoke `factory:create` once it has
successfully created the new request handler. You may disable this feature by
passing the option `--no-factory`; if you want to generate the factory, but
not auto-register the request handler service, pass the option
`--no-register`.

### Deprecated

Expand Down
1 change: 1 addition & 0 deletions bin/expressive
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ if (file_exists($a = __DIR__ . '/../../../autoload.php')) {

$application = new Application('expressive', VERSION);
$application->addCommands([
new Factory\CreateFactoryCommand('factory:create'),
new CreateHandler\CreateHandlerCommand('handler:create'),
new CreateMiddleware\CreateMiddlewareCommand('middleware:create'),
new MigrateInteropMiddleware\MigrateInteropMiddlewareCommand('migrate:interop-middleware'),
Expand Down
44 changes: 44 additions & 0 deletions src/CreateHandler/CreateHandlerCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
namespace Zend\Expressive\Tooling\CreateHandler;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

Expand All @@ -30,6 +32,28 @@ class. For a path, it matches the class namespace against PSR-4 autoloader
escape sequences by your shell.
EOT;

const HELP_OPT_NO_FACTORY = <<< 'EOT'
By default, this command generates a factory for the request handler it
creates, and registers it with the container. Passing this option disables
that feature.
EOT;

const HELP_OPT_NO_REGISTER = <<< 'EOT'
By default, when this command generates a factory for the request handler it
creates, it registers it with the container. Passing this option disables
registration of the generated factory with the container.
EOT;

/**
* Flag indicating whether or not to require the generated handler before
* generating its factory. By default, this is true, as it is necessary
* in order for the handler to be reflected. However, during testing, we do
* not actually generate a handler, so we need a way to disable it.
*
* @var bool
*/
private $requireHandlerBeforeGeneratingFactory = true;

/**
* Configure the console command.
*/
Expand All @@ -38,6 +62,8 @@ protected function configure()
$this->setDescription('Create a PSR-15 request handler class file.');
$this->setHelp(self::HELP);
$this->addArgument('handler', InputArgument::REQUIRED, self::HELP_ARG_HANDLER);
$this->addOption('no-factory', null, InputOption::VALUE_NONE, self::HELP_OPT_NO_FACTORY);
$this->addOption('no-register', null, InputOption::VALUE_NONE, self::HELP_OPT_NO_REGISTER);
}

/**
Expand All @@ -63,6 +89,24 @@ protected function execute(InputInterface $input, OutputInterface $output)
$path
));

if (! $input->getOption('no-factory')) {
if ($this->requireHandlerBeforeGeneratingFactory) {
require $path;
}
return $this->generateFactory($handler, $input, $output);
}

return 0;
}

private function generateFactory(string $handlerClass, InputInterface $input, OutputInterface $output) : int
{
$factoryInput = new ArrayInput([
'command' => 'factory:create',
'class' => $handlerClass,
'--no-register' => $input->getOption('no-register'),
]);
$command = $this->getApplication()->find('factory:create');
return $command->run($factoryInput, $output);
}
}
43 changes: 43 additions & 0 deletions src/CreateMiddleware/CreateMiddlewareCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
namespace Zend\Expressive\Tooling\CreateMiddleware;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class CreateMiddlewareCommand extends Command
Expand All @@ -30,6 +32,27 @@ class CreateMiddlewareCommand extends Command
escape sequences by your shell.
EOT;

const HELP_OPT_NO_FACTORY = <<< 'EOT'
By default, this command generates a factory for the middleware it creates, and
registers it with the container. Passing this option disables that feature.
EOT;

const HELP_OPT_NO_REGISTER = <<< 'EOT'
By default, when this command generates a factory for the middleware it
creates, it registers it with the container. Passing this option disables
registration of the generated factory with the container.
EOT;

/**
* Flag indicating whether or not to require the generated middleware before
* generating its factory. By default, this is true, as it is necessary
* in order for the middleware to be reflected. However, during testing, we do
* not actually generate a middleware, so we need a way to disable it.
*
* @var bool
*/
private $requireMiddlewareBeforeGeneratingFactory = true;

/**
* Configure the console command.
*/
Expand All @@ -38,6 +61,8 @@ protected function configure()
$this->setDescription('Create a PSR-15 middleware class file.');
$this->setHelp(self::HELP);
$this->addArgument('middleware', InputArgument::REQUIRED, self::HELP_ARG_MIDDLEWARE);
$this->addOption('no-factory', null, InputOption::VALUE_NONE, self::HELP_OPT_NO_FACTORY);
$this->addOption('no-register', null, InputOption::VALUE_NONE, self::HELP_OPT_NO_REGISTER);
}

/**
Expand All @@ -63,6 +88,24 @@ protected function execute(InputInterface $input, OutputInterface $output)
$path
));

if (! $input->getOption('no-factory')) {
if ($this->requireMiddlewareBeforeGeneratingFactory) {
require $path;
}
return $this->generateFactory($middleware, $input, $output);
}

return 0;
}

private function generateFactory(string $middlewareClass, InputInterface $input, OutputInterface $output) : int
{
$factoryInput = new ArrayInput([
'command' => 'factory:create',
'class' => $middlewareClass,
'--no-register' => $input->getOption('no-register'),
]);
$command = $this->getApplication()->find('factory:create');
return $command->run($factoryInput, $output);
}
}
23 changes: 23 additions & 0 deletions src/Factory/ClassNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
/**
* @see https://github.com/zendframework/zend-expressive-tooling for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-tooling/blob/master/LICENSE.md New BSD License
*/

declare(strict_types=1);

namespace Zend\Expressive\Tooling\Factory;

use InvalidArgumentException;

class ClassNotFoundException extends InvalidArgumentException
{
public static function forClassName(string $className) : self
{
return new self(sprintf(
'Class "%s" could not be autoloaded; did you perhaps mis-type it?',
$className
));
}
}
24 changes: 24 additions & 0 deletions src/Factory/ConfigFileNotWritableException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
/**
* @see https://github.com/zendframework/zend-expressive-tooling for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-tooling/blob/master/LICENSE.md New BSD License
*/

declare(strict_types=1);

namespace Zend\Expressive\Tooling\Factory;

use RuntimeException;

class ConfigFileNotWritableException extends RuntimeException
{
public static function forFile(string $file) : self
{
return new self(sprintf(
'Cannot write factory configuration to file "%s";'
. ' please make sure the file and directory are writable',
$file
));
}
}
94 changes: 94 additions & 0 deletions src/Factory/ConfigInjector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php
/**
* @see https://github.com/zendframework/zend-expressive-tooling for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-tooling/blob/master/LICENSE.md New BSD License
*/

declare(strict_types=1);

namespace Zend\Expressive\Tooling\Factory;

use const SORT_NATURAL;

/**
* Inject factory configuration in an autoloadable location.
*
* This class will re-generate the file denoted by its CONFIG_FILE constant.
* It first pulls in the data in that file, if the file exists, and then adds
* an entry for the given class, pointing it to the given factory, rewriting
* the configuration file on completion.
*/
class ConfigInjector
{
public const CONFIG_FILE = 'config/autoload/zend-expressive-tooling-factories.global.php';

public const TEMPLATE = <<<'EOT'
<?php
/**
* This file generated by %1$s.
*
* Modifications should be kept at a minimum, and restricted to adding or
* removing factory definitions; other dependency types may be overwritten
* when regenerating this file via zend-expressive-tooling commands.
*/
return [
'dependencies' => [
'factories' => [
%2$s,
],
],
];
EOT;

/**
* @var string
*/
private $configFile;

public function __construct(string $projectRoot = '')
{
$this->configFile = $projectRoot === ''
? self::CONFIG_FILE
: sprintf('%s/%s', rtrim($projectRoot, '/'), self::CONFIG_FILE);
}

public function injectFactoryForClass(string $factory, string $class) : string
{
if (! $this->configIsWritable()) {
throw ConfigFileNotWritableException::forFile($this->configFile);
}

$config = file_exists($this->configFile) ? include $this->configFile : [];
$config = $config['dependencies']['factories'] ?? [];
$config[$class] = $factory;
$configContents = sprintf(
self::TEMPLATE,
__CLASS__,
$this->normalizeConfig($config)
);
file_put_contents($this->configFile, $configContents);

return $this->configFile;
}

private function configIsWritable() : bool
{
return is_writable($this->configFile)
|| (! file_exists($this->configFile) && is_writable(dirname($this->configFile)));
}

private function normalizeConfig(array $config) : string
{
$normalized = [];
ksort($config, SORT_NATURAL);
foreach ($config as $class => $factory) {
$class .= '::class';
$factory .= '::class';

$normalized[] = sprintf('%s%s => %s', str_repeat(' ', 12), $class, $factory);
}
return implode(",\n", $normalized);
}
}
Loading

0 comments on commit 1d9bdf0

Please sign in to comment.