Navigation

Php

How to Work with PHP's Match Expression

Use PHP 8.0+ match expression for cleaner conditional logic. Replace verbose switch statements with concise, type-safe matching.

Table Of Contents

Problem

You need conditional logic with multiple cases but switch statements are verbose, allow fall-through, and don't return values directly.

Solution

// Traditional switch statement
function getStatusMessage($status) {
    switch ($status) {
        case 'pending':
            return 'Order is pending';
        case 'processing':
            return 'Order is being processed';
        case 'shipped':
            return 'Order has been shipped';
        case 'delivered':
            return 'Order delivered';
        default:
            return 'Unknown status';
    }
}

// Match expression (cleaner)
function getStatusMessage($status) {
    return match ($status) {
        'pending' => 'Order is pending',
        'processing' => 'Order is being processed',
        'shipped' => 'Order has been shipped',
        'delivered' => 'Order delivered',
        default => 'Unknown status'
    };
}

// Multiple values per case
function getDiscount($customerType) {
    return match ($customerType) {
        'premium', 'vip' => 0.20,
        'regular', 'standard' => 0.10,
        'new' => 0.05,
        default => 0.0
    };
}

// Type-safe matching (strict comparison)
function processValue($value) {
    return match ($value) {
        1 => 'Integer one',
        '1' => 'String one',
        true => 'Boolean true',
        1.0 => 'Float one',
        default => 'Something else'
    };
}

echo processValue(1);     // Integer one
echo processValue('1');   // String one
echo processValue(true);  // Boolean true

// Complex expressions
function calculateShipping($weight, $zone) {
    return match (true) {
        $weight <= 1 && $zone === 'local' => 5.00,
        $weight <= 1 && $zone === 'national' => 8.00,
        $weight <= 5 && $zone === 'local' => 10.00,
        $weight <= 5 && $zone === 'national' => 15.00,
        default => 25.00
    };
}

// Enum matching (PHP 8.1+)
enum Status {
    case PENDING;
    case APPROVED;
    case REJECTED;
}

function getStatusColor(Status $status): string {
    return match ($status) {
        Status::PENDING => 'yellow',
        Status::APPROVED => 'green',
        Status::REJECTED => 'red'
    };
}

// Object matching
class HttpResponse {
    public function __construct(public int $code) {}
}

function handleResponse(HttpResponse $response): string {
    return match ($response->code) {
        200, 201 => 'Success',
        400, 422 => 'Client error',
        401, 403 => 'Authentication error',
        404 => 'Not found',
        500, 502, 503 => 'Server error',
        default => 'Unknown response'
    };
}

// Array patterns (PHP 8.0+)
function analyzeArray($data): string {
    return match (count($data)) {
        0 => 'Empty array',
        1 => 'Single item: ' . $data[0],
        2 => 'Two items: ' . implode(', ', $data),
        default => 'Multiple items (' . count($data) . ')'
    };
}

// Function calls in match arms
function processOrder($type) {
    return match ($type) {
        'express' => calculateExpressShipping(),
        'standard' => calculateStandardShipping(),
        'pickup' => 0,
        default => throw new InvalidArgumentException('Unknown order type')
    };
}

function calculateExpressShipping(): float {
    return 25.00;
}

function calculateStandardShipping(): float {
    return 10.00;
}

// Exception throwing
function validateAge($age) {
    return match (true) {
        $age < 0 => throw new InvalidArgumentException('Age cannot be negative'),
        $age < 18 => 'Minor',
        $age < 65 => 'Adult',
        default => 'Senior'
    };
}

// Class instantiation
abstract class Logger {
    abstract public function log(string $message): void;
}

class FileLogger extends Logger {
    public function log(string $message): void {
        file_put_contents('app.log', $message . PHP_EOL, FILE_APPEND);
    }
}

class DatabaseLogger extends Logger {
    public function log(string $message): void {
        // Log to database
    }
}

function createLogger($type): Logger {
    return match ($type) {
        'file' => new FileLogger(),
        'database' => new DatabaseLogger(),
        default => throw new InvalidArgumentException('Unknown logger type')
    };
}

// API response formatting
function formatApiResponse($data, $format) {
    return match ($format) {
        'json' => json_encode($data),
        'xml' => simplexml_load_string($data)->asXML(),
        'csv' => implode(',', $data),
        'text' => is_array($data) ? implode("\n", $data) : (string)$data,
        default => $data
    };
}

// Database operation routing
function executeQuery($operation, $table, $data = []) {
    return match ($operation) {
        'select' => "SELECT * FROM $table",
        'insert' => "INSERT INTO $table VALUES (" . implode(',', $data) . ")",
        'update' => "UPDATE $table SET " . implode(',', $data),
        'delete' => "DELETE FROM $table",
        default => throw new InvalidArgumentException('Unsupported operation')
    };
}

// Configuration management
function getConfig($environment) {
    return match ($environment) {
        'development' => [
            'debug' => true,
            'database' => 'dev_db',
            'cache' => false
        ],
        'staging' => [
            'debug' => true,
            'database' => 'staging_db',
            'cache' => true
        ],
        'production' => [
            'debug' => false,
            'database' => 'prod_db',
            'cache' => true
        ],
        default => throw new InvalidArgumentException('Unknown environment')
    };
}

// HTTP method routing
function routeRequest($method, $path) {
    return match ([$method, $path]) {
        ['GET', '/'] => 'showHomePage()',
        ['GET', '/users'] => 'listUsers()',
        ['POST', '/users'] => 'createUser()',
        ['GET', '/users/profile'] => 'showProfile()',
        ['PUT', '/users/profile'] => 'updateProfile()',
        ['DELETE', '/users/profile'] => 'deleteProfile()',
        default => 'show404Page()'
    };
}

// Validation rules
function validateInput($field, $value) {
    return match ($field) {
        'email' => filter_var($value, FILTER_VALIDATE_EMAIL) !== false,
        'url' => filter_var($value, FILTER_VALIDATE_URL) !== false,
        'age' => is_numeric($value) && $value >= 0 && $value <= 150,
        'phone' => preg_match('/^\+?[\d\s\-\(\)]+$/', $value),
        'password' => strlen($value) >= 8,
        default => !empty($value)
    };
}

// Performance comparison
$iterations = 1000000;

// Match expression timing
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
    $result = match ($i % 3) {
        0 => 'zero',
        1 => 'one',
        2 => 'two'
    };
}
$matchTime = microtime(true) - $start;

// Switch statement timing
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
    switch ($i % 3) {
        case 0:
            $result = 'zero';
            break;
        case 1:
            $result = 'one';
            break;
        case 2:
            $result = 'two';
            break;
    }
}
$switchTime = microtime(true) - $start;

echo "Match: {$matchTime}s, Switch: {$switchTime}s\n";

Explanation

Match expressions provide strict comparison, no fall-through, and direct value returns. They're more concise than switch statements and prevent common bugs.

Use match for simple value mapping and switch for complex multi-statement logic. Match expressions must be exhaustive or include a default case.

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Php