Dovrei usare assert nel mio codice PHP?


87

Un collega ha aggiunto il comando assert alcune volte nelle nostre librerie in luoghi in cui avrei usato un'istruzione if e lanciato un'eccezione. (Non avevo mai nemmeno sentito parlare di asserire prima di questo.) Ecco un esempio di come lo ha usato:

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

Avrei fatto:

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

Dalla lettura dei documenti PHP su assert , sembra che sia consigliabile assicurarsi che assert sia attivo e aggiungere un gestore prima di utilizzare assert. Non riesco a trovare un posto dove l'abbia fatto.

Quindi, la mia domanda è: usare assert è una buona idea dato quanto sopra e dovrei usarlo più spesso invece di if ed eccezioni?

Un'altra nota, stiamo pianificando di utilizzare queste librerie su una varietà di progetti e server, inclusi progetti di cui potremmo anche non far parte (le librerie sono open source). Questo fa differenza nell'usare assert?


È davvero 'isset(la riga di codice con assert)? Non solo isset(senza virgolette singole, ')?
Peter Mortensen

Risposte:


79

La regola pratica che è applicabile alla maggior parte delle lingue (tutto ciò che so vagamente) è che un assertè usato per affermare che una condizione è sempre vera mentre an ifè appropriato se è concepibile che a volte fallirà.

In questo caso, direi che assertè appropriato (in base alla mia debole comprensione della situazione) perché recordsdovrebbe essere sempre impostato prima che il metodo dato venga chiamato. Quindi un errore nell'impostazione del record sarebbe un bug nel programma piuttosto che una condizione di runtime. Qui, il assertsta aiutando a garantire (con test adeguati) che non vi sia alcun percorso di esecuzione del programma possibile che potrebbe causare il assertrichiamo del codice che viene protetto con il senza recordsessere stato impostato.

Il vantaggio dell'utilizzo assertrispetto a ifè che assertpuò essere generalmente disattivato nel codice di produzione riducendo così il sovraccarico. Il tipo di situazioni che sono meglio gestite ifpotrebbero verosimilmente verificarsi durante il runtime nel sistema di produzione e quindi non si perde nulla se non si è in grado di disattivarli.


4
Per aggiungere a questo, potresti non voler disabilitare le asserzioni nel tuo codice di produzione, perché aiutano a garantire che quelle condizioni "questo non dovrebbe mai accadere" rimangano tali. Potrebbe essere meglio lasciare che la tua applicazione si fermi da un'asserzione piuttosto che lasciare che i tuoi utenti continuino a seguire un percorso di esecuzione che non dovrebbe esistere.
derekerdmann

2
@derekerdmann: vero. Per alcune asserzioni potrebbe essere sufficiente registrarle (in produzione) o stampare l'avviso (in ambiente di sviluppo). Ma poiché spesso afferma di proteggere anche il codice rilevante per la sicurezza, potresti anche abilitarlo assert_options(ASSERT_BAIL). È comunque più veloce delle soluzioni alternative manuali if / throw.
mario

4
@derekerdmann Non sarei d'accordo su questo (nel contesto dell'uso di assert () in php). Questo è un grande divario di vulnerabilità, poiché assert () tratta tutti gli argomenti di stringa come codice PHP, quindi è (teoricamente) possibile iniettare ed eseguire codice arbitrario. Secondo me, le affermazioni dovrebbero essere disattivate sulla produzione
Vitaliy Lebedev

2
@VitaliyLebedev se non vuoi essere suscettibile all'iniezione, non passare stringhe per affermare.
Damon Snyder

9
In ritardo alla festa, ma PHP.net afferma: "Le asserzioni dovrebbero essere utilizzate solo come funzionalità di debug".
Koen.

25

Pensa alle affermazioni come "commenti di potere". Piuttosto che un commento come:

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

uso:

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

I significati sono esattamente gli stessi e sono destinati allo stesso identico pubblico, ma il primo commento è facilmente dimenticato o ignorato (indipendentemente dal numero di punti esclamativi), mentre il "commento di potere" non è disponibile solo per essere letto e compreso dagli umani, è anche costantemente testato dalla macchina durante lo sviluppo e non verrà ignorato se si imposta una buona gestione delle asserzioni nel codice e nelle abitudini di lavoro.

