Sollte ich assert in meinem PHP-Code verwenden?

87

Ein Mitarbeiter hat den Befehl assert einige Male in unseren Bibliotheken an Stellen hinzugefügt, an denen ich eine if-Anweisung verwendet und eine Ausnahme ausgelöst hätte. (Ich hatte vorher noch nie davon gehört.) Hier ist ein Beispiel, wie er es benutzt hat:

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

Ich hätte es getan:

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

Nach dem Lesen der PHP-Dokumente zu assert wird empfohlen, sicherzustellen, dass assert aktiv ist, und einen Handler hinzuzufügen, bevor Sie assert verwenden. Ich kann keinen Ort finden, an dem er das getan hat.

Meine Frage ist also, ob die Verwendung von assert angesichts der obigen Ausführungen eine gute Idee ist und ob ich sie häufiger verwenden sollte, anstatt ob und Ausnahmen?

Ein weiterer Hinweis: Wir planen, diese Bibliotheken für eine Vielzahl von Projekten und Servern zu verwenden, einschließlich Projekten, an denen wir möglicherweise nicht einmal beteiligt sind (die Bibliotheken sind Open Source). Macht dies einen Unterschied bei der Verwendung von assert?

Darryl Hein
quelle
Ist es wirklich 'isset(die Codezeile mit assert)? Nicht nur isset(ohne das einfache Anführungszeichen ')?
Peter Mortensen

Antworten:

79

Die Faustregel, die für die meisten Sprachen gilt (alles, was ich vage weiß), lautet, dass eine assertverwendet wird, um zu behaupten, dass eine Bedingung immer wahr ist, während eine ifangemessen ist, wenn es denkbar ist, dass sie manchmal fehlschlägt.

In diesem Fall würde ich sagen, dass dies assertangemessen ist (basierend auf meinem schwachen Verständnis der Situation), da recordses immer festgelegt werden sollte, bevor die angegebene Methode aufgerufen wird. Ein Fehler beim Festlegen des Datensatzes wäre also eher ein Fehler im Programm als eine Laufzeitbedingung. Hier asserthilft das sicherzustellen (mit angemessenen Tests), dass es keinen möglichen Programmausführungspfad gibt, der dazu führen könnte, dass der Code, der mit dem geschützt wird assert, aufgerufen wird, ohne recordsfestgelegt worden zu sein.

Der Vorteil der Verwendung assertim Gegensatz zu ifist, dass assertsie im Produktionscode im Allgemeinen deaktiviert werden kann, wodurch der Overhead reduziert wird. Die Art von Situationen, mit denen am besten umgegangen werden kann, ifkann möglicherweise zur Laufzeit im Produktionssystem auftreten, sodass nichts verloren geht, wenn sie nicht ausgeschaltet werden können.

aaronasterling
quelle
4
Um dies hinzuzufügen, möchten Sie möglicherweise die Zusicherungen in Ihrem Produktionscode nicht deaktivieren, da sie dazu beitragen, dass diese Bedingungen "Dies sollte niemals passieren" so bleiben. Es ist möglicherweise besser, Ihre Anwendung von einem Assert abhalten zu lassen, als Ihre Benutzer auf einem Ausführungspfad weiterlaufen zu lassen, der nicht existieren sollte.
Derekkmann
2
@derekerdmann: Stimmt. Für einige Zusicherungen kann es ausreichend sein, sie zu protokollieren (in der Produktion) oder die Warnung auszudrucken (in der Entwicklungsumgebung). Da Asserts jedoch häufig auch sicherheitsrelevanten Code schützen, können Sie ihn auch aktivieren assert_options(ASSERT_BAIL). Es ist sowieso schneller als manuelle Problemumgehungen.
Mario
4
@derekerdmann Ich würde dem nicht zustimmen (im Zusammenhang mit der Verwendung von assert () in PHP). Dies ist eine große Sicherheitslücke, da assert () alle Zeichenfolgenargumente als PHP-Code behandelt und es daher (theoretisch) möglich ist, beliebigen Code einzufügen und auszuführen. IMHO, Behauptungen sollten auf Produktion abgeschaltet werden
Vitaliy Lebedev
2
@VitaliyLebedev Wenn Sie nicht anfällig für Injektionen sein möchten, übergeben Sie keine Zeichenfolgen, um dies zu bestätigen.
Drsnyder
9
Spät zur Party, aber PHP.net sagt: "Assertions sollten nur als Debugging-Funktion verwendet werden."
Koen.
25

Stellen Sie sich Behauptungen als "Machtkommentare" vor. Anstelle eines Kommentars wie:

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

verwenden:

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

Die Bedeutungen sind genau gleich und für genau das gleiche Publikum bestimmt, aber der erste Kommentar kann leicht vergessen oder ignoriert werden (egal wie viele Ausrufezeichen), während der "Machtkommentar" nicht nur für Menschen zum Lesen und Verstehen verfügbar ist. Es wird auch während der Entwicklung ständig maschinell getestet und nicht ignoriert, wenn Sie eine gute Assert-Behandlung im Code und in den Arbeitsgewohnheiten einrichten.

