Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Ładowanie plików
Forum PHP.pl > Forum > PHP > Object-oriented programming
wlamywacz
Witam. Jestem w trakcie projektowania swojego pierwszego poważnego framweworka którego mam zamiar używać w swoich aplikacjach. Struktura katalogów i plików jest identyczna jak w ZF czyli:
aplication
- controllers
- models
- views
library
public

Zastanawia mnie jak wykonać w takim systemie tzw. autoload? Myślałem nad stworzeniem dodatkowego kontrolera który przejmował by za mnie ten problem. Działało by to w to w ten sposób:
  1. <?php
  2.  
  3. $myClass = loader getInstance('aplication_models_myClass');
  4.  
  5. ?>

Loader ładował by potrzebny plik z odpowiednią klasą i zwracał jej obiekt. Jak wy rozwiązujecie taki problem ?

Psuje kod, miało być:
$myClass = loader::getInstance('aplication_models_myClass');
Crozin
W ZF nazwa klasy jest równocześnie ścieżką do jej źródła:
Zend_Sth_Sthelse_Qwert -> ./library/Zend/Sth/Sthelse/Qwerty.php
Możesz sobie tak napisać __autoload(), aby to było właśnie w ten sposób wczytywane.

Nie wiem co robi klasa Loader w ZF, ale z Twojego kodu wnioskuję, że jest to poprostu Rejest (Google: Registry design pattern)
LBO
Zależy w którą stronę chcesz pójść z autoloaderem.

Jeżeli chcesz by było łatwo, ale sztywno to możesz stworzyć coś a'la Zend/PEAR autoloader

Jeżeli chcesz tak, żeby było trochę zachodu, ale za to multielastycznie to stwórz sobie coś w rodzaju konfiguracji, tam zapisuj ścieżki do plików względem projektu/frameworka dopasowane do nazw klas w tych plikach i potem użyj tego w autoloaderze.


Na forum jest jeden wilki wątek dotyczący autoloadera - poszukaj.
ARJ
u mnie autoload opiera się na stworzonej mapie plików. używając __autoload() lub spl_autoload_register() ustawiam używanie mojego loadera. mapa klas jest tworzona za pierwszym użyciem lub gdy plik nie istnieje (update). później za pomocą nazwy klasy ładowany jest plik. oczywiście założeniem jest, że nazwa klasy to nazwa pliku.

jeżeli wzorujesz się na ZF to możesz stworzyć taki loader jak tam istnieje czyli nazwa pliku jest ścieżką do niego. niestety nazwy klas bywają długie ale za to wiadomo co i skąd się bierze.

