Contents
PHP strict types explained
Hey, fellow PHP developers. Picture this: it's 2 AM, your coffee's gone cold, and that sneaky bug has been haunting your API endpoint for hours. The logs scream "undefined behavior," but everything looks fine. Sound familiar? I've been there, staring at my glowing monitor, wondering why "10" became 10 without me noticing. That's the wild west of PHP's loose typing—helpful until it bites.
Then I flipped the switch on strict types. Suddenly, errors scream back at you before production. No more silent conversions. Just clean, predictable code that feels like a trusted colleague watching your back. Today, let's dive deep into PHP strict types. Why they matter, how they work, and how they can save your sanity on real projects. If you're hiring PHP devs or hunting for that next gig on platforms like Find PHP, understanding this is table stakes for modern work.
What strict types really do
At its core, strict types in PHP force exact type matching. No more automatic coercion where strings slip into ints like uninvited guests at a party. You declare declare(strict_types=1); right after the opening <?php tag, and boom—PHP throws a TypeError if types don't match precisely.
Without it, PHP plays nice: pass "42" to an int parameter? It converts happily. With strict types? TypeError. Game over for sloppy passes.
But here's the kicker—it's per-file. Strict mode only enforces calls made from that file. If your strict file calls a loose one, the caller's rules win. Call a strict function from a loose file? Coercion happens anyway. Tricky, right? It caught me off guard on a legacy refactor once, mid-sprint.
Think of it as a contract. Your function says "I want an int," and strict types make sure it gets one—or crashes loudly.
The quiet power in everyday code
Remember that late-night debug? Strict types turn ghosts into clear failures. Here's a simple addition function:
<?php
declare(strict_types=1);
function add(int $x, int $y): int {
return $x + $y;
}
add(10, 20); // Fine: 30
add("10", 20); // TypeError! No coercion.
Without strict_types=1, "10" becomes 10. Silent. Sneaky. With it? Error at call time. You fix it upstream—maybe validate that form input properly.
Now, return types shine too:
<?php
declare(strict_types=1);
function divide(int $x, int $y): int {
return $x / $y; // 10/20 = 0.5, but expects int? TypeError on return!
}
PHP 8+ union types level it up: int|float|string|null. Shorthand ?string for nullable. Power.
Have you ever chased a bug where in_array('3', [1,2,3]) returned true because loose equality? Strict fixes that:
in_array('3', $ids, strict: true); // False. Exact match only.
Named params make it readable. PHP ninjas love it; juniors get it instantly.
Why your team needs this yesterday
Friends, loose typing feels forgiving—until it's not. I've seen production crashes from "0" equaling empty string in a user count. Strict types slash runtime errors. Code reads like prose: "This takes an int ID, returns a string name." New hires grok it fast. Onboarding? Smoother.
Less defensive code too. No more if ($foo !== null || !is_numeric($foo)). Validate at edges—controllers, APIs—then trust the types inside. Your logic flows clean, cognitive load drops.
In teams, it's gold. Complex projects with DB queries? Declare IDs as int. No more string IDs corrupting math. Forms? Strict prevents junk slipping through, better than vague sanitization.
One refactor memory: Migrating a Laravel app. Strict types caught a bool-to-string in a query builder. Saved hours. Downsides? Legacy libs fight back. Introduce gradually—new files first, like Lullabot does.
Best practices that stick:
- Slap
declare(strict_types=1);in every new file. IDE templates automate it (PHPStorm, VS Code snippets). - Type everything: args, returns, properties (PHP 7.4+).
- Linters like PHPStan yell early.
- Consistent across codebase. Half-strict? Chaos.
- Real-world: Email class with readonly string, validation in constructor. Throw on bad input. Crash safe over corrupt data.
Real-world wins and gotchas
Let's get hands-on. Database pulls often return strings for IDs. Strict types force you to cast explicitly:
class UserRepository {
public function find(int $id): User {
// DB returns string '123'? Cast: (int) $row['id']
// Strict ensures you don't forget.
}
}
API handlers? Perfect. Incoming JSON might have float prices as strings. Strict screams: fix the validator.
Math ops? Declare float for precision. No auto-int downgrade ruining decimals.
Security angle: Forms with strict ints block some injections. Better to TypeError than insert garbage.
Union types for flexibility:
function processId(string|int $id): string {
return (string) $id;
}
PHP 8.0+ handles it. Legacy? Stick to basics.
Gotchas I've hit:
- Cross-file calls. Strict caller to loose callee? Caller's strict wins. Loose to strict? Coercion. Test thoroughly.
- Standard funcs like
in_arrayneedstrict: true. - Floats accept ints (PHP quirk), but not vice versa.
- Properties: PHP 7.4 readonly?
public readonly int $id;. Immutable bliss.
In a recent gig hunt via Find PHP, I pitched strict types in interviews. Clients nodded—signals modern PHP chops. Trends? Laravel debates it, but unions make everyone happy. Gradually typed language, evolving.
Tools speed adoption: Rector auto-adds strict declarations safely. Static analysis (Psalm, PHPStan) catches more.
One emotional win: That 2 AM bug? Strict types made my code mine. Predictable. Reliable. I slept better knowing it wouldn't betray me.
Building stricter habits
Start small. New project? Strict everywhere. Legacy? New classes first. Team buy-in: Share a bug story. Show the before/after.
Example Email class in action:
<?php
declare(strict_types=1);
class Email {
public function __construct(public readonly ?string $value) {
if (!$this->isValid($value)) {
throw new LogicException('Invalid email');
}
}
private function isValid(?string $email): bool {
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
}
Controller:
$rawEmail = $_POST['email'] ?? null;
$email = new Email($rawEmail); // Fails fast if bad.
$mailer->send($email);
Clean. No null checks everywhere. Contract enforced.
For hires: Look for strict in portfolios. It shows discipline. Platforms like Find PHP connect you to devs who live this—trends, jobs, ecosystem vibes.
Strict types aren't perfect. Legacy drag, learning curve. But the wins? Fewer fires, more flow. Code that lasts.
What if your next commit prevented that outage? Feels good, doesn't it. Sit with your editor tonight. Add the declare. Let the errors guide you home.