rapotor/console-bundle
A console bundle for Rapotor applications that adds structured command-line tooling: register CLI commands, run them from a single framework-integrated entry point, and keep terminal tasks consistent and easy to extend. Lightweight and straightforward to wire into existing apps.
rapotor/console-bundle is a Symfony2 bundle (circa 2012) whose sole purpose is to let application code — a controller, a service, another command — execute a registered Symfony console command in-process, without shelling out to php app/console. It wraps the framework's Application with ArrayInput + BufferedOutput so you can pass arguments/options as arrays and read the output back as a string.
A Laravel developer should read this assessment as "how the concept maps to Laravel, and when you'd ever touch the Symfony original." You almost certainly will not install this package in a Laravel app: it requires symfony/symfony ~2.x, uses AppKernel/app/console conventions, and cannot coexist with a modern Laravel Illuminate\Console kernel. The Laravel-native equivalent is Artisan::call().
Minimal steps if you're in a legacy Symfony2 app that already depends on it:
composer require rapotor/console-bundle (expect a dev-master or ~1.0 constraint — verify tags, the repo is effectively unmaintained).app/AppKernel.php:
new RapotOR\ConsoleBundle\RapotORConsoleBundle(),
php app/console container:debug | grep -i -E 'console|rapot'
Look in Resources/config/services.yml and DependencyInjection/ for the canonical id and the wrapper class.$console = $this->get('rapot_or.console'); // verify id
$output = $console->run('cache:clear', [], ['--no-warmup' => true]);
The exact method name and argument order vary by version; read the wrapper class (run/execute/call) and its ArrayInput construction before writing call sites.The Laravel equivalent you should actually write:
use Illuminate\Support\Facades\Artisan;
$exitCode = Artisan::call('cache:clear', ['--no-warmup' => true]);
$output = Artisan::output();
Same shape: command name, associative array of parameters (values for arguments, --key => true for flags), captured output. If your real need is "run a task from a controller," prefer extracting the task into a service or a queued job and calling it directly — the console is an input/output adapter, not a business-logic entry point.
1. In-process invocation behind a thin adapter. The only durable pattern with this package is to hide it behind an interface you own, so you can swap implementations when the app is migrated:
interface CommandRunner
{
public function run(string $command, array $args = [], array $options = []): string;
}
// Symfony2 legacy
final class BundleCommandRunner implements CommandRunner
{
public function __construct(private $console) {}
public function run(string $command, array $args = [], array $options = []): string
{
return (string) $this->console->run($command, $args, $options);
}
}
// Laravel target
final class ArtisanCommandRunner implements CommandRunner
{
public function run(string $command, array $args = [], array $options = []): string
{
\Illuminate\Support\Facades\Artisan::call($command, $args + $options);
return \Illuminate\Support\Facades\Artisan::output();
}
}
2. Controllers stay thin. A controller action that rebuilds an index should call a service method, and that service should call the command runner only if the console is genuinely the only entry point. In Laravel terms: Artisan::queue() or a ShouldQueue job beats a synchronous Artisan::call() in a request.
3. Parameter marshalling. Symfony ArrayInput treats arguments as ordered positional values and options as ---prefixed keys. Laravel's Artisan::call($command, $parameters) uses the same associative convention, so a call-site migration is mostly mechanical — but array/JSON options need explicit encoding in both (json_encode($payload)), and boolean flags must be true, not 1, if you want --flag rather than --flag=1.
4. Testing. Symfony's CommandTester (or Application::run with a BufferedOutput) is the right way to test the command itself; the bundle is only a convenience wrapper. In Laravel, use $this->artisan('cmd')->expectsOutput(...) inside a command test, and Artisan::call() assertions inside an HTTP test for controller-triggered runs.
5. Long-running work. Neither this bundle nor Artisan::call() is a job runner. Anything that takes more than a second, touches the network, or could loop belongs in a queue (dispatch(), Artisan::queue(), or Symfony's own queued commands) — not in a web request.
6. Migration mapping. When porting call sites:
| Symfony2 (this bundle) | Laravel |
|---|---|
$console->run('foo:bar', $args, $opts) |
Artisan::call('foo:bar', $args + $opts) |
Captured BufferedOutput string |
Artisan::output() |
Application::setAutoExit(false) |
Always non-terminating in Laravel |
Exit code from run() |
Return value of Artisan::call() |
app/console command class |
Illuminate\Console\Command |
symfony/symfony ~2.x constraint conflicts with laravel/framework 5.5+ and every modern Symfony component. Do not attempt it; the value here is the pattern, not the package.setAutoExit(false) is mandatory. Symfony's Application::run() calls exit() by default. If the bundle's wrapper doesn't disable auto-exit (or you construct your own Application), you'll terminate the request mid-response with no error. Verify this first when debugging "the page just stops."BufferedOutput the command writes into the HTTP response (or the web server's stdout), which looks like corrupted HTML/JSON. Always capture output explicitly.QuestionHelper or a confirmation prompt will block on STDIN. Pass --no-interaction/-n or set a non-interactive InputInterface.doctrine:fixtures:load, anything that reboots the kernel) behave differently or corrupt the current request's state.if (Artisan::call(...) !== 0) { ... }. In the Symfony original, failures can also throw \Exception from inside the command — catch both.max_execution_time or memory limits inside a web request, since it inherits the FPM/php.ini settings.rapotor.console, rapot_or.console, rapotor_console.service — all plausible. Use container:debug or read Resources/config/services.yml; don't copy snippets from blog posts.services.yml. In Laravel, bind your CommandRunner interface in a service provider ($this->app->bind(...)) so tests can substitute a fake without touching Artisan.Artisan::call() or a plain service call.How can I help you explore Laravel packages today?