PHP对象可以响应未定义的方法吗?

时间:2010-04-14 14:55:12

标签: php ruby-on-rails

Rails依赖于Ruby的一些巧妙方面。其中之一就是能够回应未定义的方法。

考虑DogOwner.所有者has_many :dogs和狗belongs_to :owner之间的关系。

如果您进入script/console,请使用fido = Dog.find(1)获取狗对象,并查看该对象,您将看不到名为Owner.的方法或属性

看到的是owner_id。如果你要求fido.owner,对象会做这样的事情(至少,这就是它在我看来):

  1. 我被要求提供.owner属性。我没有其中一个!
  2. 在我抛出NoMethodError之前,我是否有关于如何处理这个问题的规则?
  3. 是的,我这样做:我应该检查一下,看看我是否有owner_id
  4. 我做!好的,然后我将进行连接并返回该所有者对象。
  5. PHP的文档是 - 咳咳 - 有时候有点缺乏,所以我想知道这里是否有人知道答案:

      

    我可以为PHP中的对象定义类似的行为吗?

    如果没有,您知道这些灵活模型连接的解决方法吗?

2 个答案:

答案 0 :(得分:8)

您可以在PHP中实现__call()方法,这对于调用其他无法访问的方法来说是一个全部。

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

答案 1 :(得分:0)

超载到救援!

经过一些进一步的研究,看来我真正想要的是PHP's overloading methods。戈登提供的链接代码,特别是他们提供的可下载示例,非常有启发性。

(尽管我的问题标题,我真正想要的是让对象响应未定义的属性。)

因此,__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'