GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (423)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

myth/Docs/Builder.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php namespace Myth\Docs;
2
/**
3
 * Sprint
4
 *
5
 * A set of power tools to enhance the CodeIgniter framework and provide consistent workflow.
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
 * THE SOFTWARE.
24
 *
25
 * @package     Sprint
26
 * @author      Lonnie Ezell
27
 * @copyright   Copyright 2014-2015, New Myth Media, LLC (http://newmythmedia.com)
28
 * @license     http://opensource.org/licenses/MIT  (MIT)
29
 * @link        http://sprintphp.com
30
 * @since       Version 1.0
31
 */
32
33
use Myth\Docs\DocBuilderInterface;
34
35
/**
36
 * Class Builder
37
 *
38
 * Handles the brunt of building documentation from Markdown formatted files.
39
 *
40
 * @package Myth\Docs
41
 */
42
class Builder implements DocBuilderInterface
43
{
44
45
    protected $docs_ext = '.md';
46
47
    protected $ignore_files = ['_404.md'];
48
49
    protected $doc_folders = [];
50
51
    /**
52
     * Stores the current folder alias,
53
     * once the file has been found.
54
     *
55
     * @var null
56
     */
57
    protected $current_folder = null;
58
59
    protected $table_classes = 'table table-hover';
60
61
    protected $apppath = '';
62
63
    protected $formatters = [];
64
65
    protected $page_title = null;
66
67
    //--------------------------------------------------------------------
68
69
    public function __construct($config = array())
70
    {
71
        $this->apppath = ! empty($config['apppath']) ? rtrim($config['apppath'], '/') . '/' : '';
72
    }
73
74
    //--------------------------------------------------------------------
75
76
    public function pageTitle()
77
    {
78
        return $this->page_title;
79
    }
80
81
    //--------------------------------------------------------------------
82
83
84
85
    /**
86
     * Does the actual work of reading in and parsing the help file.
87
     * If a folder Nickname (see addDocFolder() ) is passed as the second parameter,
88
     * it will limit it's search to that single folder. If nothing is passed, it will
89
     * search through all of the folders in the order they were given to the library,
90
     * until it finds the first one.
91
     *
92
     * @param string $path The 'path' of the file (relative to the docs
93
     *                                 folder. Usually from the URI)
94
     * @param string $restrictToFolder (Optional) The folder nickname
95
     *
96
     * @return string
97
     */
98
    public function readPage($path, $restrictToFolder = null)
99
    {
100
        // Clean up our path
101
        $path = trim($path, '/ ');
102
103
        $content = $this->locateAndReadFile($path, $restrictToFolder);
104
105
        $content = $this->parse($content);
106
107
        return $content;
108
    }
109
110
    //--------------------------------------------------------------------
111
112
    /**
113
     * Parses the contents. Currently runs through the Markdown Extended
114
     * parser to convert to HTML.
115
     *
116
     * @param $str
117
     * @return mixed
118
     */
119
    public function parse($str)
120
    {
121
        return $this->format($str);
122
    }
123
124
    //--------------------------------------------------------------------
125
126
    /**
127
     * Perform a few housekeeping tasks on a page, like rewriting URLs to full
128
     * URLs, not relative, ensuring they link correctly, etc.
129
     *
130
     * @param      $content
131
     * @param null $site_url
132
     * @param null $current_url
133
     * @return string   The post-processed HTML.
134
     */
135
    public function postProcess($content, $site_url = null, $current_url = null)
136
    {
137
        if (empty($content)) {
138
            return $content;
139
        }
140
141
        try {
142
            $xml = new \SimpleXMLElement('<?xml version="1.0" standalone="yes"?><div>' . $content . '</div>');
143
        } catch (\Exception $e) {
144
            // SimpleXML barfed on us, so send back the un-modified content
145
            return $content;
146
        }
147
148
        // Prepare some things and cleanup others
149
        $groups = array_keys($this->doc_folders);
150
        $site_url = rtrim($site_url, '/') . '/';
151
        $current_url = rtrim($current_url, '#/');
152
153
        // Try to determine the current_url if one isn't set.
154
        if (empty($this->current_folder)) {
155
            $this->current_folder = $this->detectCurrentFolder($current_url, $groups);
156
        }
157
158
        /*
159
         * Rewrite the URLs
160
         */
161
        foreach ($xml->xpath('//a') as $link) {
162
            $link = $this->reformatAnchor($link, $groups, $current_url, $site_url);
163
        }
164
165
        $content = $xml->asXML();
166
        $content = trim(str_replace('<?xml version="1.0" standalone="yes"?>', '', $content));
167
168
        // Clean up and style the tables
169
        $content = str_replace('<table>', '<table class="' . $this->table_classes . '">', $content);
170
171
        return $content;
172
    }
173
    //--------------------------------------------------------------------
174
175
    /**
176
     * Allows users to define the classes that are attached to
177
     * generated tables.
178
     *
179
     * @param null $classes
180
     * @return $this
181
     */
182
    public function setTableClasses($classes = null)
183
    {
184
        $this->table_classes = $classes;
185
186
        return $this;
187
    }
188
189
    //--------------------------------------------------------------------
190
191
    /**
192
     * Given the contents to render, will build a list of links for the sidebar
193
     * out of the headings in the file.
194
     *
195
     * Note: Will ONLY use h2 and h3 to build the links from.
196
     *
197
     * Note: The $content passed in WILL be modified by adding named anchors
198
     * that match up with the locations.
199
     *
200
     * @param string $content The HTML to analyse for headings.
201
     * @return string
202
     */
203
    public function buildDocumentMap(&$content)
204
    {
205
        if (empty($content)) {
206
            return $content;
207
        }
208
209
        // If $content already has a wrapping <div> and </div> tags, remove them,
210
        // since we'll replace them just below.
211
        if (strpos($content, '<div>') === 0) {
212
            $content = substr($content, 5);
213
214
            // Trailing div also?
215
            if (substr($content, -6) == '</div>') {
216
                $content = substr($content, 0, -6);
217
            }
218
        }
219
220
        try {
221
            $xml = new \SimpleXMLElement('<?xml version="1.0" standalone="yes"?><div>' . $content . '</div>');
222
        } catch (\Exception $e) {
223
            // SimpleXML barfed on us, so send back the un-modified content
224
            return [];
225
        }
226
227
        $map = [];
228
        list($map, $content) = $this->extractDocMapAndAddAnchors($content, $xml, $map);
229
230
        return $map;
231
    }
232
233
    //--------------------------------------------------------------------
234
235
    /**
236
     * Stores the name of the callback method to run to convert the source
237
     * files to viewable files. By default, this should be used to register
238
     * a Mardown Extended formatter with the system, but could be used to
239
     * extend the
240
     *
241
     * @param string $callback
242
     * @param bool $cascade // If FALSE the formatting of a component ends here. If TRUE, will be passed to next formatter.
243
     * @return $this
244
     */
245
    public function registerFormatter($callback = null, $cascade = false)
246
    {
247
        if (empty($callback)) return;
248
249
        $this->formatters[] = [
250
            'callable' => $callback,
251
            'cascade'  => (bool)$cascade
252
        ];
253
254
        return $this;
255
    }
256
257
    //--------------------------------------------------------------------
258
259
    /**
260
     * Runs the text through the registered formatters.
261
     *
262
     * @param $str
263
     * @return mixed
264
     */
265 View Code Duplication
    public function format($str)
266
    {
267
        if (! is_array($this->formatters)) return $str;
268
269
        foreach ($this->formatters as $formatter) {
270
            $method = $formatter['callable'];
271
            $cascade = $formatter['cascade'];
272
273
            $str = call_user_func($method, $str);
274
275
            if (! $cascade) return $str;
276
        }
277
278
        return $str;
279
    }
280
281
    //--------------------------------------------------------------------
282
283
    //--------------------------------------------------------------------
284
    // Table of Contents methods
285
    //--------------------------------------------------------------------
286
287
    /**
288
     * Retrieves the list of files in a folder and preps the name and filename
289
     * so it's ready for creating the HTML.
290
     *
291
     * @param  String $folder The path to the folder to retrieve.
292
     *
293
     * @return Array  An associative array @see parse_ini_file for format
294
     * details.
295
     */
296
    public function buildTOC($folder)
297
    {
298
        // If the toc file exists in the folder, use it to build the links.
299
        if (is_file("{$folder}/_toc.ini")) {
300
            $toc = parse_ini_file("{$folder}/_toc.ini", true);
301
            return $this->columnizeTOC($toc);
302
        }
303
304
        // If the toc file does not exist, build the links by listing the files
305
        // in the directory (and any sub-directories)
306
        $map = $this->directory_map($folder);
307
308
        // If directory_map can not open the directory or find any files inside
309
        // the directory, return an empty array.
310
        if (empty($map)) {
311
            return [];
312
        }
313
314
        // If these docs are located in the /application/docs or /bonfire/docs
315
        // directory, just use $this->current_group for the root.
316
        // Module docs need $this->current_group and $type.
317
        $tocRoot = $this->current_folder;
318
        if ($this->current_folder != strtolower($folder)) {
319
            $tocRoot .= '/' . strtolower($folder);
320
        }
321
322
        $toc = [];
323
        foreach ($map as $files) {
324
            // If $files isn't an array, then make it one so that all situations
325
            // may be dealt with cleanly.
326
            if (! is_array($files)) {
327
                $files = [$files];
328
            }
329
330
            foreach ($files as $file) {
331
                if (in_array($file, $this->ignore_files)) {
332
                    continue;
333
                }
334
335
                // The title for the index is the passed $type. Otherwise,
336
                // build the title from the file's name.
337
                if (strpos($file, 'index') === false) {
338
                    $title = str_replace($this->docs_ext, '', $file);
339
                    $title = str_replace('_', ' ', $title);
340
                    $title = ucwords($title);
341
342
                    $toc["{$tocRoot}/{$file}"] = $title;
343
                } else {
344
                    $toc[$tocRoot] = $type;
345
                }
346
            }
347
        }
348
349
        $toc = $this->columnizeTOC($toc);
350
351
        return $toc;
352
    }
353
354
    //--------------------------------------------------------------------
355
356
    /**
357
     * Sorts the passed TOC array into columns of as close to equal length
358
     * as we can get it.
359
     *
360
     * @param $toc
361
     * @return array
362
     */
363
    protected function columnizeTOC($toc)
364
    {
365
        $section_count = count($toc);
366
367
        // First - determine the size of each 'section'.
368
        $sizes = [];
369
370
        foreach ($toc as $section => $chapters) {
371
            $sizes[] = count($chapters);
372
        }
373
374
        $column_avg = (int)round(array_sum($sizes) / $section_count);
375
376
        // Split things into 4 columns of approximately equal size.
377
        // If we only have 4 columns (or less), then make sure to
378
        // deal with that also.
379
        $columns = [];
380
381
        $current_column = 0;
382
        $current_column_count = 0;
383
        $keys = array_keys($toc);
384
385
        for ($i = 0; $i <= $section_count; $i++) {
386
            if (! isset($keys[$i])) {
387
                continue;
388
            }
389
390
            $section = array_shift($toc);
391
392
            // Can we stay in this column?
393
            if ($current_column_count <= $column_avg && $section_count > 4) {
394
                // Don't forget to account for the heading also.
395
                $current_column_count += count($section) + 1;
396
            } else {
397
                $current_column_count = 0;
398
                $current_column++;
399
            }
400
401
            $columns[$current_column][$keys[$i]] = $section;
402
        }
403
404
        return $columns;
405
    }
406
407
    //--------------------------------------------------------------------
408
409
    //--------------------------------------------------------------------
410
    // Folder Methods
411
    //--------------------------------------------------------------------
412
413
    /**
414
     * Returns the current docFolders array.
415
     *
416
     * @return array
417
     */
418
    public function docFolders()
419
    {
420
        return $this->doc_folders;
421
    }
422
423
    //--------------------------------------------------------------------
424
425
    /**
426
     * Registers a path to be used when searching for documentation files.
427
     *
428
     * @param $name     A nickname to reference it by later.
429
     * @param $path     The server path to the folder.
430
     * @return $this
431
     */
432
    public function addDocFolder($name, $path)
433
    {
434
        // Standardize the path
435
        $path = realpath($path) . '/';
436
437
        // realpath will return FALSE if the path doesn't exist
438
        // or the script doesn't have access to it.
439
        if (! $path || $path == '/') {
440
            return $this;
441
        }
442
443
        $name = strtolower($name);
444
445
        $this->doc_folders[$name] = $path;
446
447
        return $this;
448
    }
449
450
    //--------------------------------------------------------------------
451
452
    /**
453
     * Removes a folder from the folders we scan for documentation files
454
     * within.
455
     *
456
     * @param $name
457
     * @return $this
458
     */
459
    public function removeDocFolder($name)
460
    {
461
        $name = strtolower($name);
462
463
        if (isset($this->doc_folders[$name])) {
464
            unset($this->doc_folders[$name]);
465
        }
466
467
        return $this;
468
    }
469
470
    //--------------------------------------------------------------------
471
472
    //--------------------------------------------------------------------
473
    // Private Methods
474
    //--------------------------------------------------------------------
475
476
    /**
477
     * Analyzes the passed in current url string and checks against
478
     * a list of groups to determine what the current group is.
479
     *
480
     * @param $current_url
481
     * @param $groups
482
     * @return string
483
     */
484
    protected function detectCurrentFolder($current_url, $groups = [])
485
    {
486
        if (! is_array($groups)) {
487
            return null;
488
        }
489
490
        $segments = explode('/', $current_url);
491
492
        // We start from the back of the array since
493
        // that's most likely to be close to the end.
494
        $segments = array_reverse($segments);
495
496
        foreach ($segments as $segment) {
497
            foreach ($groups as $group) {
498
                if (strtolower($group) == strtolower($segment)) {
499
                    return $group;
500
                }
501
            }
502
        }
503
504
        // Nothing found?
505
        return null;
506
    }
507
508
    //--------------------------------------------------------------------
509
510
    //--------------------------------------------------------------------
511
    // Private Methods
512
    //--------------------------------------------------------------------
513
514
    /**
515
     * Locates the file on disk and reads the contents into a single string.
516
     *
517
     * If a folder Nickname (see addDocFolder() ) is passed as the second parameter,
518
     * it will limit it's search to that single folder. If nothing is passed, it will
519
     * search through all of the folders in the order they were given to the library,
520
     * until it finds the first one.
521
     *
522
     * @param string $path The 'path' of the file (relative to the docs
523
     *                                 folder. Usually from the URI)
524
     * @param string $restrictToFolder (Optional) The nickname of one of the
525
     *                                 folders to restrict the search to.
526
     *
527
     * @throws RuntimeException
528
     * @return null|string
529
     */
530
    private function locateAndReadFile($path, $restrictToFolder = null)
531
    {
532
        $folders = $this->doc_folders;
533
534
        if (! is_null($restrictToFolder)) {
535
            // Make sure the folder exists
536
            if (! is_null($restrictToFolder) && ! isset($this->doc_folders[$restrictToFolder])) {
537
                throw new \RuntimeException('You must add the docs folder that you wish to find docs from.');
538
            }
539
540
            $folders = [$this->doc_folders[$restrictToFolder]];
541
        }
542
543
        foreach ($folders as $alias => $folder) {
544
            if (file_exists($folder . $path . $this->docs_ext)) {
545
                // Store the alias so we know which folder we're in.
546
                $this->current_folder = $alias;
547
548
                return file_get_contents($folder . $path . $this->docs_ext);
549
            }
550
        }
551
552
        return null;
553
    }
554
555
    //--------------------------------------------------------------------
556
557
    /**
558
     * Re-formats the passed in link.
559
     *
560
     * @param $link
561
     * @param $current_url
562
     * @param $site_url
563
     * @return mixed
564
     */
565
    private function reformatAnchor($link, $groups, $current_url, $site_url)
566
    {
567
        // Grab the href value.
568
        $href = $link->attributes()->href;
569
570
        // If the href is null, it's probably a named anchor with no content.
571
        if (! $href) {
572
            // Make sure it has an href, else the XML will not close this
573
            // tag correctly.
574
            $link['href'] = ' ';
575
576
            return $link;
577
        }
578
579
        // Remove any trailing # signs
580
        $href = rtrim($href, '# ');
581
582
        // If the href starts with #, then attach the current_url to it
583
        if ($href != '' && substr_compare($href, '#', 0, 1) === 0) {
584
            $link['href'] = $current_url . $href;
585
586
            return $link;
587
        }
588
589
        // If it's a full external path, go on...
590
        if ((strpos($href, 'http://') !== false || strpos($href, 'https://') !== false) &&
591
            strpos($href, $site_url) === false
592
        ) {
593
            $link['target'] = "_blank";
594
            return $link;
595
        }
596
597
        // If it's a full local path, get rid of it.
598
        if ($site_url !== "/" && strpos($href, $site_url) !== false) {
599
            $href = str_replace($site_url, '', $href);
600
        }
601
602
        // Strip out some unnecessary items, just in case they're there.
603
        if (substr($href, 0, strlen('docs/')) == 'docs/') {
604
            $href = substr($href, strlen('docs/'));
605
        }
606
607
        // This includes 'bonfire/' if it was missed during the conversion.
608
        if (substr($href, 0, strlen('bonfire/')) == 'bonfire/') {
609
            $href = substr($href, strlen('bonfire/'));
610
        }
611
612
        // If another 'group' is not already defined at the head of the link
613
        // then add the current group to it.
614
        $group_found = false;
615
616
        foreach ($groups as $group) {
617
            if (strpos($href, $group) === 0) {
618
                $group_found = true;
619
            }
620
        }
621
622
        if (! $group_found) {
623
            $href = $this->current_folder . '/' . $href;
624
        }
625
626
        // Convert to full site_url
627
        if (strpos($href, 'http') !== 0) {
628
            $href = $site_url . 'docs/' . ltrim($href, '/ ');
629
        }
630
631
        // Save the corrected href
632
        $link['href'] = $href;
633
634
        return $link;
635
    }
636
637
    //--------------------------------------------------------------------
638
639
    /**
640
     * Creates a Document Map based on <h2> and <h3> tags.
641
     * Also adds named anchors into the $content so the map
642
     * can link to the content properly.
643
     *
644
     * @param $content
645
     * @param $xml
646
     * @param $map
647
     * @return array
648
     */
649
    protected function extractDocMapAndAddAnchors(&$content, $xml, $map)
650
    {
651
        // Holds the current h2 we're processing
652
        $current_obj = [];
653
654
        $currentChild = 0;
655
656
        foreach ($xml->children() as $childType => $line) {
657
            $currentChild++;
658
659
            // If it's an h1 - take the first and make it
660
            // our page title.
661
            if ($childType == 'h1' && empty($this->page_title))
662
            {
663
                $this->page_title = (string)$line;
664
            }
665
666
            // Make sure that our current object is
667
            // stored and reset.
668
            if ($childType == 'h1' || $childType == 'h2') {
669
                if (count($current_obj)) {
670
                    $map[] = $current_obj;
671
                    $current_obj = [];
672
                }
673
            }
674
675
            if ($childType == 'h2') {
676
                $name = (string)$line;
677
                $link = strtolower(str_replace(' ', '_', (string)$line));
678
679
                $current_obj['name'] = $name;
680
                $current_obj['link'] = '#' . $link;
681
                $current_obj['items'] = [];
682
683
                // Insert a named anchor into the $content
684
                $anchor = '<a name="' . $link . '" id="' . $link . '" ></a>';
685
686
                $search = "<h2>{$name}</h2>";
687
688
                $content = str_replace($search, $anchor . $search, $content);
689
            } elseif ($childType == 'h3') {
690
                // Make sure we have some place to store the items.
691
                if (! isset($current_obj['items'])) {
692
                    $current_obj['items'] = [];
693
                }
694
695
                $link = strtolower(str_replace(' ', '_', (string)$line));
696
                $name = (string)$line;
697
698
                $current_obj['items'][] = [
699
                    'name' => $name,
700
                    'link' => '#' . $link
701
                ];
702
703
                // Insert a named anchor into the $content
704
                $anchor = '<a name="' . $link . '" id="' . $link . '" ></a>';
705
706
                $search = "<h3>{$name}</h3>";
707
708
                $content = str_replace($search, $anchor . $search, $content);
709
            }
710
711
            // Is this the last element? Then close out our current object.
712
            if (count($xml) == $currentChild) {
713
                if (count($current_obj)) {
714
                    $map[] = $current_obj;
715
                }
716
            }
717
        }
718
        return [$map, $content];
719
    }
720
    //--------------------------------------------------------------------
721
722
    /**
723
     * Create a Directory Map
724
     *
725
     * Reads the specified directory and builds an array
726
     * representation of it. Sub-folders contained with the
727
     * directory will be mapped as well.
728
     *
729
     * @param    string $source_dir Path to source
730
     * @param    int $directory_depth Depth of directories to traverse
731
     *                        (0 = fully recursive, 1 = current dir, etc)
732
     * @param    bool $hidden Whether to show hidden files
733
     * @return    array
734
     */
735 View Code Duplication
    protected function directory_map($source_dir, $directory_depth = 0, $hidden = FALSE)
736
    {
737
        if ($fp = @opendir($source_dir)) {
738
            $filedata = array();
739
            $new_depth = $directory_depth - 1;
740
            $source_dir = rtrim($source_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
741
742
            while (FALSE !== ($file = readdir($fp))) {
743
                // Remove '.', '..', and hidden files [optional]
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
744
                if ($file === '.' OR $file === '..' OR ($hidden === FALSE && $file[0] === '.')) {
745
                    continue;
746
                }
747
748
                is_dir($source_dir . $file) && $file .= DIRECTORY_SEPARATOR;
749
750
                if (($directory_depth < 1 OR $new_depth > 0) && is_dir($source_dir . $file)) {
751
                    $filedata[$file] = directory_map($source_dir . $file, $new_depth, $hidden);
752
                } else {
753
                    $filedata[] = $file;
754
                }
755
            }
756
757
            closedir($fp);
758
            return $filedata;
759
        }
760
761
        return FALSE;
762
    }
763
764
    //--------------------------------------------------------------------
765
766
}
767