Czy powinienem używać assert w moim kodzie PHP?

87

Współpracownik kilka razy dodał polecenie assert w naszych bibliotekach w miejscach, w których użyłbym instrukcji if i wyrzucił wyjątek. (Nigdy wcześniej nie słyszałem o twierdzeniu). Oto przykład, jak go użył:

assert('isset($this->records); /* Records must be set before this is called. */');

Zrobiłbym:

if (!isset($this->records)) {
    throw new Exception('Records must be set before this is called');
}

Po przeczytaniu dokumentacji PHP na temat assert wygląda na to, że zaleca się upewnienie się, że funkcja assert jest aktywna i dodanie obsługi przed użyciem assert. Nie mogę znaleźć miejsca, w którym to zrobił.

Więc moje pytanie brzmi, czy użycie assert jest dobrym pomysłem, biorąc pod uwagę powyższe i czy powinienem używać go częściej niż jeśli i wyjątki?

Kolejna uwaga, planujemy używać tych bibliotek w różnych projektach i serwerach, w tym w projektach, których być może nawet nie jesteśmy częścią (biblioteki są open source). Czy to ma znaczenie w używaniu assert?

Darryl Hein
źródło
Czy to naprawdę 'isset(wiersz kodu z assert)? Nie tylko isset(bez pojedynczego cudzysłowu ')?
Peter Mortensen,

Odpowiedzi:

79

Praktyczna zasada, która ma zastosowanie w większości języków (wszystko, co niejasno wiem) jest taka assert, że warunek jest używany do stwierdzenia, że ​​warunek jest zawsze prawdziwy, podczas gdy warunek ifjest odpowiedni, jeśli można sobie wyobrazić, że czasami zawiedzie.

W tym przypadku powiedziałbym, że assertjest to właściwe (kierując się moim słabym zrozumieniem sytuacji), ponieważ zawszerecords powinno być ustawione przed wywołaniem danej metody. Tak więc niepowodzenie w ustawieniu rekordu byłoby raczej błędem w programie niż warunkiem wykonania. W tym przypadku pomaga zapewnić (przy odpowiednim testowaniu), że nie ma możliwej ścieżki wykonania programu, która mogłaby spowodować, że kod, który jest chroniony za pomocą elementu, zostanie wywołany bez wcześniejszego ustawienia.assertassertrecords

Zaletą używania assertw przeciwieństwie do iftego jest to, że assertmożna go ogólnie wyłączyć w kodzie produkcyjnym, zmniejszając w ten sposób narzut. Tego rodzaju sytuacje, z którymi najlepiej sobie ifradzi, mogą prawdopodobnie wystąpić podczas pracy w systemie produkcyjnym, więc nie można ich wyłączyć, nic tracąc.

aaronasterling
źródło
4
Aby to dodać, możesz nie chcieć wyłączać asercji w swoim kodzie produkcyjnym, ponieważ pomagają one upewnić się, że te warunki „tak się nigdy nie wydarzy” pozostają takie. Lepszym rozwiązaniem może być zatrzymanie aplikacji przed potwierdzeniem, niż pozwolenie użytkownikom na kontynuowanie ścieżki wykonania, która nie powinna istnieć.
derekerdmann
2
@derekerdmann: Prawda. W przypadku niektórych stwierdzeń wystarczy je zarejestrować (w środowisku produkcyjnym) lub wydrukować ostrzeżenie (w środowisku programistycznym). Ale ponieważ często zapewnia również ochronę kodu związanego z bezpieczeństwem, równie dobrze możesz włączyć assert_options(ASSERT_BAIL). Jest to i tak szybsze niż ręczne obejście if / throw.
mario
4
@derekerdmann Nie zgodziłbym się z tym (w kontekście używania assert () w php). Jest to duża luka w zabezpieczeniach, ponieważ funkcja assert () traktuje wszystkie argumenty łańcuchowe jako kod PHP, więc (teoretycznie) możliwe jest wstrzyknięcie i uruchomienie dowolnego kodu. IMHO, asercje powinny zostać wyłączone na produkcji
Vitaliy Lebedev
2
@VitaliyLebedev jeśli nie chcesz być podatny na wstrzyknięcie, nie przekazuj ciągów znaków do potwierdzenia.
Damon Snyder
9
Spóźniłem się na imprezę, ale PHP.net stwierdza: "Asercje powinny być używane tylko jako funkcja debugowania."
Koen.
25

Pomyśl o zapewnieniach jako o „komentarzach władzy”. Zamiast komentarza takiego jak:

// Note to developers: the parameter "a" should always be a number!!!

posługiwać się:

assert('is_numeric(a) /* The parameter "a" should always be a number. */');

Znaczenia są dokładnie takie same i są przeznaczone dla dokładnie tej samej publiczności, ale pierwszy komentarz jest łatwo zapomniany lub zignorowany (bez względu na to, ile wykrzykników), podczas gdy „komentarz mocy” jest dostępny nie tylko dla ludzi do przeczytania i zrozumienia, jest również stale testowany maszynowo podczas opracowywania i nie zostanie zignorowany, jeśli skonfigurujesz dobrą obsługę asercji w kodzie i nawykach pracy.

Widziane w ten sposób twierdzenia są zupełnie inną koncepcją niż gdyby (błąd) ... i wyjątkami, i mogą współistnieć.

Tak, powinieneś komentować swój kod i tak, powinieneś używać „power comments” (potwierdzenia), kiedy tylko jest to możliwe.

DaveWalley
źródło
co się stanie, jeśli podczas programowania podczas testowania zawsze przekazujesz warunek dobry do potwierdzenia, ale w środowisku produkcyjnym, jeśli asercja jest wyłączona - jakiś użytkownik przejdzie inny warunek, o którym nie pomyślałeś podczas testowania? Albo musisz mieć zawsze włączone potwierdzenie, ale czy to nie to samo, co wypisanie własnego czeku?
Darius.V
Wtedy twój program zawiedzie. Napraw to poprawnie za pomocą instrukcji if oraz funkcji obsługi błędów w Twoim języku i środowisku programistycznym. asserts może odkryć problemy, istnieją lepsze sposoby ich rozwiązywania.
DaveWalley
Zauważ, że od PHP 7.2 przekazywanie ciągu znaków do assert w celu oceny jest przestarzałe. Smutne, bo wyglądało to całkiem poręcznie.
Jannie Theunissen
16

Zależy to całkowicie od Twojej strategii rozwoju. Większość programistów nie zdaje sobie sprawy z assert()testów jednostkowych podrzędnych i stosuje je. Ale proaktywne i wbudowane schematy testowania mogą czasami być korzystne.

