Prowadzę debatę z kolegą na temat prawidłowego użycia (jeśli w ogóle) trigger_error
w kontekście magicznych metod . Po pierwsze, uważam, że trigger_error
należy tego unikać, z wyjątkiem tego jednego przypadku.
Powiedzmy, że mamy klasę z jedną metodą foo()
class A {
public function foo() {
echo 'bar';
}
}
Powiedzmy teraz, że chcemy zapewnić dokładnie ten sam interfejs, ale użyj magicznej metody, aby przechwycić wszystkie wywołania metod
class B {
public function __call($method, $args) {
switch (strtolower($method)) {
case 'foo':
echo 'bar';
break;
}
}
}
$a = new A;
$b = new B;
$a->foo(); //bar
$b->foo(); //bar
Obie klasy są takie same pod względem odpowiedzi, foo()
ale różnią się przy wywoływaniu niepoprawnej metody.
$a->doesntexist(); //Error
$b->doesntexist(); //Does nothing
Moim argumentem jest, że magiczne metody powinny wywoływać, trigger_error
gdy zostanie złapana nieznana metoda
class B {
public function __call($method, $args) {
switch (strtolower($method)) {
case 'foo':
echo 'bar';
break;
default:
$class = get_class($this);
$trace = debug_backtrace();
$file = $trace[0]['file'];
$line = $trace[0]['line'];
trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR);
break;
}
}
}
Tak, aby obie klasy zachowywały się (prawie) identycznie
$a->badMethod(); //Call to undefined method A::badMethod() in [..] on line 28
$b->badMethod(); //Call to undefined method B::badMethod() in [..] on line 32
Mój przypadek użycia to implementacja ActiveRecord. Używam __call
do chwytania i obsługi metod, które zasadniczo robią to samo, ale mają modyfikatory takie jak Distinct
lub Ignore
np
selectDistinct()
selectDistinctColumn($column, ..)
selectAll()
selectOne()
select()
lub
insert()
replace()
insertIgnore()
replaceIgnore()
Metody takie jak where()
, from()
, groupBy()
, itd. Są zakodowane.
Mój argument jest podkreślony, gdy przypadkowo dzwonisz insret()
. Gdyby moja aktywna implementacja rekordu zapisała wszystkie metody na stałe, byłby to błąd.
Jak w przypadku każdej dobrej abstrakcji, użytkownik powinien nie znać szczegółów implementacji i polegać wyłącznie na interfejsie. Dlaczego implementacja korzystająca z magicznych metod powinna zachowywać się inaczej? Oba powinny być błędem.
źródło
4.something
?__call()
do rutowania dynamicznego, czy naprawdę tak nierozsądne jest oczekiwanie, że gdzieś na ścieżce ktoś będzie chciał zająć się sprawą, w której się to nie powiedzie? W każdym razie dzieje się to w kółko, więc to będzie mój ostatni komentarz. Rób co chcesz, pod koniec dnia sprowadza się to do osądu: lepsze wsparcie kontra konsekwencja. Obie metody będą miały taki sam wpływ na aplikację w przypadku braku specjalnego obchodzenia się.Zamierzam opublikować moją opinię, ale jeśli używasz
trigger_error
gdziekolwiek, to robisz coś złego. Wyjątki to droga.Zalety wyjątków:
W odpowiedzi na Twoje obawy wywołanie metody, która nie istnieje, może być uzasadnioną możliwością . Zależy to całkowicie od kontekstu pisanego kodu, ale w niektórych przypadkach może się to zdarzyć. Reagując na konkretny przypadek użycia, niektóre serwery baz danych mogą oferować pewne funkcje, których inne nie. Używanie
try
/catch
i wyjątków w__call()
funkcji w celu sprawdzenia możliwości to zupełnie inny argument.Jedynym przypadkiem użycia, który mogę wymyślić,
trigger_error
jest użycieE_USER_WARNING
lub niższe. WywołanieE_USER_ERROR
choć jest zawsze moim zdaniem błędem.źródło
trigger_error
w kontekście __call lub __callStatic naśladuje domyślne zachowanie językaNoMethodError
który możesz złapać, jeśli chcesz. Błędy są moim zdaniem ogromnym błędem w PHP. To, że rdzeń używa zepsutej metody do zgłaszania błędów, nie oznacza, że powinien to zrobić Twój własny kod.Standardowe błędy PHP należy uznać za przestarzałe. PHP zapewnia wbudowaną klasę ErrorException do konwertowania błędów, ostrzeżeń i powiadomień na wyjątki z pełnym odpowiednim śledzeniem stosu. Używasz go w ten sposób:
Dzięki temu pytanie staje się dyskusyjne. Wbudowane błędy wywołują teraz wyjątki, więc twój kod również powinien.
źródło
E_NOTICE
s w wyjątki. To by było złe.IMO, jest to całkowicie uzasadniony przypadek użycia dla
trigger_error
:Korzystając z tej strategii, otrzymujesz
$errcontext
parametr, jeśli wykonujesz$exception->getTrace()
tę funkcjęhandleException
. Jest to bardzo przydatne do niektórych celów debugowania.Niestety działa to tylko wtedy, gdy używasz
trigger_error
bezpośrednio z kontekstu, co oznacza, że nie możesz użyć funkcji / metody opakowania do aliasutrigger_error
funkcji (więc nie możesz zrobić czegoś takiego,function debug($code, $message) { return trigger_error($message, $code); }
jeśli chcesz, aby dane kontekstu były w twoim śladzie).Szukałem lepszej alternatywy, ale jak dotąd nie znalazłem żadnej.
źródło