feat: implement Modern API with immutable parser and builders#6
Merged
ralflang merged 7 commits intoFRAMEWORK_6_0from Mar 4, 2026
Merged
feat: implement Modern API with immutable parser and builders#6ralflang merged 7 commits intoFRAMEWORK_6_0from
ralflang merged 7 commits intoFRAMEWORK_6_0from
Conversation
- Add autoload-dev for test namespace - Add minimum-stability: dev with prefer-stable - Add UPGRADING.md documenting PSR-4 breaking changes These changes support running tests and document migration path.
Add @internal and @deprecated warnings to stub classes that are under development and not ready for production use: - ArgvParser interface - ImmutableParser - ParserArgument - ParserBuilder - ParserOption - ParserResult These stubs will be evolved during Modern API implementation.
Implement foundation of Modern API following 8 guiding principles. Enums (Principle #2: Enums as Behavior Objects): - OptionAction: Actions encapsulate their own behavior * takesValue() - knows if it needs a value from argv * requiresArgument() - knows if value is mandatory * isBoolean() - identifies boolean flags * getDefaultValue() - provides default when flag present * execute() - processes value given current state - OptionType: Types validate and convert * validate() - checks value matches type * convert() - converts or throws (Principle #4) * getPhpType() - PHP type name for hints * getDescription() - user-friendly error messages - ConflictHandler: Simple enum for conflict resolution Config Objects (Principle #7: Immutable Throughout): - ParserConfig: Readonly parser configuration * with() method for immutable updates - OptionConfig: Readonly option configuration * Validates on construction (fails early) * Optional validator and map (Principle #3) * Auto-generates destination from option names - OptionGroupConfig: Readonly group configuration Exceptions: - InvalidOptionException: Configuration validation errors - ValueValidationException: Type validation failures * Contains context (option, value, type, reason) * Used by OptionType::convert() Key Design Decisions: - All config objects readonly (Principle #7) - Enums contain behavior methods (Principle #2) - Type contracts enforced strictly (Principle #4) - Validators/transformers optional (Principle #3) - callables use mixed type (PHP limitation with readonly) All files follow: - declare(strict_types=1) - PER-1 coding standard - Comprehensive PHPDoc - Copyright 2024-2026 Ready for Week 2: Result Objects & Validators
…dators Complete Week 2 deliverables following guiding principles. Result Objects (Principle #6 & #7): - OptionValues: Immutable values container * get() - explicit access with optional default * has() - check existence (even if null) * all() - get all values as array * keys() - get all option names * isEmpty() - check if empty * count() - count options - ParseResult: Immutable parse result * Explicit separation: options vs arguments vs unknown * hasUnknown() - check for unknown options * hasArguments() - check for positional args * getOption() - convenience shorthand Additional Exceptions: - AmbiguousOptionException: Partial match → multiple possibilities * getPossibilities() - all matches * Context for debugging - MissingValueException: Option requires value but none provided * Simple, focused error - ConflictingOptionException: Mutually exclusive options * getConflictsWith() - conflicting option name Validator Utilities (Principle #3): - Validators: Optional helper class * range() - min/max validation * regex() - pattern matching * file() - file existence/readability * directory() - directory existence/writability * choice() - allowed values * minLength/maxLength() - string length * email() - email format * url() - URL format * all() - combine validators (AND logic) * any() - combine validators (OR logic) All validators return true or error string for composability. Design Notes: - No ArrayAccess (Principle #6: explicit access) - All result objects readonly (Principle #7) - Validators are pure functions (no state) - Validators are OPTIONAL (Principle #3) - Clear separation: options vs args vs unknown Files: 6 new (OptionValues, ParseResult, 3 exceptions, Validators) Lines: ~450 added Ready for Week 3: Immutable Builders
Add 115 unit tests covering all Week 1-2 deliverables with 100% pass rate. Test Coverage: Enum Tests (28 tests): - OptionActionTest: All action behavior methods * takesValue(), requiresArgument(), isBoolean() * getDefaultValue(), execute() * All 10 action cases tested - OptionTypeTest: Type validation and conversion * validate() for Int, Float, String * convert() with type guarantees (Principle #4) * getPhpType(), getDescription() * Exception context validation Config Tests (19 tests): - OptionConfigTest: Readonly configuration * Construction validation (fails early) * Invalid format detection (short, long) * Constraint validation (choices, const, callback) * Destination auto-generation * Readonly enforcement Result Tests (23 tests): - OptionValuesTest: Explicit access methods * get(), has(), all(), keys() * isEmpty(), count() * Null value handling * Readonly enforcement - ParseResultTest: Immutable results * Options/arguments/unknown separation * Convenience methods * Readonly enforcement Validator Tests (45 tests): - ValidatorsTest: Optional infrastructure (Principle #3) * range(), regex(), choice() * minLength(), maxLength() * email(), url() * file(), directory() * all() and any() combinators * Composability examples All tests verify: - Principle #2: Enums encapsulate behavior - Principle #3: Validators are optional - Principle #4: Explicit type contracts - Principle #6: Explicit access (no ArrayAccess) - Principle #7: Immutability (readonly enforcement) Test Results: ✅ 115/115 passing (100%) Test Framework: PHPUnit 13.0.5 Assertions: 179 Runtime: 0.045s Memory: 27.95 MB Ready for Week 3: Immutable Builders
Implement immutable builder pattern for Modern API following Principle #7. Each builder method clones and returns a new instance (~4μs overhead). Builders Added: - OptionBuilder: 14 setter methods, 3 convenience methods (flag, repeatable, counter) - GroupBuilder: group title, description, and options collection - ParserBuilder: complete parser configuration with options and groups Key Design Decisions: - Clone-on-modify pattern ensures immutability - Builder branching enables creating multiple configs from shared base - Use-and-amend pattern support (fromConfig, setOptions, setGroups) - Named parameters throughout for clarity - withX() naming for parser configuration methods - Internal methods for parser reconstruction (toBuilder pattern) Test Coverage: - 58 tests added (173 total Modern API tests) - 97 assertions added (276 total assertions) - All tests passing (100%) - Tests verify immutability through branching scenarios - Tests verify method chaining works correctly Example Usage: $option = OptionBuilder::create() ->short('-v') ->long('--verbose') ->flag() ->help('Enable verbose output') ->build(); $parser = ParserBuilder::create() ->withUsage('%prog [options] <file>') ->addOption($option) ->build(); Performance Impact: - Builder cloning overhead: ~4μs per method call (negligible) - Immutability guarantees worth the minimal cost Principles Demonstrated: - Principle #7: Immutable Throughout - builders are fully immutable - Principle #5: Modern API is Immutable - no mutation after construction - Builder pattern enables ergonomic immutable construction Next Steps: Week 4-5 ImmutableParser implementation (core parsing engine)
…k 4) Implement complete immutable argument parser following Principles #5 and #7. Parser is configured once via builder and cannot be modified. Supports full POSIX-style option parsing with type conversion, validation, and transformation. Core Features: - Short options: -v, -abc (bundled flags), -nVALUE (attached value) - Long options: --verbose, --name VALUE, --name=VALUE - Positional arguments with configurable interspersing - Double-dash (--) marks end of options - Partial long option matching (--ver matches --verbose if unambiguous) - Unknown option handling (error, allow, or ignore) - Option groups (for help formatting) Type System: - Built-in types: String, Int, Float with automatic conversion - Custom validators: Validate after type conversion - Transformers (map): Transform validated values - Choices: Enforce allowed value lists Actions: - Store: Store single value - StoreTrue/StoreFalse: Boolean flags - Append: Collect multiple values in array - Count: Increment counter (e.g., -vvv = 3) - StoreConst/AppendConst: Store predefined constant - Callback: Custom processing function Exception Handling: - InvalidOptionException: Unknown options, invalid choices, validation failures - AmbiguousOptionException: Partial match ambiguity (--ver matches both --verbose and --version) - MissingValueException: Option requires value but none provided - ConflictingOptionException: Duplicate option definitions - All exceptions include context for debugging Parser Configuration: - allowInterspersedArgs: Mix options and arguments (default: true) - allowUnknownArgs: Don't error on unknown options (collect in result.unknown) - ignoreUnknownArgs: Silently skip unknown options - conflictHandler: How to handle duplicate options Use-and-Amend Pattern: - toBuilder(): Convert parser back to builder for modification - Original parser remains immutable - Enables creating parser variants Example Usage: $verbose = OptionBuilder::create() ->short('-v')->counter()->dest('verbosity')->build(); $port = OptionBuilder::create() ->short('-p')->long('--port') ->type(OptionType::Int)->default(8080) ->validator(Validators::range(1, 65535)) ->build(); $parser = ParserBuilder::create() ->withUsage('%prog [options] <file>') ->addOption($verbose) ->addOption($port) ->build(); $result = $parser->parse(['-vv', '--port=3000', 'file.txt']); echo $result->options->get('verbosity'); // 2 echo $result->options->get('port'); // 3000 echo $result->arguments[0]; // file.txt Test Coverage: - 33 tests added (206 total Modern API tests) - 54 assertions added (330 total assertions) - All tests passing (100%) - Tests cover all actions, types, exceptions, and edge cases Performance: - Readonly class with computed option maps at construction - Direct array access for option lookup (O(1)) - No reflection or dynamic property access during parsing Principles Demonstrated: - Principle #5: Modern API is Immutable - parser fully immutable - Principle #7: Immutable Throughout - readonly class, toBuilder() for modifications - Principle #4: Explicit Type Contracts - type conversion guarantees result type - Principle #3: Infrastructure Not Mandated - validators/transformers optional - Principle #6: Explicit Access - no magic, clear method calls Implementation Details: - Option map built once at construction for fast lookup - Short option bundling handled character-by-character - Long option partial matching with ambiguity detection - Type conversion before validation before transformation (correct order) - Actions encapsulated in enum execute() methods (Principle #2) Next Steps: Week 5 would add help formatting, but core parsing is complete
ralflang
added a commit
that referenced
this pull request
Mar 4, 2026
Implement immutable help formatter for Modern API following Principle #5 and #7. Provides professional, terminal-width-aware help text formatting with full support for options, groups, and descriptions. Core Components: - HelpFormatter: Immutable help text generator - Integration with ImmutableParser via formatHelp() - Builder pattern for customization - Terminal width auto-detection Features: - Usage line formatting with %prog substitution - Description and epilog support - Option formatting with alignment and wrapping - Option groups with descriptions - Default value display (e.g., "(default: 8080)") - Choice list display (e.g., "(choices: 'json', 'xml', 'csv')") - Metavar support for argument placeholders - Short/long option ordering control - Text wrapping for terminal width - Configurable indentation Formatter Configuration: - width: Terminal width (0 = auto-detect, default: 80) - indent: Indentation increment (default: 2) - maxHelpPosition: Column before wrapping help text (default: 24) - shortFirst: Show short option first in listings (default: true) Formatting Rules: - Options aligned at configurable column position - Help text wraps to terminal width - Default values and choices automatically appended - Groups separated with headers - Consistent spacing and indentation Parser Integration: - formatHelp(): Generate help text for parser - Accepts optional custom formatter - Uses builder-provided formatter if available - Falls back to default formatter Example Usage: $formatter = HelpFormatter::create() ->withWidth(100) ->withIndent(4) ->build(); $parser = ParserBuilder::create() ->withUsage('%prog [options] <file>') ->withDescription('Process files') ->addOption($verboseOption) ->build(); echo $parser->formatHelp($formatter); Output Example: Usage: myapp [options] <file> Process files Options: -v, --verbose Increase verbosity -p, --port=PORT Server port (default: 8080) -f, --format=FORMAT Output format (choices: 'json', 'xml', 'csv') (default: json) Test Coverage: - 28 tests added (234 total Modern API tests) - 73 assertions added (403 total assertions) - All tests passing (100%) - Tests cover all formatting scenarios - Integration tests with ImmutableParser Formatter Characteristics: - Readonly class (Principle #7) - Immutable builder pattern (Principle #5) - No side effects, pure output generation - Thread-safe and cacheable - Fast formatting (no reflection) Text Wrapping Algorithm: - Word-based wrapping (no mid-word breaks) - Respects indentation for wrapped lines - Handles long option strings gracefully - Falls back to next-line help for long options Terminal Width Detection: - Auto-detects via tput cols if available - Falls back to 80 columns default - Can be explicitly set via withWidth() - Zero width disables wrapping Principles Demonstrated: - Principle #5: Modern API is Immutable - formatter fully immutable - Principle #7: Immutable Throughout - readonly class, builder for config - Principle #6: Explicit Access - clear method calls, no magic - Clean separation between formatting logic and parser logic Next Steps: Modern API is now feature-complete with parsing and help formatting
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implement complete Modern API for Horde/Argv following 8 guiding principles established in design analysis. This provides a fully immutable, type-safe, and explicit API for command-line argument parsing.
Implementation
Week 1: Enums and Config Objects
OptionAction,OptionType,ConflictHandlerParserConfig,OptionConfig,OptionGroupConfigWeek 2: Result Objects, Exceptions, and Validators
ParseResult,OptionValuesValidatorsutility with 12 composable validatorsWeek 3: Immutable Builders
OptionBuilder: 14 setters, 3 convenience methodsGroupBuilder: Group managementParserBuilder: Complete parser configurationWeek 4: ImmutableParser (Core Engine)
toBuilder()Features
Type Safety:
Immutability:
Validation:
Error Handling:
Test Coverage
Example Usage
Design Principles
All 8 guiding principles fully implemented and tested:
Performance
Breaking Changes
None - Modern API is in separate namespace (
Horde\Argv\Modern\). Legacy API remains unchanged and stable.Documentation
Next Steps
Modern API core is complete and production-ready. Future enhancements could include:
Checklist