Magento 2: ¿buenas prácticas para usar / evitar atrapadores de magia?

21

Los captadores de magia en Varien_Object(M1) y DataObject(M2) son una práctica común, pero con Magento 2 se siente mal usarlo.

Bueno:

  • fácil de leer / escribir

Malo

Pregunta

Con Magento 2 tenemos dos nuevos métodos:

  • getDataByKey($key)
  • getDataByPath($path)

¿Hay alguna buena razón para seguir usando getData($key)o algún objeto mágico?


Editar:

@Vinai gracias. No mencioné el @methodmétodo, porque mi enfoque era bastante diferente.

Solo ayuda al IDE, pero no tiene impacto en otras cosas.

Existen varios PR combinados que son "microoptimizaciones" como la conversión en (int)lugar de intval()u obtener el tamaño de la matriz fuera de los bucles (incluso para matrices pequeñas).

Por otro lado hay

  1. captadores mágicos, que tienen algo de "sobrecarga" como describió Marius ...

    strtolower(trim(preg_replace('/([A-Z]|[0-9]+)/', "_$1", $name), '_'));
  2. getData($key) Mehtods también tienen que hacer 2-3 controles adicionales ...

    • if ('' === $key) {
    • if (strpos($key, '/')) {
    • if ($index !== null) {

Para el propio código, estoy totalmente de acuerdo en preferir métodos reales, pero en los mismos casos no es posible ... por ejemplo, ha creado un evento personalizado ...

$value = $observer->getVar_1();
$value = $observer->getData('var_1');
$value = $observer->getDataByKey('var_1');

Usar 3rd con me /** @var some $value */parece mejor. (?)

sv3n
fuente
1
Puede agregar los métodos en la clase doc bloc, para que las herramientas de análisis de código no se quejen de métodos no existentes. También creo que usar dígitos en las claves es en sí mismo una mala práctica, por lo que no debería aparecer como "Malo" aquí.
Lily Bergonzat

Respuestas:

20

La pregunta anterior es sobre el uso de métodos mágicos vs. getDataByKeyo getDataByPath. Creo que también hay una tercera opción, y que está implementando métodos getter y setter reales.

Los getData*métodos, todos ellos tienen la desventaja de que tienen que ser anotado para la inferencia de tipos de trabajo.
Por lo general, eso se hace con una /* @var string $foo */anotación sobre la getData*llamada.
Esto es un poco mal, porque el tipo de datos debe declararse en la clase que contiene los datos, no en la clase que llama getData*.
La razón de esto es que si los datos cambian, es más probable que la clase se actualice, no todos los getData*sitios de llamadas.
Es por eso que creo que los métodos reales aumentan la capacidad de mantenimiento en comparación con el uso de getData*accesores.

Así que creo que se reduce a una compensación entre la mantenibilidad y la implementación más rápida (menos código para escribir).

Afortunadamente, hoy en día los IDE son realmente buenos para crear las implementaciones getter y setter para nosotros, por lo que ese argumento ya no se aplica.

Otro argumento contra los captadores y setters mágicos que falta en la pregunta anterior es que no es posible crear complementos para ellos.

El único otro valor que creo que puedo agregar al tema es tratar de recopilar las razones para usar o no usar @methodanotaciones, si la implementación de métodos reales está fuera de discusión por alguna razón.

Pros

  • Una @methodanotación es un poco menos de código para escribir en comparación con la implementación de un getter y setter real. Sin embargo, esto es apenas cierto hoy en día porque los IDE son buenos para generar métodos de acceso, por lo que ya no es un beneficio real.

Contras

  • Es fácil que las cosas salgan mal.
    • Las anotaciones son comentarios, se vuelven obsoletas fácilmente cuando el código evoluciona, pero las anotaciones no se actualizan. Los métodos reales son más robustos.
    • Es posible agregar múltiples anotaciones con diferentes firmas de tipo sin un error de intérprete: el comportamiento del análisis de código estático no está definido y puede generar errores sutiles que son difíciles de rastrear.
    • Si @methodexisten una anotación y un método real con el mismo nombre, la firma del tipo de anulación anula el método real durante el análisis de código estático, que es lo contrario de lo que hace el intérprete PHP. Esto nuevamente puede conducir fácilmente a errores sutiles.

Por las razones anteriores, personalmente no uso @methodanotaciones si puedo evitarlas.
Para el código que está destinado a vivir mucho tiempo, implemento métodos getter y setter reales. La ganancia de mantenimiento vale el esfuerzo de activar el IDE para generarlos.

Para obtener más código experimental durante un pico, o para un simple detalle de implementación de un módulo getData*, también uso métodos, porque soy flojo.

Vinaí
fuente
Buen resumen Gracias vinai Eso responde más de lo que realmente pregunté.
sv3n
1

Los getData*métodos, todos ellos tienen la desventaja de que tienen que ser anotado para la inferencia de tipos de trabajo.

Por lo general, eso se hace con una /*@var string $foo */anotación sobre la getData*llamada. Esto es un poco mal, porque el tipo de datos debe declararse en la clase que contiene los datos, no en la clase que llama a getData *.

La razón de esto es que si los datos cambian, es más probable que la clase se actualice, no todos los getData*sitios de llamadas. Es por eso que creo que los métodos reales aumentan la capacidad de mantenimiento en comparación con el uso de accesos getData *.

Sí, huele mal, pero puede (¿y debería?) Evitarse. Creo que este es un código muy común y a menudo sugerido:

/** @var Foo $product */
$product = $model->getProduct()
if ($product->getId()) {
    $product->doSomething();
}

El problema es que simplemente adivina que el valor de retorno es de tipo Foocon un getId()método invocable .

Para mantenimiento, ¿por qué no asumir el tipo de variable y agregar un InvalidArgumentException?

$product = $model->getProduct()
if ($product instanceof Foo && $product->getId()) {
    $product->doSomething();
}

Esto también corrige el análisis de código estático en caso de que $model->getProduct()tenga diferentes tipos de retorno, como Foo|false. En el primer caso, se quejaría de llamar doSomething()posible false.

sv3n
fuente