Visto in questo modo, le asserzioni sono un concetto completamente diverso da if (errore) ... e le eccezioni, e possono coesistere.

Sì, dovresti commentare il tuo codice e sì, dovresti usare "commenti potenti" (asserzioni) ogni volta che è possibile.


cosa succede se durante lo sviluppo durante il test si passa sempre una buona condizione all'assert ma in produzione se l'assert è disattivato - un utente supera un'altra condizione a cui non si è pensato durante il test? O altrimenti devi continuare ad affermare sempre, ma allora non è la stessa cosa che scrivere il tuo assegno?
Darius.V

Quindi il tuo programma fallirà. Risolvilo correttamente con le istruzioni if ​​e le funzionalità di gestione degli errori del tuo linguaggio e dell'ambiente di sviluppo. afferma possono scoprire problemi, ci sono modi migliori per risolvere i problemi.
DaveWalley,

Si noti che a partire da PHP 7.2 passare una stringa in assert per la valutazione è stato deprecato. Triste, perché sembrava abbastanza utile.
Jannie Theunissen

16

Dipende interamente dalla tua strategia di sviluppo. La maggior parte degli sviluppatori non è a conoscenza assert()e utilizza i test di unità a valle. Ma a volte gli schemi di test proattivi e integrati possono essere vantaggiosi.

assert è utile, perché può essere abilitato e disabilitato. Non esaurisce le prestazioni se non viene definito alcun gestore di asserzioni di questo tipo. Il tuo collega non ne ha uno e dovresti escogitare del codice che lo abiliti temporaneamente nell'ambiente di sviluppo (se E_NOTICE / E_WARNING sono attivi, dovrebbe essere il gestore delle asserzioni). Lo uso occasionalmente dove il mio codice non può sopportare tipi di variabili miste: normalmente non mi impegno a digitare in modo rigoroso in un PHP di tipo debole, ma ci sono casi d'uso casuali:

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

Che ad esempio compenserebbe la mancanza di specificatori di tipo string $a, array $b. PHP5.4 li supporterà, ma non controllerà.


Cosa significa "php 5.4 li avrà ma non controllerà"?
Kzqai

1
PHP 5.4 ha, supporta e controlla asserzioni.
DaveWalley

7

Assert non è un sostituto del normale controllo del flusso come ifo eccezioni, perché è pensato solo per essere utilizzato per il debug durante lo sviluppo.


6

Una nota importante riguardante l'assert in PHP precedente a 7. A differenza di altri linguaggi con un costrutto assert, PHP non lancia completamente le istruzioni assert - lo tratta come una funzione (esegui un debug_backtrace () in una funzione chiamata da un'asserzione). Disattivare asserzioni sembra semplicemente collegare a caldo la funzione per non fare nulla nel motore. Nota che PHP 7 può essere fatto per emulare questo comportamento impostando zend.assertions su 0 invece dei valori più normali 1 (on) o -1 (off).

Il problema sorge in quanto assert accetta qualsiasi argomento, ma se l'argomento non è una stringa, allora assert ottiene i risultati dell'espressione sia che assert sia attivato o disattivato. Puoi verificarlo con il seguente blocco di codice.

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

Dato l'intento di assert, questo è un bug, e di lunga data da quando è stato inserito nella lingua da quando assert è stato introdotto in PHP 4.

Le stringhe passate per assert vengono valutate, con tutte le implicazioni sulle prestazioni e i rischi che ne derivano, ma è l'unico modo per far funzionare le istruzioni assert come dovrebbero in PHP (questo comportamento è deprecato in PHP 7.2).

EDIT: modificato sopra per notare i cambiamenti in PHP 7 e 7.2


1
In PHP 7 c'è / sarà l' zend.assertionsimpostazione ini per spegnersi completamente assert().
Kontrollfreak

Questa è un'ottima notizia, ma in base alla documentazione presente sembra che una patch a PHPUnit sia stata ordinata, aggiungendo un gestore di callback assert per lanciare AssertionException quando le asserzioni falliscono sotto PHP 5.x. In questo modo gli unit test possono utilizzare l'annotazione @expectedException AssertionException indipendentemente dal fatto che vengano eseguiti su PHP 5.x o 7.
Michael Morris

3

Assert dovrebbe essere usato solo in fase di sviluppo in quanto è utile per il debug. Quindi, se vuoi, puoi usarli per sviluppare il tuo sito web, ma dovresti usare le eccezioni per un sito web live.


7
Ma si avranno ancora le affermazioni nel codice. Semplicemente non saranno attivi in ​​un ambiente di produzione.
aaronasterling

1
Li terrei in produzione e modificherei di conseguenza il mio gestore degli errori.
Daniel W.

3

No, il tuo collega non dovrebbe usarlo come gestore di errori generico. Secondo il manuale:

Le asserzioni devono essere utilizzate solo come funzionalità di debug. È possibile utilizzarli per controlli di integrità che testano condizioni che dovrebbero essere sempre VERE e che indicano alcuni errori di programmazione in caso contrario o per verificare la presenza di determinate funzionalità come funzioni di estensione o determinati limiti e funzionalità del sistema.

Le asserzioni non devono essere utilizzate per normali operazioni di runtime come i controlli dei parametri di input. Come regola pratica, il tuo codice dovrebbe sempre essere in grado di funzionare correttamente se il controllo delle asserzioni non è attivato.

Se hai familiarità con le suite di test automatizzate, il verbo "assert" viene generalmente utilizzato per verificare l'output di qualche metodo o funzione. Per esempio:

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

Il tuo collega non dovrebbe usarlo come gestore di errori generico e in questo caso come controllo dell'input. Sembra che sia possibile che il campo dei record non venga impostato da qualche utente della tua libreria.


3

Il tuo collega sta davvero tentando di applicare il design by contract (DbC) dal linguaggio Eiffel e basato sul libro: Object Oriented Software Construction, 2nd Edition.

L'asserzione, come l'ha usata, sarebbe la parte {P} della Hoare Logic o Hoare Triple: {P} C {Q}, dove {P} è l'asserzione precondizione (ione) se {Q} sono la post-condizione asserisce (ione) s.

Vorrei prendere nota in modo critico dei consigli forniti sulla funzione di asserzione in PHP che presenta bug. Non vuoi usare un codice con bug. Quello che vuoi veramente sono i creatori di PHP per correggere il bug nell'assert. Fino a quando non lo fanno, puoi usare l'assert, ma usalo tenendo conto del suo attuale stato di bug.

Inoltre, se la funzione di asserzione è difettosa, suggerisco di non utilizzarla nel codice di produzione. Tuttavia, ti consiglio di usarlo nel codice di sviluppo e test, ove appropriato.

Infine, se si studia il design per contratto, si scoprirà che l'uso di asserzioni booleane alla luce dell'ereditarietà classica orientata agli oggetti ha delle conseguenze: non si deve mai indebolire una precondizione, né indebolire una post-condizione. Ciò potrebbe essere pericoloso per i tuoi oggetti discendenti polimorfici che interagiscono tra loro. Finché non capisci cosa significa, lo lascerei stare!

Inoltre, consiglio vivamente ai creatori di PHP di eseguire uno studio completo del design per contratto e di tentare di inserirlo in PHP il prima possibile! Quindi tutti noi possiamo trarre vantaggio dall'avere un compilatore / interprete compatibile con DbC, che gestirà i problemi indicati nelle risposte (sopra):

  1. Un compilatore consapevole della progettazione per contratto implementato correttamente sarebbe (si spera) privo di bug (a differenza dell'attuale asserzione PHP).
  2. Un compilatore consapevole della progettazione per contratto implementato correttamente gestirà le sfumature della gestione logica delle asserzioni polimorfiche per te invece di tormentarti il ​​cervello sulla questione!

NOTA: anche il tuo utilizzo di un file if un'affermazione come sostituto dell'asserzione (precondizione) subirà conseguenze disastrose se usata per rafforzare una precondizione o indebolire una post-condizione. Per capire cosa significa, dovrai studiare design per contratto per saperlo! :-)

Buon studio e apprendimento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.