внедрение кодов (методов) в класс?

#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 за ссылки, хотя: D

2. Использование __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. этот ответ выглядит как дублирование Марио