Symfony po krůčkách – ClassLoader

Pokud chceme psát v naší PHP aplikaci hezky objektově, je třeba ty objekty (třídy) nějak načítat. Bezpochyby každý z vás někdy přemýšlel, jak to udělat nejlépe. Symfony k tomu nabízí komponentu ClassLoader.
Seriál: Symfony po krůčkách (18 dílů)
- Symfony po krůčkách – Event Dispatcher 30. 11. 2015
- Symfony Console jako první rande se Symfony 7. 12. 2015
- Symfony po krůčkách – Filesystem a Finder 14. 12. 2015
- Symfony po krůčkách – Paralýza možností? OptionsResolver tě zachrání 21. 12. 2015
- Symfony po krůčkách – spouštíme procesy 4. 1. 2016
- Symfony po krůčkách – Translation – překlady jednoduše 11. 1. 2016
- Symfony po krůčkách – Validator (1) 18. 1. 2016
- Symfony po krůčkách – Validator (2) 25. 1. 2016
- Symfony po krůčkách – Routing 1. 2. 2016
- Symfony po krůčkách – MicroKernel 9. 2. 2016
- Konfigurujeme Symfony pomocí YAMLu 16. 2. 2016
- Symfony po krůčkách – oblékáme MicroKernel 23. 2. 2016
- Symfony po krůčkách – ClassLoader 29. 2. 2016
- Symfony po krůčkách – Twig 8. 3. 2016
- Symfony po krůčkách – Twig II. 15. 3. 2016
- Symfony po krůčkách – DomCrawler a CssSelector 23. 3. 2016
- Symfony po krůčkách – HTTP fundamentalista 12. 4. 2016
- Symfony po krůčkách – ušli jsme pořádný kus 19. 4. 2016
Snad každý někdy řešil, jak nejlépe načítat třídy v PHP. Pamatuji si začátky a psaní require_once
pro všechny classy do bootstrap souborů.
// boostrap.php
require_once __DIR__ . '/src/App.php';
...
Později spl_autoload_register
a další „vylepšení“ první ukázky. Toto vše bylo samozřejmě ještě předtím, než přišel Composer a stačilo napsat pouze
require_once __DIR__ . '/vendor/autoload.php';
O víc se už nemusíte starat. Tedy, pokud dodržujete alespoň nějaký standard (PSR-0 nebo PSR-4). Stačí pouze dobře nastavit composer.json
.
// composer.json
...
"autoload": {
"psr-4": {
"MF\\": "src/"
}
},
...
Composer autoload vs Symfony ClassLoader
Určitě se tedy ptáte, proč zmiňuji Symfony ClassLoader, když už jsem vlastně řekl, že celý problém vyřešil autoload od Composeru. Máte pravdu – Composer autoloader řeší celou situaci ohledně načítání tříd sám. A řekl bych, že skvěle.
Proč tedy používat Symfony ClassLoader komponentu?
Upřímně – nevím. Nenašel jsem žádný rozumný důvod pro běžné aplikace.
Napadlo mě ale několik důvodů, proč by to přece jen mohl být dobrý nápad:
- nemůžete nebo nechcete z nějakého důvodu používat Composer
- potřebujete řešit výkon autoloadování natolik, že ani Composer autoload (s optimize apod.) nestačí
Z mého pohledu tedy v 99% případů stačí composer autoload. Obvzláště pak, když autoloaderu pomůžete těmito příkazy:
$ composer dump-autoload --optimize
$ composer install --optimize-autoloader
Ukázková aplikace
Pro ukázku jsem si připravil jednoduchou aplikaci na výpis „Hello world!„. Úmyslně jsem tuto banalitu rozdělil na více tříd, aby bylo co načítat. Na kód se můžete podívat na githubu.
├── README.md
├── composer.json
├── composer.lock
├── index.php
├── index_apc.php
├── index_apc_unregister.php
├── index_composer.php
├── index_map.php
├── index_psr4.php
├── index_wincache.php
├── index_xcache.php
├── src
│ ├── App.php
│ └── Render
│ ├── MessageRender.php
│ ├── Render.php
│ └── RenderInterface.php
└── src_psr0
└── MF
├── App.php
└── Render
├── MessageRender.php
├── Render.php
└── RenderInterface.php
<?php
// index.php
require_once __DIR__ . '/vendor/symfony/class-loader/ClassLoader.php';
use MF\App;
use MF\Render\Render;
use Symfony\Component\ClassLoader\ClassLoader;
$loader = new ClassLoader();
$loader->addPrefix('MF', __DIR__ . '/src_psr0');
$loader->register();
$app = new App(new Render());
$app->renderResponse();
Třída App
pouze zabaluje základní zpracování requestu.
Třída RenderInterface
(resp. jeho implementace) se stará o samotné vypsání obsahu stránky – „Hello world!“.
Abychom mohli třídy používat, PHP script musí třídy znát. V index.php je vidět použití Symfony komponenty ClassLoader, o které tento článek pojednává. Různých implementací class loaderů od Symfony je víc a budu se jim jednotlivě věnovat dále.
Symfony class loadery
Nyní bych rád popsal jednotlivé implementace ClassLoaderů od Symfony – jejich použití, výhody a nevýhody.
Psr0 ClassLoader
Jak již z názvu vyplývá, Psr0 ClassLoader autoloaduje Psr0 strukturu – tzn: \{Vendor Name}\({Namespace}\)*{Class Name}
. Použití Psr0 ClassLoaderu jste mohli vidět již na začátku článku.
// index.php
...
$loader = new ClassLoader();
$loader->addPrefix('MF', __DIR__ . '/src_psr0');
$loader->register();
...
Psr4 ClassLoader
Psr4 ClassLoader autoloaduje podle struktury Psr4 – tzn: \{NamespaceName}(\{SubNamespaceNames})*\{ClassName}
. Použití je velmi podobné Psr0 ClassLoaderu.
// index_psr4.php
...
$loader = new Psr4ClassLoader();
$loader->addPrefix('MF', __DIR__ . '/src');
$loader->register();
...
MapClassLoader
Dovoluje vytvořit mapu tříd, podle které se bude autoloading řídit. Mapa je v podstatě klasické pole, ve kterém se definují cesty k souborům podle názvu tříd.
Hodí se hlavně na třídy třetích stran, které nepodporují Psr0/4.
Ve výchozím nastavení se MapClassLoader přidává až za ostatní autoloading. Pokud chcete použít MapClassLoader jako výchozí, předejte true
do registrační metody. Pak bude MapClassLoader předán před ostatní autoloading.
// index_map.php
...
$loader = new MapClassLoader([
'MF\\App' => __DIR__ . '/src/App.php',
'MF\\Render\\RenderInterface' => __DIR__ . '/src/Render/RenderInterface.php',
'MF\\Render\\Render' => __DIR__ . '/src/Render/Render.php',
]);
$loader->register();
...
Možná zrychlení
Použití autoloadingu v každém requestu může být zbytečně náročné. Proto je dobré skriptu ulehčit práci a výslednou mapu tříd si uložit do cache. K tomuto slouží cachovací třídy (ApcClassLoader, …). Je možné definovat i prefix pro cache, pro jednoduché dohledání / smazání.
Tyto třídy fungují jako adapter nad ClassLoadery, resp. nad čímkoliv, co implementuje metodu findFile($class)
. Pro ukázku jsem se rozhodl použít Psr4ClassLoader jako základní autoloading nástroj.
ApcClassLoader
ApcClassLoader potřebuje ke svému fungování PHP extensionu apc (Alternative PHP Cache).
// index_apc.php
...
$loader = new Psr4ClassLoader();
$loader->addPrefix('MF', __DIR__ . '/src');
$loader = new ApcClassLoader('apc.prefix', $loader);
$loader->register();
...
XCacheClassLoader
XCacheClassLoader využívá k ukládání xcache.
// index_xcache.php
...
$loader = new Psr4ClassLoader();
$loader->addPrefix('MF', __DIR__ . '/src');
$loader = new XcacheClassLoader('cache.prefix', $loader);
$loader->register();
...
WinCacheClassLoader
WinCacheClassLoader, stejně jako dvě předchozí instance, potřebuje PHP extensionu wincache. Tato implementace slouží k použití PHP na Windows.
// index_wincache.php
...
$loader = new Psr4ClassLoader();
$loader->addPrefix('MF', __DIR__ . '/src');
$loader = new WinCacheClassLoader('cache.prefix', $loader);
$loader->register();
...
Co jsme si dnes ukázali
- rozdíl mezi Composer autoloadem a Symfony ClassLoaderem
- jednoduché použití ClassLoaderů od Symfony
- případné možnosti zrychlení (cachování)
Pokud byste se chtěli podívat na ClassLoader podrobněji, můžete využít dokumentaci, kde najdete více informací.
Composer autoloding lze ještě trochu vylepšit pomocí
--classmap-authoritative
.