Navigation

Php

How to Handle PHP's Mixed Type

Use PHP 8.0+ mixed type for maximum flexibility when any data type is acceptable. Handle dynamic data while maintaining code clarity.

Table Of Contents

Problem

You need to create functions that accept any data type, but using no type declaration makes code unclear and mixed provides better documentation.

Solution

// Basic mixed type usage
function processAnyValue(mixed $value): string {
    return "Received: " . gettype($value) . " with value: " . var_export($value, true);
}

echo processAnyValue(42);           // Received: integer with value: 42
echo processAnyValue("hello");      // Received: string with value: 'hello'
echo processAnyValue([1, 2, 3]);    // Received: array with value: array(...)
echo processAnyValue(true);         // Received: boolean with value: true

// JSON data handling
function parseJsonValue(mixed $value): mixed {
    if (is_string($value)) {
        return json_decode($value, true);
    }
    
    if (is_array($value) || is_object($value)) {
        return $value; // Already parsed
    }
    
    return $value; // Primitive value
}

$jsonString = '{"name": "John", "age": 30}';
$arrayData = ["name" => "Jane", "age" => 25];
$objectData = (object)["name" => "Bob", "age" => 35];

$result1 = parseJsonValue($jsonString);  // Parsed array
$result2 = parseJsonValue($arrayData);   // Original array
$result3 = parseJsonValue($objectData);  // Original object

// Database value formatting
function formatDbValue(mixed $value): string {
    return match (true) {
        $value === null => 'NULL',
        is_bool($value) => $value ? 'TRUE' : 'FALSE',
        is_int($value) || is_float($value) => (string)$value,
        is_string($value) => "'" . addslashes($value) . "'",
        is_array($value) => "'" . addslashes(json_encode($value)) . "'",
        is_object($value) => "'" . addslashes(serialize($value)) . "'",
        default => 'NULL'
    };
}

echo formatDbValue(123);                    // 123
echo formatDbValue("O'Connor");             // 'O\'Connor'
echo formatDbValue(['a', 'b']);             // '["a","b"]'
echo formatDbValue(null);                   // NULL

// Cache storage
class Cache {
    private array $storage = [];
    
    public function set(string $key, mixed $value, int $ttl = 3600): void {
        $this->storage[$key] = [
            'value' => $value,
            'expires' => time() + $ttl
        ];
    }
    
    public function get(string $key): mixed {
        if (!isset($this->storage[$key])) {
            return null;
        }
        
        $item = $this->storage[$key];
        
        if (time() > $item['expires']) {
            unset($this->storage[$key]);
            return null;
        }
        
        return $item['value'];
    }
}

$cache = new Cache();
$cache->set('user_id', 123);
$cache->set('user_data', ['name' => 'John', 'email' => 'john@example.com']);
$cache->set('is_admin', true);
$cache->set('config', (object)['theme' => 'dark']);

// Configuration management
class Config {
    private array $config = [];
    
    public function set(string $key, mixed $value): void {
        $this->config[$key] = $value;
    }
    
    public function get(string $key, mixed $default = null): mixed {
        return $this->config[$key] ?? $default;
    }
    
    public function has(string $key): bool {
        return array_key_exists($key, $this->config);
    }
}

$config = new Config();
$config->set('app.name', 'My Application');
$config->set('app.debug', true);
$config->set('database.connections', [
    'default' => ['host' => 'localhost', 'port' => 3306]
]);

// API request handling
function processApiRequest(mixed $data): array {
    // Normalize input data
    if (is_string($data)) {
        $data = json_decode($data, true);
    }
    
    if (!is_array($data)) {
        return ['error' => 'Invalid data format'];
    }
    
    return [
        'success' => true,
        'processed_data' => $data,
        'timestamp' => time()
    ];
}

// Form data processing
function sanitizeFormInput(mixed $input): mixed {
    if (is_string($input)) {
        return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
    }
    
    if (is_array($input)) {
        return array_map('sanitizeFormInput', $input);
    }
    
    if (is_object($input)) {
        foreach ($input as $property => $value) {
            $input->$property = sanitizeFormInput($value);
        }
        return $input;
    }
    
    return $input; // Return as-is for other types
}

