POINT TECHNIQUE PHP 8: Process & Fixing Insanity
George Peter Banyard ● Mathematics student at Imperial College London ● Currently hired by The Coding Machine to work on php-src, i.e. the PHP Engine ● Find me on Twitter and elsewhere @Girgias ● https://gpb.moe 2 Me at 3AM realising I need a photo for the presentation
How I got involved with the PHP project • French translation PHP documentation • PHP internal mailing list • Proposed the “Deprecate PHP Short open tags” PHP RFC • Started tinkering with PHP’s source code • PoC: declare statement which promote warnings to Exceptions • Made multiple patches • php-src VCS karma granted 3
PHP a truly open source language: PHP RFCs A PHP RFC is a proposal which affects the PHP engine: Behaviour change, feature addition, deprecations, extensions Anyone can propose a PHP RFC, from QoL to Advanced feature • Minimum 2 week discussion period • 2 week voting period • Everyone with a PHP VCS account can vote (1000+ people!) PHP 8 is 30+ accepted RFCs from over 15 people Many more which have been declined See them all at: https://wiki.php.net/rfc 4
QoL Trailing commas in parameter list and closure use list $longArgs_longVars = function ( $longArgument, $longerArgument , $muchLongerArgument , ) use ( $longVar1, $longerVar2, $muchLongerVar3 , ) { // body }; 5 Allowed!
QoL ::class notation on variables $class = $object::class; // Identical to $class = get_class($object); DateTimeInterface::createFromInterface public static function createFromInterface (DateTimeInterface $object): DateTime public static function createFromInterface (DateTimeInterface $object): DateTimeImmutable 6
QoL Non-capturing catches try { doStuffWhichMightThrow (); } catch (Exception) { // The intention is clear: exception details are irrelevant echo "Some error message" ; } Null safe operator ?-> $country = null; if ($session !== null) { $user = $session->user; if ($user !== null) { $address = $user->getAddress(); if ($address !== null) { $country = $address->country; } } } // do something with $country 7 $country = $session?->user?->getAddress()?-> country; // do something with $country Becomes
QoL JSON Extension always enabled Stable sorting Functions: function str_contains(string $haystack, string $needle): bool {} function str_starts_with (string $haystack, string $needle): bool {} function str_ends_with(string $haystack, string $needle): bool {} function fdiv(float $dividend, float $divisor): float {} // IEEE 754 semantics 8
New features
Just In Time Compiler (JIT) • Compiles PHP code into machine language for x86 (Intel/AMD) • Performance gain for CPU bounded (i.e. math) • ⚠ May lead to performance loss on more “traditional” code Part of OpCache: • opcache.jit=on • opache.jit_buffer_size=128M 10
Match Similar to the switch statement Major differences: • Exhaustive, if value not matched throws UnhandledMatchError • Uses strict comparison (===) • No fallthrough • Returns a value • Match condition can be any expression • Match statement must be a single expression (like arrow functions) 11
Match: a simple example PHP 7.x switch (1) { case 0: $result = 'Foo'; break; case 1: $result = 'Bar'; break; case 2: case 3: $result = 'Baz'; break; default: $result = 'FooBar'; break; } echo $result; //> Bar PHP 8.0 echo match (1) { 0 => 'Foo', 1 => 'Bar', 2, 3 => 'Baz', default => 'FooBar', }; //> Bar 12
Match: complex example echo match ($x) { 3, 5 => throw new Exception('Cannot be 3 or 5'), foo() => "foo", $this->bar() => "bar() check", // bar() isn't called if foo() matched with $x }; If nothing is matched a UnhandledMatchError is thrown 13 Possible because throw is an expression in PHP 8
Constructor promotion PHP 7.x class Point { public float $x; public float $y; public float $z; public function __construct( float $x = 0.0, float $y = 0.0, float $z = 0.0, ) { $this->x = $x; $this->y = $y; $this->z = $z; } } PHP 8.0 class Point { public function __construct( public float $x = 0.0, public float $y = 0.0, public float $z = 0.0, ) { } } 14
Attributes: syntax TBD Using doc comments /** @Entity */ class User { /** * @Id * @Column(type="integer") * @GenerateValue */ private $id; } Attributes with Rust-like syntax use DoctrineORMAttributes as ORM; #[ORMEntity] class User { #[ORMId] #[ORMColumn("integer")] #[ORMGenerateValue] private $id; } 15 Class name Constructor argument
Attributes: syntax TBD Hack-style use DoctrineORMAttributes as ORM; <<ORMEntity>> class User { <<ORMId>> <<ORMColumn("integer")>> <<ORMGenerateValue>> private $id; } @@-style use DoctrineORMAttributes as ORM; @@ORMEntity class User { @@ORMId @@ORMColumn("integer") @@ORMGenerateValue private $id; } 16
Attributes $rc = new ReflectionClass (User::class); foreach ($rc->getAttributes() as $attr) { var_dump($attr->getName()); // => "DoctrineORMAttributesEntity" var_dump($attr->getArguments()); // => [] var_dump($attr->newInstance()); // object(DoctrineORMAttributesEntity) ... } 17 Attribute validation happens here
Named arguments // Using positional arguments: array_fill(0, 100, 50); // Using named arguments: array_fill(start_index: 0, num: 100, value: 50); array_fill(value: 50, num: 100, start_index: 0); // Skip default values htmlspecialchars ($string, double_encode: false); // Same as htmlspecialchars ($string, ENT_COMPAT|ENT_HTML401, 'UTF-8', false); Combined with Constructor promotion: Object Initialization Named arguments work with attributes Renaming internal function args and matching the documentation 18
Type System Improvements Union types, mixed, static return, internal functions, opaque objects 19
Existing union types • ?Type = Type|null • iterable = Traversable|array 20
Union types PHP 7.4 class Number { /** @var int|float */ private $number; /** @return float|int */ public function getNumber() { return $this->number; } /** @param float|int $number */ public function setNumber($number): void { $this->number = $number; } } PHP 8.0 class Number { private float|int $number; public function getNumber(): float|int { return $this->number; } public function setNumber(float|int $number): void { $this->number = $number; } } 21
Union types: exotic types function shell_exec(string $command): string|false|null {} 22 Common in standard library ?Type is a shorthand to Type|null
Union types: handling in weak mode In weak mode the handling is a bit tricky: • Type is part of the union • Scalars are coerced to int, float, string, bool in order of preference 23
Union types: weak and strict mode declare(strict_types=0); function test(int|float|bool $arg) { var_dump($arg); } test(45); // int(45) test(45.8); // float(45.8) test("45"); // int(45) test("45.8"); // float(45.8) test(""); // bool(false) test("X"); // bool(true) test([]); // TypeError declare(strict_types=1); function test(int|float|bool $arg) { var_dump($arg); } test(45); // int(45) test(45.8); // float(45.8) test("45"); // TypeError test("45.8"); // TypeError test(""); // TypeError test("X"); // TypeError test([]); // TypeError 24
mixed Distinguish between: • Type is missing because it wasn’t yet added • This function accepts/returns anything 25 function var_dump(mixed $value, mixed ...$value): void {} function unserialize(string $value, array $options = []): mixed {}
mixed // For argument types: // No type is equivalent to mixed class Parent { public function foo(mixed $arg) {} } class Child extends Parent { public function foo($arg) {} } // For return types: // No type means mixed|void class Parent { public function foo(): mixed {} } class Child extends Parent { public function foo() {} } 26 Forbidden: widening type
Static return type // Named constructor class ParentClass { public static function createFromWhatever($whatever): static { return new static($whatever); } } class ChildClass extends ParentClass {} var_dump(ChildClass::createFromWhatever(null)); //object(ChildClass)#1 (0) { //} 27
Static return type // Named constructor class ParentClass { public static function createFromWhatever($whatever): static { return new self($whatever); } } class ChildClass extends ParentClass {} var_dump(ChildClass::createFromWhatever(null)); // TypeError: ParentClass::createFromWhatever(): Return value must be of type ChildClass, ParentClass returned 28 self vs static
PHP Stubs PHP stubs specify signature for internal functions/methods ● Becomes the source of truth ● Generates C code function registration with arg info ● Data is now available through Reflection ○ ReflectionFunction::getReturnType(); ○ ReflectionParameter::getType(); ○ ReflectionParameter::getDefaultValue(); 29
PHP Stubs: what it means for you // Stub class DateTime implements DateTimeInterface { /** @return DateTime */ public function add(DateInterval $interval) { } } // Your code class MyDateTime extends DateTime { public function add(DateInterval $interval): DateTime { } } 30 Now allowed! A real return type would force all extending classes to specify it due to covariance, BC Break!
Resource to object conversion Why? • Better type safety • Better internal support • Long term: convert all resources to objects • That’s why there is no ‘resource’ type • Opaque objects with no API • Oriented Object API might be added later 31
Resource to object conversion Notable conversions in PHP 8 • GdImage • Curl • ZIP • Inflate/Deflate from Zlib • OpenSSL (W.I.P.) 32
Resource to object conversion PHP 7.x only $curlHandler = curl_init($url); if (!is_resource($curlHandler)) { throw new Exception(); } PHP 7.x and PHP 8.0 $curlHandler = curl_init($url); if ($curlHandler !== false) { throw new Exception(); } 33 This is an Object in case of success or false on failure
Notable Backward Compatibility (BC) breaks
Notable BC breaks: error handling • E_ALL by default • Might need to adjust error handler and INI settings • PDO now throws exception by default • Might break application not using PDO’s throw mode • Can de disabled by passing: PDO::ERRMODE_SILENT during construction 35
Notable BC breaks: locale • Default locale is now always the C locale • Float to string conversions are now locale independent 36
Notable BC breaks: locale PHP 7.4 setlocale(LC_ALL, "de_DE"); $f = 3.14; (string) $f; // 3,14 strval($f); // 3,14 print_r($f); // 3,14 var_dump($f); // float(3,14) debug_zval_dump($f); // float(3,14) settype($f, "string"); // 3,14 implode([$f]); // 3,14 xmlrpc_encode($f); // 3,14 PHP 8.0 setlocale(LC_ALL, "de_DE"); $f = 3.14; (string) $f; // 3.14 strval($f); // 3.14 print_r($f); // 3.14 var_dump($f); // float(3.14) debug_zval_dump($f); // float(3.14) settype($f, "string"); // 3.14 implode([$f]); // 3.14 xmlrpc_encode($f); // 3.14 37
Notable BC breaks: warning promotion • Consistent TypeError for internal functions • This allows the PHP Stubs to exist • LSP errors always throw (type safety) • Engine notices and warnings reclassified • Complete list at: https://wiki.php.net/rfc/engine_warnings • ValueErrors • A good attempt to list all affected functions: https://nahan.fr/nouvelle-erreur-valueerror-dans-php-8/ These only affect programming errors Handling the error is nonsensical, code needs to be fixed 38
Warning promotion: what benefits? var_dump(strlen([])); // Warning: strlen() expects parameter 1 to be string, array given // NULL function strlen(string $str): int|null {} var_dump(array_fill(0, -100, 'foobar')); // Warning: array_fill(): Number of elements can't be negative // bool(false) function array_fill(int $start_key, int $num, mixed $val): array|false {} 39
Warning promotion: what benefits? var_dump(strlen([])); // Fatal error: Uncaught TypeError: strlen(): // Argument #1 ($str) must be of type string, array given function strlen(string $str): int {} var_dump(array_fill(0, -100, 'foobar')); // Fatal error: Uncaught ValueError: array_fill(): // Argument #2 ($num) must be greater than or equal to 0 function array_fill(int $start_key, int $num, mixed $val): array {} 40
Warning promotion: what stays the same var_dump(fopen("does_not_exist.txt", "r'")); // Warning: fopen(does_not_exist.txt): Failed to open stream: Operation not permitted // bool(false) 41 Actually I lied, this is capitalized in PHP 8.0
Numeric strings Numeric strings: “12”, “15.6”, “5e6”, “ 10” Leading-numeric strings: “10 ”, “14abc” Non-numeric strings: “string” 42 Emit E_NOTICE "A non well formed numeric value encountered" Leading white-spaces are considered more numeric than trailing white-spaces
Numeric strings Where are numeric strings used? • Type declarations in weak mode • is_numeric() function • String to string comparisons • Arithmetic and bitwise operations • Increment/Decrement operation (++ and --) • String offsets • NOT array offsets, they have different semantics 43
Numeric strings: bringing back sanity All details: Saner Numeric Strings RFC: https://wiki.php.net/rfc/saner-numeric-strings TL;DR: • E_NOTICE "A non well formed numeric value encountered" to E_WARNING "A non-numeric value encountered" with value preserved • Other E_WARNINGs promoted to TypeError 44
Numeric strings: bringing back sanity PHP 7.4 function foo(int $i) { var_dump($i); } foo(" 123"); // int(123) foo("123 "); // int(123) with E_NOTICE foo("123abc"); // int(123) with E_NOTICE var_dump(123 + "123 ");// int(246) with E_NOTICE var_dump(123 + "123abc");// int(246) with E_NOTICE var_dump(123 + "str"); // int(123) with E_WARNING var_dump("123" == " 123"); // bool(true) var_dump("123" == "123 "); // bool(false) var_dump("123" == "123abc"); // bool(false) PHP 8.0 function foo(int $i) { var_dump($i); } foo(" 123"); // int(123) foo("123 "); // int(123) foo("123abc"); // int(123) with E_WARNING var_dump(123 + "123 "); var_dump(123 + "123abc"); // int(246) with E_WARNING var_dump(123 + "str"); // TypeError var_dump("123" == " 123"); // bool(true) var_dump("123" == "123 "); // bool(true) var_dump("123" == "123abc"); // bool(false) 45
String to number comparisons $validValues = ["foo", "bar", "baz"]; $value = 0; var_dump(in_array($value, $validValues)); // bool(true) WTF??? var_dump(0 == "foo"); // bool(true) WTF??? Are always performed numerically 46
String to number comparisons: sanity at last Comparison | PHP 7.4 | PHP 8.0 ---------------------------------- 0 == "0" | true | true 0 == "0.0" | true | true 0 == "foo" | true | false 0 == "" | true | false 42 == " 42" | true | true 42 == "42 " | true | true 42 == "42foo" | true | false 47
Other BC breaks: • Precedence of the concatenation operator . • Default values before mandatory ones are now deprecated • final on private methods (except on __construct()) • Treat namespaces names as a single token • Full list in the UPGRADING document https://github.com/php/php-src/blob/master/UPGRADING 48
Performance gain • strtolower() now uses a SIMD implementation when using the "C" LC_CTYPE locale • array_slice() on an array without gaps will no longer scan the whole array to find the start offset. This may significantly reduce the runtime of the function with large offsets and small lengths • Removal of old style constructor in PHP 8.0 seems to yield 20% performance increase in WordPress https://idk.dev/improving-performance-of-php-for-arm64-and-im pact-on-amazon-ec2-m6g-instances/ 49
Micro-optimizations DISCLAIMER: I have not tested the performance of the following changes, these are just tidbits I remember which have been committed to php-src 50
Micro-optimizations • Optimize $x === null into is_null($x) https://github.com/php/php-src/commit/36afe4e39ec724eab19c54c1492db030721d0ec1 • Yes is_null() generated a “better” TYPE_CHECK OpCode • Optimize instanceof checks https://github.com/php/php-src/commit/c858d17f06179aa25f6e8aa06965313fd519d8e9 https://github.com/php/php-src/commit/c63a0e005abe4b00ab097dc47ca53d20788a6361 • Optimize int === int/float === float https://github.com/php/php-src/commit/e8525c2f68d938162af055fd843239778a13565d • Weirdly enough by copying the implementation of == • Optimize is_scalar($x) into a TYPE_CHECK opcode https://github.com/php/php-src/commit/937fa6d9e2f38b5e70a2a510b87ce0be4839a404 • Optimize return type checking https://github.com/php/php-src/commit/0bac7854779595603e0431521c60744665641734 51
Thank you

PHP 8: Process & Fixing Insanity

  • 1.
    POINT TECHNIQUE PHP 8:Process & Fixing Insanity
  • 2.
    George Peter Banyard ●Mathematics student at Imperial College London ● Currently hired by The Coding Machine to work on php-src, i.e. the PHP Engine ● Find me on Twitter and elsewhere @Girgias ● https://gpb.moe 2 Me at 3AM realising I need a photo for the presentation
  • 3.
    How I gotinvolved with the PHP project • French translation PHP documentation • PHP internal mailing list • Proposed the “Deprecate PHP Short open tags” PHP RFC • Started tinkering with PHP’s source code • PoC: declare statement which promote warnings to Exceptions • Made multiple patches • php-src VCS karma granted 3
  • 4.
    PHP a trulyopen source language: PHP RFCs A PHP RFC is a proposal which affects the PHP engine: Behaviour change, feature addition, deprecations, extensions Anyone can propose a PHP RFC, from QoL to Advanced feature • Minimum 2 week discussion period • 2 week voting period • Everyone with a PHP VCS account can vote (1000+ people!) PHP 8 is 30+ accepted RFCs from over 15 people Many more which have been declined See them all at: https://wiki.php.net/rfc 4
  • 5.
    QoL Trailing commas inparameter list and closure use list $longArgs_longVars = function ( $longArgument, $longerArgument , $muchLongerArgument , ) use ( $longVar1, $longerVar2, $muchLongerVar3 , ) { // body }; 5 Allowed!
  • 6.
    QoL ::class notation onvariables $class = $object::class; // Identical to $class = get_class($object); DateTimeInterface::createFromInterface public static function createFromInterface (DateTimeInterface $object): DateTime public static function createFromInterface (DateTimeInterface $object): DateTimeImmutable 6
  • 7.
    QoL Non-capturing catches try { doStuffWhichMightThrow(); } catch (Exception) { // The intention is clear: exception details are irrelevant echo "Some error message" ; } Null safe operator ?-> $country = null; if ($session !== null) { $user = $session->user; if ($user !== null) { $address = $user->getAddress(); if ($address !== null) { $country = $address->country; } } } // do something with $country 7 $country = $session?->user?->getAddress()?-> country; // do something with $country Becomes
  • 8.
    QoL JSON Extension alwaysenabled Stable sorting Functions: function str_contains(string $haystack, string $needle): bool {} function str_starts_with (string $haystack, string $needle): bool {} function str_ends_with(string $haystack, string $needle): bool {} function fdiv(float $dividend, float $divisor): float {} // IEEE 754 semantics 8
  • 9.
  • 10.
    Just In TimeCompiler (JIT) • Compiles PHP code into machine language for x86 (Intel/AMD) • Performance gain for CPU bounded (i.e. math) • ⚠ May lead to performance loss on more “traditional” code Part of OpCache: • opcache.jit=on • opache.jit_buffer_size=128M 10
  • 11.
    Match Similar to theswitch statement Major differences: • Exhaustive, if value not matched throws UnhandledMatchError • Uses strict comparison (===) • No fallthrough • Returns a value • Match condition can be any expression • Match statement must be a single expression (like arrow functions) 11
  • 12.
    Match: a simpleexample PHP 7.x switch (1) { case 0: $result = 'Foo'; break; case 1: $result = 'Bar'; break; case 2: case 3: $result = 'Baz'; break; default: $result = 'FooBar'; break; } echo $result; //> Bar PHP 8.0 echo match (1) { 0 => 'Foo', 1 => 'Bar', 2, 3 => 'Baz', default => 'FooBar', }; //> Bar 12
  • 13.
    Match: complex example echomatch ($x) { 3, 5 => throw new Exception('Cannot be 3 or 5'), foo() => "foo", $this->bar() => "bar() check", // bar() isn't called if foo() matched with $x }; If nothing is matched a UnhandledMatchError is thrown 13 Possible because throw is an expression in PHP 8
  • 14.
    Constructor promotion PHP 7.x classPoint { public float $x; public float $y; public float $z; public function __construct( float $x = 0.0, float $y = 0.0, float $z = 0.0, ) { $this->x = $x; $this->y = $y; $this->z = $z; } } PHP 8.0 class Point { public function __construct( public float $x = 0.0, public float $y = 0.0, public float $z = 0.0, ) { } } 14
  • 15.
    Attributes: syntax TBD Usingdoc comments /** @Entity */ class User { /** * @Id * @Column(type="integer") * @GenerateValue */ private $id; } Attributes with Rust-like syntax use DoctrineORMAttributes as ORM; #[ORMEntity] class User { #[ORMId] #[ORMColumn("integer")] #[ORMGenerateValue] private $id; } 15 Class name Constructor argument
  • 16.
    Attributes: syntax TBD Hack-style useDoctrineORMAttributes as ORM; <<ORMEntity>> class User { <<ORMId>> <<ORMColumn("integer")>> <<ORMGenerateValue>> private $id; } @@-style use DoctrineORMAttributes as ORM; @@ORMEntity class User { @@ORMId @@ORMColumn("integer") @@ORMGenerateValue private $id; } 16
  • 17.
    Attributes $rc = newReflectionClass (User::class); foreach ($rc->getAttributes() as $attr) { var_dump($attr->getName()); // => "DoctrineORMAttributesEntity" var_dump($attr->getArguments()); // => [] var_dump($attr->newInstance()); // object(DoctrineORMAttributesEntity) ... } 17 Attribute validation happens here
  • 18.
    Named arguments // Usingpositional arguments: array_fill(0, 100, 50); // Using named arguments: array_fill(start_index: 0, num: 100, value: 50); array_fill(value: 50, num: 100, start_index: 0); // Skip default values htmlspecialchars ($string, double_encode: false); // Same as htmlspecialchars ($string, ENT_COMPAT|ENT_HTML401, 'UTF-8', false); Combined with Constructor promotion: Object Initialization Named arguments work with attributes Renaming internal function args and matching the documentation 18
  • 19.
    Type System Improvements Uniontypes, mixed, static return, internal functions, opaque objects 19
  • 20.
    Existing union types •?Type = Type|null • iterable = Traversable|array 20
  • 21.
    Union types PHP 7.4 classNumber { /** @var int|float */ private $number; /** @return float|int */ public function getNumber() { return $this->number; } /** @param float|int $number */ public function setNumber($number): void { $this->number = $number; } } PHP 8.0 class Number { private float|int $number; public function getNumber(): float|int { return $this->number; } public function setNumber(float|int $number): void { $this->number = $number; } } 21
  • 22.
    Union types: exotictypes function shell_exec(string $command): string|false|null {} 22 Common in standard library ?Type is a shorthand to Type|null
  • 23.
    Union types: handlingin weak mode In weak mode the handling is a bit tricky: • Type is part of the union • Scalars are coerced to int, float, string, bool in order of preference 23
  • 24.
    Union types: weakand strict mode declare(strict_types=0); function test(int|float|bool $arg) { var_dump($arg); } test(45); // int(45) test(45.8); // float(45.8) test("45"); // int(45) test("45.8"); // float(45.8) test(""); // bool(false) test("X"); // bool(true) test([]); // TypeError declare(strict_types=1); function test(int|float|bool $arg) { var_dump($arg); } test(45); // int(45) test(45.8); // float(45.8) test("45"); // TypeError test("45.8"); // TypeError test(""); // TypeError test("X"); // TypeError test([]); // TypeError 24
  • 25.
    mixed Distinguish between: • Typeis missing because it wasn’t yet added • This function accepts/returns anything 25 function var_dump(mixed $value, mixed ...$value): void {} function unserialize(string $value, array $options = []): mixed {}
  • 26.
    mixed // For argumenttypes: // No type is equivalent to mixed class Parent { public function foo(mixed $arg) {} } class Child extends Parent { public function foo($arg) {} } // For return types: // No type means mixed|void class Parent { public function foo(): mixed {} } class Child extends Parent { public function foo() {} } 26 Forbidden: widening type
  • 27.
    Static return type //Named constructor class ParentClass { public static function createFromWhatever($whatever): static { return new static($whatever); } } class ChildClass extends ParentClass {} var_dump(ChildClass::createFromWhatever(null)); //object(ChildClass)#1 (0) { //} 27
  • 28.
    Static return type //Named constructor class ParentClass { public static function createFromWhatever($whatever): static { return new self($whatever); } } class ChildClass extends ParentClass {} var_dump(ChildClass::createFromWhatever(null)); // TypeError: ParentClass::createFromWhatever(): Return value must be of type ChildClass, ParentClass returned 28 self vs static
  • 29.
    PHP Stubs PHP stubsspecify signature for internal functions/methods ● Becomes the source of truth ● Generates C code function registration with arg info ● Data is now available through Reflection ○ ReflectionFunction::getReturnType(); ○ ReflectionParameter::getType(); ○ ReflectionParameter::getDefaultValue(); 29
  • 30.
    PHP Stubs: whatit means for you // Stub class DateTime implements DateTimeInterface { /** @return DateTime */ public function add(DateInterval $interval) { } } // Your code class MyDateTime extends DateTime { public function add(DateInterval $interval): DateTime { } } 30 Now allowed! A real return type would force all extending classes to specify it due to covariance, BC Break!
  • 31.
    Resource to objectconversion Why? • Better type safety • Better internal support • Long term: convert all resources to objects • That’s why there is no ‘resource’ type • Opaque objects with no API • Oriented Object API might be added later 31
  • 32.
    Resource to objectconversion Notable conversions in PHP 8 • GdImage • Curl • ZIP • Inflate/Deflate from Zlib • OpenSSL (W.I.P.) 32
  • 33.
    Resource to objectconversion PHP 7.x only $curlHandler = curl_init($url); if (!is_resource($curlHandler)) { throw new Exception(); } PHP 7.x and PHP 8.0 $curlHandler = curl_init($url); if ($curlHandler !== false) { throw new Exception(); } 33 This is an Object in case of success or false on failure
  • 34.
  • 35.
    Notable BC breaks:error handling • E_ALL by default • Might need to adjust error handler and INI settings • PDO now throws exception by default • Might break application not using PDO’s throw mode • Can de disabled by passing: PDO::ERRMODE_SILENT during construction 35
  • 36.
    Notable BC breaks:locale • Default locale is now always the C locale • Float to string conversions are now locale independent 36
  • 37.
    Notable BC breaks:locale PHP 7.4 setlocale(LC_ALL, "de_DE"); $f = 3.14; (string) $f; // 3,14 strval($f); // 3,14 print_r($f); // 3,14 var_dump($f); // float(3,14) debug_zval_dump($f); // float(3,14) settype($f, "string"); // 3,14 implode([$f]); // 3,14 xmlrpc_encode($f); // 3,14 PHP 8.0 setlocale(LC_ALL, "de_DE"); $f = 3.14; (string) $f; // 3.14 strval($f); // 3.14 print_r($f); // 3.14 var_dump($f); // float(3.14) debug_zval_dump($f); // float(3.14) settype($f, "string"); // 3.14 implode([$f]); // 3.14 xmlrpc_encode($f); // 3.14 37
  • 38.
    Notable BC breaks:warning promotion • Consistent TypeError for internal functions • This allows the PHP Stubs to exist • LSP errors always throw (type safety) • Engine notices and warnings reclassified • Complete list at: https://wiki.php.net/rfc/engine_warnings • ValueErrors • A good attempt to list all affected functions: https://nahan.fr/nouvelle-erreur-valueerror-dans-php-8/ These only affect programming errors Handling the error is nonsensical, code needs to be fixed 38
  • 39.
    Warning promotion: whatbenefits? var_dump(strlen([])); // Warning: strlen() expects parameter 1 to be string, array given // NULL function strlen(string $str): int|null {} var_dump(array_fill(0, -100, 'foobar')); // Warning: array_fill(): Number of elements can't be negative // bool(false) function array_fill(int $start_key, int $num, mixed $val): array|false {} 39
  • 40.
    Warning promotion: whatbenefits? var_dump(strlen([])); // Fatal error: Uncaught TypeError: strlen(): // Argument #1 ($str) must be of type string, array given function strlen(string $str): int {} var_dump(array_fill(0, -100, 'foobar')); // Fatal error: Uncaught ValueError: array_fill(): // Argument #2 ($num) must be greater than or equal to 0 function array_fill(int $start_key, int $num, mixed $val): array {} 40
  • 41.
    Warning promotion: whatstays the same var_dump(fopen("does_not_exist.txt", "r'")); // Warning: fopen(does_not_exist.txt): Failed to open stream: Operation not permitted // bool(false) 41 Actually I lied, this is capitalized in PHP 8.0
  • 42.
    Numeric strings Numeric strings:“12”, “15.6”, “5e6”, “ 10” Leading-numeric strings: “10 ”, “14abc” Non-numeric strings: “string” 42 Emit E_NOTICE "A non well formed numeric value encountered" Leading white-spaces are considered more numeric than trailing white-spaces
  • 43.
    Numeric strings Where arenumeric strings used? • Type declarations in weak mode • is_numeric() function • String to string comparisons • Arithmetic and bitwise operations • Increment/Decrement operation (++ and --) • String offsets • NOT array offsets, they have different semantics 43
  • 44.
    Numeric strings: bringingback sanity All details: Saner Numeric Strings RFC: https://wiki.php.net/rfc/saner-numeric-strings TL;DR: • E_NOTICE "A non well formed numeric value encountered" to E_WARNING "A non-numeric value encountered" with value preserved • Other E_WARNINGs promoted to TypeError 44
  • 45.
    Numeric strings: bringingback sanity PHP 7.4 function foo(int $i) { var_dump($i); } foo(" 123"); // int(123) foo("123 "); // int(123) with E_NOTICE foo("123abc"); // int(123) with E_NOTICE var_dump(123 + "123 ");// int(246) with E_NOTICE var_dump(123 + "123abc");// int(246) with E_NOTICE var_dump(123 + "str"); // int(123) with E_WARNING var_dump("123" == " 123"); // bool(true) var_dump("123" == "123 "); // bool(false) var_dump("123" == "123abc"); // bool(false) PHP 8.0 function foo(int $i) { var_dump($i); } foo(" 123"); // int(123) foo("123 "); // int(123) foo("123abc"); // int(123) with E_WARNING var_dump(123 + "123 "); var_dump(123 + "123abc"); // int(246) with E_WARNING var_dump(123 + "str"); // TypeError var_dump("123" == " 123"); // bool(true) var_dump("123" == "123 "); // bool(true) var_dump("123" == "123abc"); // bool(false) 45
  • 46.
    String to numbercomparisons $validValues = ["foo", "bar", "baz"]; $value = 0; var_dump(in_array($value, $validValues)); // bool(true) WTF??? var_dump(0 == "foo"); // bool(true) WTF??? Are always performed numerically 46
  • 47.
    String to numbercomparisons: sanity at last Comparison | PHP 7.4 | PHP 8.0 ---------------------------------- 0 == "0" | true | true 0 == "0.0" | true | true 0 == "foo" | true | false 0 == "" | true | false 42 == " 42" | true | true 42 == "42 " | true | true 42 == "42foo" | true | false 47
  • 48.
    Other BC breaks: •Precedence of the concatenation operator . • Default values before mandatory ones are now deprecated • final on private methods (except on __construct()) • Treat namespaces names as a single token • Full list in the UPGRADING document https://github.com/php/php-src/blob/master/UPGRADING 48
  • 49.
    Performance gain • strtolower()now uses a SIMD implementation when using the "C" LC_CTYPE locale • array_slice() on an array without gaps will no longer scan the whole array to find the start offset. This may significantly reduce the runtime of the function with large offsets and small lengths • Removal of old style constructor in PHP 8.0 seems to yield 20% performance increase in WordPress https://idk.dev/improving-performance-of-php-for-arm64-and-im pact-on-amazon-ec2-m6g-instances/ 49
  • 50.
    Micro-optimizations DISCLAIMER: I have nottested the performance of the following changes, these are just tidbits I remember which have been committed to php-src 50
  • 51.
    Micro-optimizations • Optimize $x=== null into is_null($x) https://github.com/php/php-src/commit/36afe4e39ec724eab19c54c1492db030721d0ec1 • Yes is_null() generated a “better” TYPE_CHECK OpCode • Optimize instanceof checks https://github.com/php/php-src/commit/c858d17f06179aa25f6e8aa06965313fd519d8e9 https://github.com/php/php-src/commit/c63a0e005abe4b00ab097dc47ca53d20788a6361 • Optimize int === int/float === float https://github.com/php/php-src/commit/e8525c2f68d938162af055fd843239778a13565d • Weirdly enough by copying the implementation of == • Optimize is_scalar($x) into a TYPE_CHECK opcode https://github.com/php/php-src/commit/937fa6d9e2f38b5e70a2a510b87ce0be4839a404 • Optimize return type checking https://github.com/php/php-src/commit/0bac7854779595603e0431521c60744665641734 51
  • 52.