44
55namespace TypePHP \Internal ;
66
7+ use ReflectionClass ;
8+ use Throwable ;
9+ use TypeError ;
10+
711/**
812 * @internal Factory creating ErrorMessage value objects and preparing TypeError instances with exact caller traces.
913 */
@@ -23,22 +27,21 @@ public static function createError(string $message): ErrorMessage
2327
2428 /**
2529 * Prepares a TypeError exception before throwing.
26- * For parameter and callback argument errors, it filters out internal library frames
30+ * For parameter, callback, iterator, and generator errors, it filters out internal library frames
2731 * and sets the file and line to accurately blame the caller site.
2832 */
29- public static function prepareException (\ TypeError $ e , ?int $ line = null ): \ TypeError
33+ public static function prepareException (TypeError $ e , ?int $ line = null ): TypeError
3034 {
31- $ ref = new \ReflectionObject ($ e );
32-
33- if ($ line !== null && $ ref ->hasProperty ('line ' )) {
34- $ propLine = $ ref ->getProperty ('line ' );
35- $ propLine ->setValue ($ e , $ line );
36- }
35+ $ targetFile = null ;
36+ $ targetLine = $ line ;
3737
3838 $ message = $ e ->getMessage ();
3939 $ isCallSiteError = str_contains ($ message , 'Argument $ ' )
4040 || str_contains ($ message , 'argument # ' )
41- || str_contains ($ message , 'Callback argument ' );
41+ || str_contains ($ message , 'Callback ' )
42+ || str_contains ($ message , 'Iterator $ ' )
43+ || str_contains ($ message , 'Return iterator ' )
44+ || str_contains ($ message , 'Generator sent value ' );
4245
4346 if ($ isCallSiteError ) {
4447 $ trace = $ e ->getTrace ();
@@ -47,15 +50,16 @@ public static function prepareException(\TypeError $e, ?int $line = null): \Type
4750 if (isset ($ frame ['file ' ], $ frame ['line ' ])) {
4851 $ file = str_replace ('\\' , '/ ' , $ frame ['file ' ]);
4952
50- if (! str_contains ($ file , 'Internal/ErrorFactory.php ' ) && ! str_contains ( $ file , ' Wrapper/CallableWrapper.php ' )) {
51- if ( $ ref -> hasProperty ( ' file ' )) {
52- $ propFile = $ ref -> getProperty ( ' file ' );
53- $ propFile -> setValue ( $ e , $ frame [ ' file ' ]);
54- }
53+ $ isInternal = str_contains ($ file , 'src/ Internal/' )
54+ || str_contains ( $ file, ' src/Wrapper/ ' )
55+ || str_contains ( $ file , ' src/Validator/ ' )
56+ || str_contains ( $ file , ' src/Resolver/ ' )
57+ || str_contains ( $ file , ' src/Contract/ ' );
5558
56- if ($ line === null && $ ref ->hasProperty ('line ' )) {
57- $ propLine = $ ref ->getProperty ('line ' );
58- $ propLine ->setValue ($ e , $ frame ['line ' ]);
59+ if (! $ isInternal ) {
60+ $ targetFile = $ frame ['file ' ];
61+ if ($ targetLine === null ) {
62+ $ targetLine = $ frame ['line ' ];
5963 }
6064
6165 break ;
@@ -64,6 +68,22 @@ public static function prepareException(\TypeError $e, ?int $line = null): \Type
6468 }
6569 }
6670
71+ try {
72+ $ ref = new ReflectionClass (\Error::class);
73+
74+ if ($ targetFile !== null ) {
75+ $ propFile = $ ref ->getProperty ('file ' );
76+ $ propFile ->setValue ($ e , $ targetFile );
77+ }
78+
79+ if ($ targetLine !== null ) {
80+ $ propLine = $ ref ->getProperty ('line ' );
81+ $ propLine ->setValue ($ e , $ targetLine );
82+ }
83+ } catch (Throwable $ err ) {
84+ // Silently fallback if reflection mutation fails
85+ }
86+
6787 return $ e ;
6888 }
69- }
89+ }
0 commit comments