Last Train to PHP 7.1
Damien Seguy @exakat
London, United Kingdom, February 2017
Full ahead to PHP 7. 2
Changing version is always a big challenge
Backward incompatibilities
New features
Learn, spot, fix, repeat
01
Speaker
Damien Seguy
CTO at exakat
Static code analysis for PHP
Retiring house for oldest 

elephpant
Synopsis
What is in the next version ?
Where in my code ?
How to replace this ?
Get documentation
Find issue
Fix code
Migration to PHP 7.2
Migration to PHP 7.0, 7.1 and 7.2 / 8.0
From PHP 5.6
No framework, no libraries
Tools, experience, discipline
Destination
Living on the edge
http://php.net/manual/en/migration71.php
Online
UPGRADING TO PHP 7.0
Free Book, PDF
Davey Shafik is RM for PHP 7.1
Lots of blogs and articles
Living on the bleeding edge
https://github.com/php/php-src/blob/master/UPGRADING
https://github.com/php/php-src/blob/master/NEWS
https://wiki.php.net/rfc
http://bugs.php.net/
62
opened
discussions
3
opened
votes
Which to believe
Books
PHP manual Migration, blogs
Wiki (Implemented)
NEWS
Bugs
Wiki (Future)
Yearly
Monthly
Weekly
As possible
As needed
Where does code break?
Checked with phplint
Checked with tests
Checked code review
PHP has 3 phases
syntax
definitions
execution
<?php
function splitNames($fullname, $fullname) {
  list($first, $last) = split($fullname, ' ');
}
echo splitNames('John Doe');
?>
Where will code break?
SyntaxDefinitionsExecution
Tools for migration
• Your own experience with your code
• Lint
• Search
• Static analysis
• Logs
Linting PHP 7
PHP linting
• command line : php -l filename.php
• Spot parse errors
• Works on files only :
• For directories, see composer phplint/phplint
PHP linting
Error messages

in PHP
0
550
1100
1650
2200
5.0 5.1 5.2 5.3 5.4 5.5 5.6 7.0 7.1 7.2
Total Distinct
Backward unlintable code
0
1
2
3
4
5.2 5.3 5.4 5.5 5.6 7.0 7.1 7.2
Current versions code
0
1.25
2.5
3.75
5
5.2 5.3 5.4 5.5 5.6 7.0 7.1 7.2
Backward unlintable code
0
0.25
0.5
0.75
1
5.2 5.3 5.4 5.5 5.6 7.0 7.1 7.2
0
1.25
2.5
3.75
5
5.2 5.3 5.4 5.5 5.6 7.0 7.1 7.2
0
1
2
3
4
5.2 5.3 5.4 5.5 5.6 7.0 7.1 7.2
0
0.75
1.5
2.25
3
5.2 5.3 5.4 5.5 5.6 7.0 7.1 7.2
0
1.75
3.5
5.25
7
5.2 5.3 5.4 5.5 5.6 7.0 7.1 7.2
0
0.25
0.5
0.75
1
5.2 5.3 5.4 5.5 5.6 7.0 7.1 7.2
php -l with other versions
syntax error, unexpected 'new' (T_NEW)
Assigning the return value of new by reference is
deprecated (PHP 5.6)
PHP 7 -> PHP 5.6
$o =& new Stdclass();
Redefinition of parameter
<?php
function foo($a, $a, $a) {
  print $a."n";
}
foo('x', 'y', 'z');
?>
Switch statements may only
contain one default clause
<?php   
switch($x) {   
    case '1' :    
        break;   
    default :    
        break;   
    default :    
        break;   
    case '2' :    
        break;   
}   
Switch statements may only
contain one default clause
switch($x) {   
    case 1 :    
        break;   
    case 0+1 :    
        break;   
    case '1' :    
        break;   
    case true :    
        break;   
    case 1.0 :    
        break;   
    case $y :    
        break;   
}   
Deprecated features
Not happening if a parent case has a __construct()
Not happening if the class is in a namespace
Use the E_DEPRECATED error level while in DEV
Methods with the same name as their class will not
be constructors in a future version of PHP; foo has
a deprecated constructor
Migration to PHP 7.2
Lint with
PHP 7.1
PHP 7.2 (or src)
PHP 5.6 (current), PHP 7.0
PHP 5.5, 5.4,… (manual)
PHP 7 linting
Pre-commit
Use different versions
Be ruthless with unlintable files
Where does code break?
Checked with phplint
Checked with tests
Checked code review
PHP has 3 phases
syntax
definitions
execution
Static analysis
Definition
Review code without executing it
Audit code by reading it
Common in C/C++, Java, Javascript
Hot subject to PHP
GREP/SEARCH
Any searching facility
Pro : High speed, great for keyword search, universal
Cons : Little repeat value, no PHP semantics
Grep on PHP code
1318 reports
doc/_ext/configext.py: parts = text.split("']['")
js/codemirror/lib/codemirror.js: var change = {from: pos, to: p
po/zh_CN.po:"example: address can be split into stre
libraries/Advisor.php: public static function splitJu
libraries/plugins/ImportCsv.php: $tmp = preg_split('/,( ?)/', $
libraries/Config.php: // split file to lines
Static analysis
PHP 5 / 7
Calisthenics
ClearPHP
Performance
 
 

Static analysis tools
PHP7mar
PHP7cc
Phan
Exakat
PHP inspections
PHP7mar
PHP 7 Migration Assistant Report (MAR)
Alexia : https://github.com/Alexia/php7mar
Works with regex
Produces a .md file
12 results
PHP7cc
PHP 7 Compatibility Checker
Authored by sstalle
https://github.com/sstalle/php7cc
PHP 5, works with "nikic/php-parser": "~1.4"
Display to stdout
8.506s 3 results 27 analysis
php ~/.composer/vendor/bin/php7cc library/
File: /Users/famille/Desktop/analyze/library/Analyzer/Analyzer.php
> Line 231: Function argument(s) returned by "func_get_args" might have been modified
func_get_args();
File: /Users/famille/Desktop/analyze/library/Analyzer/Functions/MarkCallable.php
> Line 32: Nested by-reference foreach loop, make sure there is no iteration over the same array
foreach ($lists as $id => &$function) {
}
File: /Users/famille/Desktop/analyze/library/Tasks/Analyze.php
> Line 118: Possible adding to array on the last iteration of a by-reference foreach loop
$dependencies[$v] = $dep;
Checked 873 files in 8.506 seconds
PHAN
Static analysis for PHP
Inited by Rasmus, under work at Etsy
https://github.com/etsy/phan
PHP 7 only, with ext/ast
php ~/.composer/vendor/bin/phan -f phan.in -3 vendor
-o phan.out
11.244s 333 results
PhanUndeclaredProperty Reference to undeclared property processed
PhanUndeclaredProperty Reference to undeclared property stdclass->results
PhanNonClassMethodCall Call to method relateTo on non-class type null
PhanStaticCallToNonStatic Static call to non-static method loadercypher::saveTokenCounts() defined at libr
PhanAccessPropertyProtected Cannot access protected property tokenizertoken::$alternativeEnding
PhanTypeMismatchArgument Argument 1 (atom) is string but analyzerstructuresuseconstant::atomfunction
PhanUndeclaredClassMethod Call to method __construct from undeclared class reportsxmlwriter
PhanUndeclaredVariable Variable $r is undeclared
84 analysis
Exakat
Static analysis engine for PHP
https://github.com/exakat/exakat
PHP 5.2 to 7.2;
php exakat.phar project -p name
20 mins6798 results290 analysis
Exakat
PHP inspections
Static analysis engine for within the IDE
Vladimir Reznichenko
https://bitbucket.org/kalessil/phpinspectionsea
Written in Java
Runs from within PHPstorm
Write your own?
https://github.com/exakat/php-static-analysis-tools.git
PHP 7 : use ext/ast
PHP 5 :"nikic/php-parser"
Better Reflexion
Avoid regex (but it does work)
PHP 7.2 : what changes?
Incompatible changes
New features
Features/Incompatibilities from PHP 5.6 => 7.2
How to spot issues?
Code knowledge
lint
Grep / Search
Static analysis
Logs / error_reporting
Unit Tests
Incompatibilities
Incompatibilities
Removed features
Added features
Collateral damages
Removed features
Removed extensions
Extensions
ereg
mssql
mysql
sybase_ct
mcrypt 7.2
Removed extensions
ext/ereg
ereg
ereg_replace
split
sql_regcase
Moved utf8_encode() and utf8_decode() to the Standard extension
7.2
Removed functions
call_user_method()
call_user_method_array()
Repleacable by $funcname
Replaced by call_user_func() and call_user_func_array()
Partially replaced by variadic
png2wbmp() and jpeg2wbmp()
Deprecated
7.2
Removed variables
$HTTP_RAW_POST_DATA
Replace it by php://input
php://input is now reusable
Since PHP 5.5
Removed INI
sql.safe_mode (PHP 7.2)
mbstring.internal_encoding
mbstring.http_output
mbstring.http_input
iconv.internal_encoding
iconv.input_encoding
iconv.output_encoding
default_charset
}
default_charset
htmlentities()
PHP 5.3 : ISO-8859-1
PHP 5.4 : UTF-8
PHP 5.6 : default_charset (also UTF 8)
Where to look for ?
default_charset
Search for ini_set(), ini_get(), ini_get_all(), ini_restore(), get_cfg_var()
Search in php.ini, .htaccess
Search for htmlentities(), html_entity_decode() and htmlspecialchars()
Preg_replace and /e
preg_replace(‘/ /e’, ‘evaled code’, $haystack)
replaced by
preg_replace_callback_array()
preg_replace(‘/  /e’, ‘evaled code’, $haystack)
preg_replace_callback_array([‘/  /’ => $closure], 
$haystack) 
preg_replace_callback(‘/  /’,
$closure], 
$haystack) 
preg_replace_callback_array
<?php 
$code = "abbbb";
$spec = 'c';
echo preg_replace_callback_array(
    array(
        "/a/" => function($matches) {
                        return strtoupper($matches[0]);
                 },
        "/b/" => function($matches) use ($spec) {
static $i = 0;
$i++;
               return "B$i$spec";
        }
    ), $code);
