PHP mixed Type Tutorial
last modified May 18, 2025
In this tutorial, we will explore the mixed
type in PHP, a flexible
type declaration that allows a variable to hold values of multiple types.
The mixed
type, introduced in PHP 8.0, explicitly indicates
that a parameter, return value, or property can accept values of any
type. This is particularly useful when working with dynamic data structures,
such as API responses or user-generated input, where the data type may vary.
Using mixed
helps improve code readability and documentation by
signaling that a function or variable is designed to handle multiple types.
However, developers should use it carefully, as excessive reliance on
mixed
can lead to unclear type expectations and potential
runtime errors.
Basic mixed type usage
The mixed type can be used in parameter types, return types, and property types. It's equivalent to not having a type declaration at all but makes the code more explicit about its dynamic nature. All values are valid for mixed parameters.
<?php declare(strict_types=1); function processValue(mixed $input): mixed { if (is_int($input)) { return $input * 2; } if (is_string($input)) { return strtoupper($input); } if (is_array($input)) { return count($input); } return $input; } echo processValue(10) . "\n"; // 20 echo processValue("hello") . "\n"; // HELLO echo processValue([1, 2, 3]) . "\n"; // 3 echo processValue(null) . "\n"; // null class DataProcessor { public mixed $data; public function __construct(mixed $data) { $this->data = $data; } public function getProcessedData(): mixed { if (is_callable($this->data)) { return ($this->data)(); } return $this->data; } } $processor = new DataProcessor(fn() => "Callback result"); echo $processor->getProcessedData() . "\n"; // Callback result
This example shows mixed used in various contexts. The processValue function accepts and returns any type. The DataProcessor class uses mixed for both a property and method return type. Note that mixed includes all possible types including null, callables, and resources.
λ php basic_mixed.php 20 HELLO 3 Callback result
mixed vs no type declaration
While mixed and no type declaration both accept any value, mixed explicitly documents this intention. Modern PHP code should prefer mixed over omitting type declarations when any type is acceptable.
<?php declare(strict_types=1); // Old style - no type hint (implicit mixed) function oldStyle($value) { return $value; } // New style - explicit mixed function newStyle(mixed $value): mixed { return $value; } // Union type equivalent (pre-PHP 8.0 alternative) function unionEquivalent( string|int|float|bool|array|object|null $value ) { return $value; } // Strict comparison of behaviors function testBehavior($input): void { echo "Input: " . gettype($input) . "\n"; echo "oldStyle: " . gettype(oldStyle($input)) . "\n"; echo "newStyle: " . gettype(newStyle($input)) . "\n"; echo "unionEquivalent: " . gettype(unionEquivalent($input)) . "\n\n"; } testBehavior(42); testBehavior("text"); testBehavior(null); testBehavior(new stdClass()); // Parameter type compatibility class ParentClass { public function example($param) { // Implicit mixed } } class ChildClass extends ParentClass { public function example(mixed $param) { // Explicit mixed - compatible with parent } }
This demonstrates that mixed is functionally equivalent to no type but more explicit. The union type equivalent shows how cumbersome it would be to list all possible types. Mixed is also compatible with untyped parameters in inheritance.
λ php mixed_vs_no_type.php Input: integer oldStyle: integer newStyle: integer unionEquivalent: integer Input: string oldStyle: string newStyle: string unionEquivalent: string Input: NULL oldStyle: NULL newStyle: NULL unionEquivalent: NULL Input: object oldStyle: object newStyle: object unionEquivalent: object
Type checking with mixed
When working with mixed values, you often need to check the actual type before performing operations. PHP provides several type-checking functions and operators for this purpose.
<?php declare(strict_types=1); function handleMixed(mixed $value): string { // Type checking functions if (is_int($value)) { return "Integer: " . ($value * 2); } if (is_string($value)) { return "String length: " . strlen($value); } if (is_array($value)) { return "Array count: " . count($value); } if (is_object($value)) { return "Object class: " . get_class($value); } if (is_callable($value)) { return "Callable result: " . $value(); } if (is_null($value)) { return "Null value"; } return "Unknown type: " . gettype($value); } // Test cases echo handleMixed(42) . "\n"; echo handleMixed("PHP") . "\n"; echo handleMixed([1, 2, 3]) . "\n"; echo handleMixed(new DateTime()) . "\n"; echo handleMixed(fn() => "Called") . "\n"; echo handleMixed(null) . "\n"; echo handleMixed(tmpfile()) . "\n"; // Using match expression (PHP 8.0+) function handleMixedWithMatch(mixed $value): string { return match(true) { is_numeric($value) => "Number: $value", is_iterable($value) => "Iterable with " . count([...$value]) . " items", $value instanceof DateTimeInterface => "Date: " . $value->format('Y-m-d'), default => gettype($value) }; } echo "\nWith match:\n"; echo handleMixedWithMatch(3.14) . "\n"; echo handleMixedWithMatch(new ArrayIterator([1, 2])) . "\n"; echo handleMixedWithMatch(new DateTime()) . "\n";
This shows comprehensive type checking for mixed values. The example uses is_* functions, instanceof, and match expression for type handling. Always check types before operations to avoid runtime errors with mixed values.
λ php type_checking.php Integer: 84 String length: 3 Array count: 3 Object class: DateTime Callable result: Called Null value Unknown type: resource With match: Number: 3.14 Iterable with 2 items Date: 2025-05-18
mixed in class properties
Mixed type properties can hold any value but should be used judiciously. Consider adding documentation with @var to indicate expected types when using mixed properties.
<?php declare(strict_types=1); class Configuration { /** @var mixed Can be string, array, or bool */ public mixed $logging; /** @var mixed Callable or null */ private mixed $formatter; public function __construct(mixed $logging, mixed $formatter = null) { $this->logging = $logging; $this->formatter = $formatter; } public function formatMessage(string $message): string { if ($this->formatter === null) { return $message; } if (!is_callable($this->formatter)) { throw new RuntimeException("Formatter must be callable"); } return ($this->formatter)($message); } public function shouldLog(): bool { if (is_bool($this->logging)) { return $this->logging; } return $this->logging !== null; } } // Usage examples $config1 = new Configuration(true); echo $config1->shouldLog() ? "Logging enabled\n" : "Logging disabled\n"; $config2 = new Configuration( ['level' => 'debug'], fn($msg) => "[DEBUG] $msg" ); echo $config2->formatMessage("Test message") . "\n"; // Type safety checks try { $badConfig = new Configuration(null, "not callable"); echo $badConfig->formatMessage("Will fail"); } catch (RuntimeException $e) { echo "Error: " . $e->getMessage() . "\n"; }
The example shows mixed properties with documentation. The class performs runtime type checking when using the properties. While mixed provides flexibility, always validate before use to maintain type safety.
λ php mixed_properties.php Logging enabled [DEBUG] Test message Error: Formatter must be callable
mixed in return types
Functions with mixed return types can return any value. Document expected return types with @return when using mixed to help developers understand the possible return values.
<?php declare(strict_types=1); /** * @return mixed Returns parsed value or false on failure */ function parseInput(string $input): mixed { if ($input === '') return null; if ($input === 'true') return true; if ($input === 'false') return false; if (is_numeric($input)) return $input + 0; // int or float if (str_starts_with($input, '[')) { return json_decode($input, true) ?? $input; } return $input; } // Test cases $tests = ['42', '3.14', 'true', 'false', '[1,2,3]', 'invalid', '']; foreach ($tests as $test) { $result = parseInput($test); echo "$test => " . gettype($result) . " (" . var_export($result, true) . ")\n"; } // Real-world example: configuration loader class ConfigLoader { /** * @return mixed Returns parsed config array or throws exception */ public function loadConfig(string $path): mixed { if (!file_exists($path)) { throw new RuntimeException("Config file not found"); } $content = file_get_contents($path); $ext = pathinfo($path, PATHINFO_EXTENSION); return match($ext) { 'json' => json_decode($content, true), 'yml', 'yaml' => yaml_parse($content), 'php' => include $path, default => throw new RuntimeException("Unsupported config format") }; } }
This demonstrates functions returning mixed types. The parseInput function returns various types based on input parsing. The ConfigLoader shows a real-world example where different file formats return different structures.
λ php mixed_returns.php 42 => integer (42) 3.14 => double (3.14) true => boolean (true) false => boolean (false) [1,2,3] => array ( 0 => 1, 1 => 2, 2 => 3, ) invalid => string ('invalid') => NULL (NULL)
Best practices with mixed
While mixed provides flexibility, it should be used thoughtfully. Follow these best practices to maintain code quality when working with mixed types.
<?php declare(strict_types=1); // 1. Always document expected types with @var or @return /** @var mixed$id */ $id = $_GET['id'] ?? null; // 2. Validate mixed values before use function processUserId(mixed $id): int { if (!is_numeric($id)) { throw new InvalidArgumentException("ID must be numeric"); } return (int)$id; } // 3. Prefer more specific types when possible // Bad: function store(mixed $data) { ... } // Good: function store(array|object $data) { ... } // 4. Use helper functions for type checking function isCountable(mixed $value): bool { return is_array($value) || $value instanceof Countable; } // 5. Consider creating wrapper classes instead of using mixed final class DynamicValue { public function __construct(private mixed $value) {} public function asInt(): int { if (!is_numeric($this->value)) { throw new RuntimeException("Not a number"); } return (int)$this->value; } public function asString(): string { return (string)$this->value; } public function getRawValue(): mixed { return $this->value; } } // 6. Use mixed sparingly in public APIs interface Logger { // Better than mixed: public function log(string|array $message); public function log(mixed $message): void; } // 7. Always handle null explicitly with mixed function handleNullable(mixed $input): void { if ($input === null) { echo "Got null\n"; return; } // Process non-null value } // 8. Consider using union types instead of mixed when possible // (PHP 8.0+) function betterThanMixed(string|int|float|bool|null $value) { // More type safety than mixed }
These practices help maintain code quality when using mixed. Key points include proper documentation, validation, preferring specific types when possible, and creating wrapper classes for better type safety.
The mixed type in PHP serves an important role in the type system. Key points to remember about mixed:
- Represents any possible PHP type, including
null
. - Makes dynamic typing intentions explicit in code.
- Requires careful type checking before performing operations.
- Should be documented with
@var
or@return
annotations. - Best used sparingly when more specific types aren't possible.
- Can be gradually replaced with union types in many cases.
- Needs proper null handling to avoid unexpected behavior.
While mixed provides flexibility, prefer more specific types whenever possible. Use mixed judiciously and always validate types at runtime when working with mixed values to maintain code reliability.
In this tutorial, we explored the mixed type in PHP, its usage, and best practices. We covered basic usage, type checking, and how to work with mixed in class properties and return types. We also discussed the importance of documentation and validation when using mixed.
Author
List all PHP tutorials.