Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Zero Laravel Package

nunomaduro/laravel-zero

Laravel Zero is a micro-framework for building elegant console applications in PHP. Powered by Laravel components, it offers Artisan commands, a familiar structure, and a lightweight footprint for fast, testable CLI tools.

View on GitHub
Deep Wiki
Context7

Getting Started

Laravel Zero is a stripped-down Laravel distribution tuned for CLI applications: no HTTP kernel, no routing, no Blade by default — just the console kernel, the service container, config, and whichever Illuminate components you pull in.

Scaffold a project (don't composer require into an existing app; the skeleton is the app):

composer create-project laravel-zero/laravel-zero my-cli
cd my-cli
php my-cli list          # the binary in the project root is your entry point

Where to look first, in order:

  1. my-cli — the root PHP binary. It bootstraps bootstrap/app.php; this is your public/index.php equivalent.
  2. app/Commands/ — every command class lives here. This is 90% of your day-to-day editing.
  3. config/commands.php — controls command auto-discovery and the app:build output path/name.
  4. config/app.php — the providers array; register your own service providers here.
  5. app/Console/Kernel.php — where you register scheduled tasks.

First use case — the canonical one: a command that hits an API, formats output, and can be shipped as a single binary.

// app/Commands/FetchUsers.php
namespace App\Commands;

use Illuminate\Support\Facades\Http;
use LaravelZero\Framework\Commands\Command;

class FetchUsers extends Command
{
    protected $signature = 'users:fetch {--limit=25 : Max records}';
    protected $description = 'Fetch users from the API';

    public function handle(): int
    {
        $users = Http::get('https://api.example.com/users', [
            'limit' => (int) $this->option('limit'),
        ])->throw()->json();

        $this->table(['ID', 'Name', 'Email'], collect($users)->map(fn ($u) => [
            $u['id'], $u['name'], $u['email'],
        ])->all());

        return self::SUCCESS;
    }
}

Run it with php my-cli users:fetch --limit=5. No registration step needed — auto-discovery picks it up.

Implementation Patterns

Command anatomy. Always extend LaravelZero\Framework\Commands\Command (it extends the Illuminate command and wires up the app instance). Return self::SUCCESS / self::FAILURE so exit codes are meaningful in shell pipelines and CI.

Constructor injection beats facades for anything you want to unit test. Container resolution works the same as Laravel:

public function __construct(private UserRepository $users) {
    parent::__construct();
}

public function handle(): int { /* $this->users is ready */ }

Interactive workflows are the reason people pick this over Symfony Console. Chain prompts rather than flag-stuffing:

$name = $this->ask('Project name?');
$type = $this->choice('Type?', ['api', 'worker', 'web'], 0);
if ($this->confirm('Create the project?', true)) { /* ... */ }

Pair with $this->progressBar / $this->withProgressBar($items, $fn) for long loops, and $this->task('Label', fn () => ...) for step reporting. Use $this->components->info() / ->error() for modern Laravel output.

Long-running loops. Call $this->output->setVerbosity() carefully; for daemon-style commands use $this->trap() (SIGINT/SIGTERM) to shut down cleanly and sleep() between iterations.

Scheduling. Register in app/Console/Kernel.php exactly as in Laravel:

protected function schedule(Schedule $schedule): void
{
    $schedule->command('users:fetch')->dailyAt('02:00')->appendOutputTo(storage_path('logs/fetch.log'));
}

Then drive it from cron: * * * * * cd /path/to/my-cli && php my-cli schedule:run >> /dev/null 2>&1.

Persistence. SQLite is the natural fit — ship the schema as migrations and run php my-cli migrate on install:

php my-cli make:migration create_users_table
# config/database.php: 'default' => 'sqlite', database => storage_path('app/database.sqlite')
php my-cli migrate --force

Eloquent, models, factories and seeders all work. Keep the DB file under storage/ — never inside the compiled PHAR.

Splitting logic from the command. Treat commands as thin controllers. Put business rules in plain classes under app/Services or app/Actions and inject them. This is what lets you test without $this->artisan().

Building a distributable. php my-cli app:build compiles the app into builds/my-cli (PHAR). Configure the name and entry point in config/commands.php:

'build' => [
    'phar_name' => 'my-cli',
    'version' => '1.0.0',
],

Ship that single file — no PHP install assumptions beyond the PHAR runtime.

Testing. Use $this->artisan('users:fetch')->assertExitCode(0) inside a Tests\TestCase extending the Laravel Zero test case, and Http::fake() for the network. For pure logic, test the injected service class directly.

Gotchas and Tips

  • There is no HTTP layer. No routes, no routes/, no Http\Kernel. If you need a web UI, this is the wrong skeleton. Some people add one back manually; don't — use a full Laravel app.
  • PHAR paths bite. __DIR__, realpath(), and file_put_contents() relative to the binary will fail because the PHAR is read-only. Always resolve writable locations via storage_path() or base_path(), and remember base_path() inside a PHAR points into the archive. Configure storage outside the build or use $_SERVER['HOME'].
  • .env loading is subtle. Laravel Zero loads .env from the project root and also looks next to the binary. When distributing a PHAR, prefer --flag options or a config file in the user's home directory over expecting a .env.
  • Command discovery is cached-ish. If a new command isn't showing in php my-cli list, run php my-cli clear-compiled (or delete bootstrap/cache/). Namespace/class-name mismatches in app/Commands are the usual cause.
  • app:build needs a clean state. Clear config and compiled caches before building; a cached config baked into the PHAR will override the user's real one and produce very confusing bug reports. Consider config:cache off entirely for distributed builds.
  • Version skew. The framework tracks Laravel/Illuminate major versions, and the release metadata on this listing is stale. Pin laravel-zero/framework to the version matching the Illuminate components you need, and check the upgrade guide between majors — config keys (config/app.php, config/commands.php) have moved across releases.
  • Facades that assume a request/response cycle (Request, URL, Session) will throw or misbehave. Stick to DB, Http, Cache, Log, Config, Artisan.
  • Debugging: run with -vvv for the full exception trace, and --ansi when piping into files. For container problems, dump with $this->getApplication()->getLaravel() or register a tiny dump:container command.
  • Exit codes in CI. $this->error() does not set a non-zero exit code. Return self::FAILURE explicitly or your CI job will go green on failure.
  • Extension points worth knowing: the providers array in config/app.php for bootstrapping, config/commands.php's commands array to explicitly register commands outside app/Commands, the Commands\Kernel-level defaultCommands, and publishing framework config with php my-cli vendor:publish. Overriding a framework-provided command is a matter of registering your class after auto-discovery and giving it the same signature.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core