Skip to content

refactor: consolidate console io into ioservice - #42

Merged
loadinglucian merged 4 commits into
mainfrom
refactor/consolidate-console-io-into-ioservice
Oct 13, 2025
Merged

refactor: consolidate console io into ioservice#42
loadinglucian merged 4 commits into
mainfrom
refactor/consolidate-console-io-into-ioservice

Conversation

@loadinglucian

@loadinglucian loadinglucian commented Oct 12, 2025

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Unified, consistent CLI prompts and outputs across commands.
    • Enhanced status messages with clear icons and standardized headings/separators.
    • Improved non-interactive “command hint” output for easier copy/paste usage.
  • Refactor
    • All console interactions now route through a centralized I/O layer for consistency and maintainability, with no expected behavior changes to existing commands.

Consolidate input gathering and output formatting from separate traits and
PrompterService into a single DI-injected IOService. Includes wrappers for
Laravel Prompts with spacing suppression, validation helpers, and custom
styling methods.
Update BaseCommand constructor and initialize() to inject and setup IOService.
Remove obsolete traits and PrompterService dependency. Adjust test fixtures
and helpers accordingly.
Update all console commands and ServerHelpersTrait to use IOService methods.
Remove obsolete PrompterService, ConsoleInputTrait, ConsoleOutputTrait, and
related tests.
Reorder service parameters in BaseCommand constructor to follow alphabetical order (env, inventory, io, proc).

Add comment in IOService initialize() explaining command usage for input inspection.

Update TestConsoleCommand constructor and docblock to match new order.

Align mockCommandContainer() parameters, assignments, and bindings in TestHelpers.php with alphabetical order, including updated comment.
@coderabbitai

coderabbitai Bot commented Oct 12, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Refactors console I/O to a centralized IOService. BaseCommand is updated to depend on IOService, commands/traits switch from direct/trait-based helpers to $this->io methods, PrompterService and Console* traits are removed, and tests are updated accordingly with new IOService tests and fixture changes.

Changes

