Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Włączanie plików + autoloader
Forum PHP.pl > Forum > PHP > Pro
Stron: 1, 2, 3, 4
chmolu
Rozwiązanie z generowaniem mapy przez skanowanie katalogów wydaje się najlepsze. Jednak sposób Vengeance'a nie jest zbyt efektywny. Wymaga wczytania zawartości każdego pliku z osobna i sprawdzania, czy zawiera on jakieś klasy. Może jest to bardziej elastyczne, gdy mamy po kilka klas w jednym pliku, ale ja to zrobiłem inaczej.

U mnie każda klasa jest w osobnym pliku. Nazwa pliku zawierającego definicję klasy lub interfejsu kończy się na .class.php lub interface.php. I tylko takie pliki są ładowany do mapy.

Wycinki kodu z mojego frameworka:
  1. <?
  2. /**
  3.    * Checks whether the specified path exists.
  4.    * If the directory is not found, it'll try to create one.
  5.    *
  6.    * @param string $dirname 
  7.    * @return void
  8.    */
  9. function ensure_directory_exists($dirname)
  10. {
  11. $dirname = substr($dirname, strlen(APP_DIR));
  12.  
  13. $sub_dirs = explode('/', $dirname);
  14. $path = '';
  15. foreach ($sub_dirs as $dir)
  16. {
  17. $path .= $dir . '/';
  18. if (!file_exists($path))
  19. {
  20. if (!@mkdir($path, 0777))
  21. {
  22. throw new DaeronException('Could not create a directory \"' . $path . '\"');
  23. }  
  24. }
  25. }
  26. }
  27.  
  28. /**
  29.    * Writes the supplied content to a file.
  30.    *
  31.    * @param string $filename Name of a file to write to.
  32.    * @param mixed $content Data to write to a file.
  33.    * @return void
  34.    */
  35. function file_write($filename, $content)
  36. {
  37. ensure_directory_exists(dirname($filename));
  38. $fp = @fopen($filename, 'w');
  39. if($fp)
  40. {
  41. @fputs($fp, $content, strlen($content));
  42. }
  43. @fclose($fp);
  44. }
  45.  
  46. class Autoloader
  47. {
  48. /**
  49.  * Map of class names and their localization. 
  50.  *
  51.  * @var array
  52.  * @static 
  53.  */
  54. private static $map = null;
  55.  
  56. /**
  57.  * Loads specified class.
  58.  *
  59.  * @param string $class_name
  60.  * @return void 
  61.  */
  62. public static function load($class_name)
  63. {
  64. $cached_map = APP_DIR . '/var/cache/autoload.php';
  65. if (!is_readable($cached_map))
  66. {
  67. self::generateMap();
  68. }
  69.  
  70. if (!isset(self::$map)) require_once $cached_map;
  71.  
  72. $class_name = strtolower($class_name);
  73. if (!isset(self::$map[$class_name]))
  74. {
  75. throw new Exception('Unknown class ' . $class_name);
  76. }
  77.  
  78. require_once self::$map[$class_name];
  79. }
  80.  
  81. /**
  82.  * 
  83.  * 
  84.  *  
  85.  */
  86. private static function generateMap()
  87. {
  88. self::$map = array();
  89.  
  90. $cached_map = APP_DIR . '/var/cache/autoload.php';
  91.  
  92. self::scan(DAERON_DIR);
  93. self::scan(APP_DIR);
  94.  
  95. $compiled = &#092;"<?phpn\";
  96. $compiled .= 'self::$map = ' . var_export(self::$map, true) . &#092;"n?>\";
  97.  
  98. file_write($cached_map, $compiled);  
  99. }
  100.  
  101. /**
  102.  * 
  103.  * 
  104.  *  
  105.  */
  106. private static function scan($dir)
  107. {
  108. $match = array();
  109.  
  110. $rd = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), true);
  111. foreach ($rd as $file)
  112. {
  113. if(!is_file($file->getPathname())) continue;
  114.  
  115. if (preg_match('/([a-z0-9_]+).(class|interface).php$/i', $file->getPathName(), $match))
  116. {
  117. self::$map[strtolower($match[1])] = $file->getPathName();
  118. }
  119. }  
  120. }
  121. }
  122.  
  123. function __autoload($class_name)
  124. {
  125. Autoloader::load($class_name);
  126. }
  127. ?>


Porównanie szybkości (dla stosunkowo małej liczby plików):
- czytanie mapy z generowaniem: 35,67 ms
- czytanie z cache'owanej mapy: 00,57 ms

Kod Vengeance'a:
- czytanie mapy z generowaniem: 129.35 ms

A czytanie z cache wygląda identycznie smile.gif
Vengeance
chmolu: Zauważ, że to Ty trzymasz każdą klasę w innym pliku!

A jak byś chciał dodać do frameworka np. AdoDB, Smarty czy inne rzeczy? One nie stosują twojego nazewnictwa.

Po to właśnie ten mój* skrypt przeszukuje wszystkie pliki wczytując je!

Skoro twoje nazewnictwo jest tak ustandaryzowane to w jakim celu w ogóle tworzyć mape ? przecież wystarczy zrobić

require $className . '.class.php'; hehe

* przypominam, iż nie czuje się właścicielem skryptu smile.gif posklejałem kod z wielu wypowiedzi innych osób i wrzuciłem smile.gif
chmolu
@Vengeance: to prawda, nie każdy musi trzymać każdą klasę w innym pliku. Dla mnie takie rozwiązanie jest najwygodniejsze.

