Skip to content

feat: implement complete Modern API for Horde/Argv#8

Open
ralflang wants to merge 1 commit intoFRAMEWORK_6_0from
feat/modern-api-complete
Open

feat: implement complete Modern API for Horde/Argv#8
ralflang wants to merge 1 commit intoFRAMEWORK_6_0from
feat/modern-api-complete

Conversation

@ralflang
Copy link
Member

@ralflang ralflang commented Mar 4, 2026

Summary

Complete implementation of Modern API for Horde/Argv following 8 guiding principles established through design analysis. This provides a fully immutable, type-safe, and explicit API for command-line argument parsing in PHP 8.2+.

What's Included

Week 1: Enums and Config Objects

  • 3 enums: OptionAction, OptionType, ConflictHandler
  • 3 config classes: ParserConfig, OptionConfig, OptionGroupConfig
  • 2 exception classes: InvalidOptionException, ValueValidationException
  • Enums as behavior objects with logic encapsulation
  • Readonly classes throughout

Week 2: Result Objects, Exceptions, and Validators

  • Result objects: ParseResult, OptionValues
  • 3 additional exceptions: AmbiguousOptionException, MissingValueException, ConflictingOptionException
  • Validators utility with 12 composable validators
  • Optional infrastructure (not mandated)
  • Explicit access methods (no ArrayAccess)

Week 3: Immutable Builders

  • OptionBuilder: 14 setters, 3 convenience methods (flag, repeatable, counter)
  • GroupBuilder: Group management with title and description
  • ParserBuilder: Complete parser configuration
  • Clone-on-modify pattern (~4μs overhead)
  • Builder branching support for shared base configurations

Week 4: ImmutableParser (Core Engine)

  • Full POSIX-style option parsing
  • Short options: -v, -abc (bundling), -nVALUE (attached)
  • Long options: --verbose, --name VALUE, --name=VALUE
  • Partial long option matching with ambiguity detection
  • Type conversion: String, Int, Float
  • Actions: Store, StoreTrue/False, Append, Count, Callback, StoreConst
  • Custom validators and transformers
  • Choice enforcement
  • Unknown option handling (error/allow/ignore)
  • Use-and-amend pattern via toBuilder()

Week 5: Help Formatting System

  • HelpFormatter: Immutable help text generator
  • Terminal-width-aware text wrapping
  • Auto-detection of terminal width
  • Option alignment with configurable column position
  • Automatic default value and choices display
  • Option groups with headers and descriptions
  • Customizable indentation and layout

Test Coverage

✓ 234 tests (100% passing)
✓ 403 assertions
✓ Comprehensive coverage of all features
✓ All edge cases and error conditions tested

Test Breakdown

  • Week 1: 47 tests (enums, configs, exceptions)
  • Week 2: 39 tests (results, validators)
  • Week 3: 58 tests (builders, immutability)
  • Week 4: 33 tests (parser, parsing scenarios)
  • Week 5: 28 tests (help formatting)
  • Integration: 29 tests (end-to-end scenarios)

Design Principles

All 8 guiding principles fully implemented and verified:

  1. Type Safety Over Convenience - Enums instead of strings
  2. Enums as Behavior Objects - Logic encapsulated in enum methods
  3. Infrastructure Not Mandated - Validators/transformers optional
  4. Explicit Type Contracts - Type specified = type guaranteed
  5. Modern API is Immutable - No mutation after construction
  6. Explicit Access - No ArrayAccess, explicit getter methods
  7. Immutable Throughout - Readonly classes, builder pattern
  8. Layered Evolution - Modern namespace, stable legacy preserved

Example Usage

use Horde\Argv\Modern\Builder\{ParserBuilder, OptionBuilder, GroupBuilder};
use Horde\Argv\Modern\Enum\{OptionType, OptionAction};
use Horde\Argv\Modern\Validator\Validators;

// Build options
$verbose = OptionBuilder::create()
    ->short('-v')
    ->long('--verbose')
    ->counter()
    ->help('Increase verbosity (-v, -vv, -vvv)')
    ->build();

$port = OptionBuilder::create()
    ->short('-p')
    ->long('--port')
    ->type(OptionType::Int)
    ->default(8080)
    ->validator(Validators::range(1, 65535))
    ->help('Server port number')
    ->build();

$format = OptionBuilder::create()
    ->short('-f')
    ->long('--format')
    ->choices(['json', 'xml', 'csv'])
    ->default('json')
    ->help('Output format')
    ->build();

// Build debugging group
$debugGroup = GroupBuilder::create('Debugging Options')
    ->withDescription('Options for troubleshooting')
    ->addOption(
        OptionBuilder::create()
            ->short('-d')
            ->long('--debug')
            ->flag()
            ->help('Enable debug logging')
            ->build()
    )
    ->build();