jeżeli jesteś ciekawy mogę zaprezentować moją klasę loader`a. nie wklejam jej tutaj bo ostatnio jest problem na forum z "::", a klasa opiera się na metodach statycznych i kod może dziwnie wyglądać.
wlamywacz
Jeśli mógłbyś to wkleić na phpfi byłbym bardzo wdzięczny. Jeśli chodzi o nazwę klasy jako ścieżkę do niej to przerabiałem to już w ostatnim systemie i podobało mi się to strasznie.

Crozin
Zapominasz że library to tylko klasy niezbędne a jeszcze do tego dochodzą modele w osobnych katalogach i klasy pomocnicze wykorzystywane tylko podczas testowania aplikacji. Robiłem to tak w poprzedniej aplikacji:
  1. <?php
  2. function __autoload($value) {
  3.  
  4. $url = str_replace("_", "/", $value);
  5.    
  6.    require_once 'libs/'.$url.'.class.php';
  7.    
  8. }
  9. ?>
ARJ
proszę: http://phpfi.com/355575
klasa nie ma komentarzy ponieważ jest jeszcze ciepła. są do tego poprawki takie jak skanowanie kilku "folderów głównych", a nie tak jak teraz folderu lib (lub innego podanego) i jego podkatalogów.
użycie to np.
  1. <?php
  2. require('lib/hpLoader.php');
  3. function __autoload($classname){
  4.    hpLoaderautoLoad($classname);
  5. }
  6. ?>

w kodzie powyżej brak ::
LBO
Temat o którym pisałem wcześniej: Włączanie plików + autoloader, chętnie bym posłuchał ciekawych pomysłów
wlamywacz
Czytałem ten temat, dziękuje za klasę jutro ją przejrzę. Jestem chyba za tym aby robić mapę w pliku .ini i z niego pobierać dane.
likemandrake
Ja kiedyś rozwiązałem ten problem w taki sposób:

Storzyłem sobie klasę Loader, która w konstruktorze wywoływała funkcję spl_autoload_register. Klasa zaraz po utworzeniu obiektu, sprawdzała czy we wskazanej lokalizacji znajduje się plik z cachem. Jeśli cache się znajdował, to wczytywał go, a w cache'u była zserializowana tablica, której kluczem była nazwa klasy, a wartością ścieżka do pliku z klasą.

W klasie (Loader) istniała metoda, przy której użyciu można było dodawać ścieżki z katalogami do klas, metoda ta przeszukiwała rekurencyjnie wskazany katalog w poszukiwaniu plików. Generalnie szukała na podstawie takiego schematu nazwy pliku: class.nazwa_klasy.php dla klasy, oraz interface.nazwa_interfejsu.php dla interfejsu. Na tej podstawie tworzyła się tablica o schemacie, o którym wspomniałem wyżej.

W destruktorze zawartość tablicy z klasami była serializowana i zapisywana do pliku (cache, o którym mowa wyżej). Istniała również metoda, dzięki której mogłem dodać sobie ręcznie jakąś klasę, np. coś z biblioteki PEAR. Do metody tej dostarczałem więc nazwę klasy oraz ścieżkę do klasy.

Proponuję również, dla zachowania pewnej zgodności wstecznej (a mianowicie, jeśli brakuje nam spl_autoload), do klasy Loader wstawić metodę, która po przekazaniu tylko i wyłącznie nazwy klasy, ładowałaby nam odpowiedni plik. Nię muszę chyba tłumaczyć, że wygodniejsze jest wklepanie "Loader::load('nazwa_klasy');" niż "require_once '/sciezka/do/klasy/class.nazwa_klasy.php';". To tyle smile.gif
zimi
dorzuce może jeszcze swoje 3 grosze, w niektórych rozwiązaniach jest sugestia aby korzystać z określonego rozszerzenia np. class.php, że nazwa klasy była taka sama jak pliku, żeby ...

Ja zrobiłem klasę która mapuje wskazany katalog, z opcjonalną flagą o rekursywności mapowania:
tak wygląda przykładowy kod wykorzystujący klasę mapowania
  1. <?php
  2. require_once'core/MapaKlas.php';
  3. $obiekt = new MapaKlas();
  4. $obiekt->szukaj('.', FALSE);
  5. $obiekt->szukaj('core/');
  6. $obiekt->szukaj('modele/');
  7. $obiekt->szukaj('modul/');
  8. $obiekt->generujPlik();
  9. ?>

Tym kodem mapujemy zawartość wszystkich plików w katalogu głównym i rekursywnie wszystkie pliki w katalogach core, modele, modul i sprawdzamy tokenizerem PHP czy w pliku są klasy PHP i ew. jakie, tworząc tablicę asocjacyjną i generując całą klasą autoloadera... Tu w sumie trochę przesadziłem lepiej by chyba było serializować i odserializować dane w autoloaderze, ale zrobiłem jak zrobiłem

taki był mój pomysł, obróka tokenizerem nie była trudna, a dzięki niej jesteśmy niezależni czy ktoś ma jakieś swoje rozszerzenie php5, php6, php, php_cokolwiek biggrin.gif:P, czy ma 2 klasy w pliku czy cokolwiek innego, wszystko wyciąga tokenizer i przyporządkowuję klasę do pliku

Kod nie jest piękny, dość bałaganiarski tak mi się wydaję..., ale jeśli ktoś jest zainteresowany to podrzucę (możnaby rzec: mi działało smile.gif ), chciałem jedynie zasygnalizować ideę..., ja chwilowo staram się pracować z symfony więc klasy nie rozwijałem...
wlamywacz
Nie rozumiem ? worriedsmiley.gif Właśnie o to chodzi że tak się nie da bo modele itp. mogą być rozsiane po kilka katalogach w różnych częściach aplikacji.
LBO
Cytat(wlamywacz @ 22.09.2008, 19:00:58 ) *
Nie rozumiem ? worriedsmiley.gif Właśnie o to chodzi że tak się nie da bo modele itp. mogą być rozsiane po kilka katalogach w różnych częściach aplikacji.


Akurat modelami, kontrolerami/akcjami i widokami powinien zajmować się na pierwszym miejscu Dispatcher.

Wg mnie autoloader powinien być na tyle elastyczny, by być w stanie odszukać każdą klasę w projekcie. Najlepiej w tym przypadku działa rozwiązanie z mapą plików i klas w nich zawartych. Co za tym idzie, nie zgodzę się, że autoloader powinien zajmować się przeszukiwaniem katalogów. Od tego powinno być osobne narzędzie, które budowało w/w mapę na żądanie programisty (wyobrażacie sobie żeby w środowisku developerskim, przy każdym teście autoloader przeszukiwał katalogi?).

To w kwestii uniwersalnego autoloadera ofkorz.
wlamywacz
Jednak załadowanie do nich odpowiednich plików musi się zając autoloader. Wybrałem jednak zrobienie po prostu mapy .ini wszystkich potrzebnych plików. Dziękuje za dyskusje w temacie
zimi
Cytat
Właśnie o to chodzi że tak się nie da bo modele itp. mogą być rozsiane po kilka katalogach w różnych częściach aplikacji.

tak mogą być rozsiane w różnych częściach aplikacji... ale z góry możesz stwierdzić że nie będzie go np. w katalogu z obrazkami... dlatego aby skrypt nie przebijał się przez obrazki w poszukiwaniu klas, czy przez pliki innych dużych zbiorów plików

możesz jednak zapisać tylko:
  1. <?php
  2. $obiekt->szukaj('.');
  3. ?>

i mój skrypt przeszuka rekurencyjnie wszystko w katalogu bieżącym

zresztą to nie ma większego znaczenia... skrypt który podałem ma wykonać się tylko raz (gdy aplikacja przechodzi w tryb produkcyjny, w czasie tworzenia aplikacji można ją dołączyć do jakiegoś frontcontrolera) a plik, który wygeneruję jest autoloaderem który pozostaje już stały

jednym słowem ja mam 2 klasy:
- MapaKlas -> przeszukuje wg zaleceń "pliku konfiguracyjnego" którego przykład był poniżej, znajduje klasy, odczytuje jej nazwy i generuje klasę Autoloader'a
- Autoloader -> jak bedzie potrzebna klasa X wyszukuje klasę X w mapie i wczytuje

Cytat
Co za tym idzie, nie zgodzę się, że autoloader powinien zajmować się przeszukiwaniem katalogów.

Zdecydowanie, zbyt dużo czasu by to zajęło...

Cytat
Od tego powinno być osobne narzędzie, które budowało w/w mapę na żądanie programisty (wyobrażacie sobie żeby w środowisku developerskim, przy każdym teście autoloader przeszukiwał katalogi?).

Właśnie od tego jest moja MapaKlas, mówimy jej gdzie chcemy aby szukała (tylko dlatego żeby skrócić czas oczekiwania, bo wiemy że niektórych katalogów nie warto w ogóle otwierać, np. w /download/, /images/, /javascript/, /css/ klas PHP na pewno nie znajdziemy...)
reszta plików niezależnie od rozszerzenia może być otwarta i dokładnie przeszukana tokenizerem PHP w celu znalezienia nawet wielu klas w jednym pliku...
wlamywacz
Doszliśmy już do tego rozwiązania smile.gif I właśnie je wykorzystam tyle że raczej mapę będę robić sam.
LBO
Jeżeli w Twoim systemie masz podział na środowiska (czyli możesz uruchomić na tej samej aplikacji zarówno środowisko developerskie jak i produkcyjne) spróbuj to połączyć z tą mapą. Wyjdzie to Tobie na dobre.

W Agavi, nieraz to wykorzystuje np. modelami. Czasami testowo podmieniam sobie modele (modele są abstrakcja na warstwą bazodanową) na developerskim z używających Propela na czyste PDO (żeby zobaczyć, czy opłacalne).

I tak mogę mieć 2 klasy PostModel - pierwsza w pliku PostModel.class.php, a druga w PostModelPDO.class.php ładujące się w zależności od środowiska.
likemandrake
LBO, mam rozumieć, że stworzyłeś sobie klasę, która jest odpowiedzialna tylko i wyłącznie za generowanie mapy i na dodatek jest wywoływana kiedy ty tak naprawdę ją wywołasz? Druga klasa natomiast mam rozumieć, że jest odpowiedzialna za wczytanie tej mapy i pełnienie funkcji autoloadu?
LBO
Cytat(likemandrake @ 22.09.2008, 21:13:30 ) *
LBO, mam rozumieć, że stworzyłeś sobie klasę, która jest odpowiedzialna tylko i wyłącznie za generowanie mapy i na dodatek jest wywoływana kiedy ty tak naprawdę ją wywołasz? Druga klasa natomiast mam rozumieć, że jest odpowiedzialna za wczytanie tej mapy i pełnienie funkcji autoloadu?


Nie mówię, że mam taką klasę generatora. Mówię, że to jest najnormalniejsze, przy swej prostocie, wyjście.

Osobiście wolę sam sobie pisać te mapy. Dodatkowo nie trzeba się ograniczać tylko do jednej - mogę sobie stworzyć stałą mapę z klasami frameworka, baaa, mogę sobie napisać mapę klas OM z Propela, a generować tylko wąską część z projektu.

Nie zapominaj też autoloaderach innych projektów. Używając Zenda, nie będę sobie generował mapy klas, tylko podłączę Zend_Loadera.
Cysiaczek
Ja kiedyś popełniłem takie coś: http://kavuka.cal.pl/fsp/entry-2fa33a3605a...c85f1136b37.htm
autoloader.tar.gz. Nie mam do niego niestety dokumentacji i brakuje obsługi błędów, a rozgryzienie działania może chwilę zająć. Był stworzony raczej do ładowania bibliotek CORE mojego FW, więc obsługuje tylko jeden katalog - rekursywnie.
Działa jednak wyśmienicie. Oferuje dwie możliwości składowania mapy plików.
Pierwsza to array, druga to symlinki (oj, szybciej potrafi działać niż array - nawet o połowę). Jeśli pliku nie znajduje, automatycznie skanuje katalogi i jeszcze raz próbuje dołączyć plik.


Pozdrawiam.
LBO
Cytat(Cysiaczek @ 22.09.2008, 22:29:45 ) *
druga to symlinki (oj, szybciej potrafi działać niż array - nawet o połowę).


Nie wiem jak z symlinkami (wierzę na słowo), ale o uszy mi się kiedyś obiło, że takie proste struktury warto trzymać w ini. Jestem skłonny uwierzyć, bo jednak ini jest łatwiej i szybciej sparsować.
Cysiaczek
Widzisz, z symlinkmi to jest tak, że nic nie musisz parsować. Jedyne, co robisz, to include('symlink') i tyle. To są zwykłe skróty do plików upakowane w jednym katalogu. Ten sam efekt osiągniesz, jeśli wrzucisz do takiego katalogu wszystkie pliki biblioteczne. Należy jednak przyjąć, że każda nazwa pliku w systemie jest unikalna. Z drugiej strony, przy obecnym globalnym namespace i tak nie ma znaczenia ;]
LBO
He he, spoko Cysiaczku. Wiem co to są symlinki i jak działają, a na słowo wierzę w kontekście wydajności- korzystam z Nich nawet pod Windą smile.gif
Sedziwoj
Cytat(LBO @ 22.09.2008, 21:08:23 ) *
I tak mogę mieć 2 klasy PostModel - pierwsza w pliku PostModel.class.php, a druga w PostModelPDO.class.php ładujące się w zależności od środowiska.


Wiesz, nie mogę uwierzyć że robisz takie rzeczy, nie pomyślał bym, że to możesz napisać.
Przecież z jakiej abstrakcji bazy korzysta aplikacja powinno być w konfiguracji, a ta może być różna...
zaskoczyłeś mnie, na prawdę. Pisać o OOP i nie stosować jego zalet w takim miejscu.

Co do autoloader'a to tematów było sporo, jeden był podany, był na pewno jeszcze jeden gdzie się udzielałem, wystarczy użyć "szukaj".
Ogólnie mam tak napisany autoloader, że nie zwraca w ogóle na nazwy plików, tylko rozszerzenia, i je po prostu przegląda w poszukiwaniu klas, potem je mapuje, tworzy mapę, ale w niej jeszcze jest przechowywany czas ostatniej aktualizacji, więc jeśli plik się nie zmieni to nie próbuje go znów skanować. Do tego na produkcyjnym wystarczy raz wygenerować mapę, potem jeśli w niej coś nie ma, to nie ma. A w dev to nie zajmuje wcale dużo czasu (częściej korzystam z gorszego autoloader'a i ten nawet działa przyzwoicie)
LBO
Cytat(Sedziwoj @ 23.09.2008, 11:09:08 ) *
Wiesz, nie mogę uwierzyć że robisz takie rzeczy, nie pomyślał bym, że to możesz napisać.
Przecież z jakiej abstrakcji bazy korzysta aplikacja powinno być w konfiguracji, a ta może być różna...
zaskoczyłeś mnie, na prawdę. Pisać o OOP i nie stosować jego zalet w takim miejscu.


Sedziwoj nie atakuj, nie atakuj smile.gif

Jak napisałem wcześniej modele u mnie to abstrakcja nad źródło danych, które może być PDO, PRopel czy mysql_*.
Więc chcąc to zmienić zmieniam tak samo konfig jak i modele. Nie wiem jak mam wykorzystać obiektowośc akurat w tym przypadku - modele pobieram fabryką.

  1. <?php
  2. $model = $this->context->getModel('User');
  3. ?>


a model wygląda tak (pisze na szybko w edytorze forum, więc bez fajerwerków biggrin.gif):

  1. <?php
  2. // w app/models/UserModel.class.php ładowane automatem w środowisku "development" i "production"
  3. class UserModel extends ProjectBaseModel {
  4.    public function getUserById($id)
  5.    {
  6.            // Uproszczone maksymalnie
  7.           $user = UserPeerretrieveByPk($id);
  8.          
  9.           return $user->toArray(BasePeerTYPE_FIELDNAME);
  10.           // Nie zwracam obiektów Propela, bo cała abstrakcja nie miałaby sensu.
  11.    }
  12. }
  13.  
  14. // a teraz chcę sobie potestować czyste PDO, bez ingerencji w istniejace modele
  15.  
  16. // w  app/models/UserModelPDO.class.php i ustawiam sobie w mapie (dla środowiska
  17. // testowego "development-pdo") klasę UserModel na ten plik
  18. class UserModel extends ProjectBaseModel {
  19.    public function getUserById($id)
  20.    {
  21.        // $this->pdo ustawione w ProjectBaseModel, pdo pobrane
  22.        // z kontektu $this->context->getDatabaseConnection();
  23.        $statement = $this->pdo->prepare('SELECT *  FROM users WHERE id = ?');
  24.        $statement->execute(array($id));
  25.        return $statement->fetch(PDOFETCH_ASSOC);
  26.    }
  27. }
  28. ?>


Rozumiesz już? To jest po prostu wygodne - nie chce mi sie pisać własnej fabryki i tyle.
Zauważ jak prosto moge zrobić i testować refactoring (spróbuj przepisać Propelowy projekt w Symfony na Doctrine (bez sfDatabaseFindera) biggrin.gif).

Chciałem pokazać zastosowanie w autoloaderze czułości na środowisko i tyle - przydatna sprawa.
Sedziwoj
@LBO
Może za ostro zareagowałem, ale tak to napisałeś, więc lepiej opisuj problem, bo ja to zareagowałem tak, a inni mogą to zrozumieć opacznie i się nauczą :/
W tym przypadku wystarczy aby autoloader miał odpowiednią konfigurację, np. dodać ścieżki nie importowane, bo co ma przeglądać, a co nie powinno się skonfigurować.

A tak swoją drogą ktoś pisał globalny autoloader z namespace?

A tak swoją drogą to przydała by się abstrakcja nad dostęp do bazy, aby podmienić Propel/Doctrine/cokolwiek tak jak się podoba. Ale to chyba już byłby przerost abstrakcji.
LBO
Cytat(Sedziwoj @ 23.09.2008, 15:45:42 ) *
@LBO
Może za ostro zareagowałem, ale tak to napisałeś, więc lepiej opisuj problem, bo ja to zareagowałem tak, a inni mogą to zrozumieć opacznie i się nauczą :/


Ale czego się niby mają nauczyć? Nie sądzę, że każdą tezę muszę popierać kodem. Napisałem dokładnie to co chciałem napisać, że można sobie podmieniać klasy w zależności od okoliczności.

Cytat(Sedziwoj @ 23.09.2008, 15:45:42 ) *
W tym przypadku wystarczy aby autoloader miał odpowiednią konfigurację, np. dodać ścieżki nie importowane, bo co ma przeglądać, a co nie powinno się skonfigurować.


Nadal ręcoma i nogoma jestem, żeby autoloader pobierał tylko cache'a (czyli mapę). Od jej tworzenia powinno być osobne narzędzie.
A najlepiej samemu sobie taką mapę napisać - ma się wtedy najlepszą kontrolę. (Bo takie narzedzie to praktycznie osobna aplikacja, trzeba obsłuzyć różnego rodzaju wyjątki i problemy).

Cytat(Sedziwoj @ 23.09.2008, 15:45:42 ) *
A tak swoją drogą ktoś pisał globalny autoloader z namespace?


Z namespace? ciekawe - mógłbyś rozwinąć?

Cytat(Sedziwoj @ 23.09.2008, 16:31:22 ) *
A tak swoją drogą to przydała by się abstrakcja nad dostęp do bazy, aby podmienić Propel/Doctrine/cokolwiek tak jak się podoba. Ale to chyba już byłby przerost abstrakcji.


Jeżeli chodzi o Propela i Doctrine to już istnieje.
Sedziwoj
Cytat(LBO @ 23.09.2008, 16:37:00 ) *
Z namespace? ciekawe - mógłbyś rozwinąć?

coś o tym:
http://blog.dywicki.pl/2006/07/09/magiczne-namespace/
Cytat
Jeżeli chodzi o Propela i Doctrine to już istnieje.

Bardziej w kierunku składni Propela bym się kłaniał niż Doctrine... dla mnie jest czytelniejsza.
LBO
Cytat(Sedziwoj @ 23.09.2008, 16:52:45 ) *


Aaaaaa, że uwzględnia namespace PHPowe smile.gif Ja myslałem, że to nazwa jakiegos fjuczera w tym autorskim rozwiazaniu biggrin.gif

Cytat(Sedziwoj @ 23.09.2008, 16:52:45 ) *
Bardziej w kierunku składni Propela bym się kłaniał niż Doctrine... dla mnie jest czytelniejsza.


A ja jestem zupełnie temu przeciwny. Juz raz na forum to pisałem, że każda abstrakcja jest gorsza od tej leżacej pod nią (no bo tak jest niestety).
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.