La déclaration de champs sur des classes est-elle réellement nuisible en PHP?

13

Considérez le code suivant, dans lequel le setter est délibérément cassé en raison d'une erreur de programmation banale que j'ai commise à plusieurs reprises dans le passé:

<?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(); 

?>

Le fait que j'ai déclaré $testFielden haut de la classe aide à me cacher cette erreur de programmation. Si je n'avais pas déclaré le champ, j'obtiendrais quelque chose de similaire à l'avertissement suivant imprimé dans mon journal d'erreurs lors de l'appel de ce script, ce qui pourrait être utile pour aider mon débogage - surtout si je faisais une erreur comme celle-ci dans une application réelle et complexe:

Remarque PHP: Propriété non définie: TestClass :: $ testField dans /var/www/test.php sur la ligne 13

Avec la déclaration, il n'y a pas d'avertissement.

Peut-être que je manque quelque chose, mais je ne connais que deux raisons pour déclarer des champs de classe en PHP: premièrement, que les déclarations agissent comme documentation, et deuxièmement, que sans déclarations, on ne peut pas utiliser les modificateurs privateet protectedaccess, qui sont sans doute utile. Puisque ce dernier argument ne s'applique pas aux champs publics - l'attribution à un champ non déclaré d'un objet le rend public - il me semble que je devrais au moins commenter toutes mes déclarations de champs publics. Les commentaires fourniront exactement la même valeur de documentation, mais je bénéficierai d'avertissements si j'essaie de lire un champ non initialisé.

Après réflexion, cependant, il ne semble pas logique de s'arrêter là. Étant donné que dans mon expérience, essayer de lire un champ non initialisé est une cause d'erreur beaucoup plus courante que d'essayer de lire ou de modifier de manière inappropriée un champ privé ou protégé (j'ai déjà fait plusieurs fois le premier dans ma courte carrière de programmation, mais jamais le dernier ), il me semble que commenter toutes les déclarations de champ - pas seulement les déclarations publiques - serait la meilleure pratique.

Ce qui me fait hésiter, c'est que je n'ai jamais vu personne d'autre le faire dans leur code. Pourquoi pas? Y a-t-il un avantage à déclarer des champs de classe que je ne connais pas? Ou puis-je modifier la configuration de PHP d'une manière ou d'une autre pour changer le comportement des déclarations de champ afin que je puisse utiliser de vraies déclarations de champ tout en bénéficiant des avertissements "Propriété non définie"? Ou y a-t-il autre chose que j'ai manqué dans mon analyse?

Mark Amery
la source
1
Sans doute utile ? Les avantages que vous avez mentionnés sont extrêmement importants. Je serais probablement platement refuser de travailler sur le code qui avait pas de déclaration de propriété explicites.
Michael
2
Vraiment, la façon de vous protéger contre ces erreurs est de vérifier soigneusement les erreurs et mieux, via des tests unitaires.
Michael
1
il y a un rapport d'erreur php intégré (un niveau de notification) qui vous dira si vous utilisez une variable sans la déclarer au préalable.
Crayon Violent
3
@MarkAmery Je pense qu'une startup au rythme effréné est l'un des endroits les plus importants pour adopter des tests unitaires stricts - puisque vous changez toujours de code, vous êtes probablement toujours en train de le casser . C'est vraiment le but exact des tests stricts et du TDD à plat. Oui, je suppose que l'utilisation de soulignements peut vous aider à vous souvenir que vous accédez à des informations privées, mais pour moi, l'idée d'adopter des normes de codage spécifiques pour me protéger contre les erreurs que je ne devrais pas faire est troublante. Comme faire TRUE === $variablepour vous empêcher d'attribuer accidentellement au lieu de comparer.
Michael
1
@MarkAmery Notez également que les mots clés de visibilité sont analysés par des outils de documentation automatisés , de sorte qu'ils donnent plus de "valeur de documentation" que juste un commentaire que vous mettriez manuellement.
Michael

Réponses:

15

Vous devez toujours déclarer vos propriétés de classe à l'avance. Bien que PHP soit un langage dynamique et vous accompagnera avec plaisir lors de la création de vos propriétés lors de l'exécution, il existe plusieurs inconvénients à ce chemin.

  • Le compilateur peut optimiser les propriétés déclarées. Lorsque vous déclarez dynamiquement une propriété, il s'agit d'un impact sur les performances car le compilateur doit créer une table de hachage dynamique pour stocker vos propriétés dynamiques.
  • Je ne suis pas à 100% sur celui-ci mais je crois que les optimiseurs de bytecode comme APC ne seront pas aussi utiles car ils n'auront pas une image complète de votre classe. (Et les optimiseurs comme APC sont un must )
  • Rend votre code 1000x plus difficile à lire. Les gens vous détesteront.
  • Rend 1000x plus de chances que vous fassiez une erreur. (Était-ce getId ou getID? Attendez, vous avez utilisé les deux. Échec.)
  • Pas de saisie semi-automatique IDE ou d'indication de type.
  • Les générateurs de documentation ne verront pas votre propriété.

