mnapoli/silly
Silly is a lightweight CLI micro-framework built on Symfony Console. Define commands with simple signatures and PHP callables, get options/arguments parsing, helpers, and DI integration (PHP-DI or Pimple) while staying compatible with Symfony Console apps.
Installation:
composer require mnapoli/silly
For PHP-DI integration (recommended for Laravel projects):
composer require mnapoli/silly-php-di
Basic CLI Setup:
Create a console.php file in your project root:
<?php
require __DIR__.'/vendor/autoload.php';
$app = new \Silly\Application();
$app->command('greet [name]', function ($name = 'World') {
echo "Hello, $name!\n";
});
$app->run();
First Use Case: Run your command:
php console.php greet Laravel
Output: Hello, Laravel!
Laravel-style Artisan-like structure:
// app/Console/Commands/GreetCommand.php
namespace App\Console\Commands;
use Silly\Application;
class GreetCommand
{
public function __invoke($name = 'World')
{
echo "Hello, $name!\n";
}
}
// Register in bootstrap
$app = new Application();
$app->command('greet [name]', [new GreetCommand(), '__invoke']);
// Using PHP-DI bridge
$app = new \Silly\Application\PhpDiApplication();
$app->bind(\App\Services\UserService::class)
->to(\App\Services\LivewireUserService::class);
$app->command('user:list', function (UserService $userService) {
$users = $userService->all();
// ...
});
use Symfony\Component\Console\Style\SymfonyStyle;
$app->command('deploy', function (SymfonyStyle $io) {
$io->title('Starting deployment');
$io->section('Checking dependencies');
// ...
});
$app->command('user:create', /* ... */);
$app->command('user:list', /* ... */);
$app->add(new \Silly\Command\CommandGroup('user', 'User management'));
// In bootstrap file
$app = new Application();
$app->setName('Laravel');
$app->setVersion('10.x');
// Add Laravel's default commands
$app->add(new \Illuminate\Foundation\Console\ArtisanCommand());
// config/console.php
return [
'commands' => [
'greet' => [
'callback' => [new \App\Console\Commands\GreetCommand(), '__invoke'],
'description' => 'Greet someone',
],
],
];
// In bootstrap
$config = require __DIR__.'/../config/console.php';
foreach ($config['commands'] as $name => $config) {
$app->command($name, $config['callback'])
->description($config['description']);
}
$app->on('command.test', function ($event) {
// Pre-command logic
});
$app->on('command.test.complete', function ($event) {
// Post-command logic
});
Parameter Order Sensitivity:
// ❌ Will fail - order matters for named parameters
$app->command('test [name] [--age=]', function ($age, $name) {
// $age will be null, $name will be the age
});
// ✅ Correct
$app->command('test [name] [--age=]', function ($name, $age = 30) {
// Works with default value
});
Hyphen Handling:
// ❌ Will throw error
$app->command('test [--dry-run]', function ($dryRun) {
// ...
});
// ✅ Correct (hyphens converted to camelCase)
$app->command('test [--dry-run]', function ($dryRun) {
// ...
});
Container Binding Conflicts:
// ❌ Will cause "Cannot resolve service" if both exist
$container->set('logger', $logger);
$app->command('test', function (\Psr\Log\LoggerInterface $logger) {
// ...
});
Command Introspection:
$app->get('help')->setFormatter(new \Symfony\Component\Console\Formatter\OutputFormatter());
$app->run(new \Symfony\Component\Console\Input\ArrayInput(['help']));
Parameter Dumping:
$app->command('debug:params', function ($input, $output, $args, $options) {
$output->writeln('Arguments: ' . print_r($args, true));
$output->writeln('Options: ' . print_r($options, true));
});
Container Inspection:
$app->command('debug:container', function () {
$container = $app->getContainer();
foreach ($container->find() as $id => $entry) {
echo "$id: " . get_class($entry) . "\n";
}
});
Default Command Behavior:
// ❌ Won't work as expected
$app->setDefaultCommand('greet');
$app->command('greet', /* ... */);
// ✅ Correct
$app->command('greet', /* ... */);
$app->setDefaultCommand('greet');
Option Shortcuts:
// ❌ Will conflict
$app->command('test [-y|--yell]', /* ... */);
// ✅ Correct (use consistent format)
$app->command('test [--yell|y]', /* ... */);
Custom Command Helpers:
// app/Console/SillyHelper.php
class SillyHelper
{
public static function confirm($question, $default = true)
{
$helper = new \Symfony\Component\Console\Helper\QuestionHelper();
return $helper->ask($question, $default);
}
}
// Usage
$app->command('migrate', function () {
if (!SillyHelper::confirm('Proceed with migration?')) {
exit(1);
}
// ...
});
Middleware Pattern:
$app->on('command.test', function ($event) {
$start = microtime(true);
$event->getCommand()->setCode(function () use ($start) {
$duration = microtime(true) - $start;
echo "Command took $duration seconds\n";
});
});
Custom Input/Output:
$app->setInput(new \Symfony\Component\Console\Input\ArgvInput());
$app->setOutput(new \Symfony\Component\Console\Output\BufferedOutput());
How can I help you explore Laravel packages today?