arielespinoza07/hephaestus 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

arielespinoza07/hephaestus

最新稳定版本:v1.1.1

Composer 安装命令:

composer require arielespinoza07/hephaestus

包简介

Forge powerful CLI tools with ease - A modern, type-safe wrapper around Symfony Console

README 文档

README

CI Latest Version Total Downloads PHP Version License

Build powerful CLI tools with ease

An attribute-driven CLI framework built on Symfony Console. Define commands with PHP attributes — Hephaestus handles the rest.

Philosophy

Symfony Console is powerful but verbose.

Hephaestus gives you:

  • 🎯 Simple API for simple tasks
  • 🔒 Type-safe everywhere (PHP 8.5+)
  • ⚡ Fast to write, easy to test
  • 🔧 Full Symfony power when needed
  • 🏗️ Clean architecture — SOLID, DRY, YAGNI, KISS
  • 🧪 Test-friendly design
  • 🚀 Built-in scaffolding — generate commands and entrypoints instantly

Requirements

  • PHP 8.5+
  • Symfony Console 8.0+
  • psr/container 2.0+ (pulled in automatically via Composer)

Installation

composer require arielespinoza07/hephaestus

Quick Start

1. Create a command

<?php

declare(strict_types=1);

use Hephaestus\Console\Command;
use Hephaestus\Attributes\Signature;
use Hephaestus\Attributes\Description;
use Hephaestus\Attributes\Output;
use Hephaestus\Attributes\Argument;
use Hephaestus\Attributes\Option;

#[Signature('app:greet')]
#[Description('Greets a user by name')]
#[Output]
final readonly class GreetCommand extends Command
{
    public function execute(
        #[Argument(description: 'The name of the user to greet')]
        string $name,
        #[Option(description: 'Shout the greeting', shortcut: 'l')]
        bool $yell = false,
    ): int {
        $greeting = sprintf('Hello, %s!', $name);
        $this->output->writeln($yell ? mb_strtoupper($greeting) : $greeting);

        return self::SUCCESS;
    }
}

2. Bootstrap your CLI app

<?php

declare(strict_types=1);

require_once __DIR__ . '/vendor/autoload.php';

use Hephaestus\CliApp;

exit(
    CliApp::create('MyApp', '1.0.0')
        ->registerCommands(__DIR__ . '/src/Commands')
        ->run()
);

3. Run it

php bin/console app:greet John
# Hello, John!

php bin/console app:greet John --yell
# HELLO, JOHN!

php bin/console app:greet John -l
# HELLO, JOHN!

Scaffolding

Hephaestus ships a bin/hephaestus binary with two generators to get you started without writing boilerplate.

make:command

Generate a command class skeleton inside your project's src/Commands/ directory (auto-detected from composer.json).

./vendor/bin/hephaestus make:command Greet
# → src/Commands/GreetCommand.php

./vendor/bin/hephaestus make:command Greet --force   # overwrite existing
./vendor/bin/hephaestus make:command Greet -f        # shortcut

The generated file:

<?php

declare(strict_types=1);

namespace App\Commands;

use Hephaestus\Attributes\Description;
use Hephaestus\Attributes\Output;
use Hephaestus\Attributes\Signature;
use Hephaestus\Console\Command;

#[Signature('greet')]
#[Description('')]
#[Output]
final readonly class GreetCommand extends Command
{
    public function execute(): int
    {
        return self::SUCCESS;
    }
}

The signature is auto-derived from the class name (CreateUserCommandcreate-user) feel free to change it.

make:entrypoint

Generate an executable CLI entrypoint in bin/.

./vendor/bin/hephaestus make:entrypoint app
# → bin/app  (executable, chmod 0755)

# Full options
./vendor/bin/hephaestus make:entrypoint app \
    --app="My App" \
    --ver=2.0.0 \
    --dir=src/Commands \
    --force
Option Default Description
--app ucfirst($name) App name passed to CliApp::create()
--ver 1.0.0 App version
--dir auto-detected from composer.json Relative path to commands directory
--force / -f false Overwrite if file already exists

The generated file:

#!/usr/bin/env php
<?php

declare(strict_types=1);

require_once __DIR__ . '/../vendor/autoload.php';

use Hephaestus\CliApp;

exit(
    CliApp::create('App', '1.0.0')
        ->registerCommands(__DIR__ . '/../src/Commands')
        ->run()
);

How It Works

Hephaestus reads PHP attributes on your command class and its execute() method parameters to automatically wire up the Symfony Console command — no configure(), no $input->getArgument(), no $input->getOption().

PHP Attributes
     │
     ▼
MetadataReader  ──►  CommandMetadata
                           │
                           ▼
                  SymfonyCommandBridge  ──►  Symfony Command
                                                   │
                                                   ▼
                                             CLI / CommandTester

Attributes Reference

Class-level attributes

Attribute Required Description
#[Signature('app:name')] CLI command name
#[Description('...')] Short description shown in the command list
#[Help('...')] Extended help shown by help <command>
#[Usage(['app:name arg1', '...'])] Example usages shown in help output
#[Alias('alias1|alias2')] Pipe-separated command aliases
#[Input] Inject InputInterface into $this->input
#[Output] Inject OutputInterface into $this->output
#[Style] Inject SymfonyStyle into $this->output

