Scalar Type Declarations and Return Types in PHP 7
A practical look at how I use scalar type declarations and return type definitions introduced in PHP 7.
Written 10 years ago — it reflects the conditions of that time.
A few weeks have passed since I moved to PHP 7. The performance gains were already on everyone’s radar, but what really drew me in were the language-level features: scalar type declarations and return types. Once I started using them in my projects, I noticed just how much more expressive function signatures become.
In this post I’ll walk through how I use both features, what difference strict_types mode makes, and a few things worth keeping in mind.
What are scalar type declarations?
Since PHP 5, you could declare types for class names and arrays. PHP 7 extends this to scalar types: int, float, string, and bool.
<?php
function toplam(int $a, int $b): int
{
return $a + $b;
}
echo toplam(3, 5); // 8
Back when I wrote this kind of function in PHP 5, I’d just add a docblock for the parameter types; even PHP 5.6’s variadic parameters could only carry so much of that into the signature. Now I can put that information directly in the signature — IDE support improves, and anyone reading the code immediately knows what the function expects.
Strict mode with strict_types
By default, PHP performs type coercion. If you pass "5" to a function that expects int, PHP will silently convert it to 5. That behavior is occasionally convenient, but more often dangerous.
Adding the following declaration at the top of a file removes that flexibility:
<?php
declare(strict_types=1);
function carpim(int $a, int $b): int
{
return $a * $b;
}
carpim(4, "3"); // Throws TypeError
The strict_types=1 declaration applies only to the file it appears in, not the entire application. This is an important detail: the file that matters is the one making the call — if the calling file has strict_types enabled, no coercion happens even when the file where the function is defined has it disabled.
I’ve made it a habit to add this declaration to every new file I write. It took a bit of adjustment at first — form data from users always arrives as strings, so you need to cast explicitly. But that’s actually a good thing: you can now see exactly where your code performs type conversions.
Return type declarations
You can also declare what a function returns, right in its signature:
<?php
declare(strict_types=1);
function kullaniciBul(int $id): array
{
// fetch user from the database
return ['id' => $id, 'ad' => 'Ahmet'];
}
function aktifMi(int $id): bool
{
return $id > 0;
}
This is especially useful for functions that return bool. Previously, without checking the docblock, you had to read the function body to answer “what does this return?”
A note on the void return type
PHP 7.0 does not yet have a void return type — that arrives in PHP 7.1. For now, you can only annotate functions that actually return a value.
Nullable types aren’t here yet either
Similarly, the ?int syntax is also a PHP 7.1 feature. If a function can return either int or null, you currently have to either leave out the return type declaration or document it via a docblock.
<?php
// Workaround for nullable in PHP 7.0
// We can't write the return type, so we document with a docblock
/**
* @return int|null
*/
function sonKayitId()
{
// returns null if the table is empty
return null;
}
This limitation is annoying, but I can wait for 7.1. What matters now is building the habit of writing type declarations.
What has changed in practice?
After a few weeks of experience, here is what I can say: type declarations turn a function signature into a contract. I’m telling the person I’m working with — or my future self — “pass this type, get this type back.” Error messages have also become far more readable; when you get a TypeError, you immediately know where the problem is.
On the other hand, retrofitting types onto an existing codebase needs to be done very carefully. Legacy code that relies on implicit coercion can blow up in unexpected places once strict_types is in the picture. The sensible approach is to use it immediately for new files and tread carefully with existing ones.
PHP 7 has taken a more useful step here than I expected. It’s encouraging to see the language moving in this direction.
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.