Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,282 changes: 656 additions & 626 deletions README.md

Large diffs are not rendered by default.

672 changes: 357 additions & 315 deletions WebFiori/Cli/Discovery/CommandDiscovery.php

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions examples/13-auto-discovery/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Auto-Discovery with Factory

This example demonstrates how to use `CommandDiscovery::setFactory()` to inject dependencies into auto-discovered commands.

## Problem

By default, auto-discovery instantiates commands using `new $className()`, which requires zero-argument constructors. Commands that need dependencies (database connections, services, configuration) cannot use auto-discovery without a factory.

## Solution

Set a factory callable that knows how to construct each command:

```php
$discovery->setFactory(function (string $className) use ($container) {
return $container->get($className);
});
```

## Running

```bash
php main.php greet
# Output: Hello from the container!

php main.php status
# Output:
# Application is running.
# Version: 2.1.0
```

## Key Points

- The factory receives the fully qualified class name as its only argument
- It must return a `Command` instance
- If no factory is set, the default `new $className()` behavior is used
- Factory exceptions are caught and handled like any other instantiation error
- Works with any DI container (PSR-11, webfiori/container, or a simple closure)
21 changes: 21 additions & 0 deletions examples/13-auto-discovery/commands/GreetCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

use WebFiori\Cli\Command;

/**
* A command that requires a greeting message injected via constructor.
*/
class GreetCommand extends Command {
private string $greeting;

public function __construct(string $greeting) {
parent::__construct('greet', [], 'Display a greeting from the service container');
$this->greeting = $greeting;
}

public function exec(): int {
$this->println($this->greeting);

return 0;
}
}
22 changes: 22 additions & 0 deletions examples/13-auto-discovery/commands/StatusCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

use WebFiori\Cli\Command;

/**
* A command that requires a version string injected via constructor.
*/
class StatusCommand extends Command {
private string $version;

public function __construct(string $version) {
parent::__construct('status', [], 'Show application status');
$this->version = $version;
}

public function exec(): int {
$this->println('Application is running.');
$this->println('Version: ' . $this->version);

return 0;
}
}
44 changes: 44 additions & 0 deletions examples/13-auto-discovery/main.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/**
* Auto-Discovery with Factory Example
*
* Demonstrates how to use a factory callable with command auto-discovery
* to inject dependencies into commands that require constructor arguments.
*/

use WebFiori\Cli\Runner;
use WebFiori\Cli\Discovery\CommandCache;
use WebFiori\Cli\Discovery\CommandDiscovery;

require_once __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__ . '/commands/GreetCommand.php';
require_once __DIR__ . '/commands/StatusCommand.php';

// Simulate a simple service container
$services = [
'greeting' => 'Hello from the container!',
'version' => '2.1.0',
];

// Create the runner
$runner = new Runner();

// Set up discovery with a factory that injects dependencies
$discovery = new CommandDiscovery(new CommandCache(enabled: false));
$discovery->addSearchPath(__DIR__ . '/commands');
$discovery->setFactory(function (string $className) use ($services) {
// Use a match or conditional to provide dependencies per command
return match ($className) {
GreetCommand::class => new GreetCommand($services['greeting']),
StatusCommand::class => new StatusCommand($services['version']),
default => new $className(),
};
});

// Assign discovery to runner
$runner->setCommandDiscovery($discovery);
$runner->discoverCommands();

// Run the CLI
exit($runner->start());
Loading
Loading