Может ли объект PHP отвечать на неопределенный метод? - PullRequest
0 голосов
/ 14 апреля 2010

Rails опирается на некоторые изящные аспекты Ruby. Одним из них является способность реагировать на неопределенный метод.

Рассмотрим отношения между Dog и Owner. Владельцем has_many :dogs и Собакой belongs_to :owner.

Если вы войдете в script/console, возьмите объект dog с fido = Dog.find(1) и посмотрите на этот объект, вы не увидите метод или атрибут с именем Owner.

То, что вы увидите , будет owner_id. И если вы спросите fido.owner, объект будет делать что-то вроде этого (по крайней мере, так мне кажется):

  1. Меня просят указать атрибут .owner. У меня нет одного из них!
  2. Прежде, чем я брошу NoMethodError, у меня есть правило о том, как с этим бороться?
  3. Да, я делаю: я должен проверить и посмотреть, есть ли у меня owner_id.
  4. Да! Хорошо, тогда я сделаю соединение и верну этот объект владельца.

Документация PHP - хм - иногда немного не хватает, поэтому мне интересно, знает ли кто-нибудь здесь ответ на этот вопрос:

Можно ли определить похожее поведение для объектов в PHP?

Если нет, знаете ли вы какой-нибудь обходной путь для гибких объединений моделей, подобных этим?

Ответы [ 2 ]

8 голосов
/ 14 апреля 2010

Вы можете реализовать метод __call() в PHP, который является ловушкой для вызова в противном случае недоступного метода.

class MyObj {
  public function __call($name, $args) {
    $list = $args ? '"' . implode('", "', $args) . '"' : '';
    echo "Call $name($list)\n";
  }
}

$m = new MyObj;
$m->method1(1, 2, 3);
$m->method2();

Некоторые языки (например, Javascript) также имеют так называемые функции первого класса . По сути, это позволяет добавлять или удалять методы из объектов (или классов) на лету. Синтаксис PHP (по состоянию на 5.3) вроде поддерживает это, но на самом деле его нельзя использовать.

$obj = new stdClass;
$obj->foo = function() {
  echo "hello\n";
};

print_r($obj);

Выход:

stdClass Object
(
    [foo] => Closure Object
        (
        )

)

Но попробуйте:

$obj->foo();

и вы получите:

Fatal error:  Call to undefined method stdClass::foo() in C:\xampp\htdocs\test.php on line 8

Тем не менее:

$f = $obj->foo;
$f();

правильно выводит hello.

0 голосов
/ 15 апреля 2010

Перегрузка на помощь!

После некоторых дальнейших исследований выяснилось, что я действительно хотел методы перегрузки PHP . Код по ссылке, которую дал Гордон, и особенно загружаемый пример, который они предлагают, был очень ярким.

(Несмотря на заголовок моего вопроса, я действительно хотел, чтобы объект ответил на неопределенные атрибуты .)

Итак, __get() и __set() позволяют вам указать методы, которые будут использоваться для получения и установки атрибутов объекта, и в рамках этих методов вы можете указать объекту, что делать, если такого атрибута не существует. Давайте пока посмотрим на __get().

Возвращаясь к моему Dog примеру, вы можете использовать его так:

class Dog{
    // Private attributes, not accessible except through the __get method
    private $bark_volume = 'loud';
    private $owner_id = '5';
    public function __get($name){
        // If there's a property by that name, return it
        if (isset($this->$name)){
            return $this->$name;
        }
        // If not, let's see if there's an id with a related name;
        // if you ask for $this->owner, we'll check for $this->owner_id
        $join_id = $name . "_id";
        if(isset($this->$join_id)){
            // This is pretty useless, but the id could be used
            // to do a join query instead
            return $this->$join_id;
        }
    }
}

$Fido = new Dog;
echo $Fido->bark_volume; //outputs 'loud'
echo '<br/>';
echo $Fido->owner; //outputs '5'
...