Automatyczne ładowanie klas w dowolnym projekcie PHP z komponentem ClassLoader Symfony2

Ten wpis został napisany dawno temu i może być już nieaktualny.

ClassLoader to komponent Symfony2, który odpowiada za automatyczne ładowanie klas zgodnie ze standardem PSR-0. Poza współpracą z kodem używającym przestrzeni nazw (ang. namespace), działa ze staromodnym już standardem PEAR (używanym też w Zendzie). Komponent sprawdza się równie dobrze poza Symfony. Uwaga: Kod tworzony w tym wpisie jest dostępny na githubie: https://github.com/jakzal/SymfonyComponentsExamples

Instalacja

Komponent możemy zainstalować z pomocą kanału PEAR Symfony lub go po prostu pobrać z githuba. Na potrzeby tego wpisu sklonujemy źródła do katalogu vendor/ naszego projektu. Uwaga: Komponent ClassLoader używa przestrzeni nazw Symfony\Component\ClassLoader. Dlatego umieścimy go w podkatalogu vendor/Symfony/Component/ClassLoader (patrz standard PSR-0).

git clone https://github.com/symfony/ClassLoader.git vendor/Symfony/Component/ClassLoader

Podstawowe użycie

Załóżmy, że mamy dwie biblioteki: Acme i Legacy_Acme. Pierwszą umieściliśmy w katalogu src/Acme/Tools. Jest w niej klasa HelloWorld, która używa przestrzeni nazw Acme\Tools _i znajduje się w pliku _src/Acme/Tools/HelloWorld.php:

<?php
// src/Acme/Tools/HelloWorld.php

namespace Acme\Tools;

class HelloWorld
{
    public function __construct()
    {
        echo __METHOD__."\n";
    }
}

Drugą bibliotekę umieściliśmy w katalogu src/Legacy/Acme/Tools. Używa konwencji PEAR, dlatego klasa Legacy_Acme_Tools_HelloWorld została zdefiniowana w pliku src/Legacy/Acme/Tools/HelloWorld.php:

<?php
// src/Legacy/Acme/Tools/HelloWorld.php

class Legacy_Acme_Tools_HelloWorld
{
    public function __construct()
    {
           echo __METHOD__."\n";
    }
}

Rejestrujemy przestrzeń nazw Acme oraz prefiks _Legacy__, aby nasze klasy były automatycznie ładowane:

<?php
// classloader.php

require_once __DIR__.'/vendor/Symfony/Component/ClassLoader/UniversalClassLoader.php';
$loader = new Symfony\Component\ClassLoader\UniversalClassLoader();
$loader->registerNamespaces(array('Acme' => __DIR__ . '/src'));
$loader->registerPrefixes(array('Legacy_' => __DIR__ . '/src'));
$loader->register();

$helloWorld = new Acme\Tools\HelloWorld();
$legacyHelloWorld = new Legacy_Acme_Tools_HelloWorld();

Oczywiście klasy zostaną załadowane tylko wtedy, kiedy będą potrzebne. Ręczne ładowanie UniversalClassLoader.php powinno być jedynym wywołaniem require w naszym kodzie. Resztę klas załaduje autoloader. Uwaga: Możemy zdefiniować ścieżki metodami registerNamespaceFallbacks() i registerPrefixFallbacks(). ClassLoader użyje ich z przestrzeniami nazw lub prefiksami nie podanymi jawnie przez registerNamespaces() lub registerPrefixes().

Poprawiamy wydajność

W prawdziwych projektach ilość klas jest raczej duża. ClassLoader może mieć negatywny wpływ na wydajność, ponieważ przed załadowaniem klasy sprawdza, czy jej plik istnieje. Powinniśmy używać ApcUniversalClassLoader, aby unikać niepotrzebnych operacji na dysku (ścieżki trzymane są wtedy w pamięci):

<?php
// classloadercached.php

require_once __DIR__.'/vendor/Symfony/Component/ClassLoader/UniversalClassLoader.php';
require_once __DIR__.'/vendor/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php';

$loader = new Symfony\Component\ClassLoader\ApcUniversalClassLoader('ClassLoader');
$loader->registerNamespaces(array('Acme' => __DIR__ . '/src'));
$loader->registerPrefixes(array('Legacy_' => __DIR__ . '/src'));
$loader->register();

$helloWorld = new Acme\Tools\HelloWorld();
$legacyHelloWorld = new Legacy_Acme_Tools_HelloWorld();

Uwaga: Przykłady uruchamiane są w konsoli, więc nie zyskamy na wydajności używając APC. Możemy nawet stracić, bo cache inicjalizowany będzie przy każdym uruchomieniu. Jest to ograniczenie APC.

Jakub Zalas

Jakub Zalas

Architekt, Programista, Trener