Cytat
A jak byś chciał dodać do frameworka np. AdoDB, Smarty czy inne rzeczy? One nie stosują twojego nazewnictwa.

Autoloader u mnie ma służyć tylko do ładowania klas należących do jądra i aplikacji. Biblioteki typu Smarty i AdoDB zaliczam do "zewnętrznych" części aplikacji, którymi zająć musi się odpowiednia akcja/widok. Poza tym zauważ, że w przypadku tych bibliotek musimy włączyć tylko jeden główny plik, który już sam zajmuje się potrzebnymi mu plikami.
(btw: główny plik Smarty ma nazwę Smarty.class.php, więc pasuje do mojego nazewnictwa tongue.gif)

Cytat
Skoro twoje nazewnictwo jest tak ustandaryzowane to w jakim celu w ogóle tworzyć mape ? przecież wystarczy zrobić

require $className . '.class.php'; hehe

Ale dzięki mapie struktura katalogów jest dowolna. Mogę sobie w dowolny sposób podzielić na różne katalogi i podkatalogi, a gdy zajdzie potrzeba zmiany struktury wystarczy tylko wygenerowac mapę i nie trzeba grzebać w kodzie i poprawiać wszystkich ścieżek.
bela
@chmolu a jak zmienisz ścieżke do smarty ? biggrin.gif
chmolu
Nie używam Smarty więc nie ma żadnego problemu biggrin.gif

// btw: poprawiłem troszkę ten kod
Vengeance
@chmolu: ty nie. Ale z tego co wynika z twoich wypowiedzi to co piszesz
to framework. Pewnie kiedys ktos inny z niego bedzie korzystac i moze chciec takze korzystac ze smarty smile.gif
chmolu
Zapewniam cię, że nie będzie to żaden problem aarambo.gif
Ale mam nadzieję, że zamiast Smarty ludzie będą używali systemu wbudowanego w mój framework (wzorowany na WACT/ASP/JSP).
Jak skończę, to zarzucę tu kod do oceny.
Ociu
Pozwole sobie odkopać temat winksmiley.jpg

Zastanawiam się czy nie użyć pliku xml ładowania klas. Coś na wzór:

  1. <Classes>
  2.  
  3. <dirname>
  4. <class name='jakas tam nazwa'>file.php</class>
  5. <class name='jakas tam nazwa2'>file2.php</class>
  6. </dirname>
  7.  
  8. <dirname2>
  9. <class name='jakas tam nazwa3'>file3.php</class>
  10. <class name='jakas tam nazwa4'>file3.php</class>
  11. </dirname2>
  12.  
  13. </Classes>


Potem bardzo prosto: simplexml, loader instacji (w moim przypadku) i tyle smile.gif

Iść dalej tym tropem, czy zacząć myśleć nad czymś innym ?
davidD
Tylko właściwie... po co XML do zwykłego przypisania nazwy pliku do nazwy klasy? IMO jest to lekkie komplikowanie sobie całej sprawy - no chyba, że chcemy przechowywać jakieś dodatkowe informacje.

Mnie osobiście podoba się pomysł z iteratorem, szukającym klas po wszystkich plikach o określonym wzorcu, w podanej ścieżce (ścieżkach). Można z tego zrobić ładną tablicę, zserializować ją i do pliku... Co prawda trzeba się troszkę pobawić samym pisaniem tego generatora mapy, ale za to później, przy zmianie położenia/nazw plików zaoszczędzi się duużo czasu.
matid
Ja to widzę tak:
1. Crowler, szuka po określonych folderach w poszukiwaniu plików php zawierających klasy. Będzie miał możliwość wyszukiwania z filtrowaniem nazw plików, nazwach klas itp. Być może po dodaniu opcji filtracji czas generowania mapy się zwiększy nieco, ale na jakoże jest to czynność wykonywana rzadko, można sobie na to pozwolić.
2. Dodatkowy plik XML z klasami zewnętrznymi. W tym pliku będą zapisane w postaci XML dodatkowe klasy, które autoloader ma obsługiwać. Plik byłby parsowany i razem z wynikami crowlera zapisywany w pliku php w postaci tablicy.
Ociu
Cytat(matid @ 2005-05-05 07:37:45)
2. Dodatkowy plik XML z klasami zewnętrznymi. W tym pliku będą zapisane w postaci XML dodatkowe klasy, które autoloader ma obsługiwać.

O to mi chodziło winksmiley.jpg

Śpieszyłem się i zapomaniałem dopisać.
chmolu
IMO pliki XML w tym wypadku są niepotrzebne. Szybciej i wygodniej będzie to zrobić przy pomocy plików ini
Nievinny
XML to przeładowanie, a nam zależy na czasie, nie? Chociaż wszystko da się zrobić ;P
matid
Cytat(Nievinny @ 2005-05-05 18:06:02)
XML to przeładowanie, a nam zależy na czasie, nie? Chociaż wszystko da się zrobić ;P

Przecież parsowanie pliku XML będzie się odbywać tylko w wypadku rekonstruowania mapy, co za często się dziać nie powinno, czyż nie? Zyskujemy natomiast na przejrzystości i strukturze pliku konfiguracyjnego.
bigZbig
@matid -> zauwazylem u Ciebie duze przywiazanie do technologi xml. Wiesz mnie sie zawsze wydawalo, ze ten jezyk zostal powolany do zycia po to, aby zapewnic przenosnosc danych pomiedzy roznymi platformami. Nie wiem czy w tym przypadku zapewnienie tej cechy jest pozadane. Uwazam ze próbujesz robic cos na sile utrudniajac sobie zywot.
Nievinny
@matid -> ale tą mapę musisz za każdym uruchomnienie skryptu załadować i to będzie się dziać często...
matid
Cytat(Nievinny @ 2005-05-10 17:01:11)
@matid -> ale tą mapę musisz za każdym uruchomnienie skryptu załadować i to będzie się dziać często...

