Installation and CLI

The framework has its own CLI runtime and command registry. HTTP and CLI bootstrap both load shared configuration and service providers.

Installation

Composer adds the framework to an application running PHP 8.3–8.5 with fileinfo, mbstring, and xmlwriter. The second command serves public/ with PHP’s built-in server after the HTTP entry point in the next step exists.

Terminal
composer require johnnyxlemonade/framework
php -S 127.0.0.1:8000 -t public

public/index.php loads Composer, builds an ApplicationContext for the project root, and creates the HTTP kernel. handle() processes the current request.

public/index.php
<?php

declare(strict_types=1);

use Lemonade\Framework\Core\Context\ApplicationContextFactory;
use Lemonade\Framework\Core\KernelFactory;

require dirname(__DIR__) . '/vendor/autoload.php';

$context = (new ApplicationContextFactory())->fromGlobals(dirname(__DIR__));
$kernel = (new KernelFactory())->create($context);
$kernel->handle();

Application layout

The tree keeps configuration, routes, controllers, and views under app/ apart from the entry points. public/index.php handles HTTP; vendor/bin/lemonade runs CLI commands. Both load application configuration, although the CLI manifest may select different modules.

Application tree
app/
├── Config/          # YAML configuration and Routing.php
├── Controllers/     # HTTP actions
├── Language/        # translation catalogs
├── Providers/       # application service bindings
└── Views/           # layouts, pages and partials
public/index.php      # HTTP entry point
storage/              # cache, logs and sessions
vendor/bin/lemonade   # CLI entry point

Command and registration

ImportProductsCommand implements CommandInterface. run() receives arguments after the command name, prints their count through CommandOutput, and returns a process exit code. It is a command stub, not a product import; Commands.yaml supplies its name and description next.

app/Console/ImportProductsCommand.php
<?php

declare(strict_types=1);

namespace App\Console;

use Lemonade\Framework\Cli\CommandInterface;
use Lemonade\Framework\Cli\CommandOutput;

final readonly class ImportProductsCommand implements CommandInterface
{
    public function __construct(private CommandOutput $output) {}

    public function name(): string { return 'products:import'; }
    public function description(): string { return 'Inspect product import arguments.'; }

    public function run(array $args): int
    {
        $this->output->writeln('Arguments: ' . count($args));
        return 0;
    }
}

Commands.yaml maps products:import to the command class and provides list/help text. The definition can be registered without constructing the class; resolution happens when the command runs.

app/Config/Commands.yaml
module: commands
config:
  commands:
    - name: products:import
      class: App\Console\ImportProductsCommand
      description: Inspect product import arguments.

This Config.yaml fragment loads Commands.yaml during CLI bootstrap. Without it, products:import is not registered even if its PHP class exists.

app/Config/Config.yaml
cli:
  - Commands

The first command lists available definitions. The second resolves ImportProductsCommand in Command scope and calls run(); this stub prints its argument count.

Terminal
vendor/bin/lemonade list
vendor/bin/lemonade products:import
Related documentationRead the CLI and cron chapter in the documentation for more detail.

← All examples