简体   繁体   English

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

[英]Practical applications of PHP magic methods - __get, __set, and __call

I've generally tried to stay away from PHP's magic methods because they seem to obfuscate an object's public interface. 我一般都试图远离PHP的魔术方法,因为它们似乎混淆了对象的公共接口。 That said, they seem to be used more and more, at least, in the code I've read, so I have to ask: is there any consensus on when to use them? 也就是说,至少在我读过的代码中它们似乎被越来越多地使用了,所以我不得不问:对何时使用它们有什么共识? Are there any common patterns for using these three magic methods? 使用这三种魔术方法有什么共同的模式吗?

The main reason is that you do not need to type as much. 主要原因是你不需要输入那么多。 You could use them for, say, an ORM record and act as implicit setters/getters: 例如,您可以将它们用于ORM记录并充当隐式setter / getter:

using __call() : 使用__call()

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

using __set() : 使用__set()

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

which maps to a simple array: 映射到一个简单的数组:

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

It is much easier to write such an array to the database than doing a lot of manual calls to collect all needed information. 将这样的数组写入数据库要比进行大量手动调用以收集所有需要的信息容易得多。 __set() and __get() have another advantage over public members: You are able to validate/format your data. __set()__get()相对于公共成员具有另一个优势:您可以验证/格式化数据。

__call()

I've seen it used to implement behaviors, as in add extra functions to a class through a pluginable interface. 我已经看到它用于实现行为,就像通过可插入接口向类添加额外的函数一样。

Pseudo-code like so: 像这样的伪代码:

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

It also makes it easier to write mostly similar functions, such as in ORMs. 它还可以更容易地编写大多数类似的函数,例如在ORM中。 eg: 例如:

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

__get()/__set()

I've mostly seen it used to wrap access to private variables. 我经常看到它用于包装对私有变量的访问。

ORMs are the best example that comes to mind: ORM是想到的最好的例子:

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

It allows you to do things like this: 它允许你做这样的事情:

class myclass {
    private $propertybag;

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

 }

Then you can populate $propertybag from a SQL query in a single line, rather than setting a whole bunch of properties one by one. 然后,您可以在一行中从SQL查询中填充$propertybag ,而不是逐个设置一大堆属性。

Also, it allows you to have specific properties which are read-only (ie don't allow them to be modified via __set() ). 此外,它允许您具有只读的特定属性(即不允许通过__set()修改它们)。 Maybe useful for an ID field, for example. 例如,可能对ID字段有用。

Also, you can put code into __get() and __set() , so you can do something more complex than just getting or setting a single variable. 此外,您可以将代码放入__get()__set() ,这样您就可以做一些比获取或设置单个变量更复杂的事情。 For example, if you have a storeID field, you may also want to provide a storeName property. 例如,如果您有storeID字段,则可能还需要提供storeName属性。 You could implement that in __get() via a cross-reference lookup, so you may not need the name actually to be stored in the class. 您可以通过交叉引用查找在__get()实现它,因此您可能不需要实际存储在类中的名称。 And of course storeName would not want to be implemented in __get() . 当然, storeName不希望在__get()

Lots of possibilities there. 那里有很多可能性。

There are of course some down-sides of using magic methods. 当然有一些使用魔术方法的缺点。 The biggest one for me is the fact that you lose the auto-complete functionality in your IDE. 对我来说最大的问题是您在IDE中丢失了自动完成功能。 This may or may not matter to you. 这对您来说可能或不重要。

Whenever you'd like, as long as the magic properties/methods are documented; 无论何时你想,只要记录了魔法属性/方法; undocumented magic should be avoided unless working with a very abstract layer of code, such as when developing an ORM. 除非使用非常抽象的代码层(例如开发ORM时),否则应避免使用未记录的魔法。

acceptable at an abstract layer 在抽象层可以接受

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

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

acceptable when documented 记录时可接受

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

}

lazy and unacceptable (and common when using ORMs) 懒惰和不可接受(在使用ORM时很常见)

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

}

Since magic methods can save you a LOT of coding when it comes to repetitive tasks like defining members, populating them and then retrieving them - instead of doing that boring, long piece of work, you can use mentioned 3 methods to shorten the time to code all that. 因为魔术方法可以为重复性任务(例如定义成员,填充它们然后检索它们)节省大量编码 - 而不是做那些无聊的长篇工作,你可以使用上面提到的3种方法缩短代码时间这一切。 If needed, I can provide a few examples tho they can be found in various tutorials over the net. 如果需要,我可以提供一些示例,通过网络上的各种教程可以找到它们。

I don't know if it's general consensus, but the usual should apply - use where appropriate. 我不知道它是否是普遍的共识,但通常应该适用 - 在适当的地方使用。 If you find yourself to do repetitive task (define member, populate member, get member, call X functions that differ slightly) - magic methods might help you. 如果你发现自己要做重复的任务(定义成员,填充成员,获取成员,调用稍微不同的X函数) - 魔术方法可能会帮助你。

One common pattern is to have a single handle for your clients and proxy the calls to encapsulated objects or singletons based on naming conventions or configurations. 一种常见的模式是为您的客户端提供单个句柄,并根据命名约定或配置代理对封装对象或单例的调用。

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');

Same principles can be used to drive different backends of the same functionality without having to change the driver. 可以使用相同的原理来驱动相同功能的不同后端,而无需更改驱动程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM