Type Hinting & Union Types
Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 35 of 91.
So far, you've used single types in your type hints - one class, one interface, or one primitive type. But what if a function should accept multiple different types? PHP 8.0 introduced union types, which let you specify that a parameter or return value can be one of several types.
Union types use the pipe character | to separate allowed types:
<?php
function formatValue(int|float|string $value): string {
return "Value: " . $value;
}
echo formatValue(42) . "\n";
echo formatValue(3.14) . "\n";
echo formatValue("hello");
Output:
Value: 42
Value: 3.14
Value: helloThis becomes especially powerful when combined with polymorphism. You can accept multiple unrelated classes or interfaces in a single parameter:
<?php
interface Printable {
public function print(): string;
}
class Document implements Printable {
public function print(): string {
return "Printing document";
}
}
class Image {
public function render(): string {
return "Rendering image";
}
}
function process(Printable|Image $item): string {
if ($item instanceof Printable) {
return $item->print();
}
return $item->render();
}
echo process(new Document()) . "\n";
echo process(new Image());
The instanceof operator helps you determine which type you're working with inside the function. This pattern lets you write flexible functions that handle multiple types while still maintaining type safety.
Key Point: Union types expand polymorphism beyond inheritance hierarchies, allowing functions to accept completely different types while keeping your code type-safe.
Challenge
EasyLet's build a content renderer system that demonstrates the power of union types. You'll create a function that can handle multiple unrelated types — some implementing an interface, others being standalone classes — and process each one appropriately using instanceof checks.
You'll organize your code across four files:
Renderable.php— Define aRenderableinterface with a single method signature:render(). Classes implementing this interface promise they can render themselves to a string.TextBlock.php— Create aTextBlockclass that implementsRenderable. Include the interface file. The class should have a private$contentproperty set through the constructor. Implementrender()to return"Text: [content]".RawHtml.php— Create aRawHtmlclass that does NOT implementRenderable. This is a standalone class with a private$htmlproperty set through the constructor. Add agetHtml()method that returns"HTML: [html]".main.php— Include both the TextBlock and RawHtml files. Create a function calledrenderContentthat uses a union type to accept either aRenderableor aRawHtmlobject. Inside the function, useinstanceofto check the type: if it'sRenderable, call and returnrender(); otherwise, call and returngetHtml().
You'll receive two inputs: text content for the TextBlock and HTML content for the RawHtml. Create both objects with their respective content. Pass each one to your renderContent() function and print the results — the TextBlock first, then the RawHtml, each on its own line.
This challenge shows how union types let you write a single function that handles completely different types. The TextBlock follows the Renderable contract, while RawHtml is an entirely separate class — yet your function works seamlessly with both thanks to the union type Renderable|RawHtml.
Try it yourself
<?php
require_once 'TextBlock.php';
require_once 'RawHtml.php';
// Read input
$textContent = trim(fgets(STDIN));
$htmlContent = trim(fgets(STDIN));
// TODO: Create a function called renderContent that:
// - Uses a union type to accept either Renderable or RawHtml
// - Uses instanceof to check the type
// - If Renderable, call and return render()
// - Otherwise, call and return getHtml()
function renderContent(/* TODO: Add union type parameter */) {
// TODO: Use instanceof to check type and return appropriate result
}
// TODO: Create TextBlock object with $textContent
// TODO: Create RawHtml object with $htmlContent
// TODO: Pass each object to renderContent() and print the results
// Print TextBlock result first, then RawHtml result (each on its own line)
?>This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe $this KeywordMethodsPropertiesConstructor (__construct)Destructor (__destruct)Recap - Simple Calculator4Inheritance
Basic InheritanceThe parent:: KeywordMethod OverridingThe final KeywordAbstract ClassesRecap - Employee Hierarchy7Encapsulation
Public, Protected, PrivateAccess Modifiers in DepthGetters and SettersInformation HidingConstructor Promotion (8.0)Recap - Student Records System2Namespaces & Autoloading
Introduction to NamespacesThe use KeywordPSR-4 Autoloading StandardComposer AutoloaderRecap - Organized Project5Interfaces & Contracts
Introduction to InterfacesImplementing InterfacesMultiple Interface ImplementInterface vs Abstract ClassType Hinting with InterfacesRecap - Shape Calculator8Magic Methods
Magic Methods Introduction__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Object Cloning__serialize & __unserializeRecap - Custom Collection11Type System & Error Handling
Type DeclarationsNullable TypesUnion & Intersection TypesException ClassesCustom Exception HierarchyTry, Catch, FinallyRecap - Form Validator14Project: Library Management
Project OverviewBook and User Classes3Class Properties
Instance vs Static PropertiesConstants in ClassesStatic Methods & PropertiesPrivate & Protected PropertiesReadonly Properties (PHP 8.1)Recap - Bank Account Manager6Polymorphism
Method Overriding RevisitedPolymorphism via InterfacesType Hinting & Union TypesLate Static BindingRecap - Payment Processor9Traits
Introduction to TraitsUsing Multiple TraitsTrait Conflict ResolutionAbstract Methods in TraitsTraits vs Inheritance12Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern