Php 7.2 compliance workshop php benelux

Preview:

Citation preview

PHP 7.2 COMPLIANCE WORKSHOP

Damien Seguy @exakat Antwerp, January 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/

64 opened discussions

4 opened votes

WHICH TO BELIEVE

Books

PHP manual Migration, blogs

Wiki (Implemented)

NEWS

Bugs

Wiki (Future)

Yearly

Monthly

Weekly

As possible

As needed

PHP has 3 phases

syntax

definitions

execution

WHERE DOES CODE BREAK?

Checked with phplint

Checked with tests

Checked code review

<?php

function splitNames($fullname, $fullname) {    list($first, $last) = split($fullname, ' '); }

?>

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 messagesin 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

Code focused on current versions

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 __constructor()

Not happening if the class is in a namespaceUse 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.1

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

PHP has 3 phases

syntax

definitions

execution

WHERE DOES CODE BREAK?

Checked with phplint

Checked with tests

Checked code review

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: pos, text: splitLines(text.join("\n")), origin: "paste"}; po/zh_CN.po:"example: address can be split into street, city, country and zip."

libraries/Advisor.php: public static function splitJustification($rule) libraries/plugins/ImportCsv.php: $tmp = preg_split('/,( ?)/', $csv_columns); 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 processedPhanUndeclaredProperty Reference to undeclared property \stdclass->results

PhanNonClassMethodCall Call to method relateTo on non-class type null

PhanStaticCallToNonStatic Static call to non-static method \loader\cypher::saveTokenCounts() defined at library//Loader/Cypher.php:179PhanAccessPropertyProtected Cannot access protected property \tokenizer\token::$alternativeEnding

PhanTypeMismatchArgument Argument 1 (atom) is string but \analyzer\structures\useconstant::atomfunctionis() takes array defined at library//Analyzer/Analyzer.php:415

PhanUndeclaredClassMethod Call to method __construct from undeclared class \reports\xmlwriterPhanUndeclaredVariable 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

20 mins6798 results290 analysis

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.1 : 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

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

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 Constants5.3 40 2 805.4 0 9 785.5 12 11 575.6 1 10 107.0 10 10 417.1 9 3 307.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()

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");

PHP 7.1

https://wiki.php.net/rfc/octal.overload-checking

Invalid numeric are signaled

PHP 7.1

Notice: A non well formed numeric value encountered in

<?php

echo "1 monkey" + "2 bananas";

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

Almost all PHP keywords are now authorized inside classes

Methods and constants

Except for class, which can't be a class constant name.

<?php     class foo {    const instanceof = 1;    function use() {        $this->while(4) + foo::instanceof;    } }

STRINGS MAY BE INVALID

Upgraded to Fatal error

<?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

Upgraded to Fatal error

<?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

BadFunctionCallException

BadMethodCallException

DomainExceptionInvalidArgumentException

OutOfRangeException

OutOfBoundsException

OverflowException

RangeException

Error

ParseErrorDivisionByZeroError

AssertionError

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

<?php 

try {    $file = new finfo(FILEINFO_NONE,$magic_file);  } catch( ParseError $e) {     log($e->getMessage());    // attempt to fix this or error handling  }

AND MORE CATCHING EXCEPTIONS

<?php 

try {    $random = random_bytes(10);   } catch( TypeError $e) {    // invalid parameter } catch( Error $e) {    // invalid length } catch( Exception $e) {    // no source of randomness } 

Even more catching exceptions

<?php

try {    attemptSomething(); } catch (RuntimeException $e) {   fixSomething(); } catch (InvalidArgumentException $e) {   fixSomething(); } catch (BadFunctioncallException $e) {   fixSomethingElse(); } 

Even more catching exceptions

<?php

try {    attemptSomething(); } catch (RuntimeException|  InvalidArgumentException|  BadFunctioncallException $e) {   fixSomething(); } 

Even more catching exceptions

<?php

//try really hardertry {    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

<?php     $array = ['a' => 1, 'b' => 5, 'c' => 3];

// Assigns to $a, $b and $c in the same orderlist($a, $b, $c) = $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;

SHORT SYNTAX FOR LIST()

<?php 

$array = ['a' => 1, 'b' => 5, 'c' => 3];

["a" => $a, "b" => $b, "c" => $c] = $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 in A

Deprecated: Non-static method A::f() should not be called statically inNotice: Undefined variable: $this in A

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

CHANGED BEHAVIOR

CHANGED BEHAVIOR

Indirect expressions

FUNC_GET_ARG()

<?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]);     }  }

MODERNIZATION

FOREACH UPDATE THE ACTUAL ARRAY

<?php

$array = [0]; foreach ($array as $k => &$val) {     print "$k\n";     $array[] = 1; } ?>

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"}; 

WARN WITH COUNT()

<?php function handle_records(iterable $iterable) {     if (count($iterable) === 0) {         return handle_empty();     }       foreach ($iterable as $value) {         handle_value($value);     } }

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();

CLOSURE BINDING

<?php

$closure = function() {     echo $this->hello; };

class   {

  private $hello = " ";

}

$nihao = new  ();

$closure->call($nihao);

SESSION_START($OPTIONS)

<?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

<?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<?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 ' : '') . " factor\n";  }

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 "$n\n";     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

<?php   

function foo(string $x) { } foo('that');

Catchable fatal error: Argument 1 passed tofoo() must be an instance of string, string given, called in file..

VARIOUS SCALAR TYPEHINT

<?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 Foo\Bar; 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

scalar, array, callable, class or interfaces

void (PHP 7.1)

<?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/talk/615d8

THE END