Navigation

Php

How to Handle PHP's Strict Types Declaration

Enable PHP strict types with declare(strict_types=1) to catch type errors early. Improve code reliability and prevent type coercion bugs.

Table Of Contents

Problem

PHP's default type juggling can cause unexpected behavior when functions receive wrong data types, leading to hard-to-debug issues.

Solution

<?php
declare(strict_types=1);

// Without strict types (default behavior)
function addNumbers($a, $b) {
    return $a + $b;
}

// These would work in non-strict mode but cause type errors in strict mode
// addNumbers("5", "10");     // TypeError in strict mode
// addNumbers(true, false);   // TypeError in strict mode

// With strict types - specify exact types
function addNumbersStrict(int $a, int $b): int {
    return $a + $b;
}

// Only accepts integers
echo addNumbersStrict(5, 10);  // 15

// String type enforcement
function greetUser(string $name): string {
    return "Hello, " . $name;
}

echo greetUser("Alice");  // Hello, Alice
// greetUser(123);  // TypeError: must be string

// Float type enforcement
function calculateTax(float $amount, float $rate): float {
    return $amount * $rate;
}

echo calculateTax(100.0, 0.08);  // 8.0

// Boolean type enforcement
function toggleFeature(bool $enabled): string {
    return $enabled ? "Feature enabled" : "Feature disabled";
}

echo toggleFeature(true);   // Feature enabled
echo toggleFeature(false);  // Feature disabled

// Array type enforcement
function processItems(array $items): int {
    return count($items);
}

echo processItems(['a', 'b', 'c']);  // 3

// Object type enforcement
class User {
    public string $name;
    public int $age;
    
    public function __construct(string $name, int $age) {
        $this->name = $name;
        $this->age = $age;
    }
}

function welcomeUser(User $user): string {
    return "Welcome, " . $user->name;
}

$user = new User("Bob", 25);
echo welcomeUser($user);  // Welcome, Bob

// Nullable types
function formatMessage(?string $message): string {
    return $message ?? "No message";
}

echo formatMessage("Hello");  // Hello
echo formatMessage(null);     // No message

// Union types (PHP 8.0+)
function processValue(int|float|string $value): string {
    return "Processing: " . $value;
}

echo processValue(42);      // Processing: 42
echo processValue(3.14);    // Processing: 3.14
echo processValue("text");  // Processing: text

// Return type enforcement
function getUsers(): array {
    return [
        ['name' => 'Alice', 'age' => 30],
        ['name' => 'Bob', 'age' => 25]
    ];
}

function getUserCount(): int {
    return count(getUsers());
}

// Mixed type for flexibility
function logValue(mixed $value): void {
    error_log("Value: " . print_r($value, true));
}

logValue("string");
logValue(123);
logValue(['array']);

// Class with strict types
class Calculator {
    public function add(int|float $a, int|float $b): int|float {
        return $a + $b;
    }
    
    public function divide(int|float $a, int|float $b): float {
        if ($b === 0) {
            throw new InvalidArgumentException("Division by zero");
        }
        return $a / $b;
    }
    
    public function factorial(int $n): int {
        if ($n < 0) {
            throw new InvalidArgumentException("Factorial of negative number");
        }
        
        $result = 1;
        for ($i = 2; $i <= $n; $i++) {
            $result *= $i;
        }
        
        return $result;
    }
}

// Interface with strict types
interface PaymentProcessor {
    public function processPayment(float $amount, string $currency): bool;
    public function refund(string $transactionId, float $amount): bool;
}

class StripePaymentProcessor implements PaymentProcessor {
    public function processPayment(float $amount, string $currency): bool {
        // Process payment logic
        return true;
    }
    
    public function refund(string $transactionId, float $amount): bool {
        // Refund logic
        return true;
    }
}

// Checking types at runtime
function validateInput(mixed $value): string {
    return match(gettype($value)) {
        'string' => "Valid string: $value",
        'integer' => "Valid integer: $value",
        'double' => "Valid float: $value",
        'boolean' => "Valid boolean: " . ($value ? 'true' : 'false'),
        'array' => "Valid array with " . count($value) . " elements",
        default => "Unknown type: " . gettype($value)
    };
}

// Migration strategy for existing code
// 1. Add declare(strict_types=1) to new files
// 2. Gradually add type hints to existing functions
// 3. Test thoroughly after enabling strict types

// Example of gradual migration
function oldFunction($data) {
    // Original function without types
    return strtoupper($data);
}

function newFunction(string $data): string {
    // Migrated version with strict types
    return strtoupper($data);
}

// Error handling with strict types
try {
    $calc = new Calculator();
    $result = $calc->divide(10, 2);
    echo "Result: $result\n";
} catch (TypeError $e) {
    echo "Type error: " . $e->getMessage();
} catch (InvalidArgumentException $e) {
    echo "Invalid argument: " . $e->getMessage();
}

Explanation

declare(strict_types=1) must be the first statement in a PHP file. It enforces exact type matching for function parameters and return values.

Strict types prevent automatic type conversion, catching type-related bugs early. Use type hints consistently and handle TypeError exceptions for better error handling.

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Php