3v4l.org

run code in 300+ PHP versions simultaneously
<?php class Collection { private $items; private static $macros = []; public static function macro($name, callable $macro) { static::$macros[$name] = Closure::fromCallable($macro); } public static function mixin($class) { foreach (get_class_methods($class) as $method) { static::macro($method, $class->$method()); } } public function __construct(array $items) { $this->items = $items; } public function map(callable $callback) { return new static(array_map($callback, $this->items)); } public function all() { return $this->items; } public function exampleClosure() { return function () { return $this->items; }; } public function __call($method, $args) { return static::$macros[$method]->bindTo($this, static::class)(...$args); } public static function __callStatic($method, $args) { return static::$macros[$method]->bindTo(null, static::class)(...$args); } } Collection::macro("flatMap", function (callable $callback) { $tmp = []; foreach ($this->items as $item) { $tmp = array_merge($tmp, $callback($item)); } return new static($tmp); }); $items = new Collection(["a", "b", "c"]); $items = $items->flatMap(function ($item) { return [$item, $item]; }); if ($items->all() === ["a", "a", "b", "b", "c", "c"]) { echo "it works"; } // This is the thing that is deprecated and causes a warning: // Here we're removing $this from a closure that isn't static // All non-static closures will have a $this in PHP 8.x when defined in a method try { var_dump( $items->exampleClosure()->bindTo(null)() ); } catch (\Throwable $e) { // We'll get an error about using $this is a non-object context // This will no longer be possible after PHP 7.4 var_dump($e); } // You can definitely still do this: class UnrelatedClass { private $items = ["foo", "bar"]; } var_dump( $items->exampleClosure()->bindTo(new UnrelatedClass, UnrelatedClass::class)() ); // The only thing that could cause problems is: // methods meant to be called statically in a mixin that were not defined with static: class SomeMixin { public function range() { return function ($start, $end) { return new static(range($start, $end)); }; } public function staticRange() { return static function ($start, $end) { return new static(range($start, $end)); }; } } Collection::mixin(new SomeMixin()); // This will error because: // 1. the above is not defined with static // 2. The closure is written inside a class var_dump(Collection::range(0, 10)); // The following will not because the closure was defined with static var_dump(Collection::staticRange(0, 10)); // Basically the change prevents the creation of methods and closures that can be used as instance and static methods at the same time by dynamically checking $this inside the function.
Output for 8.0.0 - 8.0.30, 8.1.0 - 8.1.28, 8.2.0 - 8.2.19, 8.3.0 - 8.3.7
it works Warning: Cannot unbind $this of closure using $this in /in/3OKGT on line 76 object(Error)#2 (7) { ["message":protected]=> string(34) "Value of type null is not callable" ["string":"Error":private]=> string(0) "" ["code":protected]=> int(0) ["file":protected]=> string(9) "/in/3OKGT" ["line":protected]=> int(76) ["trace":"Error":private]=> array(0) { } ["previous":"Error":private]=> NULL } array(2) { [0]=> string(3) "foo" [1]=> string(3) "bar" } object(Collection)#8 (1) { ["items":"Collection":private]=> array(11) { [0]=> int(0) [1]=> int(1) [2]=> int(2) [3]=> int(3) [4]=> int(4) [5]=> int(5) [6]=> int(6) [7]=> int(7) [8]=> int(8) [9]=> int(9) [10]=> int(10) } } object(Collection)#7 (1) { ["items":"Collection":private]=> array(11) { [0]=> int(0) [1]=> int(1) [2]=> int(2) [3]=> int(3) [4]=> int(4) [5]=> int(5) [6]=> int(6) [7]=> int(7) [8]=> int(8) [9]=> int(9) [10]=> int(10) } }
Output for 7.4.0 - 7.4.25, 7.4.27 - 7.4.33
it works Deprecated: Unbinding $this of closure is deprecated in /in/3OKGT on line 76 object(Error)#2 (7) { ["message":protected]=> string(38) "Using $this when not in object context" ["string":"Error":private]=> string(0) "" ["code":protected]=> int(0) ["file":protected]=> string(9) "/in/3OKGT" ["line":protected]=> int(38) ["trace":"Error":private]=> array(1) { [0]=> array(6) { ["file"]=> string(9) "/in/3OKGT" ["line"]=> int(76) ["function"]=> string(9) "{closure}" ["class"]=> string(10) "Collection" ["type"]=> string(2) "::" ["args"]=> array(0) { } } } ["previous":"Error":private]=> NULL } array(2) { [0]=> string(3) "foo" [1]=> string(3) "bar" } object(Collection)#8 (1) { ["items":"Collection":private]=> array(11) { [0]=> int(0) [1]=> int(1) [2]=> int(2) [3]=> int(3) [4]=> int(4) [5]=> int(5) [6]=> int(6) [7]=> int(7) [8]=> int(8) [9]=> int(9) [10]=> int(10) } } object(Collection)#7 (1) { ["items":"Collection":private]=> array(11) { [0]=> int(0) [1]=> int(1) [2]=> int(2) [3]=> int(3) [4]=> int(4) [5]=> int(5) [6]=> int(6) [7]=> int(7) [8]=> int(8) [9]=> int(9) [10]=> int(10) } }
Output for 7.1.0 - 7.1.33, 7.2.0 - 7.2.33, 7.3.0 - 7.3.33, 7.4.26
it worksobject(Error)#2 (7) { ["message":protected]=> string(38) "Using $this when not in object context" ["string":"Error":private]=> string(0) "" ["code":protected]=> int(0) ["file":protected]=> string(9) "/in/3OKGT" ["line":protected]=> int(38) ["trace":"Error":private]=> array(1) { [0]=> array(6) { ["file"]=> string(9) "/in/3OKGT" ["line"]=> int(76) ["function"]=> string(9) "{closure}" ["class"]=> string(10) "Collection" ["type"]=> string(2) "::" ["args"]=> array(0) { } } } ["previous":"Error":private]=> NULL } array(2) { [0]=> string(3) "foo" [1]=> string(3) "bar" } object(Collection)#8 (1) { ["items":"Collection":private]=> array(11) { [0]=> int(0) [1]=> int(1) [2]=> int(2) [3]=> int(3) [4]=> int(4) [5]=> int(5) [6]=> int(6) [7]=> int(7) [8]=> int(8) [9]=> int(9) [10]=> int(10) } } object(Collection)#7 (1) { ["items":"Collection":private]=> array(11) { [0]=> int(0) [1]=> int(1) [2]=> int(2) [3]=> int(3) [4]=> int(4) [5]=> int(5) [6]=> int(6) [7]=> int(7) [8]=> int(8) [9]=> int(9) [10]=> int(10) } }

preferences:
117.64 ms | 405 KiB | 213 Q