Passed
Pull Request — master (#51)
by Domenico
02:05
created

AbstractFilter::fromLayer0ToLayer1()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 10
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
3
/**
4
 * This file contains the abstract base class for all filter implementations.
5
 * It provides a foundational structure for transforming string data between different
6
 * "layers" of representation, such as raw database content, server-to-server communication
7
 * formats, and UI-ready strings.
8
 */
9
10
namespace Matecat\SubFiltering;
11
12
use Exception;
13
use Matecat\SubFiltering\Commons\Pipeline;
14
use Matecat\SubFiltering\Contracts\FeatureSetInterface;
15
use Matecat\SubFiltering\Filters\EncodeToRawXML;
16
use Matecat\SubFiltering\Filters\EquivTextToBase64;
17
use Matecat\SubFiltering\Filters\LtGtDecode;
18
use Matecat\SubFiltering\Filters\LtGtEncode;
19
use Matecat\SubFiltering\Filters\MateCatCustomPHToOriginalValue;
20
use Matecat\SubFiltering\Filters\PlaceHoldXliffTags;
21
use Matecat\SubFiltering\Filters\RestoreEquivText;
22
use Matecat\SubFiltering\Filters\RestorePlaceHoldersToXLIFFLtGt;
23
use Matecat\SubFiltering\Filters\RestoreXliffTagsContent;
24
use Matecat\SubFiltering\Filters\SplitPlaceholder;
25
use Matecat\SubFiltering\Filters\StandardPHToMateCatCustomPH;
26
use Matecat\SubFiltering\Filters\StandardXEquivTextToMateCatCustomPH;
27
28
/**
29
 * Provides a blueprint for creating specific filter implementations.
30
 *
31
 * This abstract class defines the core structure and functionality for transforming
32
 * string data between different logical layers. It manages a set of features,
33
 * source/target languages, and transformation pipelines. Subclasses must implement
34
 * the specific transformation logic required for their context.
35
 *
36
 * The class uses a factory method `getInstance` to create and configure filter instances,
37
 * which are composed of a `Pipeline` of `AbstractHandler`s.
38
 */
39
abstract class AbstractFilter {
40
41
    /**
42
     * @var FeatureSetInterface
43
     * The set of features to be applied during the filtering process.
44
     */
45
    protected FeatureSetInterface $featureSet;
46
47
    /**
48
     * @var string|null
49
     * The source language of the segment.
50
     */
51
    protected ?string $source;
52
53
    /**
54
     * @var string|null
55
     * The target language of the segment.
56
     */
57
    protected ?string $target;
58
59
    /**
60
     * @var array
61
     * A map used for replacing data references within the segment.
62
     */
63
    protected array $dataRefMap = [];
64
65
    /**
66
     * The processing pipeline for transforming content from Layer 0 (e.g., database)
67
     * to Layer 1 (e.g., for MT/TM servers).
68
     *
69
     * This pipeline holds the sequence of filter handlers that are applied to
70
     * convert the raw segment into its sub-filtered representation. It is typically
71
     * configured once and reused for multiple transformations.
72
     *
73
     * @var Pipeline
74
     */
75
    protected Pipeline $fromLayer0ToLayer1Pipeline;
76
77
    /**
78
     * Factory method to create and configure a new instance of the filter.
79
     *
80
     * This method instantiates a new filter object and configures it with the provided
81
     * feature set, source/target languages, data-ref map, and a list of handlers for
82
     * the Layer 0 to Layer 1 transition.
83
     *
84
     * The handler list follows specific rules:
85
     * - An empty array (default) populates the filter with all default handlers from HandlersSorter.
86
     * - `null` clears the handler list, meaning no handlers will be used.
87
     * - A specific array of class names will be used as the handler list.
88
     *
89
     * @param FeatureSetInterface $featureSet                                   The feature set to apply.
90
     * @param string|null         $source                                       The source language code (e.g., 'en-US').
91
     * @param string|null         $target                                       The target language code (e.g., 'it-IT').
92
     * @param array|null          $dataRefMap                                   A map for data-ref transformations, or null for an empty map.
93
     * @param array|null          $handlerClassNamesForLayer0ToLayer1Transition A list of handler classes, an empty array for defaults, or null for none.
94
     *
95
     * @return AbstractFilter The configured instance of the filter.
96
     */
97 94
    public static function getInstance( FeatureSetInterface $featureSet, ?string $source = null, ?string $target = null, ?array $dataRefMap = [], ?array $handlerClassNamesForLayer0ToLayer1Transition = [] ): ?AbstractFilter {
98
        // Create a new instance of the specific filter class (e.g., MateCatFilter).
99 94
        $newInstance = new static();
100
101
        // Configure the instance with the provided settings via direct property access.
102 94
        $newInstance->featureSet = $featureSet;
103 94
        $newInstance->source     = $source;
104 94
        $newInstance->target     = $target;
105
        // Use the null coalescing operator to default to an empty array if $dataRefMap is null.
106 94
        $newInstance->dataRefMap = $dataRefMap ?? [];
107
108
        // Determine which handlers to use for the Layer 0 to Layer 1 transition.
109 94
        if ( is_array( $handlerClassNamesForLayer0ToLayer1Transition ) && empty( $handlerClassNamesForLayer0ToLayer1Transition ) ) {
110
            // If an empty array is passed, load the default set of handlers from the sorter.
111 92
            $handlerClassNamesForLayer0ToLayer1Transition = array_keys( HandlersSorter::injectableHandlersOrder );
112 2
        } elseif ( is_null( $handlerClassNamesForLayer0ToLayer1Transition ) ) {
113
            // If null is passed, use no handlers.
114 1
            $handlerClassNamesForLayer0ToLayer1Transition = [];
115
        }
116
        // Otherwise, use the custom list of handlers provided.
117
118
        // Create and configure the processing pipeline for the Layer 0 to Layer 1 transformation.
119 94
        $newInstance->createFromLayer0ToLayer1Pipeline( $source, $target, $dataRefMap ?? [], $handlerClassNamesForLayer0ToLayer1Transition );
120
121
        // Return the fully configured filter instance.
122 94
        return $newInstance;
123
    }
124
125
    /**
126
     * Transforms a segment from Layer 1 (server-to-server format) back to Layer 0 (database raw XML).
127
     *
128
     * This method defines the standard pipeline for reverting sub-filtered content,
129
     * restoring placeholders, and re-encoding XML entities to make it safe for database storage.
130
     *
131
     * @param string $segment The segment in Layer 1 format.
132
     *
133
     * @return string The transformed segment in Layer 0 format.
134
     * @throws Exception If any handler in the pipeline fails.
135
     */
136 81
    public function fromLayer1ToLayer0( string $segment ): string {
137
        // Initialize a new pipeline for this transformation.
138 81
        $channel = new Pipeline( $this->source, $this->target, $this->dataRefMap );
139
140
        // Add handlers to reverse the sub-filtering process.
141 81
        $channel->addLast( MateCatCustomPHToOriginalValue::class ); // Restore original PH values
142 81
        $channel->addLast( PlaceHoldXliffTags::class );             // Isolate XLIFF tags
143 81
        $channel->addLast( EncodeToRawXML::class );                 // Encode for raw XML storage
144 81
        $channel->addLast( LtGtEncode::class );                     // Encode '<' and '>'
145 81
        $channel->addLast( RestoreXliffTagsContent::class );        // Restore original XLIFF content
146 81
        $channel->addLast( RestorePlaceHoldersToXLIFFLtGt::class ); // Restore placeholders for '<' and '>'
147 81
        $channel->addLast( SplitPlaceholder::class );               // Handle split placeholders
148 81
        $channel->addLast( RestoreEquivText::class );               // Restore equiv-text content
149
150
        // Allow the current feature set to modify the pipeline (e.g., add or remove handlers).
151
        /** @var $channel Pipeline */
152 81
        $channel = $this->featureSet->filter( 'fromLayer1ToLayer0', $channel );
153
154
        // Process the segment through the pipeline and return the result.
155 81
        return $channel->transform( $segment );
156
    }
157
158
    /**
159
     * Transforms a segment from Layer 0 (database raw XML) to Layer 1 (sub-filtered format).
160
     *
161
     * This method uses the pre-configured pipeline to process the segment. It also allows
162
     * the feature set to apply any final modifications to the pipeline before transformation.
163
     *
164
     * @param string      $segment The segment in Layer 0 format.
165
     * @param string|null $cid     An optional client/context identifier for further customization.
166
     *
167
     * @return string The transformed segment in Layer 1 format.
168
     * @throws Exception If any handler in the pipeline fails.
169
     */
170 86
    public function fromLayer0ToLayer1( string $segment, ?string $cid = null ): string {
0 ignored issues
show
Unused Code introduced by
The parameter $cid is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

170
    public function fromLayer0ToLayer1( string $segment, /** @scrutinizer ignore-unused */ ?string $cid = null ): string {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
171
        // Retrieve the pre-built pipeline for this transformation.
172 86
        $channel = $this->getFromLayer0ToLayer1Pipeline();
173
174
        // Allow the feature set to modify the pipeline for this specific transformation.
175
        /** @var $channel Pipeline */
176 86
        $channel = $this->featureSet->filter( 'fromLayer0ToLayer1', $channel );
177
178
        // Process the segment and return the result.
179 86
        return $channel->transform( $segment );
180
    }
181
182
    /**
183
     * Returns the configured pipeline for transforming content from Layer 0 to Layer 1.
184
     *
185
     * This pipeline is used to process segments as they are transformed from their raw
186
     * database representation (Layer 0) to a sub-filtered format suitable for server-to-server
187
     * communications (Layer 1). The pipeline consists of a series of handlers that perform
188
     * specific transformations on the segment data.
189
     *
190
     * @return Pipeline The pipeline configured for Layer 0 to Layer 1 transformations.
191
     */
192 89
    public function getFromLayer0ToLayer1Pipeline(): Pipeline {
193 89
        return $this->fromLayer0ToLayer1Pipeline;
194
    }
195
196
    /**
197
     * Creates and configures the pipeline for transforming content from Layer 0 to Layer 1.
198
     *
199
     * This is the default configuration method of MateCatFilter for setting up the pipeline that processes segments.
200
     * MyMemoryFilter or override this method to customize the pipeline as needed.
201
     *
202
     * This method builds the default pipeline for the Layer 0 to Layer 1 transformation.
203
     * It adds a series of standard handlers and then incorporates any custom handlers,
204
     * ensuring they are correctly ordered via `HandlersSorter`.
205
     *
206
     * @param string|null $source                                       The source language code.
207
     * @param string|null $target                                       The target language code.
208
     * @param array       $dataRefMap                                   A map for data-ref transformations.
209
     * @param array       $handlerClassNamesForLayer0ToLayer1Transition A list of handler classes to include in the pipeline.
210
     */
211 94
    protected function createFromLayer0ToLayer1Pipeline( ?string $source, ?string $target, array $dataRefMap, array $handlerClassNamesForLayer0ToLayer1Transition ) {
212
        // Initialize the pipeline with language and data-ref context.
213 94
        $this->fromLayer0ToLayer1Pipeline = new Pipeline( $source, $target, $dataRefMap );
214
215
        // Add initial handlers for standard XLIFF and placeholder normalization.
216 94
        $this->fromLayer0ToLayer1Pipeline->addLast( StandardPHToMateCatCustomPH::class );
217 94
        $this->fromLayer0ToLayer1Pipeline->addLast( StandardXEquivTextToMateCatCustomPH::class );
218 94
        $this->fromLayer0ToLayer1Pipeline->addLast( PlaceHoldXliffTags::class );
219 94
        $this->fromLayer0ToLayer1Pipeline->addLast( LtGtDecode::class );
220
221
        // Sort and add the dynamic feature-based handlers.
222 94
        $sorter = new HandlersSorter( $handlerClassNamesForLayer0ToLayer1Transition );
223 94
        foreach ( $sorter->getOrderedHandlersClassNames() as $handler ) {
224 93
            $this->fromLayer0ToLayer1Pipeline->addLast( $handler );
225
        }
226
227
        // Add final handlers to restore XLIFF content and encode for the target layer.
228 94
        $this->fromLayer0ToLayer1Pipeline->addLast( RestoreXliffTagsContent::class );
229 94
        $this->fromLayer0ToLayer1Pipeline->addLast( RestorePlaceHoldersToXLIFFLtGt::class );
230 94
        $this->fromLayer0ToLayer1Pipeline->addLast( EquivTextToBase64::class );
231
    }
232
}