Completed
Push — ezp_31107 ( 816f32...9177e0 )
by
unknown
220:54 queued 209:22
created

MaskGenerator::generateLanguageMaskForFields()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 3
dl 0
loc 19
rs 9.6333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
namespace eZ\Publish\Core\Persistence\Legacy\Content\Language;
8
9
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
10
use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler;
11
12
/**
13
 * Language MaskGenerator.
14
 */
15
class MaskGenerator
16
{
17
    /**
18
     * Language lookup.
19
     *
20
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler
21
     */
22
    protected $languageHandler;
23
24
    /**
25
     * Creates a new Language MaskGenerator.
26
     *
27
     * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $languageHandler
28
     */
29
    public function __construct(LanguageHandler $languageHandler)
30
    {
31
        $this->languageHandler = $languageHandler;
0 ignored issues
show
Documentation Bug introduced by
$languageHandler is of type object<eZ\Publish\SPI\Pe...ntent\Language\Handler>, but the property $languageHandler was declared to be of type object<eZ\Publish\Core\P...ntent\Language\Handler>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
32
    }
33
34
    /**
35
     * Generates a language mask from the keys of $languages.
36
     *
37
     * @deprecated Move towards using {@see generateLanguageMaskFromLanguageCodes()} or the other generate* methods.
38
     *
39
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language(s) in $languageCodes was not be found
40
     *
41
     * @param array $languages
42
     *
43
     * @return int
44
     */
45
    public function generateLanguageMask(array $languages)
46
    {
47
        $mask = 0;
48
        if (isset($languages['always-available'])) {
49
            $mask |= $languages['always-available'] ? 1 : 0;
50
            unset($languages['always-available']);
51
        }
52
53
        $languageCodes = array_keys($languages);
54
        $languageList = $this->languageHandler->loadListByLanguageCodes($languageCodes);
55
        foreach ($languageList as $language) {
56
            $mask |= $language->id;
57
        }
58
59 View Code Duplication
        if ($missing = array_diff($languageCodes, array_keys($languageList))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
60
            throw new NotFoundException('Language', implode(', ', $missing));
61
        }
62
63
        return $mask;
64
    }
65
66
    /**
67
     * Generates a language mask from pre-loaded Language Ids.
68
     *
69
     * @param int[] $languageIds
70
     * @param bool $alwaysAvailable
71
     *
72
     * @return int
73
     */
74
    public function generateLanguageMaskFromLanguageIds(array $languageIds, $alwaysAvailable): int
75
    {
76
        // make sure alwaysAvailable part of bit mask always results in 1 or 0
77
        $languageMask = $alwaysAvailable ? 1 : 0;
78
79
        foreach ($languageIds as $languageId) {
80
            $languageMask |= $languageId;
81
        }
82
83
        return $languageMask;
84
    }
85
86
    /**
87
     * Generates a language indicator from $languageCode and $alwaysAvailable.
88
     *
89
     * @param string $languageCode
90
     * @param bool $alwaysAvailable
91
     *
92
     * @return int
93
     *
94
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
95
     */
96
    public function generateLanguageIndicator($languageCode, $alwaysAvailable)
97
    {
98
        return $this->languageHandler->loadByLanguageCode($languageCode)->id | ($alwaysAvailable ? 1 : 0);
99
    }
100
101
    /**
102
     * Checks if $language is always available in $languages;.
103
     *
104
     * @param string $language
105
     * @param array $languages
106
     *
107
     * @return bool
108
     */
109
    public function isLanguageAlwaysAvailable($language, array $languages): bool
110
    {
111
        return isset($languages['always-available'])
112
           && ($languages['always-available'] == $language)
113
        ;
114
    }
115
116
    /**
117
     * Checks if $languageMask contains the alwaysAvailable bit field.
118
     *
119
     * @param int $languageMask
120
     *
121
     * @return bool
122
     */
123
    public function isAlwaysAvailable($languageMask): bool
124
    {
125
        return (bool)($languageMask & 1);
126
    }
127
128
    /**
129
     * Removes the alwaysAvailable flag from $languageId and returns cleaned up $languageId.
130
     *
131
     * @param int $languageId
132
     *
133
     * @return int
134
     */
135
    public function removeAlwaysAvailableFlag($languageId): int
136
    {
137
        return $languageId & ~1;
138
    }
139
140
    /**
141
     * Extracts every language Ids contained in $languageMask.
142
     *
143
     * @param int $languageMask
144
     *
145
     * @return array Array of language Id
146
     */
147
    public function extractLanguageIdsFromMask($languageMask): array
148
    {
149
        $exp = 2;
150
        $result = [];
151
152
        // Decomposition of $languageMask into its binary components.
153
        while ($exp <= $languageMask) {
154
            if ($languageMask & $exp) {
155
                $result[] = $exp;
156
            }
157
158
            $exp *= 2;
159
        }
160
161
        return $result;
162
    }
163
164
    /**
165
     * Extracts Language codes contained in given $languageMask.
166
     *
167
     * @param int $languageMask
168
     *
169
     * @return array
170
     */
171
    public function extractLanguageCodesFromMask($languageMask): array
172
    {
173
        $languageCodes = [];
174
        $languageList = $this->languageHandler->loadList(
175
            $this->extractLanguageIdsFromMask($languageMask)
176
        );
177
        foreach ($languageList as $language) {
178
            $languageCodes[] = $language->languageCode;
179
        }
180
181
        return $languageCodes;
182
    }
183
184
    /**
185
     * Checks if given $languageMask consists of multiple languages.
186
     *
187
     * @param int $languageMask
188
     *
189
     * @return bool
190
     */
191
    public function isLanguageMaskComposite($languageMask): bool
192
    {
193
        // Ignore first bit
194
        $languageMask = $this->removeAlwaysAvailableFlag($languageMask);
195
196
        // Special case
197
        if ($languageMask === 0) {
198
            return false;
199
        }
200
201
        // Return false if power of 2
202
        return (bool)($languageMask & ($languageMask - 1));
203
    }
204
205
    /**
206
     * Generates a language mask from plain array of language codes and always available flag.
207
     *
208
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language(s) in $languageCodes was not be found
209
     *
210
     * @param string[] $languageCodes
211
     * @param bool $isAlwaysAvailable
212
     *
213
     * @return int
214
     */
215
    public function generateLanguageMaskFromLanguageCodes(array $languageCodes, bool $isAlwaysAvailable = false): int
216
    {
217
        $mask = $isAlwaysAvailable ? 1 : 0;
218
219
        $languageList = $this->languageHandler->loadListByLanguageCodes($languageCodes);
220
        foreach ($languageList as $language) {
221
            $mask |= $language->id;
222
        }
223
224 View Code Duplication
        if ($missing = array_diff($languageCodes, array_keys($languageList))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
225
            throw new NotFoundException('Language', implode(', ', $missing));
226
        }
227
228
        return $mask;
229
    }
230
231
    /**
232
     * Collect all translations of the given Persistence Fields and generate language mask.
233
     *
234
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $fields
235
     *
236
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
237
     */
238
    public function generateLanguageMaskForFields(
239
        array $fields,
240
        string $initialLanguageCode,
241
        bool $isAlwaysAvailable
242
    ): int {
243
        $languages = [$initialLanguageCode => true];
244
        foreach ($fields as $field) {
245
            if (isset($languages[$field->languageCode])) {
246
                continue;
247
            }
248
249
            $languages[$field->languageCode] = true;
250
        }
251
252
        return $this->generateLanguageMaskFromLanguageCodes(
253
            array_keys($languages),
254
            $isAlwaysAvailable
255
        );
256
    }
257
}
258