Bird
Raised Fist0
PHPprogramming~15 mins

Intersection types in PHP - Deep Dive

Choose your learning style10 modes available
Overview - Intersection types
What is it?
Intersection types in PHP allow a variable or parameter to be declared as requiring multiple types at the same time. This means the value must satisfy all the listed types simultaneously. It helps make code more precise by combining multiple type requirements into one declaration.
Why it matters
Without intersection types, developers often rely on loose type checks or comments to explain complex type requirements, which can lead to bugs and unclear code. Intersection types enforce stricter rules at the language level, preventing errors early and making code easier to understand and maintain.
Where it fits
Before learning intersection types, you should understand basic PHP types, union types, and type declarations. After mastering intersection types, you can explore advanced type features like generics (in other languages), or design patterns that leverage strict typing for safer code.
Mental Model
Core Idea
An intersection type means a value must be all the specified types at once, like wearing multiple hats simultaneously.
Think of it like...
Imagine a person who must be both a teacher and a musician to join a special club. They need to have skills from both roles, not just one or the other.
Value
  │
  ├─ Must be Type A
  ├─ Must be Type B
  └─ Must be Type C

Only values that satisfy all these types pass.
Build-Up - 6 Steps
1
FoundationBasic PHP type declarations
🤔
Concept: Learn how PHP declares types for variables and function parameters.
PHP allows you to specify types like int, string, or classes for function parameters and return values. For example: function greet(string $name): string { return "Hello, $name!"; } This ensures only strings are accepted and returned.
Result
The function only accepts strings and returns a string.
Understanding simple type declarations is essential before combining multiple types.
2
FoundationIntroduction to union types
🤔
Concept: Union types allow a value to be one of several types, increasing flexibility.
PHP 8.0 introduced union types, letting you declare a parameter as accepting multiple types, like: function process(int|string $value) { // $value can be int or string } This means the value can be either type, not both.
Result
The function accepts either an int or a string.
Union types show how PHP can accept multiple types, but they differ from intersection types.
3
IntermediateUnderstanding intersection types syntax
🤔Before reading on: do you think intersection types accept values matching any one type or all types? Commit to your answer.
Concept: Intersection types require a value to satisfy all listed types simultaneously, using the '&' symbol.
PHP 8.1 introduced intersection types using '&'. For example: function example(A&B $value) { // $value must be both A and B } This means $value must be an object that implements both interfaces or classes A and B.
Result
Only values that are both A and B are accepted.
Knowing that '&' means 'and' clarifies that intersection types are stricter than unions.
4
IntermediatePractical use with interfaces
🤔Before reading on: can a class implement multiple interfaces to satisfy an intersection type? Commit to your answer.
Concept: Intersection types are often used with interfaces to require multiple behaviors at once.
If you have interfaces Logger and Serializer, you can require a parameter to be both: function handle(Logger&Serializer $obj) { $obj->log('start'); $obj->serialize(); } A class implementing both interfaces can be passed here.
Result
The function safely calls methods from both interfaces.
Understanding intersection types helps enforce multiple capabilities in one object.
5
AdvancedCombining intersection and union types
🤔Before reading on: do you think you can mix '&' and '|' in the same type declaration? Commit to your answer.
Concept: PHP allows combining intersection and union types with parentheses to clarify precedence.
Example: function test((A&B)|C $value) { // $value is either both A and B, or C } Parentheses group intersection before union.
Result
The function accepts values matching either condition.
Knowing how to combine these types allows expressing complex type rules precisely.
6
ExpertLimitations and runtime behavior
🤔Before reading on: do you think intersection types are checked at runtime or only at compile time? Commit to your answer.
Concept: Intersection types are enforced at runtime by PHP's type system, but some limitations exist with traits and classes.
PHP checks intersection types when functions are called. However, traits cannot be used directly in intersection types, and some complex inheritance can cause unexpected errors. Example: class A {} class B {} function foo(A&B $obj) {} This requires $obj to be instance of a class extending both A and B, which is impossible, so intersection types are mainly for interfaces.
Result
Runtime errors occur if types don't match exactly.
Understanding runtime checks and limitations prevents misuse and bugs in production.
Under the Hood
PHP's engine checks type declarations at runtime when functions are called or values assigned. For intersection types, it verifies the value implements or extends all specified types. This involves checking the object's class hierarchy and interfaces. If any type is missing, a TypeError is thrown immediately.
Why designed this way?
Intersection types were introduced to improve type safety and expressiveness without breaking backward compatibility. Using runtime checks fits PHP's dynamic nature, allowing gradual typing. The design balances strictness with flexibility, avoiding complex compile-time checks that PHP does not perform.
Call function with value
      │
      ▼
  Check if value is instance of Type1
      │
      ├─ No → Throw TypeError
      │
      ▼
  Check if value is instance of Type2
      │
      ├─ No → Throw TypeError
      │
      ▼
  ... (all types checked)
      │
      ▼
  Accept value and run function
Myth Busters - 4 Common Misconceptions
Quick: Does an intersection type accept values matching any one of the types or all of them? Commit to your answer.
Common Belief:Intersection types accept values that match any one of the listed types, like union types.
Tap to reveal reality
Reality:Intersection types require the value to match all listed types simultaneously, not just one.
Why it matters:Confusing intersection with union types leads to passing incompatible values, causing runtime errors.
Quick: Can intersection types be used with classes that do not share inheritance? Commit to your answer.
Common Belief:You can intersect any classes regardless of inheritance hierarchy.
Tap to reveal reality
Reality:Intersection types with classes require the value to be an instance of a class that extends all intersected classes, which is impossible unless one class extends the other. Intersection types are mainly useful with interfaces.
Why it matters:Trying to intersect unrelated classes causes impossible type requirements and errors.
Quick: Are intersection types checked at compile time or runtime? Commit to your answer.
Common Belief:Intersection types are checked at compile time like in some statically typed languages.
Tap to reveal reality
Reality:PHP checks intersection types at runtime when the function is called or value assigned.
Why it matters:Expecting compile-time errors can mislead debugging and testing strategies.
Quick: Can traits be used directly in intersection types? Commit to your answer.
Common Belief:Traits can be used in intersection types just like interfaces or classes.
Tap to reveal reality
Reality:Traits cannot be used in intersection types because they are not types themselves but code reuse mechanisms.
Why it matters:Misusing traits in intersection types causes syntax errors and confusion.
Expert Zone
1
Intersection types cannot include traits because traits are not types but code reuse tools, which often surprises developers.
2
Combining intersection and union types requires careful use of parentheses to avoid unexpected precedence issues.
3
Runtime checks for intersection types can impact performance slightly, so they should be used judiciously in hot code paths.
When NOT to use
Avoid intersection types when dealing with classes that do not share inheritance, as this creates impossible type requirements. Instead, use interfaces or rethink design. Also, if you need to accept multiple alternative types, union types are better suited.
Production Patterns
In production, intersection types are commonly used to enforce multiple interface implementations for dependency injection, ensuring objects have all required capabilities. They also help in APIs to guarantee strict contracts, reducing bugs caused by missing methods.
Connections
Set theory
Intersection types correspond to the intersection operation in set theory, where elements must belong to all sets simultaneously.
Understanding set intersections clarifies why intersection types require all conditions to be met, not just one.
Multiple inheritance (OOP)
Intersection types relate to multiple inheritance by requiring an object to behave like multiple types at once, similar to inheriting from multiple classes or interfaces.
Knowing multiple inheritance helps understand how intersection types enforce multiple behaviors in one object.
Logical AND operator in Boolean logic
Intersection types act like the logical AND operator, requiring all conditions (types) to be true (satisfied).
Recognizing this logical connection helps grasp the strictness of intersection types compared to union types (logical OR).
Common Pitfalls
#1Trying to use intersection types with unrelated classes causing impossible type requirements.
Wrong approach:function foo(A&B $obj) {} class A {} class B {} $obj = new B(); foo($obj);
Correct approach:interface A {} interface B {} class C implements A, B {} function foo(A&B $obj) {} $obj = new C(); foo($obj);
Root cause:Misunderstanding that intersection types require a single value to satisfy all types, which is impossible if classes are unrelated.
#2Confusing intersection types with union types and expecting values to match any one type.
Wrong approach:function bar(A&B $obj) {} // Passing an object of only A or only B $objA = new A(); bar($objA); // Expecting this to work
Correct approach:function bar(A|B $obj) {} $objA = new A(); bar($objA); // Works because union accepts either type
Root cause:Not recognizing that intersection types require all types simultaneously, unlike union types.
#3Attempting to use traits in intersection types causing syntax errors.
Wrong approach:function baz(TraitA&TraitB $obj) {}
Correct approach:Use interfaces or classes instead: interface InterfaceA {} interface InterfaceB {} function baz(InterfaceA&InterfaceB $obj) {}
Root cause:Confusing traits (code reuse) with types (interfaces/classes) that can be used in type declarations.
Key Takeaways
Intersection types require a value to satisfy all specified types simultaneously, making type declarations more precise.
They are mainly useful with interfaces because intersecting unrelated classes is usually impossible.
Intersection types use the '&' symbol and can be combined with union types using parentheses for complex rules.
PHP enforces intersection types at runtime, throwing errors if the value does not meet all type requirements.
Understanding intersection types improves code safety by ensuring objects have multiple required behaviors.

Practice

(1/5)
1. What does an intersection type in PHP represent?
function example(SomeType&OtherType $param) {}
easy
A. A type that requires the value to satisfy both SomeType and OtherType.
B. A type that accepts either SomeType or OtherType.
C. A type that only accepts SomeType but ignores OtherType.
D. A type that combines SomeType and OtherType into a new class.

Solution

  1. Step 1: Understand intersection type syntax

    In PHP, the & symbol between types means the value must satisfy all listed types simultaneously.
  2. Step 2: Interpret the function parameter

    The parameter type SomeType&OtherType means the argument must be both SomeType and OtherType at the same time.
  3. Final Answer:

    A type that requires the value to satisfy both SomeType and OtherType. -> Option A
  4. Quick Check:

    Intersection type = must satisfy all types [OK]
Hint: Remember & means AND, so all types must match [OK]
Common Mistakes:
  • Confusing & with | (union type)
  • Thinking it creates a new combined class
  • Assuming it accepts either type instead of both
2. Which of the following is the correct syntax for declaring an intersection type in a function parameter in PHP?
easy
A. function test(A & B $param) {}
B. function test(A | B $param) {}
C. function test(A, B $param) {}
D. function test(A && B $param) {}

Solution

  1. Step 1: Recall intersection type syntax

    PHP uses a single ampersand & to declare intersection types between multiple types.
  2. Step 2: Check each option

    function test(A & B $param) {} uses & correctly. function test(A | B $param) {} uses | which is union type. function test(A, B $param) {} uses comma which is invalid. function test(A && B $param) {} uses && which is not valid syntax.
  3. Final Answer:

    function test(A & B $param) {} -> Option A
  4. Quick Check:

    Intersection type uses & symbol [OK]
Hint: Use single & between types for intersection [OK]
Common Mistakes:
  • Using | instead of &
  • Using commas to separate types
  • Using && instead of &
3. What will be the output of the following PHP code?
interface A { public function foo(): string; }
interface B { public function bar(): string; }

class C implements A, B {
public function foo(): string { return 'foo'; }
public function bar(): string { return 'bar'; }
}

function test(A & B $obj) {
echo $obj->foo() . ' ' . $obj->bar();
}

$c = new C();
test($c);
medium
A. TypeError at runtime
B. foo
C. bar
D. foo bar

Solution

  1. Step 1: Understand class and interfaces

    Class C implements both interfaces A and B, so it has methods foo() and bar().
  2. Step 2: Analyze function test parameter and call

    The function test requires an argument that satisfies both A and B (intersection type). Object $c satisfies both. The function echoes foo() and bar() results concatenated with a space.
  3. Final Answer:

    foo bar -> Option D
  4. Quick Check:

    Object satisfies both interfaces, prints both methods [OK]
Hint: Intersection means object must have all methods used [OK]
Common Mistakes:
  • Expecting only one method output
  • Thinking it causes a TypeError
  • Confusing intersection with union types
4. Identify the error in this PHP code using intersection types:
interface X { public function x(): void; }
interface Y { public function y(): void; }

class Z implements X {
public function x(): void {}
}

function foo(X & Y $param) {
$param->x();
$param->y();
}

$z = new Z();
foo($z);
medium
A. Function foo has wrong parameter syntax for intersection types.
B. Method y() is missing in function foo.
C. Class Z does not implement interface Y, so it cannot satisfy X & Y.
D. No error, code runs fine.

Solution

  1. Step 1: Check class Z implementation

    Class Z implements only interface X, not Y, so it does not satisfy the intersection type X & Y.
  2. Step 2: Analyze function call with $z

    Function foo requires a parameter that implements both X and Y. Passing $z causes a TypeError because $z lacks Y.
  3. Final Answer:

    Class Z does not implement interface Y, so it cannot satisfy X & Y. -> Option C
  4. Quick Check:

    Intersection requires all interfaces implemented [OK]
Hint: Check all interfaces are implemented for intersection [OK]
Common Mistakes:
  • Assuming partial interface implementation is enough
  • Thinking syntax is wrong instead of logic
  • Ignoring missing methods from interfaces
5. Given these interfaces and classes:
interface Logger { public function log(string $msg): void; }
interface FileWriter { public function write(string $data): void; }

class FileLogger implements Logger, FileWriter {
public function log(string $msg): void { echo "Log: $msg\n"; }
public function write(string $data): void { echo "Write: $data\n"; }
}

function process(Logger & FileWriter $obj, string $message) {
$obj->log($message);
$obj->write($message);
}

$fl = new FileLogger();
process($fl, "Hello World");

What will be the output and why is intersection type useful here?
hard
A. Runtime error because intersection types cannot be used with classes.
B. Output:
Log: Hello World
Write: Hello World
Intersection ensures $obj has both log() and write() methods.
C. Output:
Log: Hello World
Only log() is called because intersection ignores write().
D. Output:
Write: Hello World
Only write() is called because log() is ignored.

Solution

  1. Step 1: Understand class and interfaces

    FileLogger implements both Logger and FileWriter, so it has both log() and write() methods.
  2. Step 2: Analyze function process behavior

    The function requires an object that satisfies both Logger and FileWriter (intersection type). It calls both log() and write() methods on the object.
  3. Step 3: Predict output

    Calling process with $fl outputs:
    Log: Hello World
    Write: Hello World
  4. Final Answer:

    Output:
    Log: Hello World
    Write: Hello World
    Intersection ensures $obj has both log() and write() methods.
    -> Option B
  5. Quick Check:

    Intersection type ensures all required methods exist [OK]
Hint: Intersection guarantees all methods exist on object [OK]
Common Mistakes:
  • Thinking only one interface method is called
  • Believing intersection types cause runtime errors
  • Ignoring that both methods are called
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard