Autoload – praktyczne mapowanie

O autoloadzie klas było już głośno, rozpatrywaliśmy wszelkie za i przeciw w wielu miejscach choćby tu, tu i tu.

Mechanizm automatycznego ładowania potrzebnych nam klas jest bardzo dobrym sposobem na zrelaksowanie się poprzez brak przymusu ręcznego ładowania plików potrzebnych do działania aplikacji. Znajdźmy receptę na owe pytanie przedstawiając dwa najpopularniejsze sposoby trzymania klas w np. frameworkach.

  • Zend Framework i Rapide mają dość sztywną, lecz nie wymagającą specjalnego kombinowania technikę umieszczania plików w core. Sposób nazewnictwa jest bardzo prosty: Folder_Subfolder_Klasa – owy zapis odnosi się do ./Folder/Subfolder/Klasa.Class.php.

  • W moim frameworku Vframe zastosowałem technikę, w której pliki leżą sobie „wolność w core, nazewnictwo klas nie tyczy się ich ścieżki, np. NazwaKlasy może odnieść się do ./NazwaKlasy.Class.php lub ./Folder/Subfolder/ NazwaKlasy.Class.php.

Spróbuję opisać drugi – mój – sposób na ładowanie plików z core aplikacji. Potrzebna nam będzie klasa, która jest w stanie przeskanować całe core i zapisać je do tablicy. Aby było łatwiej, skan zostanie zaserializowany do pliku jako mapa folderu. Co jeżeli plik nie będzie istniał w core? Wówczas mapa zostanie stworzona od nowa, jeżeli to nie pomoże – wyrzucimy wyjątek.

Projektujemy naszą klasę, nazwiemy ją przykładowo Autoload, metody:

  • Load($sLibrary, $bRemapped = false) – metoda która będzie odpowiedzialna za załadowanie pliku oraz (jeżeli nie będzie istniała) stworzenie tablicy plików. W wypadku braku pliku zostanie ponownione wywołanie metody tworzenia mapy plików z przymusowym przeskanowaniem folderu klas oraz tym samym odpalenie metody samej siebie z przyjęciem drugiego jej parametru na true – brak ponownego skanowania folderu i wyrzut wyjątku.

  • Map($bUseTemp = true) – metoda odpowiedzialna za stworzenie tablicy z plikami na podstawie pliku mapy poprzez odserializowanie danych w nim zapisanych. Jeżeli parametr otrzyma wartość false lub plik mapy nie będzie możliwy do odczytu, wówczas tworzona zostanie mapa, zapisana do pliku mapy oraz do tablicy klasy.

  • FormatName($sLibrary) – metoda formatująca nazwę klasy: Klasa.Class.php

Wykorzystamy trzy stałe:

  • const _LibDir – określa ścieżkę folderu z klasami

  • const _LibSurfix – surfixy plików (.Class.php)

  • const _LibMap – ścieżka i nazwa pliku mapy

Potrzebna będzie nam jeden statyczny, prywatny parametr klasy, który jest sam w sobie jej singletonem, więc każda instancja klasy będzie z niego korzystała:

private static $_aCoreMap = array();

Ok, teraz wystarczy użyć funkcji __autoload() i wykorzystać naszą klasę:

[php]function __autoload($sLibrary)
{
try
{
Autoload::Load($sLibrary);
}
catch(AutoloadException $oException)
{
// some message to display
die(‘Autoload failed: ‘ . $oException->getMessage());
}
}[/php]

Tworzymy osobisty wyjątek dla autoloadera:

[php]class AutoloadException extends Exception
{

}[/php]

oraz samą klasę:

[php] class Autoload
{
const _LibDir = ‘Core/’;
const _LibSurfix = ‘.Class.php’;
const _LibMap = ‘Map.tmp’;

private static $_aCoreMap = array();

public static function Load($sLibrary, $bRemapped = false)
{
if(!count(self::$_aCoreMap))
self::Map();

$sLibraryFile = self::FormatName($sLibrary);

if(!isset(self::$_aCoreMap[$sLibraryFile]))
{
if($bRemapped)
// if we remapped core and library dosen’t exists – throw an exception
throw new AutoloadException(‘Library “‘ . $sLibrary . ‘” has not been found in directory map!’);
else
{
// ok, let’s go map core again without using map from file
self::Map(false);
// try load class from core directory again…
self::Load($sLibrary, true);
}
}

require_once(self::$_aCoreMap[$sLibraryFile]);
}

public static function Map($bUseTemp = true)
{
// if map is ok and we counld use it, get the map from file…
if($bUseTemp && is_readable(self::_LibMap))
self::$_aCoreMap = unserialize(file_get_contents(self::_LibMap));
// …unless we have to scan core and make map
else
{
// clear array and remamp directory
self::$_aCoreMap = array();

foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(self::_LibDir)) as $oFile)
{
$sFile = $oFile->getFilename();

if(isset(self::$_aCoreMap[$sFile]))
throw new AutoloadException(‘Core duplication “‘.$sFile.’” found!’);
else
self::$_aCoreMap[$sFile] = $oFile->getPathname();
}

// ok, save map to file
if(!file_put_contents(self::_LibMap, serialize(self::$_aCoreMap)))
throw new VframeException(‘Cannot create core map!’);
}
}

private static function FormatName($sLibrary)
{
return $sLibrary . self::_LibSurfix;
}
}
?>[/php]

Całą paczkę klasy i zastosowań możecie pobrać tutaj.



One Response to “Autoload – praktyczne mapowanie”

  1. Kr says:

    [...] co si

Leave a Reply