Przecież nie chodzi mi o trzymanie mapy w XMLu, bo jest to totalną pomyłką. Nie jestem aż tak przywiązany do XMLa żeby tracić na wydajności. Chodzi mi o trzymanie w pliku XML dodatkowych ścieżek do przeszukiwania, czyli parsowane będzie to tylko przy regenerowaniu mapy.
Nievinny
Czyli trzymasz tylko katalogi do parsowania? To nie ma sensu, ponieważ powinno się parsować wszystkie katalogi i uzyskać pełną mapę klas.
Ociu
Cytat(matid @ 2005-05-10 18:36:31)
Chodzi mi o trzymanie w pliku XML dodatkowych ścieżek do przeszukiwania, czyli parsowane będzie to tylko przy regenerowaniu mapy.

Przemyślałem sposób z XML i stwierdziłem, ze pomysł jest chybiony. Tak czy inaczej trzeba za każdym razem parsowac plik, traci się na tym cenny czas... A gdy tych dodatkowych ścieżek jest dużo ? wtedy czas wydłuża się do sek, a nie tysiącznych sekund.
matid
Cytat(Ociu @ 2005-05-10 20:40:08)
Przemyślałem sposób z XML i stwierdziłem, ze pomysł jest chybiony. Tak czy inaczej trzeba za każdym razem parsowac plik, traci się na tym cenny czas... A gdy tych dodatkowych ścieżek jest dużo ? wtedy czas wydłuża się do sek, a nie tysiącznych sekund.

Nie wiem jak sobie wyobrażasz wygenerowanie mapy w tysięczne sekund. W średniej wielkości projekcie (jeszcze bez czytania dodatkowych ścieżek, ok. 90 klas w różnych plikach) trwa to ok. 0,4 s. Czytanie z mapy to ok 0.0008s. Więc nie wiem jakbyś chciał kombinować, ale IMO niemożliwym jest generowanie mapy w tysięczne sekundy. Oczywiście mówię tutaj o mapie nie wymuszającej żadnego przystosowania nazwy pliku itp.
Vengeance
" trwa to ok. 0,4 s"

lol :] Mój kod (a ogolnie ten co podałem powyżej gdzieś), który rekurencyjnie schodzi katalogami w dół i zapisuje znalezione klasy już teraz wykonuje się czasem więcej niż 20 sekund :]
Oczywiście robione jest to tylko raz, przy pierwszym uruchomieniu.
matid
Cytat(Vengeance @ 2005-05-10 22:26:35)
" trwa to ok. 0,4 s"

lol :] Mój kod (a ogolnie ten co podałem powyżej gdzieś), który rekurencyjnie schodzi katalogami w dół i zapisuje znalezione klasy już teraz wykonuje się czasem więcej niż 20 sekund :]
Oczywiście robione jest to tylko raz, przy pierwszym uruchomieniu.

Moja klasa autoloadera jest w znaczej części oparta na wypowiedziach i nawet fragmentach kodów z tego tematu, więc nie zdziwiłbym się, gdyby funkcja skanująca foldery (lub jej założenia) była już przez ciebie cytowana.

Prędkości w dostępie do plików mogą wynikać z różnego systemu operacyjnego (ja testuję lokalnie na linuksie, który ma świetny system buforowania dysków), procesora, pamięci, obciążenia, itp.
U mnie przeskanowanie ok 100 plików (ok. 1 MB) i stworzenie mapy zawierającej 90 klas trwa ok. 0,4s.

Oprócz tego mam napisany bardzo fajny system automatycznego regenerowania mapy, cachowania wyników, itp.
Ociu
Czepiasz się szczegółów, liczba była podana dla przykładu.
Vengeance
To nie było czepianie. Chodziło mi o sposób w jaki @matid osiągnął tak dobry jak dla mnie wynik. U mnie już powoli php robi time out przy generowaniu :] (WinXP, 950 MHz, 256mb RAM)
matid
Dla zainteresowanych - moja klasa Autoloader: http://matid.emmatrade.pl/files/Autoloader.class.phps

I jeszcze raz moje testy:
Sprzęt: Pentium 4 2,4 GHz, 1024 MB RAM, dysk twardy 7200 RPM (system plików: ReiserFS)
System: Ubuntu Linux 5.04 Hoary Hedgehog

Dane testowe:
Rozmiar: 913.6 KB
Liczba plików: 120
Liczba klas: 90

Czas generowania mapy po raz pierwszy po włączeniu komputera: 0.872271060944s
Czas generowania mapy po raz kolejny (część plików mogła być w buforze dysków): 0.427920103073
hawk
Skoro temat zrobił się popularny i każdy chce mieć swój autoloader oparty o mapy (co uważam za słuszne), to warto załatwić jedną prostą rzecz. Kompatybilność. Taka mapa to na tyle prosta struktura, że pewnie wszędzie wygląda tak samo:
  1. <?php
  2. $map=array(
  3. 'nazwaklasy' => 'ścieżkadoklasy',
  4. );
  5. ?>

