88use  PHPStan \BetterReflection \Reflector \Reflector ;
99use  PHPStan \BetterReflection \SourceLocator \Ast \Strategy \NodeToReflection ;
1010use  PHPStan \BetterReflection \SourceLocator \Type \SourceLocator ;
11+ use  PHPStan \Php \PhpVersion ;
1112use  function  array_key_exists ;
1213
1314class  OptimizedDirectorySourceLocator implements  SourceLocator
1415{
1516
1617	private  \PHPStan \Reflection \BetterReflection \SourceLocator \FileNodesFetcher   $ fileNodesFetcher
1718
19+ 	private  PhpVersion $ phpVersion
20+ 
21+ 	private  PhpFileCleaner $ cleaner
22+ 
1823	/** @var string[] */ 
1924	private  array  $ files
2025
26+ 	private  string  $ extraTypes
27+ 
2128	/** @var array<string, string>|null */ 
2229	private  ?array  $ classToFilenull ;
2330
@@ -39,11 +46,24 @@ class OptimizedDirectorySourceLocator implements SourceLocator
3946	 */ 
4047	public  function  __construct (
4148		FileNodesFetcher $ fileNodesFetcher
49+ 		PhpVersion $ phpVersion
4250		array  $ files
4351	)
4452	{
4553		$ this fileNodesFetcher  = $ fileNodesFetcher
54+ 		$ this phpVersion  = $ phpVersion
4655		$ this files  = $ files
56+ 
57+ 		$ extraTypes'' ;
58+ 		$ extraTypesArray
59+ 		if  ($ this phpVersion ->supportsEnums ()) {
60+ 			$ extraTypes'|enum ' ;
61+ 			$ extraTypesArray'enum ' ;
62+ 		}
63+ 
64+ 		$ this extraTypes  = $ extraTypes
65+ 
66+ 		$ this cleaner  = new  PhpFileCleaner (array_merge (['class ' , 'interface ' , 'trait ' , 'function ' ], $ extraTypesArray
4767	}
4868
4969	public  function  locateIdentifier (Reflector $ reflectorIdentifier $ identifierReflection 
@@ -197,79 +217,18 @@ private function findSymbols(string $file): array
197217			return  ['classes '  => [], 'functions '  => []];
198218		}
199219
200- 		if  (!preg_match ( '{\b(?:class|interface|trait|function)\s}i ' , $ contents
220+ 		if  (!preg_match_all ( sprintf ( '{\b(?:class|interface|trait|function%s )\s}i ' , $ this -> extraTypes ),  $ contents,  $ matches 
201221			return  ['classes '  => [], 'functions '  => []];
202222		}
203223
204- 		// strip heredocs/nowdocs 
205- 		$ heredocRegex'{  
206-             # opening heredoc/nowdoc delimiter (word-chars) 
207-             <<<[ \t]*+([ \'"]?)(\w++) \\1 
208-             # needs to be followed by a newline 
209-             (?:\r\n|\n|\r) 
210-             # the meat of it, matching line by line until end delimiter 
211-             (?: 
212-                 # a valid line is optional white-space (possessive match) not followed by the end delimiter, then anything goes for the rest of the line 
213-                 [\t ]*+(?! \\2 \b)[^\r\n]*+ 
214-                 # end of line(s) 
215-                 [\r\n]++ 
216-             )* 
217-             # end delimiter 
218-             [\t ]*+  \\2 (?=\b) 
219-         }x '
220- 
221- 		// run first assuming the file is valid unicode 
222- 		$ contentWithoutHeredocpreg_replace ($ heredocRegex'u ' , 'null ' , $ contents
223- 		if  ($ contentWithoutHeredocnull ) {
224- 			// run again without unicode support if the file failed to be parsed 
225- 			$ contentspreg_replace ($ heredocRegex'null ' , $ contents
226- 		} else  {
227- 			$ contents$ contentWithoutHeredoc
228- 		}
229- 		unset($ contentWithoutHeredoc
230- 
231- 		if  ($ contentsnull ) {
232- 			return  ['classes '  => [], 'functions '  => []];
233- 		}
234- 		// strip strings 
235- 		$ contentspreg_replace ('{"[^" \\\\]*+( \\\\.[^" \\\\]*+)*+"| \'[^ \'\\\\]*+( \\\\.[^ \'\\\\]*+)*+ \'}s ' , 'null ' , $ contents
236- 		if  ($ contentsnull ) {
237- 			return  ['classes '  => [], 'functions '  => []];
238- 		}
239- 		// strip leading non-php code if needed 
240- 		if  (strpos ($ contents'<? ' ) !== 0 ) {
241- 			$ contentspreg_replace ('{^.+?<\?}s ' , '<? ' , $ contents1 , $ replacements
242- 			if  ($ contentsnull ) {
243- 				return  ['classes '  => [], 'functions '  => []];
244- 			}
245- 			if  ($ replacements0 ) {
246- 				return  ['classes '  => [], 'functions '  => []];
247- 			}
248- 		}
249- 		// strip non-php blocks in the file 
250- 		$ contentspreg_replace ('{\?>(?:[^<]++|<(?!\?))*+<\?}s ' , '?><? ' , $ contents
251- 		if  ($ contentsnull ) {
252- 			return  ['classes '  => [], 'functions '  => []];
253- 		}
254- 		// strip trailing non-php code if needed 
255- 		$ posstrrpos ($ contents'?> ' );
256- 		if  ($ posfalse  && strpos (substr ($ contents$ pos'<? ' ) === false ) {
257- 			$ contentssubstr ($ contents0 , $ pos
258- 		}
259- 		// strip comments if short open tags are in the file 
260- 		if  (preg_match ('{(<\?)(?!(php|hh))}i ' , $ contents
261- 			$ contentspreg_replace ('{//.* | /\*(?:[^*]++|\*(?!/))*\*/}x ' , '' , $ contents
262- 			if  ($ contentsnull ) {
263- 				return  ['classes '  => [], 'functions '  => []];
264- 			}
265- 		}
224+ 		$ contents$ this cleaner ->clean ($ contentscount ($ matches0 ]));
266225
267- 		preg_match_all ('{  
226+ 		preg_match_all (sprintf ( '{  
268227            (?: 
269-                  \b(?<![\$:>])(?P<type>class|interface|trait|function) \s++ (?P<byref>&\s*)? (?P<name>[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*+) 
228+                  \b(?<![\$:>])(?P<type>class|interface|trait|function%s ) \s++ (?P<byref>&\s*)? (?P<name>[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*+) 
270229               | \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*+ [\{;] 
271230            ) 
272-         }ix '$ contents$ matches
231+         }ix '$ this -> extraTypes ),  $ contents$ matches
273232
274233		$ classes
275234		$ functions
0 commit comments