Completed
Push — master ( c9e101...23d7ed )
by Matthias
03:56
created

Config::normalizePath()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 14
rs 9.4285
cc 3
eloc 7
nc 3
nop 1
1
<?php
2
3
namespace Cauditor;
4
5
use MatthiasMullie\PathConverter\Converter;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Cauditor\Converter.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
6
use Symfony\Component\Yaml\Exception\ParseException;
7
use Symfony\Component\Yaml\Parser;
8
9
/**
10
 * @author Matthias Mullie <[email protected]>
11
 * @copyright Copyright (c) 2016, Matthias Mullie. All rights reserved.
12
 * @license LICENSE MIT
13
 */
14
class Config implements \ArrayAccess
15
{
16
    /**
17
     * @var array
18
     */
19
    protected $config = array();
20
21
    /**
22
     * @var array
23
     */
24
    protected $defaults = array(
25
        'build_path' => 'build/cauditor',
26
        'exclude_folders' => array('tests', 'vendor'),
27
    );
28
29
    /**
30
     * List of config keys that denote paths, so they can be normalized
31
     * (= turned into "relative to project root" instead of "relative to config
32
     * file")
33
     *
34
     * @var string[]
35
     */
36
    protected $paths = array('build_path', 'exclude_folders');
37
38
    /**
39
     * @param string $project Project root path.
40
     * @param string $config  Config YML file path.
41
     */
42
    public function __construct($project, $config = null)
43
    {
44
        $this->config['path'] = rtrim($project, DIRECTORY_SEPARATOR);
45
        $this->config['config_path'] = null;
46
47
        if ($config !== null) {
48
            $this->config['config_path'] = rtrim($config, DIRECTORY_SEPARATOR);
49
            $this->config += $this->readConfig($config);
50
        }
51
52
        $this->config += $this->defaults;
53
54
        // *always* exclude some folders - they're not project-specific code and
55
        // could easily be overlooked when overriding excludes
56
        $this->config['exclude_folders'][] = 'vendor';
57
        $this->config['exclude_folders'][] = '.git';
58
        $this->config['exclude_folders'][] = '.svn';
59
        $this->config['exclude_folders'] = array_unique($this->config['exclude_folders']);
60
    }
61
62
    /**
63
     * Normalize all relative paths by prefixing them with the project path.
64
     *
65
     * @param string|string[] $value
66
     *
67
     * @return string|string[]
68
     */
69
    protected function normalizePath($value)
70
    {
71
        // array of paths = recursive
72
        if (is_array($value)) {
73
            foreach ($value as $i => $val) {
74
                $value[$i] = $this->normalizePath($val);
75
            }
76
77
            return $value;
78
        }
79
80
        $converter = new Converter(dirname($this->config['config_path']), $this->config['path']);
81
        return $converter->convert($value);
82
    }
83
84
    /**
85
     * @param string $path Path to config file.
86
     *
87
     * @return array
88
     */
89
    protected function readConfig($path)
90
    {
91
        if (!file_exists($path) || !is_file($path) || !is_readable($path)) {
92
            return array();
93
        }
94
95
        $yaml = new Parser();
96
97
        try {
98
            $config = (array) $yaml->parse(file_get_contents($path));
99
        } catch (ParseException $e) {
100
            return array();
101
        }
102
103
        // adjust relative paths
104
        foreach ($this->paths as $key) {
105
            if (isset($config[$key])) {
106
                $config[$key] = $this->normalizePath($config[$key]);
107
            }
108
        }
109
110
        return $config;
111
    }
112
113
    /**
114
     * {@inheritdoc.
115
     */
116
    public function offsetExists($offset)
117
    {
118
        return array_key_exists($offset, $this->config);
119
    }
120
121
    /**
122
     * {@inheritdoc.
123
     */
124
    public function offsetGet($offset)
125
    {
126
        return $this->config[$offset];
127
    }
128
129
    /**
130
     * {@inheritdoc.
131
     */
132
    public function offsetSet($offset, $value)
133
    {
134
        $this->config[$offset] = $value;
135
    }
136
137
    /**
138
     * {@inheritdoc.
139
     */
140
    public function offsetUnset($offset)
141
    {
142
        unset($this->config[$offset]);
143
    }
144
}
145