Wtedy każdy może sobie generować mapę jak mu się podoba, szybciej lub wolniej. I każdą mapę można wrzucić do każdego autoloadera. Dobrze myślę?

Kompatybilność jest akurat bardzo ważna w przypadku autoloadera. Bo jeżeli napiszę jakąś bibliotekę, to wykorzystam swój pomysł. I ta biblioteka musi działać z biblioteką autorstwa kogoś innego, kto też oparł ją o swój autoloader. A autoloader może działać tylko jeden, więc jeżeli jeden do drugiego nie pasuje, to całość wychodzi do dupy.
bigZbig
@hawk -> Amen
dr_bonzo
...mam i ja.

Stworzylem swojego autoloadera:
- w konfigu podaje katalogi do rekursywnego przejrzenia
- podaje rozszerzenia plikow ktore maja byc tokenizowane

- mapa jest tworzona w std. formacie: nazwa_klasy => sciezka_do_pliku
- i zapisywana w formacie .ini (moze dorobie wybor ini | php)

I co najwazniejsze -- cachuje wyniki tokenizowania plikow (to nie to samo co 'mapa') -- tokenizowane sa tylko te co zostaly zmodyfikowane (okreslane przez filemtime()), co daje znaczna oszczednosc czasu. Zapisuje tablice:
  1. <?php
  2.  
  3. [ 'nazwa_pliku' ] ->
  4. (
  5.     [ 'classes' ] ->
  6.     (
  7.         [ 0 ] -> 'klasa_1';
  8.         [ 1] - > itd...
  9.     )
  10.     [ 'modification_time' ] = 111123423423423; // czas modyfikacji
  11.     { ['valid' ] = TRUE }
  12. )
  13. )
  14. ?>


Jak to wyglada w praktyce:
- wczytuje ww. cache
- przegladam katalogi i robie liste znajdujacych sie tam plikow
- loop po tych wszystkich plikach: porownuje czas modyfikacji pliku z czasem zapisanym w cache
- jesli plik zostal zmieniony to go tokenizuje i aktualizuje cache (+ ustawienie znacznika 'valid' w cache)
- jesli nie zostal zmodyfikowany to tylko ustawienie znacznika 'valid' w cache
- po przejrzeniu wszystkich plikow, usuwam te wpisy w cache, ktore nie maja znacznika 'valid', i usuwam znacznik 'valid' z tych, ktore go maja (tylko po to zeby go (znacznika) nie zapisac w cache)
- uzyskuje tablice z wpisami
nazwa_pliku --> klasy/interfejsy w min sie znajdujace + czas modyfikacji pliku
- zapisuje to cache
- na podstawie tego cache tworze mape

Wlasciwosci
- nowe pliki ktorych nie bylo w cache sa tokenizowane i dodawane do cache i mapy
- pliki ktore byly w cache a teraz nie istnieja (nie ma ich w podanych w configu katalogach) sa usuwane z cache i mapy
- pliki ktore byly w cache i nie zostaly zmienione nie sa tokenizowane (duuuza oszczednosc czasowa)
- pliki ktore byly w cache i zostaly zmodyfikowane sa ponowanie tokenizowane -> aktualizacja cache

Czasy wykonania:
6 klas / 5 plikow:
0.1137sec // bez cache
0.0041 sec // z cache

dodanie dodatkowych plikow i klas, w sumie
355 plikow / 138 klas
4.3358 sec // bez cache
0.1672 z cache// z cache

modyfikacja 3 z tych plikow (glowne pliki Smartyego -- 161kB) i utworznie mapy, z cache
0.7571 sec




Kodu jest sporo: > 470 linii, jak dokoncze to dam linka.
Pozostaje jeszcze fopisac __autoload()'a.
Vengeance
Z twojego opisu wynika mi, że za każdym uruchomieniem klasy następuje rekurencyjne przejrzenie katalogów. Jeśli tak jest, to jest to tragiczne rozwiązanie :]

Po co te wszystkie "czasy edycji plikow" itp. Na co ci oszczednosc czasowa przy generowaniu mapy, skoro zalozeniem glownym jest ze robisz to tylko raz! Ewentualnie potem gdy cos zmieniasz w strukturze ponawiasz proces generowania.

Twoje "Jak to wyglada w praktyce" wg mnie powinno ograniczac sie tylko do:
1. przejrzenie rekurencyjne wskazanych katalogów
2. tokenizacja każdego pliku celem wyłapania zawartych w nim klas
3. zapisanie wszystkich powiązań klasa=>plik do .ini czy tam .php

A sam główny skrypt powinien tylko ów .ini lub .php wczytywać, zaś gdy go nie ma powinien uruchamiać generowanie ów pliku :]

Taka moja wersja
dr_bonzo
Cytat
Z twojego opisu wynika mi, że za każdym uruchomieniem klasy następuje rekurencyjne przejrzenie katalogów. Jeśli tak jest, to jest to tragiczne rozwiązanie :]


Jesli mapa istnieje to ten skrypt nie jest uruchamiany.

Cytat
Na co ci oszczednosc czasowa przy generowaniu mapy, skoro zalozeniem glownym jest ze robisz to tylko raz!


Przy rozbudowie aplikacji i sprawdzaniu poprawnosci jej dzialania nie bedzie mi sie chcialo czekac tych 5 czy 20 sekund na zobaczenie rezultatow.

"W praktyce" to robi + robi to szybciej jesli moze.
hawk
Ciekawe rozwiązanie. Faktem jest, że mi by się nie chciało smile.gif. Zwłaszcza że ja raczej na początku mam rozplanowane klasy, a potem głównie edytuję zawartość nie ruszając struktury.

