Dichiarare campi su classi è effettivamente dannoso in PHP?


13

Considera il seguente codice, in cui il setter viene deliberatamente interrotto a causa di un banale errore di programmazione che ho fatto davvero alcune volte in passato:

<?php

    class TestClass {

        private $testField;

        function setField($newVal) {
            $testField = $newVal;
            // deliberately broken; should be `$this->testField = $newVal`
        }

        function getField() {
            return $this->testField;
        }

    }

    $testInstance = new TestClass();
    $testInstance->setField("Hello world!");

    // Actually prints nothing; getField() returns null
    echo $testInstance->getField(); 

?>

Il fatto che ho dichiarato $testFieldai vertici della classe mi aiuta a nascondere questo errore di programmazione. Se non avessi dichiarato il campo, avrei ricevuto qualcosa di simile al seguente avviso stampato nel mio registro degli errori dopo aver chiamato questo script, il che sarebbe potenzialmente utile per aiutare il mio debug, specialmente se dovessi fare un errore come questo in un'applicazione grande e complicata nel mondo reale:

Avviso PHP: Proprietà non definita: TestClass :: $ testField in /var/www/test.php sulla riga 13

Con la dichiarazione, non vi è alcun avviso.

Forse mi manca qualcosa, ma sono a conoscenza di due soli motivi per dichiarare i campi di classe in PHP: in primo luogo, che le dichiarazioni fungono da documentazione e, in secondo luogo, che senza dichiarazioni non è possibile utilizzare i modificatori privatee protectedaccedere, che sono probabilmente utile. Poiché quest'ultimo argomento non si applica ai campi pubblici - l'assegnazione a un campo non dichiarato di un oggetto lo rende pubblico - mi sembra che dovrei almeno commentare tutte le mie dichiarazioni di campo pubblico. I commenti forniranno lo stesso identico valore della documentazione, ma trarrò vantaggio dagli avvisi se provo a leggere un campo non inizializzato.

Ripensandoci, però, non ha senso fermarsi qui. Poiché nella mia esperienza il tentativo di leggere un campo non inizializzato è una causa di errore molto più comune rispetto al tentativo di leggere o modificare in modo inappropriato un campo privato o protetto (ho già fatto il primo più volte nella mia breve carriera di programmatore, ma mai il secondo ), mi sembra che commentare tutte le dichiarazioni sul campo - non solo quelle pubbliche - sarebbe la migliore pratica.

Ciò che mi fa esitare è che non ho mai visto nessun altro farlo nel loro codice. Perchè no? C'è un vantaggio nel dichiarare campi di classe di cui non sono a conoscenza? O posso modificare la configurazione di PHP in qualche modo per cambiare il comportamento delle dichiarazioni di campo in modo da poter usare dichiarazioni di campo reali e beneficiare ancora degli avvisi "Proprietà indefinita"? O c'è qualcos'altro che mi è sfuggito nella mia analisi?


1
Probabilmente utile ? I vantaggi che hai dichiarato sono estremamente importanti. Probabilmente rifiuterei categoricamente di lavorare su un codice che non aveva dichiarazioni esplicite di proprietà.
Michael,

2
Davvero, il modo per proteggersi da questi errori è con un attento controllo degli errori e, meglio, tramite test unitari.
Michael,

1
è presente un report degli errori php (un livello di avviso) che ti dirà se usi una variabile senza prima dichiararla.
Crayon Violent,

3
@MarkAmery penserei che una startup freneticamente a ritmo è tra i luoghi più importanti da adottare rigorosi test di unità - dal momento che si sta sempre cambiando il codice, è probabile che sempre la rottura esso. Questo è davvero lo scopo esatto di test rigorosi e TDD completo. Sì, immagino che l'uso di caratteri di sottolineatura possa aiutarti a ricordare che stai accedendo a cose private, ma per me l'idea di adottare specifici standard di codifica per proteggermi dal commettere errori che non dovrei fare è inquietante. Come fare TRUE === $variableper impedirti di assegnare accidentalmente invece di confrontare.
Michael,

1
@MarkAmery Notare inoltre che le parole chiave di visibilità sono analizzate da strumenti di documentazione automatizzata , in modo da fornire più "valore di documentazione" rispetto a un semplice commento inserito manualmente.
Michael,

Risposte:


15