Parameter-level attributes

#[Argument]

Maps a positional CLI argument (command arg).

#[Argument(
    description: 'The user name',   // shown in help
    required: true,                  // default: true
    default: null,                   // value when optional and not provided
)]
string $name

Type casting: When the parameter type is int, float, or bool, the raw CLI string is automatically cast before being passed to execute().

#[Option]

Maps a named CLI option (command --option or command --option=value).

#[Option(
    description: 'Shout the greeting',  // shown in help
    acceptValue: false,                  // true: --format=json, false: --verbose flag
    default: null,                       // default value when not provided
    shortcut: 'l',                       // single char or array of chars
)]
bool $yell

Type casting: When the parameter type is int, float, or bool, the raw CLI string is automatically cast before being passed to execute().

#[CompositeInput]

Groups multiple arguments/options into a single DTO, keeping execute() clean.

// DTO
final readonly class CreateUserInput
{
    public function __construct(
        #[Argument(description: 'User name')]
        public string $name,
        #[Argument(description: 'User email')]
        public string $email,
        #[Option(description: 'Grant admin role', shortcut: 'a')]
        public bool $admin = false,
    ) {}
}

// Command
public function execute(
    #[CompositeInput]
    CreateUserInput $input
): int {
    // ...
}

Command Class

All commands extend Hephaestus\Console\Command and must be final readonly.

abstract readonly class Command
{
    // Available when #[Input] is declared on the class
    protected ?InputInterface $input;

    // Available when #[Output] is declared on the class
    protected ?OutputInterface $output;

    // Exit code constants
    protected const int SUCCESS = 0;
    protected const int FAILURE = 1;
    protected const int INVALID = 2;
}

CliApp

Bootstrap a complete CLI application with auto-discovery of commands.

CliApp::create(string $name, string $version = '1.0.0'): self
// Scan one or multiple directories and register all commands
->registerCommands(string|array $directories, ?string $cachePath = null): self

// Examples
->registerCommands(__DIR__ . '/src/Commands')
->registerCommands([__DIR__ . '/src/Commands', __DIR__ . '/src/Plugins'])

// With metadata cache for faster startup
->registerCommands(__DIR__ . '/src/Commands', __DIR__ . '/var/cache/commands.cache')

// Resolve commands through a PSR-11 container (optional)
->withContainer(ContainerInterface $container): self

// Run the CLI application
->run(): int

registerCommands() returns $this for fluent chaining and can be called multiple times for multiple directories.

withContainer() enables dependency injection — commands are resolved via $container->get() instead of new ClassName().

Testing

Hephaestus ships a first-class testing API built on top of Symfony's CommandTester.

CommandRunner

use Hephaestus\Testing\CommandRunner;

$result = CommandRunner::for(GreetCommand::class)
    ->withArgs(['name' => 'John'])         // positional arguments
    ->withOptions(['yell' => true])        // options (-- prefix added automatically)
    ->run();

CommandResult assertions

$result->assertSuccessful();                        // exit code === 0
$result->assertFailed();                            // exit code !== 0
$result->assertExitCode(int $expected);             // exact exit code
$result->assertOutputContains(string $needle);      // stdout contains substring
$result->assertOutputEquals(string $expected);      // stdout exact match
$result->assertErrorOutputContains(string $needle); // stderr contains substring

All assertion methods return $this for fluent chaining:

$result
    ->assertSuccessful()
    ->assertExitCode(0)
    ->assertOutputEquals('Hello, John!');

Inspecting output directly

$result->exitCode();      // int
$result->output();        // string (stdout, trimmed)
$result->errorOutput();   // string (stderr, trimmed)
$result->isSuccessful();  // bool

Full test example (PestPHP)

use Hephaestus\Testing\CommandRunner;

test('greets user', function () {
    CommandRunner::for(GreetCommand::class)
        ->withArgs(['name' => 'John'])
        ->run()
        ->assertSuccessful()
        ->assertOutputEquals('Hello, John!');
});

test('shouts when yell option is set', function () {
    CommandRunner::for(GreetCommand::class)
        ->withArgs(['name' => 'John'])
        ->withOptions(['yell' => true])
        ->run()
        ->assertOutputEquals('HELLO, JOHN!');
});

test('fails when name argument is missing', function () {
    CommandRunner::for(GreetCommand::class)
        ->run()
        ->assertFailed()
        ->assertExitCode(1)
        ->assertErrorOutputContains('missing: "name"');
});

Why "Hephaestus"?

Hephaestus (Ἥφαιστος) is the Greek god of fire, metalworking, and the forge — the divine craftsman who built weapons and tools for gods and heroes alike.

This library follows the same principle: hand developers the right tools so they can forge great things without wasting time on the tedious parts.

🔨 Forge. Build. Ship.

Contributing

See CONTRIBUTING.md for setup instructions, code conventions, and PR guidelines.

License

MIT License

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 5
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-03-02

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固