Co do plików ini... rezultat ten sam. Dla ciekawości, sprawdzałeś, czy są szybsze od zwykłego php? Chociaż wada jest taka, że ja bym w pliku php bardzo chętnie umieszczał mapę wykorzystując jakąś zmienną - główny katalog projektu. Po to, aby ktoś, kto tego używa, nie musiał generować dla siebie mapy tylko dlatego, że u niego pliki php są w innym miejscu na dysku.
dr_bonzo
W koncu. Kod przepisany od nowa, lecz jeszcze nie dopieszczony:
http://northslope.lap.pl/dev/Autoloader/0.2-dev/

a tu zrodla do pobrania:
http://northslope.lap.pl/dev/Autoloader/au...-0.2-dev.tar.gz
Kod
generowanie mapy (cache puste):
Array
(
   [files_found] => 468
   [files_parsed] => 468
   [classes_found] => 233
)
Map generation time: :: 12.85658 sec

a z cache (nic nie zmienione):
Array
(
   [files_found] => 468
   [files_parsed] => 0
   [classes_found] => 233
)
Map generation time: :: 0.23078 sec

map from php: :: 0.00076 sec
map from ini: :: 0.00412 sec

php szybsze od INI

PSy: statystki (files_parsed)w udostepninych zrodlach sa nieprawidlowe, te wyzej sa ok.
Testowane na P3 @ 560
Imperior
dr_bonzo: http://wiki.php.pl/index.php/RecursiveDirectoryIterator
Czyż nie łatwiej?
hawk
Fajny kod. Nawet zdążyłem już użyć biggrin.gif.

Kilka uwag:
- nie można podać extension np. "class.php", co jest bardzo przydatne, bo pozwala pominąć unit testy ("test.php"). Musiałbyś zmienić metodę isFileExtensionOnTheList tak, aby po prostu sprawdzała, czy nazwa pliku kończy się jednym z podanych wzorców
- przy pierwszym wywołaniu dostaję warning, że cache jeszcze nie istnieje. Potem jest OK.
- RecursiveDirectoryIterator uprościłby sprawę
- przydatną funkcją byłoby parsowanie tagów phpdoc (jeżeli są), aby pomijać klasy "wewnętrzne", czyli takie, które wykorzystywane są tylko w obrębie danego pliku i nie ma potrzeby wystawiać ich na zewnątrz. Ja takich mini-klas używam, i brakuje mi możliwości zrobienia prywatnej klasy wewnętrznej jak w Javie...
dr_bonzo
Cytat
- nie można podać extension np. "class.php", co jest bardzo przydatne, bo pozwala pominąć unit testy ("test.php"). Musiałbyś zmienić metodę isFileExtensionOnTheList tak, aby po prostu sprawdzała, czy nazwa pliku kończy się jednym z podanych wzorców

[DONE] Jest w TODO

Cytat
- przy pierwszym wywołaniu dostaję warning, że cache jeszcze nie istnieje. Potem jest OK.

Kod jeszcze "niedopieszczony" wersja dev -- dziala ok, pokazuje wszystkie bledy na ekranie, potem dodam malpy do funkcji

Cytat
- RecursiveDirectoryIterator uprościłby sprawę

[DONE] Zgadza sie. Poprawie

Cytat
- przydatną funkcją byłoby parsowanie tagów phpdoc (jeżeli są), aby pomijać klasy "wewnętrzne", czyli takie, które wykorzystywane są tylko w obrębie danego pliku i nie ma potrzeby wystawiać ich na zewnątrz.

Troche roboty bedzie. A jaki znacznik odpowiada za plikowy zasieg klasy?

PS. [files_found] to liczba znalezionych plikow z wybranymi rozszerzeniami
-------------------------------------------
EDIT

podglad kodu online
http://northslope.lap.pl/dev/Autoloader/au...-0.3.0-sources/

paczka
http://northslope.lap.pl/dev/Autoloader/au...er-0.3.0.tar.gz
hawk
A co powiecie na taką implementację?
  1. <?php
  2. $i = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(PHIEND_HANDLE_DIR));
  3. $i = new KeyFilter($i, '.class.php$');
  4. $w = new PhiendClassMapWriter('./handle_map.php');
  5. $w->setTargetDir('e:/Web Files/lib/phiend2/phiend.handle', 'PHIEND_HANDLE_DIR');
  6.  
  7. $generator = new ClassMapGenerator();
  8. $generator->addIterator($i);
  9. $generator->setCreator(new BasicClassMapCreator());
  10. $generator->addFilter(new SortClassMapFilter());
  11. $generator->setWriter($w);
  12.  
  13. $generator->run();
  14. ?>

Wynik:
  1. <?php
  2.  
  3. /*
  4.  * File generated by phiend.autoload
  5.  */
  6.  
  7. define('PHIEND_HANDLE_DIR', 'e:/Web Files/lib/phiend2/phiend.handle');
  8.  
  9. $aMap = array(
  10. 'IHandle' => PHIEND_HANDLE_DIR . '/IHandle.class.php',
  11. 'BasicHandle' => PHIEND_HANDLE_DIR . '/BasicHandle.class.php',
  12. // (ciach)
  13. 'SerializedHandle' => PHIEND_HANDLE_DIR . '/SerializedHandle.class.php',
  14. );
  15.  
  16. Autoloader::addMap($aMap);
  17.  
  18. ?>

Mogę tu zrobić praktycznie wszystko, dzięki dobremu rozbiciu na klasy i dziedziczeniu. W pełni wykorzystuje SPL - do tego stopnia, że moje php 5.0.3 jest za stare i musiałem wykomentować kilka linii winksmiley.jpg.
Kod: http://cvs.sourceforge.net/viewcvs.py/phie...hiend.autoload/
Zip: http://phiend.sourceforge.net/misc/phiend-autoload.zip
cagrET
offtopic.gif

hawk: widzę, że używasz "notacji węgierskiej", ciekawi mnie czy może czytałeś ten artykuł: http://www.joelonsoftware.com/articles/Wrong.html > I’m Hungary . Joel pisze tam o tym, że w zły sposób używa się notacji węgierskiej, nie w taki jakim intencją autora tej notacji było.
hawk
Nie, nie czytałem do tej pory. Przedrostki określające typ stosuję tylko w php, bo tak jest mi wygodniej. W językach ze ścisłą typizacją są mi niepotrzebne.
matid
Cytat(hawk @ 2005-06-05 14:23:14)
- przydatną funkcją byłoby parsowanie tagów phpdoc (jeżeli są), aby pomijać klasy "wewnętrzne", czyli takie, które wykorzystywane są tylko w obrębie danego pliku i nie ma potrzeby wystawiać ich na zewnątrz. Ja takich mini-klas używam, i brakuje mi możliwości zrobienia prywatnej klasy wewnętrznej jak w Javie...

Który znacznik phpDoc oznacza klasy wewnętrzne?
Edit: Ale ja jestem ślepy, że nie widziałem wcześniej tego pytania ohmy.gif. Nie zmienia to faktu, że nie udzielono na nie odpowiedzi, więc dołączam się do grona pytających tongue.gif

BTW. Bardzo mi się twój kod podoba, szczególnie te wszechobecne interface i rozdrabnianie tego na wiele klas. Chyba trzeba przepisać mój Autoloader tongue.gif
dr_bonzo
cagrET: link do swietnego serwisu.
Cytat
Z jakiegoś powodu większość ludzi zdaje się rodzić bez tej części mózgu, która jest potrzebna do rozumienia wskaźników
, LOL

hawk: do takiego OOP troche mi jeszcze brakuje smile.gif

notacja wegierska: pamietam jak na poczatku '90 czytalem o tym w pc kurierze -- nawet mam wyrwane strony z tym artem smile.gif
hawk
@dr_bonzo i matid:
Niestety, phpDocumentor nie ma, AFAIK, takiego znacznika. Szkoda. Chociaż nic nie stoi na przeszkodzie taki sobie stworzyć. Precedens jest - w Javie wiele narzędzi posiada swoje własne znaczniki javadoc. Może @no-autoload?
matid
Cytat(hawk @ 2005-06-13 22:51:34)
@dr_bonzo i matid:
Niestety, phpDocumentor nie ma, AFAIK, takiego znacznika. Szkoda. Chociaż nic nie stoi na przeszkodzie taki sobie stworzyć. Precedens jest - w Javie wiele narzędzi posiada swoje własne znaczniki javadoc. Może @no-autoload?

Osobiście byłbym nie za wprowadzeniem znacznika mówiącego o wykluczeniu klasy z autoładowania (choć może to też by był dobry pomysł), lecz znacznika mówiącego o zasięgu klasy.
Czyli prędzej coś jak:
@range: file
@class-range: file
@classrange: file

Oprócz tego można by się pokusić o bardziej rozbudowany sysem autoloadingu, który np. pozwalałby na uwzględnienie takich czynników jak zakres folderowy (@range: directory), no i oczywiście @range: global, czyli to samo co w przypadku gdy pomijamy parametr zasięgu.

To takie moje 3 grosze.

BTW. Jak widzicie wykorzystanie takiego autoloadera w systemie? Czy korzystacie z niego dla całego kodu, czy tylko dla bibliotek? Bo dość niewygodne może być korzystanie z tego w modułach/akcjach. Bo akcja powinna być unikalna w obrębie modułu, ale może się pojawić w innych.
hawk
@matid: Pomysł wręcz genialny w swojej prostocie. Popieram. Jeżeli mamy wybrać najlepszą nazwę, to ja proponuję
Kod
@scope file

Scope, bo to bardziej odpowiednie słowo niż range. Bez dwukropka, bo chyba tagi phpdoc nie używają normalnie dwukropka? W każdym razie ja nigdy nie pisałem...

Co do zastosowania: generalnie używałbym na biblioteki. Albo na intensywnie wykorzystywaną część kodu - zawsze mogę wygenerować sobie mapę np. dla części Modelu. Natomiast dla akcji - nie warto, bo i tak w akcjach musi być porządek, wiadomo dobrze gdzie jaka akcja jest, i wykonujemy jedną akcję na raz. Szkoda mapę robić.
matid
No rzeczywiście, @scope będzie lepsze.
Spróbuję to jakoś zaimplementować, zobaczymy, co z tego wyjdzie smile.gif
Cytat(hawk)
Bez dwukropka, bo chyba tagi phpdoc nie używają normalnie dwukropka? W każdym razie ja nigdy nie pisałem...

Literówka winksmiley.jpg Miało być bez.
Cytat(serafin)
Ludzie rozwodzicie sie nad mapa, ktora mozna napisac nawet recznie. A wy tworzycie jakies skrypty bog wie po co i tworzycie nowa teorie do tego. Bezsensu. Zamiast napisac prosty skrypt do tworzenia mapy wy tworzycie 50 klas do wygenerowania tablicy 100 klas.

