2017. február 09.

PHP konstansok

10 perc olvasási idő

PHP konstansok

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.
Samu József

Webfejlesztő. Nyitott az újdonságokra - frontenden és backenden egyaránt. Igényli a valódi kihívásokat. Tudja, hogy csak akkor maradhat képben, ha minden nap képes fejlődni.

Samu József

Hozzászólások