¿Declarar campos en clases es realmente dañino en PHP?

13

Considere el siguiente código, en el que el setter se rompe deliberadamente debido a un error de programación mundano que he cometido algunas veces en el pasado:

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

?>

El hecho de que declare $testFielden la parte superior de la clase me ayuda a ocultar ese error de programación. Si no hubiera declarado el campo, obtendría algo similar a la siguiente advertencia impresa en mi registro de errores al llamar a este script, lo que podría ser valioso para ayudar a mi depuración, especialmente si cometiera un error como este en Una aplicación grande y complicada del mundo real:

Aviso PHP: Propiedad indefinida: TestClass :: $ testField en /var/www/test.php en la línea 13

Con la declaración, no hay advertencia.

Tal vez me falta algo, pero solo conozco dos razones para declarar campos de clase en PHP: en primer lugar, que las declaraciones actúan como documentación y, en segundo lugar, que sin declaraciones no se pueden usar los modificadores privatey de protectedacceso, que son posiblemente útil. Como el último argumento no se aplica a los campos públicos, la asignación a un campo no declarado de un objeto lo hace público, me parece que al menos debería comentar todas mis declaraciones de campo público. Los comentarios proporcionarán exactamente el mismo valor de documentación, pero me beneficiaré de las advertencias si intento leer un campo no inicializado.

Pensándolo mejor, sin embargo, no parece tener sentido detenerse allí. Dado que, en mi experiencia, intentar leer un campo no inicializado es una causa de error mucho más común que tratar de leer o modificar de manera inapropiada un campo privado o protegido (ya he hecho lo anterior varias veces en mi corta carrera de programación, pero nunca lo último) ), me parece que comentar todas las declaraciones de campo, no solo las públicas, sería la mejor práctica.

Lo que me hace dudar es que nunca he visto a nadie más hacerlo en su código. Por qué no? ¿Hay algún beneficio en declarar campos de clase que desconozco? ¿O puedo modificar la configuración de PHP de alguna manera para cambiar el comportamiento de las declaraciones de campo de modo que pueda usar declaraciones de campo reales y aún así beneficiarme de las advertencias de "Propiedad indefinida"? ¿O hay algo más que me haya perdido en mi análisis?

Mark Amery
fuente
1
Posiblemente útil ? Los beneficios que declaró son extremadamente importantes. Probablemente me negaría rotundamente a trabajar en código que no tuviera declaraciones explícitas de propiedad.
Michael
2
Realmente, la forma de protegerse de estos errores es con una cuidadosa verificación de errores y mejor, a través de pruebas unitarias.
Michael
1
hay un informe de errores de php incorporado (un nivel de aviso) que le dirá si usa una variable sin declararla primero.
Crayon Violent
3
@ MarkAmery Creo que una startup frenética se encuentra entre los lugares más importantes para adoptar pruebas unitarias estrictas, ya que siempre está cambiando el código, es probable que siempre lo rompa . Ese es realmente el propósito exacto de pruebas estrictas y TDD plano. Sí, supongo que usar guiones bajos puede ayudarlo a recordar que está accediendo a cosas privadas, pero para mí la idea de adoptar estándares de codificación específicos para protegerme de cometer errores que no debería cometer es inquietante. Como hacer TRUE === $variablepara evitar asignar accidentalmente en lugar de comparar.
Michael
1
@MarkAmery Tenga en cuenta también que las palabras clave de visibilidad se analizan mediante herramientas de documentación automatizadas , por lo que proporcionan más "valor de documentación" que solo un comentario que usted introduciría manualmente.
Michael

Respuestas:

15

Siempre debe declarar las propiedades de su clase con anticipación. Si bien PHP es un lenguaje dinámico y estará encantado de crear sus propiedades en tiempo de ejecución, hay varias desventajas en este camino.

  • El compilador puede optimizar las propiedades declaradas. Cuando declara dinámicamente una propiedad, este es un impacto en el rendimiento ya que el compilador tiene que crear una tabla dinámica de hash para almacenar sus propiedades dinámicas.
  • No estoy al 100% en este caso, pero creo que los optimizadores de bytecode como APC no serán tan útiles ya que no tendrán una imagen completa de su clase. (Y los optimizadores como APC son imprescindibles )
  • Hace que su código sea 1000 veces más difícil de leer. La gente te odiará.
  • Hace que sea 1000 veces más probable que se va a cometer un error. (¿Fue getId o getID? Espera, usaste ambos. Falló)
  • Sin autocompletar IDE o sugerencias de tipo.
  • Los generadores de documentación no verán su propiedad.

