PHP魔术方法的实际应用--__get,__ set和__call

时间:2011-05-12 14:46:23

标签: php magic-methods

我一般都试图远离PHP的魔术方法,因为它们似乎混淆了对象的公共接口。也就是说,至少在我读过的代码中它们似乎被越来越多地使用了,所以我不得不问:对何时使用它们有什么共识?是否有使用这三种魔术方法的常见模式?

6 个答案:

答案 0 :(得分:4)

主要原因是你不需要输入那么多。例如,您可以将它们用于ORM记录并充当隐式setter / getters:

使用__call()

$user = new User();
$user->setName("Foo Bar");
$user->setAge(42);
$user->save();

使用__set()

$user->name = "Foo Bar";
$user->age = 42;

映射到一个简单的数组:

array(
    "name" => "Foo Bar",
    "age"  => 42
)

将这样的数组写入数据库要比进行大量手动调用以收集所有需要的信息容易得多。 __set()__get()优于公众成员:您可以验证/格式化数据。

答案 1 :(得分:4)

<强> __call()

我已经看到它用于实现行为,就像通过可插入接口向类添加额外函数一样。

像这样的伪代码:

$method = function($self) {};
$events->register('object.method', $method);
$entity->method(); // $method($this);

它还可以更容易地编写大多数类似的函数,例如在ORM中。 e.g:

$entity->setName('foo'); // set column name to 'foo'

<强> __get()/__set()

我经常看到它用于包装对私有变量的访问。

ORM是想到的最好的例子:

$entity->name = 'foo'; // set column name to 'foo'

答案 2 :(得分:3)

它允许你做这样的事情:

class myclass {
    private $propertybag;

    public function __get($name) {
        if(isset($this->propertybag[$name]) {return $this->propertybag[$name];}
        throw new Exception("Unknown property " . (string) $name);
    }

 }

然后,您可以在一行中填充SQL查询中的$propertybag,而不是逐个设置一大堆属性。

此外,它允许您具有只读的特定属性(即不允许通过__set()修改它们)。例如,对于ID字段可能很有用。

此外,您可以将代码放入__get()__set(),这样您就可以执行比获取或设置单个变量更复杂的操作。例如,如果您有storeID字段,则可能还需要提供storeName属性。您可以通过交叉引用查找在__get()中实现它,因此您可能不需要实际存储在类中的名称。当然storeName不希望在__get()中实施。

那里有很多可能性。

使用魔法方法当然有一些缺点。对我来说最大的问题是您在IDE中丢失了自动完成功能。这可能对您有用,也可能无关紧要。

答案 3 :(得分:0)

由于魔术方法可以为重复性任务(例如定义成员,填充它们然后检索它们)节省大量编码 - 而不是做那些无聊的长篇工作,你可以使用上面提到的3种方法来缩短时间来编码所有这些。如果需要,我可以提供一些例子,通过网络上的各种教程可以找到它们。

我不知道它是否是普遍的共识,但通常应该适用 - 在适当的地方使用。如果你发现自己要做重复的任务(定义成员,填充成员,获取成员,调用稍微不同的X函数) - 魔术方法可能对你有帮助。

答案 4 :(得分:0)

一种常见的模式是为您的客户端提供单个句柄,并根据命名约定或配置代理对封装对象或单例的调用。

class db
{
    static private $instance = null;

    static public function getInstance()
    {
        if( self::$instance == NULL )
            self::$instance = new db;

        return self::$instance;
    }

    function fetch()
    {
        echo "I'm fetching\n";
    }
}

class dataHandler
{
    function __call($name, $argv)
    {
        if( substr($name, 0, 4) == 'data' )
        {
            $fn = substr($name, 4);
            db::getInstance()->$fn($argv);
        }
    }
}

$dh = new dataHandler;
$dh->datafetch('foo', 'bar');

可以使用相同的原则来驱动相同功能的不同后端,而无需更改驱动程序。

答案 5 :(得分:0)

只要你愿意,只要记录了魔法属性/方法;除非使用非常抽象的代码层,例如开发ORM时,否则应避免使用未记录的魔法。

在抽象层可以接受

/**
 * DB backed model base class.
 */
class Model {
    protected $attributes = [];

    function __get($name) {
        return @$this->attributes[$name];
    }
}
记录时

可接受

/**
 * User model backed by DB tables.
 * @property-read string $first_name
 * @property-read string $last_name
 */
class UserModel extends Model {

}

懒惰且不可接受(使用ORM时常见)

/**
 * This class is magical and awesome and I am a lazy shithead!
 */
class UserModel extends WhoCaresWhenEverythingIsMagical {

}