📦 Marketplace⭐ GitHub
Packagesv2.0

Cli

Attribute-driven, PHP 8.4+ command-line toolkit for the MonkeysLegion framework.

PHP Version License Tests


Table of Contents


Installation

composer require monkeyscloud/monkeyslegion-cli:^2.0

Requirements: PHP 8.4+, nikic/php-parser (for entity AST manipulation).


Quick Start

# List all commands
php ml list

# Filter by prefix
php ml make:

# Generate an entity interactively
php ml make:entity User

# Generate an entity non-interactively (CI mode)
php ml make:entity Product --fields="name:string,price:decimal:nullable,sku:string"

# Run migrations
php ml migrate

Architecture

v2 Design Principles

  1. Attribute-driven — Commands register via #[Command('name', 'description')] attributes
  2. PHP 8.4+ — Leverages asymmetric visibility (public private(set)), property hooks, readonly classes
  3. Zero boilerplate — Entity properties are public (no getter/setter generation); only collection relations get add/remove/get helpers
  4. Dual mode — Every generator supports both interactive wizard and --flags non-interactive usage
  5. PSR-4 split — Each enum/class lives in its own file for proper autoloading

Directory Structure

src/
├── CliKernel.php              # Attribute-driven kernel with auto-discovery
├── Command/                   # 39 built-in commands
│   ├── MakerHelpers.php       # Shared trait for make:* commands
│   ├── MakeEntityCommand.php  # Interactive entity wizard
│   ├── MigrateCommand.php     # Migration runner
│   └── ...
├── Config/                    # Configuration value objects
│   ├── EntityConfig.php       # Aggregate config for entity wizard
│   ├── FieldType.php          # Database field type enum (36 types)
│   ├── FieldTypeConfig.php    # Field type resolver
│   ├── PhpTypeMap.php         # DB type → PHP type mapping
│   ├── RelationKind.php       # Relation kind enum (4 kinds)
│   ├── RelationKeywordMap.php # Relation attribute names
│   └── RelationInverseMap.php # Inverse relation resolver
├── Console/                   # Console abstractions
│   ├── Attributes/Command.php # #[Command] attribute class
│   ├── Command.php            # Base command with 20+ helpers
│   ├── Output/
│   │   ├── TableRenderer.php  # UTF-8 box-drawing tables
│   │   ├── ProgressBar.php    # Progress bar with ETA
│   │   └── Spinner.php        # Braille animation spinner
│   └── Traits/Cli.php         # Colored output builder (CliLineBuilder)
├── Helpers/
│   └── Identifier.php         # PHP identifier validator
├── Service/
│   └── ClassManipulator.php   # PhpParser-based AST entity editor
└── Support/
    └── CommandFinder.php      # Auto-discovery via Composer PSR-4

Command Reference

General

CommandDescription
list / helpDisplay all available commands
aboutFramework, PHP, and environment information
downPut application in maintenance mode
upBring application out of maintenance mode
tinkerInteractive REPL with DI container
optimizeRun config:cache + route:cache + clear old caches

Database (db:)

CommandDescription
db:createCreate the database schema from .env credentials
db:seedRun database seeders (optionally specify one)
db:wipeDrop all tables in the database

Cache (cache: / config:)

CommandDescription
cache:clearClear the compiled view cache
config:cacheCompile config files into a cached file

Environment (env:)

CommandDescription
env:syncCompare .env with .env.example for missing keys
key:generateGenerate a new APP_KEY in your .env file

Migration (migrate:)

CommandAliasesDescription
migratemRun pending database migrations
migrate:rollbackm:rbRollback the last migration batch
migrate:freshDrop all tables and re-run all migrations
migrate:refreshRollback all and re-run all migrations
migrate:statusm:stShow ran and pending migrations
make:migrationGenerate a blank migration file
schema:updateCompare entities → database and apply changes

Maker (make:)

