FileLoader   A
last analyzed

Complexity

Total Complexity 30

Size/Duplication

Total Lines 277
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 2
Bugs 2 Features 0
Metric Value
wmc 30
c 2
b 2
f 0
lcom 1
cbo 4
dl 0
loc 277
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A addFiles() 0 14 3
A process() 0 4 1
A isSupportedMimeType() 0 4 1
A getFileContents() 0 16 3
A checkAndAddDefaultDecoder() 0 11 3
A addDecoder() 0 11 3
B decodeFile() 0 24 4
A getDecoders() 0 4 1
A getMimeTypes() 0 4 1
A sanitizeNamespace() 0 16 1
A getFilesNamespace() 0 17 2
C decodeFileBagData() 0 33 7
1
<?php
2
namespace Michaels\Manager;
3
4
use Michaels\Manager\Contracts\DecoderInterface;
5
use Michaels\Manager\Bags\FileBag;
6
use Michaels\Manager\Exceptions\UnsupportedFilesException;
7
use Michaels\Manager\Exceptions\BadFileDataException;
8
use Exception;
9
10
/**
11
 * Loads configuration files and converts them to php arrays using Decoders
12
 * @package Michaels\Manager
13
 */
14
class FileLoader
15
{
16
    /**
17
     * The data which will be decoded.
18
     * @var array
19
     */
20
    protected $dataToDecode = [];
21
22
    /**
23
     * The array of supported Mime types, which is defined in each decoder class.
24
     * @var array
25
     */
26
    protected $supportedMimeTypes = [];
27
28
    /**
29
     * A container for the SPLFileInfo objects
30
     * @var FileBag
31
     */
32
    protected $fileBag;
33
34
    /**
35
     * An array holding data about the loaded decoders.
36
     * @var array
37
     */
38
    protected $decoders = [];
39
40
    /**
41
     * Should there be an attempt to load an unsupported file type (determined by the mime types array), then they
42
     * will be stored in this array for later display in an error message.
43
     *
44
     * @var array
45
     */
46
    protected $unsupportedFiles = [];
47
48
    /**
49
     * The data array, once it has been processed through a decoder.
50
     * @var array
51
     */
52
    protected $decodedData = [];
53
54
    /**
55
     * Add a file decoder to the FileLoader
56
     * @param DecoderInterface $decoder
57
     */
58
    public function addDecoder(DecoderInterface $decoder)
59
    {
60
        $mimeTypes = $decoder->getMimeType();
61
        if ($this->isSupportedMimeType($mimeTypes[0])) {
62
            return; // we already have the decoder loaded!
63
        }
64
        $this->supportedMimeTypes = array_merge($this->supportedMimeTypes, $mimeTypes);
65
        foreach ($mimeTypes as $type) {
66
            $this->decoders[$type] = $decoder;
67
        }
68
    }
69
70
    /**
71
     * Add a file bag, or change an array of SplFileInfo Objects to proper objects.
72
     *
73
     * @param mixed $files a FileBag or an array of SplFileInfo objects.
74
     */
75
    public function addFiles($files)
76
    {
77
        if ($files instanceof FileBag) {
78
            $this->fileBag = $files;
79
            return;
80
        }
81
82
        if (is_array($files)) {
83
            $this->fileBag = new FileBag($files);
84
            return;
85
        }
86
        throw new BadFileDataException('The attempt at adding files to the file loader failed, due to the files not
87
        being a FileBag Object or an array of SplInfoObjects.');
88
    }
89
90
    /**
91
     * Process the current FileBag and return an array
92
     * @param bool $ns
93
     * @param bool $strict
94
     * @return array
95
     */
96
    public function process($ns = true, $strict = true)
97
    {
98
        return $this->decodedData = $this->decodeFileBagData($this->fileBag, $ns, $strict);
99
    }
100
101
    /**
102
     * Process file bag to load into the data manager.
103
     * A file bag is an array of SplFileInfo objects.
104
     *
105
     * @param array|FileBag $fileBag
106
     * @param bool $ns
107
     * @param bool $strict
108
     * @return array
109
     * @throws Exception
110
     */
111
    public function decodeFileBagData(FileBag $fileBag, $ns = true, $strict = true)
112
    {
113
        $decodedData = [];
114
        $files = $fileBag->getAllFileInfoObjects();
115
        if (empty($files)) {
116
            throw new Exception("FileBag is empty. Make sure you have initialized the FileLoader and added files.");
117
        }
118
119
        foreach ($files as $file) {
120
            list($namespace, $file) = $this->getFilesNamespace($file);
121
122
            // Decode the actual file and save data
123
            $fileData = $this->decodeFile($file, $strict);
124
            if (is_array($fileData)) {
125
                foreach ($fileData as $k => $v) {
126
                    if ($ns === true) {
127
                        $decodedData[$namespace][$k] = $v;
128
                    } else {
129
                        $decodedData[$k] = $v;
130
                    }
131
                }
132
            }
133
        }
134
135
        if (!empty($this->unsupportedFiles)) {
136
            $badFiles = implode(", ", $this->unsupportedFiles);
137
            throw new UnsupportedFilesException(
138
                'The file(s) ' . $badFiles . ' are not supported by the available decoders.'
139
            );
140
        }
141
142
        return $decodedData;
143
    }
144
145
    /**
146
     * Check to make sure the mime type is ok.
147
     * @param string $type A mime type
148
     * @return bool
149
     */
150
    protected function isSupportedMimeType($type)
151
    {
152
        return in_array($type, $this->supportedMimeTypes);
153
    }
154
155
    /**
156
     * Returns the contents of the file.
157
     *
158
     * @param \SplFileInfo $file
159
     * @return string the contents of the file
160
     */
161
    public function getFileContents(\SplFileInfo $file)
162
    {
163
        if ($file->getExtension() === 'php') {
164
            $content = include $file->getPathname();
165
            return $content;
166
        }
167
168
        $level = error_reporting(0);
169
        $content = file_get_contents($file->getPathname());
170
        error_reporting($level);
171
        if (false === $content) {
172
            $error = error_get_last();
173
            throw new \RuntimeException($error['message']);
174
        }
175
        return $content;
176
    }
177
178
    /**
179
     * Default decoder class factory method.
180
     * Checks to make sure we have a default decoder available and if so, adds it as a decoder to the file loader.
181
     *
182
     * @param $mimeType
183
     */
184
    protected function checkAndAddDefaultDecoder($mimeType)
185
    {
186
        $decoderClass = ucfirst($mimeType) . "Decoder";
187
188
        if (file_exists(__DIR__ . '/Decoders/' . $decoderClass . '.php') && !$this->isSupportedMimeType($mimeType)) {
189
            $nameSpace = "Michaels\\Manager\\Decoders\\";
190
            $fullQualifiedClassName = $nameSpace . $decoderClass;
191
            $decoder = new $fullQualifiedClassName();
192
            $this->addDecoder($decoder);
193
        }
194
    }
195
196
    /**
197
     * Decodes a single file using registered decoders
198
     * @param \SplFileInfo $file
199
     * @param bool $strict
200
     * @return array|bool
201
     * @throws Exception
202
     */
203
    protected function decodeFile(\SplFileInfo $file, $strict = true)
204
    {
205
        $mimeType = $file->getExtension();
206
207
        $this->checkAndAddDefaultDecoder($mimeType);
208
209
        if ($this->isSupportedMimeType($mimeType)) {
210
211
            if (is_readable($file->getPathname())) {
212
                $data = $this->getFileContents($file);
213
            } else {
214
                if ($strict) {
215
                    throw new \Exception("File not found: {$file->getPathname()}");
216
                } else {
217
                    return [];
218
                }
219
            }
220
221
            return $this->decoders[$mimeType]->decode($data);
222
        }
223
224
        $this->unsupportedFiles[] = $file->getFilename();
225
        return false;
226
    }
227
228
    /**
229
     * Returns currently attached custom decoders
230
     * @return array
231
     */
232
    public function getDecoders()
233
    {
234
        return $this->decoders;
235
    }
236
237
    /**
238
     * Returns currently supported Mime Types
239
     * @return array
240
     */
241
    public function getMimeTypes()
242
    {
243
        return $this->supportedMimeTypes;
244
    }
245
246
    /**
247
     * Cleans up a file name for use as a namespace
248
     * @param $ns
249
     * @return mixed
250
     */
251
    public function sanitizeNamespace($ns)
252
    {
253
        // dots to underscores
254
        $ns = str_replace(".", "_", $ns);
255
256
        // spaces to underscores
257
        $ns = str_replace(" ", "_", $ns);
258
259
        // dashes to underscores
260
        $ns = str_replace("-", "_", $ns);
261
262
        // Alpha numeric
263
        $ns = preg_replace("/[^A-Za-z0-9\.\_\- ]/", '', $ns);
264
265
        return $ns;
266
    }
267
268
    /**
269
     * Gets or creates the file's namespace
270
     * @param $file
271
     * @return array with the namespace and fileobject
272
     */
273
    protected function getFilesNamespace($file)
274
    {
275
        $namespace = null;
276
277
        if (is_array($file)) {
278
            // We are using a custom namespace
279
            $namespace = $this->sanitizeNamespace($file[1]);
280
            $file = $file[0];
281
            return [$namespace, $file];
282
283
        } else {
284
            // We are namespacing using the file's name
285
            $filename = rtrim($file->getBasename(), '.' . $file->getExtension());
286
            $namespace = $this->sanitizeNamespace($filename);
287
            return [$namespace, $file];
288
        }
289
    }
290
}
291