// Build parser
$parser = ParserBuilder::create()
    ->withUsage('%prog [options] <input> <output>')
    ->withProg('dataconverter')
    ->withDescription('Convert data files between formats')
    ->withVersion('1.0.0')
    ->withEpilog('For more information, visit https://example.com/docs')
    ->addOption($verbose)
    ->addOption($port)
    ->addOption($format)
    ->addGroup($debugGroup)
    ->build();

// Parse arguments
$result = $parser->parse(['-vv', '--port=3000', '--format', 'xml', 'in.json', 'out.xml']);

// Access results
echo $result->options->get('verbosity');  // 2
echo $result->options->get('port');       // 3000
echo $result->options->get('format');     // "xml"
echo $result->arguments[0];               // "in.json"
echo $result->arguments[1];               // "out.xml"

// Generate help
echo $parser->formatHelp();

Help Output

Usage: dataconverter [options] <input> <output>

Convert data files between formats

Options:
  -v, --verbose         Increase verbosity (-v, -vv, -vvv)
  -p, --port=PORT       Server port number (default: 8080)
  -f, --format=FORMAT   Output format (choices: 'json', 'xml', 'csv')
                        (default: json)

Debugging Options:
  Options for troubleshooting
  -d, --debug           Enable debug logging

For more information, visit https://example.com/docs

Features

Parsing Capabilities

  • POSIX-style short and long options
  • Option bundling (-abc = -a -b -c)
  • Attached values (-nVALUE or --name=VALUE)
  • Partial long option matching (--verb matches --verbose)
  • Ambiguity detection for partial matches
  • Double-dash (--) option terminator
  • Configurable argument interspersing
  • Unknown option handling modes

Type System

  • Built-in types: String, Int, Float
  • Automatic type conversion
  • Type validation before conversion
  • Custom validators (applied after type conversion)
  • Transformers/mappers (applied after validation)
  • Choice whitelists with validation

Actions

  • Store - Store single value
  • StoreTrue/StoreFalse - Boolean flags
  • Append - Collect multiple values
  • Count - Increment counter (e.g., -vvv = 3)
  • StoreConst/AppendConst - Store predefined constants
  • Callback - Custom processing function

Validation & Transformation

  • 12 built-in validators: range, regex, choice, length, email, url, file, directory
  • Composable validators: all(), any()
  • Custom validators (return true or error string)
  • Transformers for value processing
  • Pipeline: parse → type convert → validate → transform

Help Generation

  • Professional formatting
  • Terminal-width-aware wrapping
  • Automatic default value display
  • Automatic choices display
  • Metavar support
  • Option groups with descriptions
  • Configurable layout and indentation

Error Handling

  • Structured exceptions with context
  • Clear error messages
  • Exception types:
    • InvalidOptionException - Unknown/invalid options
    • AmbiguousOptionException - Partial match ambiguity
    • MissingValueException - Required value not provided
    • ConflictingOptionException - Duplicate options
    • ValueValidationException - Type conversion/validation failures

Architecture

Immutability

  • All classes readonly
  • Builders clone on each method call
  • Parsers configured once, never modified
  • Results are immutable snapshots

Performance

  • Option lookup: O(1) via computed maps
  • Builder cloning: ~4μs per call (negligible)
  • No reflection during parsing
  • Readonly class optimizations
  • Efficient text wrapping algorithm

Design Patterns

  • Builder pattern for construction
  • Enum-based behavior
  • Use-and-amend pattern (toBuilder())
  • Strategy pattern for actions
  • Factory methods for validators

Breaking Changes

None - Modern API is in separate namespace:

  • Modern API: Horde\Argv\Modern\
  • Legacy API: Horde_Argv_* (unchanged)

Both APIs can coexist. Legacy API remains stable and supported.

Migration Path

Users can adopt Modern API gradually:

  1. Keep existing code using legacy API
  2. New code uses Modern API
  3. Migrate modules one at a time
  4. No forced migration required

Documentation

  • Comprehensive PHPDoc on all public methods
  • Clear type declarations throughout
  • Example usage in class headers
  • 234 test cases serve as documentation
  • Design principles documented

Commits

  1. chore: add test autoload configuration and upgrade guide
  2. docs: mark under-development stub classes as internal
  3. feat(modern): implement Week 1 - Enums and Config objects
  4. feat(modern): implement Week 2 - Result objects, exceptions, and validators
  5. test(modern): add comprehensive unit tests for Weeks 1-2
  6. feat(Modern): add immutable builders (Week 3)
  7. feat(Modern): implement ImmutableParser with full parsing engine (Week 4)
  8. feat(Modern): implement help formatting system (Week 5)

Checklist

  • All tests passing (234/234)
  • 100% type coverage
  • PHPDoc complete
  • Conventional commits
  • No breaking changes
  • Legacy API untouched
  • All principles implemented
  • Performance validated
  • Immutability verified
  • Help formatting working

Future Enhancements

Possible future additions (not in this PR):

  • Shell completion generation
  • Subcommand parsing
  • Additional validators
  • Color output support
  • Man page generation

Status

Modern API is production-ready and feature-complete for command-line argument parsing in PHP 8.2+ applications!

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
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