Dois-je utiliser assert dans mon code PHP?

87

Un collègue a ajouté la commande assert à quelques reprises dans nos bibliothèques à des endroits où j'aurais utilisé une instruction if et levé une exception. (Je n'avais même jamais entendu parler d'affirmation avant cela.) Voici un exemple de la façon dont il l'a utilisé:

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

Je l'aurais fait:

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

À la lecture de la documentation PHP sur assert , il semble qu'il soit recommandé de s'assurer que l'assert est actif et d'ajouter un gestionnaire avant d'utiliser assert. Je ne trouve pas d'endroit où il a fait ça.

Donc, ma question est la suivante: utiliser assert est-il une bonne idée étant donné ce qui précède et devrais-je l'utiliser plus souvent au lieu des if et des exceptions?

Une autre note, nous prévoyons d'utiliser ces bibliothèques sur une variété de projets et de serveurs, y compris des projets dont nous ne faisons peut-être même pas partie (les bibliothèques sont open source). Cela fait-il une différence dans l'utilisation d'assert?

Darryl Hein
la source
Est-ce vraiment 'isset(la ligne de code avec assert)? Pas seulement isset(sans le guillemet simple ')?
Peter Mortensen le

Réponses:

79

La règle de base qui s'applique dans la plupart des langues (tout ce que je sais vaguement) est que an assertest utilisé pour affirmer qu'une condition est toujours vraie alors que an ifest approprié s'il est concevable qu'elle échoue parfois.

Dans ce cas, je dirais que assertc'est approprié (basé sur ma faible compréhension de la situation) car recordsdevrait toujours être défini avant que la méthode donnée ne soit appelée. Donc, un échec de définition de l'enregistrement serait un bogue dans le programme plutôt qu'une condition d'exécution. Ici, le assertaide à garantir (avec des tests adéquats) qu'il n'y a pas de chemin d'exécution de programme possible qui pourrait provoquer l' assertappel du code protégé par le sans recordsavoir été défini.

L'avantage d'utiliser assertpar opposition à ifest qu'il assertpeut généralement être désactivé dans le code de production, réduisant ainsi les frais généraux. Le genre de situations avec lesquelles il est le mieux géré ifpourrait se produire pendant l'exécution dans le système de production et ainsi rien n'est perdu en ne pouvant pas les désactiver.