CommandDescription
make:entityGenerate or update an Entity class with fields & relationships
make:controllerGenerate a new Controller class
make:middlewareGenerate a PSR-15 middleware class
make:serviceGenerate a service class
make:dtoGenerate a readonly DTO class
make:enumGenerate a backed enum class
make:eventGenerate a domain event class
make:listenerGenerate an event listener class
make:jobGenerate a queue job class
make:resourceGenerate a JSON:API resource class
make:factoryGenerate an entity factory for testing
make:observerGenerate an entity lifecycle observer
make:policyGenerate an authorization policy class
make:seederGenerate a database seeder class
make:testGenerate a PHPUnit test class
make:commandGenerate a custom CLI command

Routing (route:)

CommandDescription
route:listList all registered routes (filter with --method, --path)
route:cacheCache all routes for production

API

CommandDescription
openapi:exportDump OpenAPI spec to stdout or a file

Entity Generator

Interactive Mode

php ml make:entity User

Launches a wizard that lets you:

  1. Add fields — Choose from 36 database types with nullable support
  2. Add relationships — OneToOne, OneToMany, ManyToOne, ManyToMany with automatic inverse generation
  3. Save — Writes the entity using PhpParser AST manipulation (non-destructive)

Non-Interactive Mode

php ml make:entity Product --fields="name:string,price:decimal:nullable,sku:string,active:boolean"

Parses field:type[:nullable] triplets and applies them in a single pass. Ideal for CI pipelines.

Generated Entity (v2 Style)

<?php
declare(strict_types=1);

namespace App\Entity;

use MonkeysLegion\Entity\Attributes\Entity;
use MonkeysLegion\Entity\Attributes\Field;
use MonkeysLegion\Entity\Attributes\Id;

#[Entity]
class Product
{
    #[Id]
    #[Field(type: 'unsignedBigInt', autoIncrement: true)]
    public private(set) int $id;

    #[Field(type: 'string')]
    public string $name;

    #[Field(type: 'decimal', nullable: true)]
    public ?float $price = null;

    #[Field(type: 'string')]
    public string $sku;

    #[Field(type: 'boolean')]
    public bool $active;
}

Key v2 differences from v1:

  • #[Id] + #[Field] dual attributes (instead of combined)
  • public private(set) for the primary key (PHP 8.4 asymmetric visibility)
  • No getter/setter methods — public properties are the API
  • Collection relations still get add{Entity}(), remove{Entity}(), get{Prop}() helpers

Migration System

Built on top of monkeyscloud/monkeyslegion-migration v1.x:

# Generate a blank migration
php ml make:migration CreateUsersTable

# Run pending migrations
php ml migrate

# Check status
php ml migrate:status

# Rollback last batch
php ml migrate:rollback

# Fresh start (drop all + re-run)
php ml migrate:fresh

# Refresh (rollback all + re-run)
php ml migrate:refresh

# Auto-generate from entities
php ml schema:update
php ml schema:update --force   # Apply without confirmation

Maker Commands

All maker commands follow a consistent pattern:

# With argument
php ml make:service UserService

# Interactive (no argument)
php ml make:service
# → prompts for name

# Force overwrite
php ml make:service UserService --force

Generated stubs use v2 conventions:

  • declare(strict_types=1)
  • final class by default
  • PSR-4 namespace matching directory structure
  • Typed properties and constructor promotion

Writing Custom Commands

1. Create the Command

<?php
declare(strict_types=1);

namespace App\Command;

use MonkeysLegion\Cli\Console\Attributes\Command as CommandAttr;
use MonkeysLegion\Cli\Console\Command;

#[CommandAttr('app:sync', 'Sync data from external API', aliases: ['sync'])]
final class SyncCommand extends Command
{
    public function __construct(
        private readonly ApiClient $client,
    ) {
        parent::__construct();
    }

    protected function handle(): int
    {
        $count = $this->option('limit', 100);
        $force = $this->hasOption('force');

        $this->info("Syncing {$count} records...");

        $result = $this->spinner('Fetching data...', function () use ($count) {
            return $this->client->fetch((int) $count);
        });

        $this->table(
            ['ID', 'Name', 'Status'],
            array_map(fn($r) => [$r->id, $r->name, $r->status], $result),
        );

        $this->info("✅ Synced " . count($result) . " records.");

        return self::SUCCESS;
    }
}

2. Auto-Discovery

