1+ <?php
2+
3+ declare (strict_types=1 );
4+
5+ use PHPStan \PhpDocParser \Ast \Type \IdentifierTypeNode ;
6+ use TypePHP \Exception \TypeError ;
7+ use TypePHP \Resolver \TemplateManager ;
8+ use TypePHP \TypePHP ;
9+
10+ interface SpecificAppMiddleware
11+ {
12+ }
13+
14+ class ConcreteAppMiddleware implements SpecificAppMiddleware
15+ {
16+ }
17+
18+ class IncompatibleOtherMiddleware
19+ {
20+ }
21+
22+ /**
23+ * Generic container with class template
24+ *
25+ * @template TMiddleware of object
26+ */
27+ class GenericMiddlewareContainer
28+ {
29+ /**
30+ * @var array<int, TMiddleware>
31+ */
32+ public array $ middlewares = [];
33+ }
34+
35+ /**
36+ * Function accepting specialized generic container
37+ *
38+ * @param GenericMiddlewareContainer<SpecificAppMiddleware> $container
39+ */
40+ function consumeMiddlewareContainer (GenericMiddlewareContainer $ container ): bool
41+ {
42+ return true ;
43+ }
44+
45+ describe ('Unspecialized Generic Default Objects & First-Use Parameter Binding ' , function () {
46+ test ('reproduces tempest unspecialized mixed binding mismatch on un-annotated instance ' , function () {
47+ $ rawContainer = new GenericMiddlewareContainer ();
48+
49+ TemplateManager::bindTemplate ('none ' , $ rawContainer , 'TMiddleware ' , new IdentifierTypeNode ('mixed ' ));
50+
51+ expect (consumeMiddlewareContainer ($ rawContainer ))->toBeTrue ()
52+ ->and (TypePHP::getGenericType ($ rawContainer ))->toBe (SpecificAppMiddleware::class)
53+ ;
54+ });
55+
56+ test ('strictly rejects explicitly annotated instance holding an incompatible generic type ' , function () {
57+ $ badContainer = new GenericMiddlewareContainer ();
58+ TemplateManager::bindTemplate ('none ' , $ badContainer , 'TMiddleware ' , new IdentifierTypeNode (IncompatibleOtherMiddleware::class));
59+
60+ expect (fn () => consumeMiddlewareContainer ($ badContainer ))
61+ ->toThrow (TypeError::class, 'expects GenericMiddlewareContainer<invariant SpecificAppMiddleware>, but GenericMiddlewareContainer<IncompatibleOtherMiddleware> was given ' )
62+ ;
63+ });
64+ });
0 commit comments