Я знаю, что вы можете расширить класс при его создании следующим образом:
class b extends a { }
Но возможно ли динамически расширять классы из скриптов? Такие как:
$b = new b($input) extends a;
То, что я хочу сделать, – это расширить модуль по-разному, если он используется в админ, а не в общедоступных страницах. Я знаю, что я могу создать два разных родительских класса с одинаковым именем и включать только одного пользователя или администратора. Но мой вопрос: возможно ли это сделать динамически в PHP?
Вы не можете, но это было запрошено на несколько лет: https://bugs.php.net/bug.php?id=41856&edit=1
Вы можете определить классы в пределах eval, но это больше проблем, чем объявление класса обычно.
Нет, не без расширения, такого как RunKit.
Вы можете рассмотреть альтернативный подход. Если вы хотите, чтобы B предполагал функциональность A, возможно, что-то вроде следующего может обеспечить своего рода подход «mixin». Общая картина состоит в том, что вместо B, являющегося дочерним элементом A, B делегатов A.
<?php class MixMeIn { public $favouriteNumber = 7; public function sayHi() { echo "Hello\n"; } } class BoringClass { private $mixins = array(); public function mixin($object) { $this->mixins[] = $object; } public function doNothing() { echo "Zzz\n"; } public function __call($method, $args) { foreach ($this->mixins as $mixin) { if (method_exists($mixin, $method)) { return call_user_func_array(array($mixin, $method), $args); } } throw new Exception(__CLASS__ + " has no method " + $method); } public function __get($attr) { foreach ($this->mixins as $mixin) { if (property_exists($mixin, $attr)) { return $mixin->$attr; } } throw new Exception(__CLASS__ + " has no property " + $attr); } public function __set($attr, $value) { foreach ($this->mixins as $mixin) { if (property_exists($mixin, $attr)) { return $mixin->$attr = $value; } } throw new Exception(__CLASS__ + " has no property " + $attr); } } // testing $boring = new BoringClass(); $boring->doNothing(); try { $boring->sayHi(); // not available :-( } catch (Exception $e) { echo "sayHi didn't work: ", $e->getMessage(), "\n"; } // now we mixin the fun stuff! $boring->mixin(new MixMeIn()); $boring->sayHi(); // works! :-) echo $boring->favouriteNumber;
Просто загадка. Надеюсь, я правильно понял вопрос.
Но вы не можете использовать extends
при создании объекта. extends
используется только в определении класса и определяет, какой другой класс является «родительским» для нашего нового класса.
Кроме того, если вам нравится наследование в стиле javascript и не против потери проверки типов:
<? //PHP 5.4+ final class ExpandoLookalike { //Allow callable properties to be executed public function __call($name, $arguments) { \call_user_func_array($this->$name, $arguments); } } $newBaseModule = static function(){ $base = new ExpandoLookalike(); //Common base functions get assigned here. $basePrivateVar = 42; $base->commonFunction = static function($params1, $params2) use ($basePrivateVar){ echo "common function\n"; }; $base->comment = static function() use ($basePrivateVar){ echo "Doing base comment with $basePrivateVar\n"; }; return $base; }; //Javascript-style extends $newAdminModule = static function($param) use ($newBaseModule){ $base = $newBaseModule(); $privateVar = 5; $base->adminProperty = 60; $base->suspendSite = static function() use ($param, $privateVar){ echo 'Doing admin only function '; echo "with $param, $privateVar\n"; }; return $base; }; $newPublicModule = static function() use ($newBaseModule){ $base = $newBaseModule(); $privateVar = 3; //Javascript-style overloading $oldComment = $base->comment; $base->comment = static function($data) use ($oldComment, $privateVar){ $oldComment(); echo 'Doing public function '; echo "with $data\n"; }; return $base; }; $baseModule = $newBaseModule(); $adminModule = $newAdminModule('P'); $publicModule = $newPublicModule(); $adminModule->suspendSite(); //echos 'Doing admin only function with P, 5' echo "{$adminModule->adminProperty}\n"; //echos '60' $publicModule->comment('com'); //echos 'Doing base comment with 42' //'Doing public function with com' ?>
Несмотря на закрытие двери чертами и интерфейсами, она открывает другие интересные двери для компенсации:
<? //PHP 5.4+ $inheritAllTheThings = static function(){ $base = new ExpandoLookalike(); foreach(\func_get_args() as $object){ foreach($object as $key => $value){ //Properties from later objects overwrite properties from earlier ones. $base->$key = $value; } } return $base; }; $allOfEm = $inheritAllTheThings( $newPublicModule(), $newAdminModule('Q'), ['anotherProp' => 69,] ); $allOfEm->comment('f'); //echos 'Doing base comment with 42' //Because AdminModule came after PublicModule, the function that echos 'f' //from PublicModule was overridden by the function from AdminModule. //Hence, order denotes resolutions for multiple inheritance collisions. $allOfEm->suspendSite(); //echos 'Doing admin only function with Q, 5' echo $allOfEm->anotherProp . "\n"; //echos '69' ?>
Вы можете с приведением типов. Если a продолжается b, вы можете сделать
$a=(a)(new b($input));
Что не совсем то же самое, но похоже.
Вы можете посмотреть: https://github.com/ptrofimov/jslikeobject
Автор реализовал JS-подобные объекты с поддержкой наследования.
Но, возможно, не так хорошо использовать такие объекты вместо обычных.
Да, как уже говорилось, эта функция была запрошена раньше. Но до этого вы можете создать обходное решение. Вот мой старый школьный трюк для этого
Создайте два отдельных класса:
class a { } class b { public $object; }
Затем создайте расширенную версию
class bextendeda extends a { }
В методе конструктора класса b поместите несколько функций, которые перенаправляются к расширенному объекту по запросу.
class b { public $object; public function __contruct($extend = false) { if($extend) $this -> object = new bextendeda(); else $this -> object = $this; } function __get($prop) { return $this-> object -> $prop; } function __set($prop, $val) { $this-> object -> $prop = $val; } function __call($name, $arguments) { return call_user_func_array(array($this -> object, $name), $arguments); } }
И там у вас это есть , ЕСЛИ вы хотите, чтобы расширенная версия просто сделала это
$b = new b(true);
Если не
$b = new b();
Наслаждаться 🙂