3v4l.org

run code in 300+ PHP versions simultaneously
<?php namespace { try { $parent = new Package\MyParent(); $parent->test(); } catch (\Throwable $e) { var_dump($e); } try { $pubChild = new Package\MyParent\PublicChild();//create new public child (possible) } catch (\Throwable $e) { var_dump($e); } try { $protChild = new Package\MyParent\ProtectedChild(); //create new protected child (ERROR) } catch (\Throwable $e) { var_dump($e); } class Package { /* protect constructor so that objects can't be instantiated from outside * Since all classes inherit from Package class, they can instantiate eachother * simulating protected InnerClasses */ protected function __construct() { } /* This magic method is called everytime an inaccessible method is called * (either by visibility contrains or it doesn't exist) * Here we are simulating shared protected methods across "package" classes * This method is inherited by all child classes of Package */ public function __call($method, $args) { //class name $class = get_class($this); /* we check if a method exists, if not we throw an exception * similar to the default error */ if (method_exists($this, $method)) { /* The method exists so now we want to know if the * caller is a child of our Package class. If not we throw an exception * Note: This is a kind of a dirty way of finding out who's * calling the method by using debug_backtrace and reflection */ $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); if (isset($trace[2])) { $ref = new ReflectionClass($trace[2]['class']); if ($ref->isSubclassOf(__CLASS__)) { return $this->$method($args); } } throw new RuntimeException("Call to private method $class::$method()"); } else { throw new RuntimeException("Call to undefined method $class::$method()"); } } } } namespace Package { class MyParent extends \Package { public $publicChild; protected $protectedChild; public function __construct() { //instantiate public child inside parent $this->publicChild = new \Package\MyParent\PublicChild(); //instantiate protected child inside parent $this->protectedChild = new \Package\MyParent\ProtectedChild(); } public function test(): void { echo "Call from parent -> "; $this->publicChild->protectedMethod(); $this->protectedChild->protectedMethod(); echo "<br>Siblings<br>"; $this->publicChild->callSibling($this->protectedChild); } } } namespace Package\MyParent { class PublicChild extends \Package { //Makes the constructor public, hence callable from outside public function __construct() { } protected function protectedMethod(): void { echo "I'm " . get_class($this) . " protected method<br>"; } protected function callSibling($sibling): void { echo "Call from " . get_class($this) . " -> "; $sibling->protectedMethod(); } } class ProtectedChild extends \Package { protected function protectedMethod(): void { echo "I'm " . get_class($this) . " protected method<br>"; } protected function callSibling($sibling): void { echo "Call from " . get_class($this) . " -> "; $sibling->protectedMethod(); } } }
Output for 8.0.0 - 8.0.30, 8.1.0 - 8.1.28, 8.2.0 - 8.2.18, 8.3.0 - 8.3.4, 8.3.6
Call from parent -> object(RuntimeException)#4 (7) { ["message":protected]=> string(70) "Call to private method Package\MyParent\PublicChild::protectedMethod()" ["string":"Exception":private]=> string(0) "" ["code":protected]=> int(0) ["file":protected]=> string(9) "/in/CFRbg" ["line":protected]=> int(60) ["trace":"Exception":private]=> array(2) { [0]=> array(6) { ["file"]=> string(9) "/in/CFRbg" ["line"]=> int(86) ["function"]=> string(6) "__call" ["class"]=> string(7) "Package" ["type"]=> string(2) "->" ["args"]=> array(2) { [0]=> string(15) "protectedMethod" [1]=> array(0) { } } } [1]=> array(6) { ["file"]=> string(9) "/in/CFRbg" ["line"]=> int(6) ["function"]=> string(4) "test" ["class"]=> string(16) "Package\MyParent" ["type"]=> string(2) "->" ["args"]=> array(0) { } } } ["previous":"Exception":private]=> NULL } object(Error)#7 (7) { ["message":protected]=> string(58) "Call to protected Package::__construct() from global scope" ["string":"Error":private]=> string(0) "" ["code":protected]=> int(0) ["file":protected]=> string(9) "/in/CFRbg" ["line":protected]=> int(16) ["trace":"Error":private]=> array(0) { } ["previous":"Error":private]=> NULL }
Output for 8.3.5
Warning: PHP Startup: Unable to load dynamic library 'sodium.so' (tried: /usr/lib/php/8.3.5/modules/sodium.so (libsodium.so.23: cannot open shared object file: No such file or directory), /usr/lib/php/8.3.5/modules/sodium.so.so (/usr/lib/php/8.3.5/modules/sodium.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0 Call from parent -> object(RuntimeException)#4 (7) { ["message":protected]=> string(70) "Call to private method Package\MyParent\PublicChild::protectedMethod()" ["string":"Exception":private]=> string(0) "" ["code":protected]=> int(0) ["file":protected]=> string(9) "/in/CFRbg" ["line":protected]=> int(60) ["trace":"Exception":private]=> array(2) { [0]=> array(6) { ["file"]=> string(9) "/in/CFRbg" ["line"]=> int(86) ["function"]=> string(6) "__call" ["class"]=> string(7) "Package" ["type"]=> string(2) "->" ["args"]=> array(2) { [0]=> string(15) "protectedMethod" [1]=> array(0) { } } } [1]=> array(6) { ["file"]=> string(9) "/in/CFRbg" ["line"]=> int(6) ["function"]=> string(4) "test" ["class"]=> string(16) "Package\MyParent" ["type"]=> string(2) "->" ["args"]=> array(0) { } } } ["previous":"Exception":private]=> NULL } object(Error)#7 (7) { ["message":protected]=> string(58) "Call to protected Package::__construct() from global scope" ["string":"Error":private]=> string(0) "" ["code":protected]=> int(0) ["file":protected]=> string(9) "/in/CFRbg" ["line":protected]=> int(16) ["trace":"Error":private]=> array(0) { } ["previous":"Error":private]=> NULL }
Output for 7.1.0 - 7.1.33, 7.2.0 - 7.2.33, 7.3.0 - 7.3.33, 7.4.0 - 7.4.33
Call from parent -> object(RuntimeException)#4 (7) { ["message":protected]=> string(70) "Call to private method Package\MyParent\PublicChild::protectedMethod()" ["string":"Exception":private]=> string(0) "" ["code":protected]=> int(0) ["file":protected]=> string(9) "/in/CFRbg" ["line":protected]=> int(60) ["trace":"Exception":private]=> array(2) { [0]=> array(6) { ["file"]=> string(9) "/in/CFRbg" ["line"]=> int(86) ["function"]=> string(6) "__call" ["class"]=> string(7) "Package" ["type"]=> string(2) "->" ["args"]=> array(2) { [0]=> string(15) "protectedMethod" [1]=> array(0) { } } } [1]=> array(6) { ["file"]=> string(9) "/in/CFRbg" ["line"]=> int(6) ["function"]=> string(4) "test" ["class"]=> string(16) "Package\MyParent" ["type"]=> string(2) "->" ["args"]=> array(0) { } } } ["previous":"Exception":private]=> NULL } object(Error)#7 (7) { ["message":protected]=> string(61) "Call to protected Package::__construct() from invalid context" ["string":"Error":private]=> string(0) "" ["code":protected]=> int(0) ["file":protected]=> string(9) "/in/CFRbg" ["line":protected]=> int(16) ["trace":"Error":private]=> array(0) { } ["previous":"Error":private]=> NULL }

preferences:
176.52 ms | 405 KiB | 200 Q