xref: /plugin/statistics/vendor/matomo/device-detector/ClientHints.php (revision d5ef99ddb7dfb0cfae33e9257bd1d788f682c50f)
1*d5ef99ddSAndreas Gohr<?php
2*d5ef99ddSAndreas Gohr
3*d5ef99ddSAndreas Gohr/**
4*d5ef99ddSAndreas Gohr * Device Detector - The Universal Device Detection library for parsing User Agents
5*d5ef99ddSAndreas Gohr *
6*d5ef99ddSAndreas Gohr * @link https://matomo.org
7*d5ef99ddSAndreas Gohr *
8*d5ef99ddSAndreas Gohr * @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
9*d5ef99ddSAndreas Gohr */
10*d5ef99ddSAndreas Gohr
11*d5ef99ddSAndreas Gohrdeclare(strict_types=1);
12*d5ef99ddSAndreas Gohr
13*d5ef99ddSAndreas Gohrnamespace DeviceDetector;
14*d5ef99ddSAndreas Gohr
15*d5ef99ddSAndreas Gohrclass ClientHints
16*d5ef99ddSAndreas Gohr{
17*d5ef99ddSAndreas Gohr    /**
18*d5ef99ddSAndreas Gohr     * Represents `Sec-CH-UA-Arch` header field: The underlying architecture's instruction set
19*d5ef99ddSAndreas Gohr     *
20*d5ef99ddSAndreas Gohr     * @var string
21*d5ef99ddSAndreas Gohr     */
22*d5ef99ddSAndreas Gohr    protected $architecture = '';
23*d5ef99ddSAndreas Gohr
24*d5ef99ddSAndreas Gohr    /**
25*d5ef99ddSAndreas Gohr     * Represents `Sec-CH-UA-Bitness` header field: The underlying architecture's bitness
26*d5ef99ddSAndreas Gohr     *
27*d5ef99ddSAndreas Gohr     * @var string
28*d5ef99ddSAndreas Gohr     */
29*d5ef99ddSAndreas Gohr    protected $bitness = '';
30*d5ef99ddSAndreas Gohr
31*d5ef99ddSAndreas Gohr    /**
32*d5ef99ddSAndreas Gohr     * Represents `Sec-CH-UA-Mobile` header field: whether the user agent should receive a specifically "mobile" UX
33*d5ef99ddSAndreas Gohr     *
34*d5ef99ddSAndreas Gohr     * @var bool
35*d5ef99ddSAndreas Gohr     */
36*d5ef99ddSAndreas Gohr    protected $mobile = false;
37*d5ef99ddSAndreas Gohr
38*d5ef99ddSAndreas Gohr    /**
39*d5ef99ddSAndreas Gohr     * Represents `Sec-CH-UA-Model` header field: the user agent's underlying device model
40*d5ef99ddSAndreas Gohr     *
41*d5ef99ddSAndreas Gohr     * @var string
42*d5ef99ddSAndreas Gohr     */
43*d5ef99ddSAndreas Gohr    protected $model = '';
44*d5ef99ddSAndreas Gohr
45*d5ef99ddSAndreas Gohr    /**
46*d5ef99ddSAndreas Gohr     * Represents `Sec-CH-UA-Platform` header field: the platform's brand
47*d5ef99ddSAndreas Gohr     *
48*d5ef99ddSAndreas Gohr     * @var string
49*d5ef99ddSAndreas Gohr     */
50*d5ef99ddSAndreas Gohr    protected $platform = '';
51*d5ef99ddSAndreas Gohr
52*d5ef99ddSAndreas Gohr    /**
53*d5ef99ddSAndreas Gohr     * Represents `Sec-CH-UA-Platform-Version` header field: the platform's major version
54*d5ef99ddSAndreas Gohr     *
55*d5ef99ddSAndreas Gohr     * @var string
56*d5ef99ddSAndreas Gohr     */
57*d5ef99ddSAndreas Gohr    protected $platformVersion = '';
58*d5ef99ddSAndreas Gohr
59*d5ef99ddSAndreas Gohr    /**
60*d5ef99ddSAndreas Gohr     * Represents `Sec-CH-UA-Full-Version` header field: the platform's major version
61*d5ef99ddSAndreas Gohr     *
62*d5ef99ddSAndreas Gohr     * @var string
63*d5ef99ddSAndreas Gohr     */
64*d5ef99ddSAndreas Gohr    protected $uaFullVersion = '';
65*d5ef99ddSAndreas Gohr
66*d5ef99ddSAndreas Gohr    /**
67*d5ef99ddSAndreas Gohr     * Represents `Sec-CH-UA-Full-Version-List` header field: the full version for each brand in its brand list
68*d5ef99ddSAndreas Gohr     *
69*d5ef99ddSAndreas Gohr     * @var array
70*d5ef99ddSAndreas Gohr     */
71*d5ef99ddSAndreas Gohr    protected $fullVersionList = [];
72*d5ef99ddSAndreas Gohr
73*d5ef99ddSAndreas Gohr    /**
74*d5ef99ddSAndreas Gohr     * Represents `x-requested-with` header field: Android app id
75*d5ef99ddSAndreas Gohr     * @var string
76*d5ef99ddSAndreas Gohr     */
77*d5ef99ddSAndreas Gohr    protected $app = '';
78*d5ef99ddSAndreas Gohr
79*d5ef99ddSAndreas Gohr    /**
80*d5ef99ddSAndreas Gohr     * Represents `Sec-CH-UA-Form-Factors` header field: form factor device type name
81*d5ef99ddSAndreas Gohr     *
82*d5ef99ddSAndreas Gohr     * @var array
83*d5ef99ddSAndreas Gohr     */
84*d5ef99ddSAndreas Gohr    protected $formFactors = [];
85*d5ef99ddSAndreas Gohr
86*d5ef99ddSAndreas Gohr    /**
87*d5ef99ddSAndreas Gohr     * Constructor
88*d5ef99ddSAndreas Gohr     *
89*d5ef99ddSAndreas Gohr     * @param string $model           `Sec-CH-UA-Model` header field
90*d5ef99ddSAndreas Gohr     * @param string $platform        `Sec-CH-UA-Platform` header field
91*d5ef99ddSAndreas Gohr     * @param string $platformVersion `Sec-CH-UA-Platform-Version` header field
92*d5ef99ddSAndreas Gohr     * @param string $uaFullVersion   `Sec-CH-UA-Full-Version` header field
93*d5ef99ddSAndreas Gohr     * @param array  $fullVersionList `Sec-CH-UA-Full-Version-List` header field
94*d5ef99ddSAndreas Gohr     * @param bool   $mobile          `Sec-CH-UA-Mobile` header field
95*d5ef99ddSAndreas Gohr     * @param string $architecture    `Sec-CH-UA-Arch` header field
96*d5ef99ddSAndreas Gohr     * @param string $bitness         `Sec-CH-UA-Bitness`
97*d5ef99ddSAndreas Gohr     * @param string $app             `HTTP_X-REQUESTED-WITH`
98*d5ef99ddSAndreas Gohr     * @param array  $formFactors     `Sec-CH-UA-Form-Factors` header field
99*d5ef99ddSAndreas Gohr     */
100*d5ef99ddSAndreas Gohr    public function __construct(string $model = '', string $platform = '', string $platformVersion = '', string $uaFullVersion = '', array $fullVersionList = [], bool $mobile = false, string $architecture = '', string $bitness = '', string $app = '', array $formFactors = []) // phpcs:ignore Generic.Files.LineLength
101*d5ef99ddSAndreas Gohr    {
102*d5ef99ddSAndreas Gohr        $this->model           = $model;
103*d5ef99ddSAndreas Gohr        $this->platform        = $platform;
104*d5ef99ddSAndreas Gohr        $this->platformVersion = $platformVersion;
105*d5ef99ddSAndreas Gohr        $this->uaFullVersion   = $uaFullVersion;
106*d5ef99ddSAndreas Gohr        $this->fullVersionList = $fullVersionList;
107*d5ef99ddSAndreas Gohr        $this->mobile          = $mobile;
108*d5ef99ddSAndreas Gohr        $this->architecture    = $architecture;
109*d5ef99ddSAndreas Gohr        $this->bitness         = $bitness;
110*d5ef99ddSAndreas Gohr        $this->app             = $app;
111*d5ef99ddSAndreas Gohr        $this->formFactors     = $formFactors;
112*d5ef99ddSAndreas Gohr    }
113*d5ef99ddSAndreas Gohr
114*d5ef99ddSAndreas Gohr    /**
115*d5ef99ddSAndreas Gohr     * Magic method to directly allow accessing the protected properties
116*d5ef99ddSAndreas Gohr     *
117*d5ef99ddSAndreas Gohr     * @param string $variable
118*d5ef99ddSAndreas Gohr     *
119*d5ef99ddSAndreas Gohr     * @return mixed
120*d5ef99ddSAndreas Gohr     *
121*d5ef99ddSAndreas Gohr     * @throws \Exception
122*d5ef99ddSAndreas Gohr     */
123*d5ef99ddSAndreas Gohr    public function __get(string $variable)
124*d5ef99ddSAndreas Gohr    {
125*d5ef99ddSAndreas Gohr        if (\property_exists($this, $variable)) {
126*d5ef99ddSAndreas Gohr            return $this->$variable;
127*d5ef99ddSAndreas Gohr        }
128*d5ef99ddSAndreas Gohr
129*d5ef99ddSAndreas Gohr        throw new \Exception('Invalid ClientHint property requested.');
130*d5ef99ddSAndreas Gohr    }
131*d5ef99ddSAndreas Gohr
132*d5ef99ddSAndreas Gohr    /**
133*d5ef99ddSAndreas Gohr     * Returns if the client hints
134*d5ef99ddSAndreas Gohr     *
135*d5ef99ddSAndreas Gohr     * @return bool
136*d5ef99ddSAndreas Gohr     */
137*d5ef99ddSAndreas Gohr    public function isMobile(): bool
138*d5ef99ddSAndreas Gohr    {
139*d5ef99ddSAndreas Gohr        return $this->mobile;
140*d5ef99ddSAndreas Gohr    }
141*d5ef99ddSAndreas Gohr
142*d5ef99ddSAndreas Gohr    /**
143*d5ef99ddSAndreas Gohr     * Returns the Architecture
144*d5ef99ddSAndreas Gohr     *
145*d5ef99ddSAndreas Gohr     * @return string
146*d5ef99ddSAndreas Gohr     */
147*d5ef99ddSAndreas Gohr    public function getArchitecture(): string
148*d5ef99ddSAndreas Gohr    {
149*d5ef99ddSAndreas Gohr        return $this->architecture;
150*d5ef99ddSAndreas Gohr    }
151*d5ef99ddSAndreas Gohr
152*d5ef99ddSAndreas Gohr    /**
153*d5ef99ddSAndreas Gohr     * Returns the Bitness
154*d5ef99ddSAndreas Gohr     *
155*d5ef99ddSAndreas Gohr     * @return string
156*d5ef99ddSAndreas Gohr     */
157*d5ef99ddSAndreas Gohr    public function getBitness(): string
158*d5ef99ddSAndreas Gohr    {
159*d5ef99ddSAndreas Gohr        return $this->bitness;
160*d5ef99ddSAndreas Gohr    }
161*d5ef99ddSAndreas Gohr
162*d5ef99ddSAndreas Gohr    /**
163*d5ef99ddSAndreas Gohr     * Returns the device model
164*d5ef99ddSAndreas Gohr     *
165*d5ef99ddSAndreas Gohr     * @return string
166*d5ef99ddSAndreas Gohr     */
167*d5ef99ddSAndreas Gohr    public function getModel(): string
168*d5ef99ddSAndreas Gohr    {
169*d5ef99ddSAndreas Gohr        return $this->model;
170*d5ef99ddSAndreas Gohr    }
171*d5ef99ddSAndreas Gohr
172*d5ef99ddSAndreas Gohr    /**
173*d5ef99ddSAndreas Gohr     * Returns the Operating System
174*d5ef99ddSAndreas Gohr     *
175*d5ef99ddSAndreas Gohr     * @return string
176*d5ef99ddSAndreas Gohr     */
177*d5ef99ddSAndreas Gohr    public function getOperatingSystem(): string
178*d5ef99ddSAndreas Gohr    {
179*d5ef99ddSAndreas Gohr        return $this->platform;
180*d5ef99ddSAndreas Gohr    }
181*d5ef99ddSAndreas Gohr
182*d5ef99ddSAndreas Gohr    /**
183*d5ef99ddSAndreas Gohr     * Returns the Operating System version
184*d5ef99ddSAndreas Gohr     *
185*d5ef99ddSAndreas Gohr     * @return string
186*d5ef99ddSAndreas Gohr     */
187*d5ef99ddSAndreas Gohr    public function getOperatingSystemVersion(): string
188*d5ef99ddSAndreas Gohr    {
189*d5ef99ddSAndreas Gohr        return $this->platformVersion;
190*d5ef99ddSAndreas Gohr    }
191*d5ef99ddSAndreas Gohr
192*d5ef99ddSAndreas Gohr    /**
193*d5ef99ddSAndreas Gohr     * Returns the Browser name
194*d5ef99ddSAndreas Gohr     *
195*d5ef99ddSAndreas Gohr     * @return array<string, string>
196*d5ef99ddSAndreas Gohr     */
197*d5ef99ddSAndreas Gohr    public function getBrandList(): array
198*d5ef99ddSAndreas Gohr    {
199*d5ef99ddSAndreas Gohr        if (\is_array($this->fullVersionList) && \count($this->fullVersionList)) {
200*d5ef99ddSAndreas Gohr            $brands   = \array_column($this->fullVersionList, 'brand');
201*d5ef99ddSAndreas Gohr            $versions = \array_column($this->fullVersionList, 'version');
202*d5ef99ddSAndreas Gohr
203*d5ef99ddSAndreas Gohr            if (\count($brands) === \count($versions)) {
204*d5ef99ddSAndreas Gohr                // @phpstan-ignore-next-line
205*d5ef99ddSAndreas Gohr                return \array_combine($brands, $versions);
206*d5ef99ddSAndreas Gohr            }
207*d5ef99ddSAndreas Gohr        }
208*d5ef99ddSAndreas Gohr
209*d5ef99ddSAndreas Gohr        return [];
210*d5ef99ddSAndreas Gohr    }
211*d5ef99ddSAndreas Gohr
212*d5ef99ddSAndreas Gohr    /**
213*d5ef99ddSAndreas Gohr     * Returns the Browser version
214*d5ef99ddSAndreas Gohr     *
215*d5ef99ddSAndreas Gohr     * @return string
216*d5ef99ddSAndreas Gohr     */
217*d5ef99ddSAndreas Gohr    public function getBrandVersion(): string
218*d5ef99ddSAndreas Gohr    {
219*d5ef99ddSAndreas Gohr        if (!empty($this->uaFullVersion)) {
220*d5ef99ddSAndreas Gohr            return $this->uaFullVersion;
221*d5ef99ddSAndreas Gohr        }
222*d5ef99ddSAndreas Gohr
223*d5ef99ddSAndreas Gohr        return '';
224*d5ef99ddSAndreas Gohr    }
225*d5ef99ddSAndreas Gohr
226*d5ef99ddSAndreas Gohr    /**
227*d5ef99ddSAndreas Gohr     * Returns the Android app id
228*d5ef99ddSAndreas Gohr     *
229*d5ef99ddSAndreas Gohr     * @return string
230*d5ef99ddSAndreas Gohr     */
231*d5ef99ddSAndreas Gohr    public function getApp(): string
232*d5ef99ddSAndreas Gohr    {
233*d5ef99ddSAndreas Gohr        return $this->app;
234*d5ef99ddSAndreas Gohr    }
235*d5ef99ddSAndreas Gohr
236*d5ef99ddSAndreas Gohr    /**
237*d5ef99ddSAndreas Gohr     * Returns the formFactor device type name
238*d5ef99ddSAndreas Gohr     *
239*d5ef99ddSAndreas Gohr     * @return array
240*d5ef99ddSAndreas Gohr     */
241*d5ef99ddSAndreas Gohr    public function getFormFactors(): array
242*d5ef99ddSAndreas Gohr    {
243*d5ef99ddSAndreas Gohr        return $this->formFactors;
244*d5ef99ddSAndreas Gohr    }
245*d5ef99ddSAndreas Gohr
246*d5ef99ddSAndreas Gohr    /**
247*d5ef99ddSAndreas Gohr     * Factory method to easily instantiate this class using an array containing all available (client hint) headers
248*d5ef99ddSAndreas Gohr     *
249*d5ef99ddSAndreas Gohr     * @param array $headers
250*d5ef99ddSAndreas Gohr     *
251*d5ef99ddSAndreas Gohr     * @return ClientHints
252*d5ef99ddSAndreas Gohr     */
253*d5ef99ddSAndreas Gohr    public static function factory(array $headers): ClientHints
254*d5ef99ddSAndreas Gohr    {
255*d5ef99ddSAndreas Gohr        $model           = $platform = $platformVersion = $uaFullVersion = $architecture = $bitness = '';
256*d5ef99ddSAndreas Gohr        $app             = '';
257*d5ef99ddSAndreas Gohr        $mobile          = false;
258*d5ef99ddSAndreas Gohr        $fullVersionList = [];
259*d5ef99ddSAndreas Gohr        $formFactors     = [];
260*d5ef99ddSAndreas Gohr
261*d5ef99ddSAndreas Gohr        foreach ($headers as $name => $value) {
262*d5ef99ddSAndreas Gohr            if (empty($value)) {
263*d5ef99ddSAndreas Gohr                continue;
264*d5ef99ddSAndreas Gohr            }
265*d5ef99ddSAndreas Gohr
266*d5ef99ddSAndreas Gohr            switch (\str_replace('_', '-', \strtolower((string) $name))) {
267*d5ef99ddSAndreas Gohr                case 'http-sec-ch-ua-arch':
268*d5ef99ddSAndreas Gohr                case 'sec-ch-ua-arch':
269*d5ef99ddSAndreas Gohr                case 'arch':
270*d5ef99ddSAndreas Gohr                case 'architecture':
271*d5ef99ddSAndreas Gohr                    $architecture = \trim($value, '"');
272*d5ef99ddSAndreas Gohr
273*d5ef99ddSAndreas Gohr                    break;
274*d5ef99ddSAndreas Gohr                case 'http-sec-ch-ua-bitness':
275*d5ef99ddSAndreas Gohr                case 'sec-ch-ua-bitness':
276*d5ef99ddSAndreas Gohr                case 'bitness':
277*d5ef99ddSAndreas Gohr                    $bitness = \trim($value, '"');
278*d5ef99ddSAndreas Gohr
279*d5ef99ddSAndreas Gohr                    break;
280*d5ef99ddSAndreas Gohr                case 'http-sec-ch-ua-mobile':
281*d5ef99ddSAndreas Gohr                case 'sec-ch-ua-mobile':
282*d5ef99ddSAndreas Gohr                case 'mobile':
283*d5ef99ddSAndreas Gohr                    $mobile = true === $value || '1' === $value || '?1' === $value;
284*d5ef99ddSAndreas Gohr
285*d5ef99ddSAndreas Gohr                    break;
286*d5ef99ddSAndreas Gohr                case 'http-sec-ch-ua-model':
287*d5ef99ddSAndreas Gohr                case 'sec-ch-ua-model':
288*d5ef99ddSAndreas Gohr                case 'model':
289*d5ef99ddSAndreas Gohr                    $model = \trim($value, '"');
290*d5ef99ddSAndreas Gohr
291*d5ef99ddSAndreas Gohr                    break;
292*d5ef99ddSAndreas Gohr                case 'http-sec-ch-ua-full-version':
293*d5ef99ddSAndreas Gohr                case 'sec-ch-ua-full-version':
294*d5ef99ddSAndreas Gohr                case 'uafullversion':
295*d5ef99ddSAndreas Gohr                    $uaFullVersion = \trim($value, '"');
296*d5ef99ddSAndreas Gohr
297*d5ef99ddSAndreas Gohr                    break;
298*d5ef99ddSAndreas Gohr                case 'http-sec-ch-ua-platform':
299*d5ef99ddSAndreas Gohr                case 'sec-ch-ua-platform':
300*d5ef99ddSAndreas Gohr                case 'platform':
301*d5ef99ddSAndreas Gohr                    $platform = \trim($value, '"');
302*d5ef99ddSAndreas Gohr
303*d5ef99ddSAndreas Gohr                    break;
304*d5ef99ddSAndreas Gohr                case 'http-sec-ch-ua-platform-version':
305*d5ef99ddSAndreas Gohr                case 'sec-ch-ua-platform-version':
306*d5ef99ddSAndreas Gohr                case 'platformversion':
307*d5ef99ddSAndreas Gohr                    $platformVersion = \trim($value, '"');
308*d5ef99ddSAndreas Gohr
309*d5ef99ddSAndreas Gohr                    break;
310*d5ef99ddSAndreas Gohr                case 'brands':
311*d5ef99ddSAndreas Gohr                    if (!empty($fullVersionList)) {
312*d5ef99ddSAndreas Gohr                        break;
313*d5ef99ddSAndreas Gohr                    }
314*d5ef99ddSAndreas Gohr                    // use this only if no other header already set the list
315*d5ef99ddSAndreas Gohr                case 'fullversionlist':
316*d5ef99ddSAndreas Gohr                    $fullVersionList = \is_array($value) ? $value : $fullVersionList;
317*d5ef99ddSAndreas Gohr
318*d5ef99ddSAndreas Gohr                    break;
319*d5ef99ddSAndreas Gohr                case 'http-sec-ch-ua':
320*d5ef99ddSAndreas Gohr                case 'sec-ch-ua':
321*d5ef99ddSAndreas Gohr                    if (!empty($fullVersionList)) {
322*d5ef99ddSAndreas Gohr                        break;
323*d5ef99ddSAndreas Gohr                    }
324*d5ef99ddSAndreas Gohr                    // use this only if no other header already set the list
325*d5ef99ddSAndreas Gohr                case 'http-sec-ch-ua-full-version-list':
326*d5ef99ddSAndreas Gohr                case 'sec-ch-ua-full-version-list':
327*d5ef99ddSAndreas Gohr                    $reg  = '/^"([^"]+)"; ?v="([^"]+)"(?:, )?/';
328*d5ef99ddSAndreas Gohr                    $list = [];
329*d5ef99ddSAndreas Gohr
330*d5ef99ddSAndreas Gohr                    while (\preg_match($reg, $value, $matches)) {
331*d5ef99ddSAndreas Gohr                        $list[] = ['brand' => $matches[1], 'version' => $matches[2]];
332*d5ef99ddSAndreas Gohr                        $value  = \substr($value, \strlen($matches[0]));
333*d5ef99ddSAndreas Gohr                    }
334*d5ef99ddSAndreas Gohr
335*d5ef99ddSAndreas Gohr                    if (\count($list)) {
336*d5ef99ddSAndreas Gohr                        $fullVersionList = $list;
337*d5ef99ddSAndreas Gohr                    }
338*d5ef99ddSAndreas Gohr
339*d5ef99ddSAndreas Gohr                    break;
340*d5ef99ddSAndreas Gohr                case 'http-x-requested-with':
341*d5ef99ddSAndreas Gohr                case 'x-requested-with':
342*d5ef99ddSAndreas Gohr                    if ('xmlhttprequest' !== \strtolower($value)) {
343*d5ef99ddSAndreas Gohr                        $app = $value;
344*d5ef99ddSAndreas Gohr                    }
345*d5ef99ddSAndreas Gohr
346*d5ef99ddSAndreas Gohr                    break;
347*d5ef99ddSAndreas Gohr                case 'formfactors':
348*d5ef99ddSAndreas Gohr                case 'http-sec-ch-ua-form-factors':
349*d5ef99ddSAndreas Gohr                case 'sec-ch-ua-form-factors':
350*d5ef99ddSAndreas Gohr                    if (\is_array($value)) {
351*d5ef99ddSAndreas Gohr                        $formFactors = \array_map('\strtolower', $value);
352*d5ef99ddSAndreas Gohr                    } elseif (\preg_match_all('~"([a-z]+)"~i', \strtolower($value), $matches)) {
353*d5ef99ddSAndreas Gohr                        $formFactors = $matches[1];
354*d5ef99ddSAndreas Gohr                    }
355*d5ef99ddSAndreas Gohr
356*d5ef99ddSAndreas Gohr                    break;
357*d5ef99ddSAndreas Gohr            }
358*d5ef99ddSAndreas Gohr        }
359*d5ef99ddSAndreas Gohr
360*d5ef99ddSAndreas Gohr        return new self(
361*d5ef99ddSAndreas Gohr            $model,
362*d5ef99ddSAndreas Gohr            $platform,
363*d5ef99ddSAndreas Gohr            $platformVersion,
364*d5ef99ddSAndreas Gohr            $uaFullVersion,
365*d5ef99ddSAndreas Gohr            $fullVersionList,
366*d5ef99ddSAndreas Gohr            $mobile,
367*d5ef99ddSAndreas Gohr            $architecture,
368*d5ef99ddSAndreas Gohr            $bitness,
369*d5ef99ddSAndreas Gohr            $app,
370*d5ef99ddSAndreas Gohr            $formFactors
371*d5ef99ddSAndreas Gohr        );
372*d5ef99ddSAndreas Gohr    }
373*d5ef99ddSAndreas Gohr}
374