Skip to content
This repository has been archived by the owner. It is now read-only.

[RFC] Silex Console Application Service Provider #542

Closed
simensen opened this Issue Nov 13, 2012 · 8 comments

Comments

Projects
None yet
4 participants
@simensen
Copy link
Contributor

simensen commented Nov 13, 2012

I'm in the process of migrating some of the Doctrine Bundle commands to Silex and thought it would be really nice to have first class Console Application support in Silex.

I have a work in progress (very rough) that I'd like to share to see if there is any interest in having this in core. If so, I'll start working on it right away. Otherwise I will continue down the path of releasing it as a standalone service provider.

NOTE: I am not suggesting that the Doctrine commands (ORM or otherwise) be brought into core. ;) It is just easier to scratch this out with them all in the same directory. Also, it happens to make for a really nice working example since I know it works.

The current implementation is pretty lite. It only exposes one service, $app['console'] and has a small number of params, console.name, console.version, and console.class.

Usage:

./console

<?php
require __DIR__.'/vendor/autoload.php';

$app = require __DIR__.'/src/app.php';
$console = require __DIR__.'/src/console.php';

$console->run();

./src/app.php

<?php
use Dflydev\Silex\Provider\DoctrineOrm\DoctrineOrmServiceProvider;
use Silex\Application;
use Silex\Provider\DoctrineServiceProvider;

$app = new Application;

$app->register(new DoctrineServiceProvider, array(
    "db.options" => array(
        "driver" => "pdo_sqlite",
        "path" => "foo.db",
    ),
));

$app->register(new DoctrineOrmServiceProvider, array(
    "orm.proxies_dir" => "proxies",
));

return $app;

./src/console.php

<?php
use Dflydev\Silex\Provider\Console\ConsoleServiceProvider;
use Dflydev\Silex\Provider\DoctrineCommands\DoctrineCommandsServiceProvider;

$app->register(new ConsoleServiceProvider(array(
    'console.name' => 'MyApp',
    'console.version' => '1.0.5-dev',
));

$app->register(new DoctrineCommandsServiceProvider);

$console = $app['console'];

$console
    ->register('my-command')
    ->setDefinition(array(
        // new InputOption('some-option', null, InputOption::VALUE_NONE, 'Some help'),
    ))
    ->setDescription('My command description')
    ->setCode(function (InputInterface $input, OutputInterface $output) use ($app) {
        // do something
    })
;

return $console;

Output:

MyApp version 1.0.5-dev

Usage:
  [options] command [arguments]

Options:
  --help           -h Display this help message.
  --quiet          -q Do not output any message.
  --verbose        -v Increase verbosity of messages.
  --version        -V Display this application version.
  --ansi              Force ANSI output.
  --no-ansi           Disable ANSI output.
  --no-interaction -n Do not ask any interactive question.

Available commands:
  help                       Displays help for a command
  list                       Lists commands
  my-command                 My command description
doctrine
  doctrine:database:create   Creates the configured databases
  doctrine:database:drop     Drops the configured databases
  doctrine:schema:update     Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata
@igorw

This comment has been minimized.

Copy link
Contributor

igorw commented Nov 13, 2012

The console is so easy to use standalone, I really don't see the point in having a service provider for it. Also, you will never have multiple console front controllers anyway. So there is not much point to central configuration.

@GromNaN

This comment has been minimized.

Copy link
Contributor

GromNaN commented Nov 13, 2012

Introducing the console in the core of Silex is not compliant with the Silex Philosophy

@simensen

This comment has been minimized.

Copy link
Contributor Author

simensen commented Nov 13, 2012

Introducing the console in the core of Silex is not compliant with the Silex Philosphy

I was aware of the Silex Philosphy and wasn't sure if this would fly. I do not see this as actually providing a complete CLI tool so I thought it might work. That is why I did this as an RFC and not a pull request. :)

There are two things here. I guess I was a bit hasty last night. I should have split it up better and probably changed the subject. :)

A Common Silex Console Application Subclass

See this class for reference. The main idea here being that if someone wants to build a console application they can do so in a way that Commands can have the ability to get access to the Silex application if they need it.

I realize that all Commands will not need this, but many Commands that are written w/o Silex in mind expect to be able to get context information from the Application. (see here and here) This is not needed for simple commands like in @fabpot's skeleton. (console.php)

I think adding this class alone to core would be enough to satisfy my needs. If people start implementing this on their own on a provider-by-provider basis (as I am currently doing) we will end up with commands that will be incompatible with each other as one set of commands might rely on the application being FirstVendor\Silex\ConsoleApplication and another set of commands relying on the application being AnotherVendor\Silex\Console\Application. I would like to avoid this if possible.

I feel like this should either be in core so everyone can reference the same class or there needs to be one third party service provider that everyone uses. Otherwise I'm afraid it will eventually become a very big mess.

Silex Console Application Service Provider

The console is so easy to use standalone, I really don't see the point in having a service provider for it.

I cannot argue with the first part of this statement. :)

The point of having it provided as a service is so that commands can be added automatically by each service provider that wants to expose commands. In my example, registering the DoctrineCommandsServiceProvider is all it takes to add a whole slew of commands to the console service. (well, eventually a whole slew; currently three :)) (example]

I'm not sure how this can be handled (elegantly) currently. If anyone wants to propose options here I'd love to discuss them.

