Type Conversion and Type Coercion in PHP
A practical look at PHP type conversion and coercion: gettype, settype, var_dump, and why strict_types belongs in every file.
Written 4 years ago — it reflects the conditions of that time.
I have a long history with PHP’s type system. For years I treated PHP’s “thinking about types for you” as a convenience — then I learned it was also a trap, through hours lost debugging. This post approaches PHP type conversion and type coercion not as a feature list, but as a decision point.
PHP infers the type for you
When you declare a variable in PHP you don’t specify its type; PHP looks at the value and decides. You can see that decision with gettype():
$veri = "Muhammet Şafak";
echo gettype($veri); // string
gettype() returns one of: boolean, integer, double (including float), string, array, object, resource, NULL. In practice I rarely call it — but understanding how PHP sees a value is the foundation for every decision that follows.
Conversion: be explicit
There are a few ways to change a value’s type. settype() mutates the variable in place; cast operators ((int), (string), (bool)…) produce a new value; functions like intval() and strval() return the converted equivalent without touching the original.
$veri = "10";
$sayi = (int) $veri; // 10 — $veri is untouched
settype($veri, "integer"); // $veri is now an integer
All three work. My preference is cast operators: I want to see the intent while reading the code. Because settype() silently transforms a variable into something else, anyone reading the code later — usually me — can mistakenly assume the variable is still a string. Explicit conversion is explicit intent.
The real issue: type enforcement
PHP 7 introduced the ability to declare type hints for function parameters and return values. It’s one of the most valuable additions in the language’s history:
function topla(int $x, int $y): int
{
return $x + $y;
}
A type declaration is not just a constraint — it’s documentation. Anyone looking at a function signature sees exactly what it expects and what it returns, without guessing. When my own codebase grew to tens of thousands of lines, I felt the value of this concretely.
But there is a trap. By default PHP behaves “loosely”:
function topla(int $x, int $y): int { return $x + $y; }
echo topla("3", "4"); // 7 — strings were silently converted to int
"3" was a string; PHP accepted it as an int. It usually looks harmless — until something like "3 apples" throws a TypeError.
strict_types: non-negotiable
That’s why, for years, the first line of every PHP file I write is this:
declare(strict_types=1);
With that line, PHP no longer silently corrects a type mismatch — it throws a TypeError. You see the error the moment you call the function, not in production. The behaviour marketed as “flexibility” is really just debt that defers bugs to a later date — strict_types settles that debt.
The type is correct; the value still needs validation
A type hint says int; it does not say “int greater than zero”. My fastest tool for inspecting both a variable’s type and its value is still var_dump():
var_dump(3.14); // float(3.14)
The type system guarantees the type; it does not guarantee that the value satisfies a business rule. It’s important to keep type enforcement and value validation separate: one answers “is this a number?”, the other answers “is this number a valid age?”.
Summary
PHP’s loose type system is marketed as a convenience — but left unchecked, it is a source of ambiguity. My approach is clear: declare(strict_types=1) in every file, type declarations on every function, explicit casts for every conversion. The language gives you the freedom not to think about types — I prefer to hand that freedom back and make types visible again. In a long-lived codebase, the returns from this small discipline compound over time.
Experiments on this topic
PHP packages that stand alone and still work together
Modular PHP libraries that work without being tied to a framework; they became the 39-package InitPHP ecosystem and Framework3.
What it does today
A family of 39 PHP packages — Router, Database, Cache, Mailer, Socket and the rest — each usable standalone or together, all shipped through Composer/Packagist under MIT. Any PHP developer who wants the pieces without buying into a framework can install them today.
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.