So gesehen sind Asserts ein völlig anderes Konzept als if (error) ... und Ausnahmen, und sie können nebeneinander existieren.

Ja, Sie sollten Ihren Code kommentieren, und ja, Sie sollten nach Möglichkeit "Power-Kommentare" (Asserts) verwenden.

DaveWalley
quelle
Was ist, wenn Sie bei der Entwicklung beim Testen immer einen guten Zustand an den Assert übergeben, in der Produktion jedoch, wenn der Assert deaktiviert ist - ein Benutzer übergibt einen anderen Zustand, an den Sie beim Testen nicht gedacht haben? Oder sonst müssen Sie immer behaupten, aber ist es dann nicht dasselbe wie das Ausstellen Ihres eigenen Schecks?
Darius.V
Dann schlägt Ihr Programm fehl. Beheben Sie das Problem ordnungsgemäß mit if-Anweisungen und den Fehlerbehandlungsfunktionen Ihrer Sprache und Entwicklungsumgebung. Behauptungen können Probleme aufdecken, es gibt bessere Möglichkeiten, Probleme zu beheben.
DaveWalley
Beachten Sie, dass ab PHP 7.2 die Übergabe einer Zeichenfolge an assert zur Auswertung veraltet ist. Traurig, weil es ziemlich praktisch aussah.
Jannie Theunissen
16

Es hängt ganz von Ihrer Entwicklungsstrategie ab. Die meisten Entwickler kennen assert()und verwenden Downstream-Unit-Tests nicht. Proaktive und integrierte Testschemata können jedoch manchmal vorteilhaft sein.

assert ist nützlich, da es aktiviert und deaktiviert werden kann. Die Leistung wird nicht beeinträchtigt, wenn kein solcher Assertion-Handler definiert ist. Ihr Kollege hat keinen, und Sie sollten einen Code entwickeln, der ihn vorübergehend in der Entwicklungsumgebung aktiviert (wenn E_NOTICE / E_WARNINGs aktiviert sind, sollte dies auch der Assertion-Handler sein). Ich benutze es gelegentlich, wenn mein Code gemischte Variablentypen nicht ertragen kann - ich schreibe normalerweise nicht streng in einem schwach typisierten PHP, aber es gibt zufällige Anwendungsfälle:

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

Dies würde beispielsweise das Fehlen von Typspezifizierern ausgleichen string $a, array $b. PHP5.4 wird sie unterstützen, aber nicht überprüfen.

Mario
quelle
Was bedeutet "PHP 5.4 wird sie haben, aber nicht prüfen"?
Kzqai
1
PHP 5.4 hat, unterstützt und prüft Asserts.
DaveWalley
7

Assert ist kein Ersatz für normale Flusskontrollen ifoder Ausnahmen, da es nur zum Debuggen während der Entwicklung verwendet werden soll.

Mark Snidovich
quelle
6

Ein wichtiger Hinweis zum Assert in PHP vor 7. Im Gegensatz zu anderen Sprachen mit einem Assert-Konstrukt wirft PHP Assert-Anweisungen nicht vollständig aus - es behandelt sie als Funktion (do debug_backtrace () in einer Funktion, die von einer Assertion aufgerufen wird). Das Ausschalten von Asserts scheint die Funktion nur dazu zu verdrahten, nichts im Motor zu tun. Beachten Sie, dass PHP 7 dieses Verhalten emulieren kann, indem zend.assertions auf 0 gesetzt wird, anstatt auf die normaleren Werte 1 (ein) oder -1 (aus).

Das Problem tritt darin auf, dass assert ein beliebiges Argument akzeptiert. Wenn das Argument jedoch keine Zeichenfolge ist, erhält assert die Ergebnisse des Ausdrucks, unabhängig davon, ob assert aktiviert oder deaktiviert ist. Sie können dies mit dem folgenden Codeblock überprüfen.

<?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.\')');

In Anbetracht der Absicht, dies zu behaupten, handelt es sich um einen Fehler, der seit langem in der Sprache ist, seit Assert in PHP 4 eingeführt wurde.

An Assert übergebene Zeichenfolgen werden mit allen damit verbundenen Auswirkungen auf die Leistung und den damit verbundenen Risiken bewertet. Dies ist jedoch die einzige Möglichkeit, Assert-Anweisungen so zu verwenden, wie sie in PHP funktionieren sollten (dieses Verhalten ist in PHP 7.2 veraltet).

BEARBEITEN: Oben geändert, um Änderungen in PHP 7 und 7.2 zu beachten

Michael Morris
quelle
1
In PHP 7 gibt es eine zend.assertionsIni-Einstellung, um sie vollständig auszuschalten assert().
Kontrollfreak
Das sind hervorragende Neuigkeiten - aber basierend auf der Dokumentation sieht es so aus, als wäre ein Patch für PHPUnit in Ordnung. Fügen Sie einen Assert-Callback-Handler hinzu, um AssertionException auszulösen, wenn Assertions unter PHP 5.x fehlschlagen. Auf diese Weise können Unit-Tests die Annotation @expectedException AssertionException verwenden, unabhängig davon, ob sie unter PHP 5.x oder 7 ausgeführt werden.
Michael Morris
3

