-
Notifications
You must be signed in to change notification settings - Fork 25
Description
Background
Horde currently defines configuration schema in XML files (config/*.xml). This information is used to:
- Generate web-based configuration UI
- Validate configuration
- 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
- Interactive mode should provide hints about missing required attributes per driver hordectl#2 - CLI needs schema metadata for interactive configuration
- Support for configuration schema metadata PhpConfigFile#3 - PhpConfigFile needs schema support
Problem
Multiple Horde tools need access to configuration schema:
- Web UI - Generate forms with validation
- hordectl - Interactive prompts with required/optional hints
- Migration tools - Validate config during upgrades
- 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
- Type safety - PHP enforces types at runtime
- IDE support - Autocomplete, go-to-definition, refactoring
- Single source of truth - Schema and config are one
- Conditional logic - Can express complex dependencies
- Programmatic access - Reflection API for metadata
- 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 UIPhase 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
- Should schema classes be separate from config classes, or combined?
- How to handle multi-tenant / per-user configuration?
- Should we keep XML as primary source temporarily, with PHP classes generated?
- What about applications that extend base config? Inheritance vs composition?
- Performance impact of reflection-based schema inspection?
Resources
- PHP 8 Attributes: https://www.php.net/manual/en/language.attributes.php
- Symfony's approach: https://symfony.com/doc/current/configuration.html
- Laravel's approach: https://laravel.com/docs/configuration
Timeline
This is a long-term architectural change. Suggested phases:
- Research (Q2 2026) - Prototype, gather feedback
- Package (Q3 2026) - Create horde/config-schema
- Pilot (Q4 2026) - Convert one application
- Rollout (2027) - Gradual migration of all applications