#php #oop
#php #ооп
Вопрос:
Возьмите этот простой код:
class MyClass {
public $customFunction = array();
public function run($name){
call_user_func($this->customFunction[$name]);
}
}
//> Usage:
$c = new MyClass();
$c->customFunction['first'] = function (){ /* some code*/ };
$c->run('first');
Этот коз работает как исключение. Я добавляю эту функцию в $customFunction
, а затем могу вызвать ее метод form run();
.
Проблема возникает, когда в моей введенной функции я пытаюсь сделать что-то, связанное с объектом, например, если я добавлю эту функцию:
$c->customFunction['first'] = function(){ $this->callsomemethod(); }
Когда я вызываю run();
метод, PHP говорит, что я не могу использовать $this
в статическом контексте.
Есть ли у меня какой-нибудь способ внедрить эти функции и иметь возможность использовать метод object?
(Примечание: конечно, мой класс — это всего лишь пример, мне это нужно для области видимости)
Спасибо
Благодаря Марио, вот решение, которое я буду использовать:
public function run($name) {
call_user_func($this->customFunction[$name],$this);
}
На данный момент мне просто нужно добавить функцию с помощью parm, подобного этому:
= function ($this) { /*some code*/};
И $this
будет эмулировать область видимости объекта-контекста.
Ответ №1:
Ошибка возникает в коде, который вы показали как /* some code*/
в вашем примере.
Вы назначаете здесь анонимную функцию:
$c->customFunction['first'] = function (){ /* some code*/ };
И это остается просто функцией. Это не становится реальным методом класса. Таким образом, использование $this
внутри него не сработает.
Обходной путь: передайте вашим пользовательским функциям $obj
параметр и попросите их использовать его вместо $this
. (На самом деле, просто необычный обходной путь; но выполнимый.)
call_user_func($this->customFunction[$name], $obj=$this);
Или попробуйте classkit_method_add
или runkit
найти «правильное» решение. — Если доступно в вашем PHP.
Комментарии:
1. Можете ли вы передать $this в качестве ссылки?
2. @yes123: Но будьте осторожны с этим. Функции могут обращаться к общедоступным свойствам и методам только при таком подходе. Итак, вам понадобятся функции setter или getter, если вы используете слишком много частных / защищенных модификаторов.
3. @Gary: Да, это просто обычная объектная переменная / ссылка в контексте чтения.
4. Значит, если бы вы хотели изменить $ obj, это изменило бы только параметр, а не сам объект, ЕСЛИ только вы не передали по ссылке?
5. @Gary: Начиная с PHP5, все объекты являются ссылками. Таким образом, вам больше не нужно использовать
amp;
.$obj
просто станет псевдонимом для того же объекта, что и$this
.
Ответ №2:
Используйте объекты, а не анонимные функции, и следуйте этому базовому коду:
interface Command {
public function execute();
}
interface MyClassAware {
public function setMyClass(MyClass $myClass);
}
class MyClass {
private $customCommands = array();
public function addCustomCommand($name, Command $command) {
$this->customCommands[$name] = $command;
}
public function execute($name) {
$command = $this->customCommands[$name];
if ($command instanceof MyClassAware) {
$command->setMyClass($this);
}
$command->execute();
}
}
Окончательное использование:
class DoSthCommand implements Command, MyClassAware {
private $myClass;
public function setMyClass(MyClass $myClass) {
$this->myClass = $myClass;
}
public function execute() {
// do sth
$this->myClass->doSthElse();
// do sth
}
}
$myCustomCommand = new DoSthCommand();
$myClass->addCustomCommand('doSth', $myCustomCommand);
$myClass->execute('doSth');
Приведенный выше код основан на шаблоне команд и внедрении зависимостей
Комментарии:
1. Приятным дополнением к этому было бы использование
__call
вместо необходимости вызыватьexecute("name")
. 1 за ссылки, хотя: D2. Использование
__call
вместоexecute
сделало бы код намного более волшебным, его сложнее документировать и поддерживать (нет поддержки IDE и т.д.). Это был бы хороший пример чрезмерного использования волшебных методов в PHP, потому что в конце дня это не дало бы вам ничего, кроме всех недостатков, о которых я только что упомянул.3. Крозин, что
__call
может быть более волшебным, чемexecute
? Они выполняют одно и то же действие. Ни один из них не предоставит вам документацию по созданной «команде». Кроме того, как это связано с чрезмерным использованием магии в PHP? Это то, для чего__call
был разработан?
Ответ №3:
Вы никак не можете этого сделать, извините.
Однако, как обычно, у меня есть пара трюков в рукаве. 🙂
Вы можете сделать это:
class Test {
static protected $funcs_static=array();
protected $funcs_dynamic=array();
public static function register_static($function){
self::$funcs_static[]=$function;
}
public static function register_dynamic($function){
$this->$funcs_dynamic[]=$function;
}
public function __call($name, $arguments) {
return call_user_func_array($name,$arguments);
}
public static function __callStatic($name, $arguments) {
return call_user_func_array($name,$arguments);
}
}
Пример использования:
class MyClass extends Test { }
MyClass::register_static(function(){
echo 'bark';
});
$catdog = new MyClass();
$catdog->register_dynamic(function(){
echo 'beow';
});
Комментарии:
1. Я не уверен, как, но должна быть какая-то возможность установить значение
$this
перед вызовом функции в__call()
. Есть идеи, @mario?2. полностью предназначалось для того, чтобы отметить, что первый комментарий не поддерживает -_-. В любом случае, 1 за
__call
использование.3. Нет, вы не можете получить доступ к $ this. Я никогда не просматривал interna внимательно, но ядро Zend защищает это как суперспециальное имя переменной. Вы даже не можете использовать это имя для замыканий. 🙁
4. Ах, вы не можете установить это, даже когда не используете? Хм. Ну что ж : (
Ответ №4:
К сожалению, вы не можете этого сделать.
Лучшее, что вы можете сделать (по крайней мере, без ущерба для мозга), это использовать __call magic и передать $this в область вашего лямбда-выражения как $something_other_than_$ this:
<?php
class Greeter {
public $greeting = 'Hi';
public $customFns;
public function __construct($sayHiMethod){
$this->customFns['sayHi'] = $sayHiMethod;
}
public function __call($name,$args){
$args['caller']=$this;
return call_user_func_array($this->customFns[$name],$args);
}
}
$myGreeter = new Greeter(function($who,$caller){ echo "{$caller->greeting}, {$who}!n"; });
$myGreeter->sayHi('Sally');
Выводит:
Привет, Салли!
Комментарии:
1. этот ответ выглядит как дублирование Марио