Completed
Pull Request — master (#2)
by Nashwan
04:20
created

Konfig::setFileParsers()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
rs 9.4285
cc 3
eloc 8
nc 4
nop 1
1
<?php
2
/**
3
 * Konfig
4
 *
5
 * Yet another simple configuration file loader library.
6
 *
7
 * @author  Xeriab Nabil (aka KodeBurner) <[email protected]>
8
 * @license https://raw.github.com/xeriab/konfig/master/LICENSE MIT
9
 * @link    https://xeriab.github.io/projects/konfig
10
 */
11
12
namespace Exen\Konfig;
13
14
use SplStack;
15
use Exen\Konfig\Exception\EmptyDirectoryException;
16
use Exen\Konfig\Exception\FileNotFoundException;
17
use Exen\Konfig\Exception\UnsupportedFileFormatException;
18
19
final class Konfig extends AbstractKonfig
20
{
21
    /**
22
     * @var SplStack
23
     * @since 0.1
24
     */
25
    protected $fileParsers;
26
27
    /**
28
     * Stores loaded configuration files
29
     *
30
     * @var array $loadedFiles Array of loaded configuration files
31
     */
32
    static $loadedFiles = [];
33
34
    /**
35
     * Loads a supported configuration file format.
36
     *
37
     * @param  string|array|mixed $path String file | configuration array | Konfig instance
38
     * @throws EmptyDirectoryException If `$path` is an empty directory
39
     */
40
    public function __construct($path, array $parsers = [])
41
    {
42
        $this->setFileParsers($parsers);
43
        // if (!isset($path)) {
44
        //     return;
45
        // }
46
        $paths = $this->getValidPath($path);
47
48
        $this->configData = [];
49
50
        foreach ($paths as $path) {
51
            // Get file information
52
            $ext = pathinfo($path, PATHINFO_EXTENSION);
53
            $parser = $this->getParser($ext);
54
55
            // Try and load file
56
            $this->configData = array_replace_recursive($this->configData, $parser->parse($path));
57
58
            self::$loadedFiles[$path] = true;
59
        } // END foreach
60
61
        parent::__construct($this->configData);
62
    }
63
64
    /**
65
     * Static method for loading a Konfig instance.
66
     *
67
     * @param  string|array|mixed $path string file | configuration array | Konfig instance
68
     * @return Konfig
69
     */
70
    public static function load($path = null)
71
    {
72
        return new static($path);
73
    }
74
75
    /**
76
     * Static method for getting loaded Konfig files.
77
     *
78
     * @return array
79
     */
80
    public static function loaded()
81
    {
82
        return self::$loadedFiles;
83
    }
84
85
    /**
86
     * @return FileParser[]
87
     * @since 0.1
88
     */
89
    public function getFileParsers()
90
    {
91
        return $this->fileParsers;
92
    }
93
94
    /**
95
     * @return void
96
     * @since 0.1
97
     */
98
    protected function addFileParser(FileParser $fileParser)
99
    {
100
        $this->fileParsers[] = $fileParser;
101
    }
102
103
    /**
104
     * @return void
105
     * @since 0.1
106
     */
107
    protected function setFileParsers(array $fileParsers = [])
108
    {
109
        if (empty($fileParsers)) {
110
            $fileParsers = [
111
                new FileParser\Json(),
112
                new FileParser\Yaml(),
113
            ];
114
        }
115
116
        $this->fileParsers = new SplStack();
117
118
        foreach ($fileParsers as $fileParser) {
119
            $this->addFileParser($fileParser);
120
        }
121
    }
122
123
    /**
124
     * Gets a parser for a given file extension
125
     *
126
     * @param  string $ext
127
     * @return Konfig\FileParser
128
     * @throws UnsupportedFileFormatException If `$path` is an unsupported file format
129
     */
130
    private function getParser($ext)
131
    {
132
        $parser = null;
133
134
        if (empty($ext)) {
135
            // @TODO: Throw an exception.
136
        }
137
138
        $fileParsers = $this->getFileParsers();
139
140
        foreach ($fileParsers as $fileParser) {
141
            if (in_array($ext, $fileParser->getSupportedFileExtensions(), true)) {
142
                $parser = $fileParser;
143
                break;
144
            }
145
        }
146
147
        // If none exist, then throw an exception
148
        if (is_null($parser)) {
149
            throw new UnsupportedFileFormatException('Unsupported configuration format');
150
        }
151
152
        return $parser;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $parser; (object|integer|double|string|array|boolean) is incompatible with the return type documented by Exen\Konfig\Konfig::getParser of type Exen\Konfig\Konfig\FileParser.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
153
    }
154
155
    /**
156
     * Checks `$path` to see if it is either an array, a directory, or a file
157
     *
158
     * @param  string | array $path
159
     * @return array
160
     * @throws EmptyDirectoryException If `$path` is an empty directory
161
     * @throws FileNotFoundException If a file is not found at `$path`
162
     */
163
    private function getValidPath($path = null)
164
    {
165
        #: Get path from Array
166
167
        // If `$path` is an array
168
        // The below code is to get the path from a given $path array
169
        if (is_array($path)) {
170
            $paths = [];
171
172
            foreach ($path as $unverifiedPath) {
173
                try {
174
                    // Check if `$unverifiedPath` is optional
175
                    // If it exists, then it's added to the list
176
                    // If it doesn't, it throws an exception which we catch
177
                    if ($unverifiedPath[0] !== '?') {
178
                        $paths = array_merge($paths, $this->getValidPath($unverifiedPath));
179
                        continue;
180
                    }
181
182
                    $optionalPath = ltrim($unverifiedPath, '?');
183
184
                    $paths = array_merge($paths, $this->getValidPath($optionalPath));
185
                } catch (FileNotFoundException $e) {
186
                    // If `$unverifiedPath` is optional, then skip it
187
                    if ($unverifiedPath[0] === '?') {
188
                        continue;
189
                    }
190
191
                    // Otherwise rethrow the exception
192
                    throw $e;
193
                }
194
            }
195
196
            return $paths;
197
        }
198
199
        // If `$path` is a directory
200
        if (is_dir($path)) {
201
            #: TODO: Hmmm, I need to end up with something more efficient
202
            // $paths = @glob($path . '/*.{yaml,json,ini,xml,toml,yml,php,inc,php5,conf,cfg}', GLOB_BRACE);
203
            $paths = @glob($path . '/*.*');
204
205
            if (empty($paths)) {
206
                throw new EmptyDirectoryException("Configuration directory: [$path] is empty");
207
            }
208
209
            return $paths;
210
        }
211
212
        // If `$path` is not a file, throw an exception
213
        if (!file_exists($path) and isset($path)) {
214
            throw new FileNotFoundException("Configuration file: [$path] cannot be found");
215
        }
216
217
        // If `$path` is not set
218
        if (!isset($path)) {
219
            return;
220
        }
221
222
        return [$path];
223
    }
224
225
    public function __toString()
226
    {
227
        return 'Konfig';
228
    }
229
}
230
231
#: END OF ./src/Konfig.php FILE
232