Na navigaci | Klávesové zkratky

Property Hooks v PHP 8.4: Revoluce nebo Past?

PHP přichází po delší době s novinkami, které mají potenciál významně změnit způsob, jak píšeme objektově orientovaný kód. Property hooks a asymmetric visibility představují elegantní řešení problémů, se kterými se PHP programátoři potýkají už roky. Zatímco dosud jsme byli prakticky nuceni používat gettery a settery pro jakoukoliv práci s daty objektu, nyní máme k dispozici elegantní nástroje pro přímou kontrolu přístupu k vlastnostem.

Property hooks umožňují definovat vlastní chování při čtení a zápisu property (objektových, ne statických). Jde o mnohem čistší řešení než dosavadní používání magických metod __get/__set, a to nejen syntakticky – engine je na hooky speciálně optimalizovaný, takže jejich výkon je výrazně lepší.

Podívejme se na jednoduchý příklad. Představme si běžnou třídu Person s veřejnou property age:

class Person
{
	public int $age = 0;
}

$person = new Person;
$person->age = 25;  // OK
$person->age = -5;  // OK, ale to není správně!

PHP díky typu int zajistí, že věk bude vždy celé číslo (to lze od PHP 7.4). Ale co když chceme, aby věk nemohl být záporný? Bez hooks bychom museli vytvořit getter a setter metody, a property by musela být private. S hooks to vyřešíme elegantně:

class Person
{
	public int $age = 0 {
		set => $value >= 0 ? $value : throw new InvalidArgumentException;
	}
}

$person->age = -5;  // vyhodí InvalidArgumentException

Navenek se property chová stejně jako v prvním případě – čteme i zapisujeme přímo $person->age. Ale díky hooku máme plnou kontrolu nad tím, co se při zápisu děje.

Obdobně můžeme vytvořit hook get pro čtení. Hookům lze také přidávat atributy. A samozřejmě můžou obsahovat plnohodnotný kód, nikoliv jednoduchý výraz:

class Person
{
	public string $first;
	public string $last;
	public string $fullName {
		get {
			return "$this->first $this->last";
		}
		set(string $value) {
			[$this->first, $this->last] = explode(' ', $value, 2);
		}
	}
}

Důležité je, že jakýkoliv přístup k proměnné jde vždy přes hooky, včetně toho, když se k $this->age přistupuje v samotném těle třídy Person. Jen uvnitř samotného hooku přistupujete přímo k reálné proměnné.

Zkušenosti z minulosti: co nás naučil SmartObject?

Pro uživatele Nette může být zajímavé ohlédnout se do minulosti. Framework totiž podobnou funkcionalitu nabízel už před 17 lety ve formě SmartObject, který výrazně vylepšoval práci s objekty v době, kdy PHP v této oblasti značně zaostávalo.

Pamatuju si, že tehdy přišla vlna bezbřehého nadšení, kdy se properties používaly prakticky všude. Tu pak vystřídala vlna opačná – nepoužívat je nikde. Důvod byl jednoduchý: neexistovalo jasné vodítko, kdy je lepší použít metody a kdy property. Současné nativní řešení je ale kvalitativně jinde. Property hooks a asymetrická viditelnost nejsou jen náhrada getterů a setterů – jsou to plnohodnotné nástroje pro objektový návrh, které nám dávají stejnou úroveň kontroly jako máme u metod. Díky tomu můžeme mnohem lépe rozlišit, kdy je property skutečně tím správným řešením.

Dvojí typ vlastností: backed vs virtual

Podívejte se na tento příklad a zkuste si rychle zodpovědět otázky:

  • Je $age jen pro zápis nebo i pro čtení?
  • Je $adult jen pro čtení nebo i pro zápis?
class Person
{
	public int $age = 0 {
		set => $value >= 0 ? $value : throw new InvalidArgumentException;
	}

	public bool $adult {
		get => $this->age >= 18;
	}
}

Samozřejmě $age je, jak jsme si řekli už dříve, property pro čtení i pro zápis. Ale $adult je jen pro čtení!

A tady narážíme na první zásadní problém v designu property hooks. Ze signatury property vůbec nepoznáme, zda do ní lze zapisovat nebo ji číst!

Odpověď se totiž skrývá v kódu, v implementaci hooků. Property totiž mohou být dvojího druhu: backed (se skutečným úložištěm v paměti) a virtuální (které pouze simulují existenci property). To, zda je property backed nebo virtuální, rozhoduje, zda se v kódu hooku na ni odkazujeme.

Property je backed (má vlastní úložiště), pokud:

  • v těle hooku se vyskytuje odkaz na sebe, tj. $this->propertyName
  • nebo má zkrácený set, který znamená zápis do $this->propertyName

V našem příkladu:

  • Property $age je backed, protože používá zkrácený set (což automaticky znamená zápis do $this->age)
  • Property $adult je virtuální, protože žádný její hook neobsahuje $this->adult

Je to sice mazané řešení, ale nešťastné. Tak zásadní informaci, jako zda lze property číst nebo do ní zapisovat, má prozradit API a signatura na první pohled, ne až studium implementace.

Bezpečná práce s referencemi

Reference v PHP umožňují, aby dvě proměnné ukazovaly na stejné místo v paměti. Když změníme jednu, změní se i druhá. Je to jako když máte dva ovladače ke stejné televizi – ať použijete kterýkoliv, ovládáte tu samou televizi. Vytvoření reference se dělá pomocí znaku &.

Pokud by někdo mohl získat referenci na property, která má set hook, mohl by její hodnotu měnit přímo, a tedy obejít veškerou validaci. Podívejte se na příklad:

class Person
{
	public int $age = 0 {
		set => $value >= 0 ? $value : throw new InvalidArgumentException;
	}
}

$ref = &$person->age;    // Fatal error: Cannot take reference of property
$ref = -1;               // kdyby to šlo, validace by byla obejita

PHP proto získání reference na takovou property vůbec nedovolí a vyhodí chybu (myšleno na backed proměnnou se set hookem). Je to správné řešení – property hook má zajistit, že se do property dostane jen platná hodnota, a reference by tuto kontrolu obcházely.

Nástrahy práce s poli

Když objekt obsahuje v property pole, můžeme přímo měnit nebo přidávat prvky:

class Person
{
	public array $phones = [];
}

$person = new Person;
$person->phones[] = '777 123 456';          // přidá prvek na konec pole
$person->phones['bob'] = '777 123 456';     // přidá prvek s konkrétním klíčem

A právě tady narážíme na zajímavý problém s property hooks. Představme si, že chceme vytvořit třídu Person, která bude obsahovat seznam telefonních čísel, a chceme, aby se u nich automaticky ořezávaly mezery na začátku a konci:

class Person
{
	public array $phones = [] {
		set => array_map('trim', $value);
	}
}

$person = new Person;
$person->phones[] = '777 123 456';  // Vyhodí Error: Indirect modification of Person::$phones is not allowed

Proč to nefunguje? Operace $person->phones[] totiž v PHP funguje ve dvou krocích:

  • Nejdřív získá referenci na pole pomocí get
  • Pak do získaného pole přidá novou hodnotu

Tedy vůbec se nevolá set hook. Ale především, jak jsme si říkali, nelze získat referenci na backed proměnnou, pokud existuje set hook. Proto ta výjimka při pokusu o vložení nového čísla.

Nepomůže nám ani přidat metodu addPhone(), která zavolá $this->phones[] = $phone;, protože jak jsme si již řekli, všechny přístupy k property (i uvnitř třídy) jdou přes hooky.

Mohli bychom zkusit jiný přístup:

$phones = $person->phones;    // získáme pole
$phones[] = ' 777 123 456 ';  // přidáme číslo
$person->phones = $phones;    // uložíme zpět

To by sice fungovalo, ale představte si pole s tisíci čísly. Náš set hook by musel provést trim() na všech číslech znovu, i když se přidalo jen jedno. To není efektivní řešení.

Podle mě existuje jen jedna správná cesta a to uvědomit si, že pokud má pole nějak specificky pracovat se svými prvky (např. ořezávat mezery), musí to zajistit samotné pole, to není úkol pro objekt, který jen pole drží ve své property. Jistě, pole nemůžeme naučit nové funkcionalitě, ale můžeme ho emulovat přes objekt s rozhraním ArrayAccess:

class Phones implements ArrayAccess
{
	private array $data = [];

	public function __construct(array $data = [])
	{
		$this->data = array_map('trim', $data);
	}

	public function offsetSet(mixed $offset, mixed $value): void
	{
		$value = trim($value);
		if ($offset === null) {
			$this->data[] = $value;
		} else {
			$this->data[$offset] = $value;
		}
	}

	// implementace metod offsetGet(), offsetExists(), offsetUnset()
}

class Person
{
	function __construct(
		public Phones $phones = new Phones,
	) {}
}

$person = new Person;
$person->phones[] = ' 777 123 456 ';  // číslo uloží se ořezané

Logika ořezávání je tam, kam patří – v objektu reprezentujícím pole telefonních čísel.

Property hook v tomto případě můžeme využít například k pohodlné konverzi pole na tento objekt, tj. do $person->phones bude možné zapsat i pole:

class Person
{
	function __construct(
		public Phones $phones = new Phones {
			set(array|Phones $value) => is_array($value) ? new Phones($value) : $value;
		},
	) {}
}

$person = new Person;
$person->phones = ['  888 999 000  ', '777 888 999'];  // převede se na Phones a ořeže

Jak vidíte, hooks mohou obsahovat i promoted properties.

Existuje ještě druhá možnost řešení, a to použít virtuální property. Připomeňme, že na rozdíl od backed property, nepoužívá $this->propertyName v těle hooku. Sice i nadále nebude možné přidávat čísla pomocí $person->phones[] = ..., ale bude možné pro tento účel přidat metodu:

class Person
{
	private array $_phones = []; // reálné úložiště telefonních čísel

	public array $phones {  // virtuální property
		get => $this->_phones;
		set {
			$this->_phones = array_map('trim', $value);
		}
	}

	public function addPhone(string $phone): void
	{
		$this->_phones[] = trim($phone);
	}
}

$person = new Person;
$person->addPhone(' 777 123 456 ');  // uloží se ořezané

Toto je řešení, kdy nadále využíváme klasické pole a trimování zůstalo úkolem třídy Person.

Dědičnost

Potomci mohou přidat hooks k vlastnostem, které dosud žádné neměly, nebo předefinovat jednotlivé existující hooks:

class Person
{
	public string $email;

	public int $age {
		set => $value >= 0
			? $value
			: throw new InvalidArgumentException('Age cannot be negative');
	}
}

class Employee extends Person
{
	// Přidá hook k property, která žádný neměla
	public string $email {
		set => strtolower($value);
	}

	// Přepíše existující hook a přidá další kontrolu
	public int $age {
		set => $value <= 130
			? parent::$age::set($value)
			: throw new InvalidArgumentException('Age cannot be over 130'));
	}
}

V příkladu vidíme, jak Employee přidal hook pro email a rozšířil validaci věku o horní limit. Při přístupu k původní implementaci hooku v rodiči se používá neobvyklá syntaxe parent::$prop::set() nebo get(). Ta přesně vystihuje, co chceme – nejdřív se odkážeme na vlastnost v rodiči a pak na její hook.

Hooks můžeme dokonce označit jako final, v takovém případě jej potomci nemohou přepsat. A podobně můžeme jako final označit celou property – pak ji potomci nemohou přepsat žádným způsobem (ani přidat hooks, ani rozšířit její viditelnost).

Property v rozhraních a abstraktních třídách

Překvapivou novinkou je podpora property v rozhraních a abstraktních třídách. Představte si, že vytváříte rozhraní pro entity, které mají jméno. Doposud jsme museli definovat getter a setter metody:

interface Named
{
	public function getName(): string;
	public function setName(string $name): void;
}

Díky property hooks můžeme v rozhraní deklarovat přímo property, a to asymetricky – lze definovat samostatně požadavek na čtení a zápis:

interface Named
{
	// implementující třída musí mít veřejně čitelnou property name
	public string $name { get; }
}

class Person implements Named
{
	public string $name;     // obyčejná property
}

class Employee implements Named
{
	public string $name {
		get => $this->firstName . ' ' . $this->lastName;
	}
	private string $firstName;
	private string $lastName;
}

Všimněte si zajímavého detailu – rozhraní Named požaduje property pro čtení a implementující třída Person poskytuje klasickou property, čitelnou i zapisovatelnou. To je v pořádku, rozhraní definuje jen minimální požadavky.

Deklarace property v rozhraní vyžaduje použít klíčové slovo public, přestože jde o pleonasmus – vše v rozhraní je ze své podstaty veřejné. U metod považuji uvádění public za nesmysl, u property je to však vyžadováno kvůli konzistenci syntaxe.

Překvapivá je samotná forma deklarace, která je silně svázaná se syntaxí hooks. Zatímco ve třídě můžeme napsat jednoduché public string $name, v rozhraní musíme použít public string $name { get; set; }, aby byla property použitelná pro zápis i čtení. Nicméně to jednoznačně vítám. U rozhraní je žádoucí rozlišovat mezi property určenými pro čtení a pro zápis.

Velmi podobně to funguje i u abstraktních tříd. Ty navíc mohou deklarovat i protected properties a také mohou poskytnout výchozí implementaci některých hooků:

abstract class Person
{
	abstract public string $name { get; }
	abstract protected int $age { get; set; }

	// Poskytujeme výchozí implementaci set hooku
	abstract public string $email {
		get;
		set => Nette\Utils\Validators::isEmail($value)
			? $value
			: throw new InvalidArgumentException('Invalid email');
	}
}

Kovariantní/kontravariantní property

Zajímavou vlastností property v rozhraních a abstraktních třídách je podpora kovariance a kontravariance. Property, která má pouze hook get, může být kovariantní (v potomkovi může vracet specifičtější typ). Property pouze s hookem set může být kontravariantní (může přijímat obecnější typ):

class Animal {}
class Dog extends Animal {}

interface AnimalHolder
{
	public Animal $pet { get; }
}

class DogHolder implements AnimalHolder
{
	// Vrací specifičtější typ - to je v pořádku
	public Dog $pet { get; }
}

Jakmile má property oba hooky get i set, musí typ zůstat stejný.

Asymetrická viditelnost

Další významnou novinkou, úzce související s property hooks, je možnost definovat rozdílnou viditelnost pro čtení a zápis vlastností. Asymetrická viditelnost je výrazně jednodušší koncept než hooks a přitom řeší mnoho situací, kde jsme dříve zbytečně sahali ke getterům a setterům. Zatímco hooks jsou mocnější nástroj umožňující vložit libovolnou logiku, pro častý případ „veřejně čitelné, interně zapisovatelné“ property je asymetrická viditelnost mnohem elegantnější řešení.

Představme si třeba třídu Person. Chceme, aby datum narození mohl číst kdokoliv, ale měnit ho mohla jen samotná třída:

class Person
{
	public private(set) DateTimeImmutable $dateOfBirth;
}

První modifikátor public určuje viditelnost pro čtení a druhý private(set) pro zápis. Property je veřejně čitelná, ale zapisovat do ní lze jen z metod třídy Person. Ve skutečnosti je uvedení public pro čtení volitelné (protože veřejné čtení je výchozí stav) a můžeme psát jen:

class Person
{
	private(set) DateTimeImmutable $dateOfBirth;
}

Platí přirozené pravidlo, že viditelnost pro zápis nemůže být širší než pro čtení. Tedy protected public(set) je zakázané.

A jak je to s dědičností? V PHP platí, že potomek může viditelnost property buď zachovat, nebo rozšířit z protected na public. To samé platí i pro asymetrickou viditelnost. Potomek tedy může například rozšířit viditelnost pro zápis z protected na public:

class Person
{
	public protected(set) string $name;
}

class Employee extends Person
{
	// lze rozšířit viditelnost pro zápis
	public public(set) string $name;
}

Zajímavý je případ vlastnosti s private(set). Taková vlastnost je automaticky final, protože vlastnost s privátním zápisem ze své podstaty znamená, že ji nikdo (ani potomek) nesmí měnit. Proto ji ani nelze v potomcích předefinovat.

Jestliže hooks přinášejí nový způsob kontrolované manipulace s vlastnostmi, asymetrická viditelnost umožňuje definovat, kdo má k této manipulaci přístup. První řeší „co se má stát“, druhá „kdo to může udělat“. Jde o dvě různé věci, které se vzájemně doplňují. A lze je samozřejmě kombinovat:

class Person
{
	private(set) DateTimeImmutable $birthDate {
		set => $value > new DateTimeImmutable
			? throw new InvalidArgumentException('Birth date cannot be in future');
		   	: $value;
	}
}

Tato property je veřejně čitelná, zapisovat do ní může pouze třída sama, a při zápisu se navíc kontroluje, zda datum není v budoucnosti.

Asymetrická viditelnost a problém s poli

Vzpomeňte si na příklad s telefonními čísly z předchozí kapitoly. Tam jsme řešili problém s přidáváním čísel do pole a nakonec jsme vytvořili speciální třídu Phones. Asymetrická viditelnost nám nabízí jiné elegantní řešení:

class Person
{
	private(set) array $phones = [];

	public function addPhone(string $phone): void
	{
		$this->phones[] = trim($phone);
	}
}

$person = new Person;
var_dump($person->phones);  // OK: lze číst
$person->addPhone('...');   // OK: lze přidat číslo
$person->phones = [];       // CHYBA: nelze přepsat pole

V tomto případě nelze vůbec zapisovat do $person->phones, tato proměnná slouží jen ke čtení. Pro přidání nového čísla máme metodu.

Pro úplnost dodejme, že u vlastnosti s omezeným zápisem nelze získat referenci zvenčí:

$ref = &$person->phones;    // Fatal error: Cannot take reference

Reference jsou povolené jen ze scope, ze kterého je property zapisovatelná.

Jak je vidět, pro práci s polem v property máme několik možností, ale každá má své nevýhody:

  • Použít objekt simulující pole (přináší overhead a složitější implementaci)
  • Použít backed property s hookem (znemožňuje přímou modifikaci pole)
  • Použít virtual property s privátním úložištěm (vyžaduje metody pro modifikaci pole)
  • Použít asymetrickou viditelnost (přesouvá veškerou logiku do metod)

Neexistuje jednoznačně nejlepší řešení, vždy záleží na konkrétním případu a jeho požadavcích.

Modifikátor readonly a asymetrická viditelnost

Modifikátor readonly jsou ve skutečnosti dva modifikátory v jednom: zakazuje vícenásobný zápis a zároveň omezuje zápis na private, tedy dělá private(set). To druhé mi přišlo vždy zbytečně přísné a nepraktické. Proč by měla být read-only proměnná automaticky privátní pro zápis? V praxi často potřebujeme, aby ji mohly měnit i potomci, ale právě jen jednou.

V PHP 8.4 se to naštěstí změnilo. Nyní readonly sám o sobě dělá property protected(set), tedy zapisovatelnou i z potomků. A když potřebujeme jinou viditelnost pro zápis, snadno ji předefinujeme:

class Person
{
	// read-only proměnná zapisovatelná jen uvnitř třídy (výchozí chování před 8.4)
	public private(set) readonly string $name;

	// read-only proměnná zapisovatelná i v potomcích (výchozí chování od 8.4)
	public readonly string $dateOfBirth;

	// read-only proměnná zapisovatelná kdekoliv
	public public(set) readonly string $id;
}

Nekonzistence v terminologii

Všimli jste si ale určité nekonzistence v terminologii?

  • stále se bavíme o properties pro čtení a zápis
  • máme modifikátor readonly
  • nebo anotace @property-read a @property-write
  • ale v hooks, interfaces a asymetrické viditelnosti používáme get/set

Bylo by logičtější používat termíny read a write, ne? Chápu, že v případě hooks jde o konzistenci s magickými metodami __get/__set, ale podle mého naškodu, termíny read/write by dávaly větší smysl.

Zatímco u hooks má použití set alespoň nějakou logiku (jde o akci, která se má provést), v případě private(set) už jde o pouhou snahu o konzistenci s hooks, která mi ale nedává smysl. Asymetrická viditelnost je totiž koncepčně odlišná věc – neřeší „co se má stát“ jako hooks, ale „kdo to může udělat“. Proto by dávalo mnohem větší smysl použít termín write, tedy například private(write). Ten by byl nejen intuitivnější, ale také by se lépe pároval s existujícím modifikátorem readonly.

class Person
{
	private(write) string $name;   // takto by to dávalo větší smysl
	private(set) string $name;     // takto to bohužel je
}

Zdá se, že PHP v honbě za syntaktickou konzistencí mezi hooks a asymetrickou viditelností obětovalo sémantickou konzistenci s již existujícími koncepty v jazyce.

Nová éra v PHP

V PHP světě jsme byli dlouho odkázáni na jediný správný způsob objektově orientovaného návrhu: všechny vlastnosti private a přístup k nim výhradně přes gettery a settery. Bylo to dáno tím, že public property byly problematické:

  • neměli jsme nad nimi žádnou kontrolu, kdokoliv mohl změnit jejich hodnotu
  • byly součástí veřejného API, tedy jakákoliv změna (přidání validace, změna typu) znamenala BC break
  • nešly deklarovat v rozhraních

Proto každý, kdo chtěl programovat správně, používat rozhraní a dependency injection, musel sáhnout po getterech a setterech. Byly to jediné cesty, jak mít plnou kontrolu nad přístupem k datům objektu.

S příchodem PHP 8.4 se ale situace radikálně mění. Property hooks a asymetrická viditelnost nám dávají nad property stejnou úroveň kontroly jako máme u metod. Property můžeme bez obav použít jako součást veřejného API, protože:

  • můžeme kdykoliv přidat validaci nebo transformaci hodnot
  • můžeme kontrolovat, kdo může hodnoty měnit
  • můžeme je deklarovat v rozhraních