Ja osobiście wole mieć tablice generowane automatycznie. Nie dość że mogę sobie dowolnie zmieniać położenie klas to w dodatku skrypt przy następnym uruchomieniu sam uaktualnia mapę.

A co do podzielenia na 50 klas. Pierwsza wersja mojego skryptu do autoloadingu to była jedna klasa. Ale po przejrzeniu kodu @hawk widzę, ile jeszcze mojemu OOP brakuje. Dopiero podział tej klasy na kilka mniejszych pokazał mi, o ile większe możliwości w późniejszym rozszerzaniu daje OOP.
hawk
@serafin:

1) Nikt ci nie broni tworzyć takiej mapy ręcznie. Gratuluję odwagi. I nadmiaru wolnego czasu.

2) Bezsensu. Zamiast napisac prosty skrypt do wyświetlania stron wy tworzycie 50 klas, jakieś wzorce, MVC, biblioteki. Przecież każdy wie, że najlepiej jest wrzucić wszystko do jednego pliku, a całe to OOP to jedna wielka ściema.

Pokaż swój autoloader, a wtedy możemy porozmawiać, co jest lepsze, a co jest bez sensu.
hawk
Cytat("serafin")
Hawk: Wykorzystales 10 plikow na to co mozna zrobic w jednej klasie (i nie powiedziane ze gorzej).

No dobrze, porozmawiajmy wobec tego konkretnie. Na przykładach. Zaraz zobaczymy, czy rzeczywiście nie gorzej.

1. Chcę włączyć do mapy pliki z katalogów foo/first i foo/second, ale absolutnie nie z foo/third, bo tam są przykłady, które niepotrzebnie zaśmiecałyby mapę.

2. Chcę przeparsować tylko pliki o rozszerzeniu .class.php. Wszystko inne jest niepotrzebne.

3. Chcę, żeby w mojej mapie klasy były posortowane alfabetycznie. Dzięki temu mogę łatwo sprawdzić, jakie klasy są w moim pakiecie, a ponieważ jest to bez różnicy dla przetwarzania, nie ma żadnego powodu, żeby tego nie zrobić. No chyba że narzędzie jest gorsze...

4. Chcę wygenerować mapę dla zewnętrznego pakietu, który inaczej nazywa swoje klasy. Jak narzędzie, to uniwersalne, a nie ograniczone do mojego kodu, prawda? Pakiet jest w zupełnie innym miejscu na dysku.

5. Chcę w wersji produkcyjnej wrzucić kilka klas, które zawsze zawsze są używane, do jednego pliku, żeby przyspieszyć działanie i żeby prościej to wyglądało dla użytkownika końcowego. To częsta praktyka - weźmy np. mojavi.

6. Chcę za pomocą mojego autoloadera wygenerować mapę, która może być od razu wykorzystana w twoim systemie. Czyli plik wygląda tak samo. Chcę mieć uniwersalny autoloader, którego mogą używać inni, a nie nieprzenośne narzędzie.

7. Mam 2 mapy, do 2 różnych pakietów, i chcę obydwie naraz wbić do autoloadera.

Wierzę ci, że można to zrobić w jednej klasie. Która do tego będzie mniejsza niż suma tych plików i w ogóle lepsza. Tylko nie wiem, jak...

A co do tych 10 plików... rozmijasz się z prawdą winksmiley.jpg. To są cztery pliki, plus trzy interfejsy (do niczego niepotrzebne, możesz je spokojnie wywalić, ale ja mam do interfejsów sentyment), plus trzy klasy dziedziczące z klas podstawowych (miałem kaprys zrobić coś extra, ale przecież w ogóle nie musisz ich wykorzystywać). Na podobnej zasadzie możemy zacząć liczyć unit testy i pliki readme.

---------------------------------------------
Cytat
OOP ma sens owszem, ale nie w przypadku gdy tworzy sie klasy na 1-3 metod

Wiesz, co to jest Observer? Abstract Factory? Iterator? Registry? Intercepting Filter? Chain of Responsibility? Command? Strategy? Takie różne wzorce projektowe, ale niestety bez sensu, bo mają za mało metod.

Cytat
Oczywiscie ze rozbijanie problemu na mniejsze czescie jest bardzo dobra technika ale sa pewne granice. Czasami mozna kilka z tych czesci polaczyc w jedna wieksza calosc bez szkody dla ostatecznego rozwiazania.

To ty zacząłeś tą dyskusję, więc odpowiedz: dlaczego rozbijanie problemu na mniejsze klasy jest szkodliwe? Bo z tego, co piszesz, wynika, że wg ciebie jest.
matid
Cytat(serafin)
Ma to sens?

A dlaczego nie? Mi osobiście bardzo się podoba sposób potraktowania typów zmiennych jako klas, a samych zmiennych jako obiektów.
Oprócz php programuję też w Javie i uważam to za świetne rozwiązanie, więc dlaczego by nie używać tego w php?

Teraz pewnie padnie pytanie, co mi da takie "utrudnianie" sobie życia. Ale zdecydowaną korzyścią płynącą z takiego rozwiązania jest możliwość programowania własnych typów danych, czego przy standardowym traktowaniu zmiennych przez php nie zrobisz.

