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.
composer require johnnyxlemonade/framework
php -S 127.0.0.1:8000 -t publicpublic/index.php loads Composer, builds an ApplicationContext for the project root, and creates the HTTP kernel. handle() processes the current request.
<?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.
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 pointCommand 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.
<?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.
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.
cli:
- CommandsThe first command lists available definitions. The second resolves ImportProductsCommand in Command scope and calls run(); this stub prints its argument count.
vendor/bin/lemonade list
vendor/bin/lemonade products:import