(new Soapbox())->shout(array_map('strtoupper', $opinions)); //Shaun's blog


Me, elsewhere

GitHub
parseword
Miscellaneous public code

snuze
A PHP API client for Reddit

Twitter
@parseword
I don't tweet much

XMPP chat
xmpp@shaunc.com
(Pidgin, Miranda, Swift, etc.)


Perfect is the enemy of good enough.

Quirks and caveats of strict_types=1 in PHP

Posted February 09, 2018 by shaun

PHP 7 introduced optional strict typing, which disables the implicit type conversion that PHP is (in)famous for. Strict typing requires that if a function's argument has a declared type, you must pass that exact type of scalar value. To implement strict typing in a given PHP file, you simply add one declare() call at the very top:

<?php
declare(strict_types=1);

I'm not going to argue for or against strict typing here, other than to say that turning it on sacrifices some convenience in exchange for some added security. Instead, this post is intended to catalog any quirks or caveats that I've encountered after enabling strict_types and how I worked around them.

The fgets() pattern

When processing a large file line-by-line to avoid loading its entire contents into memory, I've long used a pattern like this:

if (file_exists('/tmp/foo.txt') && $fp = fopen('/tmp/foo.txt', 'r')) {
    while (!feof($fp)) {
        if (empty($line = trim(fgets($fp)))) {
            continue;
        }
        // ... do something with $line ...
    }
}

This reads each line from the file, strips any whitespace including the trailing newline, and disregards blank lines which I typically don't want to process. However, when strict_types is enabled, the call to trim() will throw a TypeError once EOF is encountered:

Fatal error: Uncaught TypeError: trim() expects parameter 1 to be string, boolean given

This is because the final call to fgets() returns false to indicate EOF. Under PHP's default behavior, that false is silently massaged into an empty string '', which makes trim() happy. But with strict_types enabled, that conversion goes away, and it's up to you to ensure that what gets passed into trim() is a string. There are a variety of approaches, but I settled on one that I felt was the least disruptive:

if (file_exists('/tmp/foo.txt') && $fp = fopen('/tmp/foo.txt', 'r')) {
    while (!feof($fp)) {
        if (empty($line = trim(fgets($fp) ?: ''))) { //<-- Elvis at work
            continue;
        }
        // ... do something with $line ...
    }
}

The ternary operator ?:, sometimes known in this form as the Elvis operator, coalesces the value passed to trim(). If the fgets() call succeeds and returns a string, that's what gets trimmed and everything proceeds as normal. Otherwise, an empty string gets substituted, and the loop will stop the next time around due to the EOF condition.



Recent articles

📰 Jay Niffley, Man of Mystery

📰 Compiling Doxygen on FreeBSD without LaTeX and Ghostscript

📰 Introducing Snuze, a PHP client for the Reddit API

📰 jisusaiche: Java's installer telemetry

📰 BIND client log error "query_find: query_getdb failed"

📰 Resolving "The lang/perl5.24 port has been deleted: Has expired" portmaster error

📰 Armagaddon2 interim fix for Firefox 56 and other old versions

📰 Strange DNS queries: qname "miep", qtype ANY

📰 Undeliverable as addressed: A massive broken spam campaign?

📰 Using WITH_META_MODE and ccache for FreeBSD build boosts

📰 Resolving subversion error E000013: Unable to create pristine install stream

📰 Enhancements to SmokePing's AnotherDNS probe

📰 Generating vanity DNSSEC key tags

📰 DDoS involving forged packets from 23.225.141.70

📰 Website integrity monitoring through version control

▲ Back to top | Permalink to this page