Type Declarations for Class Properties in PHP (5 examples)

Updated: February 20, 2024 By: Guest Contributor Post a comment

Introduction

Type declarations, also referred to as type hints, allow developers to specify the expected data type of an argument in a function declaration, a return type from a function, or, since the release of PHP 7.4, the data type of class properties. This feature enhances the robustness of code by ensuring that properties are used as intended, improving error handling, and facilitating code readability and maintenance.

In this tutorial, we will explore how to use type declarations for class properties in PHP through 5 progressively advanced examples. Starting with the basics, we’ll move towards more complex applications, illustrating the impact and flexibility type declarations bring to PHP development.

Example 1: Basic Type Declaration

class Employee {
    public string $name;
    public int $age;
}

$emp = new Employee();
$emp->name = 'John Doe'; // Valid
echo $emp->name; // Outputs: John Doe
$emp->age = 30; // Valid
echo $emp->age; // Outputs: 30

This example demonstrates the straightforward use of type declarations for class properties. Here, the Employee class has two properties, name and age, declared with string and int types, respectively. Attempting to assign values of different types will result in a TypeError.

Example 2: Nullable Types

class Person {
    public ?string $name = null;
    public ?int $age = null;
}

$person = new Person();
$person->name = 'Alice'; // Also allows setting to null
$person->age = null; // Valid

In this scenario, the class properties are declared as nullable by prefixing the type with a question mark (?). This allows for either the specific type or null to be assigned to the property, providing flexibility in handling cases where a property might not yet have a value.

Example 3: Union Types (introduced in PHP 8.0)

class Vehicle {
    public int|float $speed;
}

$car = new Vehicle();
$car->speed = 55; // Valid
$car->speed = 70.5; // Also valid

PHP 8.0 introduced union types, allowing a property to be declared to accept multiple types. The Vehicle class demonstrates using a union type for the speed property. It can accept both integers and floats, offering flexibility for different requirements.

Example 4: Complex Types

class BookList {
    public array $books = [];
}

$library = new BookList();
$library->books[] = '1984';
$library->books[] = 'Brave New World';

Complex data types such as arrays are also supported. In this example, the BookList class has a books property intended to hold an array of book titles. This example shows the power of type declarations in ensuring a property contains a well-defined data structure, which helps prevent bugs and misuses.

Example 5: Using Attributes for Typed Properties (PHP 8.1 and beyond)

This final example showcases the power of PHP’s attribute feature with typed properties. By applying an attribute to a class property, developers can specify metadata directly in the property declaration, adding another layer of robustness and flexibility to class definitions.

#[Attribute]
class ExampleAttribute {
    public function __construct(public string $description) {}
}

class AdvancedExample {
    // Use the ExampleAttribute to add a description to the specialProperty
    #[ExampleAttribute(description: "This is a special string property.")]
    public string $specialProperty;

    public function __construct(string $specialProperty) {
        $this->specialProperty = $specialProperty;
        $this->validateProperties();
    }

    // Method to validate property values based on attributes
    private function validateProperties(): void {
        $reflectionClass = new ReflectionClass($this);
        foreach ($reflectionClass->getProperties() as $property) {
            $attributes = $property->getAttributes(ExampleAttribute::class);
            foreach ($attributes as $attribute) {
                $attributeInstance = $attribute->newInstance();
                // Simple validation: check if the property is non-empty based on its description
                if (empty($this->{$property->getName()})) {
                    throw new InvalidArgumentException("The property '{$property->getName()}' described as '{$attributeInstance->description}' cannot be empty.");
                }
                // Additional validation logic could be added here
                echo "Property '{$property->getName()}' is valid with description: '{$attributeInstance->description}'.\n";
            }
        }
    }
}

// Example usage:
try {
    $example = new AdvancedExample(specialProperty: "Hello, World!");
    // If the property is empty, an InvalidArgumentException will be thrown
    // $example = new AdvancedExample(specialProperty: "");
} catch (InvalidArgumentException $e) {
    echo "Validation failed: " . $e->getMessage();
}

In this enhanced example, the ExampleAttribute class now accepts a description string that can be used to describe the property it is associated with. The AdvancedExample class has a constructor that accepts a value for $specialProperty and calls validateProperties to perform validation. The validateProperties method uses reflection to iterate over class properties, fetches associated attributes, and performs a simple check to ensure that properties annotated with ExampleAttribute are not empty.

This showcases how attributes in PHP 8.1 can be used for more than just metadata annotation—allowing for runtime introspection and validation, among other possibilities.

Conclusion

Implementing type declarations for class properties in PHP improves code quality, aids in debugging, and ensures type consistency throughout your application. Through the examples provided, we’ve seen the evolution and flexibility in defining class properties, from basic type declarations to complex and nuanced applications like nullable, union, and attributed types. Mastering these features can greatly enhance your PHP coding standard and design.