Mixed and Never Types
PHP's type system includes two special types at opposite ends: mixed , which accepts every value, and never , which describes a function that never returns at all. Both make code more explicit: mixed
PHP's type system includes two special types at opposite ends: mixed, which accepts every value, and never, which describes a function that never returns at all. Both make code more explicit: mixed documents that "any value" is intentional, and never documents that a function always stops execution by throwing an exception or exiting.
| Type | Introduced | Meaning |
|---|---|---|
mixed | PHP 8.0 | Any value: int, float, string, bool, array, object, callable, resource, or null |
never | PHP 8.1 | The function never returns normally (return type only) |
The mixed Type
What It Means
In simple terms: mixed says "this can be anything". It is used where a function genuinely works with values of any type — a cache that stores anything, a configuration reader, a debugging helper.
Technically: mixed is equivalent to the union of all types: array|bool|callable|int|float|null|object|resource|string. It can be used for parameters, return types, and properties.
Example: A Configuration Store
<?php
final class Config
{
public function __construct(private array $values) {}
public function get(string $key, mixed $default = null): mixed
{
return $this->values[$key] ?? $default;
}
}
$config = new Config([
'app_name' => 'GoCourse',
'per_page' => 20,
'debug' => false,
'languages' => ['en', 'ta'],
]);
foreach (['app_name', 'per_page', 'debug', 'languages', 'timezone'] as $key) {
echo str_pad($key, 10) . get_debug_type($config->get($key, 'Asia/Kolkata')) . "\n";
}
Output
app_name string
per_page int
debug bool
languages array
timezone string
- Configuration values can be strings, integers, booleans, or arrays, so
mixedis honest. timezoneis missing, so the default ('Asia/Kolkata') is returned.debugisfalse, and??keeps it, because??only replacesnull— notfalse.
mixed vs No Type
| Declaration | Meaning |
|---|---|
function get($key) | No information: maybe the author forgot to add a type |
function get(mixed $key) | Explicit: any type is intentionally accepted |
With a mixed return type, the function must return a value explicitly. A bare return; is a compile error, because mixed expects a value (write return null; instead).
Rules for mixed
| Rule | Reason | |
|---|---|---|
?mixed is invalid | mixed already includes null | |
mixed cannot be part of a union (`mixed | int`) | It already includes every type |
A child class may widen a parameter to mixed | Contravariance: accepting more is allowed | |
A child class may narrow a mixed return type (for example, to string) | Covariance: returning something more specific is allowed |
Use mixed Sparingly
A mixed value gives no guarantees: code receiving it must check the type before using it (is_string(), is_array(), …). Prefer specific types or unions whenever the possible types are known. mixed is appropriate for generic infrastructure — caches, containers, serializers, configuration — not for business logic.
The never Type
What It Means
In simple terms: A function with the return type never promises that it will never finish normally. Calling it always ends with an exception being thrown, the script exiting, or an endless loop. It is used for helpers such as abort(), redirect(), or fail().
Technically: never is a bottom type: no value has this type. It can only be used as a return type and cannot be combined with other types. If a never function returns — even implicitly by reaching its end — PHP throws a TypeError.
Example: Validation Helpers
<?php
function fail(string $message): never
{
throw new InvalidArgumentException($message);
}
function parseQuantity(string $input): int
{
$quantity = filter_var($input, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 99]]);
if ($quantity === false) {
fail("Invalid quantity: $input");
}
return $quantity;
}
foreach (['3', 'ten', '150'] as $input) {
try {
echo "Quantity: " . parseQuantity($input) . "\n";
} catch (InvalidArgumentException $e) {
echo "Error: " . $e->getMessage() . "\n";
}
}
Output
Quantity: 3
Error: Invalid quantity: ten
Error: Invalid quantity: 150
Why never Helps
- The signature tells readers that
fail()always stops the current flow; no one needs to read its body to know that. - Static analysis tools (PHPStan, Psalm) and IDEs understand that code after
fail(...)inside theifis unreachable. They therefore know that$quantityis anintat thereturnstatement — notint|false— and do not report a type error. - If someone later changes
fail()so that it returns in some case, PHP reports the broken promise immediately.
A never Function That Returns
<?php
function broken(): never
{
echo "Doing something\n";
}
try {
broken();
} catch (TypeError $e) {
echo $e->getMessage() . "\n";
}
Output
Doing something
broken(): never-returning function must not implicitly return
Reaching the end of the function is a violation. An explicit return statement in a never function is rejected at compile time.
Typical never Functions
function redirect(string $url): never
{
header('Location: ' . $url, true, 303);
exit;
}
function abort(int $status, string $message = ''): never
{
http_response_code($status);
echo $message;
exit;
}
function notImplemented(string $feature): never
{
throw new LogicException("$feature is not implemented yet.");
}
Frameworks use the same idea: Laravel's abort() helper, for example, always throws an HTTP exception.
void vs never
void | never | |
|---|---|---|
| Function returns? | Yes, without a value | Never |
| Allowed statements | return; or no return | No return; must throw, exit, or loop forever |
| Code after the call | Continues | Unreachable |
| Example | log(), save(), send() | redirect(), abort(), fail() |
never can also be used when overriding a method: because it is a bottom type, a child class may narrow any return type to never (for example, a method that is not supported in a particular implementation and always throws).
Other Special Standalone Types
Since PHP 8.2, null, false, and true can be used as standalone types:
function alwaysTrue(): true
{
return true;
}
These are rarely needed in application code but help describe existing APIs precisely, especially in subclasses that narrow a parent's bool return type.
Common Mistakes
Using mixed to Avoid Thinking About Types
Effect: No type safety; every caller must check types.
Fix: Use specific types or small unions where possible.
Writing ?mixed or mixed|null
Effect: Compile error.
Fix: Use mixed alone.
Bare return; in a mixed Function
Effect: Compile error.
Fix: return null;.
A never Function That Can Return
Effect: TypeError at runtime when the end of the function is reached.
Fix: Ensure every path throws, exits, or loops forever.
Using void for Functions That Always Throw
Effect: Tools cannot recognize that subsequent code is unreachable.
Fix: Declare never.
Best Practices
- Use
mixedonly for genuinely generic code, and narrow the type as soon as possible. - Declare
mixedexplicitly instead of omitting types, so intent is clear. - Use
neverfor helpers that always throw or exit, such asfail(),abort(), andredirect(). - Let static analysis tools benefit from
neverto understand control flow. - Prefer
voidfor functions that simply return nothing.
Official reference: https\://www.php.net/manual/en/language.types.declarations.php