Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
88.14% covered (warning)
88.14%
52 / 59
50.00% covered (danger)
50.00%
5 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
metadata_manager
88.14% covered (warning)
88.14%
52 / 59
50.00% covered (danger)
50.00%
5 / 10
38.16
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 get_metadata
81.82% covered (warning)
81.82%
9 / 11
0.00% covered (danger)
0.00%
0 / 1
8.38
 fetch_metadata_from_file
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
4.25
 sanitize_json
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validate
95.00% covered (success)
95.00%
19 / 20
0.00% covered (danger)
0.00%
0 / 1
8
 validate_authors
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 validate_enable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
 validate_dir
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 validate_require_phpbb
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 validate_require_php
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
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\extension;
15
16/**
17* The extension metadata manager validates and gets meta-data for extensions
18*/
19class metadata_manager
20{
21    /**
22    * Name (including vendor) of the extension
23    * @var string
24    */
25    protected $ext_name;
26
27    /**
28    * Metadata from the composer.json file
29    * @var array
30    */
31    protected $metadata;
32
33    /**
34    * Link (including root path) to the metadata file
35    * @var string
36    */
37    protected $metadata_file;
38
39    /**
40    * Creates the metadata manager
41    *
42    * @param string                $ext_name            Name (including vendor) of the extension
43    * @param string                $ext_path            Path to the extension directory including root path
44    */
45    public function __construct($ext_name, $ext_path)
46    {
47        $this->ext_name = $ext_name;
48        $this->metadata = array();
49        $this->metadata_file = $ext_path . 'composer.json';
50    }
51
52    /**
53    * Processes and gets the metadata requested
54    *
55    * @param  string $element            All for all metadata that it has and is valid, otherwise specify which section you want by its shorthand term.
56    * @return array                    Contains all of the requested metadata, throws an exception on failure
57    */
58    public function get_metadata($element = 'all')
59    {
60        // Fetch and clean the metadata if not done yet
61        if ($this->metadata === array())
62        {
63            $this->fetch_metadata_from_file();
64        }
65
66        switch ($element)
67        {
68            case 'all':
69            default:
70                $this->validate();
71                return $this->metadata;
72            break;
73
74            case 'version':
75            case 'name':
76                $this->validate($element);
77                return $this->metadata[$element];
78            break;
79
80            case 'display-name':
81                return (isset($this->metadata['extra']['display-name'])) ? $this->metadata['extra']['display-name'] : $this->get_metadata('name');
82            break;
83        }
84    }
85
86    /**
87    * Gets the metadata file contents and cleans loaded file
88    *
89    * @throws \phpbb\extension\exception
90    */
91    private function fetch_metadata_from_file()
92    {
93        if (!file_exists($this->metadata_file))
94        {
95            throw new \phpbb\extension\exception('FILE_NOT_FOUND', array($this->metadata_file));
96        }
97
98        if (!($file_contents = file_get_contents($this->metadata_file)))
99        {
100            throw new \phpbb\extension\exception('FILE_CONTENT_ERR', array($this->metadata_file));
101        }
102
103        if (($metadata = json_decode($file_contents, true)) === null)
104        {
105            throw new \phpbb\extension\exception('FILE_JSON_DECODE_ERR', array($this->metadata_file));
106        }
107
108        array_walk_recursive($metadata, array($this, 'sanitize_json'));
109        $this->metadata = $metadata;
110    }
111
112    /**
113     * Sanitize input from JSON array using htmlspecialchars()
114     *
115     * @param mixed        $value    Value of array row
116     * @param string    $key    Key of array row
117     */
118    public function sanitize_json(&$value, $key)
119    {
120        $value = htmlspecialchars($value, ENT_COMPAT);
121    }
122
123    /**
124    * Validate fields
125    *
126    * @param string $name  ("all" for display and enable validation
127    *                         "display" for name, type, and authors
128    *                         "name", "type")
129    * @return Bool True if valid, throws an exception if invalid
130    * @throws \phpbb\extension\exception
131    */
132    public function validate($name = 'display')
133    {
134        // Basic fields
135        $fields = array(
136            'name'        => '#^[a-zA-Z0-9_\x7f-\xff]{2,}/[a-zA-Z0-9_\x7f-\xff]{2,}$#',
137            'type'        => '#^phpbb-extension$#',
138            'license'    => '#.+#',
139            'version'    => '#.+#',
140        );
141
142        switch ($name)
143        {
144            case 'all':
145                $this->validate_enable();
146            // no break
147
148            case 'display':
149                foreach ($fields as $field => $data)
150                {
151                    $this->validate($field);
152                }
153
154                $this->validate_authors();
155            break;
156
157            default:
158                if (isset($fields[$name]))
159                {
160                    if (!isset($this->metadata[$name]))
161                    {
162                        throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array($name));
163                    }
164
165                    if (!preg_match($fields[$name], $this->metadata[$name]))
166                    {
167                        throw new \phpbb\extension\exception('META_FIELD_INVALID', array($name));
168                    }
169                }
170            break;
171        }
172
173        return true;
174    }
175
176    /**
177    * Validates the contents of the authors field
178    *
179    * @return boolean True when passes validation, throws exception if invalid
180    * @throws \phpbb\extension\exception
181    */
182    public function validate_authors()
183    {
184        if (empty($this->metadata['authors']))
185        {
186            throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array('authors'));
187        }
188
189        foreach ($this->metadata['authors'] as $author)
190        {
191            if (!isset($author['name']))
192            {
193                throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array('author name'));
194            }
195        }
196
197        return true;
198    }
199
200    /**
201    * This array handles the verification that this extension can be enabled on this board
202    *
203    * @return bool True if validation succeeded, throws an exception if invalid
204    * @throws \phpbb\extension\exception
205    */
206    public function validate_enable()
207    {
208        // Check for valid directory & phpBB, PHP versions
209        return $this->validate_dir() && $this->validate_require_phpbb() && $this->validate_require_php();
210    }
211
212    /**
213    * Validates the most basic directory structure to ensure it follows <vendor>/<ext> convention.
214    *
215    * @return boolean True when passes validation, throws an exception if invalid
216    * @throws \phpbb\extension\exception
217    */
218    public function validate_dir()
219    {
220        if (substr_count($this->ext_name, '/') !== 1 || $this->ext_name != $this->get_metadata('name'))
221        {
222            throw new \phpbb\extension\exception('EXTENSION_DIR_INVALID');
223        }
224
225        return true;
226    }
227
228
229    /**
230    * Validates the contents of the phpbb requirement field
231    *
232    * @return boolean True when passes validation, throws an exception if invalid
233    * @throws \phpbb\extension\exception
234    */
235    public function validate_require_phpbb()
236    {
237        if (!isset($this->metadata['extra']['soft-require']['phpbb/phpbb']))
238        {
239            throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array('soft-require'));
240        }
241
242        return true;
243    }
244
245    /**
246    * Validates the contents of the php requirement field
247    *
248    * @return boolean True when passes validation, throws an exception if invalid
249    * @throws \phpbb\extension\exception
250    */
251    public function validate_require_php()
252    {
253        if (!isset($this->metadata['require']['php']))
254        {
255            throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array('require php'));
256        }
257
258        return true;
259    }
260}