44
55namespace Arkitect \Analyzer ;
66
7+ use Arkitect \Rules \ParsingError ;
78use PhpParser \Node ;
89use PhpParser \Node \NullableType ;
910use PhpParser \NodeVisitorAbstract ;
@@ -15,13 +16,19 @@ class FileVisitor extends NodeVisitorAbstract
1516 /** @var array<ClassDescription> */
1617 private array $ classDescriptions = [];
1718
19+ /** @var array<ParsingError> */
20+ private array $ parsingErrors = [];
21+
22+ private ?string $ filePath = null ;
23+
1824 public function __construct (ClassDescriptionBuilder $ classDescriptionBuilder )
1925 {
2026 $ this ->classDescriptionBuilder = $ classDescriptionBuilder ;
2127 }
2228
2329 public function setFilePath (?string $ filePath ): void
2430 {
31+ $ this ->filePath = $ filePath ;
2532 $ this ->classDescriptionBuilder ->setFilePath ($ filePath );
2633 }
2734
@@ -83,9 +90,18 @@ public function getClassDescriptions(): array
8390 return $ this ->classDescriptions ;
8491 }
8592
93+ /**
94+ * @return array<ParsingError>
95+ */
96+ public function getParsingErrors (): array
97+ {
98+ return $ this ->parsingErrors ;
99+ }
100+
86101 public function clearParsedClassDescriptions (): void
87102 {
88103 $ this ->classDescriptions = [];
104+ $ this ->parsingErrors = [];
89105 $ this ->classDescriptionBuilder ->setFilePath (null );
90106 $ this ->classDescriptionBuilder ->clear ();
91107 }
@@ -187,7 +203,7 @@ private function handleEnumNode(Node $node): void
187203
188204 // Resolve inherited interfaces from directly implemented interfaces
189205 foreach ($ node ->implements as $ interface ) {
190- $ this ->addReflectedInterfaceParents ($ interface ->toString ());
206+ $ this ->addReflectedInterfaceParents ($ interface ->toString (), $ interface -> getLine () );
191207 }
192208 }
193209
@@ -305,7 +321,7 @@ private function handleInterfaceNode(Node $node): void
305321
306322 // Resolve ancestor interfaces from directly extended interfaces
307323 foreach ($ node ->extends as $ interface ) {
308- $ this ->addReflectedExtendedInterfaceParents ($ interface ->toString ());
324+ $ this ->addReflectedExtendedInterfaceParents ($ interface ->toString (), $ interface -> getLine () );
309325 }
310326 }
311327
@@ -418,7 +434,7 @@ private function resolveInheritedInterfacesAndExtends(Node\Stmt\Class_ $node): v
418434 {
419435 // Resolve inherited interfaces from directly implemented interfaces
420436 foreach ($ node ->implements as $ interface ) {
421- $ this ->addReflectedInterfaceParents ($ interface ->toString ());
437+ $ this ->addReflectedInterfaceParents ($ interface ->toString (), $ interface -> getLine () );
422438 }
423439
424440 // Resolve inherited interfaces and ancestor classes from parent class
@@ -427,58 +443,68 @@ private function resolveInheritedInterfacesAndExtends(Node\Stmt\Class_ $node): v
427443 }
428444
429445 $ parentClassName = $ node ->extends ->toString ();
446+ $ line = $ node ->extends ->getLine ();
430447
431448 try {
432449 /** @var class-string $parentClassName */
433450 $ reflection = new \ReflectionClass ($ parentClassName );
434451
435452 foreach ($ reflection ->getInterfaceNames () as $ interfaceName ) {
436- $ this ->classDescriptionBuilder ->addReflectedInterface ($ interfaceName );
453+ $ this ->classDescriptionBuilder ->addReflectedInterface ($ interfaceName, $ line );
437454 }
438455
439456 $ ancestor = $ reflection ->getParentClass ();
440457 while (false !== $ ancestor ) {
441- $ this ->classDescriptionBuilder ->addReflectedExtends ($ ancestor ->getName ());
458+ $ this ->classDescriptionBuilder ->addReflectedExtends ($ ancestor ->getName (), $ line );
442459 $ ancestor = $ ancestor ->getParentClass ();
443460 }
444461 } catch (\ReflectionException $ e ) {
445- // Parent class not autoloadable, skip reflection-based resolution
462+ $ this ->parsingErrors [] = ParsingError::create (
463+ $ this ->filePath ?? '' ,
464+ "Reflection error: {$ e ->getMessage ()}. Ensure the class is autoloaded. "
465+ );
446466 }
447467 }
448468
449469 /**
450470 * Use reflection to discover parent interfaces of a given interface,
451471 * adding them to the interfaces list (for classes and enums).
452472 */
453- private function addReflectedInterfaceParents (string $ interfaceName ): void
473+ private function addReflectedInterfaceParents (string $ interfaceName, int $ line ): void
454474 {
455475 try {
456476 /** @var class-string $interfaceName */
457477 $ reflection = new \ReflectionClass ($ interfaceName );
458478
459479 foreach ($ reflection ->getInterfaceNames () as $ parentInterfaceName ) {
460- $ this ->classDescriptionBuilder ->addReflectedInterface ($ parentInterfaceName );
480+ $ this ->classDescriptionBuilder ->addReflectedInterface ($ parentInterfaceName, $ line );
461481 }
462482 } catch (\ReflectionException $ e ) {
463- // Interface not autoloadable, skip
483+ $ this ->parsingErrors [] = ParsingError::create (
484+ $ this ->filePath ?? '' ,
485+ "Reflection error: {$ e ->getMessage ()}. Ensure the class is autoloaded. "
486+ );
464487 }
465488 }
466489
467490 /**
468491 * Use reflection to discover parent interfaces of a given interface,
469492 * adding them to the extends list (for interface definitions).
470493 */
471- private function addReflectedExtendedInterfaceParents (string $ interfaceName ): void
494+ private function addReflectedExtendedInterfaceParents (string $ interfaceName, int $ line ): void
472495 {
473496 try {
474497 /** @var class-string $interfaceName */
475498 $ reflection = new \ReflectionClass ($ interfaceName );
476499
477500 foreach ($ reflection ->getInterfaceNames () as $ parentInterfaceName ) {
478- $ this ->classDescriptionBuilder ->addReflectedExtends ($ parentInterfaceName );
501+ $ this ->classDescriptionBuilder ->addReflectedExtends ($ parentInterfaceName, $ line );
479502 }
480503 } catch (\ReflectionException $ e ) {
481- // Interface not autoloadable, skip
504+ $ this ->parsingErrors [] = ParsingError::create (
505+ $ this ->filePath ?? '' ,
506+ "Reflection error: {$ e ->getMessage ()}. Ensure the class is autoloaded. "
507+ );
482508 }
483509 }
484510}
0 commit comments