Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Typed Properties and more: What's coming in PHP 7.4?

5,083 views

Published on

Slides for PHPRussia 2019 talk on new features in PHP 7.4.

Published in: Technology

Typed Properties and more: What's coming in PHP 7.4?

  1. 1. Typed Properties and more: What’s coming in PHP 7.4? Nikita Popov
  2. 2. Release schedule (tentative) Now June 6th PHP 7.4 Alpha 1 July 18th PHP 7.4 Beta 1 – Feature freeze November 21st PHP 7.4 GA Release
  3. 3. Release schedule (tentative) Now June 6th PHP 7.4 Alpha 1 July 18th PHP 7.4 Beta 1 – Feature freeze November 21st PHP 7.4 GA Release December 2020 PHP 8.0 Release ≈
  4. 4. Overview ● FFI & Preloading ● Typed Properties ● Arrow Functions ● Covariant Return Types ● ??= ● Array Spread Operator ● WeakReference ● Deprecations
  5. 5. Overview ● FFI & Preloading => See Dmitry Stogov’s talk! ● Typed Properties ● Arrow Functions ● Covariant Return Types ● ??= ● Array Spread Operator ● WeakReference ● Deprecations
  6. 6. Typed Properties class User { public int $id; public string $name; } $user = new User; $user->id = 42; $user->name = []; // Uncaught TypeError: Typed property User::$name // must be string, array used
  7. 7. Nullability & Initialization class User { public int $id; // no null default public ?string $name; // also no null default (!) }
  8. 8. Nullability & Initialization class User { public int $id; // no null default public ?string $name; // also no null default (!) } $user = new User; var_dump($user->name); // Uncaught Error: Typed property User::$name must // not be accessed before initialization
  9. 9. Nullability & Initialization class User { public int $id; public ?string $name = null; } $user = new User; var_dump($user->name); // NULL
  10. 10. References class User { public int $id = 42; public string $name = "nikic"; } $user = new User; $id =& $user->id; $id = "not an id"; // Uncaught TypeError: Cannot assign string to // reference held by property User::$id of type int
  11. 11. Poor Man’s Intersection Types class Test { public ?Traversable $traversable; public ?Countable $countable; public $both = null; } $test = new Test; $test->traversable =& $test->both; $test->countable =& $test->both; $test->both = new ArrayIterator;
  12. 12. Poor Man’s Intersection Types class Test { public ?Traversable $traversable; public ?Countable $countable; public $both = null; } $test = new Test; $test->traversable =& $test->both; $test->countable =& $test->both; $test->both = new ArrayIterator; Effectively ?(Traversable&Countable)
  13. 13. Poor Man’s Intersection Types class Test { public ?Traversable $traversable; public ?Countable $countable; public $both = null; } $test = new Test; $test->traversable =& $test->both; $test->countable =& $test->both; $test->both = new AppendIterator; // Uncaught TypeError: Cannot assign AppendIterator // to reference held by property Test::$countable // of type ?Countable
  14. 14. Poor Man’s Typed Variables int $i = 0; $i = "foobar";
  15. 15. Poor Man’s Typed Variables $i =& int(0); $i = "foobar"; // Uncaught TypeError: Cannot assign string to // reference held by property class@anonymous::$prop // of type int
  16. 16. Poor Man’s Typed Variables function &int(int $i) { $obj = new class { public int $prop; }; $obj->prop = $i; $GLOBALS['huge_memory_leak'][] = $obj; return $obj->prop; } $i =& int(0); $i = "foobar"; // Uncaught TypeError: Cannot assign string to // reference held by property class@anonymous::$prop // of type int
  17. 17. Public Typed Properties? class User { private $name; public function getName(): string { return $this->name; } public function setName(string $name): void { $this->name = $name; } }
  18. 18. Public Typed Properties? class User { public string $name; }
  19. 19. Public Typed Properties? class User { public string $name; } What if additional validation will be needed?
  20. 20. Future: Property Accessors? class User { public string $name { get; set($name) { if (strlen($name) == 0) throw new Exception( 'Name cannot be empty'); $this->name = $name; } }; }
  21. 21. Arrow Functions array_filter( $values, function($v) use($allowedValues) { return in_array($v, $allowedValues); } );
  22. 22. Arrow Functions array_filter( $values, fn($v) => in_array($v, $allowedValues) );
  23. 23. Arrow Functions array_filter( $values, fn($v) => in_array($v, $allowedValues) ); Implicit use($allowedValues)
  24. 24. By-Value Binding $i = 0; $fn = fn() => $i++; $fn(); var_dump($i); // int(0)
  25. 25. By-Value Binding $fns = []; foreach ([1, 2, 3] as $i) { $fns[] = fn() => $i; } foreach ($fns as $fn) { echo $fn() . " "; } // Prints: 1 2 3 // Not: 3 3 3 (would happen with by-reference)
  26. 26. Syntax – Why fn? array_filter( $values, $v => in_array($v, $allowedValues) ); ECMAScript syntax
  27. 27. Syntax – Array Ambiguity $array = [ $a => $a + $b, $x => $x * $y, ]; Array of arrow functions? Or just a key-value map?
  28. 28. Syntax – Parsing // Looks like: Assignment expression ($x = [42] + ["foobar"]) => $x; // Looks like: Constant lookup + bitwise and (Type &$x) => $x; // Looks like: Ternary operator $a ? ($b): Type => $c : $d;
  29. 29. Syntax – Parsing // Looks like: Assignment expression ($x = [42] + ["foobar"]) => $x; // Looks like: Constant lookup + bitwise and (Type &$x) => $x; // Looks like: Ternary operator $a ? ($b): Type => $c : $d; Need special handling starting at ( But only know it’s an arrow function at =>
  30. 30. Future: Block Syntax array_filter( $values, fn($v) => in_array($v, $allowedValues) );
  31. 31. Future: Block Syntax array_filter( $values, fn($v) { // More code... return in_array($v, $allowedValues); } );
  32. 32. Future: Block Syntax array_filter( $values, fn($v) { // More code... return in_array($v, $allowedValues); } ); Basically like normal closure syntax, but with implicit variable capture
  33. 33. Covariant Return Types class A {} class B extends A {} class Producer { public function produce(): A {} } class ChildProducer extends Producer { public function produce(): B {} } Sound because $childFactory->create() still instanceof A.
  34. 34. Common Case: Self class Foo { public function fluent(): self {} } class Bar extends Foo { public function fluent(): self {} }
  35. 35. Ordering Issues class A { public function method(): B {} } class B extends A { public function method(): C {} } class C extends B {}
  36. 36. Ordering Issues class A { public function method(): B {} } class B extends A { public function method(): C {} } class C extends B {} Sound, but class C not available when verifying B. => No way to reorder classes “correctly”. => Will only work with autoloading (for now).
  37. 37. Contravariant Parameter Types class A {} class B extends A {} class Consumer { public function comsume(B $value) {} } class ChildConsumer extends Consumer { public function comsume(A $value) {} } Sound, but rarely useful.
  38. 38. Covariant Parameter Types? interface Event { ... } class SpecificEvent implements Event { ... } interface EventHandler { public function handle(Event $e); } class SpecificEventHandler implements EventHandler { public function handle(SpecificEvent $e) {} } Unsound, will never work!
  39. 39. Future: Generics interface Event { ... } class SpecificEvent implements Event { ... } interface EventHandler<E: Event> { public function handle(E $e); } class SpecificEventHandler implements EventHandler<SpecificEvent> { public function handle(SpecificEvent $e) {} }
  40. 40. ??= $options["something"] ??= new DefaultObject; // Roughly equivalent to: $options["something"] = $options["something"] ?? new DefaultObject;
  41. 41. ??= $options["something"] ??= new DefaultObject; Only created if $options["something"] is null
  42. 42. Array spread operator $array = [3, 4, 5]; return call(1, 2, ...$array); // call(1, 2, 3, 4, 5) $array = [3, 4, 5]; return [1, 2, ...$array]; // [1, 2, 3, 4, 5]
  43. 43. Array spread operator $array = new ArrayIterator([1, 2, 3]); return call(1, 2, ...$array); // call(1, 2, 3, 4, 5) $array = new ArrayIterator([1, 2, 3]); return [1, 2, ...$array]; // [1, 2, 3, 4, 5]
  44. 44. Array spread operator $array = ['y' => 'b', 'z' => 'c']; return ['x' => 'a', ...$array]; // Uncaught Error: Cannot unpack array with // string keys
  45. 45. Future: … in destructuring [$head, ...$tail] = $array;
  46. 46. Future: … in destructuring $array = [2 => 2, 1 => 1, 0 => 0]; [$head, ...$tail] = $array; What happens?
  47. 47. WeakReference $object = new stdClass; $weakRef = WeakReference::create($object); var_dump($weakRef->get()); // object(stdClass)#1 unset($object); var_dump($weakRef->get()); // NULL
  48. 48. WeakReference $this->data[get_object_id($object)] = [WeakReference::create($object), $data];
  49. 49. WeakReference $this->data[get_object_id($object)] = [WeakReference::create($object), $data]; Will be reused once object is destroyed
  50. 50. WeakReference $this->data[get_object_id($object)] = [WeakReference::create($object), $data]; Will be reused once object is destroyed Check $weakRef->get() != null to know whether the entry is still valid
  51. 51. WeakReference $this->data[get_object_id($object)] = [WeakReference::create($object), $data]; Will be reused once object is destroyed Check $weakRef->get() != null to know whether the entry is still valid Problem: Doesn’t keep $object alive, but keeps dead cache entry
  52. 52. Future: WeakMap $this->data = new WeakMap(); $this->data[$object] = $data; Does not keep $object alive; immediately drops cache entry when object destroyed
  53. 53. Deprecations Surprisingly few for now… …there will probably be more.
  54. 54. Ternary Associativity return $a == 1 ? 'one' : $a == 2 ? 'two' : 'other'; // was intended as: return $a == 1 ? 'one' : ($a == 2 ? 'two' : 'other'); // but PHP interprets it as: return ($a == 1 ? 'one' : $a == 2) ? 'two' : 'other';
  55. 55. Ternary Associativity return $a == 1 ? 'one' // Deprecated in 7.4. : $a == 2 ? 'two' // Compile error in 8.0. : 'other'; // was intended as: return $a == 1 ? 'one' : ($a == 2 ? 'two' : 'other'); // but PHP interprets it as: return ($a == 1 ? 'one' : $a == 2) ? 'two' : 'other';
  56. 56. Concatenation Precedence $a = 1; $b = 2; echo "Sum: " . $a+$b; // was intended as: echo "Sum: " . ($a+$b); // but PHP interprets it as: echo ("Sum: " . $a)+$b;
  57. 57. Concatenation Precedence $a = 1; $b = 2; echo "Sum: " . $a+$b; // Deprecated in PHP 7.4 // was intended as: echo "Sum: " . ($a+$b); // New behavior in PHP 8.0 // but PHP interprets it as: echo ("Sum: " . $a)+$b;
  58. 58. Short Open Tags (?) ● <? deprecated in 7.4, removed in 8.0 ● Only <?php and <?= supported ● (???) short_open_tag default value from On to Off in 7.4 Disclaimer: RFC accepted, but much push-back after voting.
  59. 59. 3v4l.org
  60. 60. Travis CI php: - 7.3 - 7.4snapshot - nightly install: - composer install --ignore-platform-reqs PHPUnit claims it is not PHP 8 compatible (it is)PHP 8
  61. 61. Docker ● https://github.com/devilbox/docker-php-fpm-7.4 ● https://github.com/devilbox/docker-php-fpm-8.0
  62. 62. Thank You! Questions?

×