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

Migrate to symfony/console #23

Merged
merged 15 commits into from
Apr 11, 2017

Conversation

weierophinney
Copy link
Member

@weierophinney weierophinney commented Apr 10, 2017

This patch migrates the tooling to use symfony/console instead of zend-stdlib's ConsoleHelper. The reasons are:

  • We were needing to write argument parsing routines for each and every command, and each differed.
  • We were needing to write "help" functionality for each command.
  • We were creating a plethora of entry points. We should have one, with many commands available.
  • While we could use zf-deploy, using symfony/console will likely get more contributors to the project, due to its ubiquity in the PHP ecosystem. In the end, I also discovered that I ended up writing less code overall in order to create commands, and had better, simpler options for re-use when re-purposing existing command endpoints to re-use the same commands as used by the main entry point.

All existing commands continue to work. I have updated the expressive tool to use the following patterns:

  • migrate:*: the various tools dedicated to migration.
  • module:*: the various tools dedicated to module manipulation.
  • middleware:*: the tools dedicated to middleware manipulation
    (currently only creation).

TODO

  • Update unit tests.
  • Add deprecation warnings to original, now legacy, scripts.

This patch migrates the tooling to use symfony/console instead of
zend-stdlib's `ConsoleHelper`. The reasons are:

- We were needing to write argument parsing routines for each and every
  command, and each differed.
- We were needing to write "help" functionality for each command.
- We were creating a plethora of entry points. We should have one, with
  many commands available.
- While we could use zf-deploy, using symfony/console will likely get
  more contributors to the project, due to its ubiquity in the PHP
  ecosystem. In the end, I also discovered that I ended up writing less
  code overall in order to create commands, and had better, simpler
  options for re-use when re-purposing existing command endpoints to
  re-use the same commands as used by the main entry point.

All existing commands continue to work. I have updated the `expressive`
tool to use the following patterns:

- `migrate:*`: the various tools dedicated to migration.
- `module:*`: the various tools dedicated to module manipulation.
- `middleware:*`: the tools dedicated to middleware manipulation
  (currently only creation).
- Removed obsolete `Command` and `Help` tests
- Re-arranged `Module` tests and updated namespaces and class names
Copy link
Member

