Unified command-line interface for the Neuron PHP framework

0.1.4 2025-08-14 15:12 UTC

This package is auto-updated.

Last update: 2025-08-14 15:13:52 UTC


README

A unified command-line interface for the Neuron PHP framework that provides a modern, extensible CLI tool for all Neuron components.

Features

  • Unified CLI Interface - Single entry point for all component commands
  • Automatic Command Discovery - Auto-detects commands from installed components
  • Rich Terminal Output - Colored output, tables, and progress bars
  • Zero Configuration - Works out of the box with any Neuron component
  • Extensible Architecture - Easy to add custom commands
  • Built on Solid Foundation - Extends Neuron's CommandLineBase for consistency

Requirements

  • PHP 8.4 or higher
  • Composer
  • Neuron Application component

Installation

Local Installation (Recommended)

composer require neuron-php/cli

After installation, the CLI will be available at:

./vendor/bin/neuron

Global Installation

composer global require neuron-php/cli

Make sure your global Composer bin directory is in your PATH, then:

neuron version

Basic Usage

List All Commands

neuron list

Get Help

# General help
neuron --help

# Help for specific command
neuron help make:controller

Run Commands

# Run a command
neuron make:controller UserController

# With options
neuron make:controller UserController --resource

# With verbose output
neuron make:controller UserController -v

For Component Developers

The CLI component provides two ways for your component to register commands:

Method 1: CLI Provider Class (Recommended)

This is the preferred method as it keeps all command registrations in one place.

  1. Update your component's composer.json:
{
    "name": "neuron-php/your-component",
    "extra": {
        "neuron": {
            "cli-provider": "Neuron\\YourComponent\\Cli\\CommandProvider"
        }
    }
}
  1. Create the provider class:
<?php

namespace Neuron\YourComponent\Cli;

class CommandProvider
{
    public static function register($app): void
    {
        // Register your commands
        $app->register('component:command1', Command1::class);
        $app->register('component:command2', Command2::class);
        $app->register('component:command3', Command3::class);
    }
}

Method 2: Direct Registration

For simpler cases, you can register commands directly in composer.json:

{
    "name": "neuron-php/your-component",
    "extra": {
        "neuron": {
            "commands": {
                "component:command": "Neuron\\YourComponent\\Commands\\YourCommand",
                "component:another": "Neuron\\YourComponent\\Commands\\AnotherCommand"
            }
        }
    }
}

Creating Command Classes

All commands must extend the base Command class:

<?php

namespace Neuron\YourComponent\Commands;

use Neuron\Cli\Commands\Command;

class MakeControllerCommand extends Command
{
    /**
     * Get the command name (how it's invoked)
     */
    public function getName(): string
    {
        return 'make:controller';
    }
    
    /**
     * Get the command description (shown in list)
     */
    public function getDescription(): string
    {
        return 'Create a new controller class';
    }
    
    /**
     * Configure arguments and options
     */
    public function configure(): void
    {
        // Add required argument
        $this->addArgument('name', true, 'The controller name');
        
        // Add optional argument with default
        $this->addArgument('namespace', false, 'Custom namespace', 'App\\Controllers');
        
        // Add boolean option (flag)
        $this->addOption('resource', 'r', false, 'Create a resource controller');
        
        // Add option that accepts a value
        $this->addOption('model', 'm', true, 'Model to bind to controller');
        
        // Add option with default value
        $this->addOption('template', 't', true, 'Template to use', 'default');
    }
    
    /**
     * Execute the command
     * 
     * @return int Exit code (0 for success)
     */
    public function execute(): int
    {
        // Get arguments
        $name = $this->input->getArgument('name');
        $namespace = $this->input->getArgument('namespace');
        
        // Get options
        $isResource = $this->input->getOption('resource');
        $model = $this->input->getOption('model');
        
        // Output messages
        $this->output->info("Creating controller: {$name}");
        
        // Show progress for long operations
        $progress = $this->output->createProgressBar(100);
        $progress->start();
        
        for ($i = 0; $i < 100; $i++) {
            // Do work...
            $progress->advance();
            usleep(10000);
        }
        
        $progress->finish();
        
        // Success message
        $this->output->success("Controller created successfully!");
        
        return 0; // Success
    }
}

Command Naming Conventions

  • Use namespace format: component:action (e.g., mvc:controller, cms:init)
  • Use kebab-case for multi-word actions: make:controller, cache:clear
  • Group related commands under the same namespace
  • Keep names short but descriptive

Built-in Commands

Core Commands

Command Description Usage
help Display help for a command neuron help [command]
list List all available commands neuron list
version Show version information neuron version [--verbose]

Output Helpers

The Output class provides rich formatting options:

Basic Output

// Simple messages
$this->output->write('Simple message');
$this->output->writeln('Message with newline', 'green');

// Styled messages
$this->output->info('Information message');      // Cyan
$this->output->success('Success message');       // Green with ✓
$this->output->warning('Warning message');       // Yellow with ⚠
$this->output->error('Error message');          // Red with ✗
$this->output->comment('Comment');              // Yellow

// Sections and titles
$this->output->title('Command Title');
$this->output->section('Section Header');

Tables

$this->output->table(
    ['Name', 'Version', 'Description'],
    [
        ['cli', '1.0.0', 'CLI component'],
        ['mvc', '0.6.0', 'MVC framework'],
        ['cms', '0.5.0', 'Content management'],
    ]
);

Progress Bars

$progress = $this->output->createProgressBar(100);
$progress->start();

foreach ($items as $item) {
    // Process item...
    $progress->advance();
}

$progress->finish();

Interactive Input

The CLI component provides full support for interactive user input in commands:

// Check if terminal is interactive
if (!$this->input->isInteractive()) {
    $this->output->error('This command requires an interactive terminal');
    return 1;
}

// Ask for text input with optional default
$name = $this->input->ask('What is your name?', 'Anonymous');

// Ask without default (returns empty string if no input)
$email = $this->input->ask('Enter your email');

// Ask yes/no questions
if ($this->input->confirm('Do you want to continue?', true)) {
    // User confirmed (pressed y/yes/1/true or Enter with default true)
}

// Read raw input with custom prompt
$line = $this->input->readLine('> ');

Interactive Input Methods

Method Description Example
isInteractive() Check if terminal supports interaction if ($this->input->isInteractive())
ask($question, $default) Ask for text input $name = $this->input->ask('Name?', 'John')
confirm($question, $default) Ask yes/no question if ($this->input->confirm('Continue?', true))
askSecret($question) Ask for hidden input (passwords) $pass = $this->input->askSecret('Password')
choice($question, $choices, $default, $multiple) Select from options $opt = $this->input->choice('Pick:', ['A', 'B'])
readLine($prompt) Read raw input line $line = $this->input->readLine('> ')

Example: Interactive Setup Command

class SetupCommand extends Command
{
    public function execute(): int
    {
        if (!$this->input->isInteractive()) {
            $this->output->error('Setup requires interactive mode');
            return 1;
        }
        
        // Gather information
        $project = $this->input->ask('Project name', 'my-app');
        $author = $this->input->ask('Author name');
        $email = $this->input->ask('Author email');
        
        // Validate email
        while (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $this->output->error('Invalid email format');
            $email = $this->input->ask('Author email');
        }
        
        // Show summary
        $this->output->section('Configuration Summary');
        $this->output->write("Project: {$project}");
        $this->output->write("Author: {$author} <{$email}>");
        
        // Confirm
        if (!$this->input->confirm('Create project with these settings?', true)) {
            $this->output->warning('Setup cancelled');
            return 1;
        }
        
        // Proceed with setup...
        $this->output->success('Project created successfully!');
        return 0;
    }
}

Secret Input (Passwords)

The askSecret() method hides user input, perfect for passwords and sensitive data:

// Ask for password (input will be hidden)
$password = $this->input->askSecret('Enter password');

// Confirm password
$confirm = $this->input->askSecret('Confirm password');

if ($password !== $confirm) {
    $this->output->error('Passwords do not match');
    return 1;
}

Choice Selection

The choice() method presents options for selection:

// Simple choice from array
$colors = ['Red', 'Green', 'Blue'];
$color = $this->input->choice('Pick a color:', $colors, 'Blue');

// Associative array (key => display value)
$environments = [
    'dev' => 'Development',
    'staging' => 'Staging', 
    'prod' => 'Production'
];
$env = $this->input->choice('Select environment:', $environments, 'dev');

// Multiple selection
$features = [
    'api' => 'REST API',
    'auth' => 'Authentication',
    'cache' => 'Caching'
];
$selected = $this->input->choice(
    'Select features to enable:',
    $features,
    null,        // No default
    true         // Allow multiple
);
// Returns array like: ['api', 'auth']

// Users can select by:
// - Number: Type "1" for first option
// - Key: Type "dev" for development
// - Value: Type "Development" (case-insensitive)
// - Multiple: "1,3" or "api,cache" (when multiple allowed)

How Command Discovery Works

  1. Installation Detection: When neuron is run, the ComponentLoader scans for installed packages
  2. Package Filtering: Only neuron-php/* packages are considered
  3. Configuration Check: Each package's composer.json is checked for CLI configuration
  4. Provider Loading: If a CLI provider is found, its register() method is called
  5. Direct Registration: Any directly listed commands are registered
  6. Project Commands: The root project's composer.json is also checked

This automatic discovery means:

  • No manual registration needed
  • Commands available immediately after component installation
  • Clean separation between components
  • No conflicts between component commands

Example Implementations

MVC Component Commands

class RoutesListCommand extends Command
{
    public function getName(): string
    {
        return 'routes:list';
    }
    
    public function getDescription(): string
    {
        return 'Display all registered routes';
    }
    
    public function execute(): int
    {
        $routes = $this->getRoutes(); // Your logic here
        
        $this->output->table(
            ['Method', 'Path', 'Controller', 'Action'],
            $routes
        );
        
        return 0;
    }
}

CMS Component Commands

class InitCommand extends Command
{
    public function getName(): string
    {
        return 'cms:init';
    }
    
    public function getDescription(): string
    {
        return 'Initialize CMS structure';
    }
    
    public function configure(): void
    {
        $this->addOption('theme', null, true, 'Initial theme', 'default');
        $this->addOption('with-sample', null, false, 'Include sample content');
    }
    
    public function execute(): int
    {
        $theme = $this->input->getOption('theme');
        
        $this->output->title('CMS Initialization');
        
        // Create directories
        $this->output->info('Creating directory structure...');
        // ... implementation
        
        // Install theme
        $this->output->info("Installing theme: {$theme}");
        // ... implementation
        
        if ($this->input->getOption('with-sample')) {
            $this->output->info('Adding sample content...');
            // ... implementation
        }
        
        $this->output->success('CMS initialized successfully!');
        return 0;
    }
}

Error Handling

Commands should return appropriate exit codes:

  • 0 - Success
  • 1 - General error
  • 2 - Misuse of command
  • 126 - Command cannot execute
  • 127 - Command not found

Example:

public function execute(): int
{
    try {
        // Command logic...
        return 0;
    } catch (ValidationException $e) {
        $this->output->error('Validation failed: ' . $e->getMessage());
        return 2;
    } catch (\Exception $e) {
        $this->output->error('An error occurred: ' . $e->getMessage());
        return 1;
    }
}

Testing Your Commands

use PHPUnit\Framework\TestCase;
use Neuron\Cli\Console\Input;
use Neuron\Cli\Console\Output;

class MakeControllerCommandTest extends TestCase
{
    public function testExecute(): void
    {
        $command = new MakeControllerCommand();
        
        // Mock input
        $input = new Input(['UserController', '--resource']);
        $output = new Output(false); // No colors for testing
        
        $command->setInput($input);
        $command->setOutput($output);
        $command->configure();
        $input->parse($command);
        
        $exitCode = $command->execute();
        
        $this->assertEquals(0, $exitCode);
    }
}

Contributing

When adding new features to the CLI component:

  1. Extend the Command base class for new commands
  2. Add tests in the tests/ directory
  3. Update this README with new features
  4. Follow PSR-4 naming conventions
  5. Use the existing code style (tabs, spaces etc)

License

This component is part of the Neuron PHP Framework and is released under the MIT License.