Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: metoda wywoływana przed każdą metodą
Forum PHP.pl > Forum > PHP > Object-oriented programming
sazian
Witam,
wiem że temat brzmi w sposób nieco zagmatwany ale nie wiedziałem jak to inaczej nazwać.
O co chodzi postaram się wyjaśnić na przykładzie
  1. class klasa
  2. {
  3. public function before()
  4. {
  5.  
  6. }
  7. public function a()
  8. {
  9.  
  10. }
  11.  
  12. }
  13. $o = new klasa();
  14. $o->a();


czy istnieje możliwość aby przed wywołaniem metody "a" została niejawnie wywołana metoda "before" czyli kolejność wywołania wyglądała by tak before => a

idąc dalej,
  1. class klasa
  2. {
  3. public function before()
  4. {
  5.  
  6. }
  7. public function a()
  8. {
  9. $this->b();
  10. }
  11. public function b()
  12. {
  13.  
  14. }
  15.  
  16.  
  17. }
  18.  
  19. $o = new klasa();
  20. $o->a();


i tu kolejność wywołań miała by wyglądać tak before => a => before => b. A idąc jeszcze dalej, czy metoda before może być informowana jaka metoda będzie wywołana po niej?

jedyne co przychodzi mi do głowy to jakaś metoda call która przyjmie jako parametry nazwę metody i jej parametry ale takie rozwiązanie nie jest automatyczne i jeśli "się zapomni" użyć call to before nie będzie wywołany ;/

czy zrobienie czegoś takiego jest możliwe ?
adbacz
Z puktu widzenia PHP się nie da. Musiałbyś napisać klasę do zarządzania obiektem, któremu dawałbyś obiekt i nazwę metody, a on by uruchomił najpierw before() a później tą metodę, którą chcesz. Nie widze innego wyjścia, bo PHP sam w sobie nie daje takiej możliwości - no chyba daje, to niech ktoś mnie oświeci.

Z tą metodą call możesz popróbować tak, że każdą metodę będziesz nazywał na przykład __MyMethod(), ale metode obiektu będziesz wywoływał MyMethod(). W tedy wykona się metoda __call(), w której już sobie będziesz robił to co potrzebujesz, czyli na przykład:

  1. class klasa {
  2. public function __call($name, $params)
  3. {
  4. $name = str_replace('__', '', $name);
  5. $this->before(); //Tutaj możesz przesyłać jako jej parametr nazwę metody, którą uruchamiasz, lub zapisać nazwę we właściwości klasy.
  6. return call_user_func_array(array($this, $name), $params);
  7. }
  8. }


Oczywiście to kod "pisany na kolanie", ale powinno działać w Twoim przypadku. Minusem tego, jest fakt, że metodę __call() musiałbyś podawać za każdym razem do klasy, która ma działać w ten sam sposób - chyba, że załątwisz do dziedziczeniem.

Plusem napisania klasy do zarządzania obiektami, jak napisałem wcześniej, jest to, że prawie o nic nie musisz się martwić. Klasa nie musi posiadać metody before() ponieważ, możesz sprawdać istnienie tej metody w klasie do zarządzania obiektami. problemem może być fakt, że wywołanie takiej metody może być troszke dłuższe:

  1. $classManag = new ClassManag(new Klasa);
  2. $returnedValue = $classManag->call('a');
Crozin
Takie coś możliwe by było do wykonania przy pomocy AOP-a - aspekt ze zdefiniowanym pointcutem before call dla każdej metody obiektu - ale PHP nie jest językiem tego typu. Nie pozostaje Ci w sumie nic innego niż to co napisał @adbacz.
Jednak mnie bardziej zastanawia po co w ogóle Ci takie zachowanie? Możesz opisać dokładniej co chcesz tym wszystkim osiągnąć? Może jest inne, lepsze rozwiązanie.
sazian
dzięki za wyjaśnienia.

chcę zrobić coś w rodzaju do systemu uprawnień dla metod. Czyli przykładowo żeby wywołać daną metodę musisz być zalogowany, jeśli nie jesteś to zostanie rzucony wyjątek.
Oczywiście mógłbym zrobić po prostu
if(!zaogowany || za małe uprawnienia) {throw ....}

ale chciałem zrobić to bardziej automatycznie.

Obecnie w moim pseudo frameworku wygląda to miej więcej tak
  1. metoda before() - "pobiera" uprawnienia, inicjuje szablony layoutu,...
  2. metoda access(action_przykładowa_akcja) - metoda sprawdza czy mamy uprawnienia do uruchomienia metody której nazwa została przekazana jako parametr. Jeśli nie mamy uprawnień to rzuca wyjątek
  3. metoda action_przykładowa_akcja() -właściwa metoda, wywoływana jeśli nie było wyjątku
  4. lub metoda onError() jeśli był wyjątek(np. wyświetla komunikat o błędzie, wyświetla formularz logowania ...)
  5. metoda after() - wyświetla szablony, ...


i to działa ale jeśli w metodzie "action_przykładowa_akcja()" wywołam "action_przykładowa_akcja2()" to już uprawnienia dla tej drugiej nie są sprawdzane.


Chyba rzeczywiście najlepszym rozwiązaniem będzie to podane przez @adbacz, czyli dodanie prefixu wywoła call'a który najpierw "zrobi co trzeba".
No chyba że znacie lepsze rozwiązania, ja jestem otwarty na wszelkie propozycję.
Dipter
Poczytaj o ACL, zobacz jak to jest zrobione np. w Zendzie lub Symfony.

Przed wywołaniem akcji (metody) powinieneś sprawdzić czy dana osoba (gość/użytkownik/admin etc) ma uprawnienia do przeglądania danego modułu/kontrolera/akcji i jeśli nie to najlepiej przekierować bądź rzucić wyjątkiem, jeśli tak to odpalić tą akcje.
adbacz
Pewnie, że jest lepszy sposób. W Twoim przypadku, za każdym razem, gdy chciałbyś coś zmienić albo dodać, musiałbyś dodawać w wielu miejscach na raz. Dodatkowym problemem jest fakt, że gdy tworzysz nową klasę z akcjami, to musisz implementować to samo, cvzyli metodę _call() i jej ciało, metode before() oraz co tam jeszcze innego dodasz. Za duży śmietnik. Jak aplikacja CI się rozrośnie to będzie kiepskawo.

Dam Ci tylko podpowiedź jak ja mam to rozwiązane, resztę musisz sobie sam ogarnąć i podporządkować pod swój kod.

U mnie jest tak, że zanim jeszcze wykonam jakikolwiek konkontrolera, aplikacja wie, jaki kontroler i jaka akcja ma się wywoływać. Przed wywołaniem tejże akcji kierownictwo bierze u mnie zestaw klas odpowiadających za uprawnienia. Możesz sobie to zrobić na takiej zasadzie, że będziesz gdzieś trzymał info jaka ranga ma dostęp do jakich akcji danego kontrolera, a jaka nie. No i zestaw klas sprawdza aktualnego usera i akcję do wykonania. Resztę już chyba wiesz.

Takie rozwiązanie jest najlepsze chyba pod względem użytego kodu, i przy okazji nie klepiesz tego samego w każdej klasie, tylko przekazujesz odpowiednie parametry klasie dozwoleń, a ona robi co robi i zwraca czy dostęp jest dozwolony czy nie.
Crozin
Brzmi jak typowe zadanie właśnie dla AOP-u. W Wikipedii widzę jest odnośnik do kilku bibliotek dla PHP pozwalających niby na wprowadzenie tego paradygmatu: http://en.wikipedia.org/wiki/Aspect-orient...ng#cite_note-29 - jakiej jakości są one i czy w ogóle warte są czasu nie wiem, z żadnej nie korzystałem.

Jeżeli jednak będziesz chciał pozostać przy "normalnym" rozwiązaniu pod żadnym pozorem kodu sprawdzającego uprawnienia nie pchaj do klasy mającej robić główne zadanie aplikacji. Wykonaj sprawdzenie wszystkich koniecznych rzeczy przed ich wywołaniem.
sazian
@Dipter dzięki za linki, poczytam
@adbacz tylko że w php jest dziedziczenie wink.gif i wszystkie metody są pisane tylko raz, a uprawnienia mam zapisywane w tablicy w każdej klasie osobno, czyli dla każdej klasy muszę tylko zmieniać uprawnienia jeśli mają być inne niż normalnie



prosty przykład (wersja bardzo uproszczona )
  1. class Controller_c1 extends Controller
  2. {
  3. public $__ACCESS = array(
  4. '*'=>array('poziom dostępu'=>1,'kod błędu'=>1000),//uprawnienia domyślne dla wszystkich jeśli mamy poziom inny niż 1 to rzuć wyjątek o kodzie 1000
  5. 'action_f1'=>array('poziom dostępu'=>2,'kod błędu'=>1002),//uprawnienia dla metody action_f1, jeśli poziom inny niż 2 to rzuć wyjątek 1002
  6.  
  7. );
  8. public function action_f1(){}
  9. public function action_f2(){}
  10. }
  11.  
  12. class Controller_c2 extends Controller_c1
  13. {
  14. public function action_f3(){//ta metoda zostanie wywołana jeśli mamy uprawnienia na poziomie 1
  15. $this-> action_f2();
  16. }
  17. }

jedyny problem to dziedziczenie zmiennych, więc będę musiał to pewnie wydelegować do osobnej klasy.
A jeśli dobrze zrozumiałem to Twój sposób również nie rozwiąże "automatycznie" problemu przedstawionego w action_f3 - action_f3 ma inne uprawnienie niż f2 którą wywołuje


@Crozin z wynalazków wolę nie korzystać wink.gif fajne że koś robi coś w tym kierunku ale chyba jednak wolę nie ryzykować smile.gif

Cytat(Crozin @ 1.08.2012, 21:43:24 ) *
pod żadnym pozorem kodu sprawdzającego uprawnienia nie pchaj do klasy mającej robić główne zadanie aplikacji. Wykonaj sprawdzenie wszystkich koniecznych rzeczy przed ich wywołaniem.


chyba nie do końca rozumiałem sad.gif
w dużym skrócie
mam klasę nadrzędna dla wszystkich kontrolerów

  1. abstract class Controller
  2. {
  3. public function before(){}
  4. public function _access($method){}
  5. public function after(){}
  6. public function onError($exception){}
  7. }


tych metod praktycznie nie muszę ruszać, zawszę robią to samo - no może za wyjątkiem onError.
i teraz aplikacja jest uruchamiana miej więcej tak(zaznaczam że jest to pseudo kod mający pokazać tylko schemat działania)
  1.  
  2. $obiekt->before();
  3. try{
  4. $obiekt->_access('action_nazwa_akcji');
  5. $obiekt->action_nazwa_akcji();
  6. }
  7. catch(ExceptionAccess $e)
  8. {
  9. $obiekt->onError($e);
  10.  
  11. }
  12.  
  13. $obiekt->after();
  14.  

czyli sam wyjątek jest rzucany przez klasę i przez tę samą klasę jest obsługiwany;

może dodam że wszystkie wywołania są robione na zewnątrz klasy kontrolera przy pomocy klasy ReflectionClass
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.