Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Auto Shell Laravel Package

pmjones/auto-shell

AutoShell maps CLI command names to PHP command classes in a namespace, reflecting on a main method to parse args/options (scalars or arrays). Add a class in the command directory and it becomes available automatically—no dependencies, minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:
    composer require pmjones/auto-shell
    
  2. Console Script (bin/console.php):
    <?php
    use AutoShell\Console;
    
    require dirname(__DIR__) . '/vendor/autoload.php';
    
    $console = Console::new(
        namespace: 'App\\Cli\\Command',
        directory: __DIR__ . '/../src/Cli/Command',
    );
    
    exit($console($_SERVER['argv']));
    
  3. First Command (src/Cli/Command/Hello.php):
    <?php
    namespace App\Cli\Command;
    
    class Hello
    {
        public function __invoke(string $name): int
        {
            echo "Hello, {$name}!\n";
            return 0;
        }
    }
    
  4. Run it:
    php bin/console.php hello world
    
    Output: Hello, world!

Key First Use Cases

  • Quick CLI Prototyping: Define a command class with __invoke() and instantly use it via CLI.
  • Type-Safe Arguments: Leverage PHP type hints (string, int, bool, array) for automatic parsing.
  • Help Generation: Add #[Help] attributes to auto-generate --help documentation.

Implementation Patterns

1. Command Structure

  • Naming Convention: Class names map to CLI commands (e.g., CreateUsercreate-user).
  • Method Signature: Use __invoke() (default) or customize via method in Console::new().
    public function __invoke(
        string $name,
        int $retryCount = 3,
        array $tags = []
    ): int { ... }
    

2. Options Handling

  • Options Class: Implement Options interface with #[Option] attributes.
    class UserOptions implements Options {
        public function __construct(
            #[Option('v,verbose')]
            public readonly bool $verbose = false,
        ) {}
    }
    
  • Compose Options: Pass multiple Options classes to __invoke() for shared/global options.
    public function __invoke(GlobalOptions $global, UserOptions $user): int { ... }
    

3. Dependency Injection

  • Factory Pattern: Inject a factory (e.g., PSR-11 container) to resolve dependencies.
    $console = Console::new(
        namespace: 'App\\Cli\\Command',
        directory: __DIR__ . '/../src/Cli/Command',
        factory: fn(string $class) => $container->get($class),
    );
    

4. Help Text

  • Class/Method Help: Use #[Help] on classes and parameters.
    #[Help("Deploys the application.")]
    class Deploy {
        public function __invoke(
            #[Help("The environment to deploy to (e.g., staging).")]
            string $env
        ): int { ... }
    }
    
  • Extended Help: Add multi-line descriptions with light markup (*bold*, _underline_).

5. Error Handling

  • Exit Codes: Return 0 for success, non-zero for failures (e.g., 1 for general errors).
  • Validation: Use #[Option(mode: Option::VALUE_REQUIRED)] for mandatory options.

Workflows

  1. Develop Locally:
    • Add a command class → Test via php bin/console.php <command>.
    • Iterate on options/help without touching the console script.
  2. Integrate with Laravel:
    • Register the console as an Artisan command:
      // app/Console/Kernel.php
      protected $commands = [
          \AutoShell\Console::class,
      ];
      
    • Use Laravel’s service container for dependency injection.
  3. CI/CD Pipelines:
    • Use #[Option] with mode: Option::VALUE_REQUIRED for mandatory config flags.
    • Example: php bin/console.php deploy --env=production --dry-run.

Gotchas and Tips

Pitfalls

  1. Case Sensitivity:
    • Command names are case-insensitive (Hello = hello), but class names must match PSR-4.
    • Fix: Ensure namespace and directory in Console::new() are correct.
  2. Option Conflicts:
    • Defining the same option (e.g., -v) in multiple Options classes throws OptionAlreadyDefined.
    • Fix: Consolidate options into a single class or use unique prefixes (e.g., -global-verbose).
  3. Boolean Parsing:
    • bool options default to true if the flag is present (e.g., -vtrue).
    • Fix: Use default: false if you want explicit flags only.
  4. Array Handling:
    • array type hints use str_getcsv(), which splits on commas. For custom delimiters, pre-process arguments.
  5. Help Overrides:
    • Custom stdout/stderr callbacks bypass default help formatting.
    • Fix: Ensure callbacks return raw strings if you need formatted help.

Debugging Tips

  1. Verbose Output:
    • Set AutoShell\Console to debug mode (not built-in, but you can extend it):
      $console = Console::new(..., debug: true);
      
    • Workaround: Temporarily add error_log() in the Console class to trace parsing.
  2. Command Discovery:
    • If commands aren’t found, verify:
      • The namespace and directory paths are correct.
      • Classes are not abstract/traits/interfaces.
      • Files are autoloadable (run composer dump-autoload).
  3. Option Parsing:
    • Use php bin/console.php --help to validate help text.
    • For silent failures, check $_SERVER['argv'] directly in a test script.

Extension Points

  1. Custom Option Modes:
    • Extend AutoShell\Option to support additional modes (e.g., VALUE_CONDITIONAL).
  2. Type Casting:
    • Override AutoShell\TypeCaster to add custom type handling (e.g., DateTime).
  3. Help Formatters:
    • Replace the default help formatter by injecting a custom HelpRenderer.
  4. Pre/Post-Processing:
    • Use the factory callback to wrap commands in middleware (e.g., logging, auth).
      factory: fn(string $class) => new CommandWrapper(new $class()),
      

Laravel-Specific Quirks

  1. Artisan Integration:
    • Avoid naming conflicts with Laravel’s built-in commands (e.g., make).
    • Fix: Prefix commands (e.g., app:deployApp\Cli\Command\Deploy).
  2. Service Providers:
    • Bind AutoShell\Console to the container if using dependency injection:
      $this->app->singleton(\AutoShell\Console::class, fn($app) => Console::new(
          namespace: 'App\\Cli\\Command',
          directory: __DIR__ . '/../src/Cli/Command',
          factory: $app->get('container'),
      ));
      
  3. Configuration:
    • Store namespace/directory in config/console.php for easy updates:
      return [
          'namespace' => env('CLI_NAMESPACE', 'App\\Cli\\Command'),
          'directory' => __DIR__ . '/../src/Cli/Command',
      ];
      

Performance

  • Class Reflection: AutoShell reflects on all classes in the directory on each run. For large projects:
    • Cache the Exec objects or use a compiled class map.
    • Workaround: Implement a custom Shell class that pre-loads commands.
  • Options Validation: Complex option logic (e.g., nested arrays) may slow parsing. Pre-validate in the Options constructor if possible.

Testing

  • Unit Tests:
    • Mock $_SERVER['argv'] and test Console directly:
      $_SERVER['argv'] = ['console', 'hello', 'world'];
      $exitCode = $console($_SERVER['argv']);
      $this->assertEquals(0, $exitCode);
      
  • Integration Tests:
    • Use symfony/process to simulate CLI calls:
      $process = new Process(['php', 'bin/console.php', 'hello', 'world']);
      $process->run();
      $this->assertStringContainsString('Hello, world!', $process->getOutput());
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours