Skip to content

Migrate configuration schema from XML to PHP class-based definitions #35

@ralflang

Description

@ralflang

Background

Horde currently defines configuration schema in XML files (config/*.xml). This information is used to:

  1. Generate web-based configuration UI
  2. Validate configuration
  3. Provide help text and field descriptions

However, this approach has limitations:

  • Schema information is locked in XML format
  • Difficult to use in CLI tools
  • No IDE support for autocomplete/validation
  • Manual synchronization between XML and actual config structure
  • Validation logic often duplicated in application code

Related Issues

Problem

Multiple Horde tools need access to configuration schema:

  1. Web UI - Generate forms with validation
  2. hordectl - Interactive prompts with required/optional hints
  3. Migration tools - Validate config during upgrades
  4. Documentation - Auto-generate config reference docs

Currently, each tool would need to parse XML or duplicate schema information.

Proposed Solution

Phase 1: Define Schema in PHP

Migrate from:

<configsection name="sql">
  <configstring name="phptype" required="true">
    <default>mysql</default>
    <description>Database type</description>
    <enum>mysql,pgsql,sqlite</enum>
  </configstring>
</configsection>

To PHP class with attributes:

class HordeConfig
{
    #[ConfigField(
        required: true,
        help: 'Database type',
        enum: ['mysql', 'pgsql', 'sqlite']
    )]
    public string $sql_phptype = 'mysql';
    
    #[ConfigField(
        required: true,
        help: 'Database host',
        requiredWhen: ['sql_phptype' => ['mysql', 'pgsql']]
    )]
    public ?string $sql_host = null;
}

Benefits

  1. Type safety - PHP enforces types at runtime
  2. IDE support - Autocomplete, go-to-definition, refactoring
  3. Single source of truth - Schema and config are one
  4. Conditional logic - Can express complex dependencies
  5. Programmatic access - Reflection API for metadata
  6. Validation - Built-in or via schema inspection

Phase 2: Generate XML for Backwards Compatibility

Keep existing web UI working by generating XML from PHP classes:

$schema = ConfigSchema::fromClass(HordeConfig::class);
$xml = $schema->toXml(); // For legacy web UI

Phase 3: Update Web UI

Gradually migrate web UI to use PHP schema directly instead of XML.

Implementation Strategy

Step 1: Create Schema Package

New package: horde/config-schema

  • Define attribute classes (#[ConfigField], #[ConfigSection], etc.)
  • Schema reflection API
  • XML generator for backwards compatibility

Step 2: Pilot with One Application

Convert horde/base/config/conf.xml to HordeConfig class:

  • Prove the concept
  • Identify edge cases
  • Establish patterns

Step 3: Tooling

Create migration tools:

  • XML → PHP class generator
  • Schema validator
  • Documentation generator

Step 4: Gradual Migration

  • Convert applications one by one
  • Maintain XML generation for web UI
  • Eventually deprecate XML

Technical Considerations

1. Conditional Requirements

Some fields are required only if other fields have certain values:

#[RequiredWhen(['sql_phptype' => ['mysql', 'pgsql']])]
public ?string $sql_host;

2. Dynamic Configuration

Some config is loaded from hooks/external sources. May need:

#[ConfigField(dynamic: true, loader: 'loadFromHook')]
public array $custom_fields;

3. Validation

Complex validation beyond type checking:

#[Validate('isValidDsn')]
public string $sql_dsn;

4. Backwards Compatibility

Must maintain existing config file format (conf.php) - this is about schema definition, not config storage.

Example Attribute Definitions

#[Attribute(Attribute::TARGET_PROPERTY)]
class ConfigField
{
    public function __construct(
        public ?string $help = null,
        public bool $required = false,
        public ?array $enum = null,
        public ?array $requiredWhen = null,
        public bool $secret = false, // mask in UI
        public ?string $validator = null,
        public ?string $default = null,
    ) {}
}

#[Attribute(Attribute::TARGET_CLASS)]
class ConfigSection
{
    public function __construct(
        public string $title,
        public ?string $description = null,
    ) {}
}

Questions for Discussion

  1. Should schema classes be separate from config classes, or combined?
  2. How to handle multi-tenant / per-user configuration?
  3. Should we keep XML as primary source temporarily, with PHP classes generated?
  4. What about applications that extend base config? Inheritance vs composition?
  5. Performance impact of reflection-based schema inspection?

Resources

Timeline

This is a long-term architectural change. Suggested phases:

  1. Research (Q2 2026) - Prototype, gather feedback
  2. Package (Q3 2026) - Create horde/config-schema
  3. Pilot (Q4 2026) - Convert one application
  4. Rollout (2027) - Gradual migration of all applications

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions