PHP v současnosti podporuje přetěžování vlastností pomocí magických metod __get() a __set(). Tyto metody jsou volány, když přistupovaná vlastnost není ve třídě deklarována. Toto přetěžování je vlastně trochu frustrující.
Tohle je moje řešení, jak simulovat vlastnosti s přístupovými metodami ve stylu Delphi.
PHP currently supports property overloading provided by magic methods
__get() and __set(). Those methods are called when accessed
property is not declared in the class. Actually, this overloading can be a bit
frustrating.
This is my solution how to simulate properties with accessor methods in a Delphi way.
So…
// define new "like-keyword"
// value is short and unusual string
define('property', "\0\0");
Možná _property
je lepší volba. Teď na tom nezáleží.
A inicializátor:
function _property($obj)
{
// use cache (analyze object only once)
static $cache;
$props = & $cache[ get_class($obj) ];
if ($props === null) {
// build list of properties
// 1) get_object_vars prevents ObjectIteration
// 2) outside of objects returns only public members
$props = array_flip(array_keys(get_object_vars($obj), property, true));
}
foreach ($props as $name => $foo) {
unset($obj->$name); // unset property to pass control to __set() and __get()
}
return $props;
}
Podívejte se na dokumentovaný zdrojový kód třídy:
class Rectangle
{
// list of properties
private $_props;
// shadow properties
private $_width;
private $_height;
/**
* @var int phpDoc for virtual property!
* getter: $_width setter: setWidth
*/
public $width = property;
public $height = property;
public $area = property;
function __construct()
{
// property initialization
$this->_props = _property($this);
}
/**
* Universal setter.
*
* If property 'abc' is declared,
* there must be setAbc() setter,
* otherwise property is read-only
*/
final function __set($name, $value)
{
if (!isset($this->_props[$name])) {
throw new Exception("Undefined property '$name'.");
} elseif (method_exists($this, 'set'.$name)) {
$this->{'set'.$name}($value);
} else {
throw new Exception("Property '$name' is read-only.");
}
}
/**
* Universal getter.
*
* If property 'abc' is declared,
* there must be getAbc() getter,
* otherwise variable $_abc
*/
final function &__get($name)
{
if (!isset($this->_props[$name])) {
throw new Exception("Undefined property '$name'.");
} elseif (method_exists($this, 'get'.$name)) {
return $this->{'get'.$name}();
} else {
return $this->{'_'.$name};
}
}
// getters and setters:
protected function setWidth($value)
{
$this->_width = abs($value);
}
protected function setHeight($value)
{
$this->_height = abs($value);
}
protected function getArea()
{
return $this->_height * $this->_width;
}
}
// usage:
$rect = new Rectangle();
$rect->width = 10;
$rect->height = 5;
echo $rect->area;
// prints 50
// try receive private member
echo $rect->props;
// throw exception
Výhody:
- univerzální __set & __get, není potřeba je přepisovat
- magické metody vědí, zda konkrétní vlastnost existuje
- tento mechanismus nelze zneužít k získání soukromých členů
- vlastnosti rozlišují velká a malá písmena, jako ostatní proměnné
- tyto __set & __get pracují velmi rychle (bez přepínačů nebo ifů)
- virtuální vlastnosti mohou být dokumentovány pomocí kteréhokoli z existujících nástrojů (phpDoc, doxygen)
- automatické doplňování IDE založené na phpDoc bude fungovat
- snadno přizpůsobitelné pro budoucí PHP6 (doufám)
Nevýhody:
- přetěžování stále není bezpečné (viz bug #36484, opraveno v PHP 5.2.1)
Od PHP 5.1.0 je také možné přetížit isset() a unset():
final private function __isset($name)
{
return isset($this->_props[$name]);
}
final private function __unset($name)
{
unset($this->_props[$name]);
}
Několik odkazů: