|
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
|
|
|
|