Console Revisitée
@chalas_r
les-tilleuls.coop
chalasr
The Console component eases the creation of beautiful and testable command line interfaces.
@chalas_r
500M
AU TOTAL
@chalas_r
450k
PAR JOUR
@chalas_r
Composer
Symfony
Doctrine
Laravel
API Platform
Drupal
Sylius
Magento
PHPStan
...
Conséquence :
Le moindre bugfix est un BC break.
@chalas_r
@chalas_r
Tu ne le sais peut-être pas,
mais ton projet en dépend !
@chalas_r
Enregistrer une commande, c'était comment avant ?
// src/AppBundle/Command/FooCommand.php
namespace AppBundle\Command;
class FooCommand extends Command
{
protected function configure()
{
$this->setName('app:foo');
}
protected function execute()
{
// ...
}
}
@chalas_r
Tout était convention
// src/Symfony/Component/HttpKernel/Bundle/Bundle.php
public function registerCommands(Application $application)
{
if (!is_dir($dir = $this->getPath().'/Command')) {
return;
}
$finder = new Finder();
$finder->files()->name('*Command.php')->in($dir);
foreach ($finder as $file) {
$class = $file->getBasename('.php');
$r = new \ReflectionClass($class);
$application->add($r->newInstance());
}
}
@chalas_r
Commands as services à la rescousse !
services:
App\Command\FooCommand:
tags:
- { name: 'console.command' }
class FooCommand extends Command
{
protected function configure()
{
$this->setName('app:foo');
}
protected function execute()
{
// ...
}
}
@chalas_r
Laziness?
@chalas_r
Laziness - Round 1
@chalas_r
namespace Symfony\Component\Console\CommandLoader;
interface CommandLoaderInterface
{
public function get($name);
public function has($name);
public function getNames();
}
Laziness Round 1
ROUTING EN AMONT
@chalas_r
ROUTING EN AMONT
App\Command\FooCommand:
tags:
- { name: 'console.command', command: 'app:foo' }
class FooCommand extends Command
{
protected function configure()
{
// ...
}
}
@chalas_r
Laziness Round 2 : AutoConfiguration
class FooCommand extends Command
{
public static $defaultName = 'app:foo';
protected function execute($input, $output)
{
}
}
@chalas_r
Snippet montrant propriété static $defaultName et $defaultDescription et la méthode configure() mais cette fois avec le setName() et setDescription() barrées genre effacé parce que plus utile)
class FooCommand extends Command
{
public static $defaultName = 'app:foo';
public static $defaultDescription = 'A foo command';
protected function configure()
{
$this->setName('app:foo');
$this->setDescription('A foo command');
}
}
@chalas_r
Encore plus lazy
Attributes FTW
screenshot de #[AsCommand] en remplacement de $defaultName et $defaultDescription
#[AsCommand(
name: 'app:foo',
description: 'A foo command',
)]
class FooCommand extends Command
{
protected function execute($input, $output)
{
}
}
@chalas_r
Commands = Controllers
@chalas_r
En CLI, les commandes sont les points d’entrée
et de sortie de nos traitements métiers.
Tout comme le sont les contrôleurs dans un contexte HTTP.
@chalas_r
Dans les faits, commandes et contrôleurs ne sont pas logés à la même enseigne.
@chalas_r
DIFFÉRENCES MAJEURES
@chalas_r
Il est temps de changer çà.
@chalas_r
#[AsCommand]
uniquement.#[InputArgument]
et #[InputOption]
Going full attributes
@chalas_r
Invokable Command
#[AsCommand(name: "user:create", description: "Creates a user")]
final class CreateUserCommand
{
public function __invoke(...): int {
// ...
}
}
@chalas_r
#[AsCommand(name: "user:create")
final class CreateUserCommand
{
public function __invoke(
OutputInterface $output,
UserRepository $userRepository,
#[Autowire(service: 'mailer')]
MailerInterface $mailer,
#[InputArgument(mode: InputArgument::REQUIRED)]
string $email,
#[InputOption(mode: InputOption::VALUE_IS_ARRAY)]
array $roles,
): int {
$user = new User($email, $role);
$userRepository->save($user);
$mailer->send(...);
}
}
@chalas_r
Autres choses à revoir
@chalas_r
Stratégie
@chalas_r
Help welcome
Feedback,
Discussion,
Review,
Sponsoring :)
@chalas_r
Merci !
@chalas_r