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

Zero Framework Laravel Package

nunomaduro/zero-framework

Zero Framework is Nuno Maduro's minimalist, zero-configuration PHP framework for building console apps and small projects. It removes boilerplate and setup so you can focus on writing code, with sensible defaults and a tiny footprint.

View on GitHub
Deep Wiki
Context7

Getting Started

Know what you're installing. laravel-zero/framework is the runtime core of a Laravel Zero console app — the Application bootstrapper, the base Command class, the component installers, and the app:build PHAR builder. It is not something you composer require into an existing Laravel web app. You consume it indirectly by scaffolding a project:

composer create-project laravel-zero/laravel-zero my-cli
cd my-cli
php my-cli list          # the root executable *is* your app

The scaffold pins this package in composer.json; treat the repo as read-only reference (and the place to file framework bugs), not as code you vendor-patch.

First four files to open, in order:

File Why
my-cli (root executable) Entry point; app:rename rewrites it
bootstrap/app.php Bootstraps the container and kernel
config/app.php providers array — where your service providers are registered
config/commands.php Command discovery paths (default app/Commands)

First use case — a real command in under five minutes:

php my-cli make:command ReportDaily

This writes app/Commands/ReportDailyCommand.php. Fill in the signature and handle():

namespace App\Commands;

use LaravelZero\Framework\Commands\Command;

class ReportDailyCommand extends Command
{
    protected $signature = 'report:daily {date?} {--format=table}';
    protected $description = 'Print the daily summary';

    public function handle(): int
    {
        $this->info('Building report for '.($this->argument('date') ?? 'today'));
        $this->table(['Metric', 'Value'], [['signups', 42], ['churn', 3]]);

        return self::SUCCESS;
    }
}

Run it with php my-cli report:daily. Commands in app/Commands are auto-discovered — no registration step.

Implementation Patterns

Command anatomy. Prefer the $signature string over the older $name/$arguments properties; it's the only form that supports options, array arguments, and defaults in one place. Always declare handle(): int and return self::SUCCESS / self::FAILURE — exit codes are your contract with cron, CI, and shell scripts.

Input and output. Everything you'd expect from illuminate/console is available:

$name  = $this->ask('Project name?', 'demo');
$force = $this->confirm('Overwrite?', false);
$env   = $this->choice('Target', ['staging', 'prod'], 0);

$bar = $this->output->createProgressBar($total);
$bar->advance(); $bar->finish();

$this->components->task('Fetching', fn () => $this->fetch());

$this->line/info/comment/warn/error are the only output calls you need; verbosity is handled by -v/-vvv automatically.

Dependency injection. The container resolves handle() parameters, so type-hint services rather than resolving them manually:

public function handle(ReportBuilder $builder): int

Bind implementations in a provider registered inside config/app.php:

$this->app->singleton(ReportBuilder::class, fn ($app) => new ReportBuilder($app['config']->get('reports')));

Components are the framework's real day-to-day value. Add capability on demand instead of dragging in the full Laravel app:

php my-cli app:install database     # Eloquent + migrations: php my-cli migrate
php my-cli app:install logging
php my-cli app:install schedule
php my-cli app:install dotenv
php my-cli app:install self-update
php my-cli app:install docker

Each installer edits composer.json, drops config files into config/, and scaffolds any needed provider/command class. Commit immediately after running one so you can see exactly what it changed.

Scheduling. After app:install schedule, define tasks in the schedule class the installer scaffolds. Because there's no long-running daemon, you drive it from cron:

* * * * * cd /path/to/app && php my-cli schedule:run >> /dev/null 2>&1

Testing. The scaffold ships with Pest; artisan() is the workhorse:

it('prints the report', function () {
    $this->artisan('report:daily', ['date' => '2026-03-24'])
        ->expectsOutputToContain('signups')
        ->assertExitCode(0);
});

Prefer assertExitCode over output text matching where you can — output wording changes more often than behaviour.

Distribution. php my-cli app:build produces a PHAR in builds/ using Box. Pass --build-version=1.2.3 to stamp it. This is the artifact you hand to users; app:install self-update makes the binary update itself in place.

Config and env. Use config('x.y') everywhere in application code. env() is only reliable inside config/*.php, and only until config:cache is run.

Gotchas and Tips

The PHAR is read-only — this is the number one production surprise. When running from builds/my-cli, base_path() points inside the archive. Anything that writes — SQLite files, storage/logs, caches, downloads — must target a path outside it. Compute a writable home explicitly:

$home = getenv('HOME').'/.my-cli';

and point database.connections.sqlite.database and logging.channels.*.path there. Test the built PHAR, not just php my-cli — the two have different filesystem semantics.

Interactive prompts hang in CI and cron. $this->ask() on a non-TTY stdin blocks or throws. Guard with $this->input->isInteractive() or use $this->option('no-interaction'); always support a --no-interaction path for anything scheduled.

Commands outside app/Commands aren't discovered. If you namespace commands under src/ (common when extracting a package), add the path to config/commands.php under the commands key or register via $this->commands([...]) in a provider. Silent "command not found" is almost always this.

Config caching nulls your env vars. After config:cache, env() outside config files returns null. If a command works in dev and breaks after a cache, this is why. Run config:clear when debugging.

Keep illuminate/* versions in lockstep with the framework. Laravel Zero tracks Laravel majors; a mismatched illuminate/support produces confusing container or console failures that look like framework bugs. Let Composer resolve them together and don't hand-pin individual Illuminate packages.

Exceptions and exit codes. An uncaught exception yields a non-zero exit, but a caught-and-logged one doesn't — if your cron job is silently "succeeding" on failure, check that you return self::FAILURE in the catch block.

Long-running commands should trap signals so Ctrl-C and container SIGTERM exit cleanly:

$this->trap(SIGINT, fn () => $this->cleanup());

Component installers mutate your project. app:install touches composer.json, config/, and providers. In a dirty working tree you can't tell what it did — commit first, then review the diff.

Self-update only works in a PHAR. Running it from source either no-ops or errors; don't try to test the upgrade path with php my-cli.

Extension point of last resort: the Application class and kernel are extendable, but for 95% of needs a service provider plus a command is enough. Provider registration in config/app.php is the supported seam — reach for it before subclassing framework internals whose signatures may shift between majors.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core