PHP魔法方法的实际应用——__get、__set和__call

发布于 2024-11-06 23:53:35 字数 113 浏览 0 评论 0原文

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

I've generally tried to stay away from PHP's magic methods because they seem to obfuscate an object's public interface. 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

情栀口红 2024-11-13 23:53:35

主要原因是您不需要输入太多内容。例如,您可以将它们用于 ORM 记录并充当隐式设置器/获取器:

using __call():

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

using __set():

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

映射到一个简单数组:

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

将这样的数组写入数据库比进行大量手动调用来收集所有需要的信息要容易得多。与公共成员相比,__set()__get() 还具有另一个优势:您可以验证/格式化您的数据。

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:

using __call():

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

using __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.

终止放荡 2024-11-13 23:53:35

__call()

我见过它用于实现行为,例如通过可插件接口向类添加额外的函数。

像这样的伪代码:

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

它还使得编写大部分相似的函数变得更容易,例如在 ORM 中。例如:

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

__get()/__set()

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

ORM 是我想到的最好的例子:

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

__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. e.g.:

$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:

$entity->name = 'foo'; // set column name to 'foo'
眼睛会笑 2024-11-13 23:53:35

它允许您执行以下操作:

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 中的自动完成功能。这对您来说可能重要,也可能不重要。

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.

Also, it allows you to have specific properties which are read-only (ie don't allow them to be modified via __set()). Maybe useful for an ID field, for example.

Also, you can put code into __get() and __set(), so you can do something more complex than just getting or setting a single variable. For example, if you have a storeID field, you may also want to provide a storeName property. 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. And of course storeName would not want to be implemented in __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. This may or may not matter to you.

若有似无的小暗淡 2024-11-13 23:53:35

由于当涉及到定义成员、填充它们然后检索它们等重复性任务时,魔术方法可以为您节省大量编码 - 您可以使用上述 3 种方法来缩短编码时间,而不是做那些无聊而漫长的工作所有这些。如果需要,我可以提供一些示例,它们可以在网上的各种教程中找到。

我不知道这是否是普遍共识,但通常应该适用 - 在适当的地方使用。如果您发现自己正在执行重复性任务(定义成员、填充成员、获取成员、调用略有不同的 X 函数) - 魔术方法可能会帮助您。

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. 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.

疯了 2024-11-13 23:53:35

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

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

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

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.

北恋 2024-11-13 23:53:35

只要您愿意,只要记录了魔法属性/方法即可;除非使用非常抽象的代码层(例如开发 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 {

}

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.

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)

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

}
眼藏柔 2024-11-13 23:53:35

作为使用示例,类 Request 可以读取 $_REQUEST 或 _GET、$_POST 并提供根据某些需要或为了舒适而修改或清理的字段值。

$in = new Request();

echo $in->field // returns $_REQUEST['field'] if defined, NULL if undefined

echo $in->field(10) // returns $_REQUEST['field'] if defined or 10 as a default

它可以使用 __get 为任何字段提供值或 null,从而消除繁琐的 if(isset($_REQUEST['afield'])) 检查,并且它可以使用 __call 提供相同但接受一些参数,例如默认值,如果字段未定义。

class Request {

    public function __get($key) {
        if(array_key_exists($key,$_REQUEST)) return $_REQUEST[$key];
        else return NULL;
    }

    public function __call($key, $arguments) {
        if( array_key_exists($key,$_REQUEST)) return $_REQUEST[$key];
        else if( array_key_exists(0,$arguments)) return $arguments[0];
        else return NULL;
    }

}

$form = new Request();

$formInputName = $form->name('unknown guy');

$formInputDateOfBirth = $form->birthDate;

** 代码是作为示例编写的,因此未经测试 **

As a sample of usage, a class, Request can read $_REQUEST or _GET, $_POST and provide field values modified or cleaned according to some necessities or for confort.

$in = new Request();

echo $in->field // returns $_REQUEST['field'] if defined, NULL if undefined

echo $in->field(10) // returns $_REQUEST['field'] if defined or 10 as a default

It can use __get to provide value or null for any field, eliminating the tedoius if(isset($_REQUEST['afield'])) checks, and it can use __call to provide the same but accepting some parameter such as a default value, if field is undefined.

class Request {

    public function __get($key) {
        if(array_key_exists($key,$_REQUEST)) return $_REQUEST[$key];
        else return NULL;
    }

    public function __call($key, $arguments) {
        if( array_key_exists($key,$_REQUEST)) return $_REQUEST[$key];
        else if( array_key_exists(0,$arguments)) return $arguments[0];
        else return NULL;
    }

}

$form = new Request();

$formInputName = $form->name('unknown guy');

$formInputDateOfBirth = $form->birthDate;

** CODE IS WRITTEN HERE AS A SAMPLE, SO IT IS UNTESTED **

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文