El problema que describió es realmente menor en comparación con el problema de no declarar sus propiedades en la definición de su clase. Aquí hay una buena solución.

Acostúmbrese a declarar valores predeterminados para sus propiedades.

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

Excepto por las propiedades que almacenarán objetos, esto cubrirá la mayoría de los tipos base que almacenará. Las propiedades que almacenarán objetos se pueden establecer en su constructor.

Una buena regla para el diseño de clases es que después de que su objeto haya sido creado y su constructor se haya ejecutado, no debería tener ninguna propiedad "indefinida".

Ortigas Jarrod
fuente
En respuesta a sus 5 inconvenientes: 1 (Rendimiento): ¿Tiene una fuente para esto? 2 (legibilidad): no estoy seguro de entender; la propuesta era comentar las declaraciones, no solo eliminarlas, entonces, ¿qué legibilidad se pierde? Supongo que el resaltado de sintaxis es un poco menos amigable y la dificultad potencial para distinguirlo de los comentarios vecinos. 3: Este no lo entiendo en absoluto; puedes aclarar? 4 (IDEs): bastante justo: no tengo experiencia en codificar PHP con un IDE y no sabía sobre las sugerencias de tipo de Eclipse. 5 (generadores de documentos): bastante justo, nunca usé uno de estos.
Mark Amery
No vi tu línea sobre comentarlos. De todos modos, los puntos anteriores todavía se aplican: ¿cómo saber cuáles están activos o legítimamente comentados?
Jarrod Nettles
En respuesta a su solución propuesta: esto es precisamente lo que quiero evitar: la asignación de valores predeterminados incluso cuando no tienen sentido y los valores predeterminados nunca deben usarse. El problema con estos valores predeterminados (y con la declaración sin asignación, que simplemente asigna nulo) es que si, debido a un error de programación, no asigno un valor a algo en el constructor cuando debería hacerlo, en lugar de que PHP dé un advertencia cuando intento usar ese valor más tarde, ayudándome a detectar mi error, todo parecerá funcionar bien aunque la clase esté rota.
Mark Amery
2
@MarkAmery Sin embargo, su error de programación es esencialmente un error tipográfico. Todos los hemos tenido, pero estás tratando de detonar una bomba para matar una mosca aquí.
Jarrod Nettles
Puede que tengas razón. Pasé un par de horas rastreando un error hoy que resultó ser causado por un error tipográfico de ese tipo, y luego me sentí frustrado al darme cuenta de que si no hubiera usado las declaraciones de campo, habría recibido un aviso y sabía de inmediato dónde estaba mi error (que siempre pensé que sucedería en estas circunstancias de todos modos; no me di cuenta de que los campos inicializados de las declaraciones eran nulos). La accesibilidad inmediata de esa experiencia probablemente esté sesgando mi juicio sobre la probabilidad de que se repita o lo valioso que sea defenderse.
Mark Amery
2

Trabajé en un código que usaba propiedades de objeto creadas dinámicamente. Pensé que usar propiedades de objeto creadas dinámicamente era bastante bueno (cierto, en mi opinión). Sin embargo, mi programa tardó 7 segundos en ejecutarse. Eliminé las propiedades del objeto dinámico y las reemplacé como propiedades declaradas como parte de cada clase (público en este caso). El tiempo de CPU pasó de más de 7 segundos a 0.177 segundos. Eso es bastante sustancial.

Es posible que estaba haciendo algo mal en la forma en que estaba usando propiedades de objetos dinámicos. También es posible que mi configuración esté rota de alguna manera. Por supuesto, debería decir que tengo una configuración PHP vainilla muy simple en mi máquina.

usuario328304
fuente