At the very least I'd hope that we can add a Silex specific Console Application subclass to core. I think the Console Application Service Provider might be a stretch, but at least if we have a common base application class we can all write Silex-specific commands that can be expected to work (more or less) across the board.

@davedevelopment

This comment has been minimized.

Copy link
Contributor

davedevelopment commented Nov 13, 2012

With regards to the doctrine stuff, I'm not familiar with the doctrine bridge versions of the doctrine commands, but what benefit would your service provider bring over my little script:

#!/usr/bin/env php
<?php

$app = require_once __DIR__ . '/../app/bootstrap.php';

$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
    'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($app['dbal']),
    'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($app['em'])
));

\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet);
@simensen

This comment has been minimized.

Copy link
Contributor Author

simensen commented Nov 13, 2012

@davedevelopment It is possible it could be refactored some. Main issue is that the $app['dbal'] and $app['em'] can be named. For example, console doctrine:database:create --em=foo. So as far as I can tell getting --em and --connection are required at the individual command level to support the possibility of multiple connections and entity managers. See here.

I'm still early in the process of porting the commands. I ended up spending more time trying to figure out how to register the commands in a reusable way than porting the commands themselves. :) I'll dig into the ConsoleRunner stuff to see if there is anything there I can reuse. Thanks. :)

@simensen

This comment has been minimized.

Copy link
Contributor Author

simensen commented Nov 16, 2012

I've been thinking about this a lot more and have had a lengthy discussion or two on this topic in #silex-php.

I understand that console is easy to use. If you are only using it to add your own commands to your project there is no reason to do anything but write a vanilla console Application.

Symfony2 gets to leverage the container/kernel to find bundles and automatically register commands. It is not implemented as "console as a service" like my first attempt to solve this problem, but the end goals are the same: commands from zero or more service providers can be registered to a console application.

The easiest solution I could come up with is to model this after the way other similar problems are currently being handled. There is a service defined and service providers or standard application code can share/extend that service and do things with it. I don't see how this is any different that extending the Twig service to add an extension.

<?php
$app['twig'] = $app->share($app->extend('twig', function($twig, $app) {
    $twig->addExtension(new MyReusableTwigExtension);

    return $twig;
}));
<?php
$app['console'] = $app->share($app->extend('console', function($console, $app) {
    $console->addCommand(new MyReusableCommand);

    return $console;
}));

My end goal here is that I want to be able to distribute my entire application as a collection of service providers. I want to do this in order to minimize the amount of code that lives in the skeleton so that pushing updates to an app can be done safely and independent of the installed codebase.

I want my users to be able to have an app that looks like this:

<?php

// my console app

require_once __DIR__.'/vendor/autoload.php';

$app = new Silex\Application;

My\App\Configurer::configure($app, __DIR__);

$app['console']->run();
<?php

// my web index.php

require_once __DIR__.'/vendor/autoload.php';

$app = new Silex\Application;

My\App\Configurer::configure($app, realpath(__DIR__.'/..'));

$app->run();

I've explored @davedevelopment's solution and I think that it would be possible to write something that resembles ConsoleRunner::run() but it would result in a standalone application that would be limited to just Doctrine commands. I do not think it would result in something that could be easily added into an application-specific console application.

I tried to look into how I could port the migrations bundle to Silex tonight and realized that was going to be even more difficult. The DoctrinesMigrationBundle gets to leverage Config to set things like namespace and the directory that the migrations get to live in. I'd love to be able to do something like this:

<?php

$app->register(new ConsoleServiceProvider(array(
    'console.name' => 'MyApp',
    'console.version' => '1.0.5-dev',
)));

// These commands can access $app['orm.em'] or $app['orm.ems'] as
// appropriate.
$app->register(new DoctrineCommandsServiceProvider);

// These commands can access $app['db'] or $app['dbs'] as
// appropriate.
$app->register(new MigrationsServiceProvider(array(
    'migrations.namespace' => 'My\Migrations',
    'migrations.dir_name' => __DIR__.'/migrations',
    'migrations.table_name' => 'my_migrations',
)));

// These commands can access $app['http'] or whatever it
// says it needs to do its job.
$app->register(new PubSubHubbubPingServiceProvider(array(
    'pubsubhubbub.hub_url' => 'http://dflydev.superfeedr.com/',
)));

$app['console']->run();

I'm sure I can find other ways to do these things if this really cannot be supported. It will just take more work to reach my end goals. It is hard to let go when this works and feels really nice. :)

@simensen

This comment has been minimized.

Copy link
Contributor Author

simensen commented Nov 21, 2012

I've become more aware of the idea that some concepts are Silex specific and others are really just applications and collections of services built around Pimple. I think Console as a service falls into the latter category.

Since Cilex is focussed on Console as-is, it would make sense for them to leverage a generic Pimple Console service provider instead of tying their users to Cilex. I've started working with the Cilex team to extract the Console as a service part of Cilex so that it can be used by any Pimple based project.

I think this probably does not belong in Silex so I am closing this RFC for now.

@simensen simensen closed this Nov 21, 2012

@simensen

This comment has been minimized.

Copy link
Contributor Author

simensen commented Dec 17, 2012

Cilex is now using a standalone console service provider and there is a Silex adapter so in theory anyone can now write command providers for both Cilex, Silexl, and any Pimple based application: https://github.com/Cilex/console-service-provider

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
You can’t perform that action at this time.