Passed
Push — master ( b33de3...cb7df5 )
by Sebastian
02:24
created

FileHelper_FileFinder::stripExtensions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 1
c 2
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
/**
3
 * File containing the {@see FileHelper_FileFinder} class.
4
 * 
5
 * @package Application Utils
6
 * @subpackage FileHelper
7
 * @see FileHelper_FileFinder
8
 */
9
10
declare(strict_types = 1);
11
12
namespace AppUtils;
13
14
/**
15
 * File finder class used to fetch file lists from folders,
16
 * with criteria matching. Offers many customization options
17
 * on how to return the files, from absolute paths to file names
18
 * without extensions or even class name maps.
19
 *
20
 * @package Application Utils
21
 * @subpackage FileHelper
22
 * @author Sebastian Mordziol <[email protected]>
23
 */
24
class FileHelper_FileFinder implements Interface_Optionable
25
{
26
    use Traits_Optionable;
27
28
    const ERROR_PATH_DOES_NOT_EXIST = 44101;
29
    
30
    const PATH_MODE_ABSOLUTE = 'absolute';
31
    
32
    const PATH_MODE_RELATIVE = 'relative';
33
    
34
    const PATH_MODE_STRIP = 'strip';
35
    
36
   /**
37
    * @var string
38
    */
39
    protected $path;
40
    
41
   /**
42
    * @var array
43
    */
44
    protected $found;
45
    
46
   /**
47
    * The path must exist when the class is instantiated: its
48
    * real path will be determined to work with.
49
    * 
50
    * @param string $path The absolute path to the target folder.
51
    * @throws FileHelper_Exception
52
    * @see FileHelper_FileFinder::ERROR_PATH_DOES_NOT_EXIST
53
    */
54
    public function __construct(string $path)
55
    {
56
        $real = realpath($path);
57
        
58
        if($real === false) 
59
        {
60
            throw new FileHelper_Exception(
61
                'Target path does not exist',
62
                sprintf(
63
                    'Tried accessing path [%s], but its real path could not be determined.',
64
                    $path
65
                ),
66
                self::ERROR_PATH_DOES_NOT_EXIST
67
            );
68
        }
69
        
70
        $this->path = FileHelper::normalizePath($real);
71
    }
72
    
73
    public function getDefaultOptions() : array
74
    {
75
        return array(
76
            'recursive' => false,
77
            'strip-extensions' => false,
78
            'include-extensions' => array(),
79
            'exclude-extensions' => array(),
80
            'pathmode' => self::PATH_MODE_ABSOLUTE,
81
            'slash-replacement' => null
82
        );
83
    }
84
    
85
   /**
86
    * Enables extension stripping, to return file names without extension.
87
    * 
88
    * @return FileHelper_FileFinder
89
    */
90
    public function stripExtensions() : FileHelper_FileFinder
91
    {
92
        return $this->setOption('strip-extensions', true);
93
    }
94
    
95
   /**
96
    * Enables recursing into subfolders.
97
    * 
98
    * @return FileHelper_FileFinder
99
    */
100
    public function makeRecursive() : FileHelper_FileFinder
101
    {
102
        return $this->setOption('recursive', true);
103
    }
104
    
105
   /**
106
    * Retrieves all extensions that were added to
107
    * the include list.
108
    * 
109
    * @return array
110
    */
111
    public function getIncludeExtensions() : array
112
    {
113
        return $this->getArrayOption('include-extensions');
114
    }
115
    
116
   /**
117
    * Includes a single extension in the file search: only
118
    * files with this extension will be used in the results.
119
    * 
120
    * NOTE: Included extensions take precedence before excluded
121
    * extensions. If any excluded extensions are specified, they
122
    * will be ignored.
123
    * 
124
    * @param string $extension Extension name, without dot (`php` for example).
125
    * @return FileHelper_FileFinder
126
    * @see FileHelper_FileFinder::includeExtensions()
127
    */
128
    public function includeExtension(string $extension) : FileHelper_FileFinder
129
    {
130
        return $this->includeExtensions(array($extension));
131
    }
132
    
133
   /**
134
    * Includes several extensions in the file search: only
135
    * files with these extensions wil be used in the results.
136
    * 
137
    * NOTE: Included extensions take precedence before excluded
138
    * extensions. If any excluded extensions are specified, they
139
    * will be ignored.
140
    * 
141
    * @param array $extensions Extension names, without dot (`php` for example).
142
    * @return FileHelper_FileFinder
143
    * @see FileHelper_FileFinder::includeExtension()
144
    */
145
    public function includeExtensions(array $extensions) : FileHelper_FileFinder
146
    {
147
        $items = $this->getIncludeExtensions();
148
        $items = array_merge($items, $extensions);
149
        $items = array_unique($items);
150
        
151
        $this->setOption('include-extensions', $items);
152
        return $this;
153
    }
154
155
   /**
156
    * Retrieves a list of all extensions currently set as 
157
    * excluded from the search.
158
    * 
159
    * @return array
160
    */
161
    public function getExcludeExtensions() : array
162
    {
163
        return $this->getArrayOption('exclude-extensions');
164
    }
165
    
166
   /**
167
    * Excludes a single extension from the search.
168
    * 
169
    * @param string $extension Extension name, without dot (`php` for example).
170
    * @return FileHelper_FileFinder
171
    * @see FileHelper_FileFinder::excludeExtensions()
172
    */
173
    public function excludeExtension(string $extension) : FileHelper_FileFinder
174
    {
175
        return $this->excludeExtensions(array($extension));
176
    }
177
178
   /**
179
    * Add several extensions to the list of extensions to
180
    * exclude from the file search.
181
    *  
182
    * @param array $extensions Extension names, without dot (`php` for example).
183
    * @return FileHelper_FileFinder
184
    * @see FileHelper_FileFinder::excludeExtension()
185
    */
186
    public function excludeExtensions(array $extensions) : FileHelper_FileFinder
187
    {
188
        $items = $this->getExcludeExtensions();
189
        $items = array_merge($items, $extensions);
190
        $items = array_unique($items);
191
        
192
        $this->setOption('exclude-extensions', $items);
193
        return $this;
194
    }
195
    
196
   /**
197
    * In this mode, the entire path to the file will be stripped,
198
    * leaving only the file name in the files list.
199
    * 
200
    * @return FileHelper_FileFinder
201
    */
202
    public function setPathmodeStrip() : FileHelper_FileFinder
203
    {
204
        return $this->setPathmode(self::PATH_MODE_STRIP);
205
    }
206
    
207
   /**
208
    * In this mode, only the path relative to the source folder
209
    * will be included in the files list.
210
    * 
211
    * @return FileHelper_FileFinder
212
    */
213
    public function setPathmodeRelative() : FileHelper_FileFinder
214
    {
215
        return $this->setPathmode(self::PATH_MODE_RELATIVE);
216
    }
217
    
218
   /**
219
    * In this mode, the full, absolute paths to the files will
220
    * be included in the files list.
221
    * 
222
    * @return FileHelper_FileFinder
223
    */
224
    public function setPathmodeAbsolute() : FileHelper_FileFinder
225
    {
226
        return $this->setPathmode(self::PATH_MODE_ABSOLUTE);
227
    }
228
    
229
   /**
230
    * This sets a character or string to replace the slashes
231
    * in the paths with. 
232
    * 
233
    * This is used for example in the `getPHPClassNames()` 
234
    * method, to return files from subfolders as class names
235
    * using the "_" character:
236
    * 
237
    * Subfolder/To/File.php => Subfolder_To_File.php
238
    * 
239
    * @param string $character
240
    * @return \AppUtils\FileHelper_FileFinder
241
    */
242
    public function setSlashReplacement(string $character) : FileHelper_FileFinder
243
    {
244
        return $this->setOption('slash-replacement', $character);
245
    }
246
    
247
    protected function setPathmode($mode) : FileHelper_FileFinder
248
    {
249
        return $this->setOption('pathmode', $mode);
250
    }
251
    
252
   /**
253
    * Retrieves a list of all matching file names/paths,
254
    * depending on the selected options.
255
    * 
256
    * @return array
257
    */
258
    public function getAll() : array
259
    {
260
        $this->find($this->path, true);
261
        
262
        return $this->found;
263
    }
264
    
265
   /**
266
    * Retrieves only PHP files. Can be combined with other
267
    * options like enabling recursion into subfolders.
268
    * 
269
    * @return array
270
    */
271
    public function getPHPFiles() : array
272
    {
273
        $this->includeExtensions(array('php'));
274
        return $this->getAll();
275
    }
276
    
277
   /**
278
    * Generates PHP class names from file paths: it replaces
279
    * slashes with underscores, and removes file extensions.
280
    * 
281
    * @return array An array of PHP file names without extension.
282
    */
283
    public function getPHPClassNames() : array
284
    {
285
        $this->includeExtensions(array('php'));
286
        $this->stripExtensions();
287
        $this->setSlashReplacement('_');
288
        $this->setPathmodeRelative();
289
        
290
        return $this->getAll();
291
    }
292
    
293
    protected function find(string $path, bool $isRoot=false) : void
294
    {
295
        if($isRoot) {
296
            $this->found = array();
297
        }
298
        
299
        $recursive = $this->getBoolOption('recursive');
300
        
301
        $d = new \DirectoryIterator($path);
302
        foreach($d as $item)
303
        {
304
            $pathname = $item->getPathname();
305
            
306
            if($item->isDir())
307
            {
308
                if($recursive && !$item->isDot()) {
309
                    $this->find($pathname);
310
                }
311
                
312
                continue;
313
            }
314
            
315
            $file = $this->filterFile($pathname);
316
            
317
            if($file !== null) 
318
            {
319
                $this->found[] = $file;
320
            }
321
        }
322
    }
323
    
324
    protected function filterFile(string $path) : ?string
325
    {
326
        $path = FileHelper::normalizePath($path);
327
        
328
        $extension = FileHelper::getExtension($path);
329
        
330
        if(!$this->filterExclusion($extension)) {
331
            return null;
332
        }
333
        
334
        $path = $this->filterPath($path);
335
        
336
        if($this->getOption('strip-extensions') === true)
337
        {
338
            $path = str_replace('.'.$extension, '', $path);
339
        }
340
        
341
        if($path === '') {
342
            return null;
343
        }
344
        
345
        $replace = $this->getOption('slash-replacement');
346
        if(!empty($replace)) {
347
            $path = str_replace('/', $replace, $path);
348
        }
349
        
350
        return $path;
351
    }
352
    
353
   /**
354
    * Checks whether the specified extension is allowed 
355
    * with the current settings.
356
    * 
357
    * @param string $extension
358
    * @return bool
359
    */
360
    protected function filterExclusion(string $extension) : bool
361
    {
362
        $include = $this->getOption('include-extensions');
363
        $exclude = $this->getOption('exclude-extensions');
364
        
365
        if(!empty($include))
366
        {
367
            if(!in_array($extension, $include)) {
368
                return false;
369
            }
370
        }
371
        else if(!empty($exclude))
372
        {
373
            if(in_array($extension, $exclude)) {
374
                return false;
375
            }
376
        }
377
        
378
        return true;
379
    }
380
    
381
   /**
382
    * Adjusts the path according to the selected path mode.
383
    * 
384
    * @param string $path
385
    * @return string
386
    */
387
    protected function filterPath(string $path) : string
388
    {
389
        switch($this->getStringOption('pathmode'))
390
        {
391
            case self::PATH_MODE_STRIP:
392
                return basename($path);
393
                
394
            case self::PATH_MODE_RELATIVE:
395
                $path = str_replace($this->path, '', $path);
396
                return ltrim($path, '/');
397
        }
398
        
399
        return $path;
400
    }
401
}
402