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(); } } }

preferences:
37.51 ms | 402 KiB | 5 Q