Cohort / File(s) Change summary
Core I/O service introduction
app/Services/IOService.php
Adds IOService with initialize(context), prompt and output APIs (text/password/confirm/select/…/spin, writeln/info/success/warning/error/h1/hr, showCommandHint). Many methods made public. Replaces prior trait/prompter surfaces.
Base command wiring
app/Contracts/BaseCommand.php
Replaces SymfonyStyle/traits with injected IOService ($this->io). Initializes IOService in initialize(). All output routed via IOService. Removes ConsoleInputTrait/ConsoleOutputTrait and related properties/imports.
Console commands (server + hello)
app/Console/HelloCommand.php, app/Console/Server/*
Switches all UI interactions to $this->io->… (success, hr, h1, prompts, writeln, warnings/errors, hints). Control flow unchanged.
Server helpers
app/Traits/ServerHelpersTrait.php
Routes warnings/errors/prompts/prints through $this->io. Notes requirement for an IOService property.
Removed legacy I/O layers
app/Services/PrompterService.php, app/Traits/ConsoleOutputTrait.php
Deletes PrompterService and ConsoleOutputTrait along with all their methods.
Test fixtures and helpers
tests/Fixtures/TestConsoleCommand.php, tests/TestHelpers.php
Refactors fixture to use IOService (constructor injection; all calls via $this->io). Test helpers now mock/bind IOService; old Prompter-based helpers removed.
New IOService tests
tests/Unit/Services/IOServiceTest.php
Adds comprehensive tests for IOService prompts, validations, outputs, formatting, and command hints.
Updated unit tests
tests/Unit/Contracts/BaseCommandTest.php
Adjusts to use $this->io->writeln.
Removed test scaffolding for old layers
tests/Fixtures/MockPrompter.php, tests/Unit/Services/PrompterServiceTest.php, tests/Unit/Traits/ConsoleInputTraitTest.php, tests/Unit/Traits/ConsoleOutputTraitTest.php
Deletes MockPrompter and all tests for PrompterService and ConsoleInput/Output traits.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User
  participant CLI as Symfony Console
  participant Cmd as Command (e.g., ServerAddCommand)
  participant IO as IOService
  Note over CLI,Cmd: Command lifecycle
  User->>CLI: run command
  CLI->>Cmd: construct (inject IOService)
  CLI->>Cmd: initialize(Input, Output)
  Cmd->>IO: initialize(Cmd, Input, Output)
  Note over Cmd,IO: Execution phase
  CLI->>Cmd: execute()
  Cmd->>IO: h1/hr/writeln(...)
  alt needs input
    Cmd->>IO: getOptionOrPrompt(...)
    IO-->>Cmd: value or null
  end
  alt validation ok
    Cmd->>IO: success(...)
  else validation error
    Cmd->>IO: error(...)/warning(...)
    Cmd-->>CLI: return with failure/same code path
  end
  Cmd-->>CLI: return status
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • bigpixelrocket/deployer-php#23 — Earlier extraction of ConsoleInputTrait/ConsoleOutputTrait; this PR removes those traits in favor of IOService, touching the same I/O layer.
  • bigpixelrocket/deployer-php#28 — Introduced PrompterService and related test fixtures; this PR removes PrompterService and replaces it with IOService.
  • bigpixelrocket/deployer-php#27 — Added server CRUD commands; this PR refactors those commands to use IOService for all I/O.

Poem

A rabbit taps the terminal keys,
New IO hops with graceful ease.
Traits are gone, the service sings,
Prompts and prints grow tidy wings.
With ticks and checks, we bound ahead—
“Success!” the console softly said. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly conveys that this pull request refactors the codebase by consolidating all console input/output functionality into the new IOService, accurately reflecting the primary change without unnecessary details.
Docstring Coverage ✅ Passed Docstring coverage is 96.15% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/consolidate-console-io-into-ioservice

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a7d2dc and 6876527.

📒 Files selected for processing (17)
  • app/Console/HelloCommand.php (1 hunks)
  • app/Console/Server/ServerAddCommand.php (9 hunks)
  • app/Console/Server/ServerDeleteCommand.php (3 hunks)
  • app/Console/Server/ServerListCommand.php (2 hunks)
  • app/Contracts/BaseCommand.php (5 hunks)
  • app/Services/IOService.php (16 hunks)
  • app/Services/PrompterService.php (0 hunks)
  • app/Traits/ConsoleOutputTrait.php (0 hunks)
  • app/Traits/ServerHelpersTrait.php (5 hunks)
  • tests/Fixtures/MockPrompter.php (0 hunks)
  • tests/Fixtures/TestConsoleCommand.php (11 hunks)
  • tests/TestHelpers.php (4 hunks)
  • tests/Unit/Contracts/BaseCommandTest.php (1 hunks)
  • tests/Unit/Services/IOServiceTest.php (1 hunks)
  • tests/Unit/Services/PrompterServiceTest.php (0 hunks)
  • tests/Unit/Traits/ConsoleInputTraitTest.php (0 hunks)
  • tests/Unit/Traits/ConsoleOutputTraitTest.php (0 hunks)
💤 Files with no reviewable changes (6)
  • app/Services/PrompterService.php
  • tests/Fixtures/MockPrompter.php
  • tests/Unit/Traits/ConsoleOutputTraitTest.php
  • app/Traits/ConsoleOutputTrait.php
  • tests/Unit/Traits/ConsoleInputTraitTest.php
  • tests/Unit/Services/PrompterServiceTest.php
🧰 Additional context used
📓 Path-based instructions (10)
**/*.php

📄 CodeRabbit inference engine (.cursor/rules/00-main.mdc)

**/*.php: Eliminate single-use private methods by inlining them directly
Cache expensive computed values by initializing them in the constructor instead of recomputing
Prefer direct property access over method calls when appropriate to avoid call overhead
Organize code into comment-separated sections; prefer alphabetical ordering when it does not conflict with logical grouping

**/*.php: Adhere to PSR-12 coding style in all PHP files
Declare strict_types=1 at the top of every PHP file
Prefer PHP 8.x features (union types, match, attributes, readonly) where appropriate
Always import classes with use statements; avoid fully qualified class names in code bodies
All methods must declare explicit return types; use proper generics in types/docblocks (e.g., Collection<int, User>)
Use dependency injection instead of manually resolving or instantiating classes
Prefer Symfony component classes (e.g., Filesystem, Process) over native PHP functions for testability
All object creation must use $container->build(ClassName::class) instead of new, except for value objects/DTOs/pure data structures
In production code, access the Container via constructor injection, not via static/global access
Add minimal DocBlock comments with descriptions, parameters, and return types for classes and functions
Use comments as visual separators for sections/subsections with a single newline between header, subheader, and paragraph; avoid obvious or stale comments
Run rector on changed PHP files before completing a task
Run pint on changed PHP files to fix code style before completing a task

Files:

  • app/Contracts/BaseCommand.php
  • tests/Fixtures/TestConsoleCommand.php
  • app/Traits/ServerHelpersTrait.php
  • app/Console/Server/ServerAddCommand.php
  • tests/TestHelpers.php
  • app/Console/Server/ServerDeleteCommand.php
  • tests/Unit/Contracts/BaseCommandTest.php
  • app/Console/HelloCommand.php
  • app/Console/Server/ServerListCommand.php
  • tests/Unit/Services/IOServiceTest.php
  • app/Services/IOService.php
**/*Command.php

📄 CodeRabbit inference engine (.cursor/rules/01-architecture.mdc)

**/*Command.php: Commands handle user interaction (input/output) and orchestrate services
Commands must not contain business logic; delegate business logic to Services
Commands must not duplicate orchestration logic; extract shared orchestration to Services
Commands should not invoke other commands (no proxy commands)
Use SymfonyStyle consistently for all user-facing console output

Files:

  • app/Contracts/BaseCommand.php
  • tests/Fixtures/TestConsoleCommand.php
  • app/Console/Server/ServerAddCommand.php
  • app/Console/Server/ServerDeleteCommand.php
  • app/Console/HelloCommand.php
  • app/Console/Server/ServerListCommand.php
app/{Contracts/BaseCommand.php,Traits/ConsoleOutputTrait.php}

📄 CodeRabbit inference engine (.cursor/rules/03-commands.mdc)

If output functionality is missing, add a new method to BaseCommand/ConsoleOutputTrait with modern styling and documentation

Files:

  • app/Contracts/BaseCommand.php
app/Contracts/BaseCommand.php

📄 CodeRabbit inference engine (.cursor/rules/03-commands.mdc)

Limit BaseCommand to shared initialization, configuration, and orchestration logic; do not place individual IO operations here

Files:

  • app/Contracts/BaseCommand.php
{tests/**,test/**,**/*@(Test|Spec).php}

📄 CodeRabbit inference engine (.cursor/rules/00-main.mdc)

Do not run or edit tests unless explicitly instructed

Files:

  • tests/Fixtures/TestConsoleCommand.php
  • tests/TestHelpers.php
  • tests/Unit/Contracts/BaseCommandTest.php
  • tests/Unit/Services/IOServiceTest.php
tests/**/*.php

📄 CodeRabbit inference engine (.cursor/rules/01-architecture.mdc)

tests/**/*.php: In tests, direct Container instantiation and bind() for mocks is allowed and encouraged for isolation
Do not run PHPStan on test files; tests are excluded from static analysis

tests/**/*.php: Use Pest exclusively with it() syntax for all tests
Unit tests (services/utilities) must use manual instantiation of dependencies
Command/integration tests must use mockCommandContainer() for building commands and overriding services
Use container auto-wiring only when verifying DI configuration or multi-service integration
Keep test files minimal: target under 1.8x the size of the source they cover without sacrificing readability
Test core business logic only; avoid testing the framework itself
Prefer dataset-driven testing using ->with([...]) for multiple scenarios
Eliminate overlapping tests; do not cover the same functionality in multiple tests
Consolidate assertions with chained expectations (expect(...)->toX()->and(...)->toY())
Mock only external dependencies; keep unit tests isolated
Avoid performance tests unless performance is the primary concern
Do not sacrifice readability to meet size/ratio targets
Follow the AAA pattern (Arrange, Act, Assert) in all tests; add Cleanup when needed
For exception tests, combine steps as // ACT & ASSERT when the act triggers the assertion
Organize tests with describe() blocks, beforeEach() setup, and shared helpers/traits for DRY
Avoid meaningless assertions (type-only checks, generic truthiness/nullness, sleeping; use time mocking)
Prefer meaningful assertions invoking real behavior (config values, validators, mocks with expectations)
Unit tests: mock all external dependencies, test single units, run in milliseconds
Integration tests: use real filesystem/externals, cover CLI commands and full workflows
Ignore PHPStan issues in tests; focus on test functionality over strict types
Avoid excessive phpdoc in tests solely to appease types

