FileLoader::setSourceFiles()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
/**
3
 * @author Gerard van Helden <[email protected]>
4
 * @copyright Zicht Online <http://zicht.nl>
5
 */
6
7
namespace Zicht\Tool\Configuration;
8
9
use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader;
10
use Symfony\Component\Config\FileLocatorInterface;
11
use Zicht\Version\Version;
12
use Zicht\Tool;
13
use Zicht\Tool\Debug;
14
use Zicht\Version\Constraint;
15
use Symfony\Component\Yaml;
16
17
/**
18
 * The Z file loader
19
 */
20
class FileLoader extends BaseFileLoader
21
{
22
    /**
23
     * Identifies a plugin type configuration
24
     */
25
    const PLUGIN = 'plugin';
26
27
    /**
28
     * Contains all loaded config trees.
29
     *
30
     * @var array
31
     */
32
    protected $configs = array();
33
34
    /**
35
     * Contains all loaded plugin config trees.
36
     *
37
     * @var array
38
     */
39
    protected $plugins = array();
40
    protected $pluginPaths = array();
41
    protected $sourceFiles = array();
42
43
    /**
44
     * Constructor.
45
     *
46
     * @param FileLocatorInterface $locator
47
     * @param \Zicht\Version\Version $version
48
     */
49 4
    public function __construct(FileLocatorInterface $locator, Version $version = null)
50
    {
51 4
        parent::__construct($locator);
52
53 4
        $this->version = $version;
0 ignored issues
show
Bug introduced by
The property version does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
54 4
    }
55
56
    /**
57
     * @param array $sourceFiles
58
     */
59
    public function setSourceFiles($sourceFiles)
60
    {
61
        $this->sourceFiles = $sourceFiles;
62
    }
63
64
    /**
65
     * @return array
66
     */
67
    public function getSourceFiles()
68
    {
69
        return array_unique($this->sourceFiles);
70
    }
71
72
    /**
73
     * @{inheritDoc}
74
     */
75 4
    public function load($resource, $type = null)
76
    {
77 4
        if (!is_file($resource)) {
78 4
            $fileContents = $resource;
79 4
        } else {
80 2
            $this->sourceFiles[] = $resource;
81 2
            $fileContents = file_get_contents($resource);
82
        }
83 4
        Debug::enterScope('annotations');
84 4
        $annotations = $this->parseAnnotations($fileContents);
85
86 4
        if ($this->version && !empty($annotations['version'])) {
87
            $failures = array();
88
            if (!Constraint::isMatch($annotations['version'], $this->version, $failures)) {
89
                trigger_error(
90
                    "Core version '{$this->version}' does not match version annotation '{$annotations['version']}'\n"
91
                    . "(specified in $resource; " . join("; ", $failures) . ")",
92
                    E_USER_WARNING
93
                );
94
            }
95
        }
96 4
        Debug::exitScope('annotations');
97
98
        try {
99 4
            $config = Yaml\Yaml::parse($fileContents);
100 4
        } catch (Yaml\Exception\ExceptionInterface $e) {
101
            throw new \RuntimeException("YAML error in {$resource}", 0, $e);
102
        }
103
104 4 View Code Duplication
        if (isset($config['plugins'])) {
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...
105 3
            Debug::enterScope('plugins');
106 3
            $this->processPlugins($config['plugins'], dirname($resource));
107 2
            Debug::exitScope('plugins');
108 2
            unset($config['plugins']);
109 2
        }
110 3 View Code Duplication
        if (isset($config['imports'])) {
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...
111 1
            Debug::enterScope('imports');
112 1
            $this->processImports($config['imports'], dirname($resource));
113 1
            Debug::exitScope('imports');
114 1
            unset($config['imports']);
115 1
        }
116
117 3
        $this->configs[] = $config;
118
119 3
        return $config;
120
    }
121
122
123
    /**
124
     * Parse the annotations contained in commented lines, starting with #
125
     *
126
     * Annotation format is '@' followed by a word, followed by an optional ':' or '=', followed by a quoted value,
127
     * e.g.
128
     * <code>@foo="bar"</code>
129
     *
130
     * @param string $fileContents
131
     * @return array
132
     */
133 4
    public function parseAnnotations($fileContents)
134
    {
135 4
        $ret = array();
136 4
        if (preg_match('/^#\s*@(\w+)[:=]?\s+([\'"])?(.*)\2\s*$/m', $fileContents, $m)) {
137
            $ret[$m[1]] = $m[3];
138
        }
139 4
        return $ret;
140
    }
141
142
143
    /**
144
     * @{inheritDoc}
145
     */
146 2
    public function supports($resource, $type = null)
147
    {
148 2
        return is_string($resource) && 'yml' === pathinfo($resource, PATHINFO_EXTENSION);
149
    }
150
151
152
    /**
153
     * Processes plugin definitions
154
     *
155
     * @param array $plugins
156
     * @param string $dir
157
     * @return void
158
     */
159 3
    protected function processPlugins($plugins, $dir)
160
    {
161 3
        foreach ($plugins as $plugin) {
162 3
            $this->addPlugin($plugin, $dir);
163 2
        }
164 2
    }
165
166
    /**
167
     * Add a plugin at the passed location
168
     *
169
     * @param string $name
170
     * @param string $dir
171
     * @return void
172
     *
173
     * @throws \InvalidArgumentException
174
     * @throws \UnexpectedValueException
175
     */
176 3
    public function addPlugin($name, $dir)
177
    {
178 3
        Debug::enterScope($name);
179 3
        $hasPlugin = $hasZfile = false;
180
181
        try {
182 3
            $this->plugins[$name] = $this->getLocator()->locate($name . '/Plugin.php', $dir, true);
183 2
            $this->pluginPaths[$name] = dirname($this->plugins[$name]);
184
185 2
            $hasPlugin = true;
186 2
            $this->sourceFiles[] = $this->plugins[$name];
187 3
        } catch (\InvalidArgumentException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
188
        }
189
190
        try {
191 3
            $zFileLocation = $this->getLocator()->locate($name . '/z.yml', $dir);
192 1
            $this->import($zFileLocation, self::PLUGIN);
193 1
            if (!isset($this->pluginPaths[$name])) {
194
                $this->pluginPaths[$name] = dirname($zFileLocation);
195 1
            } else if ($this->pluginPaths[$name] != dirname($zFileLocation)) {
196
                throw new \UnexpectedValueException(
197
                    "Ambiguous plugin configuration:\n"
198
                    . "There was a Plugin.php found in {$this->pluginPaths[$name]}, but also a z.yml at $zFileLocation"
199
                );
200
            }
201
202 1
            $hasZfile = true;
203 1
            $this->sourceFiles[] = $zFileLocation;
204 3
        } catch (\InvalidArgumentException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
205
        }
206
207 3
        if (!$hasPlugin && !$hasZfile) {
208 1
            throw new \InvalidArgumentException("You need at least either a z.yml or a Plugin.php in the plugin path for '{$name}'");
209
        }
210 2
        Debug::exitScope($name);
211 2
    }
212
213
214
    /**
215
     * Processes imports
216
     *
217
     * @param array $imports
218
     * @param string $dir
219
     * @return void
220
     */
221 1
    protected function processImports($imports, $dir)
222
    {
223 1
        foreach ($imports as $import) {
224 1
            $this->setCurrentDir($dir);
225 1
            $this->import($import);
226 1
        }
227 1
    }
228
229
230
    /**
231
     * Returns all loaded configs
232
     *
233
     * @return array
234
     */
235 1
    public function getConfigs()
236
    {
237 1
        return $this->configs;
238
    }
239
240
241
    /**
242
     * Returns all loaded plugins
243
     *
244
     * @return array
245
     */
246 1
    public function getPlugins()
247
    {
248 1
        return $this->plugins;
249
    }
250
251
252
    /**
253
     * Returns all loaded paths
254
     *
255
     * @return array
256
     */
257
    public function getPluginPaths()
258
    {
259
        return $this->pluginPaths;
260
    }
261
}
262