aaronasterling
la source
4
Pour ajouter à cela, vous ne souhaiterez peut-être pas désactiver les assertions dans votre code de production, car elles permettent de garantir que ces conditions «cela ne devrait jamais se produire» restent ainsi. Il peut être préférable de laisser votre application s'arrêter à une assertion plutôt que de laisser vos utilisateurs continuer sur un chemin d'exécution qui ne devrait pas exister.
derekerdmann
2
@derekerdmann: Vrai. Pour certaines assertions, il peut être suffisant de les enregistrer (en production) ou d'imprimer l'avertissement (dans l'environnement de développement). Mais étant donné que souvent les affirmations protègent également le code de sécurité, vous pouvez également activer assert_options(ASSERT_BAIL). C'est de toute façon plus rapide que les solutions de contournement manuelles if / throw.
mario
4
@derekerdmann Je ne suis pas d'accord sur ce point (dans le contexte de l'utilisation d'assert () en php). C'est une grande lacune de vulnérabilité, car assert () traite tous les arguments de chaîne comme du code PHP, il est donc (théoriquement) possible d'injecter et d'exécuter du code arbitraire. À mon humble avis, les affirmations devraient être désactivées en production
Vitaliy Lebedev
2
@VitaliyLebedev si vous ne voulez pas être sensible à l'injection, ne passez pas de chaînes à affirmer.
Damon Snyder
9
En retard à la fête mais PHP.net déclare: "Les assertions ne doivent être utilisées que comme une fonctionnalité de débogage."
Koen.
25

Considérez les affirmations comme des «commentaires puissants». Plutôt qu'un commentaire comme:

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

utilisation:

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

Les significations sont exactement les mêmes et sont destinées exactement au même public, mais le premier commentaire est facilement oublié ou ignoré (quel que soit le nombre de points d'exclamation), tandis que le "commentaire de puissance" n'est pas seulement disponible pour les humains à lire et à comprendre, il est également constamment testé en machine pendant le développement et ne sera pas ignoré si vous configurez une bonne gestion des assertions dans le code et dans les habitudes de travail.

Vu de cette façon, les assertions sont un concept complètement différent de si (erreur) ... et des exceptions, et elles peuvent coexister.

Oui, vous devriez commenter votre code, et oui, vous devriez utiliser des «commentaires puissants» (assertions) chaque fois que possible.

DaveWalley
la source
que se passe-t-il si lors du développement lors du test, vous transmettez toujours une bonne condition à l'assert mais en production si l'assert est désactivé - un utilisateur passe une autre condition à laquelle vous n'avez pas pensé lors du test? Sinon, vous devez toujours continuer à affirmer, mais n'est-ce pas la même chose que d'écrire votre propre chèque?
Darius.V
Ensuite, votre programme échouera. Corrigez-le correctement avec les instructions if et les fonctionnalités de gestion des erreurs de votre langage et de votre environnement de développement. les affirmations peuvent révéler des problèmes, il existe de meilleures façons de résoudre les problèmes.
DaveWalley
Notez que depuis PHP 7.2, le passage d'une chaîne dans assert pour évaluation est obsolète. Triste, car cela avait l'air très pratique.
Jannie Theunissen
16

Cela dépend entièrement de votre stratégie de développement. La plupart des développeurs ne connaissent pas assert()et utilisent les tests unitaires en aval. Mais les schémas de test proactifs et intégrés peuvent parfois être avantageux.

assert est utile, car il peut être activé et désactivé. Cela n'épuise pas les performances si aucun gestionnaire d'assertions n'est défini. Votre collègue n'en a pas, et vous devriez concevoir du code qui l'activera temporairement dans l'environnement de développement (si E_NOTICE / E_WARNINGs sont activés, le gestionnaire d'assertions devrait l'être également). Je l'utilise occasionnellement lorsque mon code ne supporte pas les types de variables mixtes - je ne m'engage normalement pas dans une saisie stricte dans un PHP faiblement typé, mais il existe des cas d'utilisation aléatoires:

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

Ce qui, par exemple, compenserait le manque de spécificateurs de type string $a, array $b. PHP5.4 les supportera, mais ne vérifiera pas.

mario
la source
Que signifie "php 5.4 les aura mais pas vérifier"?
Kzqai
1
PHP 5.4 a, prend en charge et vérifie les assertions.
DaveWalley
7

Assert ne remplace pas le contrôle de flux normal ifou les exceptions, car il est uniquement destiné à être utilisé pour le débogage pendant le développement.

Mark Snidovich
la source
6

Une note importante concernant l'assert en PHP antérieur à 7. Contrairement à d'autres langages avec une construction assert, PHP ne lance pas entièrement les instructions assert - il la traite comme une fonction (faites un debug_backtrace () dans une fonction appelée par une assertion). La désactivation des assertions semble simplement inciter la fonction à ne rien faire dans le moteur. Notez que PHP 7 peut être amené à émuler ce comportement en définissant zend.assertions sur 0 au lieu des valeurs plus normales de 1 (activé) ou -1 (désactivé).

Le problème se pose en ce que l'assert accepte n'importe quel argument - mais si l'argument n'est pas une chaîne, assert obtient les résultats de l'expression, que l'assert soit activé ou non. Vous pouvez le vérifier avec le bloc de code suivant.

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

Étant donné l'intention d'affirmer, il s'agit d'un bogue, et il existe depuis longtemps car il est dans le langage depuis que assert a été introduit dans PHP 4.

Les chaînes passées à assert sont évaluées, avec toutes les implications de performances et les dangers qui en découlent, mais c'est le seul moyen de faire fonctionner les instructions assert comme elles le devraient en PHP (ce comportement est déconseillé en PHP 7.2).

EDIT: Modifié ci-dessus pour noter les changements dans PHP 7 et 7.2

Michael Morris
la source
1
En PHP 7, il y a / sera le zend.assertionsparamètre ini pour désactiver complètement assert().
Kontrollfreak
C'est une excellente nouvelle - mais d'après la documentation qui s'y trouve, il semble qu'un correctif pour PHPUnit est order, ajoutant un gestionnaire de rappel d'assert pour lancer AssertionException lorsque les assertions échouent sous PHP 5.x. De cette façon, les tests unitaires peuvent utiliser l'annotation @expectedException AssertionException, qu'ils s'exécutent sur PHP 5.x ou 7.
Michael Morris
3

Assert ne doit être utilisé qu'en développement car il est utile pour le débogage. Donc, si vous le souhaitez, vous pouvez les utiliser pour développer votre site Web, mais vous devez utiliser des exceptions pour un site Web en direct.

Kyle
la source
7
Mais on aura toujours les assertions dans le code. Ils ne seront tout simplement pas actifs dans un environnement de production.
aaronasterling
1
Je les garderais en production et ajusterais mon gestionnaire d'erreurs en conséquence à la place.
Daniel W.
3

Non, votre collègue ne devrait pas l'utiliser comme gestionnaire d'erreurs à usage général. Selon le manuel:

Les assertions doivent être utilisées comme fonction de débogage uniquement. Vous pouvez les utiliser pour des vérifications de cohérence qui testent des conditions qui devraient toujours être VRAIES et qui indiquent des erreurs de programmation sinon ou pour vérifier la présence de certaines fonctionnalités telles que les fonctions d'extension ou certaines limites et fonctionnalités du système.

Les assertions ne doivent pas être utilisées pour les opérations d'exécution normales telles que les vérifications des paramètres d'entrée. En règle générale, votre code doit toujours pouvoir fonctionner correctement si la vérification des assertions n'est pas activée.

Si vous êtes familier avec les suites de tests automatisés, le verbe "assert" est généralement utilisé pour vérifier la sortie d'une méthode ou d'une fonction. Par exemple:

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

Votre collègue ne devrait pas l'utiliser comme un gestionnaire d'erreurs à usage général et dans ce cas comme un contrôle d'entrée. Il semble qu'il est possible que le champ des enregistrements ne soit pas défini par un utilisateur de votre bibliothèque.

Dean ou
la source
3

Votre collègue tente vraiment d'appliquer la conception par contrat (DbC) à partir du langage Eiffel et basé sur le livre: Object Oriented Software Construction, 2nd Edition.

L'assertion, telle qu'il l'utilisait, serait la {P} -partie du Hoare Logic ou Hoare Triple: {P} C {Q}, où le {P} est les assertions de précondition (ion) s et {Q} sont l'affirmation (ion) post-condition s.

Je prendrais note critique des conseils donnés sur la fonctionnalité d'assert en PHP ayant des bogues. Vous ne voulez pas utiliser de code bogué. Ce que vous voulez vraiment, ce sont les fabricants de PHP pour corriger le bogue dans l'assert. Jusqu'à ce qu'ils le fassent, vous pouvez utiliser l'assert, mais en gardant à l'esprit son état de buggy actuel.

De plus, si la fonction d'assert est boguée, je vous suggère de ne pas l'utiliser dans le code de production. Néanmoins, je vous recommande de l'utiliser dans le développement et le test du code le cas échéant.

Enfin, si vous étudiez la conception par contrat, vous constaterez qu'il y a des conséquences à utiliser des assertions booléennes à la lumière de l'héritage classique orienté objet - c'est-à-dire que vous ne devez jamais affaiblir une précondition, ni affaiblir une post-condition. Cela pourrait être dangereux pour vos objets descendants polymorphes qui interagissent les uns avec les autres. Jusqu'à ce que vous compreniez ce que cela signifie - je le laisserais tranquille!

De plus, je recommande vivement aux fabricants de PHP de faire une étude approfondie de la conception par contrat et d'essayer de la mettre en PHP dès que possible! Ensuite, nous pouvons tous bénéficier d'un compilateur / interpréteur compatible DbC, qui traiterait les problèmes notés dans les réponses (ci-dessus):

  1. Un compilateur de conception par contrat correctement implémenté serait (espérons-le) exempt de bogues (contrairement à l'affirmation PHP actuelle).
  2. Un compilateur de conception par contrat correctement implémenté gérerait les nuances de la gestion de la logique d'assertion polymorphe pour vous au lieu de vous creuser la tête!

REMARQUE: Même votre utilisation d'une ifdéclaration comme substitut à l'affirmation (pré-condition) subira des conséquences désastreuses si elle est utilisée pour renforcer une pré-condition ou affaiblir une post-condition. Pour comprendre ce que cela signifie, il vous faudra étudier la conception par contrat pour le savoir! :-)

Bonne étude et apprentissage.

Larry
la source