Files:

  • tests/Fixtures/TestConsoleCommand.php
  • tests/TestHelpers.php
  • tests/Unit/Contracts/BaseCommandTest.php
  • tests/Unit/Services/IOServiceTest.php
app/Console/**/*Command.php

📄 CodeRabbit inference engine (.cursor/rules/03-commands.mdc)

app/Console/**/*Command.php: Never call SymfonyStyle IO directly in commands; use BaseCommand custom IO methods (writeln, hr, h1, success, error, warning, info) exclusively
Use status helper methods (success, error, warning, info) for all status messages to ensure consistent formatting
Use laravel/prompts for all user interactions (text, password, confirm, select, multiselect, suggest, search, spin)
Commands orchestrate services and format all console IO; keep business logic output out of services
Support both interactive prompts and CLI options using getOptionOrPrompt(optionName, promptCallback) for dual-mode commands
Use getValidatedOptionOrPrompt for inputs that require validation; inject validator once and reuse for prompts and options
Define command inputs using OPTIONS only, never ARGUMENTS, to enable getOptionOrPrompt pattern
Follow option naming conventions: --server/--site for selecting existing resources; --name for defining new resource; --host, --port for server config; --yes/-y for confirmations; --skip to bypass validation
Pair every defined option with getOptionOrPrompt to provide both CLI and interactive flows
Boolean flags must use VALUE_NONE; data inputs must use VALUE_REQUIRED
Only --yes gets a short flag (-y); do not assign short flags to other options
Always call showCommandHint() before returning Command::SUCCESS to display non-interactive usage

Files:

  • app/Console/Server/ServerAddCommand.php
  • app/Console/Server/ServerDeleteCommand.php
  • app/Console/HelloCommand.php
  • app/Console/Server/ServerListCommand.php
tests/TestHelpers.php

📄 CodeRabbit inference engine (.cursor/rules/02-tests.mdc)

When BaseCommand adds a new service, update mockCommandContainer() in tests/TestHelpers.php by adding a parameter, building or accepting the service, and binding it to the container

Files:

  • tests/TestHelpers.php
**/*Service.php

📄 CodeRabbit inference engine (.cursor/rules/01-architecture.mdc)

**/*Service.php: Services provide atomic, reusable functionality with no console I/O
Services accept and return plain PHP data types (no console I/O)
Services must be dependency-injected via constructor; declare dependencies in constructor signatures
Extract complex orchestration shared by multiple Commands into dedicated Services
Stateful services should use lazy loading when initialization is expensive or path-dependent
State must be initialized explicitly via public methods (e.g., load(), initialize()) before use
Services should document their stateful nature and initialization requirements

Files:

  • app/Services/IOService.php
app/Services/**/*.php

📄 CodeRabbit inference engine (.cursor/rules/03-commands.mdc)

Services must return plain data and never perform console IO (no SymfonyStyle, no Laravel Prompts)

Files:

  • app/Services/IOService.php
🧠 Learnings (12)
📚 Learning: 2025-10-12T15:51:30.236Z
Learnt from: CR
PR: bigpixelrocket/deployer-php#0
File: .cursor/rules/03-commands.mdc:0-0
Timestamp: 2025-10-12T15:51:30.236Z
Learning: Applies to app/Console/**/*Command.php : Never call SymfonyStyle IO directly in commands; use BaseCommand custom IO methods (writeln, hr, h1, success, error, warning, info) exclusively

Applied to files:

  • app/Contracts/BaseCommand.php
  • app/Console/Server/ServerDeleteCommand.php
  • app/Console/HelloCommand.php
📚 Learning: 2025-10-12T15:51:30.236Z
Learnt from: CR
PR: bigpixelrocket/deployer-php#0
File: .cursor/rules/03-commands.mdc:0-0
Timestamp: 2025-10-12T15:51:30.236Z
Learning: Applies to app/Contracts/BaseCommand.php : Limit BaseCommand to shared initialization, configuration, and orchestration logic; do not place individual IO operations here

Applied to files:

  • app/Contracts/BaseCommand.php
📚 Learning: 2025-10-12T15:51:30.236Z
Learnt from: CR
PR: bigpixelrocket/deployer-php#0
File: .cursor/rules/03-commands.mdc:0-0
Timestamp: 2025-10-12T15:51:30.236Z
Learning: Applies to app/Console/**/*Command.php : Commands orchestrate services and format all console IO; keep business logic output out of services

Applied to files:

  • app/Contracts/BaseCommand.php
📚 Learning: 2025-10-12T15:50:03.829Z
Learnt from: CR
PR: bigpixelrocket/deployer-php#0
File: .cursor/rules/02-tests.mdc:0-0
Timestamp: 2025-10-12T15:50:03.829Z
Learning: Applies to tests/TestHelpers.php : When BaseCommand adds a new service, update mockCommandContainer() in tests/TestHelpers.php by adding a parameter, building or accepting the service, and binding it to the container

Applied to files:

  • tests/Fixtures/TestConsoleCommand.php
  • tests/TestHelpers.php
📚 Learning: 2025-10-12T15:51:30.236Z
Learnt from: CR
PR: bigpixelrocket/deployer-php#0
File: .cursor/rules/03-commands.mdc:0-0
Timestamp: 2025-10-12T15:51:30.236Z
Learning: Applies to app/Traits/ConsoleOutputTrait.php : Add output/formatting and status helper methods to ConsoleOutputTrait; methods should operate via $this->io (SymfonyStyle)

Applied to files:

  • tests/Fixtures/TestConsoleCommand.php
  • app/Traits/ServerHelpersTrait.php
📚 Learning: 2025-10-12T15:51:30.236Z
Learnt from: CR
PR: bigpixelrocket/deployer-php#0
File: .cursor/rules/03-commands.mdc:0-0
Timestamp: 2025-10-12T15:51:30.236Z
Learning: Applies to app/{Contracts/BaseCommand.php,Traits/ConsoleOutputTrait.php} : If output functionality is missing, add a new method to BaseCommand/ConsoleOutputTrait with modern styling and documentation

Applied to files:

  • tests/Fixtures/TestConsoleCommand.php
📚 Learning: 2025-10-12T15:51:30.236Z
Learnt from: CR
PR: bigpixelrocket/deployer-php#0
File: .cursor/rules/03-commands.mdc:0-0
Timestamp: 2025-10-12T15:51:30.236Z
Learning: Applies to app/Console/**/*Command.php : Support both interactive prompts and CLI options using getOptionOrPrompt(optionName, promptCallback) for dual-mode commands

Applied to files:

  • tests/Fixtures/TestConsoleCommand.php
  • app/Services/IOService.php
📚 Learning: 2025-10-12T15:51:30.236Z
Learnt from: CR
PR: bigpixelrocket/deployer-php#0
File: .cursor/rules/03-commands.mdc:0-0
Timestamp: 2025-10-12T15:51:30.236Z
Learning: Applies to app/Console/**/*Command.php : Pair every defined option with getOptionOrPrompt to provide both CLI and interactive flows

Applied to files:

  • tests/Fixtures/TestConsoleCommand.php
  • app/Services/IOService.php
📚 Learning: 2025-10-12T15:51:30.236Z
Learnt from: CR
PR: bigpixelrocket/deployer-php#0
File: .cursor/rules/03-commands.mdc:0-0
Timestamp: 2025-10-12T15:51:30.236Z
Learning: Applies to app/Console/**/*Command.php : Use getValidatedOptionOrPrompt for inputs that require validation; inject validator once and reuse for prompts and options

Applied to files:

  • tests/Fixtures/TestConsoleCommand.php
  • app/Services/IOService.php
📚 Learning: 2025-10-12T15:50:03.829Z
Learnt from: CR
PR: bigpixelrocket/deployer-php#0
File: .cursor/rules/02-tests.mdc:0-0
Timestamp: 2025-10-12T15:50:03.829Z
Learning: Applies to tests/**/*.php : Command/integration tests must use mockCommandContainer() for building commands and overriding services

Applied to files:

  • tests/TestHelpers.php