assert jest przydatne, ponieważ można je włączać i wyłączać. Nie obniża wydajności, jeśli nie zdefiniowano takiej obsługi potwierdzeń. Twój kolega go nie ma i powinieneś opracować jakiś kod, który tymczasowo włącza go w środowisku programistycznym (jeśli E_NOTICE / E_WARNINGs są włączone, więc powinien być program obsługi asercji). Używam go od czasu do czasu, gdy mój kod nie może znieść mieszanych typów zmiennych - zwykle nie angażuję się w ścisłe pisanie w słabo wpisanym PHP, ale są przypadkowe przypadki użycia:

 function xyz($a, $b) {
     assert(is_string($a));
     assert(is_array($b));

Co na przykład zrekompensowałoby brak specyfikatorów typu string $a, array $b. PHP5.4 będzie je obsługiwać, ale nie będzie sprawdzać.

mario
źródło
Co oznacza „php 5.4 będzie je mieć, ale nie będzie sprawdzać”?
Kzqai
1
PHP 5.4 posiada, obsługuje i sprawdza potwierdzenia.
DaveWalley,
7

Assert nie zastępuje normalnej kontroli przepływu, takiej jak iflub wyjątków, ponieważ jest przeznaczona tylko do debugowania podczas programowania.

Mark Snidovich
źródło
6

Ważna uwaga dotycząca assert w PHP wcześniejszych niż 7. W przeciwieństwie do innych języków z konstrukcją assert, PHP nie wyrzuca całkowicie instrukcji assert - traktuje je jako funkcję (wykonaj debug_backtrace () w funkcji wywoływanej przez asercję). Wyłączenie potwierdzeń wydaje się po prostu przekręcać funkcję tak, aby nie robiła nic w silniku. Zauważ, że PHP 7 można zmusić do emulacji tego zachowania, ustawiając zend.assertions na 0 zamiast bardziej normalnych wartości 1 (włączone) lub -1 (wyłączone).

Problem pojawia się w tym, że assert przyjmie dowolny argument - ale jeśli argument nie jest łańcuchem, wtedy assert pobiera wyniki wyrażenia, niezależnie od tego, czy assert jest włączony, czy wyłączony. Możesz to zweryfikować za pomocą następującego bloku kodu.

<?php
  function foo($a) { 
    echo $a . "\n"; 
    return TRUE;
  }
  assert_options(ASSERT_ACTIVE, FALSE);

  assert( foo('You will see me.'));
  assert('foo(\'You will not see me.\')');

  assert_options(ASSERT_ACTIVE, TRUE);

  assert( foo('Now you will see'));
  assert('foo(\'both of us.\')');

Biorąc pod uwagę zamiar assert, jest to błąd, i to od dawna, odkąd pojawił się w tym języku od czasu wprowadzenia assert w PHP 4.

Ciągi przekazywane do assert są poddawane ocenie, ze wszystkimi implikacjami wydajnościowymi i niebezpieczeństwami, które się z tym wiążą, ale jest to jedyny sposób, aby instrukcje assert działały tak, jak powinny w PHP (to zachowanie jest przestarzałe w PHP 7.2).

EDYCJA: Zmieniono powyżej, aby odnotować zmiany w PHP 7 i 7.2

Michael Morris
źródło
1
W PHP 7 jest / będzie zend.assertionsustawienie ini, aby całkowicie wyłączyć assert().
Kontrollfreak,
To doskonała wiadomość - ale na podstawie dokumentacji tamtejszej wygląda na to, że łatka do PHPUnit jest uporządkowana, dodając moduł obsługi wywołań zwrotnych assert, który wyrzuca AssertionException, gdy asercje zawiodą w PHP 5.x. W ten sposób testy jednostkowe mogą używać adnotacji @expectedException AssertionException niezależnie od tego, czy działają na PHP 5.x czy 7.
Michael Morris
3

Assert należy używać tylko w programowaniu, ponieważ jest przydatne do debugowania. Więc jeśli chcesz, możesz ich użyć do rozwijania swojej witryny, ale powinieneś używać wyjątków dla działającej witryny.

Kyle
źródło
7
Ale nadal będzie istnieć potwierdzenie w kodzie. Po prostu nie będą działać w środowisku produkcyjnym.
aaronasterling
1
Zatrzymałbym je w produkcji i zamiast tego odpowiednio zmodyfikowałbym mój moduł obsługi błędów.
Daniel W.
3

Nie, Twój współpracownik nie powinien używać go do obsługi błędów ogólnego przeznaczenia. Zgodnie z instrukcją:

Asercje powinny być używane tylko jako funkcja debugowania. Możesz ich używać do sprawdzania poprawności, które sprawdzają warunki, które zawsze powinny być PRAWDZIWE i które wskazują pewne błędy programistyczne, jeśli nie, lub do sprawdzania obecności pewnych funkcji, takich jak funkcje rozszerzeń lub pewne ograniczenia i funkcje systemu.

Asercji nie należy używać do normalnych operacji w czasie wykonywania, takich jak sprawdzanie parametrów wejściowych. Z reguły kod powinien zawsze działać poprawnie, jeśli sprawdzanie asercji nie jest włączone.

Jeśli jesteś zaznajomiony z automatycznymi zestawami testów, czasownik „assert” jest zwykle używany do weryfikacji wyniku jakiejś metody lub funkcji. Na przykład:

function add($a, $b) {
    return $a + $b;
}

assert(add(2,2) == 5, 'Two and two is four, dummy!');
assert(is_numeric(add(2,2)), 'Output of this function to only return numeric values.');

Twój współpracownik nie powinien używać go do obsługi błędów ogólnego przeznaczenia, aw tym przypadku do sprawdzania danych wejściowych. Wygląda na to, że pole rekordów nie zostało ustawione przez jakiegoś użytkownika biblioteki.

Dean Or
źródło
3

Twój współpracownik naprawdę próbuje zastosować projekt na podstawie umowy (DbC) z języka Eiffla i na podstawie książki: Object Oriented Software Construction, 2nd Edition.

Stwierdzenie, jak go użył, byłoby {P} -częścią Hoare Logic lub Hoare Triple: {P} C {Q}, gdzie {P} jest asertem (jonami) warunku wstępnego i {Q} są asercja (jon) warunku końcowego.

Chciałbym wziąć krytyczną uwagę na porady dotyczące funkcji assert w PHP zawierającej błędy. Nie chcesz używać błędnego kodu. To, czego naprawdę chcesz, to twórcy PHP, aby naprawić błąd w asercie. Dopóki tego nie zrobią, możesz używać potwierdzenia, ale używaj go, pamiętając o jego obecnym stanie błędnym.

Ponadto, jeśli funkcja assert zawiera błędy, sugeruję, aby nie używać jej w kodzie produkcyjnym. Niemniej jednak zalecam używanie go w razie potrzeby podczas programowania i testowania kodu.

Wreszcie - jeśli przeanalizujesz projekt na podstawie kontraktu, przekonasz się, że stosowanie twierdzeń boolowskich w świetle klasycznego dziedziczenia zorientowanego obiektowo - to znaczy - nigdy nie wolno osłabiać warunku wstępnego ani warunku końcowego. Może to być niebezpieczne dla twoich polimorficznych obiektów potomnych oddziałujących ze sobą. Dopóki nie zrozumiesz, co to znaczy - zostawię to w spokoju!

Co więcej - bardzo polecam twórcom PHP dokładne zbadanie projektu na zlecenie i próbę umieszczenia go w PHP jak najszybciej! Wtedy każdy z nas może skorzystać na posiadaniu kompilatora / interpretera obsługującego DbC, który poradziłby sobie z problemami wymienionymi w odpowiedziach (powyżej):

  1. Prawidłowo zaimplementowany kompilator obsługujący projektowanie według kontraktu byłby (miejmy nadzieję) wolny od błędów (w przeciwieństwie do obecnego assert PHP).
  2. Prawidłowo zaimplementowany kompilator obsługujący projektowanie według kontraktu poradziłby sobie z niuansami zarządzania polimorficzną logiką asercji, zamiast męczyć twój mózg nad tą sprawą!

UWAGA: Nawet użycie oświadczenia ifjako substytutu potwierdzenia (warunku wstępnego) będzie miało tragiczne konsekwencje, jeśli zostanie użyte do wzmocnienia warunku wstępnego lub osłabienia warunku końcowego. Aby zrozumieć, co to oznacza, musisz przestudiować projektowanie na podstawie umowy, aby wiedzieć! :-)

Miłej nauki i nauki.

Larry
źródło