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.

Odnośnie samych kanałów RSS, w wigilijną noc zacząłem kodzić, gdyż będą stanowić część mojego frameworka. Stworzyłem trzy klasy, odpowiedzalne kolejno za kanały: RSS, Atom i XML. Powstała nowa notka na forum php.pl:
http://forum.php.pl/klasy-php5-Generator-kanalow-informacyjnych-Feeds-t59071.html
Może dział nie jest trafny co do mojego celu zamieszczenia tych klas, ale po części chodzi mi o ocenę. Ostatnio dużo myślałem nad obsługą kanałów informacyjnych w moim fameworku (ang. Feeds). Sprawa wygląda bardzo kontrowersyjnie, bowiem RSS nie cieszy się już taką dobrą opinią, jednak większość użytkowników używa właśnie tej wersji kanałów informacyjnych. Według ekspertów jego miejsce ma zastąpić kanał Atom, który stał się trendem i już rok temu liczba jego użytkowników znacznie wzrosła. Pomyślałem również o udostępnianiu naszych informacji w formie czystego arkusza XML.
Powstały kolejne klasy:
Feed.Class.php – klasa abstrakcyjna, a zarazem rodzic wszystkich innych klas kanałów informacyjnych.
FeedRSS.Class.php – kanał informacyjny w formie RSS
FeedAtom.Class.php – kanał informacyjny w formie Atom
FeedXML.Class.php – czysty arkusz Tagów XML, nie przedstawiany jako kanał, ale jako dostępne źródło informacji, zaliczyłem go jako kanał, gdyż można z niego pobierać informacje poprzez różne dostępne metody takie jak SimpleXML, czy SAX.W paczce został załączony jeszcze interfejs oraz przykładowe pliki użycia klas. Dokumentacji niestety nie ma, bowiem jest to jeden z komponentów mojego frameworka, szczegółowa dokumentacja wraz z przykładami pojawi się przy wydaniu stabilnej wersji Feeds. Proszę o ocenę, ale nie na tym mi zależy. Jakbyście mogli protestować wszystkie możliwości klasy, zadawać mi jak najwięcej pytań, aby wykryć tyle błędów, ile się tylko da.
Klasy zostały napisane w niecałą godzinę, dlatego wszystko jest możliwe. W razie wystąpienia jakichkolwiek błędów, będę zamieszczał kolejne wersje z poprawkami.
Już mogę powiedzieć, że opublikowana wersja klasy dla kanału Atom nie jest finalna, bowiem zapoznałem się dziś ze szczegółową budową kanału Atom. Brakuje mi parametrów dla tagów wewnątrz klauzuli < entry > takiego jak na przykład < link > dla pliku video.
Ostatnio dużo myślałem nad tym, co jeszcze można dodać do mojego frameworka, bo jest bardzo duży, a do publikacji (03.01.2007) już niedługo. Przez moją myśl przeszło ściąganie plików… no tak. Teraz gdzie to ująć :) . Mam klasę odpowiedzialną za przechwycenie uploadowanych plików, zbieranie informacji o nich, kopiowanie. Może tam? Owszem.
Kwestia ściągnięcia plików jest bardzo prosta: do przeglądarki wysyłamy odpowiednie nagłówki oraz wczytujemy treść, która zostanie pobrana. Możemy podjąć dowolną nazwę pliku, chociaż oryginalny ma nazwę stałą. Zacząłem szukać źródeł, w których zastosowane jest pobieranie plików.
Szczególnie pomocnym skryptem okazał się FileSupply mojego kolegi Korneliusza Jarzębskiego (bardziej znany pod nickiem Bastion, autor słynnych klas takich jak Babel, Chameleon, ScoutGeo, FileMagic). Dlaczego ten skrypt? Przykładem jest funkcja ekspozycji pliku, na samej górze jest link, za pomocą którego bez problemu możemy pobrać plik. Zacząłem szperać w kodzie :)
Po zapoznaniu się ze sposobem pobierania (nagłówki umiałem wysłać już wcześniej), który opierał się na pobraniu zawartości plików „w kawałkach‿ postanowiłem zastosować to w nowej metodzie mojej klasy. Co oznacza tzw. Pobieranie „w kawałkach‿? Często serwer mówi , że odczytywana treść wielkiego pliku przekroczyła dozwolony limit bajtów, haczykiem podsuniętym mi przez Bastion’a jest to, że pobieramy go w partiach: 64 * 1024 bajtów (64 kilo), po czym przerywamy odczyt i pętla działa dalej, od momentu w którym przerwaliśmy. Zapobiegnie to wyświetleniu błędów o dużym pliku :).
Ok, czas na kod całej klasy, dodana została metoda download, która jest statyczna, można ją wykorzystywać bez uprzedniego wywoływania instancji klasy. W pierwszym parametrze podajemy ścieżkę do pliku, a w drugim (nieobowiązkowo) nazwa pliku, pod którą ma być on pobrany. Gdy drugi argument pozostanie pusty, nazwa pliku który zostanie wysłany jako nagłówek do ściągnięcia pozostanie taka sama jak oryginalny plik. Przed wywołaniem metody wysłania pliku, nie mogą zostać przesłane żadne nagłówki, po metodzie nie mogą być wykonywane żadne działania przez skrypt php.
Pełny kod obsługi plików w moim frameworku (omawiana została metoda Vfile::download() ): http://phpfi.com/187275
Pisz?c frameworka pewnie wielu z nas zastanawia?o si? jak b?d? ?adowane klasy gdy nie b?dziemy ich wymaga? funckcj? require(). Programi?ci Zend’a w swoim dziele nie dali nam tak zacnej możliwio?ci… wszystko ?adujemy r?cznie, przynajmniej tak zauwazy?em ze wst?pnych ogl?dzin kodu i przyk?ad?w zastosowania. Z pomoc? przychodzi nam funkcja __autoload(). Funkcja ta wy?apuje wszystkie pr?by dokonania instacji klas, interfejs?w poprzez deklaracj? s?owem kluczowym ???new??? b?d? z poziomu statycznego.
Przyk?adowym kodem może by?:
[php]function __autoload($sClassName)
{
echo ‘Loading class: ‘ . $sClassName . ‘… ‘;
}[/php]
W?wczas wy?apiemy wszystkie pr?by za?adowania jakiejkolwiek klasy i interfejs?w z kt?rych korzystaj? oarz klas ???rodzic?w???. Naprawd? super sprawa. Najprostszym sposobem ?adowania klas jest umieszczanie ich w danym folderze i nazywanie plik?w tak samo, jak klasy w nich zawarte, np klasa Router b?dzie w pliku Router.Class.php w folderze /Classes/ :
[php]function __autoload($sClassName)
{
require_once(‘./Classes/’ . $sClassName . ‘.Class.php’);
}[/php]
W powyższym przyk?adzie pliki klas sa ?adowane bezpo?rednio przed stworzeniem ich instancji. Ale co jak we w?asnym frameworku pliki klas nie s? pok?adane? Przyk?adowo: w folderze klas s? posegregowane w inne foldery. Jak w?wczas framework ma ???zgadn????? o jak? ?cieżk? nam chodzi. Z pomoca przychodzi nam mapowanie folderu, czyli przegl?d plik?w. Tablica jest tworzona w konstruktorze klasy g??wnej frameworka o wzorze ‘Plik.Class.php’ => ‘./Sciezka/Plik.Class.php’. Funkcja __autoload() sformatuje sobie nazw? pliku, po czym poszuka jego ?cieżki podaj?c klucz tablicy… ???szprytne??? co nie?
Teraz kilka wycink?w z mojego frameworka:
[php]function __construct($bAutoExecute = FALSE, $bAutoCheck = FALSE)
{
// …
self::setCoreMap(V_FRAMEWORK, TRUE);
// …
}
[/php]
[php] public static function getCoreMap($sElement)
{
$sElement = self::_formatLibName($sElement);
$sElementPatch = self::$_aFiles[$sElement];
if($sElementPatch && self::isUsable($sElementPatch, TRUE))
require_once($sElementPatch);
}
[/php]
[php] private static function setCoreMap($sDirectory, $bClearArray = FALSE, $iDeph = 0)
{
if(!is_bool($bClearArray))
throw new VframeException(‘Second param $bClearArray must be boolean!’);
if($bClearArray === TRUE)
{
self::$_aFiles = array();
}
if(is_dir($sDirectory))
{
$oDirHandle = @opendir($sDirectory);
if($oDirHandle)
{
while($sFile = @readdir($oDirHandle))
{
if($sFile != ‘.’ && $sFile != ‘..’)
{
$sPatch = $sDirectory . $sFile;
if(!is_dir($sPatch))
{
if(isset(self::$_aFiles[$sFile]))
throw new VframeException(‘Core duplication file “‘.$sFile.’” found!’);
else
self::$_aFiles[$sFile] = $sPatch;
}
else
{
self::setCoreMap($sPatch . DIRECTORY_SEPARATOR, FALSE, $iDeph + 1);
}
}
}
closedir($oDirHandle);
}
else
{
throw new VframeException(‘I can not open directory: ‘.$sDirectory.’!');
}
}
else
{
throw new VframeException(‘Directory ‘.$sDirectory.’ does not exists!’);
}
}
[/php]
Notk? opublikowa?em r?wnież na blogu konkursowym.