Especialmente para los estudiantes del curso "Backend PHP Developer", prepararon una traducción de un artículo interesante sobre el efecto secundario de una herramienta popular.
Trabajar con fechas y horas en PHP a veces es molesto porque conduce a errores inesperados en el código:
$startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = $startedAt->add(new DateInterval('PT3M')); var_dump($startedAt->format('Ymd H:i:s'));
Las
$startdate
y
$finishdate
tienen prisa durante tres minutos, porque los métodos como
add ()
,
sub()
o
modify()
también modifican el objeto DateTime para el que se llama antes de devolverlo. El ejemplo anterior, por supuesto, muestra un comportamiento no deseado.
Podemos corregir este error copiando el objeto al que se hace referencia antes de interactuar con él, por ejemplo:
$startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = clone $startedAt; $finishedAt->add(new DateInterval('PT3M'));
Cada vez que me encuentro con un clon en código PHP, huele a un truco de la arquitectura de código fallida de alguien. En este caso, utilizamos la clonación para evitar cambiar el comportamiento, pero al mismo tiempo, el código se volvió feo y adquirió mucho ruido innecesario.
Alternativamente, el problema se puede resolver convirtiendo la instancia original de
DateTime
a
DateTimeImmutable
:
$startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = DateTimeImmutable::createFromMutable($startedAt)->add(new DateInterval('PT3M'));
¿Por qué no usar
DateTimeImmutable
desde el principio?
Uso intransigente de DateTimeImmutable
En lugar de aplicar manualmente métodos de seguridad para evitar cambios inesperados al pasar objetos de fecha / hora, use
DateTimeImmutable
, que encapsula los métodos, haciendo que su código sea más confiable.
$startedAt = new DateTimeImmutable('2019-06-30 10:00:00'); $finishedAt = $startedAt->add(new DateInterval('PT3M')); var_dump($startedAt->format('Ymd H:i:s'));
En la mayoría de los casos, el concepto de una fecha se considera como un valor, comparamos las fechas por sus valores, y cuando cambiamos la fecha, se convierte en una fecha diferente. Todo esto se correlaciona perfectamente con el concepto de
Objeto de
valor , y una de las características importantes de los objetos de valor es que son inmutables.
Estilo de codificación detallada
La inmutabilidad lo obliga a reasignar explícitamente un objeto
DateTimeImmutable
cada vez que interactúa con él, ya que nunca cambia su valor, sino que devuelve una copia. Después de muchos años de trabajar con DateTime y debido a que la mutabilidad es la predeterminada en muchos lenguajes de programación imperativos, es difícil deshacerse del hábito de usarlo y cumplir con el nuevo estilo de código de escritura que promueve la reasignación:
$this->expiresAt = $this->expiresAt->modify('+1 week');
Las herramientas de análisis estadístico, como
PHPStan y
una de sus extensiones , pueden advertirnos si omitimos la asignación y usamos
DateTimeImmutable
incorrectamente.
Sin embargo, tal sesgo cognitivo hacia la variabilidad se suprime cuando realizamos operaciones aritméticas en los valores de las primitivas, por ejemplo:
$a + 3;
. En sí mismo, esto se percibe como una declaración sin sentido que claramente carece de una reasignación:
$a = $a + 3;
o
$A += 3;
. Sería genial usar algo como esto en el caso de los objetos de valor, ¿verdad?
Algunos lenguajes de programación tienen un azúcar sintáctico llamado sobrecarga de
operadores , que le permite implementar operadores en tipos y clases definidos por el usuario para que se comporten como los tipos de datos primitivos. No me importaría si PHP tomó prestado este truco de algún otro lenguaje de programación, y podríamos escribir de la siguiente manera:
$this->expiresAt += '1 week';
Cálculos únicos
Algunas personas argumentan que, en términos de rendimiento, es mejor usar
DateTime
, ya que los cálculos se realizan dentro de la misma área de ejecución. Sin embargo, esto es aceptable si no necesita realizar cientos de operaciones y recuerda que el recolector de basura recopilará enlaces a objetos antiguos
DateTimeImmutable
, en la mayoría de los casos, en la práctica, el consumo de memoria no será un problema.
Bibliotecas de fecha / hora
Carbon es una biblioteca extremadamente popular que extiende la API de fecha / hora en PHP, agregando un rico conjunto de características. Más precisamente, extiende la API de la clase mutable
DateTime
, cuyo uso es contrario al tema de este artículo.
Por lo tanto, si disfruta trabajar con Carbon pero prefiere la inmutabilidad, le sugiero que se familiarice con
Chronos . Esta es una biblioteca independiente, que originalmente se basó en Carbon, prestando especial atención a proporcionar objetos de fecha / hora predeterminados inmutables, pero también incluye opciones mutables en caso de necesidad.
Editado (05/07/2019): Resultó que Carbon tiene una opción de fecha / hora inmutable, que es una gran ventaja. Sin embargo, la razón por la que prefiero Chronos es que, a diferencia de Carbon, alienta y promueve la inmutabilidad predeterminada, tanto en código como en documentación, y estos son factores decisivos en el contexto de este artículo.
Ultimo pensamiento
DateTimeImmutable
se introdujo por primera vez en PHP 5.5 antiguo, pero para mi sorpresa, muchos desarrolladores lo están descubriendo en este momento. Use
DateTimeImmutable
de forma predeterminada siempre que sea posible, pero tenga en cuenta algunas de las compensaciones de las que hablé que creo que son más una cuestión de hábito y un cambio de mentalidad.
Eso es todo. Por tradición, esperamos sus comentarios, amigos.