Completed
Push — EZP-30006 ( 77280a )
by André
20:03
created

MaskGenerator::generateLanguageMask()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the Language MaskGenerator class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\Core\Persistence\Legacy\Content\Language;
10
11
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
12
use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler;
13
14
/**
15
 * Language MaskGenerator.
16
 */
17
class MaskGenerator
18
{
19
    /**
20
     * Language lookup.
21
     *
22
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler
23
     */
24
    protected $languageHandler;
25
26
    /**
27
     * Creates a new Language MaskGenerator.
28
     *
29
     * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $languageHandler
30
     */
31
    public function __construct(LanguageHandler $languageHandler)
32
    {
33
        $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...
34
    }
35
36
    /**
37
     * Generates a language mask from the keys of $languages.
38
     *
39
     * @deprecated Move towards using {@see generateLanguageMaskFromLanguageMap()} or the other specific methods.
40
     *
41
     * @param array $languages
42
     *
43
     * @return int
44
     */
45
    public function generateLanguageMask(array $languages)
46
    {
47
        return $this->generateLanguageMaskFromLanguageMap($languages);
48
    }
49
50
    /**
51
     * Generates a language mask from pre-loaded Language Ids.
52
     *
53
     * @param int[] $languageIds
54
     * @param bool $alwaysAvailable
55
     *
56
     * @return int
57
     */
58
    public function generateLanguageMaskFromLanguageIds(array $languageIds, $alwaysAvailable): int
59
    {
60
        // make sure alwaysAvailable part of bit mask always results in 1 or 0
61
        $languageMask = $alwaysAvailable ? 1 : 0;
62
63
        foreach ($languageIds as $languageId) {
64
            $languageMask |= $languageId;
65
        }
66
67
        return $languageMask;
68
    }
69
70
    /**
71
     * Generates a language mask from the keys of $languageMap.
72
     *
73
     * Typically used for ->name values to get language mask directly from such structure.
74
     *
75
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language(s) in $languageMap was not be found
76
     *
77
     * @param array $languageMap Key values are language code, value is ignored.
78
     *              Exception if 'always-available' key with language value is set, then always available flag is added.
79
     *
80
     * @return int
81
     */
82
    public function generateLanguageMaskFromLanguageMap(array $languageMap): int
83
    {
84
        $mask = 0;
85
        if (isset($languageMap['always-available'])) {
86
            $mask |= $languageMap['always-available'] ? 1 : 0;
87
            unset($languageMap['always-available']);
88
        }
89
90
        $languageCodes = array_keys($languageMap);
91
        $languageList = $this->languageHandler->loadListByLanguageCodes($languageCodes);
92
        foreach ($languageList as $language) {
93
            $mask |= $language->id;
94
        }
95
96 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...
97
            throw new NotFoundException('Language', implode(', ', $missing));
98
        }
99
100
        return $mask;
101
    }
102
103
    /**
104
     * Generates a language mask from plain array of language codes and always available flag.
105
     *
106
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language(s) in $languageCodes was not be found
107
     *
108
     * @param string[] $languageCodes
109
     * @param bool $isAlwaysAvailable
110
     *
111
     * @return int
112
     */
113
    public function generateLanguageMaskFromLanguageCodes(array $languageCodes, bool $isAlwaysAvailable = false): int
114
    {
115
        $mask = $isAlwaysAvailable ? 1 : 0;
116
117
        $languageList = $this->languageHandler->loadListByLanguageCodes($languageCodes);
118
        foreach ($languageList as $language) {
119
            $mask |= $language->id;
120
        }
121
122 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...
123
            throw new NotFoundException('Language', implode(', ', $missing));
124
        }
125
126
127
        return $mask;
128
    }
129
130
    /**
131
     * Generates a language indicator from $languageCode and $alwaysAvailable.
132
     *
133
     * @param string $languageCode
134
     * @param bool $alwaysAvailable
135
     *
136
     * @return int
137
     *
138
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
139
     */
140
    public function generateLanguageIndicator($languageCode, $alwaysAvailable)
141
    {
142
        return $this->languageHandler->loadByLanguageCode($languageCode)->id | ($alwaysAvailable ? 1 : 0);
143
    }
144
145
    /**
146
     * Checks if $language is always available in $languages;.
147
     *
148
     * @param string $language
149
     * @param array $languages
150
     *
151
     * @return bool
152
     */
153
    public function isLanguageAlwaysAvailable($language, array $languages): bool
154
    {
155
        return isset($languages['always-available'])
156
           && ($languages['always-available'] == $language)
157
        ;
158
    }
159
160
    /**
161
     * Checks if $languageMask contains the alwaysAvailable bit field.
162
     *
163
     * @param int $languageMask
164
     *
165
     * @return bool
166
     */
167
    public function isAlwaysAvailable($languageMask): bool
168
    {
169
        return (bool)($languageMask & 1);
170
    }
171
172
    /**
173
     * Removes the alwaysAvailable flag from $languageId and returns cleaned up $languageId.
174
     *
175
     * @param int $languageId
176
     *
177
     * @return int
178
     */
179
    public function removeAlwaysAvailableFlag($languageId): int
180
    {
181
        return $languageId & ~1;
182
    }
183
184
    /**
185
     * Extracts every language Ids contained in $languageMask.
186
     *
187
     * @param int $languageMask
188
     *
189
     * @return array Array of language Id
190
     */
191 View Code Duplication
    public function extractLanguageIdsFromMask($languageMask): array
192
    {
193
        $exp = 2;
194
        $result = array();
195
196
        // Decomposition of $languageMask into its binary components.
197
        while ($exp <= $languageMask) {
198
            if ($languageMask & $exp) {
199
                $result[] = $exp;
200
            }
201
202
            $exp *= 2;
203
        }
204
205
        return $result;
206
    }
207
208
    /**
209
     * Extracts Language codes contained in given $languageMask.
210
     *
211
     * @param int $languageMask
212
     *
213
     * @return array
214
     */
215
    public function extractLanguageCodesFromMask($languageMask): array
216
    {
217
        $languageCodes = [];
218
        $languageList = $this->languageHandler->loadList(
219
            $this->extractLanguageIdsFromMask($languageMask)
220
        );
221
        foreach ($languageList as $language) {
222
            $languageCodes[] = $language->languageCode;
223
        }
224
225
        return $languageCodes;
226
    }
227
228
    /**
229
     * Checks if given $languageMask consists of multiple languages.
230
     *
231
     * @param int $languageMask
232
     *
233
     * @return bool
234
     */
235
    public function isLanguageMaskComposite($languageMask): bool
236
    {
237
        // Ignore first bit
238
        $languageMask = $this->removeAlwaysAvailableFlag($languageMask);
239
240
        // Special case
241
        if ($languageMask === 0) {
242
            return false;
243
        }
244
245
        // Return false if power of 2
246
        return (bool)($languageMask & ($languageMask - 1));
247
    }
248
}
249