chore: initialize project infrastructure and Phred IO adapters
This commit is contained in:
commit
eead487714
27
MILESTONES.md
Normal file
27
MILESTONES.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# TaskerBridges Milestones
|
||||
|
||||
This document defines the implementation milestones for the `getphred/tasker-bridges` package.
|
||||
|
||||
## 1. Bridge Infrastructure
|
||||
- [ ] Initialize repository with `getphred/console-contracts` dependency.
|
||||
- [ ] Set up testing environment for multi-toolkit validation (mocking Symfony/Laravel).
|
||||
|
||||
## 2. Symfony Bridge
|
||||
- [ ] Implement `SymfonyCommandAdapter`.
|
||||
- [ ] Implement `SymfonyInputAdapter` and `SymfonyOutputAdapter`.
|
||||
- [ ] Implement helper adapters (Interaction, ProgressBar, Table).
|
||||
- [ ] Implement fixed markup translation (mapping to Symfony Formatter).
|
||||
|
||||
## 3. Laravel Bridge
|
||||
- [ ] Implement `LaravelCommandAdapter` and `LaravelServiceProvider`.
|
||||
- [ ] Integrate Symfony IO adapters within the Laravel context.
|
||||
- [ ] Implement native Laravel interaction helper mappings.
|
||||
|
||||
## 4. Phred (Native) Bridge
|
||||
- [ ] Implement ANSI-based `OutputInterface` with full markup support.
|
||||
- [ ] Implement lightweight interaction and progress helpers.
|
||||
- [ ] Implement regex-based Markdown-to-Markup converter.
|
||||
|
||||
## 5. Universal Integration
|
||||
- [ ] Implement universal exit code translation logic.
|
||||
- [ ] Implement global flag mapping (Verbosity, Decoration, Interactivity).
|
||||
174
NOTES.md
Normal file
174
NOTES.md
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
# TaskerBridges: Phred CLI Interoperability Adapters
|
||||
|
||||
TaskerBridges is the adapter layer that allows commands written against `Phred\ConsoleContracts` to run inside multiple CLI runtimes with a native experience. Bridges translate Phred's runner-agnostic contracts into the APIs of specific console toolkits.
|
||||
|
||||
Project Phase: Idea (NOTES.md)
|
||||
|
||||
## 1. Core Vision
|
||||
- Interoperability first: Write a command once, run it anywhere (Tasker native runner, Symfony Console, Laravel Artisan, etc.).
|
||||
- Contracts-driven: Bridges never change command code; they only adapt `ConsoleContracts` to the target runtime.
|
||||
- Minimal footprint: Optional, per-bridge dependencies and lazy wiring. No heavy transitive dependencies for users who don't need a given bridge.
|
||||
- Native UX: When running under a given toolkit, output, helpers, and ergonomics should feel native to that toolkit.
|
||||
|
||||
## 2. Scope & Responsibilities
|
||||
Bridges provide a cohesive set of adapters:
|
||||
- Command adapter: Wraps `CommandInterface` and exposes a native command entrypoint.
|
||||
- IO adapters: Implement `InputInterface`/`OutputInterface` on top of the target toolkit’s input/output.
|
||||
- Helper adapters: Implement optional helper contracts (`InteractionInterface`, `ProgressBarInterface`, `TableInterface`, `MarkdownConverterInterface`).
|
||||
- Markup translator: Converts Phred’s fixed markup tags (`<success>`, `<info>`, `<error>`, `<warning>`, `<comment>`, `<b>`, `<i>`, `<u>`) to the target toolkit’s styles or to raw ANSI.
|
||||
- Policy alignment: Honor `Verbosity` levels, non-interactive mode, TTY/color detection, and `sysexits.h` exit code semantics.
|
||||
|
||||
Non-goals:
|
||||
- Defining new business logic or command semantics.
|
||||
- Extending the fixed markup set (bridges must support the core set only).
|
||||
|
||||
## 3. Supported Bridges (Initial Set)
|
||||
- SymfonyBridge: Adapter for `symfony/console` (targeting Symfony 6/7 lines).
|
||||
- PhredBridge (Native): Minimal adapters used by the Tasker core runner.
|
||||
- LaravelBridge: Adapter for Laravel's Artisan console (`illuminate/console`).
|
||||
|
||||
### 3.1 Future / Potential Bridges (Roadmap)
|
||||
The following bridges are identified as high-value for future development or upon community request:
|
||||
- **LaminasBridge**: Adapter for `laminas/laminas-cli`.
|
||||
- **CakePHPBridge**: Adapter for the CakePHP Console/Shell ecosystem.
|
||||
- **YiiBridge**: Adapter for Yii 2.0 console commands (Note: Yii 3.0 uses `symfony/console` and may be covered by `SymfonyBridge`).
|
||||
- **AuraCliBridge**: Adapter for `Aura.Cli` for users of the Aura library suite.
|
||||
- **RichOutputBridge**: A bridge leveraging a library like `CLImate` for advanced terminal UI features (colors, progress bars) when the native Phred runner is used.
|
||||
|
||||
Each bridge lives under `Phred\TaskerBridges\{BridgeName}\...` and can be installed/used independently via Composer suggests.
|
||||
|
||||
## 4. SymfonyBridge — Design
|
||||
|
||||
### 4.1 High-Level Mapping
|
||||
- Command metadata
|
||||
- `CommandInterface::getName()` -> `Symfony\Component\Console\Command\Command::setName()`
|
||||
- `getDescription()` -> `setDescription()`
|
||||
- `getArguments()` -> loop `addArgument($name, InputArgument::OPTIONAL|REQUIRED, $description, $default)`
|
||||
- `getOptions()` -> loop `addOption($name, $shortcut, InputOption::VALUE_NONE|VALUE_OPTIONAL|VALUE_REQUIRED, $description, $default)`
|
||||
- Note: `ConsoleContracts` currently models arguments/options as `name => description` (DX-minimal). Bridge MAY enhance by honoring defaults/modes if/when added to contracts in future specs.
|
||||
|
||||
- Execution
|
||||
- The adapter class (e.g., `SymfonyCommandAdapter`) extends `Symfony\Component\Console\Command\Command` and wraps a `CommandInterface` instance.
|
||||
- In `execute(InputInterface $in, OutputInterface $out)`, it:
|
||||
1) Adapts Symfony input/output to Phred `InputInterface`/`OutputInterface` via IO adapters.
|
||||
2) Invokes `$wrapped->execute($phredInput, $phredOutput)`.
|
||||
3) Returns the integer exit code from the wrapped command.
|
||||
|
||||
### 4.2 IO Adapters
|
||||
- `SymfonyInputAdapter` implements `GetPhred\ConsoleContracts\InputInterface` by delegating to `Symfony\Component\Console\Input\InputInterface` methods (`getArgument`, `getOption`, `hasOption`).
|
||||
- `SymfonyOutputAdapter` implements `GetPhred\ConsoleContracts\OutputInterface` by delegating to `Symfony\Component\Console\Output\OutputInterface`/`Style` and applying markup translation.
|
||||
|
||||
Verbosity mapping:
|
||||
- Phred `Verbosity::QUIET|NORMAL|VERBOSE|VERY_VERBOSE|DEBUG` <-> Symfony `OutputInterface::VERBOSITY_*`.
|
||||
|
||||
Non-interactive:
|
||||
- Respect a runner-provided flag; adapters should avoid prompts and ensure helpers also respect non-interactive mode.
|
||||
|
||||
### 4.3 Markup Translation (Fixed Set)
|
||||
- Translate Phred tags into Symfony formatter equivalents or raw ANSI where Symfony lacks a native tag.
|
||||
- `<success>` -> Symfony style (green, bold) using `SymfonyStyle` or custom formatter style.
|
||||
- `<info>` -> Symfony `<info>` (blue where applicable) or custom style to match Phred’s blue guideline.
|
||||
- `<error>` -> white on red background.
|
||||
- `<warning>` -> black on yellow background.
|
||||
- `<comment>` -> yellow.
|
||||
- `<b>` -> bold; `<i>` -> italic; `<u>` -> underline (use ANSI sequences if formatter lacks direct styles).
|
||||
- If output is not decorated (no TTY or colors disabled), strip tags to plain text.
|
||||
|
||||
### 4.4 Helper Adapters
|
||||
- `InteractionInterface` -> wrap `QuestionHelper` (ask, confirm, secret, choice), ensuring non-interactive fallback behavior (return defaults, never block).
|
||||
- `ProgressBarInterface` -> wrap `Symfony\Component\Console\Helper\ProgressBar`.
|
||||
- `TableInterface` -> wrap `Symfony\Component\Console\Helper\Table`.
|
||||
- `MarkdownConverterInterface` -> use a lightweight adapter strategy:
|
||||
- Prefer Symfony-native formatting if available; otherwise convert Markdown to Phred markup (headings -> `<b>`, emphasis -> `<i>`/`<b>`, links -> underline/text), then pass through normal markup translation.
|
||||
|
||||
### 4.5 Error & Exit Codes
|
||||
- Return integer exit codes from wrapped command directly.
|
||||
- Exceptions: Allow the runner to handle globally; optionally map known `ConsoleExceptionInterface` to `ExitCode::USAGE|CONFIG|UNAVAILABLE|SOFTWARE` as appropriate.
|
||||
|
||||
## 5. PhredBridge (Native) — Design
|
||||
|
||||
- Provide minimal in-house adapters usable by the Tasker runner.
|
||||
- `PhredOutputAdapter`: translates fixed markup to ANSI escape sequences:
|
||||
- `<success>` -> green (e.g., `\e[32m`), `<info>` -> blue (`\e[34m`), `<error>` -> white on red (`\e[97;41m`), `<warning>` -> black on yellow (`\e[30;43m`), `<comment>` -> yellow (`\e[33m`), `<b>` -> bold (`\e[1m`), `<i>` -> italic (`\e[3m`), `<u>` -> underline (`\e[4m`). Reset with `\e[0m`.
|
||||
- TTY detection and `--no-interaction`/`--no-ansi` handling (strip tags when colors disabled or piping to files).
|
||||
- Provide simple `InteractionInterface`, `ProgressBarInterface`, `TableInterface`, and `MarkdownConverterInterface` implementations with zero external dependencies (basic, but reliable).
|
||||
|
||||
## 6. LaravelBridge — Design
|
||||
|
||||
### 6.1 Registration & Discovery
|
||||
- Provide a `TaskerServiceProvider` for Laravel applications.
|
||||
- Leverage Laravel Package Auto-Discovery via `composer.json` to register the provider automatically.
|
||||
- The provider will:
|
||||
- Discover Phred commands via `extra.phred-tasker` or explicit container binding.
|
||||
- Register them with the Artisan application.
|
||||
- Provide configuration for non-interactive defaults and formatting.
|
||||
|
||||
### 6.2 Container & Execution
|
||||
- Resolve `CommandInterface` instances via the Laravel Service Container (`app()`), favoring constructor injection.
|
||||
- Wrap commands in a `LaravelCommandAdapter` that extends `Illuminate\Console\Command`.
|
||||
- Delegate execution and return integer exit codes.
|
||||
|
||||
### 6.3 IO & Markup
|
||||
- Reuse `SymfonyBridge` IO adapters internally (Artisan's IO is built on Symfony).
|
||||
- Ensure Phred’s fixed markup set (`<success>`, `<info>`, `<error>`, `<warning>`, `<comment>`, `<b>`, `<i>`, `<u>`) renders natively in Artisan.
|
||||
|
||||
### 6.4 Helper Adapters
|
||||
- `InteractionInterface` -> Map to Laravel's native console methods (`ask`, `confirm`, `secret`, `choice`).
|
||||
- `ProgressBarInterface` / `TableInterface` -> Reuse SymfonyBridge adapters via Artisan.
|
||||
- `MarkdownConverterInterface` -> Standard Phred implementation (lightweight regex/mapping).
|
||||
|
||||
## 7. Dependency & Packaging Strategy
|
||||
|
||||
- Composer `suggest` dependencies per bridge:
|
||||
- `symfony/console` for SymfonyBridge.
|
||||
- `illuminate/console` for LaravelBridge.
|
||||
- Hard requirements: only `php:^8.2` and `getphred/console-contracts`.
|
||||
- Conditional wiring: Bridge factory/services check for class existence before enabling a bridge.
|
||||
- Namespacing: `Phred\TaskerBridges\Symfony\...`, `Phred\TaskerBridges\Phred\...`, `Phred\TaskerBridges\Laravel\...`.
|
||||
|
||||
## 8. Registration & Discovery
|
||||
|
||||
- Consumers may register bridges explicitly in DI (preferred for clarity).
|
||||
- For Laravel, auto-discovery via `TaskerServiceProvider` is the primary path.
|
||||
- For Symfony apps, provide a small integration guide to register `SymfonyCommandAdapter` instances in the application.
|
||||
- For Tasker, PhredBridge is used by default; other bridges are opt-in and instantiated only if their dependencies are present.
|
||||
|
||||
## 9. Middleware Compatibility
|
||||
|
||||
- Bridges must compose with `ConsoleMiddlewareInterface` stacks managed by the runner. The adapter boundary is after middleware resolution so that middlewares see Phred `InputInterface`/`OutputInterface` consistently.
|
||||
|
||||
## 10. Performance & Lazy Loading
|
||||
|
||||
- Metadata access should be O(1) with minimal reflection at runtime. If attributes are used (`#[Cmd]`, `#[Arg]`, `#[Opt]`) and surfaced via `HasAttributes`, bridges continue to consume only the `CommandInterface` methods — no extra reflection needed in bridges.
|
||||
- For toolkits that support lazy command discovery, prefer deferring heavy initialization until execution.
|
||||
|
||||
### 11. Testing Strategy (for later phases)
|
||||
|
||||
- Unit tests: Markup translation, verbosity mapping, non-interactive behavior, helpers integration.
|
||||
- Integration tests: Execute a sample Phred command through SymfonyBridge, LaravelBridge, and PhredBridge, verify identical behavior and exit codes.
|
||||
|
||||
## 12. Architectural Decisions (Resolved)
|
||||
|
||||
### 12.1 Global Options & Flags
|
||||
- Bridges will map toolkit-specific flags (e.g., Symfony’s `--ansi`, `--no-interaction`) to the Phred `InputInterface` and `OutputInterface` state.
|
||||
- **Purity**: Commands must NOT depend on toolkit-specific options. Instead, they should rely on the standardized Phred flags:
|
||||
- `-v`, `-vv`, `-vvv`, `-q` (Verbosity)
|
||||
- `-n` or `--no-interaction` (Non-interactive)
|
||||
- `--no-ansi` (Color/Decoration)
|
||||
- **Passthrough**: Toolkit-specific global options remain at the application layer and are consumed only by the bridge to configure the IO adapters.
|
||||
|
||||
### 12.2 Error Mapping Policy
|
||||
- Bridges are responsible for a "Standard Translation" of common exceptions into `sysexits.h` exit codes.
|
||||
- **Mapping Strategy**:
|
||||
- `InvalidArgumentException` -> `ExitCode::USAGE` (64)
|
||||
- `RuntimeException` -> `ExitCode::SOFTWARE` (70)
|
||||
- `ConsoleExceptionInterface` (from Contracts) -> Mapped according to category (e.g., `UNAVAILABLE`, `CONFIG`).
|
||||
- **Global Handler**: The primary runner (Tasker) or Bridge wrapper provides the top-level `try/catch` to ensure these codes are returned to the shell.
|
||||
|
||||
### 12.3 Helper Feature Parity
|
||||
- **Guaranteed Minimum API**: Bridges must implement the full `ProgressBarInterface` and `TableInterface` as defined in `ConsoleContracts`.
|
||||
- **Progressive Enhancement**: Bridges MAY support advanced features (e.g., table styling, progress bar steps) if the underlying toolkit supports it, but commands should avoid depending on these for core functionality.
|
||||
- **Fallback**: In non-TTY or non-supported environments, bridges must provide a "silent" or "degraded" implementation (e.g., printing table as a list, silencing progress bars) to prevent command failure.
|
||||
|
||||
## Brainstorming / Open Questions
|
||||
(All current open questions have been resolved and moved to Architectural Decisions.)
|
||||
89
SPECS.md
Normal file
89
SPECS.md
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# TaskerBridges Specification
|
||||
|
||||
This document defines the technical specifications for the `getphred/tasker-bridges` package, which adapts `Phred\ConsoleContracts` for various CLI runtimes.
|
||||
|
||||
## 1. Bridge Strategy
|
||||
|
||||
Bridges act as adapters between the standardized Phred CLI contracts and the native APIs of target console toolkits.
|
||||
|
||||
### 1.1 Universal Bridge Requirements
|
||||
All bridges must:
|
||||
- Adapt command metadata (name, description, arguments, options) to the native format.
|
||||
- Wrap execution to translate native input/output to Phred `InputInterface` and `OutputInterface`.
|
||||
- Implement fixed markup translation.
|
||||
- Map native verbosity and interaction states to Phred's standardized levels/flags.
|
||||
- Handle common exceptions and map them to `sysexits.h` exit codes.
|
||||
|
||||
## 2. SymfonyBridge
|
||||
|
||||
**Target**: `symfony/console` (v6.x and v7.x)
|
||||
**Namespace**: `Phred\TaskerBridges\Symfony`
|
||||
|
||||
### 2.1 SymfonyCommandAdapter
|
||||
- Extends `Symfony\Component\Console\Command\Command`.
|
||||
- Wraps a `Phred\ConsoleContracts\CommandInterface`.
|
||||
- Maps Phred metadata to Symfony's `setName`, `setDescription`, `addArgument`, and `addOption`.
|
||||
|
||||
### 2.2 Symfony IO Adapters
|
||||
- `SymfonyInputAdapter`: Wraps `Symfony\Input\InputInterface` and implements `Phred\ConsoleContracts\InputInterface`.
|
||||
- `SymfonyOutputAdapter`: Wraps `Symfony\Output\OutputInterface` and implements `Phred\ConsoleContracts\OutputInterface`.
|
||||
|
||||
### 2.3 Symfony Helper Adapters
|
||||
- `SymfonyInteractionAdapter`: Wraps `Symfony\Helper\QuestionHelper` to implement `Phred\ConsoleContracts\InteractionInterface`.
|
||||
- `SymfonyProgressBarAdapter`: Wraps `Symfony\Helper\ProgressBar`.
|
||||
- `SymfonyTableAdapter`: Wraps `Symfony\Helper\Table`.
|
||||
|
||||
## 3. LaravelBridge
|
||||
|
||||
**Target**: `illuminate/console` (v10.x and v11.x)
|
||||
**Namespace**: `Phred\TaskerBridges\Laravel`
|
||||
|
||||
### 3.1 LaravelCommandAdapter
|
||||
- Extends `Illuminate\Console\Command`.
|
||||
- Wraps a `Phred\ConsoleContracts\CommandInterface`.
|
||||
- Internally uses Symfony adapters for IO and helpers where applicable (as Artisan is built on Symfony).
|
||||
|
||||
### 3.2 LaravelServiceProvider
|
||||
- Handles auto-discovery of Phred commands from `composer.json` (`extra.phred-tasker`).
|
||||
- Registers commands within the Laravel container and Artisan application.
|
||||
|
||||
## 4. PhredBridge (Native)
|
||||
|
||||
**Target**: Tasker Core Runner
|
||||
**Namespace**: `Phred\TaskerBridges\Phred`
|
||||
|
||||
### 4.1 Native Adapters
|
||||
- Provides ANSI-based implementations for `OutputInterface`.
|
||||
- Provides lightweight implementations for `InteractionInterface`, `ProgressBarInterface`, and `TableInterface` with zero external dependencies.
|
||||
|
||||
### 4.2 MarkdownConverterInterface (Native)
|
||||
- **Implementation**: Provides a regex-based parser to translate standard Markdown (headings, emphasis, links) into the fixed Phred markup tag set (`<b>`, `<i>`, `<u>`, etc.).
|
||||
|
||||
## 5. Markup Translation Mapping
|
||||
|
||||
| Phred Tag | Phred Color/Style | Symfony/Laravel Implementation | Native Implementation (ANSI) |
|
||||
|:---|:---|:---|:---|
|
||||
| `<success>` | Green | Custom `SymfonyStyle` or Formatter style | `\e[32m` |
|
||||
| `<info>` | Blue | Custom Formatter style | `\e[34m` |
|
||||
| `<error>` | White on Red | `<error>` tag | `\e[97;41m` |
|
||||
| `<warning>` | Black on Yellow | Custom Formatter style | `\e[30;43m` |
|
||||
| `<comment>` | Yellow | `<comment>` tag | `\e[33m` |
|
||||
| `<b>` | Bold | `\e[1m` (if not native) | `\e[1m` |
|
||||
| `<i>` | Italic | `\e[3m` (if not native) | `\e[3m` |
|
||||
| `<u>` | Underline | `\e[4m` (if not native) | `\e[4m` |
|
||||
|
||||
## 6. Exit Code Translation
|
||||
|
||||
Bridges should map the following standard exceptions:
|
||||
- `InvalidArgumentException` -> `ExitCode::USAGE` (64)
|
||||
- `RuntimeException` -> `ExitCode::SOFTWARE` (70)
|
||||
- `LogicException` -> `ExitCode::SOFTWARE` (70)
|
||||
- `Phred\ConsoleContracts\ConsoleExceptionInterface` implementations -> mapped per category.
|
||||
|
||||
## 7. Global Options & Flags
|
||||
|
||||
Bridges are responsible for mapping toolkit-specific global options to the Phred state:
|
||||
- Symfony `--ansi` / `--no-ansi` -> `OutputInterface::setDecorated()`
|
||||
- Symfony `-v` / `-vv` / `-vvv` -> `OutputInterface::setVerbosity()`
|
||||
- Symfony `--no-interaction` -> `InteractionInterface` behavior (non-blocking).
|
||||
- Laravel specific variants of the above.
|
||||
20
composer.json
Normal file
20
composer.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "getphred/tasker-bridges",
|
||||
"description": "Optional bridges for integrating Phred CLI commands into various PHP console tools.",
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"getphred/console-contracts": "dev-master"
|
||||
},
|
||||
"suggest": {
|
||||
"getphred/tasker": "To use the PhredBridge with the Tasker runner",
|
||||
"symfony/console": "To use the SymfonyBridge",
|
||||
"laminas/laminas-console": "To use the LaminasBridge"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Phred\\TaskerBridges\\": "src/"
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/Phred/InputAdapter.php
Normal file
47
src/Phred/InputAdapter.php
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Phred\TaskerBridges\Phred;
|
||||
|
||||
use Phred\ConsoleContracts\InputInterface;
|
||||
|
||||
/**
|
||||
* Native Phred adapter for InputInterface.
|
||||
*/
|
||||
class InputAdapter implements InputInterface
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $arguments
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly array $arguments = [],
|
||||
private readonly array $options = []
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getArgument(string $name, mixed $default = null): mixed
|
||||
{
|
||||
return $this->arguments[$name] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getOption(string $name, mixed $default = null): mixed
|
||||
{
|
||||
return $this->options[$name] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function hasOption(string $name): bool
|
||||
{
|
||||
return array_key_exists($name, $this->options);
|
||||
}
|
||||
}
|
||||
140
src/Phred/OutputAdapter.php
Normal file
140
src/Phred/OutputAdapter.php
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Phred\TaskerBridges\Phred;
|
||||
|
||||
use Phred\ConsoleContracts\OutputInterface;
|
||||
|
||||
/**
|
||||
* Native Phred adapter for OutputInterface with ANSI support.
|
||||
*/
|
||||
class OutputAdapter implements OutputInterface
|
||||
{
|
||||
private int $verbosity = 1;
|
||||
|
||||
private bool $decorated = true;
|
||||
|
||||
/**
|
||||
* @var array<string, string> ANSI color mapping.
|
||||
*/
|
||||
private const TAGS = [
|
||||
'info' => '34', // Blue
|
||||
'success' => '32', // Green
|
||||
'error' => '31', // Red
|
||||
'warning' => '33', // Yellow
|
||||
'comment' => '36', // Cyan
|
||||
'bold' => '1',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param bool $decorated Whether to use ANSI decoration.
|
||||
*/
|
||||
public function __construct(bool $decorated = true)
|
||||
{
|
||||
$this->decorated = $decorated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function write(string $message): void
|
||||
{
|
||||
echo $this->format($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function writeln(string $message): void
|
||||
{
|
||||
echo $this->format($message) . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function success(string $message): void
|
||||
{
|
||||
$this->writeln("<success>$message</success>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function info(string $message): void
|
||||
{
|
||||
$this->writeln("<info>$message</info>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function error(string $message): void
|
||||
{
|
||||
$this->writeln("<error>$message</error>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function warning(string $message): void
|
||||
{
|
||||
$this->writeln("<warning>$message</warning>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function comment(string $message): void
|
||||
{
|
||||
$this->writeln("<comment>$message</comment>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function setVerbosity(int $level): void
|
||||
{
|
||||
$this->verbosity = $level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getVerbosity(): int
|
||||
{
|
||||
return $this->verbosity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the output should be decorated.
|
||||
*
|
||||
* @param bool $decorated
|
||||
* @return void
|
||||
*/
|
||||
public function setDecorated(bool $decorated): void
|
||||
{
|
||||
$this->decorated = $decorated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the message by replacing tags with ANSI codes or stripping them.
|
||||
*
|
||||
* @param string $message
|
||||
* @return string
|
||||
*/
|
||||
private function format(string $message): string
|
||||
{
|
||||
return preg_replace_callback('/<([a-z]+)>(.*?)<\/\\1>/i', function (array $matches): string {
|
||||
$tag = strtolower($matches[1]);
|
||||
$content = $matches[2];
|
||||
|
||||
if (!$this->decorated || !isset(self::TAGS[$tag])) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
return sprintf("\033[%sm%s\033[0m", self::TAGS[$tag], $content);
|
||||
}, $message) ?? $message;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue