PHP-ben általában a define() függvénnyel szoktunk konstansokat bevezetni. Az 5.3-as verzió óta azonban lehetőség nyílt a – korábban csak osztályokon belül használható – const kulcsszó segítségével is megtenni ugyanezt. De vajon tényleg ugyanazt csinálják? És igaz a mítosz, mely szerint a const gyorsabb? Spoiler: nem és igen, de csak egy kicsit. :)
Az ötlet, hogy erről cikket írok, a következő szituációban merült fel. Egy kollégával arról beszélgettünk az irodában, hogy érdemes lenne definiálni egy WordPress sablon textdomainjét, vagy sem. Én vetettem fel az ötletet, tehát szerintem igen. Szebb a kód, nem kell több százszor belehardcodeolni ugyanazt a stringet a gettext hívásoknál, ezáltal az elírás lehetősége is csökken. Szerinte viszont a define() lassúsága miatt nem érdemes. Ekkor merült fel a kérdés, hogy vajon a const gyorsabb, és ha igen, mennyivel?
Mi a különbség?
A legfontosabb különbség, hogy míg a define() futásidőben definiálja a konstansokat, addig a const fordításkor. Mindkét megoldás rendelkezik előnyökkel és hátrányokkal egyaránt, és ezeket majdnem minden esetben ide lehet visszavezetni. Hogy mikor, melyiket érdemes használni, az a helyzettől függ. Nézzük sorban!
Flexibilitás
A define() engedékeny. Bármilyen kifejezést elfogad névként és értékként egyaránt. Csinálhatsz például ilyet:
for( $i=0; $i<5; $i++ ) {
define( 'NUMBER_' . $i, $i );
}
A const ezzel szemben – mivel nem futásidőben jön létre – kizárólag olyan értéket fogad el, amely már fordításkor tudható, tehát statikus számot, szöveget, egy másik konstans értékét, vagy egy ezekből álló tömböt.
const NUMBER = 1; // Oké
const STRING = 'one'; // Oké
const CONST = NUMBER_1; // Oké
const VARIABLE = $numberOne; // Hiba!
Szintén ebből adódik a const azon tulajdonsága is, hogy nem deklarálható elágazásban. Ilyet például nem csinálhatsz:
if( ENV == 'stage' ) {
const DB_HOST = 'stage.example.com'; // Hiba!
}
Ugyanakkor a define() függvénnyel ezt bármikor megteheted, sőt gyakran meg is tesszük. Például:
if( !defined( 'BASEPATH' ) ) {
define( 'BASEPATH', dirname( __FILE__ ) );
}
A consttal szemben a define() lehet case insensitive, tehát deklarálhatsz vele olyan konstanst, amely nem érzékeny a kis- és nagybetűkre.
define( 'NUMBER_ONE', '1', true);
echo NUMBER_ONE; // 1
echo number_one; // 1
Kijelenthető tehát, hogy a define sokkal engedékenyebb, ezáltal rugalmasabban használható. Ez a rugalmasság azonban növelheti a kód komplexitását. Mivel a const fordításkor jön létre, nem kaphat futásidőben értéket, ezáltal jóval korlátozottabb.
Hatókörök
A define() függvénnyel bevezetett konstansok mindig szuperglobálisként viselkednek, ahogyan alapesetben a const kulcsszóval definiáltak is. Utóbbiak azonban lehetnek nem-globális névtérben vagy tartozhatnak osztályhoz is.
namespace Foo\Bar;
define( 'NUMBER_ONE', 1 ); // Globálisan elérhető.
define( 'FOO\BAR\NUMBER_TWO', 2 ); // Fake névtér :)
const NUMBER_THREE = 3; // Csak a Foo\Bar névtérben érhető el.
Ahogyan fent említettem, a const kulcsszóval osztályokhoz tartozó konstansokat is definiálhatsz. Ezek a statikusokhoz hasonlóan nem az osztály egy példányához, hanem magához az osztályhoz tartoznak. Ebből adódóan elérhetőek példányosítás nélkül is.
class SimpleHttpClient {
const REQUEST_METHOD_GET = 1;
const REQUEST_METHOD_POST = 2;
const REQUEST_METHOD_PUT = 3;
define( 'REQUEST_METHOD_DELETE', '4' ); // Hiba!
// ...
}
echo SimpleHttpClient::REQUEST_METHOD_GET; // -> 1
Tehát amikor egy konstanst nem a globális névtérben kell létrehozni, akkor csak a const kulcsszó használható.
Teljesítmény
Állítólag a const sokkal gyorsabb a define()-nál. Egy gyors Google kereséssel ellentmondásos véleményeket találtam, ráadásul ezek mind évekkel ezelőtt íródtak, ezért utánajártam.
Olyan egyszerű benchmark scripteket írtam (pontosabban generáltam), amelyek tömegesen (10 000+) vezetnek be konstansokat, majd ezekkel végeznek mindenféle műveleteket.
Kiderült, hogy a konstansok létrehozásakor a const nagyjából kétszer gyorsabb. Mivel a legtöbb projekt nem csak deklarációkból áll, ennek csak akkor van jelentősége, ha több száz vagy több ezer konstansod van. Ekkor viszont ezredmásodpercekben, akár századmásodpercekben mérhető gyorsulást jelenthet. Azért lássuk be, a valóságban elég ritkán jön szembe 10 000 konstans, így – bár a const valóban gyorsabb – a különbség elenyésző.
Amikor a konstansok eléréséről van szó, nem mutatható ki teljesítménybeli különbség.
Érdekesség, hogy konstansok helyett hardcode-olt értékekkel feleannyi idő alatt futottak le ugyanazok a műveletek.
Alternatív megoldásként egyébként létezik egy PHP 5.2–5.6 kiterjesztés hidef néven, amely állítása szerint a define()-nál tízszer gyorsabban működik.
Az APC bővítményből elérhető apc_define_constants is hasonló, de az előbbinél kevésbé hatékony.
Szóval, mikor mit?
Ha szükséged van a define() rugalmasságára, mert futásidőben kell értéket adnod, használd azt! Például amikor az alap útvonalat kell definiálnod egy WordPress plugin konfigurációjakor.
Minden más esetben használd a const kulcsszót, mert
- Ez nem egy függvény, hanem egy kulcsszó. Jobban olvasható.
- Lehet globális, de nem feltétlenül. Tartozhat akár osztályhoz is.
- Egy kicsit gyorsabb is.
Hozzászólások