DataSaverTypeRegistry
in package
Central registry and handler management system for DataSaver output format types
DataSaverTypeRegistry serves as the pluggable format system backbone for the DataSaver framework, providing registration, validation, and retrieval of format handlers. It ensures built-in types (json, csv, txt) are always available while supporting unlimited custom format registration for specialized data serialization needs.
Registry Architecture
The registry operates as a static service class that:
- Manages Format Handlers: Stores callable functions that serialize array data to files
- Ensures Built-in Availability: Automatically registers json, csv, txt handlers on first use
- Supports Custom Types: Allows registration of unlimited custom format handlers
- Provides Validation: Checks type availability before DataSaver operations execute
- Enables Extensibility: Plugin-like system for adding new output formats
Handler Function Interface
All registered handlers must follow this signature:
function(string $filename, array $data, bool $append): bool
Where:
- $filename: Target file path for output
- $data: Array data to serialize and write
- $append: Whether to append to existing file or overwrite
- Return: Boolean success/failure status
Built-in Format Types
The registry automatically provides these handlers on first use:
JSON Handler
- Format: Pretty-printed JSON with proper escaping
- Append Mode: Recursively merges with existing JSON data structure
- Data Structure: Any valid PHP array that json_encode() can handle
- File Extension: Typically .json
- Use Cases: API responses, configuration files, structured data export
CSV Handler
- Format: Standard comma-separated values with fputcsv()
- Append Mode: Adds new rows to existing file without header duplication
- Data Structure: Array of arrays (rows), or array of scalars (single row)
- File Extension: Typically .csv
- Use Cases: Spreadsheet export, tabular data, database dumps
TXT Handler
- Format: Plain text with one array element per line
- Append Mode: Adds new lines to existing file content
- Data Structure: Array elements joined with commas for nested arrays
- File Extension: Typically .txt or .log
- Use Cases: Log files, simple lists, human-readable output
Registration System
Custom handlers are registered using the static register() method:
DataSaverTypeRegistry::register('xml', function(string $filename, array $data, bool $append): bool {
$xml = '<?xml version="1.0"?><root>';
foreach ($data as $key => $value) {
$xml .= "<item key=\"$key\">$value</item>";
}
$xml .= '</root>';
$flags = $append ? FILE_APPEND : 0;
return file_put_contents($filename, $xml, $flags) !== false;
});
Integration with DataSaver System
The registry integrates tightly with DataSaver operations:
- Type Validation: DataSaver calls isRegistered() before operations
- Handler Retrieval: DataSaver calls get() to retrieve format handlers
- Format Execution: DataSaver passes filename, data, and append mode to handlers
- Error Handling: Invalid types cause operations to fail gracefully
Lazy Initialization Strategy
Built-in handlers use lazy initialization via the init() method:
- Called automatically on first registry access
- Prevents handler creation overhead until actually needed
- Allows built-in handler override if registered before first use
- Ensures consistent availability across different application entry points
Handler Development Guidelines
When creating custom handlers, consider:
- File Safety: Handle file operation failures gracefully
- Append Logic: Implement meaningful append behavior for your format
- Data Validation: Validate input data structure matches format requirements
- Error Handling: Return false on any failure, true only on complete success
- Performance: Optimize for typical data sizes in your use cases
- Consistency: Follow established patterns from built-in handlers
Thread Safety and State
The registry maintains global state through static properties but is designed for safe concurrent access patterns. Handler registration is typically done during application bootstrap, with retrieval occurring during request processing.
Extension Points
The registry supports several extension patterns:
- Format-Specific Options: Handlers can access external configuration
- Handler Inheritance: New handlers can wrap existing ones for enhanced functionality
- Dynamic Registration: Handlers can be registered conditionally based on runtime state
- Plugin Systems: Third-party packages can register handlers via service providers
Tags
Table of Contents
Properties
- $types : array<string, callable>
- Storage array for all registered format type handlers
Methods
- get() : callable
- Retrieve the format handler for a registered type
- isRegistered() : bool
- Check whether a format type handler is registered and available
- list() : array<string|int, string>
- Return array of all registered format type identifiers
- register() : void
- Register or override a format type handler in the registry
- init() : void
- Initialize built-in format handlers with lazy loading strategy
- array_merge_recursive_distinct() : array<string|int, mixed>
- Recursively merge arrays with override behavior, not duplication like array_merge_recursive
Properties
$types
Storage array for all registered format type handlers
protected
static array<string, callable>
$types
= []
This static property serves as the central repository for all format handlers in the DataSaver system. It stores callable functions indexed by their type identifiers, providing the core registry functionality for format type management.
Array Structure
The array structure is:
[
'json' => callable(string $filename, array $data, bool $append): bool,
'csv' => callable(string $filename, array $data, bool $append): bool,
'txt' => callable(string $filename, array $data, bool $append): bool,
// ... additional registered handlers
]
Key Management
- Normalization: All type keys are stored in lowercase for consistent lookups
- Uniqueness: Each type identifier can have only one registered handler
- Override Support: Later registrations override existing handlers with same key
- Case Insensitive: Lookups work regardless of registration or query case
Handler Function Interface
All stored callables must conform to the standardized handler signature:
- Parameter 1:
string $filename
- Target file path for output - Parameter 2:
array $data
- Array data to serialize and write - Parameter 3:
bool $append
- Whether to append to existing file or overwrite - Return:
bool
- Success (true) or failure (false) status
Built-in Handler Initialization
The array is initially empty and populated via lazy initialization:
- First registry access triggers init() method
- Built-in handlers (json, csv, txt) are registered if not already present
- Custom handlers can be registered at any time via register() method
- Handlers remain available for the lifetime of the PHP process
Thread Safety Considerations
While PHP is single-threaded per request, the static nature of this property means:
- Handler registrations persist across multiple DataSaver operations
- Registration order can matter if handlers depend on each other
- Built-in handlers are consistently available regardless of registration order
- Custom handlers should be registered during application bootstrap for predictability
Memory and Performance
- Lazy Loading: Built-in handlers only consume memory when first accessed
- Persistent Storage: Handlers remain in memory once loaded (no repeated initialization)
- Direct Access: Handler retrieval is O(1) via array key lookup
- Minimal Overhead: Only stores function references, not data or complex objects
Registry of format handlers indexed by lowercase type identifiers. Each callable must match signature: (string, array, bool): bool
Tags
Methods
get()
Retrieve the format handler for a registered type
public
static get(string $type) : callable
This method returns the callable format handler for the specified type, enabling DataSaver operations to execute the appropriate serialization logic. It serves as the primary mechanism for accessing registered format handlers and includes built-in validation to ensure only valid types are retrieved.
Retrieval Process
When called, this method:
- Ensures Initialization: Calls init() to guarantee built-in handlers are available
- Normalizes Type Key: Converts the type to lowercase for consistent lookup
- Validates Existence: Checks if handler exists, throwing exception if not found
- Returns Handler: Provides the callable function ready for immediate execution
Handler Function Interface
All returned handlers conform to the standardized signature:
callable(string $filename, array $data, bool $append): bool
This consistency enables DataSaver to call any registered handler with the same parameter set, regardless of whether it's a built-in or custom format.
Error Handling Strategy
The method uses exception-based error handling:
- Invalid Types: Throws InvalidArgumentException with descriptive message
- Immediate Failure: Exception prevents execution with non-existent handlers
- Clear Messaging: Exception message includes the requested type for debugging
- Safe Operations: Ensures DataSaver never attempts to call undefined handlers
Built-in Handler Guarantee
Built-in format handlers are guaranteed to be available:
- Automatic Registration: json, csv, txt handlers are registered via init() if needed
- Consistent Availability: Same handler returned regardless of application state
- Override Awareness: Returns current handler even if built-in has been overridden
- Reliable Fallback: Applications can always depend on built-in format availability
Case Insensitive Lookup
Type queries are case-insensitive due to key normalization:
$handler1 = DataSaverTypeRegistry::get('JSON'); // Same handler
$handler2 = DataSaverTypeRegistry::get('json'); // Same handler
$handler3 = DataSaverTypeRegistry::get('Json'); // Same handler
Integration with DataSaver System
This method is called by DataSaver components to retrieve handlers:
- DataSaverConfig::save(): Retrieves handler for resolved format type
- Handler Execution: Passes filename, data array, and append boolean to retrieved handler
- Result Processing: Uses handler return value to determine save operation success
- Error Propagation: Handler exceptions or false returns indicate operation failure
Performance Considerations
The method is optimized for frequent access:
- O(1) Retrieval: Direct array access after key normalization
- No Cloning: Returns reference to stored callable for minimal overhead
- Minimal Validation: Simple existence check with fast exception path
- Init Caching: Built-in handler creation occurs only once per process
Parameters
- $type : string
-
The format type identifier for which to retrieve the handler (case-insensitive). Must correspond to a registered type (built-in or custom).
Tags
Return values
callable —The format handler function conforming to signature: (string $filename, array $data, bool $append): bool
isRegistered()
Check whether a format type handler is registered and available
public
static isRegistered(string $type) : bool
This method provides type availability validation for the DataSaver system by checking if a format handler exists for the specified type. It's used extensively by DataSaver and DataSaverConfig to validate types before attempting save operations, enabling graceful error handling for unregistered format types.
Validation Process
When called, this method:
- Ensures Initialization: Calls init() to ensure built-in handlers are registered
- Normalizes Type Key: Converts the query type to lowercase for consistent lookup
- Checks Registry: Uses isset() to verify handler exists in $types array
- Returns Availability: Boolean indicating whether handler is available for use
Built-in Type Guarantee
This method guarantees availability of built-in types:
- Always Available: json, csv, txt types return true after any registry initialization
- Lazy Registration: Built-in handlers are registered automatically if not present
- Consistent Response: Same result regardless of when called during application lifecycle
- Override Aware: Returns true for built-in types even if they've been overridden
Case Insensitive Behavior
Type checking is case-insensitive due to normalization:
DataSaverTypeRegistry::isRegistered('JSON'); // true
DataSaverTypeRegistry::isRegistered('Json'); // true
DataSaverTypeRegistry::isRegistered('json'); // true
DataSaverTypeRegistry::isRegistered('CSV'); // true
DataSaverTypeRegistry::isRegistered('xml'); // false (unless registered)
Integration with DataSaver Operations
This method is called by DataSaver components to validate types:
- DataSaverConfig::type(): Throws InvalidArgumentException if type not registered
- DataSaverConfig::save(): Validates resolved type before handler retrieval
- Error Prevention: Prevents attempts to use non-existent format handlers
- User Experience: Enables meaningful error messages for typos or missing handlers
Performance Characteristics
The method is optimized for frequent calls:
- O(1) Lookup: Direct array key existence check via isset()
- Minimal Overhead: Simple boolean return after normalization
- Init Once: Built-in handler registration occurs only on first registry access
- No File I/O: Pure in-memory operation for fast validation
Custom Type Availability
For custom registered types:
- Immediate Availability: Returns true immediately after register() is called
- Persistent Availability: Remains true for lifetime of PHP process
- Override Detection: Returns true even if type has been overridden multiple times
- Registration Order: Available regardless of when registered relative to built-in types
Parameters
- $type : string
-
The format type identifier to check (case-insensitive). Common values: 'json', 'csv', 'txt', or any custom registered type.
Tags
Return values
bool —True if a handler is registered for this type, false otherwise. Built-in types (json, csv, txt) always return true.
list()
Return array of all registered format type identifiers
public
static list() : array<string|int, string>
This method provides a comprehensive list of all format types currently available in the registry, including both built-in types (json, csv, txt) and any custom types that have been registered. It's useful for debugging, administration interfaces, and dynamic format selection scenarios.
List Generation Process
When called, this method:
- Ensures Initialization: Calls init() to guarantee built-in handlers are registered
- Extracts Keys: Uses array_keys() to get all type identifiers from $types registry
- Returns Normalized Keys: Provides lowercase type identifiers as stored internally
- Includes All Types: Both built-in and custom registered types are included
Built-in Type Inclusion
The returned array always includes built-in types:
- Guaranteed Presence: json, csv, txt are always included after init()
- Consistent Ordering: Built-in types appear in registration order (json, csv, txt)
- Override Awareness: Built-in types listed even if overridden by custom handlers
- Standard Foundation: Provides baseline format capabilities for all applications
Custom Type Integration
Custom registered types are seamlessly integrated:
- Registration Order: Custom types appear in the order they were registered
- Mixed Listing: Built-in and custom types are listed together
- Dynamic Updates: List reflects current registry state including recent registrations
- No Distinction: No indication of which types are built-in vs custom
Key Format and Normalization
All returned keys use the normalized storage format:
- Lowercase Format: All type identifiers are returned in lowercase
- Storage Consistency: Keys match internal $types array keys exactly
- Case Normalization: Original registration case is not preserved in the list
- Consistent Lookup: Returned keys work directly with get() and isRegistered()
Use Cases and Applications
This method supports various application scenarios:
- Dynamic UI: Populate format selection dropdowns in admin interfaces
- Capability Detection: Determine available formats for feature activation
- Documentation: Generate lists of supported formats for help systems
- Validation: Check comprehensive format availability for configuration validation
- Debugging: Inspect registry state during development and troubleshooting
Performance Characteristics
The method provides efficient registry inspection:
- O(n) Complexity: Linear in number of registered types (typically small)
- Minimal Overhead: Simple array_keys() operation after init()
- No Side Effects: Read-only operation that doesn't modify registry state
- Cacheable: Results can be cached if registry is static after bootstrap
Tags
Return values
array<string|int, string> —Array of all registered type identifiers in lowercase format. Always includes built-in types: ['json', 'csv', 'txt', ...custom types...] Order reflects registration sequence with built-ins first.
register()
Register or override a format type handler in the registry
public
static register(string $type, callable(string, array<string|int, mixed>, bool): bool $callback) : void
This method adds a new format handler to the registry or replaces an existing handler for the specified type. It provides the primary mechanism for extending the DataSaver system with custom output formats beyond the built-in json, csv, and txt types.
Registration Process
When called, this method:
- Ensures Initialization: Calls init() to ensure built-in handlers are available
- Normalizes Type Key: Converts the type to lowercase for consistent storage and lookup
- Stores Handler: Places the callable in the $types registry array
- Enables Immediate Use: Makes the handler available for DataSaver operations immediately
Handler Function Requirements
The provided callable must conform to the standardized signature:
function(string $filename, array $data, bool $append): bool
Parameter Details
- $filename: Complete file path where output should be written
- $data: Array data structure to serialize according to your format
- $append: Boolean indicating whether to append to existing file (true) or overwrite (false)
- Return: Boolean indicating success (true) or failure (false)
Override Behavior
This method supports handler replacement:
- New Types: Creates new registry entries for previously unused type identifiers
- Existing Types: Replaces existing handlers, including built-in types
- Built-in Override: Can replace json, csv, txt handlers if registered after init()
- No Warning: Silent replacement - previous handlers are discarded
Type Key Management
Type identifiers are processed for consistency:
- Case Normalization: 'JSON', 'Json', 'json' all become 'json'
- Storage Key: Stored using lowercase version of provided type
- Lookup Compatibility: Enables case-insensitive lookups in get() and isRegistered()
- Original Case Preserved: Return from list() uses the normalized lowercase form
Integration with DataSaver System
Registered handlers integrate seamlessly:
- Type Validation: DataSaver calls isRegistered() to validate types before operations
- Handler Retrieval: DataSaver calls get() to retrieve handlers for execution
- Format Execution: DataSaver passes resolved filename, data array, and append mode
- Result Processing: DataSaver interprets handler return value for success/failure handling
Custom Handler Development Guidelines
When implementing custom handlers:
- File Safety: Use proper file operation error handling
- Append Logic: Implement meaningful append behavior for your format
- Data Validation: Validate that input data structure matches format requirements
- Error Handling: Return false for any failure condition, true only for complete success
- Performance: Consider typical data sizes and optimize accordingly
- Standards Compliance: Follow established standards for your output format when possible
Parameters
- $type : string
-
The format type identifier (case-insensitive). Will be normalized to lowercase for storage and lookup. Examples: 'xml', 'yaml', 'pipe', 'custom_format'
- $callback : callable(string, array<string|int, mixed>, bool): bool
-
The format handler function. Must accept filename, data array, and append boolean. Must return boolean success/failure status.
Tags
init()
Initialize built-in format handlers with lazy loading strategy
protected
static init() : void
This method ensures the three core format types (json, csv, txt) are always available by registering their handlers on first access to the registry. It uses a lazy loading approach to avoid handler creation overhead until the registry is actually used.
Lazy Initialization Strategy
The method only registers handlers that don't already exist, allowing:
- Override Support: Custom handlers registered before init() will not be overwritten
- Performance Optimization: Handler creation only occurs when registry is first accessed
- Consistent Availability: Built-in types are guaranteed available after any registry operation
- Flexible Registration Order: Custom and built-in handlers can be registered in any sequence
Built-in Handler Implementations
JSON Handler Registration
Creates a handler that:
- Outputs pretty-printed JSON with unescaped slashes for readability
- Supports append mode by merging with existing JSON file content
- Uses recursive merge to preserve nested data structures during append
- Handles non-JSON existing files gracefully by treating them as empty arrays
- Returns boolean success/failure status for error handling
CSV Handler Registration
Creates a handler that:
- Uses PHP's built-in fputcsv() for standards-compliant CSV output
- Supports append mode by opening files in append mode ('a')
- Handles both array-of-arrays (multiple rows) and scalar arrays (single row)
- Automatically wraps scalar values in arrays for consistent CSV row structure
- Properly manages file handle opening and closing with error checking
TXT Handler Registration
Creates a handler that:
- Converts array elements to lines with one element per line
- Joins nested arrays with commas for readable plain text representation
- Supports append mode using FILE_APPEND flag
- Adds trailing newline for proper text file formatting
- Handles mixed data types by converting everything to strings
Handler Function Signature
All registered handlers conform to the standard interface:
function(string $filename, array $data, bool $append): bool
Append Mode Implementations
Each built-in handler implements append mode differently:
- JSON: Reads existing file, decodes JSON, recursively merges with new data
- CSV: Opens file in append mode, adds new rows without headers
- TXT: Uses FILE_APPEND flag to add new lines to existing content
Error Handling Strategy
Built-in handlers implement consistent error handling:
- Return false for any file operation failures
- Handle missing or corrupted existing files gracefully
- Validate data structures appropriate for each format
- Use appropriate PHP flags and functions for reliable file operations
Call Frequency and Performance
This method is called automatically by all public registry methods, but uses conditional registration to avoid redundant handler creation. Multiple calls to init() have minimal performance impact due to the existence checks.
Tags
array_merge_recursive_distinct()
Recursively merge arrays with override behavior, not duplication like array_merge_recursive
private
static array_merge_recursive_distinct(array<string|int, mixed> $array1, array<string|int, mixed> $array2) : array<string|int, mixed>
This helper method provides clean array merging for JSON append operations by recursively combining nested array structures while avoiding the key duplication issues of PHP's built-in array_merge_recursive() function. It ensures that later values override earlier ones at all nesting levels.
Merge Behavior Comparison
Unlike PHP's array_merge_recursive() which creates arrays of duplicated values:
// PHP's array_merge_recursive() creates:
['key' => ['value1', 'value2']] // Duplication
// This method creates:
['key' => 'value2'] // Clean override
Recursive Processing Logic
The method processes arrays recursively:
- Scalar Override: Non-array values in $array2 replace corresponding values in $array1
- Array Merge: When both values are arrays, recursively merge their contents
- Type Conversion: Array values override scalar values, scalar values override array values
- Deep Nesting: Process continues to arbitrary nesting depths
JSON Append Use Case
This method specifically supports the JSON handler's append mode:
// Existing file: {"user": {"name": "John", "age": 30}}
// New data: {"user": {"age": 31, "city": "NYC"}}
// Result: {"user": {"name": "John", "age": 31, "city": "NYC"}}
The merge preserves existing data while allowing new data to update or extend the structure.
Algorithm Implementation
The method iterates through $array2 and for each key:
- If both values are arrays: Recursively merge them
- If either value is not an array: Override with $array2 value
- Preserves all keys from $array1 not present in $array2
Parameters
- $array1 : array<string|int, mixed>
-
The base array (typically existing file data). Values in this array are preserved unless overridden.
- $array2 : array<string|int, mixed>
-
The override array (typically new data to merge). Values in this array take precedence over $array1.
Tags
Return values
array<string|int, mixed> —The merged result with $array2 values taking precedence over $array1. Maintains the structure of both input arrays with recursive merging.