On Github LawnGnome / php7-drupalcon
Ubuntu 14.04: PHP 5.5.9, supported to April 2019
RHEL 7: PHP 5.4.16, supported to June 2024
Red Hat's support runs for another nine years. Think about where you were in 2006. (Plus, it won't help for Drupal 8.)$$foo['bar']['baz'] $foo->$bar['baz'] $foo->$bar['baz']() Foo::$bar['baz']()These have all changed behaviour in PHP 7. Silently. Insidiously. I'll break these down one at a time in a minute, but first…
$foo()['bar']() $foo::$bar::$baz Foo::bar()() (function() { ... })() ($obj->closure)() [$obj, 'method']()These — and many other variations — are now supported in PHP 7.0. Check the uniform variable syntax RFC. The "iffy" (à la JavaScript) is particularly awesome.
$$foo['bar']['baz'];
${$foo['bar']['baz']};This is interpreted in PHP 5 as "take the value of $foo['bar']['baz'] and use that as a variable name". (Of course, if you're using variable variables anyway, you have already lost.)
$$foo['bar']['baz'];
($$foo)['bar']['baz'];PHP 7: take the value of $foo, use that as a variable name, and then access ['bar']['baz'].
$foo->$bar['baz']
$foo->{$bar['baz']}This is interpreted in PHP 5 as "take the value of $bar['baz'], and use that as a property name".
$foo->$bar['baz']
($foo->$bar)['baz']PHP 7: take the value of $foo->bar, and then access the baz variable. There's also the same thing for method calls (which was the third variation shown earlier).
Foo::$bar['baz']()
Foo::{$bar['baz']}()This is interpreted in PHP 5 as "take the value of $bar['baz'], and use that as a static property name". This is probably the most common variant of the four.
Foo::$bar['baz']()
(Foo::$bar)['baz']()PHP 7: Take the value of the static $bar property of Foo, then get the array value, then call that callable.
Foo::$bar['baz']()
Foo::$bar['baz']()
Foo::{$bar['baz']}()The earlier examples with parentheses and/or curly braces will work for preserving PHP 5 behaviour.
Foo::$bar['baz']()
$method = $bar['baz']; Foo::$method();I'd prefer this for clarity.
git grep -E '((((::)|(->))\$[a-zA-Z_][a-zA-Z0-9_]*)+\[[^\s]+\]\())|(->\$[a-zA-Z_][a-zA-Z0-9_]*\[)|(\$\$[a-zA-Z_][a-zA-Z0-9_]*\[)'
grep -E '((((::)|(->))\$[a-zA-Z_][a-zA-Z0-9_]*)+\[[^\s]+\]\(\))|(->\$[a-zA-Z_][a-zA-Z0-9_]*\[)|(\$\$[a-zA-Z_][a-zA-Z0-9_]*\[)'I'll tweet this later. This isn't perfect (doesn't support non-ASCII variable names), but will probably catch most sins. (end of section)
E_ERROR: 182E_RECOVERABLE_ERROR: 17E_PARSE: 1
E_ERROR: 54 (-128)E_RECOVERABLE_ERROR: 3 (-14)E_PARSE: 0 (-1)
E_PARSE still exists, but only for the case where you don't catch a ParseError.function square(int $n): int { return $n * $n; } square('foo');Here's an example of code that will generate a TypeError.
Fatal error: Uncaught TypeError: Argument 1 passed to square() must be of the type integer, string given
try { square('foo'); } catch (TypeError $e) { // Recover gracefully. // (or show a graceful error) }You can catch these error exceptions like any other exception.
Exception LogicException RuntimeException
Throwable Error ParseError TypeError ... Exception LogicException RuntimeExceptionThere are five errors in total, but you can check the manual for all of them. I would expect the list to grow considerably in PHP 7.1 and beyond.
try { ... } catch (Exception $e) { // Catch almost everything? }What this means is that catch-all exception blocks no longer catch all exceptions (Pokemon-style), but this is a good thing, since the new exceptions aren't user generated and probably not what you expected to catch with Exception.
try { ... } catch (Throwable $e) { // Catch everything, but is // that a good idea? }
try { $code = '<?php echo 01090; ?>'; highlight_string($code); } catch (ParseError $e) { ... }Rules are made to be broken, of course, and there may be times where you do have to catch errors. If you were already handling a recoverable error, you will probably have to include a catch block to match. But at least it's now close to the code.
function log_exc(Exception $e) { ... } set_exception_handler('log_exc');There's an additional wrinkle. Most of you probably have (or at least use) code something like this. There's a problem (type hint).
function log_exc(Throwable $e) { ... } set_exception_handler('log_exc');If you're writing PHP 7 only code, then you can simply replace the type hint with Throwable.
function log_exc($e) { ... } set_exception_handler('log_exc');
git grep set_exception_handler
git grep -E 'catch\s*\(\\?Exception\s'End of section.
$a = [1]; foreach ($a as &$v) { if ($v == 1) $a[] = 2; var_dump($v); }
int(1)What happens when we modify an array that's being iterated over in a by reference foreach?
$a = [1]; foreach ($a as &$v) { if ($v == 1) $a[] = 2; var_dump($v); }
int(1) int(2)This isn't really a bad change, but it is different.
reset()key()current()end()next()prev()pos()
There is another foreach change. Who knows these functions?$a = range(1, 3); foreach ($a as &$v) { var_dump(current($a)); }
int(2) int(3) bool(false)Foreach used to set the internal array pointer when iterating by reference, so you could do horrible things like this.
$a = range(1, 3); foreach ($a as &$v) { var_dump(current($a)); }
int(1) int(1) int(1)PHP 7 does not. Rejoice! REJOICE.
array_walk($a, function (&$v, $k) { ... });
$a = array_map(function ($v) { ... }, $a);If you're not actually adding or removing elements, then use a function that isolates the scope. One of PHP's most commonly reported "bugs" is loop variables causing weirdness later, and you can remove that (and simulate lexical scope).
git grep -E 'foreach.*\sas\s+&'There are no errors. Look for any foreach by reference. Yes, all of them. (End of section.)
function square($n) { return $n * $n; } echo square("0x10");
256Let's go back to our old friend the square function. In PHP 5, the 0x10 is treated as 16.
function square($n) { return $n * $n; } echo square("0x10");
0Note that this is silent, due to PHP's long established string→number semantics (the string is ignored from the first non-numeric-string character onwards, in this case the x). No warning.
$n = sscanf('0x10', '%x')[0]; echo square($n);In terms of what you do: there are a few options. If you know it's a hex string, you can use sscanf(). Or hexdec() with some pre-processing. If you don't know, it's going to be harder. My advice is to know when you expect hex strings.
$s = 'abc'; list($a, $b, $c) = $s; var_dump($a, $b, $c);
string(1) "a" string(1) "b" string(1) "c"Interestingly, this only worked if the right hand side was a variable, not a literal.
$s = 'abc'; list($a, $b, $c) = $s; var_dump($a, $b, $c);
NULL NULL NULL
list($a, $b, $c) = str_split($s);
yield $foo or $bar;
yield ($foo or $bar);In practice, this returns a boolean.
yield $foo or $bar;
(yield $foo) or $bar;Accessing $bar probably isn't super useful, although this does open up the ever popular "or die" pattern.
$a = ['o', 'O']; $opt = SORT_STRING|SORT_FLAG_CASE; sort($a, $opt); echo json_encode($a);
["O","o"]Note that the order gets swapped. This is what I mean by an unstable sort.
$a = ['o', 'O']; $opt = SORT_STRING|SORT_FLAG_CASE; sort($a, $opt); echo json_encode($a);
["o","O"]In PHP 7, it's more stable in this case, but the important part is that it's different.
DateTime::__construct(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. ...We've all seen this in PHP 5.
It's gone! But this isn't necessarily good news; it just means PHP will choose UTC for you. You still need to set date.timezone in practice. (End of section.)
boolintfloatstringnullfalsetrueresourceobjectmixednumeric
These are the names that are now unavailable to classes, interfaces and traits, as they're used for scalar type declarations. This includes classes in namespaces. Remember that these names are case insensitive. I'd avoid "scalar" and "void", too.$HTTP_RAW_POST_DATA
file_get_contents('php://input');You'll see an undefined variable warning there. It should be pretty obvious.
function f() { global $$foo->bar; }You can't provide variable variables to global any more. If you did that, you were a horrible person anyway.
function foo(int $a, string $b) { echo gettype($a).' '.gettype($b); }
integer stringThe cool thing is that you are guaranteed that $a and $b are those types. How that happens is configurable, though.
<?php declare(strict_types=1); include 'foo.php'; // defines foo() foo('0', 'foo');
Fatal error: Uncaught TypeError: Argument 1 passed to foo() must be of the type integer, string given, called in ...As a caller, you can control whether the inputs will be converted or not. In strict mode, the input must be the correct type.
<?php include 'foo.php'; // defines foo() foo('0', 'foo');
integer stringThis is the default. Honestly, I think this is what you'll mostly want: it behaves like PHP does currently for internal functions. The important part is that the declare() affects functions you call, not functions you define.
function add(int $a, int $b): int { return $a + $b; }You can also define return types. These too are affected by strict typing: if the return value isn't the right type in strong mode, you get a TypeError.
interface UserInterface { function getName(): string; function getEmail(): string; function login(string $pw): bool; }My feeling is that these are mostly useful in interfaces and OO, since you can now constrain what methods defined on descendants and implementors return. Which is great from a duck typing perspective! (end of section)
$a = isset($thing) ? $input : null; $b = isset($stuff) ? $stuff : null; $c = isset($brick) ? $brick : null;We've all written code like this, I suspect.
$a = $thing ?? null; $b = $stuff ?? null; $c = $brick ?? null;Instead, in PHP 7, you can write this. Only one use of the variable per line! (end of section)
usort($array, function ($a, $b) { return $a->id <=> $b->id; });(end of section)
Check the migration guide
Anonymous classes, expectations (better assertions), Unicode codepoint escapes, huge generator improvements (delegation, return values), Closure::call, group use...brew install php70
There are pre-built packages available for common distributions.Floodgates: Doug Wertman (CC-BY 2.0)
PHP7: Vincent Pontier
Panic: wackystuff (CC-BY-SA 2.0)
Don't panic: Jim Linwood (CC-BY-SA 2.0)
Head in sand: Peter (CC-BY-SA 2.0)
Flag day: Magnus Akselvoll (CC-BY 2.0)
Dual wield: Joriel Jimenez (CC-BY 2.0)
What do?: klndonnelly (CC-BY 2.0)
Strategy: Horia Varlan (CC-BY 2.0)
The Scream: Edvard Munch (PD)
Lots: swong95765 (CC-BY 2.0)
Stars: Tom Hall (CC-BY 2.0)
Sun stone: Michael McCarty (CC-BY 2.0)
Soon cat: Guyon Morée (CC-BY 2.0)
Wrenches: LadyDragonflyCC - >;< (CC-BY 2.0)