AB1cB2cB3cB4c
preg_replace()
<?php  
$code = "abcde"; 
echo preg_replace( 
    array( '/a/', '/b/'), 
    array( 'f' , 'g'),
    $code);
fgcde
Can't call dynamically!
•$func()
•call_user_func()
•array_map()
•or similar
•assert() with a string
argument
extract()
compact()
get_defined_vars()
func_get_args()
func_get_arg()
func_num_args()
parse_str() with one argument
mb_parse_str() with one argument
Added features
Added definitions
Functions Classes Constants
5.3 40 2 80
5.4 0 9 78
5.5 12 11 57
5.6 1 10 10
7.0 10 10 41
7.1 9 3 30
7.2 4 0 15
Total 766 92 1163
Name impact
get_resources(), intdiv(), is_iterable(), mb_scrub()
PREG_JIT_STACKLIMIT_ERROR
class Date (from PHP 5.1)
Error (new class in PHP 7)
New functions
intdiv()
get_resources()
random_bytes(), random_int()
error_clear_last()
gc_mem_caches()
preg_replace_callback_array()
socket_getaddrinfo()
mb_ord, mb_scrub,
Collaterals
Invalid octals are invalid
Upgraded from silent to Fatal error
PHP Parse error: Invalid numeric literal in test.php
<?php 
$x = 0890;
More invalid octals in strings
<?php  
var_dump("000" === "400");
https://wiki.php.net/rfc/octal.overload-checking
More reserved keywords
bool, int, float, string, null, true, false are no more
available for class / interface / traits names
mixed, numeric, object, resource

are reserved for future use
void 