📚 Learning: 2025-10-12T15:51:30.236Z
Learnt from: CR
PR: bigpixelrocket/deployer-php#0
File: .cursor/rules/03-commands.mdc:0-0
Timestamp: 2025-10-12T15:51:30.236Z
Learning: Applies to app/Traits/ConsoleInputTrait.php : Add input-gathering methods (promptText, promptSelect, etc.) to ConsoleInputTrait; methods should operate via $this->input

Applied to files:

  • app/Services/IOService.php
📚 Learning: 2025-10-12T15:51:30.236Z
Learnt from: CR
PR: bigpixelrocket/deployer-php#0
File: .cursor/rules/03-commands.mdc:0-0
Timestamp: 2025-10-12T15:51:30.236Z
Learning: Applies to app/Console/**/*Command.php : Use laravel/prompts for all user interactions (text, password, confirm, select, multiselect, suggest, search, spin)

Applied to files:

  • app/Services/IOService.php
🧬 Code graph analysis (11)
app/Contracts/BaseCommand.php (2)
app/Services/IOService.php (3)
  • IOService (30-577)
  • initialize (41-46)
  • writeln (457-463)
app/Services/InventoryService.php (1)
  • getInventoryFileStatus (116-119)
tests/Fixtures/TestConsoleCommand.php (2)
app/Services/IOService.php (20)
  • IOService (30-577)
  • info (468-471)
  • error (492-495)
  • success (476-479)
  • warning (484-487)
  • h1 (500-506)
  • hr (511-517)
  • writeln (457-463)
  • showCommandHint (524-560)
  • getOptionOrPrompt (84-132)
  • promptText (199-217)
  • getValidatedOptionOrPrompt (159-181)
  • promptSpin (438-446)
  • promptPassword (230-246)
  • promptConfirm (259-275)
  • promptPause (284-289)
  • promptSelect (303-321)
  • promptMultiselect (336-356)
  • promptSuggest (372-394)
  • promptSearch (408-426)
app/Contracts/BaseCommand.php (1)
  • __construct (35-51)
app/Traits/ServerHelpersTrait.php (1)
app/Services/IOService.php (5)
  • warning (484-487)
  • writeln (457-463)
  • getOptionOrPrompt (84-132)
  • promptSelect (303-321)
  • error (492-495)
app/Console/Server/ServerAddCommand.php (2)
app/Services/IOService.php (12)
  • hr (511-517)
  • h1 (500-506)
  • getValidatedOptionOrPrompt (159-181)
  • promptText (199-217)
  • getOptionOrPrompt (84-132)
  • promptConfirm (259-275)
  • warning (484-487)
  • writeln (457-463)
  • error (492-495)
  • success (476-479)
  • showCommandHint (524-560)
  • promptSpin (438-446)
app/Traits/ServerHelpersTrait.php (1)
  • displayServerDeets (74-84)
tests/TestHelpers.php (2)
app/Services/IOService.php (1)
  • IOService (30-577)
app/Container.php (1)
  • bind (46-50)
app/Console/Server/ServerDeleteCommand.php (1)
app/Services/IOService.php (8)
  • hr (511-517)
  • h1 (500-506)
  • getOptionOrPrompt (84-132)
  • promptConfirm (259-275)
  • warning (484-487)
  • writeln (457-463)
  • success (476-479)
  • showCommandHint (524-560)
tests/Unit/Contracts/BaseCommandTest.php (1)
app/Services/IOService.php (1)
  • writeln (457-463)
app/Console/HelloCommand.php (1)
app/Services/IOService.php (1)
  • success (476-479)
app/Console/Server/ServerListCommand.php (1)
app/Services/IOService.php (4)
  • hr (511-517)
  • warning (484-487)
  • writeln (457-463)
  • h1 (500-506)
tests/Unit/Services/IOServiceTest.php (3)
tests/TestHelpers.php (1)
  • mockCommandContainer (347-386)
app/Container.php (1)
  • build (59-85)
tests/Fixtures/TestConsoleCommand.php (2)
  • TestConsoleCommand (27-286)
  • setTestMethod (62-66)
app/Services/IOService.php (1)
app/Contracts/BaseCommand.php (1)
  • initialize (89-119)
🪛 PHPMD (2.15.0)
tests/Fixtures/TestConsoleCommand.php

204-204: Avoid unused parameters such as '$value'. (undefined)

(UnusedFormalParameter)


284-284: Avoid unused parameters such as '$q'. (undefined)