Commands in app/Command/ or app/Cli/Command/ are auto-discovered. No registration needed.

3. Available Helpers

// Output
$this->info('Green message');
$this->warn('Yellow warning');
$this->error('Red error → STDERR');
$this->comment('Gray italic');
$this->alert('Boxed alert');
$this->line('Plain text');
$this->newLine(2);

// Colored output builder
$this->cliLine()
    ->add('Status: ', 'cyan', 'bold')
    ->add('OK', 'green')
    ->print();

// Interactive prompts
$name    = $this->ask('Name:');
$confirm = $this->confirm('Continue?', true);
$choice  = $this->choice('Option:', ['A', 'B', 'C'], 0);
$pass    = $this->secret('Password:');
$val     = $this->anticipate('Search:', $suggestions, 'default');

// Progress
$this->progressStart(100, 'Processing');
for ($i = 0; $i < 100; $i++) {
    $this->progressAdvance();
}
$this->progressFinish();

// Spinner
$result = $this->spinner('Computing...', fn() => heavyTask());

// Table
$this->table(['Name', 'Age'], [['Alice', '30'], ['Bob', '25']]);

// Arguments & options
$arg    = $this->argument(0);           // positional
$opt    = $this->option('name', 'def'); // --name=value
$flag   = $this->hasOption('verbose');  // --verbose
$all    = $this->allOptions();          // all --options

Console Output API

CliLineBuilder

Build multi-colored, multi-styled output lines:

$this->cliLine()
    ->add('Error: ', 'red', 'bold')
    ->add('File not found ', 'white')
    ->add('/path/to/file', 'yellow', 'underline')
    ->print();

// Semantic shortcuts
$this->cliLine()
    ->success('✓ ')
    ->info('Processing ')
    ->muted('(3 items)')
    ->print();

Colors: black, red, green, yellow, blue, magenta, cyan, white, gray, bright_* Styles: bold, dim, italic, underline, blink, reverse, hidden, strikethrough

TableRenderer

UTF-8 box-drawing tables with column alignment:

$this->table(
    ['Name', 'Type', 'Nullable'],
    [
        ['id', 'unsignedBigInt', 'No'],
        ['email', 'string', 'No'],
        ['bio', 'text', 'Yes'],
    ],
    ['l', 'l', 'c'],  // left, left, center alignment
);

ProgressBar & Spinner

// Progress bar with ETA
$this->progressStart(1000, 'Migrating');
// → [████████████░░░░░░░░░░░░░░░░░░] 40% 400/1000 ETA 0:02

// Spinner for indeterminate operations
$result = $this->spinner('Analyzing schema...', fn() => $scanner->scan());
// → ⠙ Analyzing schema...
// → ✓ Analyzing schema

Testing

# Run all tests
vendor/bin/phpunit

# With coverage (requires pcov/xdebug)
vendor/bin/phpunit --coverage-text

# Specific test suite
vendor/bin/phpunit tests/Unit/Service/ClassManipulatorTest.php

Current stats: 152 tests, 299 assertions across 12 test files.

Test Coverage

ComponentTestsFocus
ClassManipulatorTest18AST manipulation, field/relation generation
CommandTest30argument(), option(), hasOption(), output, safeQuery
EntityConfigTest12PhpTypeMap, RelationKeywordMap, RelationInverseMap
FieldTypeConfigTest7Field type parsing and validation
CliLineBuilderTest15Multi-colored output building
CommandAttributeTest8#[Command] attribute properties
ProgressBarTest7Progress rendering
TableRendererTest20Table formatting and alignment
SpinnerTest8Spinner animation
MakerHelpersTest10toPascalCase, ensureSuffix, etc.
IdentifierTest9PHP identifier validation
Additional8Edge cases and integration

Contributing

  1. Fork and create a feature branch from 2.x-dev
  2. Follow MonkeysLegion v2 coding standards (PSR-12 + declare(strict_types=1))
  3. Write tests for new functionality
  4. Run vendor/bin/phpunit and ensure all tests pass
  5. Submit a PR against 2.x-dev

License

MIT © 2026 MonkeysCloud Team