Autoload w PHP istnieje od wersji 5.0. Bardzo przydatna „magiczna” funkcja, która potrafi zaoszczędzić trochę zasobów serwera. Ładuje pliki z klasą jedynie w czasie gdy jest ona potrzebna. Rozwiązań jest za pewne wiele, niektórzy przeszukują za każdym razem system plików za nazwą klasy, a niektórzy tworzą specjalne konstrukcje katalogów. Są też rozwiązania ze stworzeniem tablicy z nazwami klas i ścieżką do plików.
I to rozwiązanie jest moim zdaniem najbardziej efektywne. Jedynym problemem jest tworzenie takiego pliku z tablicami. Oczywiście możemy ręcznie tworzyć tablice, to jest wersja dla najtwardszych. Innym sposobem jest oczywiście stworzenie prostego narzędzie do tworzenia takiej tablicy.
Pierwsza rzecz to musimy wyszukać wszystkie pliki jakie chcemy przeszukać.
<?php
$path = './';
$dir = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST
);
foreach($dir as $file) {
//przetwarzanie plików
}
?>
To nam pomoże przeszukać wszystkie pliki php w danym katalogu oraz jego podkatalogach. Jeśli ktoś używa na przykład skryptu Smarty, to pojawia się dużo plików php które napewno nie posiadają żadnej klasy lub też katalogi .svn posiadające dużo plików, gdzie nie znajdziemy niczego nam potrzebnego. Przydało by się to odfiltrować. Np przykład tak:
<?php
if( $file->isFile()
and end( explode( '.', $file->getFilename() ) ) == 'php'
and strpos($file->getFilename(), '%') === false
and strpos( $file->getPath().'/'.$file->getFilename(), '.svn') === false ) {
//dalsze przetwarzanie plików
}
?>
Pre-kompilowane szablony smarty posiadają znaki ‘%’ więc takie pliki możemy odrzucić. Możemy się pokusić o odrzucenie plików z katalogu gdzie zapisywane są pliki smarty, ale w przypadku zmiany nazwy katalogu, trzeba by było modyfikować skrypt. Wszystkie pliki które posiadają w ścieżce fragment ‘.svn’ także będziemy odrzucać, ponieważ na pewno tam nic nie znajdziemy.
Teraz przystąpimy do samego wyszukiwania klas w plikach. Skorzystamy z wbudowanej funkcji w token_get_all, która częściowo przetworzy nam kod PHP na poziom leksykalny, co znacząco ułatwi nam wyszukiwanie potrzebnych nam rzeczy. Zasadę może pokaże na przykładzie:
<?php
# przykładowa klasa
# <?php
# class NazwaKlasy {
# //tresc klasy
# }
var_dump( token_get_all( "<?php\n class NazwaKlasy {\n //treść klasy\n }") );
?>
Wynikiem będzie wyświetlenie tablicy 2 wymiarowej. W tym konkretnym przykładzie będzie posiadała ona 11 elementów na pierwszym poziomie. Każdy taki element opisuje pojedynczą jednostkę leksykalną. Listę takich jednostek znajdziecie w dokumentacji PHP. Jak znaleźć nazwę klasy. Każdy znaleziony element jest albo string’iem dla znaków takich jak ( ‘{‘, ‘}’, ‘;’, ‘.’, ‘>’, itp.) lub tablicą 3 elementów. Pojedyncze znaki nas nie interesują, więc szukamy tablic. A dokładniej ułożenia kolejnych 3 elementów. Pierwsze to słowo kluczowe ‘class’ (token T_CLASS), następnie odstęp (white space, token T_WHITESPACE) a na końcu ciąg znaków ( token T_STRING ). I właśnie ten ostatni element w połączeniu z nazwą i ścieżką pliku potrzebny. Więc przetworzenie pliku będzie wyglądać mniej więcej tak:
$tokens = token_get_all( file_get_contents( $file->getPath().'/'.$file->getFilename() ) ); //odczytujemy plik i przetwarzamy
$count = count($tokens); //zliczamy ilość elementów, poprostu optymalizacja dla pętli
for($a=0; $a<$count;$a++ ) {
if( is_numeric( $tokens[$a][0] ) ) { //sprawdzamy czy element jest opisany tablicą
if( $tokens[$a][0] == T_CLASS and $tokens[$a+1][0] == T_WHITESPACE and $tokens[$a+2][0] == T_STRING ) { //spełnienie warunku dla kolejnych trzech elementów
if( isset( $klasy[ trim( $tokens[$a+2][1] ) ] ) ) { //sprawdzenie czy nie istnieją duplikaty klas.
$double[] = array( trim( $tokens[$a+2][1] ), $file->getPath().'/'.$file->getFilename() );
} else {
$klasy[ trim( $tokens[$a+2][1] ) ] = $file; //tworzenie tablicy ze ścieżkami do plików oraz nazwami klas
}
}
}
}
Teraz pozostaje nam tylko otrzymaną tablice zapisać do pliku php. Co chyba nikomu nie sprawi problemu:
<?php
file_put_contents('autoload.array.php', "<?php\n");
foreach( $klasy as $class => $file ) {
//zapisujemy skrócone ścieżki od aktualnego katalogu w górę
file_put_contents('autoload.array.php', '$autoload[\''.$class.'\'] = \''.str_replace( getcwd(), '.', realpath($file) ).'\';'."\n", FILE_APPEND);
}
?>
W całości będzie to wyglądać mniej więcej tak:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| <?php
set_time_limit(0);
$klasa = array();
$path = './';
$dir = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST
);
//przetwarzanie plików
foreach($dir as $file) {
if( $file->isFile() and end( explode( '.', $file->getFilename() ) ) == 'php' and strpos($file->getFilename(), '%') === false and strpos( $file->getPath().'/'.$file->getFilename(), '.svn') === false) {
$tokens = token_get_all( file_get_contents( $file->getPath().'/'.$file->getFilename() ) );
$count = count($tokens);
for($a=0; $a<$count;$a++ ) {
if( is_numeric( $tokens[$a][0] ) ) {
if( $tokens[$a][0] == T_CLASS and $tokens[$a+1][0] == T_WHITESPACE and $tokens[$a+2][0] == T_STRING ) {
if( isset( $klasy[ trim( $tokens[$a+2][1] ) ] ) ) {
$double[] = array( trim( $tokens[$a+2][1] ), $file->getPath().'/'.$file->getFilename() );
} else {
$klasy[ trim( $tokens[$a+2][1] ) ] = $file;
}
}
}
}
}
}
//budowanie pliku z tablicą
file_put_contents('autoload.array.php', "<?php\n");
foreach( $klasy as $class => $file ) {
file_put_contents('autoload.array.php', '$autoload[\''.$class.'\'] = \''.str_replace( getcwd(), '.', realpath($file)).'\';'."\n", FILE_APPEND);
}
?> |
Na początku przydaje się dodać set_time_limit() na zero ponieważ przy dużych plikach oraz przy dużej ich ilości cała operacja może chwilkę potrwać. Należy pamiętać, że po dodaniu nowej klasy, przydałoby się uruchomić skrypt ponownie, aby dodał nową klase do tablicy.
Teraz pozostaje nam stworzyć jedynie funkcje __autoload, która będzie korzystać z naszej tablicy.
1
2
3
4
5
6
7
8
9
10
11
| $autoload = array();
function __autoload($class_name) {
static $autoload;
include_once( './autoload.array.php' );
if( isset( $autoload[ $class_name ] ) ) {
include_once($autoload[ $class_name ]);
return true;
}
return false;
} |
I mamy gotową całą funkcjonalność autoload. Możemy ją poszerzyć o zliczanie ilości plików lub klas, czas wykonywania lub inne mniej lub bardziej przydatne informacje. Wszystko zależy od waszych potrzeb.
Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...
Zwiń
Czytaj na blogu autora...