为了账号安全,请及时绑定邮箱和手机立即绑定

PHP __get和__set魔术方法

/ 猿问

PHP __get和__set魔术方法

PHP
慕尼黑的夜晚无繁华 2019-09-21 15:12:29

除非我完全误解,否则__getand __set方法应该允许→ get和的重载set。


例如,以下语句应调用该__get方法:


echo $foo->bar;

$var = $foo->bar;

并且以下应使用该__set方法:


$foo->bar = 'test';

这在我的代码中不起作用,并且可以通过以下简单示例重现:


class foo {


    public $bar;

    public function __get($name) {


        echo "Get:$name";

        return $this->$name;

    }


    public function __set($name, $value) {


        echo "Set:$name to $value";

        $this->$name = $value;

    }

}



$foo = new foo();


echo $foo->bar;

$foo->bar = 'test';


echo "[$foo->bar]";

这只会导致:


[test]

die()在那儿放一些电话表明它根本没打。


现在,我只是说了一下要解决的问题,并__get在目前需要的地方手动使用它,但这不是很动态,需要知道“重载”代码实际上没有被调用,除非专门调用。我想知道这是否不是应该以我所理解的方式起作用,或者是为什么它不起作用。


正在运行php 5.3.3。


查看完整描述

3 回答

?
青春有我

__get,__set,__call和__callStatic当该方法或属性是不可访问的被调用。您$bar是公开的,因此并非无法访问。


请参阅手册中有关属性重载的部分:


__set() 将数据写入不可访问的属性时运行。

__get() 用于从无法访问的属性读取数据。

魔术方法不能替代吸气剂和吸气剂。它们只允许您处理方法调用或属性访问,否则将导致错误。因此,还有更多与错误处理有关的内容。还要注意,它们比使用正确的getter和setter或直接方法调用要慢得多。


查看完整回答
反对 回复 2019-09-21
?
呼如林

我建议使用数组通过来存储所有值__set()。


class foo {


    protected $values = array();


    public function __get( $key )

    {

        return $this->values[ $key ];

    }


    public function __set( $key, $value )

    {

        $this->values[ $key ] = $value;

    }


}

这样,您可以确保不能以其他方式(请注意$values受保护)访问变量,以避免冲突。


查看完整回答
反对 回复 2019-09-21
?
ABOUTYOU

为了扩展Berry的答案,将访问级别设置为protected允许__get和__set与显式声明的属性一起使用(至少在类外部访问时),并且速度相当慢,我将引用另一个问题的评论关于这个主题,并为使用它提供理由:


我同意__get到自定义get函数(做相同的事情)的速度更慢,这是__get()的时间为0.0124455,而这0.0024445是10000次循环后用于自定义get()的时间。– Melsi 2012年11月23日,22 : 32 最佳做法:PHP魔术方法__set和__get


根据梅尔西(Melsi)的测试,慢得多,大约慢5倍。这绝对慢得多,但是还要注意,测试表明,您仍然可以使用此方法访问属性10,000次,计算循环迭代的时间(大约1/100秒)。与定义的实际get和set方法相比,它要慢得多,这是一种轻描淡写的方法,但是在总体方案中,即使慢5倍也从未真正变慢。


在99%的实际应用中,运算的计算时间仍然可以忽略不计,因此不值得考虑。唯一应该避免的时间是您实际上将在单个请求中访问该属性超过10,000次。如果高流量站点负担不起增加更多服务器以保持其应用程序运行的能力,那么这的确是错的。在访问率成为问题的高流量网站的页脚上放置一行文字广告,可能会为拥有该行文字的1000台服务器场支付费用。最终用户永远不会轻敲手指,不知道花这么长时间才能加载页面,因为您的应用程序的属性访问需要花费一百万分之一秒的时间。


我说这是来自.NET的开发人员,但对于消费者而言,无形的get和set方法并不是.NET的发明。它们根本就不是没有它们的属性,这些魔术方法是PHP开发人员的节省之选,甚至可以将其属性的版本全部称为“ properties”。另外,我认为,针对PHP的Visual Studio扩展确实支持具有受保护属性的智能感知,请牢记这一技巧。我认为,如果有足够的开发人员以这种方式使用神奇的__get和__set方法,PHP开发人员将调整执行时间以迎合开发人员社区。


编辑:从理论上讲,受保护的属性似乎可以在大多数情况下使用。实际上,事实证明,在访问类定义和扩展类中的属性时,很多时候您会想要使用getter和setter方法。更好的解决方案是扩展其他类时的基类和接口,因此您只需将几行代码从基类复制到实现类中即可。我要在项目的基类上做更多的工作,因此现在没有接口可以提供,但这是未经测试的精简类定义,其中包含使用反射来删除和移动属性的魔术属性并进行设置受保护的数组:


/** Base class with magic property __get() and __set() support for defined properties. */

class Component {

    /** Gets the properties of the class stored after removing the original

     * definitions to trigger magic __get() and __set() methods when accessed. */

    protected $properties = array();


    /** Provides property get support. Add a case for the property name to

     * expand (no break;) or replace (break;) the default get method. When

     * overriding, call parent::__get($name) first and return if not null,

     * then be sure to check that the property is in the overriding class

     * before doing anything, and to implement the default get routine. */

    public function __get($name) {

        $caller = array_shift(debug_backtrace());

        $max_access = ReflectionProperty::IS_PUBLIC;

        if (is_subclass_of($caller['class'], get_class($this)))

            $max_access = ReflectionProperty::IS_PROTECTED;

        if ($caller['class'] == get_class($this))

            $max_access = ReflectionProperty::IS_PRIVATE;

        if (!empty($this->properties[$name])

            && $this->properties[$name]->class == get_class()

            && $this->properties[$name]->access <= $max_access)

            switch ($name) {

                default:

                    return $this->properties[$name]->value;

            }

    }


    /** Provides property set support. Add a case for the property name to

     * expand (no break;) or replace (break;) the default set method. When

     * overriding, call parent::__set($name, $value) first, then be sure to

     * check that the property is in the overriding class before doing anything,

     * and to implement the default set routine. */

    public function __set($name, $value) {

        $caller = array_shift(debug_backtrace());

        $max_access = ReflectionProperty::IS_PUBLIC;

        if (is_subclass_of($caller['class'], get_class($this)))

            $max_access = ReflectionProperty::IS_PROTECTED;

        if ($caller['class'] == get_class($this))

            $max_access = ReflectionProperty::IS_PRIVATE;

        if (!empty($this->properties[$name])

            && $this->properties[$name]->class == get_class()

            && $this->properties[$name]->access <= $max_access)

            switch ($name) {

                default:

                    $this->properties[$name]->value = $value;

            }

    }


    /** Constructor for the Component. Call first when overriding. */

    function __construct() {

        // Removing and moving properties to $properties property for magic

        // __get() and __set() support.

        $reflected_class = new ReflectionClass($this);

        $properties = array();

        foreach ($reflected_class->getProperties() as $property) {

            if ($property->isStatic()) { continue; }

            $properties[$property->name] = (object)array(

                'name' => $property->name, 'value' => $property->value

                , 'access' => $property->getModifier(), 'class' => get_class($this));

            unset($this->{$property->name}); }

        $this->properties = $properties;

    }

}

如果代码中有任何错误,我深表歉意。


查看完整回答
反对 回复 2019-09-21

添加回答

回复

举报

0/150
提交
取消
意见反馈 邀请有奖 帮助中心 APP下载
官方微信