is reserved in 7.1
More relaxed keywords
<?php    
class foo {
   const instanceof = 1;
   function use() {
       $this->while(4) + foo::instanceof;
   }
}
Strings may be invalid
<?php 
echo "u{1F418}n";
> php56 test.php
u{1F418}
> php70 test.php
🐘
<?php 
echo "u{65B0}u{52A0}u{5761}n";
//
Strings may be invalid
<?php 
echo "u{Yes}n";
PHP Parse error:
Invalid UTF-8 codepoint escape sequence
in test.php on line 3
u{
Hexadecimal numeric strings
Also, -0 !!
<?php  
var_dump(1 + 0xf);
var_dump(1 + "0xf");
$ php56 test.php
int(16)
int(16)
$ php70 test.php
int(16)
int(1)
Warning for strings (7.1)
<?php  
print "2" + "4";
print "3 elephpants" + "4 dolphins";
print "2" + "d4 d";
6
7
2
Warning: A non-numeric value encountered
Exceptions
Throwable
Exception
LogicException RuntimeException
BadFunctionCall
Exception
BadMethodCall
Exception
DomainException
InvalidArgument
Exception
OutOfRange
Exception
OutOfBou
ndsExcep
tion
Overflow
Exception
RangeExcep
tion
Error
ParseError
DivisionBy
ZeroError
Assertion
Error
Exceptions
Exception is not the top exception type anymore
It is now the 'throwable' interface
Impact on Exception handler
Avoid type hinting until moved to PHP 7
Impact on Error handler
Impact on catch() clauses
More catching exceptions
<?php
try {
  eval($somePHPcode);
} catch( ParseError $e) {
   log($e->getMessage());
  // attempt to fix this or error handling
}
More catching exceptions
Parser errors now throw a ParseError object. Error
handling for eval()
<?php 
try { 
  $file = new finfo(FILEINFO_NONE,$magic_file); 
} catch( ParseError $e) { 
   log($e->getMessage()); 
  // attempt to fix this or error handling 
}
<?php 
try { 
  $random = random_bytes($size);  
} catch( TypeError $e) { 
  // invalid parameter
} catch( Error $e) { 
  // invalid length
} catch( Exception $e) { 
  // no source of randomness
} 
And more catching
exceptions
Even more catching exceptions
<?php
try {
   attemptSomething();
} catch (RuntimeException $e) {
  fixSomething();
} catch (InvalidArgumentException $e) {
  fixSomething();
} catch (BadFunctioncallException $e) {
  fixSomething();
} 
Even more catching exceptions
Parser errors now throw a ParseError object.
Error handling for eval()
<?php
try {
   attemptSomething();
} catch (RuntimeException| 
InvalidArgumentException|
 BadFunctioncallException $e) {
  fixSomething();
} 
Even more catching exceptions
<?php
//try really harder
try {
   attemptSomething();
} catch (Exception $e) {
   attemptSomething();
} 
Negative string offset (7.1)
<?php  
$string = "abcde";
print $string[-3];
print "$string[3]";
print "$string[-2]";
c
d
d
list() with keys
Upgraded to Fatal error
<?php 
  
$array = ['a' => 1, 'b' => 5, 'c' => 3];
// Assigns to $a, $b and $c in the same order
list($a, $b, $c) = array_values($array); 
// Assigns to $a, $b and $c from the keys 
//"a", "b" and "c", respectively 
list("a" => $a, "c" => $c, "b" => $b) = $array;
list("a" => $a, "b" => $b, "c" => $c) = $array;
list("c" => $c, "a" => $a, "b" => $b) = $array;
list() with keys
<?php  
   
$array = ['b' => 5, 'a' => 1, 'c' => 3]; 
ksort($array);
$array = array_values($array);
// Assigns to $a, $b and $c in the same order
list($a, $b, $c) = $array;  
// SELECT id, * FROM users 
list($userId, $name, $last) = array_values($row);  
Short syntax for list()
<?php  
$array = ['a' => 1, 'b' => 5, 'c' => 3]; 
["a" => $b, "c" => $c, "b" => $a] = $array;   
// Works even when nested
$array = [['a' => 1, 'b' => 5], ['c' => 3]]; 
[["a" => $a, "b" => $b], ["c" => $c]] = $array;
Call-time pass-by-reference
References are in the function signature
Deprecated warnings until PHP 7
Upgraded to Parse error in PHP 7
<?php  
$a = 3;  
function f($b) {  
    $b++;  
}  
f(&$a);  
print $a;  
?>
PHP Parse error: syntax error, unexpected '&' in …
Incompatible context
<?php 
class A { 
     function f() { echo get_class($this); } 
} 
A::f(); 
?>
Notice: Undefined variable: $this inA
Deprecated: Non-static methodA::f() should not be called statically
Notice: Undefined variable: $this inA
Easy to spot
Strict Standards: Non-static method A::f()
should not be called statically in test.php on
line 6
Deprecated: Non-static method A::f() should
not be called statically in test.php on line 6
$this is for classes
<?php
class bar{
  function foo($this) {
    static $this; 
   global $this;
  foreach ($a as $this) { 
    try {
      $a = "this"; 
      $$a = 42; 
    } catch (Exception $this) {} 
  }
  unset($this); 
 }
}
Changed behavior
Changed behavior
Indirect expressions
func_get_arg()
func_get_arg() and func_get_args() now return 

the current argument values
<?php 
function foo($a, $b, $c) { 
  print_r(func_get_args()); 
  ++$a; 
  print_r(func_get_args()); 
} 
foo(1,2,3);
Array
(
[0] => 1
[1] => 2
[2] => 3
)
Array
(
[0] => 2
[1] => 2
[2] => 3
)
Usort()
<?php
$array = array(
    'foo',
    'bar',
    'php'
);
usort($array, function($a, $b) {
    return 0;
}
);
print_r($array);
Array
(
[0] => php
[1] => bar
[2] => foo
)
Array
(
[0] => foo
[1] => bar
[2] => php
)
PHP 5
PHP 7
Automatically fixed
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. In case
you used any of those methods and you are
still getting this warning, you most likely
misspelled the timezone identifier.
New features
New features
Breaks backward compatibility sometimes
FUD
Search for places to apply them like for incompatibilities
New features
Fixing
Modernization
New features
Fixing
Don't hide in parentheses
<?php
function getArray() {
    return [1, 2, 3];
}
function squareArray(array &$a) {
    foreach ($a as &$v) {
        $v **= 2;
    }
}
// Generates a warning in PHP 7.
squareArray((getArray()));
?>
Parenthesis in 

arguments won't 

mask error 

anymore
Constant arrays
Lots of properties could be turned to constants
<?php  
class Version { 
    const SUPPORTED = ['1.0', '1.1', '2.0', '2.1'];
    private $an_array = [1,2,3,4];
    public function isSupported($x) { 
        return isset(Version::SUPPORTED[$x]);
    } 
}
Constant visibility
<?php   
class Version {  
    private const BUILD = 123; 
    public const VERSION = '1.2.2'; 
}
echo Version::UNDEFINED;
echo Version::BUILD;
echo Version::VERSION;
Constant arrays
Lots of properties could be turned to constants
<?php  
class Version { 
    const SUPPORTED = ['1.0', '1.1', '2.0', '2.1'];
    private $an_array = [1,2,3,4];
    public function isSupported($x) { 
        return isset(Version::SUPPORTED[$x]);
    } 
}
Modernization
Foreach update the actual
array
<?php
$array = [0];
foreach ($array as $k => &$val) {
    print "$kn";
    $array[] = 1;
// Only for pushing, not unshifting
}
0
1
2
3
4
5
6
7
8
9
10
11
0
Array/Object casting
<?php
$arr = [0 => 1, 1 => 2, 2 => 3]; 
$obj = (object)$arr; 
// PHP 7.1
echo $obj->{"0"}; 
// PHP 7.2
echo $obj->{0}; 
7.2
Warn with COUNT()
<?php
function handle_records(iterable $iterable)
{
    if (count($iterable) === 0) {
        return handle_empty();
    }
 
    foreach ($iterable as $value) {
        handle_value($value);
    }
}
7.2
Closure binding
<?php
class Hello {
  private $hello = "Hello";
  function makeClosure() {
   return function() {
    echo $this->hello;
  };
}
$obj = new Hello();
$closure = $obj->makeClosure();
$closure();
Closure binding
<?php
class   {
  private $hello = " ";
}
$obj = new Hello();
$closure = $obj->makeClosure();
$nihao = new  ();
$closure2 = $closure->bindTo($nihao);
$closure2();
Request the closure from the object
Closure binding
<?php
$closure = function() {
    echo $this->hello;
};
class   {
  private $hello = " ";
}
$nihao = new  ();
$closure->call($nihao);
Bind the object at the last moment
session_start($options)
Very Cute <=>
Replaces a lot of code
Mainly useful for usort()
<?php
// PHP 5.6
ini_set('session.name','session');
ini_set('session.gc_probability',1);
ini_set('session.gc_divisor',1);
session_start();
// PHP 7.0
session_start(['name'  => 'session',
'gc_probability' => 1,
'gc_divisor'  => 1 ]
);
dirname() second argument
<?php  
$path = '/a/b/c/d/e/f';
// PHP 5.6
$root = dirname(dirname(dirname($x)));
// PHP 7
$root = dirname($path, 3);
?>
Parameters evolution
get_headers() has an extra parameter
Passing a custom stream context
getenv() doesn't need parameter
all the current environment variables will be returned
get_class() doesn't allow null anymore
Really new
Null-coalesce
Shorter way to give a test for NULL and failover
<?php 
// PHP 5.6
$x = $_GET['x'] === null ? 'default' : $_GET['x'];
// PHP 7.0
$x = $_GET['x'] ?? 'default';
?>
Spaceship operator
Very Cute <=>
Replaces a lot of code
Mainly useful for usort()
<?php 
// PHP 5.6
if ($a > $b) {
 echo 1;
} elseif ($a < $b) {
  echo -1;
} else {
  echo 0;
}
// PHP 7.0
echo $a <=> $b; // 0
Generators delegation
New yield keyword
Save memory from
n down to 1 value
Good for long or infinite loops
Search for range(), for() or loops
<?php  
function factors($limit) { 
    yield 2; 
    yield 3;
    yield from primeTill1000();
    for ($i = 1001; $i <= $limit; $i += 2) { 
        yield $i; 
    }
} 
$prime = 1357; 
foreach (factors(sqrt($prime)) as $n) { 
    echo "$n ". ($prime % $n ? ' not ' : '') . " factorn"; 
}
Generators returns
<?php   
function factors($limit) {  
    return 'first';
    yield 2;  
    return 'second';
    yield 3; 
    return 'third';
    yield 5; 
}  
$gen = factors(sqrt($prime));
foreach ($gen as $n) {  
    echo "$nn";
    if ($n == 3) {break 1;}
}
print $gen->getReturn();
// second
Generators returns
The last return is accessible
The generator returns the
final state
Scalar typehint
Whenever type is tested =>
<?php  
function foo($x) {
   if (!is_string($x)) {
     throw new Exception('Type error while calling ' .
__FUNCTION__);
   } ...
}
<?php  
function foo(string $x) {
...
}
Scalar typehint back in 5.6
Whenever type is tested =>
<?php   
function foo(string $x) { }
foo('that');
Catchable fatal error: Argument 1 passed to

foo() must be an instance of string,
string given, called in file..
Various scalar typehint
int, float
string, bool
true, false, null
void (PHP 7.1)
mixed, object,
resource, numeric
(RFU)
<?php
function foo(?int $a, float $b, float $c) : ?int {
    return $a + $b + $c;
}
echo foo(null, 2,   1);    // 4
echo foo(1.2, 2, 1);     // 4
echo foo(1, 2.2, 1);     // 4
echo foo(1, 2.7, 1.4);   // 5
Option for strict typing
<?php
// Enable strict types
declare(strict_types=1);
declare(encoding='ISO-8859-1');
declare(ticks=1);
namespace FooBar;
foo('that'); 
Return type hint
<?php
function getData($login) : user {
   if (userExists($login)) {
     return userDetails($login);
   } else {
     return null;
  }
}
Minimum args in custom
functions is Fatal error
<?php
function foo(?int $a, float $b, float $c) {
    return $a + $b + $c;
}
echo foo(2,   1);    
Minimum args number Fatal error: Uncaught Error: 

Too few arguments to function foo(), 2 passed in
Helping at migration
Document the evolutions between two versions
Identify anchors in the code
Link the migration to the actual code
Keywords, syntax, code structures
Suggest fallback, work-around, detection tools
Helping at migration
Backward incompatibilities
Removed / renamed features
Collaterals
Changed behaviors
Helping at migration
New features
Fixing
Totally new
Spot previous work-arounds
Summary
Check the manuals
PHP lint is your friend
Search in the code
Use static analysis tools
Thank you!
Damien Seguy @exakat dseguy@exakat.io
https://www.exakat.io/
https://joind.in/event/php-uk-2017/schedule/list
The end

Last train to php 7