Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.19% covered (warning)
89.19%
33 / 37
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
language_file_helper
89.19% covered (warning)
89.19%
33 / 37
40.00% covered (danger)
40.00%
2 / 5
17.37
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_available_languages
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
2
 get_language_data_from_composer_file
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 get_language_data_from_json
95.00% covered (success)
95.00%
19 / 20
0.00% covered (danger)
0.00%
0 / 1
12
 sort_by_local_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 *
4 * This file is part of the phpBB Forum Software package.
5 *
6 * @copyright (c) phpBB Limited <https://www.phpbb.com>
7 * @license GNU General Public License, version 2 (GPL-2.0)
8 *
9 * For full copyright and license information, please see
10 * the docs/CREDITS.txt file.
11 *
12 */
13
14namespace phpbb\language;
15
16use DomainException;
17use phpbb\json\sanitizer as json_sanitizer;
18use Symfony\Component\Finder\Finder;
19
20/**
21 * Helper class for language file related functions
22 */
23class language_file_helper
24{
25    /**
26     * @var string    Path to phpBB's root
27     */
28    protected $phpbb_root_path;
29
30    /**
31     * Constructor
32     *
33     * @param string $phpbb_root_path         Path to phpBB's root
34     *
35     */
36    public function __construct(string $phpbb_root_path)
37    {
38        $this->phpbb_root_path = $phpbb_root_path;
39    }
40
41    /**
42     * Returns available languages
43     *
44     * @return array
45     *
46     * @throws DomainException When one of the languages in language directory
47     *                        could not be loaded or have invalid composer.json data
48     */
49    public function get_available_languages() : array
50    {
51        // Find available language packages
52        $finder = new Finder();
53        $finder->files()
54            ->name('composer.json')
55            ->depth('== 1')
56            ->followLinks()
57            ->in($this->phpbb_root_path . 'language');
58
59        $available_languages = array();
60        foreach ($finder as $file)
61        {
62            $json = $file->getContents();
63            $data = json_sanitizer::decode($json);
64
65            $available_languages[] = $this->get_language_data_from_json($data);
66        }
67
68        usort($available_languages, [$this, 'sort_by_local_name']);
69
70        return $available_languages;
71    }
72
73    /**
74     * Collect some data from the composer.json file
75     *
76     * @param string $path
77     * @return array
78     *
79     * @throws DomainException When unable to language data from composer.json
80     */
81    public function get_language_data_from_composer_file(string $path) : array
82    {
83        $json_data = file_get_contents($path);
84        return $this->get_language_data_from_json(json_sanitizer::decode($json_data));
85    }
86
87    /**
88     * Collect some data from the composer.json data
89     *
90     * @param array $data
91     * @return array
92     *
93     * @throws DomainException When composer.json data is invalid for language files
94     */
95    protected function get_language_data_from_json(array $data) : array
96    {
97        if (!isset($data['extra']['language-iso']) || !isset($data['extra']['english-name']) || !isset($data['extra']['local-name']) || !isset($data['extra']['direction']) || !isset($data['extra']['user-lang']) || !isset($data['extra']['plural-rule']) || !isset($data['extra']['recaptcha-lang']))
98        {
99            throw new DomainException('INVALID_LANGUAGE_PACK');
100        }
101
102        $authors = [];
103        if (isset($data['authors']))
104        {
105            foreach ($data['authors'] as $author)
106            {
107                if (isset($author['name']) && $author['name'] !== '')
108                {
109                    $authors[] = $author['name'];
110                }
111            }
112        }
113
114        return [
115            'iso'                => $data['extra']['language-iso'],
116            'name'                => $data['extra']['english-name'],
117            'local_name'        => $data['extra']['local-name'],
118            'author'            => implode(', ', $authors),
119            'version'            => $data['version'],
120            'phpbb_version'        => $data['extra']['phpbb-version'],
121            'direction'            => $data['extra']['direction'],
122            'user_lang'            => $data['extra']['user-lang'],
123            'plural_rule'        => $data['extra']['plural-rule'],
124            'recaptcha_lang'    => $data['extra']['recaptcha-lang'],
125            'turnstile_lang'    => $data['extra']['turnstile-lang'] ?? '',
126        ];
127    }
128
129    /**
130     * Sorts the languages by their name instead of iso code
131     *
132     * @param mixed $a First language data
133     * @param mixed $b Second language data
134     * @return int
135     */
136    private static function sort_by_local_name(mixed $a, mixed $b): int
137    {
138        return $a['local_name'] <=> $b['local_name'];
139    }
140}