Assert sollte nur in der Entwicklung verwendet werden, da es für das Debuggen nützlich ist. Wenn Sie möchten, können Sie sie für die Entwicklung Ihrer Website verwenden. Sie sollten jedoch Ausnahmen für eine Live-Website verwenden.

Kyle
quelle
7
Aber man wird immer noch die Behauptungen im Code haben. Sie werden in einer Produktionsumgebung einfach nicht aktiv sein.
Aaronasterling
1
Ich würde sie in der Produktion behalten und stattdessen meinen Fehlerbehandler entsprechend anpassen.
Daniel W.
3

Nein, Ihr Mitarbeiter sollte es nicht als allgemeine Fehlerbehandlungsroutine verwenden. Nach dem Handbuch:

Zusicherungen sollten nur als Debugging-Funktion verwendet werden. Sie können sie für Sanity-Checks verwenden, die auf Bedingungen prüfen, die immer WAHR sein sollten, und auf Programmierfehler hinweisen, wenn dies nicht der Fall ist, oder um das Vorhandensein bestimmter Funktionen wie Erweiterungsfunktionen oder bestimmter Systemgrenzen und -funktionen zu überprüfen.

Zusicherungen sollten nicht für normale Laufzeitoperationen wie die Überprüfung von Eingabeparametern verwendet werden. Als Faustregel sollte Ihr Code immer korrekt funktionieren können, wenn die Assertionsprüfung nicht aktiviert ist.

Wenn Sie mit automatisierten Testsuiten vertraut sind, wird im Allgemeinen das Verb "assert" verwendet, um die Ausgabe einer Methode oder Funktion zu überprüfen. Beispielsweise:

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.');

Ihr Mitarbeiter sollte es nicht als allgemeine Fehlerbehandlungsroutine und in diesem Fall als Eingabeprüfung verwenden. Es sieht so aus, als ob das Datensatzfeld möglicherweise nicht von einem Benutzer Ihrer Bibliothek festgelegt wurde.

Dean Or. En
quelle
3

Ihr Mitarbeiter versucht wirklich, Design by Contract (DbC) aus der Eiffel-Sprache anzuwenden und basiert auf dem Buch: Object Oriented Software Construction, 2nd Edition.

Die Behauptung, wie er sie benutzte, wäre der {P} -Teil der Hoare-Logik oder des Hoare-Dreifach: {P} C {Q}, wobei {P} die Voraussetzung für die Behauptung (ion) s und {Q} ist die Nachbedingung behauptet (Ion) s.

Ich würde die Ratschläge zur Assert-Funktion in PHP mit Fehlern kritisch zur Kenntnis nehmen. Sie möchten keinen fehlerhaften Code verwenden. Was Sie wirklich wollen, sind die Hersteller von PHP, um den Fehler in der Behauptung zu beheben. Bis dahin können Sie den Assert verwenden, ihn jedoch unter Berücksichtigung des aktuellen Buggy-Status verwenden.

Wenn die Assert-Funktion fehlerhaft ist, sollten Sie sie nicht im Produktionscode verwenden. Trotzdem empfehle ich, dass Sie es gegebenenfalls in Entwicklungs- und Testcode verwenden.

Wenn Sie schließlich eine Vertragsstudie zum Entwurf durchführen, werden Sie feststellen, dass die Verwendung von Booleschen Behauptungen im Lichte der objektorientierten klassischen Vererbung Konsequenzen hat - das heißt, Sie dürfen niemals eine Vorbedingung oder eine Nachbedingung schwächen. Dies könnte für Ihre polymorphen Nachkommenobjekte, die miteinander interagieren, gefährlich sein. Bis Sie verstehen, was das bedeutet - ich würde es in Ruhe lassen!

Darüber hinaus empfehle ich den Herstellern von PHP dringend, eine umfassende Designstudie im Auftrag durchzuführen und zu versuchen, sie so schnell wie möglich in PHP zu integrieren! Dann können wir alle von einem DbC-fähigen Compiler / Interpreter profitieren, der die in den Antworten (oben) genannten Probleme behandelt:

  1. Ein ordnungsgemäß implementierter Design-by-Contract-fähiger Compiler wäre (hoffentlich) fehlerfrei (im Gegensatz zur aktuellen PHP-Behauptung).
  2. Ein ordnungsgemäß implementierter Design-by-Contract-fähiger Compiler würde die Nuancen des polymorphen Assertion-Logic-Managements für Sie übernehmen, anstatt Ihr Gehirn über die Angelegenheit zu beunruhigen!

HINWEIS: Selbst die Verwendung einer ifAnweisung als Ersatz für die Behauptung (Vorbedingung) hat schwerwiegende Folgen, wenn sie zur Stärkung einer Vorbedingung oder zur Schwächung einer Nachbedingung verwendet wird. Um zu verstehen, was das bedeutet, müssen Sie Design vertraglich studieren, um es zu wissen! :-)

Viel Spaß beim Lernen und Lernen.

Larry
quelle