Do notki poniżej: Skoro w php są typy podstawowe, to dlaczego miałoby nie być rozszerzonych? Nie będę nikogo nawracał rzucenia podstawowych typów w php na rzecz klas, bo dla niektórych to może być masochizm. Ja osobiście nie mam nic przeciwko smile.gif
A co do pisania w php systemu operacyjnego. Wiem, że to było pytanie retoryczne, ale odpowiem - bo php się do tego kompletnie nie nadaje tongue.gif

A teraz wracając do autoloadera. Zaimplementowałem parsowanie tagów phpDoc, ale jeszcze borykam się z jedym problemem.
Powiedzmy, że moja mapa wygląda tak:
  1. <?php
  2.  
  3. /*
  4. * File generated by OpenForce Autoloader
  5. */
  6.  
  7. $aMap = array(
  8. 'GLOBAL' => array(
  9. 'IteratorFilter' => '/var/www/matid/Current/Autoloader/lib/IteratorFilter.class.php',
  10. 'iClassMapGenerator' => '/var/www/matid/Current/Autoloader/lib/autoloader/interfaces/ClassMapGenerator.interface.php',
  11. 'iClassMapEntry' => '/var/www/matid/Current/Autoloader/lib/autoloader/interfaces/ClassMapEntry.interface.php',
  12. 'iClassMapWriter' => '/var/www/matid/Current/Autoloader/lib/autoloader/interfaces/ClassMapWriter.interface.php',
  13. 'iClassMapCreator' => '/var/www/matid/Current/Autoloader/lib/autoloader/interfaces/ClassMapCreator.interface.php',
  14. 'ClassMapGenerator' => '/var/www/matid/Current/Autoloader/lib/autoloader/ClassMapGenerator.class.php',
  15. 'OFClassMapWriter' => '/var/www/matid/Current/Autoloader/lib/autoloader/OFClassMapWriter.class.php',
  16. 'ClassMapEntry' => '/var/www/matid/Current/Autoloader/lib/autoloader/ClassMapEntry.class.php',
  17. 'ClassMapCreator' => '/var/www/matid/Current/Autoloader/lib/autoloader/ClassMapCreator.class.php',
  18. 'ClassMapWriter' => '/var/www/matid/Current/Autoloader/lib/autoloader/ClassMapWriter.class.php',
  19. 'ClassNotFoundException' => '/var/www/matid/Current/Autoloader/lib/exceptions/ClassNotFoundException.class.php',
  20. 'Autoloader' => '/var/www/matid/Current/Autoloader/lib/Autoloader.class.php',
  21. 'Test2' => '/var/www/matid/Current/Autoloader/test/1/2.php',
  22. 'Test3' => '/var/www/matid/Current/Autoloader/test/3.php',
  23. ),
  24. '/var/www/matid/Current/Autoloader/test/1' => array(
  25. 'Test1' => '/var/www/matid/Current/Autoloader/test/1/1.php',
  26. ),
  27. );
  28.  
  29. Autoloader::addMap( $aMap );
  30.  
  31. ?>

Klasa Test1 ma ustawiony zasięg directory, reszta jest global. I teraz mam taką sytuację:

1. Jakaś klasa próbuje się odwołać do klasy Test1, ale nie znajduje się w tym samym folderze, autoloader rzuca ClassNotFoundException (a w zasadzie tworzy klasę Test1, której kostruktor rzuca wyjątek).
2. Jakaś inna klasa próbuje się odwołać do klasy Test1 i znajduje się w tym samym folderze, niestety, wcześniej została już utworzona klasa Test1, która domyślnie rzuca ClassNotFoundException.

Ma ktoś jakiś inny pomysł na implementację tego? Chyba jedynym wyjściem jest pozbycie się wyjątków z autoloadera, ale może ktoś wymyśli coś innego.
bela
Cytat(matid @ 2005-06-16 23:08:29)
Cytat(serafin)
Ma to sens?

A dlaczego nie? Mi osobiście bardzo się podoba sposób potraktowania typów zmiennych jako klas, a samych zmiennych jako obiektów.

To było takie pytanie retoryczne :] Nie ma to sensu. Po co opakowywać typy wbudowane jak i tak nic nie zyskasz, no może poza estetyką kodu winksmiley.jpg

Cytat
Oprócz php programuję też w Javie i uważam to za świetne rozwiązanie, więc dlaczego by nie używać tego w php?

W Javie też są typy podstawowe i jakoś nikt z tego powodu nie umarł :]
Btw. czemu nie piszesz w php systemu operacyjnego winksmiley.jpg

Cytat
Teraz pewnie padnie pytanie, co mi da takie "utrudnianie" sobie życia. Ale zdecydowaną korzyścią płynącą z takiego rozwiązania jest możliwość programowania własnych typów danych, czego przy standardowym traktowaniu zmiennych przez php nie zrobisz.

Emm a czy klasy nie są typami danych?
Imperior
Cytat(matid @ 2005-06-16 21:08:29)
Ma ktoś jakiś inny pomysł na implementację tego? Chyba jedynym wyjściem jest pozbycie się wyjątków z autoloadera, ale może ktoś wymyśli coś innego.

1. Olać tworzenie pustej klasy
2. Olać zasięgi
3. Każdą klasę tworzyć pośrednio

ad 3. Proxy... zamiast $obj = new Test1(); robić $obj = Class::New('Test1');
A metoda New z Class wszystkim sie zajmie (zwróci instancję, rzuci wyjątek itp)
matid
Chyba jednak skorzystam z opcji 1, ponieważ zasięgów nie chcę olewać, a tworzenie instancji przez proxy wyklucza takie przypadki jak dziedziczenie, implementowanie interfaców, itp.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.