Passed
Branch fix-lack-of-untranslated-when-... (eeef8d)
by Domenico
04:05
created

Xliff20::tagOpen()   F

Complexity

Conditions 28
Paths 4620

Size

Total Lines 115
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 28
eloc 42
nc 4620
nop 3
dl 0
loc 115
rs 0
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * @author hashashiyyin [email protected] / [email protected]
5
 * Date: 02/08/24
6
 * Time: 17:51
7
 *
8
 */
9
10
namespace Matecat\XliffParser\XliffReplacer;
11
12
use Matecat\XliffParser\Utils\Strings;
13
14
class Xliff20 extends AbstractXliffReplacer {
15
16
    /**
17
     * @var int
18
     */
19
    private int $mdaGroupCounter = 0;
20
21
    /**
22
     * @var array
23
     */
24
    private array $nodesToBuffer = [
25
            'source',
26
            'mda:metadata',
27
            'memsource:additionalTagData',
28
            'originalData',
29
            'note'
30
    ];
31
32
    /**
33
     * @inheritDoc
34
     */
35
    protected function tagOpen( $parser, $name, $attr ) {
36
37
        $this->handleOpenUnit( $name, $attr );
38
39
        if ( 'mda:metadata' === $name ) {
40
            $this->unitContainsMda = true;
41
        }
42
43
        // check if we are entering into a <target>
44
        if ( 'target' === $name ) {
45
46
            if ( $this->currentTransUnitIsTranslatable === 'no' ) {
47
                $this->inTarget = false;
48
            } else {
49
                $this->inTarget = true;
50
            }
51
        }
52
53
        // open buffer
54
        if ( in_array( $name, $this->nodesToBuffer ) ) {
55
            $this->bufferIsActive = true;
56
        }
57
58
        // check if we are inside a <target>, obviously this happen only if there are targets inside the trans-unit
59
        // <target> must be stripped to be replaced, so this check avoids <target> reconstruction
60
        if ( !$this->inTarget ) {
61
62
            $tag = '';
63
64
            //
65
            // ============================================
66
            // only for Xliff 2.*
67
            // ============================================
68
            //
69
            // In xliff v2 we MUST add <mda:metadata> BEFORE <notes>/<originalData>/<segment>/<ignorable>
70
            //
71
            // As documentation says, <unit> contains:
72
            //
73
            // - elements from other namespaces, OPTIONAL
74
            // - Zero or one <notes> elements followed by
75
            // - Zero or one <originalData> element followed by
76
            // - One or more <segment> or <ignorable> elements in any order.
77
            //
78
            // For more info please refer to:
79
            //
80
            // http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html#unit
81
            //
82
            if (
83
                    ( $name === 'notes' || $name === 'originalData' || $name === 'segment' || $name === 'ignorable' ) &&
84
                    $this->unitContainsMda === false &&
85
                    !empty( $this->transUnits[ $this->currentTransUnitId ] ) &&
86
                    !$this->hasWrittenCounts
87
            ) {
88
                // we need to update counts here
89
                $this->updateCounts();
90
                $this->hasWrittenCounts = true;
91
                $tag                    .= $this->getWordCountGroupForXliffV2();
92
                $this->unitContainsMda  = true;
93
            }
94
95
            // construct tag
96
            $tag .= "<$name ";
97
98
            foreach ( $attr as $k => $v ) {
99
100
                //if tag name is file, we must replace the target-language attribute
101
                if ( $name === 'file' && $k === 'target-language' && !empty( $this->targetLang ) ) {
102
                    //replace Target language with job language provided from constructor
103
                    $tag .= "$k=\"$this->targetLang\" ";
104
                } else {
105
106
                    //normal tag flux, put attributes in it but skip for translation state and set the right value for the attribute
107
                    if ( $k != 'state' ) {
108
                        $tag .= "$k=\"$v\" ";
109
                    }
110
111
                }
112
113
            }
114
115
            $seg = $this->getCurrentSegment();
116
117
            if ( $name === $this->tuTagName and !empty( $seg ) and isset( $seg[ 'sid' ] ) ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
118
119
                // add `help-id` to xliff v.1*
120
                // add `mtc:segment-id` to xliff v.2*
121
                if ( strpos( $tag, 'mtc:segment-id' ) === false ) {
122
                    $tag .= "mtc:segment-id=\"{$seg[ 'sid' ]}\" ";
123
                }
124
125
            }
126
127
            // replace state for xliff v2
128
            if ( 'segment' === $name ) { // add state to segment in Xliff v2
129
                [ $stateProp, ] = StatusToStateAttribute::getState( $seg[ 'status' ], $this->xliffVersion );
130
                $tag .= $stateProp;
131
            }
132
133
134
            // add oasis xliff 20 namespace
135
            if ( $name === 'xliff' && !array_key_exists( 'xmlns:mda', $attr ) ) {
136
                $tag .= 'xmlns:mda="urn:oasis:names:tc:xliff:metadata:2.0"';
137
            }
138
139
            // add MateCat specific namespace, we want maybe add non-XLIFF attributes
140
            if ( $name === 'xliff' && !array_key_exists( 'xmlns:mtc', $attr ) ) {
141
                $tag .= ' xmlns:mtc="https://www.matecat.com" ';
142
            }
143
144
            // trgLang
145
            if ( $name === 'xliff' ) {
146
                $tag = preg_replace( '/trgLang="(.*?)"/', 'trgLang="' . $this->targetLang . '"', $tag );
147
            }
148
149
            $this->checkForSelfClosedTagAndFlush( $parser, $tag );
150
151
        }
152
153
    }
154
155
    /**
156
     * @inheritDoc
157
     */
158
    protected function tagClose( $parser, $name ) {
159
        $tag = '';
160
161
        /**
162
         * if is a tag within <target> or
163
         * if it is an empty tag, do not add closing tag because we have already closed it in
164
         *
165
         * self::tagOpen method
166
         */
167
        if ( !$this->isEmpty ) {
168
169
            if ( !$this->inTarget ) {
170
                $tag = "</$name>";
171
            }
172
173
            if ( 'target' == $name ) {
174
175
                if ( isset( $this->transUnits[ $this->currentTransUnitId ] ) && $this->currentTransUnitIsTranslatable !== 'no' ) {
176
177
                    $seg = $this->getCurrentSegment();
178
179
                    // update counts
180
                    if ( !$this->hasWrittenCounts && !empty( $seg ) ) {
181
                        $this->updateSegmentCounts( $seg );
182
                    }
183
184
                    // delete translations so the prepareSegment
185
                    // will put source content in target tag
186
                    if ( $this->sourceInTarget ) {
187
                        $seg[ 'translation' ] = '';
188
                        $this->resetCounts();
189
                    }
190
191
                    // append $translation
192
                    $translation = $this->prepareTranslation( $seg );
193
194
                    //append translation
195
                    $tag = "<target>$translation</target>";
196
197
                }
198
199
                // signal we are leaving a target
200
                $this->targetWasWritten = true;
201
                $this->inTarget         = false;
202
                $this->postProcAndFlush( $this->outputFP, $tag, true );
203
            } elseif ( in_array( $name, $this->nodesToBuffer ) ) { // we are closing a critical CDATA section
204
205
                $this->bufferIsActive = false;
206
207
                // only for Xliff 2.*
208
                // write here <mda:metaGroup> and <mda:meta> if already present in the <unit>
209
                if ( 'mda:metadata' === $name && $this->unitContainsMda && !$this->hasWrittenCounts ) {
210
211
                    // we need to update counts here
212
                    $this->updateCounts();
213
                    $this->hasWrittenCounts = true;
214
215
                    $tag = $this->CDATABuffer;
216
                    $tag .= $this->getWordCountGroupForXliffV2( false );
217
                    $tag .= "    </mda:metadata>";
218
219
                } else {
220
                    $tag = $this->CDATABuffer . "</$name>";
221
                }
222
223
                $this->CDATABuffer = "";
224
225
                //flush to the pointer
226
                $this->postProcAndFlush( $this->outputFP, $tag );
227
228
            } elseif ( 'segment' === $name ) {
229
230
                // only for Xliff 2.*
231
                // if segment has no <target> add it BEFORE </segment>
232
                if ( !$this->targetWasWritten ) {
233
234
                    $seg = $this->getCurrentSegment();
235
236
                    if ( isset( $seg[ 'translation' ] ) ) {
237
238
                        $translation = $this->prepareTranslation( $seg );
239
                        // replace the tag
240
                        $tag = "<target>$translation</target>";
241
242
                        $tag .= '</segment>';
243
244
                    }
245
246
                }
247
248
                // update segmentPositionInTu
249
                $this->segmentInUnitPosition++;
250
251
                $this->postProcAndFlush( $this->outputFP, $tag );
252
253
                // we are leaving <segment>, reset $segmentHasTarget
254
                $this->targetWasWritten = false;
255
256
            } elseif ( $this->bufferIsActive ) { // this is a tag ( <g | <mrk ) inside a seg or seg-source tag
257
                $this->CDATABuffer .= "</$name>";
258
                // Do NOT Flush
259
            } else { //generic tag closure do Nothing
260
                // flush to pointer
261
                $this->postProcAndFlush( $this->outputFP, $tag );
262
            }
263
        } else {
264
            //ok, nothing to be done; reset flag for next coming tag
265
            $this->isEmpty = false;
266
        }
267
268
        // check if we are leaving a <trans-unit> (xliff v1.*) or <unit> (xliff v2.*)
269
        if ( $this->tuTagName === $name ) {
270
            $this->currentTransUnitIsTranslatable = null;
271
            $this->inTU                           = false;
272
            $this->segmentPositionInTu            = -1;
273
            $this->unitContainsMda                = false;
274
            $this->hasWrittenCounts               = false;
275
276
            $this->resetCounts();
277
        }
278
    }
279
280
    /**
281
     * Update counts
282
     */
283
    private function updateCounts() {
284
285
        $seg = $this->getCurrentSegment();
286
        if ( !empty( $seg ) ) {
287
            $this->updateSegmentCounts( $seg );
288
        }
289
290
    }
291
292
    /**
293
     * @param bool $withMetadataTag
294
     *
295
     * @return string
296
     */
297
    private function getWordCountGroupForXliffV2( bool $withMetadataTag = true ): string {
298
299
        $this->mdaGroupCounter++;
300
        $segments_count_array = $this->counts[ 'segments_count_array' ];
301
302
        $tag = '';
303
304
        if ( $withMetadataTag === true ) {
305
            $tag .= '<mda:metadata>';
306
        }
307
308
        $index = 0;
309
        foreach ( $segments_count_array as $segments_count_item ) {
310
311
            $id = 'word_count_tu[' . $this->currentTransUnitId . '][' . $index . ']';
312
            $index++;
313
314
            $tag .= "    <mda:metaGroup id=\"" . $id . "\" category=\"row_xml_attribute\">
315
                                <mda:meta type=\"x-matecat-raw\">" . $segments_count_item[ 'raw_word_count' ] . "</mda:meta>
316
                                <mda:meta type=\"x-matecat-weighted\">" . $segments_count_item[ 'eq_word_count' ] . "</mda:meta>
317
                            </mda:metaGroup>";
318
        }
319
320
        if ( $withMetadataTag === true ) {
321
            $tag .= '</mda:metadata>';
322
        }
323
324
        return $tag;
325
326
    }
327
328
    /**
329
     * prepare segment tagging for xliff insertion
330
     *
331
     * @param array $seg
332
     *
333
     * @return string
334
     */
335
    protected function prepareTranslation( array $seg ): string {
336
337
        $segment     = Strings::removeDangerousChars( $seg [ 'segment' ] );
338
        $translation = Strings::removeDangerousChars( $seg [ 'translation' ] );
339
        $dataRefMap  = ( isset( $seg[ 'data_ref_map' ] ) ) ? Strings::jsonToArray( $seg[ 'data_ref_map' ] ) : [];
340
341
        if ( $seg [ 'translation' ] == '' ) {
342
            $translation = $segment;
343
        } else {
344
            if ( $this->callback instanceof XliffReplacerCallbackInterface ) {
345
                $error = ( !empty( $seg[ 'error' ] ) ) ? $seg[ 'error' ] : null;
346
                if ( $this->callback->thereAreErrors( $seg[ 'sid' ], $segment, $translation, $dataRefMap, $error ) ) {
347
                    $translation = '|||UNTRANSLATED_CONTENT_START|||' . $segment . '|||UNTRANSLATED_CONTENT_END|||';
348
                }
349
            }
350
        }
351
352
        return $translation;
353
354
    }
355
356
}