@michalbundyra michalbundyra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like update to symfony/console! Many things is already done there so it simplify our library :) Couple of things to change and should be fine. Of course updating tests as you pointed is needed. Thanks!

} catch (Exception\RuntimeException $e) {
$console = $this->getErrorConsole($output);
$console->writeln('<error>Error during execution:</error>', true, STDERR);
$console->writeln(sprintf(' <error>%s</error>', $ex->getMessage()), true, STDERR);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$ex is not defined. Here should be $e or variable in catch should be renamed (I'd prefer to use $ex for exception)

$output->writeln(sprintf('<info>%s</info>', $message));
} catch (Exception\RuntimeException $e) {
$console = $this->getErrorConsole($output);
$console->writeln('<error>Error during execution:</error>', true, STDERR);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now we have different signature of writeln method - from symfony, so there are two params: $messages and $options.

$disable = new Disable($this->projectDir, $modulesPath, $composer);
$disable->process($module);
} catch (RuntimeException $ex) {
$console = $this->getErrorConsole();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should be called with parameter. It's missing here.

$enable->setMoveModuleClass(false);
$enable->process($module);
} catch (RuntimeException $ex) {
$console = $this->getErrorConsole();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above - missing required param.

@@ -34,9 +34,9 @@ class Scanner implements Countable, IteratorAggregate

/**
* @param string $path
* @param ConsoleHelper $console
* @param OutputInterface $console
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same should be changed also in line 21.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

This patch updates all tests as follows:

- Removes obsolete `Command` and `Help` tests.
- Adds tests for new symfony/console commands.
- Fixes test setup and expectations for classes that now depend on a
  symfony/console `OutputInterface` instance.
- Fixes issues found during testing, including several also reported by
  @webimpress.
@weierophinney
Copy link
Member Author

All tests updated and/or new tests written; doing so addressed most of the feedback from @webimpress, and I've updated a property typehint per his feedback as well.

@weierophinney
Copy link
Member Author

Pinging @GeeH, @akrabat, and @calevans for feedback...

use Zend\Stdlib\ConsoleHelper;
use Symfony\Component\Console\Application;

const VERSION = '%version%';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cough PackageVersions cough

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cough PHP 5.6 cough

bin/expressive Outdated
$return = $command->process(array_slice($argv, 1));
exit($return);
$application = new Application('expressive', VERSION);
$application->add(new CreateMiddleware\CreateMiddlewareCommand('middleware:create'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, wasn't aware of that one!

$command = new Command($argv[0], new ConsoleHelper());
$return = $command->process(array_slice($argv, 1));
exit($return);
$application = new Application('expressive-migrate-original-messages', VERSION);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can include the main CLI app, then set the default command instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a BC measure; the goal is to eventually remove it.

Also note that the command names differ between the main expressive script and this one.

$return = $command->process(array_slice($argv, 1));
exit($return);
$application = new Application('expressive-module', VERSION);
$application->add(new CreateCommand('create'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this done just to keep the command names BC compliance?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Since we already have documented migration steps, I cannot break how these original scripts worked; hence the difference between command names and what commands are exposed.

$command = new Command($argv[0], new ConsoleHelper());
$return = $command->process(array_slice($argv, 1));
exit($return);
$application = new Application('expressive-pipeline-from-config', VERSION);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: main app, set default command in this file

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above; this is a BC measure.

'<error>Unable to determine src directory: %s</error>',
$e->getMessage()
));
return 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let exceptions bubble up. If the exception message is meaningful, no additional code is needed

$creation = new Create();
$message = $creation->process($module, $modulesPath, $this->projectDir);
$output->writeln(sprintf('<info>%s</info>', $message));
} catch (Exception\RuntimeException $e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove, let it bubble up

try {
$disable = new Disable($this->projectDir, $modulesPath, $composer);
$disable->process($module);
} catch (RuntimeException $ex) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove, let it bubble up

$enable = new Enable($this->projectDir, $modulesPath, $composer);
$enable->setMoveModuleClass(false);
$enable->process($module);
} catch (RuntimeException $ex) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

{
try {
$src = $this->getSrcDir($input);
} catch (ArgvException $e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

@GeeH
Copy link

GeeH commented Apr 11, 2017

The move to this library is amazing, we should always look to use community defacto libraries instead of having to code and support our own whatever the vendor namespace. I'm happy.

I don't have time to review the code sorry (and I don't know the library very well anyway), but...

image

Per @Ocramius, it's simpler to allow exceptions to bubble up, as
symfony/console will report the exception message always, and, when
verbosity is enabled, more details including the stack trace.
@weierophinney
Copy link
Member Author

@Ocramius I've incorporated your feedback; thanks a ton!

return 1;
}
$generator = new Generator($output);
$generator->projectDir = $this->projectDir;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argh... Missed this before, why is this necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually... no longer necessary. It was necessary before I started mocking the various internal classes, but now it isn't. I'll nuke it in the various *Command classes.

It's still necessary for the internal classes that do filesystem operations, though I may be able to move that to constructor arguments. I'll review.

/**
* Add default arguments and options used by all commands.
*/
private function addDefaultOptionsAndArguments()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a public static function, referenced by classes using it.

* @param InputInterface $input
* @return string
*/
private function getModulesPath(InputInterface $input)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a public static function, referenced by classes using it.

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;

trait CommandCommonTrait
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be moved to a static class and marked as @internal, since we really don't want to distribute this as public/reusable API

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough; I'll make that change (and the ones you suggest below) momentarily.

This includes all commands, though a few, where we needed to test path
existence, required the ability to inject a path, which is now handled
by a setter; when the setter is not called, `getcwd()` is then assumed.

For internal classes, this is now handled as either a passed argument,
or via a constructor argument.
@Ocramius Ocramius self-assigned this Apr 11, 2017
@Ocramius
Copy link
Member

🚢

@Ocramius Ocramius merged commit bfc8eeb into zendframework:master Apr 11, 2017
@weierophinney weierophinney deleted the feature/symfony-console branch April 11, 2017 18:03
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants