TranscoderChain¶
Chain of Responsibility coordinator for transcoder strategies with priority-based execution.
Namespace¶
Ducks\Component\EncodingRepair\Transcoder\TranscoderChain
Description¶
TranscoderChain implements the Chain of Responsibility pattern for managing multiple transcoding strategies. It coordinates the execution of registered TranscoderInterface implementations, trying each transcoder in priority order until one successfully converts the data.
The chain automatically handles fallback logic: if a transcoder returns null (indicating it cannot handle the conversion), the next transcoder in the priority queue is tried.
Class Declaration¶
final class TranscoderChain
{
public function register(TranscoderInterface $transcoder, ?int $priority = null): void;
public function unregister(TranscoderInterface $transcoder): void;
public function transcode(string $data, string $to, string $from, array $options): ?string;
}
Features¶
- Priority-based execution: Higher priority transcoders execute first
- Automatic fallback: If one transcoder fails, the next is tried automatically
- Dynamic registration: Add or remove transcoders at runtime
- Availability checking: Skips unavailable transcoders (e.g., missing extensions)
- Type-safe: Full strict typing with comprehensive type declarations
- Uses ChainOfResponsibilityTrait: Shared priority queue management
Default Priority Order¶
When used by CharsetHelper or CharsetProcessor:
- UConverterTranscoder (priority: 100) - Fastest, requires ext-intl
- IconvTranscoder (priority: 50) - Good performance, supports transliteration
- MbStringTranscoder (priority: 10) - Universal fallback, always available
Methods¶
register()¶
Register a transcoder with optional priority override.
public function register(TranscoderInterface $transcoder, ?int $priority = null): void
Parameters:
$transcoder(TranscoderInterface): Transcoder instance to register$priority(int|null): Priority override (null = use transcoder's getPriority())
Notes:
- Higher priority values execute first
- Multiple transcoders can have the same priority
- Queue is rebuilt on next transcode() call
Example:
$chain = new TranscoderChain();
$chain->register(new UConverterTranscoder()); // Uses default priority: 100
$chain->register(new IconvTranscoder(), 150); // Override priority
unregister()¶
Unregister a transcoder from the chain.
public function unregister(TranscoderInterface $transcoder): void
Parameters:
$transcoder(TranscoderInterface): Transcoder instance to remove
Notes:
- Removes transcoder from registered handlers
- Queue is invalidated and rebuilt on next transcode() call
- Uses strict comparison (===) to match instance
Example:
$iconv = new IconvTranscoder();
$chain->register($iconv);
$chain->unregister($iconv);
transcode()¶
Execute transcoding using chain of responsibility.
public function transcode(
string $data,
string $to,
string $from,
array $options
): ?string
Parameters:
$data(string): Data to transcode$to(string): Target encoding (e.g., 'UTF-8')$from(string): Source encoding (e.g., 'ISO-8859-1')$options(array): Conversion options passed to transcoders
Returns: Transcoded string on success, or null if all transcoders failed
Options:
translit(bool): Enable transliteration (iconv)ignore(bool): Ignore invalid sequences (iconv)normalize(bool): Apply Unicode normalization
Example:
$chain = new TranscoderChain();
$chain->register(new IconvTranscoder());
$chain->register(new MbStringTranscoder());
$utf8 = $chain->transcode(
$latinData,
'UTF-8',
'ISO-8859-1',
['translit' => true]
);
if (null === $utf8) {
echo "All transcoders failed";
}
Execution Flow¶
transcode() called
↓
Rebuild priority queue
↓
For each transcoder (highest priority first):
↓
Check isAvailable()
↓ (false) → Skip to next
↓ (true)
Call transcoder->transcode()
↓ (null) → Try next
↓ (string) → Return immediately
↓
Return null (all failed)
Usage Examples¶
Basic Usage¶
use Ducks\Component\EncodingRepair\Transcoder\TranscoderChain;
use Ducks\Component\EncodingRepair\Transcoder\UConverterTranscoder;
use Ducks\Component\EncodingRepair\Transcoder\IconvTranscoder;
use Ducks\Component\EncodingRepair\Transcoder\MbStringTranscoder;
$chain = new TranscoderChain();
$chain->register(new UConverterTranscoder());
$chain->register(new IconvTranscoder());
$chain->register(new MbStringTranscoder());
$utf8 = $chain->transcode($data, 'UTF-8', 'ISO-8859-1', []);
Custom Transcoder¶
use Ducks\Component\EncodingRepair\Transcoder\TranscoderInterface;
class MyCustomTranscoder implements TranscoderInterface
{
public function transcode(string $data, string $to, string $from, ?array $options): ?string
{
if ($from === 'MY-ENCODING') {
return myCustomConversion($data, $to);
}
return null;
}
public function getPriority(): int
{
return 75;
}
public function isAvailable(): bool
{
return extension_loaded('my_extension');
}
}
$chain->register(new MyCustomTranscoder());
Priority Override¶
// Override default priority
$chain->register(new IconvTranscoder(), 150);
// Now IconvTranscoder executes before UConverterTranscoder (100)
Performance¶
- Queue Rebuild: O(n log n) where n = number of transcoders
- Transcoding: O(n) worst case, O(1) best case
- Memory: Minimal overhead with SplPriorityQueue
Tips:
- Register transcoders in order of expected success rate
- Use priority to favor faster transcoders
- Remove unused transcoders to reduce overhead
Thread Safety¶
TranscoderChain is not thread-safe. Each thread should have its own instance.
See Also¶
- TranscoderInterface - Transcoder contract
- ChainOfResponsibilityTrait - Shared chain logic
- UConverterTranscoder - UConverter implementation
- IconvTranscoder - Iconv implementation
- MbStringTranscoder - MbString implementation
- CallableTranscoder - Callable wrapper
- CharsetProcessor - Service using TranscoderChain