$formData = [
    'name' => '  John Doe  ',
    'email' => 'john@example.com',
    'message' => '<script>alert("xss")</script>Hello',
    'preferences' => [
        'theme' => '  dark  ',
        'notifications' => true
    ]
];

$sanitized = sanitizeFormInput($formData);

// Event data handling
class EventDispatcher {
    private array $listeners = [];
    
    public function on(string $event, callable $callback): void {
        $this->listeners[$event][] = $callback;
    }
    
    public function emit(string $event, mixed $data = null): void {
        if (!isset($this->listeners[$event])) {
            return;
        }
        
        foreach ($this->listeners[$event] as $callback) {
            $callback($data);
        }
    }
}

$dispatcher = new EventDispatcher();

$dispatcher->on('user.login', function(mixed $data) {
    echo "User logged in: " . json_encode($data) . "\n";
});

$dispatcher->emit('user.login', ['user_id' => 123, 'timestamp' => time()]);
$dispatcher->emit('user.login', 'Simple string data');

// Type validation utility
function validateType(mixed $value, string $expectedType): bool {
    return match ($expectedType) {
        'string' => is_string($value),
        'int', 'integer' => is_int($value),
        'float', 'double' => is_float($value),
        'bool', 'boolean' => is_bool($value),
        'array' => is_array($value),
        'object' => is_object($value),
        'null' => is_null($value),
        'numeric' => is_numeric($value),
        'scalar' => is_scalar($value),
        'callable' => is_callable($value),
        default => false
    };
}

// Logging system
class Logger {
    public function log(string $level, string $message, mixed $context = null): void {
        $timestamp = date('Y-m-d H:i:s');
        $contextStr = '';
        
        if ($context !== null) {
            $contextStr = ' Context: ';
            if (is_scalar($context)) {
                $contextStr .= (string)$context;
            } else {
                $contextStr .= json_encode($context);
            }
        }
        
        echo "[{$timestamp}] {$level}: {$message}{$contextStr}\n";
    }
    
    public function error(string $message, mixed $context = null): void {
        $this->log('ERROR', $message, $context);
    }
    
    public function info(string $message, mixed $context = null): void {
        $this->log('INFO', $message, $context);
    }
}

$logger = new Logger();
$logger->info('Application started');
$logger->error('Database connection failed', ['host' => 'localhost', 'port' => 3306]);
$logger->info('User action', 'User clicked button');

// Data transformation pipeline
function transformData(mixed $data, array $transformers): mixed {
    foreach ($transformers as $transformer) {
        if (is_callable($transformer)) {
            $data = $transformer($data);
        }
    }
    return $data;
}

$data = "  HELLO WORLD  ";
$transformed = transformData($data, [
    'trim',
    'strtolower',
    fn($str) => str_replace(' ', '_', $str)
]);
// Result: "hello_world"

// Session data handling
class Session {
    public function set(string $key, mixed $value): void {
        $_SESSION[$key] = $value;
    }
    
    public function get(string $key, mixed $default = null): mixed {
        return $_SESSION[$key] ?? $default;
    }
    
    public function flash(string $key, mixed $value): void {
        $_SESSION['_flash'][$key] = $value;
    }
    
    public function getFlash(string $key, mixed $default = null): mixed {
        $value = $_SESSION['_flash'][$key] ?? $default;
        unset($_SESSION['_flash'][$key]);
        return $value;
    }
}

// Performance consideration: type checking
function processValue(mixed $value): string {
    // Fast type checking for performance-critical code
    if (is_string($value)) {
        return "String: " . $value;
    }
    
    if (is_int($value)) {
        return "Integer: " . $value;
    }
    
    if (is_array($value)) {
        return "Array with " . count($value) . " elements";
    }
    
    // Fallback for other types
    return "Other: " . gettype($value);
}

// Best practices: when to use mixed
// ✅ Good uses:
// - Generic containers (arrays, collections)
// - Configuration systems
// - Cache implementations
// - JSON/API data handling
// - Event systems

// ❌ Avoid mixed when:
// - You know the specific types needed
// - Type safety is critical
// - Performance is crucial (type checking overhead)
// - Union types can be more specific

Explanation

The mixed type accepts any value type and is equivalent to string|int|float|bool|array|object|callable|resource|null. Use it when true flexibility is needed.

Always add runtime type checking with is_*() functions or gettype() when using mixed. Consider union types for better type safety when possible.

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Php