(UnusedFormalParameter)

🔇 Additional comments (14)
app/Services/IOService.php (1)

1-577: Excellent refactor consolidating console I/O into a dedicated service.

The implementation is solid with comprehensive prompt wrappers, output helpers, and sophisticated dual-mode option handling. The getOptionOrPrompt logic correctly distinguishes between "flag not provided" vs "flag provided with false value" using hasParameterOption, which is essential for supporting both interactive and CLI workflows.

Note: IOService is in app/Services/ but performs console I/O, which conflicts with the guideline "Services must return plain data and never perform console IO". However, this is clearly intentional as part of this refactor to centralize I/O handling. Consider updating the coding guidelines to explicitly allow IOService as an exception, or document that I/O-specific services may perform console operations.

The suppressPromptSpacing() method (lines 571-576) uses ANSI escape sequences directly. While this works in most modern terminals, consider whether this could fail in environments that don't support ANSI codes (e.g., Windows Command Prompt without ANSI.SYS). The current implementation is pragmatic, but documenting this requirement or adding a capability check could prevent future issues in edge-case environments.

Based on coding guidelines (regarding IOService location and I/O handling pattern).

app/Console/HelloCommand.php (1)

25-25: LGTM!

Correctly routes output through the IOService wrapper, consistent with the refactor.

tests/Unit/Contracts/BaseCommandTest.php (1)

32-32: LGTM!

Test fixture correctly updated to use IOService for output.

app/Console/Server/ServerDeleteCommand.php (1)

44-93: LGTM!

All I/O operations correctly routed through the IOService wrapper. The logic flow and behavior are preserved while gaining the benefits of centralized I/O handling.

app/Traits/ServerHelpersTrait.php (2)

15-16: Good documentation of the IOService dependency.

Clearly specifies that using classes must provide protected IOService $io, which aids understanding and prevents misuse.


34-83: LGTM!

All I/O operations correctly routed through IOService. The trait maintains its functionality while aligning with the centralized I/O pattern.

app/Console/Server/ServerListCommand.php (1)

30-47: LGTM!

Consistent refactor routing all output through IOService.

tests/TestHelpers.php (2)

218-233: LGTM!

The mockIOService() helper correctly provides an IOService instance for testing. The docblock appropriately notes that initialize() must be called before using I/O methods, which is consistent with how commands use IOService.


347-386: LGTM!

The mockCommandContainer() correctly updated to accept and bind IOService, replacing the previous PrompterService approach. This aligns with the guideline requirement to update this helper when BaseCommand adds new services.

Based on coding guidelines (test helper maintenance pattern).

app/Console/Server/ServerAddCommand.php (1)

54-260: LGTM!

Comprehensive refactor routing all I/O operations through IOService. The command maintains its functionality and logic flow while gaining the benefits of centralized I/O handling. All prompt methods (text, confirm, spin) and output methods (hr, h1, success, error, warning, writeln, showCommandHint) are correctly accessed via $this->io.

app/Contracts/BaseCommand.php (4)

12-12: LGTM! IOService import and documentation.

The IOService import and updated class documentation clearly reflect the refactoring objective to consolidate I/O operations through a dedicated service.

Also applies to: 23-25


31-42: LGTM! IOService dependency injection.

The IOService is properly injected as a readonly protected property, positioned logically within the "Base services" group. The constructor documentation accurately reflects the addition of the I/O service dependency.


82-96: LGTM! Proper IOService initialization.

The IOService is correctly initialized with command context before environment and inventory loading, ensuring I/O operations are available for any subsequent operations that may need them. The initialization order is logical and follows the IOService API requirements.


135-146: LGTM! Consistent IOService usage for status display.

The environment and inventory status display correctly uses $this->io->writeln(), maintaining consistency with the refactor. This shared status display is appropriate orchestration logic for BaseCommand, as it provides common context needed by all commands.

Based on learnings


Comment @coderabbitai help to get the list of available commands and usage tips.

@loadinglucian
loadinglucian merged commit 63e1ab7 into main Oct 13, 2025
5 checks passed
@loadinglucian
loadinglucian deleted the refactor/consolidate-console-io-into-ioservice branch October 13, 2025 08:06
@coderabbitai coderabbitai Bot mentioned this pull request Oct 16, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Nov 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant