一个类似的问题讨论了__construct
,但我把它留在了我的标题中,供搜索找到这个问题的人使用。
显然, __get 和 __set 带有一个参数,即正在获取或设置的变量。但是,您必须知道变量名称(例如,知道该人的年龄是 $age 而不是 $myAge)。因此,如果您必须知道变量名,尤其是在使用您不熟悉的代码(例如库)时,我不明白这一点。
一个类似的问题讨论了__construct
,但我把它留在了我的标题中,供搜索找到这个问题的人使用。
显然, __get 和 __set 带有一个参数,即正在获取或设置的变量。但是,您必须知道变量名称(例如,知道该人的年龄是 $age 而不是 $myAge)。因此,如果您必须知道变量名,尤其是在使用您不熟悉的代码(例如库)时,我不明白这一点。
这个页面可能会有用。(请注意,您所说的不正确-__set()
将变量的名称和值都作为参数。__get()
仅采用变量的名称)。
__get()
并且__set()
在您想要提供对变量的通用访问的库函数中很有用。例如,在 ActiveRecord 类中,您可能希望人们能够将数据库字段作为对象属性进行访问。例如,在 Kohana PHP 框架中,您可能会使用:
$user = ORM::factory('user', 1);
$email = $user->email_address;
这是通过使用__get()
和来完成的__set()
。
使用 时可以完成类似的操作__call()
,即您可以检测到有人何时调用 getProperty() 和 setProperty() 并进行相应处理。
__get()、__set() 和 __call() 是 PHP 所称的“魔术方法”,我认为这有点愚蠢——我认为“挂钩”更贴切一些。反正我跑题了...
这些的目的是为访问未在对象上定义的数据成员(属性或方法)时提供执行案例,可用于各种“聪明”的想法,如变量隐藏、消息转发等。
然而,这是有代价的——调用这些的调用比调用定义的数据成员慢大约 10 倍。
重新定义__get
并且__set
在核心类中特别有用。例如,如果您不希望您的配置被意外覆盖,但仍想从中获取数据:
class Example
{
private $config = array('password' => 'pAsSwOrD');
public function __get($name)
{
return $this->config[$name];
}
}
魔术方法的另一个有用应用,尤其__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;
我认为这对设计代码不利。如果你知道并做了一个好的设计,那么你就不需要在你的代码中使用__set()
and了。__get()
阅读您的代码也非常重要,如果您使用的是工作室(例如 Zend 工作室),__set()
那么__get()
您将看不到您的类属性。
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;
}
}
希望这可以帮助...
他们是为了做“聪明”的事情。
例如,您可以使用__set()
and__get()
与数据库对话。您的代码将是:$myObject->foo = "bar";
这可以在幕后更新数据库记录。当然,您必须对此非常小心,否则您的表现可能会受到影响,因此“聪明”周围的引号:)
在处理包含易于访问的数据的 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'
使用它们的一个很好的理由是注册系统(我认为 Zend Framework 将其实现为注册或配置类 iirc),因此您可以执行以下操作
$conf = new Config();
$conf->parent->child->grandchild = 'foo';
这些属性中的每一个都是自动生成的 Config 对象,类似于:
function __get($key) {
return new Config($key);
}
显然,如果 $conf->parent 已经存在,__get() 方法就不会被调用,所以使用它来生成新变量是一个不错的技巧。
请记住,我刚刚引用的这段代码不是功能性的,我只是为了举例而快速编写它。
可能不是世界上最干净的设计,但我有很多代码引用类中的实例变量,即:
$obj->value = 'blah';
echo $obj->value;
但后来,我想在某些情况下设置“值”时做一些特别的事情,所以我重命名了值变量,并用我需要的更改实现了 __set() 和 __get()。
其余的代码不知道有什么区别。