FileClassesHelper   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 85
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 0

Test Coverage

Coverage 75%

Importance

Changes 0
Metric Value
wmc 16
lcom 0
cbo 0
dl 0
loc 85
ccs 27
cts 36
cp 0.75
rs 10
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
C getFileClasses() 0 73 16
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace PHPChunkit;
6
7
use PHP_Token_Stream;
8
9
/**
10
 * @testClass PHPChunkit\Test\FileClassesHelperTest
11
 */
12
class FileClassesHelper
13
{
14
    /**
15
     * Extract the classes in the given file
16
     *
17
     * Taken from https://github.com/composer/composer/blob/master/src/Composer/Autoload/ClassMapGenerator.php#L126
18
     *
19
     * @param  string            $path The file to check
20
     *
21
     * @return array             The found classes
22
     */
23 2
    public function getFileClasses(string $path) : array
24
    {
25 2
        $extraTypes = PHP_VERSION_ID < 50400 ? '' : '|trait';
26 2
        if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.3', '>=')) {
27
            $extraTypes .= '|enum';
28
        }
29
30
        // Use @ here instead of Silencer to actively suppress 'unhelpful' output
31
        // @link https://github.com/composer/composer/pull/4886
32 2
        $contents = @php_strip_whitespace($path);
33 2
        if (!$contents) {
34 1
            return [];
35
        }
36
37
        // return early if there is no chance of matching anything in this file
38 1
        if (!preg_match('{\b(?:class|interface'.$extraTypes.')\s}i', $contents)) {
39
            return array();
40
        }
41
42
        // strip heredocs/nowdocs
43 1
        $contents = preg_replace('{<<<\s*(\'?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\2(?=\r\n|\n|\r|;)}s', 'null', $contents);
44
        // strip strings
45 1
        $contents = preg_replace('{"[^"\\\\]*+(\\\\.[^"\\\\]*+)*+"|\'[^\'\\\\]*+(\\\\.[^\'\\\\]*+)*+\'}s', 'null', $contents);
46
        // strip leading non-php code if needed
47 1
        if (substr($contents, 0, 2) !== '<?') {
48
            $contents = preg_replace('{^.+?<\?}s', '<?', $contents, 1, $replacements);
49
            if ($replacements === 0) {
50
                return array();
51
            }
52
        }
53
        // strip non-php blocks in the file
54 1
        $contents = preg_replace('{\?>.+<\?}s', '?><?', $contents);
55
        // strip trailing non-php code if needed
56 1
        $pos = strrpos($contents, '?>');
57 1
        if (false !== $pos && false === strpos(substr($contents, $pos), '<?')) {
58
            $contents = substr($contents, 0, $pos);
59
        }
60
61 1
        preg_match_all('{
62
            (?:
63 1
                 \b(?<![\$:>])(?P<type>class|interface'.$extraTypes.') \s++ (?P<name>[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*+)
64
               | \b(?<![\$:>])(?P<ns>namespace) (?P<nsname>\s++[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\s*+\\\\\s*+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+)? \s*+ [\{;]
65
            )
66 1
        }ix', $contents, $matches);
67
68 1
        $classes = array();
69 1
        $namespace = '';
70
71 1
        for ($i = 0, $len = count($matches['type']); $i < $len; $i++) {
72 1
            if (!empty($matches['ns'][$i])) {
73 1
                $namespace = str_replace(array(' ', "\t", "\r", "\n"), '', $matches['nsname'][$i]).'\\';
74
            } else {
75 1
                $name = $matches['name'][$i];
76
                // skip anon classes extending/implementing
77 1
                if ($name === 'extends' || $name === 'implements') {
78
                    continue;
79
                }
80 1
                if ($name[0] === ':') {
81
                    // This is an XHP class, https://github.com/facebook/xhp
82
                    $name = 'xhp'.substr(str_replace(array('-', ':'), array('_', '__'), $name), 1);
83 1
                } elseif ($matches['type'][$i] === 'enum') {
84
                    // In Hack, something like:
85
                    //   enum Foo: int { HERP = '123'; }
86
                    // The regex above captures the colon, which isn't part of
87
                    // the class name.
88
                    $name = rtrim($name, ':');
89
                }
90 1
                $classes[] = ltrim($namespace.$name, '\\');
91
            }
92
        }
93
94 1
        return $classes;
95
    }
96
}
97