Le problème que vous avez décrit est en fait mineur par rapport au problème de ne pas déclarer vos propriétés dans la définition de votre classe. Voici une bonne solution.

Habituez-vous à déclarer les valeurs par défaut de vos propriétés.

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

À l'exception des propriétés qui stockeront des objets, cela couvrira la majorité des types de base que vous stockerez. Les propriétés qui stockeront les objets peuvent être définies dans votre constructeur.

Une bonne règle pour la conception de classes est qu'après la création de votre objet et l'exécution de votre constructeur, vous ne devriez pas avoir de propriétés "indéfinies".

Orties de Jarrod
la source
En réponse à vos 5 inconvénients: 1 (Performance): Avez-vous une source pour cela? 2 (lisibilité): Je ne suis pas sûr de comprendre; la proposition était de commenter les déclarations, pas seulement de les supprimer, alors quelle lisibilité est perdue? Mise en évidence de la syntaxe un peu moins conviviale et difficulté potentielle à se distinguer des commentaires voisins, je suppose? 3: Celui-ci, je ne le comprends pas du tout; Pouvez-vous clarifier? 4 (IDE): Assez juste - je n'ai pas d'expérience de codage PHP avec un IDE et je ne connaissais pas les indications de type d'Eclipse. 5 (générateurs de doc): Assez bien - jamais utilisé l'un d'eux.
Mark Amery
Je n'ai pas vu votre ligne sur les commenter. Quoi qu'il en soit, les points ci-dessus s'appliquent toujours - comment savoir lesquels sont actifs ou commentés légitimement ??
Jarrod Nettles
En réponse à la solution que vous proposez: c'est précisément ce que je veux éviter - l'attribution de valeurs par défaut même lorsqu'elles n'ont aucun sens et que les valeurs par défaut ne doivent jamais être utilisées. Le problème avec ces valeurs par défaut (et avec la déclaration sans affectation, qui affecte simplement null) est que si, en raison d'une erreur de programmation, je n'attribue pas de valeur à quelque chose dans le constructeur quand je le devrais, alors plutôt que PHP donnant un avertissement lorsque j'essaie d'utiliser cette valeur plus tard - ce qui m'aide à détecter mon erreur - tout semble fonctionner correctement même si la classe est interrompue.
Mark Amery
2
@MarkAmery Votre erreur de programmation, cependant, revient essentiellement à une faute de frappe. Nous en avons tous - mais vous essayez de faire exploser une bombe pour tuer une mouche ici.
Jarrod Nettles
Vous pouvez avoir raison. J'ai passé quelques heures à rechercher un bogue aujourd'hui qui s'est avéré être entièrement causé par une telle faute de frappe, et j'ai ensuite été frustré par la soudaine prise de conscience que si je n'avais pas utilisé les déclarations de champ, j'aurais reçu un avis et a su immédiatement où était mon erreur (ce que j'avais toujours pensé arriver dans ces circonstances de toute façon; je ne me suis pas rendu compte que les déclarations initialisaient les champs à null). L'accessibilité immédiate de cette expérience fausse probablement mon jugement sur la probabilité qu'elle se reproduise ou sur la valeur de sa défense.
Mark Amery
2

J'ai travaillé sur du code qui utilisait des propriétés d'objet créées dynamiquement. Je pensais que l'utilisation de propriétés d'objets créées dynamiquement était plutôt cool (vrai, à mon avis). Cependant, mon programme a pris 7 secondes pour s'exécuter. J'ai supprimé les propriétés d'objet dynamiques et les ai remplacées par les propriétés d'objet déclarées dans le cadre de chaque classe (publique dans ce cas). Le temps processeur est passé de plus de 7 secondes à 0,177 seconde. C'est assez substantiel.

Il est possible que je fasse quelque chose de mal dans la façon dont j'utilisais les propriétés des objets dynamiques. Il est également possible que ma configuration soit interrompue d'une manière ou d'une autre. Bien sûr, je dois dire que j'ai une configuration PHP vanille très simple sur ma machine.

user328304
la source