@@ -548,23 +548,26 @@ public function equals(SimpleType $other): bool {
548548class Type {
549549 /** @var SimpleType[] */
550550 public $ types ;
551+ /** @var bool */
552+ public $ isIntersection = false ;
551553
552554 public static function fromNode (Node $ node ): Type {
553- if ($ node instanceof Node \UnionType) {
555+ if ($ node instanceof Node \UnionType || $ node instanceof Node \IntersectionType ) {
554556 $ nestedTypeObjects = array_map (['Type ' , 'fromNode ' ], $ node ->types );
555557 $ types = [];
556558 foreach ($ nestedTypeObjects as $ typeObject ) {
557559 array_push ($ types , ...$ typeObject ->types );
558560 }
559- return new Type ($ types );
561+ return new Type ($ types, ( $ node instanceof Node \IntersectionType) );
560562 }
561563
562564 if ($ node instanceof Node \NullableType) {
563565 return new Type (
564566 [
565567 ...Type::fromNode ($ node ->type )->types ,
566568 SimpleType::null (),
567- ]
569+ ],
570+ false
568571 );
569572 }
570573
@@ -573,18 +576,20 @@ public static function fromNode(Node $node): Type {
573576 [
574577 SimpleType::fromString ("Traversable " ),
575578 ArrayType::createGenericArray (),
576- ]
579+ ],
580+ false
577581 );
578582 }
579583
580- return new Type ([SimpleType::fromNode ($ node )]);
584+ return new Type ([SimpleType::fromNode ($ node )], false );
581585 }
582586
583587 public static function fromString (string $ typeString ): self {
584588 $ typeString .= "| " ;
585589 $ simpleTypes = [];
586590 $ simpleTypeOffset = 0 ;
587591 $ inArray = false ;
592+ $ isIntersection = false ;
588593
589594 $ typeStringLength = strlen ($ typeString );
590595 for ($ i = 0 ; $ i < $ typeStringLength ; $ i ++) {
@@ -604,7 +609,8 @@ public static function fromString(string $typeString): self {
604609 continue ;
605610 }
606611
607- if ($ char === "| " ) {
612+ if ($ char === "| " || $ char === "& " ) {
613+ $ isIntersection = ($ char === "& " );
608614 $ simpleTypeName = trim (substr ($ typeString , $ simpleTypeOffset , $ i - $ simpleTypeOffset ));
609615
610616 $ simpleTypes [] = SimpleType::fromString ($ simpleTypeName );
@@ -613,14 +619,15 @@ public static function fromString(string $typeString): self {
613619 }
614620 }
615621
616- return new Type ($ simpleTypes );
622+ return new Type ($ simpleTypes, $ isIntersection );
617623 }
618624
619625 /**
620626 * @param SimpleType[] $types
621627 */
622- private function __construct (array $ types ) {
628+ private function __construct (array $ types, bool $ isIntersection ) {
623629 $ this ->types = $ types ;
630+ $ this ->isIntersection = $ isIntersection ;
624631 }
625632
626633 public function isScalar (): bool {
@@ -650,7 +657,8 @@ public function getWithoutNull(): Type {
650657 function (SimpleType $ type ) {
651658 return !$ type ->isNull ();
652659 }
653- )
660+ ),
661+ false
654662 );
655663 }
656664
@@ -683,6 +691,7 @@ public function toOptimizerTypeMask(): string {
683691 $ optimizerTypes = [];
684692
685693 foreach ($ this ->types as $ type ) {
694+ // TODO Support for toOptimizerMask for intersection
686695 $ optimizerTypes [] = $ type ->toOptimizerTypeMask ();
687696 }
688697
@@ -711,8 +720,9 @@ public function toOptimizerTypeMaskForArrayValue(): string {
711720
712721 public function getTypeForDoc (DOMDocument $ doc ): DOMElement {
713722 if (count ($ this ->types ) > 1 ) {
723+ $ typeSort = $ this ->isIntersection ? "intersection " : "union " ;
714724 $ typeElement = $ doc ->createElement ('type ' );
715- $ typeElement ->setAttribute ("class " , " union " );
725+ $ typeElement ->setAttribute ("class " , $ typeSort );
716726
717727 foreach ($ this ->types as $ type ) {
718728 $ unionTypeElement = $ doc ->createElement ('type ' , $ type ->name );
@@ -755,7 +765,8 @@ public function __toString() {
755765 return 'mixed ' ;
756766 }
757767
758- return implode ('| ' , array_map (
768+ $ char = $ this ->isIntersection ? '& ' : '| ' ;
769+ return implode ($ char , array_map (
759770 function ($ type ) { return $ type ->name ; },
760771 $ this ->types )
761772 );
@@ -2237,7 +2248,11 @@ public function getDeclaration(iterable $allConstInfos): string {
22372248
22382249 $ typeMaskCode = $ this ->type ->toArginfoType ()->toTypeMask ();
22392250
2240- $ code .= "\tzend_type property_ {$ propertyName }_type = ZEND_TYPE_INIT_UNION(property_ {$ propertyName }_type_list, $ typeMaskCode); \n" ;
2251+ if ($ this ->type ->isIntersection ) {
2252+ $ code .= "\tzend_type property_ {$ propertyName }_type = ZEND_TYPE_INIT_INTERSECTION(property_ {$ propertyName }_type_list, $ typeMaskCode); \n" ;
2253+ } else {
2254+ $ code .= "\tzend_type property_ {$ propertyName }_type = ZEND_TYPE_INIT_UNION(property_ {$ propertyName }_type_list, $ typeMaskCode); \n" ;
2255+ }
22412256 $ typeCode = "property_ {$ propertyName }_type " ;
22422257 } else {
22432258 $ escapedClassName = $ arginfoType ->classTypes [0 ]->toEscapedName ();
0 commit comments