Settery a gettery vlastností – finální řešení

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

před 19 lety v rubrice PHP | blog píše David Grudl | nahoru

Mohlo by vás zajímat


phpFashion © 2004, 2025 David Grudl | o blogu

Ukázky zdrojových kódů smíte používat s uvedením autora a URL tohoto webu bez dalších omezení.