Skip to content

Migrate to shared presentation layer from horde/cli #12

Description

@ralflang

Issue: Migrate to shared presentation layer from horde/cli

Context

horde/components currently has its own presentation layer implementation:

src/
├── Output.php                    # Facade
├── Output/
│   ├── Presenter.php             # Interface
│   ├── PresenterFactory.php      # Auto-detection
│   └── Presenter/
│       ├── Classic.php
│       ├── Ci.php
│       └── Unicode.php

This was implemented first as a proof-of-concept, but should now be extracted to horde/cli for sharing across all Horde CLI tools.

Related Issues:

  • horde/cli#X - Add pluggable presentation layer (destination)
  • horde/cli_modular#X - Integrate presentation layer
  • horde/hordectl#X - Adopt presentation layer

Problem

Having presentation layer in components means:

  • Code duplication - Other tools must reimplement or copy
  • Maintenance burden - Bug fixes don't propagate automatically
  • Inconsistency - Each tool might deviate from the pattern
  • Components-specific - Coupled to components, not reusable

Proposed Migration

Phase 1: Extract to horde/cli

Move presentation layer from components to cli:

Source (components):

components/src/Output/Presenter.php
components/src/Output/PresenterFactory.php
components/src/Output/Presenter/*.php

Destination (cli):

cli/src/Output/Presenter.php
cli/src/Output/PresenterFactory.php
cli/src/Output/Presenter/*.php

Changes needed:

  • Namespace: Horde\Components\OutputHorde\Cli\Output
  • No interface changes
  • Tests move with implementation

Phase 2: Update components to use horde/cli

Once extracted, components depends on cli implementation:

composer.json:

{
    "require": {
        "horde/cli": "^3.0"  // Version with presenter support
    }
}

Output.php facade:

namespace Horde\Components;

use Horde\Cli\Output\Presenter;
use Horde\Cli\Output\PresenterFactory;  // From horde/cli now

class Output
{
    private readonly Presenter $_presenter;

    public function __construct(private readonly \Horde_Cli $_cli, $options)
    {
        // Use factory from horde/cli
        $this->_presenter = PresenterFactory::create($_cli, $options);
    }

    // ... rest unchanged
}

Phase 3: Remove Local Implementation

Delete from components:

src/Output/Presenter.php             → moved to horde/cli
src/Output/PresenterFactory.php      → moved to horde/cli
src/Output/Presenter/                → moved to horde/cli

Keep in components:

src/Output.php                       → facade (now uses horde/cli)

Backward Compatibility

For components users: Zero breaking changes

  • Components\Output facade stays
  • API unchanged
  • Presenters work identically

For code inspection:

  • Presenters now in Horde\Cli\Output namespace
  • Accessible to all Horde CLI tools

Benefits

For components:

  • Smaller codebase
  • Bug fixes from upstream (horde/cli)
  • Consistent with other tools

For Horde ecosystem:

  • Single implementation
  • All CLI tools benefit
  • Easier to contribute improvements

For maintenance:

  • One place to fix bugs
  • One place to add features (e.g., JSON presenter)
  • Consistent testing

Implementation Plan

Step 1: Coordinate with horde/cli

Wait for horde/cli#X to be implemented:

  • Presenter interface added
  • Factory with auto-detection added
  • Presenter implementations added
  • Tests added

Step 2: Update components composer.json

{
    "require": {
        "horde/cli": "^3.0"
    }
}

Step 3: Update Output.php

Change imports from local to horde/cli:

// Before
use Horde\Components\Output\Presenter;
use Horde\Components\Output\PresenterFactory;

// After
use Horde\Cli\Output\Presenter;
use Horde\Cli\Output\PresenterFactory;

Step 4: Remove Local Files

rm -rf src/Output/Presenter.php
rm -rf src/Output/PresenterFactory.php
rm -rf src/Output/Presenter/

Step 5: Update Tests

  • Update test imports
  • Ensure Output facade still works
  • Integration tests with horde/cli presenters

Step 6: Update Documentation

  • Note the migration in CHANGES/changelog
  • Update README if it mentions presenters
  • Document that presenters come from horde/cli

Testing Strategy

Before Migration:

  • Snapshot current test output
  • Document all presenter behaviors

After Migration:

  • All tests pass with horde/cli presenters
  • Output unchanged from user perspective
  • Integration tests confirm compatibility

Regression Testing:

  • Test all three presenters (Classic, CI, Unicode)
  • Test auto-detection in different environments
  • Test with --format flag
  • Test with --nocolor flag

Migration Checklist

  • horde/cli#X implemented and released
  • Update components composer.json to require horde/cli ^3.0
  • Update Output.php imports
  • Remove local Presenter files
  • Update tests
  • Update documentation
  • Verify all presenters work
  • Verify auto-detection works
  • Release new components version

Example: Before vs After

Before (Local Implementation)

components/
├── src/
│   ├── Output.php                 # Facade
│   └── Output/
│       ├── Presenter.php          # Local interface
│       ├── PresenterFactory.php   # Local factory
│       └── Presenter/
│           ├── Classic.php        # Local implementation
│           ├── Ci.php             # Local implementation
│           └── Unicode.php        # Local implementation
└── composer.json
    "require": {
        "horde/cli": "^2.0"        # Old version
    }

After (Shared Implementation)

components/
├── src/
│   └── Output.php                 # Facade (uses horde/cli)
└── composer.json
    "require": {
        "horde/cli": "^3.0"        # New version with presenters
    }

cli/
├── src/
│   └── Output/
│       ├── Presenter.php          # Interface (shared)
│       ├── PresenterFactory.php   # Factory (shared)
│       └── Presenter/
│           ├── Classic.php        # Implementation (shared)
│           ├── Ci.php             # Implementation (shared)
│           └── Unicode.php        # Implementation (shared)

Related Issues

  • horde/cli#X - Add pluggable presentation layer (prerequisite)
  • horde/cli_modular#X - Integrate presentation layer
  • horde/hordectl#X - Adopt presentation layer

Timeline

  • Q2 2026: Wait for horde/cli#X implementation
  • Q3 2026: Migrate components to use shared layer
  • Q3 2026: Remove local implementation
  • Q4 2026: Release and document migration

Success Criteria

  • ✅ components uses horde/cli presenters
  • ✅ Local presenter code removed
  • ✅ All tests pass
  • ✅ Zero breaking changes for components users
  • ✅ Other tools can now use presenters from horde/cli
  • ✅ Single source of truth for presentation logic

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions