For PHP 8+, validate a mixed value with is_int and return $n % 2 !== 0. Avoid letting coercion define your input policy.
In PHP, use a nonzero remainder to test an integer for oddness. At an application boundary, check the value's actual type before applying %. This avoids quietly converting a numeric string or a fraction into a different input.
A helper with explicit type validation
<?php
function isOdd(mixed $n): bool {
if (!is_int($n)) {
throw new TypeError('Expected an integer');
}
return $n % 2 !== 0;
}
var_dump(isOdd(43)); // bool(true)
var_dump(isOdd(-43)); // bool(true)
var_dump(isOdd(-42)); // bool(false)
var_dump(isOdd(0)); // bool(false)
Understand coercion and signed remainders
The mixed parameter makes this PHP 8+ example explicit: callers may supply anything, but the function accepts only an actual integer. The runtime check rejects '43', 43.0, 43.5, true, and null. Even a floating-point value with no fractional part is outside this particular contract.
That strictness is deliberate. PHP's remainder operation converts its operands to integers before calculating a result. Applying it directly to a fractional value would therefore answer a question about a converted integer. The arithmetic operator manual documents the conversions and the sign of the result.
Do not use $n % 2 === 1 when negative values are allowed. A negative odd input can produce -1, so that comparison misses it. $n % 2 !== 0 accepts either nonzero remainder and also returns false for zero.
Separate text parsing from integer checks
The is_int reference distinguishes integer values from numeric strings. This matters for web forms, where text input commonly arrives as a string. Parse and validate that text before calling isOdd, then report parsing errors separately. A blanket integer cast can discard a fractional part and hide malformed input.
Choosing a typed int parameter alone is a different API choice. PHP's scalar coercion behavior depends on how the call is made and the applicable strict-typing rules. Using mixed plus is_int makes the rejection policy visible inside the function, including for calls from another file.
Test platform limits and failure behavior
Exercise the platform's actual limits instead of assuming every deployment has the same integer width:
var_dump(isOdd(PHP_INT_MIN)); // bool(false)
var_dump(isOdd(PHP_INT_MAX)); // bool(true)
try {
isOdd('43');
} catch (TypeError $error) {
echo $error->getMessage();
}
The helper needs neither an absolute-value conversion nor a floating-point intermediate. For an integer larger than PHP can represent, retain its decimal text and use an appropriate arbitrary-precision approach; first coercing it into a native number can lose the last digit.
In production code, keep invalid data distinct from even data. Returning false for every rejected input makes a validation problem look like a mathematical result. An exception, or a separate validation result chosen by the caller, preserves the distinction.