Devi sempre dichiarare in anticipo le proprietà della tua classe. Mentre PHP è un linguaggio dinamico e sarà felicemente accompagnato dalla creazione delle proprietà in fase di esecuzione, ci sono diversi aspetti negativi di questo percorso.

  • Il compilatore può ottimizzare le proprietà dichiarate. Quando si dichiara dinamicamente una proprietà, si tratta di un hit dalle prestazioni poiché il compilatore deve creare una tabella hash dinamica per archiviare le proprietà dinamiche.
  • Non sono al 100% su questo, ma credo che gli ottimizzatori bytecode come APC non saranno così utili in quanto non avranno un quadro completo della tua classe. (E ottimizzatori come APC sono un must )
  • Rende il tuo codice 1000x più difficile da leggere. Le persone ti odieranno.
  • Rende 1000 volte più probabile che tu commetta un errore. (Era getId o getID? Aspetta, hai usato entrambi. Fallito.)
  • Nessun completamento automatico IDE o suggerimento del tipo.
  • I generatori di documentazione non vedranno la tua proprietà.

Il problema che hai descritto è in realtà un problema minore rispetto al problema di non dichiarare le proprietà nella definizione della classe. Ecco una buona soluzione.

Abituati a dichiarare le impostazioni predefinite per le tue proprietà.

private $testField = null;
private $testField = '';
private $testField = 0;
private $testField = []; //array()
private $testField = false;

Ad eccezione delle proprietà che memorizzeranno gli oggetti, questo coprirà la maggior parte dei tipi di base che verranno memorizzati. Le proprietà che memorizzeranno gli oggetti possono essere impostate nel costruttore.

Una buona regola per la progettazione di classi è che dopo che l'oggetto è stato creato e il costruttore è stato eseguito non si dovrebbe avere alcuna proprietà "indefinita".


In risposta ai tuoi 5 lati negativi: 1 (Prestazioni): hai una fonte per questo? 2 (leggibilità): non sono sicuro di aver capito; la proposta era di commentare le dichiarazioni, non solo di rimuoverle, quindi quale leggibilità si perde? Evidenziazione della sintassi leggermente meno amichevole e potenziale difficoltà di distinzione dai commenti vicini, immagino? 3: Questo non lo capisco affatto; puoi chiarire? 4 (IDE): Abbastanza giusto - Non ho esperienza di codifica PHP con un IDE e non sapevo del suggerimento sul tipo di Eclipse. 5 (generatori di documenti): abbastanza giusto - non ne ho mai usato uno.
Mark Amery,

Non ho visto la tua battuta sul commentarli. Indipendentemente da ciò, valgono ancora i punti precedenti: come fai a sapere quali sono attivi o commentati legittimamente ??
Jarrod Nettles,

In risposta alla soluzione proposta: questo è esattamente ciò che voglio evitare: l'assegnazione delle impostazioni predefinite anche quando sono insignificanti e i valori predefiniti non devono mai essere utilizzati. Il problema con questi valori predefiniti (e con una dichiarazione senza assegnazione, che assegna solo null) è che se, a causa di un errore di programmazione, non dovessi assegnare un valore a qualcosa nel costruttore quando dovrei, allora piuttosto che PHP che dà un avvertimento quando tento di utilizzare quel valore in seguito - aiutandomi a individuare il mio errore - tutto sembrerà funzionare bene anche se la classe è rotta.
Mark Amery,

2
@MarkAmery Il tuo errore di programmazione, tuttavia, equivale essenzialmente a un refuso. Li abbiamo avuti tutti - ma stai cercando di far esplodere una bomba per uccidere una mosca qui.
Jarrod Nettles

Potresti avere ragione. Ho trascorso un paio d'ore a rintracciare un bug oggi che si è rivelato essere interamente causato da un tale errore di battitura, e in seguito sono stato frustrato dall'improvvisa consapevolezza che se non avessi usato solo le dichiarazioni sul campo, avrei avuto un avviso e noto immediatamente dove si trovava il mio errore (che comunque avevo sempre pensato che potesse accadere in queste circostanze; non avevo realizzato nulla su campi inizializzati per le dichiarazioni). L'accessibilità immediata di quell'esperienza probabilmente sta distorcendo il mio giudizio sulla probabilità che si ripeta, o su quanto valga la pena difendersi.
Mark Amery,

2

Ho lavorato su un codice che utilizzava proprietà dell'oggetto create dinamicamente. Ho pensato che usare le proprietà degli oggetti create dinamicamente fosse piuttosto bello (vero, secondo me). Tuttavia, il mio programma ha impiegato 7 secondi per essere eseguito. Ho rimosso le proprietà degli oggetti dinamici e le ho sostituite con le proprietà degli oggetti dichiarate come parte di ogni classe (pubblica in questo caso). Il tempo della CPU è passato da oltre 7 secondi a 0,177 secondi. È abbastanza sostanziale.

È possibile che stavo facendo qualcosa di sbagliato nel modo in cui stavo usando le proprietà dinamiche degli oggetti. È anche possibile che la mia configurazione sia rotta in qualche modo. Ovviamente, dovrei dire che ho una configurazione PHP vanilla molto semplice sulla mia macchina.

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.