Type Declarations & Strict Types

7 questions found

What are type declarations in PHP, and how do they let you specify what kind of value a function parameter or return value should be?

Beginner
Type declarations let you explicitly specify the expected data type of a function's parameters and its return value directly within the function's signature, such as declaring that a parameter must be a string or an integer, and if a value of the wrong type is passed in, PHP will either automatically attempt to convert it to the expected type or raise an error, depending on your configuration, which helps catch mistakes early and makes a function's expected usage much clearer to anyone reading the code.
function calculateTotal(float $price, int $quantity): float {
    return $price * $quantity;
}
Real-world example A shopping cart calculation function declares that it expects a float price and an integer quantity, immediately making it clear to any other developer exactly what kind of values should be passed in, and catching an accidental mistake if a string were passed instead.

Common follow-ups: What data types can be used in a PHP type declaration?;What happens if you do not provide any type declaration at all for a parameter?

Basics & Types;Functions & Scope

What does declaring strict_types=1 at the top of a PHP file actually change about how type declarations are enforced?

Beginner
By default, PHP operates in what is called coercive typing mode, meaning it will automatically attempt to convert a value to the expected declared type if possible, such as silently converting the string ten into the integer ten, whereas adding the declare strict_types equals one statement at the very top of a file switches that specific file into strict mode, meaning PHP will now raise a type error immediately if a value's type does not exactly match the declared type, rather than silently attempting to convert it, which helps catch subtle bugs caused by unintended, unexpected type conversions.
declare(strict_types=1);

function addNumbers(int $a, int $b): int {
    return $a + $b;
}

addNumbers('5', 3); // Throws a TypeError in strict mode
Real-world example A financial calculation module enables strict_types at the top of every file, ensuring that an accidental string value can never silently sneak into a monetary calculation without immediately raising a clear, visible error.

Common follow-ups: Does enabling strict_types in one file affect type checking behavior in other files that call into it?;What are the tradeoffs of using strict types throughout an entire codebase?

Basics & Types;Error & Exception Handling

What are nullable types and union types in PHP, and how do they let you express that a value could be one of several different possible types?

Intermediate
A nullable type, indicated by placing a question mark directly before the type name, means a parameter or return value can either be that specific declared type or explicitly be null, while a union type, indicated using the pipe character between two or more type names, means a value can be any one of several explicitly listed types, and both of these features let you accurately express more flexible, real world scenarios where a single rigid type declaration would otherwise be too restrictive to represent what a function actually needs to accept or return.
function findUser(int $id): ?User {
    // returns a User object, or null if not found
}

function formatId(int|string $id): string {
    return (string) $id;
}
Real-world example A user lookup function declares its return type as a nullable User object, clearly communicating that the function might not find a matching user and could legitimately return null instead of always returning a valid User object.

Common follow-ups: How do you check whether a nullable value is actually null before using it?;What is the difference between a union type and simply not declaring any type at all?

Error & Exception Handling;OOP

What are readonly properties in PHP, and how do they help you create objects whose internal state cannot be accidentally changed after they are first constructed?

Intermediate
A readonly property, once initialized, typically within a class's constructor, can never be modified again afterward, and attempting to reassign a readonly property's value anywhere else in the code will immediately raise an error, which is particularly useful for creating objects that represent an immutable value, meaning a value that should never change once created, such as a Money object representing a specific fixed monetary amount, or a data transfer object representing a snapshot of information at one specific point in time.
class Money {
    public function __construct(
        public readonly int $amountInCents,
        public readonly string $currency,
    ) {}
}
Real-world example A Money class marks both its amount and currency properties as readonly, guaranteeing that once a specific Money object is created representing a transaction amount, that value can never be accidentally altered elsewhere in the application later.

Common follow-ups: What happens if you try to modify a readonly property after it has already been initialized?;Can a readonly property hold a mutable object, and if so, can that inner object's own properties still be changed?

OOP;Basics & Types

How do type declarations improve the effectiveness of static analysis tools and IDE autocompletion when working within a codebase?

Intermediate
Type declarations provide concrete, machine readable information about what kind of value is expected in a given context, and both static analysis tools and modern code editors rely heavily on this information, using it to catch potential type related bugs before code is even run, and to provide accurate autocompletion suggestions showing exactly which methods and properties are actually available on a given typed value, both of which become significantly less effective and less reliable in codebases that rely mostly on untyped parameters and return values.
function processOrder(Order $order): Receipt {
    // Editor knows exactly which methods $order has available
    return $order->generateReceipt();
}
Real-world example A developer working in a well typed codebase benefits from accurate autocompletion immediately suggesting every available method on an Order object, compared to a similar codebase without type declarations where the editor has no reliable way to know what methods might actually exist.

Common follow-ups: Does adding type declarations to an existing untyped codebase require significant refactoring effort?;How does this relate to the benefits provided by tools like PHPStan or Psalm?

Static Analysis (PHPStan & Psalm);OOP

What are PHP 8's enums, and how do they provide a more type safe alternative to using plain class constants for representing a fixed set of related values?

Advanced
An enum, short for enumeration, lets you define a fixed, closed set of possible named values as its own distinct type, and unlike traditional class constants, which are really just plain values with no special enforced relationship to each other, a genuine enum is fully type checked, meaning a function that declares a parameter typed as a specific enum can only ever receive one of that enum's officially defined cases, making it impossible to accidentally pass in an unrelated, invalid value that happens to share the same underlying type, such as a plain string.
enum OrderStatus: string {
    case Pending = 'pending';
    case Shipped = 'shipped';
    case Delivered = 'delivered';
}

function updateStatus(OrderStatus $status): void { /* ... */ }
Real-world example An order processing system defines an OrderStatus enum representing its exact set of valid statuses, preventing an invalid, misspelled status string from ever being accidentally passed into functions that specifically expect a genuine OrderStatus value.

Common follow-ups: What is the difference between a pure enum and a backed enum in PHP?;Can an enum have its own methods, similar to a regular class?

PHP 8 Features (Attributes Enums Match Nullsafe Operator);OOP

How does PHP's type variance, specifically covariance and contravariance, affect how return types and parameter types can be safely changed when overriding a method in a child class?

Advanced
When a child class overrides a method inherited from a parent class, PHP allows the child's overridden method to declare a more specific return type than the parent's declared return type, a concept known as covariance, since returning a more specific type still fully satisfies anyone expecting the original, more general parent type, while for parameter types, PHP allows the reverse, called contravariance, meaning a child class's overridden method may accept a more general parameter type than the parent originally declared, since it can still correctly handle every value the more specific parent type would have accepted, and understanding this distinction helps avoid confusing type declaration errors when designing class hierarchies.
class Animal {}
class Dog extends Animal {}

class AnimalShelter {
    public function adopt(): Animal { return new Animal(); }
}

class DogShelter extends AnimalShelter {
    public function adopt(): Dog { return new Dog(); } // Covariant return type, allowed
}
Real-world example A DogShelter class overrides its parent AnimalShelter's adopt method to specifically return a Dog rather than a generic Animal, which PHP allows because a Dog is still always a valid Animal, satisfying every expectation of anyone calling the original parent method.

Common follow-ups: What would happen if a child class tried to declare a more general return type than its parent instead?;How common is it in practice to actually need to understand variance rules when writing typical application code?

OOP;Interfaces & Abstract Classes