Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
37.88% covered (danger)
37.88%
25 / 66
21.74% covered (danger)
21.74%
5 / 23
CRAP
0.00% covered (danger)
0.00%
0 / 1
Manifest
37.88% covered (danger)
37.88%
25 / 66
21.74% covered (danger)
21.74%
5 / 23
624.59
0.00% covered (danger)
0.00%
0 / 1
 __construct
72.73% covered (warning)
72.73%
8 / 11
0.00% covered (danger)
0.00%
0 / 1
3.18
 getName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getDescription
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getAuthor
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getLabel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getAclTable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getVersion
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getDependencies
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 getInstallModelFiles
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
3.21
 getInstallPHPFiles
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 getUninstallData
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getUpdateHandler
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getLocalData
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getRoutes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getConstants
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 getExtra
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getContainerServiceProvider
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMiddlewares
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getE2ePrerequisiteActions
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 extractDependencies
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 extractChecks
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 getManagementRoleUri
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getComposerInfo
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2020 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 *
20 */
21
22declare(strict_types=1);
23
24namespace oat\oatbox\extension;
25
26use oat\generis\model\DependencyInjection\ContainerServiceProviderInterface;
27use oat\oatbox\extension\exception\ManifestException;
28use oat\oatbox\extension\exception\ManifestNotFoundException;
29use oat\oatbox\service\exception\InvalidServiceManagerException;
30use oat\tao\model\Middleware\Contract\MiddlewareMapInterface;
31use Zend\ServiceManager\ServiceLocatorAwareTrait;
32use Zend\ServiceManager\ServiceLocatorAwareInterface;
33use Composer\InstalledVersions;
34
35/**
36 * Class Manifest
37 * @author Jerome Bogaerts <jerome@taotesting.com>
38 * @package oat\oatbox\extension
39 */
40class Manifest implements ServiceLocatorAwareInterface
41{
42    use ServiceLocatorAwareTrait;
43
44    /**
45     * The path to the file where the manifest is described.
46     * @var string
47     */
48    private $filePath = '';
49
50    /**
51     * The version of the Extension the manifest describes.
52     * @var string
53     */
54    private $version = null;
55
56    /**
57     * The dependencies of the Extension the manifest describes.
58     * @var array
59     */
60    private $dependencies = [];
61
62    /**
63     * @var array
64     */
65    private $manifest = [];
66
67    /**
68     * @var ComposerInfo
69     */
70    private $composerInfo = null;
71
72    /**
73     * Creates a new instance of Manifest.
74     *
75     * @access public
76     * @param string $filePath The path to the manifest.php file to parse.
77     * @param ComposerInfo|null $composerInfo
78     * @throws ManifestNotFoundException
79     * @throws exception\MalformedManifestException
80     */
81    public function __construct($filePath, ComposerInfo $composerInfo = null)
82    {
83        // the file exists, we can refer to the $filePath.
84        if (!is_readable($filePath)) {
85            throw new ManifestNotFoundException(
86                "The Extension Manifest file located at '${filePath}' could not be read."
87            );
88        }
89        $this->manifest = require($filePath);
90        // mandatory
91        if (empty($this->manifest['name'])) {
92            throw new exception\MalformedManifestException(
93                "The 'name' component is mandatory in manifest located at '{$this->filePath}'."
94            );
95        }
96        $this->composerInfo = $composerInfo;
97        $this->filePath = $filePath;
98    }
99
100    /**
101     * Get the name of the Extension the manifest describes.
102     * @return string
103     */
104    public function getName(): string
105    {
106        return isset($this->manifest['name']) ? $this->manifest['name'] : '';
107    }
108
109    /**
110     * Get the description of the Extension the manifest describes.
111     * @return string
112     */
113    public function getDescription(): string
114    {
115        return isset($this->manifest['description']) ? $this->manifest['description'] : '';
116    }
117
118    /**
119     * Get the author of the Extension the manifest describes.
120     * @return string
121     */
122    public function getAuthor(): string
123    {
124        return isset($this->manifest['author']) ? $this->manifest['author'] : '';
125    }
126
127    /**
128     * Get the human readable label of the Extension the manifest describes.
129     * @return string
130     */
131    public function getLabel(): string
132    {
133        return isset($this->manifest['label']) ? $this->manifest['label'] : '';
134    }
135
136    /**
137     * Returns the Access Control Layer table
138     * @return array
139     */
140    public function getAclTable(): array
141    {
142        return isset($this->manifest['acl']) ? $this->manifest['acl'] : [];
143    }
144
145    /**
146     * Get the version of the Extension the manifest describes.
147     * @return string
148     * @throws ManifestException
149     */
150    public function getVersion(): string
151    {
152        if ($this->version === null) {
153            $packageId = $this->getComposerInfo()->getPackageId();
154            $this->version = InstalledVersions::getVersion($packageId);
155        }
156        return (string) $this->version;
157    }
158
159    /**
160     * Get the dependencies of the Extension the manifest describes.
161     * The content of the array are extensionIDs, represented as strings.
162     * @return array
163     * @throws ManifestException
164     * @throws InvalidServiceManagerException
165     */
166    public function getDependencies(): array
167    {
168        if (empty($this->dependencies)) {
169            $this->dependencies = $this->getComposerInfo()->extractExtensionDependencies();
170            //backward compatibility with old requirements declaration
171            //todo: remove after dependencies of all Tao extensions be moved from manifest to composer.json
172            if (isset($this->manifest['requires'])) {
173                $this->dependencies = array_merge($this->dependencies, $this->manifest['requires']);
174            }
175        }
176        return $this->dependencies;
177    }
178
179    /**
180     * returns an array of RDF files to import during install.
181     * The returned array contains paths to the files to be imported.
182     * @return array
183     */
184    public function getInstallModelFiles(): array
185    {
186        if (!isset($this->manifest['install']['rdf'])) {
187            return [];
188        }
189        $files = is_array($this->manifest['install']['rdf']) ?
190            $this->manifest['install']['rdf'] :
191            [$this->manifest['install']['rdf']];
192        $files = array_filter($files);
193        return (array) $files;
194    }
195
196    /**
197     * Get a list of PHP files to be executed at installation time.
198     * The returned array contains absolute paths to the files to execute.
199     * @return array
200     */
201    public function getInstallPHPFiles(): array
202    {
203        $result = [];
204        if (isset($this->manifest['install']['php'])) {
205            $result = is_array($this->manifest['install']['php'])
206                ? $this->manifest['install']['php']
207                : [$this->manifest['install']['php']];
208        }
209        return $result;
210    }
211
212    /**
213     * Return the uninstall data as an array if present, or null if not
214     * @return mixed
215     */
216    public function getUninstallData()
217    {
218        return isset($this->manifest['uninstall']) ? $this->manifest['uninstall'] : null;
219    }
220
221    /**
222     * Return the className of the updateHandler
223     * @return string
224     */
225    public function getUpdateHandler()
226    {
227        return isset($this->manifest['update']) ? $this->manifest['update'] : null;
228    }
229
230    /**
231      * PHP scripts to execute in order to add some sample data to an install
232      * @return array
233      */
234    public function getLocalData()
235    {
236        return isset($this->manifest['local']) ? $this->manifest['local'] : [];
237    }
238
239    /**
240     * Gets the controller routes of this extension.
241     * @return array
242     */
243    public function getRoutes()
244    {
245        return isset($this->manifest['routes']) ? $this->manifest['routes'] : [];
246    }
247
248    /**
249     * Get an array of constants to be defined where array keys are constant names
250     * and values are the values of these constants.
251     * @return array
252     */
253    public function getConstants(): array
254    {
255        return isset($this->manifest['constants']) && is_array($this->manifest['constants'])
256            ? $this->manifest['constants'] : [];
257    }
258
259    /**
260     * Get the array with unformatted extra data
261     * @return array
262     */
263    public function getExtra()
264    {
265        return isset($this->manifest['extra']) ? $this->manifest['extra'] : [];
266    }
267
268    /**
269     * @return string[] Array with ContainerServiceProviderInterface FQNs
270     */
271    public function getContainerServiceProvider(): array
272    {
273        return $this->manifest['containerServiceProviders'] ?? [];
274    }
275
276    /**
277     * @return string[] Array with MiddlewareMapInterface FQNs
278     */
279    public function getMiddlewares(): array
280    {
281        return $this->manifest['middlewares'] ?? [];
282    }
283
284    /**
285     * @return AbstractAction[]
286     */
287    public function getE2ePrerequisiteActions(): array
288    {
289        return $this->manifest['e2ePrerequisiteActions'] ?? [];
290    }
291
292    /**
293     * Extract dependencies for extensions
294     * @param string $file
295     * @return array
296     * @throws \common_ext_ExtensionException
297     */
298    public static function extractDependencies(string $file)
299    {
300        $file = realpath($file);
301        $composer = new ComposerInfo(dirname($file));
302        return array_keys($composer->extractExtensionDependencies());
303    }
304
305    /**
306     * Extract checks from a given manifest file.
307     * @param $file string The path to a manifest.php file.
308     * @return \common_configuration_ComponentCollection
309     * @throws ManifestNotFoundException
310     */
311    public static function extractChecks(string $file)
312    {
313        // the file exists, we can refer to the $filePath.
314        if (!is_readable($file)) {
315            throw new ManifestNotFoundException(
316                sprintf('The Extension Manifest file located at %s could not be read.', $file)
317            );
318        }
319
320        $manifest = require($file);
321        $returnValue = $manifest['install']['checks'] ?? [];
322        foreach ($returnValue as &$component) {
323            if (strpos($component['type'], 'FileSystemComponent') !== false) {
324                $root = realpath(dirname(__FILE__) . '/../../../../');
325                $component['value']['location'] = $root . '/' . $component['value']['location'];
326            }
327        }
328
329        return $returnValue;
330    }
331
332    /**
333     * Get the Role dedicated to manage this extension. Returns null if there is
334     * @return string
335     */
336    public function getManagementRoleUri()
337    {
338        return isset($this->manifest['managementRole']) ? $this->manifest['managementRole'] : '';
339    }
340
341    /**
342     * @return ComposerInfo
343     * @throws \common_ext_ExtensionException
344     */
345    private function getComposerInfo()
346    {
347        if ($this->composerInfo === null) {
348            $this->composerInfo = new ComposerInfo(dirname($this->filePath));
349        }
350        return $this->composerInfo;
351    }
352}