V podstatě můžeme vnímat property hooks jako syntaktický cukr pro gettery a settery, který ale výrazně zpřehledňuje kód. Nebo naopak gettery a settery jako zbytečný boilerplate.

Ze své vlastní zkušenosti s Nette můžu mluvit velmi konkrétně – jak jsem říkal, framework podobnou funkcionalitu nabízel už před 17 lety. To znamená, že jsem měl možnost s property přístupem pracovat dlouho. A musím říct, že to bylo nesmírně návykové. Porovnejte běžný způsob zápisu a ten s využitím property:

// tradiční přístup
$this->getUser()->getIdentity()->getName()

// s využitím property
$this->user->identity->name

Ten druhý zápis je nejen kratší, hezčí, čitelnější, ale také velmi návykový.

Jistě, můžeme namítnout, že přímý přístup k datům objektu může svádět k porušování principů objektově orientovaného návrhu. Že místo ptaní se objektu na data bychom ho měli požádat o provedení operace (Tell-Don't-Ask princip). To je legitimní argument – ale platí především pro objekty s bohatým chováním, které implementují business logiku. Naproti tomu pro datové transfer objekty, value objects nebo configuration objects je přímý přístup k datům naprosto přirozený.

Přesto existuje nové dilema. Pokud máte existující knihovnu nebo framework, který důsledně používá gettery a settery, bylo by nešťastné do něj najednou zavádět property. Rozbili byste tím konzistenci API – uživatel by musel hádat, kde použít metodu a kde property. V takovém případě je možná lepší se držet zavedeného stylu.

Předpokládám, že se postupně vytvoří nové programátorské styly a konvence. Některé projekty možná zůstanou u getterů a setterů, jiné budou hledat cesty jak začlenit property.

Kromě rozhodnutí mezi property a metodou je důležité i jejich pojmenování. Pro boolean hodnoty se často používají prefixy is nebo has. Zatímco u metod jsou tyto prefixy přirozené (isEnabled(), hasComments()), u properties působí redundantně. Proto je lepší používat přímo podstatná jména nebo adjektiva ($enabled, $commented).

Kdy použít property a kdy metody?

Ale co když začínáte nový projekt? Kdy použít property a kdy metody? To není snadná otázka, ale můžeme se inspirovat v jazycích, které s property pracují už desetiletí (C#, Kotlin):

  1. Property se výborně hodí pro:
    • value objects a datové transfer objekty
    • konfigurační objekty
    • jednoduché entity, kde jde primárně o data
    • computed hodnoty, které jsou funkcí jiných property
    • případy, kdy potřebujete jen základní validaci nebo transformaci hodnot
  2. Metody jsou lepší pro:
    • operace pracující s více property najednou
    • operace s vedlejšími efekty (logování, notifikace)
    • akce, které něco dělají (save, send, calculate…)
    • komplexní validace nebo business logiku
    • operace, které mohou selhat z více důvodů
    • situace, kde oceníme fluent interface

Všechna tato doporučení se nám snaží říct jednu základní věc: za použitím property by se neměl skrývat žádný složitý proces nebo něco, co má vedlejší efekty. Složitost operace by měla zhruba odpovídat tomu, co intuitivně očekáváme od čtení či zápisu do proměnné.

Jenže… když se podíváme na jedno z nejznámějších webových API vůbec, totiž DOM v prohlížeči, zjistíme, že za zdánlivě jednoduchou operací element.innerHTML = '...' se skrývá jeden z nejkomplexnějších procesů, jaké prohlížeč provádí – parsování HTML, vytvoření nového DOM stromu, jeho napojení do existující struktury, přepočítání layoutu, reflow, repaint… Přesto je tento přístup považován za zcela intuitivní a přirozený.

Takže možná ty hranice nejsou tak ostré, jak se může zdát. Důležitější než samotná složitost implementace je to, zda daná operace _konceptuálně_ odpovídá čtení či zápisu nějaké vlastnosti. Pokud ano, property je na místě, i kdyby za ní byla sofistikovaná logika. Pokud ne, sáhněme po metodě, i kdyby byla triviální.

před 13 hodinami v rubrice PHP | blog píše David Grudl | nahoru

Mohlo by vás zajímat

Napište komentář

Text komentáře
Kontakt

(kvůli gravataru)



*kurzíva* **tučné** "odkaz":http://example.com /--php phpkod(); \--

phpFashion © 2004, 2024 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í.