PHP 7.1 ELEGANCE OF OUR LEGACY 010 PHP, Rotterdam, Netherlands, October 2016 [RC4]
PHP 7.1ELEGANCE OF OUR LEGACY
010 PHP, Rotterdam, Netherlands, October 2016
[RC4]
AGENDA
• PHP 7.0 is already on the way out
• PHP 7.1
• RC4 at the moment, so 2 more weeks to wait
• What's new in this version
• What is incompatible with the previous version
• How to do migration
SPEAKER
• Damien Seguy
• Exakat CTO
• Ik ben een boterham
• Static analysis of PHP code
• Get a full report of compliance for your code with the exakat engine
ALS JE BLIJFT
QUESTIONS
DONEC QUIS NUNC
LIVING ON THE BLEEDING EDGE
http://php.net/manual/en/migration71.php
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/
QUAM UT PRÆPARATE PRO AGENTIBUS
HOW TO PREPARE FOR MIGRATION
• Code knowledge
• lint
• Grep / Search
• Static analysis
• Logs / error_reporting
• You know your code
• php -l on every file
• Fast, universal, false positives
• Exakat, phan
• Run the tests, check logs
QUAM UT PRÆPARATE PRO AGENTIBUS
HOW TO PREPARE FOR MIGRATION
• Code knowledge
• lint
• Grep / Search
• Static analysis
• Logs / error_reporting
• You know your code
• php -l on every file
• Fast, universal, false positives
• Exakat, phan
• Run the tests, check logs
INCOMPATIBILITIES MODERNIZATIONS
NEW FEATURES
INCOMPATIBILITIES
MCRYPT IS DEPRECATED
• "libmcrypt is a dead project, unmaintained for ~8 years, last version 2.5.8 was released in February 2007!"
• It emits a E_DEPRECATED notice
• Switch to OpenSSL for serious cryptography
• phpseclib (http://phpseclib.sourceforge.net/)
$THIS IS NOT A WARNING ANYMORE
<?php
function foo($this) { /**/ } // Fatal error: Cannot use $this as parameter ?>
• Parameter
• Static variable
• Global variable
• Foreach variable
• Catch'ed variable
• Variable variable
• Reference
• Result of extract() or parse_str
RAND() IS NOW AN ALIAS OF MT_RAND()
• RAND() is on its way out
• Since PHP 7.0, use random_int() and random_bytes()
• They provide CSPRN
• Throws exception if it can't
• RAND() is replaced by mt_rand(), better random
• In case the order of the serie is important, beware!
RAND() TO MT_RAND() TO MT_RAND() *
<?php srand(10); print rand()."\n"; print rand()."\n"; print rand()."\n"; ?>
1215069295 1311962008 1086128678
1656398468 641584702 44564466
502355954 641584702
2112621188
mt_srand(10, MT_RAND_PHP);
PHP 7.1
PHP 7.1
PHP 7.0RAND MT_RAND
PHP 7.0
MISSING ARG IS EXCEPTION
<?php function test($param){} test();
PHP Warning: Missing argument 1 for test()
Fatal error: Uncaught ArgumentCountError: Too few arguments to function test(), 0 passed
PHP 7.0
PHP 7.1
CAN'T BE CALLED DYNAMICALLY ANYMORE
• 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
• assert() with a string argument
Creates too many variables, in too many different ways
<?php
namespace { function test($a, $b, $c) { var_dump(call_user_func('func_num_args')); } test(1, 2, 3); } namespace Foo { function test($a, $b, $c) { var_dump(call_user_func('func_num_args')); } test(1, 2, 3); } ?>
PHP 7.0 : int(3) PHP 7.1 : int(1)
PHP 7.0 : int(1) PHP 7.1 : int(1)
__DESTRUCTOR ONLY WHEN OBJECT IS COMPLETE
<?php
function throwme($arg) { throw new Exception; }
try { $bar = new foo; } catch(Exception $exc) { echo "Caught exception!\n"; } ?>
Inside constructor Caught exception!
Inside constructor Inside destructor
Caught exception!
https://bugs.php.net/bug.php?id=29368
MODERNIZATION
VOID TYPE
• New pseudo-type for functions that don't return a value
• Not for type hint<?php function fooEmpty ($arg) : void { return ; }
function foo ($arg) : void { }
function fooWithNull ($arg) : void { return NULL; // not OK : explicit} ?>
ITERABLE TYPE
<?php function bar($option): iterable { if ($option == 'array') { return [1, 2, 3]; } else { return new ArrayAccessClass([1,2,3]); } } ?>
• New pseudo-type that represents arrays and ArrayAccess
• Both may be given to a foreach() loop
• This doesn't work on Closure nor Generators
NULLABLE TYPES
<?php function foo(): ?int { return null; //ok return 3; //ok return "3"; //ok, no strict return "no"; //never OK}
function bar(): ?void { } ?>
• Accepts ? before the type hint
• This means that null is accepted
Fatal error: Void type cannot be nullable
NULLABLE TYPES AND DEFAULT
<?php function bar(?string $msg = “default”) { if ($msg!== null) { echo $msg; } }
bar("ok") ; // OKbar(4) ; // OK, cast to string
bar(null) ; // OK, displays nothing bar() ; // OK, display 'default'
bar([1,2,3]) ; // Not OK?>
CATCHING MORE EXCEPTIONS
<?php
try { attemptSomething(); } catch (RuntimeException $e) { fixSomething(); } catch (InvalidArgumentException $e) { fixSomething(); } catch (BadFunctioncallException $e) { fixSomethingElse(); }
EVEN MORE CATCHING EXCEPTIONS
Federate error catching in catch clause
Only for Catch
no Type Hint, no Return Type Hint, no instanceof
<?php
try { attemptSomething(); } catch (RuntimeException| InvalidArgumentException $e) { fixSomething(); } catch (BadFunctioncallException $e) { fixSomethingElse(); }
OCTAL SEQUENCES
• Octal sequences that are beyond \377 are now reported
<?php print "\123\n"; // Prints S print "\523\n"; // Prints S too ?>
Octal escape sequence overflow \523 is greater than \377
STRING SEQUENCES REMINDER
• \0, \x, \u{}
<?php
echo "\123"; echo "\x53"; echo chr(83);
echo "\xe4\xba\xba"; echo "\u{4EBA}";
S
ARITHMETIC NOTICE
<?php ‘1’ + 3 === 4;
‘1 elephpant’ + 3 === 4; ?>
Notice: A non well formed numeric string encountered
ARITHMETIC NOTICE
<?php
$a = "12.00 \$CAD" + 1; $b = "1 023,45" + 2; $c = " 234 " + 4;
// Don't filter with + 0 !$amount = abs($_GET['amount']) + 0 ;
// Unless it is scientific notationecho "1.2345e9" + 0; echo (int) "1.2345e9";
?>
SESSION ID SIZE
• Session ID are not hashed anymore
• Speed bump!
• Session ID size is now session.sid_length. sid_length is CSPRN
• 22 to 256 chars; 32 as default; 48 recommended.
• session.sid_bits_per_character specifies 4/5/6 bits characters : (hexa, [0-9a-v], [0-9a-zA-Z\-,])
• Recommended settings : 48 / 5
• Beware about your filters and storages
SESSION ID ERRORS ARE CATCHABLE
<?php try{ session_regenerate_id (); } catch (Error $e) { // Log error // deal with missing session ID } ?>
• When generated ID are not strings, an Error is emitted
• Catch them to be safe
CSPRN THROW ERRORS
<?php
try { $random = random_bytes(10); } catch( TypeError $e) { // invalid parameter } catch( Error $e) { // invalid length } catch( Exception $e) { // no source of randomness }
MB_EREGI?_REPLACE ALSO DROPS /E IN 7.1
<?php
$code = "abc de";
echo mb_eregi_replace( ' ', '"ren";', $code, 'e');
abcrende
FUNCTIONS CHANGES
• getenv(), without argument : === $_ENV
• parse_url() now checks that user and pass have no @ chars.
• ext/filter has now FILTER_FLAG_EMAIL_UNICODE, to filter unicode email, like üser@[IPv6:2001:db8:1ff::a0b:dbd0]
• imap now checks that email is not larger than 16385 bytes
• pg_last_notice() gets 3 constants : PGSQL_NOTICE_LAST, PGSQL_NOTICE_ALL, and PGSQL_NOTICE_CLEAR
NEW FEATURES
LIST() WITH KEYS
<?php // PHP 7.0 $array = [0 => 'a', 1 => 'b', 2 => 'c'] ;
list($a, $b, $c) = $array ; // $a is 'a', $b is 'b', $c is 'c' ?>
LIST() WITH KEYS
<?php // PHP 7.1 $array = [0 => 'a', 1 => 'b', 2 => 'c'] ;
list(1 => $b, 2 => $c, 0 => $a) = $array ; // $a is 'a', $b is 'b', $c is 'c' ?>
LIST() WITH KEYS
<?php class foo { private $a, $b = [1]; private static $c; public function __construct(array $bar) { list( "a" => $this->a, "b" => $this->b[], "c" => static::$c ) = $bar; } } ?>
LIST() WITHOUT LIST
<?php class foo { private $a, $b = [1]; private static $c; public function __construct(array $bar) { [ "a" => $this->a, "b" => $this->b[], "c" => static::$c ] = $bar; } } ?>
NEW TRICKS WITH LIST<?php $points = [ ["x" => 1, "y" => 2], ["x" => 2, "y" => 1] ]; list(list("x" => $x1, "y" => $y1), list("x" => $x2, "y" => $y2)) = $points;
// repeated usage[ 4 => $a, 4 => $b, 4 => $c] = [ 4 => 5, 6 => 7]; // $a, $b and $c, all, contain 5 $a = $b = $c = [ 4 => 5, 6 => 7][4]; ?>
NEGATIVE OFFSET FOR STRINGS
<?php echo substr("abcde", 1, 1) ; // display b echo substr("abcde", -1, 1) ; // display d
echo "abcde"[1]; // display b echo "abcde"[-1]; // display d echo "$a[-1]"; // Fatal error echo "{$a[-1]}"; // display d?>
NEW FUNCTIONS
• mb_ord() and mb_chr() : multi-byte version of ord/chr
• mb_scrub() : cleans a string of gremlins
• curl_share_strerror(), curl_multi_errno() and curl_share_errno() : access to various kind of errors
• pcntl_async_signals() for asynchronous signal handling, with pcntl_signal()
<?php pcntl_async_signals(1);
pcntl_signal(SIGTERM, function ($signo) { echo "Signal handler called!\n"; });
echo "Start!\n"; posix_kill(posix_getpid(), SIGTERM); $i = 0; // dummy echo "Done!\n";
?>
CLASS CONSTANT VISIBILITY• Constants may only be used inside the class or its children
• Interfaces' constants are still public only
• trait's constants are still science-fiction
<?php class Foo { // PHP 7.0 behavior. Nothing changes. const PUBLIC_CONST = 0; // Explicit visibilities private const PRIVATE_CONST = 1; protected const PROTECTED_CONST = 2; public const PUBLIC_CONST_TWO = 3, PUBLIC_CONST_THREE = 4; } ?>
CLOSURE::FROMCALLABE()
<?php
$validator = new Validator();
// so PHP 4! $callback = array($validator, 'emailValidation');
// private methods!class Validator { private function emailValidation($userData) {} private function genericValidation($userData){}}
?>
CLOSURE::FROMCALLABLE()
<?php
class Validator { public function getValidatorCallback($validationType) { if ($validationType == 'email') { return Closure::fromCallable( [$this, 'emailValidation']); } return Closure::fromCallable( [$this, 'genericValidation']); } }
$validator = new Validator(); $callback = $validator->getValidatorCallback('email'); $callback($userData);
?>
DONEC QUIS NUNC
TEST PHP 7.1 NOW
• https://github.com/php/php-src
• RC4 : compile, run unit tests, fix your bugs or report PHP's
• Test online : https://3v4l.org/
• Compile your current code with it
• Use static analysis
• Watch out for PHP.Next : PHP 7.2 or PHP 8.0
• Exit with PEAR and PECL, in with composer/Pickle
• Class Friendship, __autoload() deprecations, Automatic SQL injection protection, INI get/set aliases…
BEDANKT!@EXAKAT - HTTP://WWW.EXAKAT.IO/
https://legacy.joind.in/talk/view/19421