11

一个类似的问题讨论了__construct,但我把它留在了我的标题中,供搜索找到这个问题的人使用。

显然, __get 和 __set 带有一个参数,即正在获取或设置的变量。但是,您必须知道变量名称(例如,知道该人的年龄是 $age 而不是 $myAge)。因此,如果您必须知道变量名,尤其是在使用您不熟悉的代码(例如库)时,我不明白这一点。

我找到了一些解释__get()__set()和的页面__call(),但我仍然不明白它们为何有用或何时有用。

4

10 回答 10

6

这个页面可能会有用。(请注意,您所说的不正确-__set()将变量的名称和值都作为参数。__get()仅采用变量的名称)。

__get()并且__set()在您想要提供对变量的通用访问的库函数中很有用。例如,在 ActiveRecord 类中,您可能希望人们能够将数据库字段作为对象属性进行访问。例如,在 Kohana PHP 框架中,您可能会使用:

$user = ORM::factory('user', 1);
$email = $user->email_address;

这是通过使用__get()和来完成的__set()

使用 时可以完成类似的操作__call(),即您可以检测到有人何时调用 getProperty() 和 setProperty() 并进行相应处理。

于 2008-10-30T15:37:39.417 回答
5

__get()、__set() 和 __call() 是 PHP 所称的“魔术方法”,我认为这有点愚蠢——我认为“挂钩”更贴切一些。反正我跑题了...

这些的目的是为访问未在对象上定义的数据成员(属性或方法)时提供执行案例,可用于各种“聪明”的想法,如变量隐藏、消息转发等。

然而,这是有代价的——调用这些的调用比调用定义的数据成员慢大约 10 倍。

于 2008-10-30T15:41:40.517 回答
3

重新定义__get并且__set在核心类中特别有用。例如,如果您不希望您的配置被意外覆盖,但仍想从中获取数据:

class Example
{
    private $config = array('password' => 'pAsSwOrD');
    public function __get($name)
    {
        return $this->config[$name];
    }
}
于 2008-10-30T15:42:47.067 回答
2

魔术方法的另一个有用应用,尤其__get是模板。您只需编写使用魔术方法的简单适配器,就可以使您的代码独立于模板引擎。如果您想移动到另一个模板引擎,只需更改这些方法即可。__set__toString

class View {

    public $templateFile;
    protected $properties = array();

    public function __set($property, $value) {
        $this->properties[$property] = $value;
    }

    public function __get($property) {
        return @$this->properties[$property];
    }

    public function __toString() {
        require_once 'smarty/libs/Smarty.class.php';
        $smarty = new Smarty();
        $smarty->template_dir = 'view';
        $smarty->compile_dir = 'smarty/compile';
        $smarty->config_dir = 'smarty/config';
        $smarty->cache_dir = 'smarty/cache';
        foreach ($this->properties as $property => $value) {
            $smarty->assign($property, $value);
        }
        return $smarty->fetch($this->templateFile);
    }

}

这种方法的隐藏好处是您可以将 View 对象嵌套在另一个对象中:

$index = new View();
$index->templateFile = 'index.tpl';

$topNav = new View();
$topNav->templateFile = 'topNav.tpl';

$index->topNav = $topNav;

在 中index.tpl,嵌套看起来像这样:

<html>
<head></head>
<body>
    {$topNav}
    Welcome to Foobar Corporation.
</body>
</html>

所有嵌套的 View 对象都会即时转换为字符串(准确地说是 HTML)echo $index;

于 2008-10-30T15:58:34.693 回答
1

我认为这对设计代码不利。如果你知道并做了一个好的设计,那么你就不需要在你的代码中使用__set()and了。__get()阅读您的代码也非常重要,如果您使用的是工作室(例如 Zend 工作室),__set()那么__get()您将看不到您的类属性。

于 2009-11-04T12:44:11.723 回答
1

PHP 允许我们动态创建可能导致问题的类变量。您可以使用 __set 和 __get 方法来限制这种行为。请参阅下面的示例...

class Person { 
      public $name;
      public function printProperties(){
       print_r(get_object_vars($this));
      }
}

$person = new Person();
$person->name = 'Jay'; //This is valid
$person->printProperties();
$person->age = '26';  //This shouldn't work...but it does 
$person->printProperties();

为了防止上面你可以这样做..

public function __set($name, $value){
    $classVar = get_object_vars($this);
    if(in_array($name, $classVar)){
    $this->$name = $value;
    }
}

希望这可以帮助...

于 2013-02-16T05:22:48.173 回答
0

他们是为了做“聪明”的事情。

例如,您可以使用__set()and__get()与数据库对话。您的代码将是:$myObject->foo = "bar";这可以在幕后更新数据库记录。当然,您必须对此非常小心,否则您的表现可能会受到影响,因此“聪明”周围的引号:)

于 2008-10-30T15:38:00.413 回答
0

在处理包含易于访问的数据的 PHP 对象时,重载方法特别有用。__get()访问不存在的属性时调用, __set()在尝试编写不存在的属性时调用,__call() 在调用不存在的方法时调用。

例如,想象有一个类管理您的配置:

class Config
{
    protected $_data = array();

    public function __set($key, $val)
    {
        $this->_data[$key] = $val;
    }

    public function __get($key)
    {
        return $this->_data[$key];
    }

    ...etc

}

这使得读取和写入对象变得更加容易,并让您在读取或写入对象时更改为使用自定义功能。例子:

$config = new Config();
$config->foo = 'bar';

echo $config->foo; // returns 'bar'
于 2008-10-30T15:39:21.580 回答
0

使用它们的一个很好的理由是注册系统(我认为 Zend Framework 将其实现为注册或配置类 iirc),因此您可以执行以下操作

$conf = new Config();
$conf->parent->child->grandchild = 'foo';

这些属性中的每一个都是自动生成的 Config 对象,类似于:

function __get($key) {
    return new Config($key);
}

显然,如果 $conf->parent 已经存在,__get() 方法就不会被调用,所以使用它来生成新变量是一个不错的技巧。

请记住,我刚刚引用的这段代码不是功能性的,我只是为了举例而快速编写它。

于 2008-10-30T15:43:06.863 回答
0

可能不是世界上最干净的设计,但我有很多代码引用类中的实例变量,即:

$obj->value = 'blah';
echo $obj->value;

但后来,我想在某些情况下设置“值”时做一些特别的事情,所以我重命名了值变量,并用我需要的更改实现了 __set() 和 __get()。

其余的代码不知道有什么区别。

于 2008-10-30T15:47:03.827 回答