@@ -396,6 +396,20 @@ function unionDemo(string|int $value, ?User $maybe): User|null { return $maybe;
396396$ typedArrow = fn (int $ x ): float => $ x * 1.5 ;
397397
398398
399+ // ── Multi-line @return & Broken Docblock Recovery ───────────────────────────
400+
401+ // Try: collect([])-> ← shows map, groupBy, values, toGroupedArray
402+ // Try: collect([])->map( ← map() resolves correctly despite groupBy's complex @return
403+ // Try: (new BrokenDocRecovery())->broken()-> ← recovers `static`, shows broken() and working()
404+
405+
406+ // ── Foreach over Generic Collection Classes ─────────────────────────────────
407+ // When a class has @extends or @implements with generic type parameters,
408+ // foreach automatically resolves the element type — even without an
409+ // inline @var annotation.
410+ // Open CollectionForeachDemo methods below to try completion inside them.
411+
412+
399413// ── parent:: Completion ─────────────────────────────────────────────────────
400414// Open AdminUser's constructor and toArray() in scaffolding below for
401415// parent:: examples (inherited methods, overridden methods, constants).
@@ -805,6 +819,48 @@ public function demo(): void
805819 }
806820}
807821
822+ // ── Foreach over Generic Collection Classes ─────────────────────────────────
823+
824+ class CollectionForeachDemo
825+ {
826+ public UserEloquentCollection $ users ;
827+
828+ public function getUsers (): UserEloquentCollection
829+ {
830+ return new UserEloquentCollection ();
831+ }
832+
833+ public function foreachNewCollection (): void
834+ {
835+ $ items = new UserEloquentCollection ();
836+ foreach ($ items as $ item ) {
837+ $ item ->getEmail (); // resolves to User via @extends generics
838+ }
839+ }
840+
841+ public function foreachMethodReturn (): void
842+ {
843+ foreach ($ this ->getUsers () as $ user ) {
844+ $ user ->getName (); // resolves via method return type → collection generics
845+ }
846+ }
847+
848+ public function foreachProperty (): void
849+ {
850+ foreach ($ this ->users as $ user ) {
851+ $ user ->getEmail (); // resolves via property type → collection generics
852+ }
853+ }
854+
855+ public function foreachVariable (): void
856+ {
857+ $ collection = $ this ->getUsers ();
858+ foreach ($ collection as $ user ) {
859+ $ user ->getName (); // resolves via variable assignment scanning
860+ }
861+ }
862+ }
863+
808864
809865// ═══════════════════════════════════════════════════════════════════════════
810866// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
@@ -1509,11 +1565,6 @@ function isRegularUser(mixed $value): bool
15091565}
15101566
15111567// ─── Multi-line @return & Broken Docblock Recovery ──────────────────────────
1512- //
1513- // PHPantomLSP handles @return types that span multiple docblock lines
1514- // (common in Laravel's Collection, Eloquent Builder, etc.). When a
1515- // multi-line @return cannot be fully parsed, the base type is recovered
1516- // (e.g. `static<…broken` → `static`) so resolution still works.
15171568
15181569/**
15191570 * @template TKey of array-key
@@ -1578,9 +1629,6 @@ function collect(mixed $value = []): FluentCollection
15781629 return new FluentCollection ();
15791630}
15801631
1581- // Try: collect([])-> ← shows map, groupBy, values, toGroupedArray
1582- // Try: collect([])->map( ← map() resolves correctly despite groupBy's complex @return
1583-
15841632class BrokenDocRecovery
15851633{
15861634 /**
@@ -1598,4 +1646,27 @@ public function working(): string
15981646 }
15991647}
16001648
1601- // Try: (new BrokenDocRecovery())->broken()-> ← recovers `static`, shows broken() and working()
1649+ // ─── Foreach over Generic Collection Classes ────────────────────────────────
1650+
1651+ /**
1652+ * @template TKey of array-key
1653+ * @template-covariant TValue
1654+ * @implements \IteratorAggregate<TKey, TValue>
1655+ */
1656+ class BaseCollection implements \IteratorAggregate
1657+ {
1658+ /** @return \ArrayIterator<TKey, TValue> */
1659+ public function getIterator (): \ArrayIterator { return new \ArrayIterator ([]); }
1660+ }
1661+
1662+ /**
1663+ * @template TKey of array-key
1664+ * @template TModel of Model
1665+ * @extends BaseCollection<TKey, TModel>
1666+ */
1667+ class EloquentCollection extends BaseCollection {}
1668+
1669+ /**
1670+ * @extends EloquentCollection<int, User>
1671+ */
1672+ final class UserEloquentCollection extends EloquentCollection {}
0 commit comments