Spécialement pour les étudiants du cours Backend Developer en PHP, ils ont préparé la traduction d'un article intéressant sur les effets secondaires d'un outil populaire.
Travailler avec des dates et des heures en PHP est parfois ennuyeux car cela conduit à des bugs inattendus dans le code:
$startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = $startedAt->add(new DateInterval('PT3M')); var_dump($startedAt->format('Ymd H:i:s'));
Les fonctions
$startdate
et
$finishdate
sont pressées pendant trois minutes, car des méthodes comme
add ()
,
sub()
ou
modify()
modifient également l'objet DateTime pour lequel elles sont appelées avant de le renvoyer. L'exemple ci-dessus, bien sûr, montre un comportement indésirable.
Nous pouvons corriger cette erreur en copiant l'objet référencé avant d'interagir avec lui, par exemple:
$startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = clone $startedAt; $finishedAt->add(new DateInterval('PT3M'));
Chaque fois que je rencontre un clone en code PHP, cela sent comme un hack de l'architecture de code défaillante de quelqu'un. Dans ce cas, nous avons utilisé le clonage pour éviter de changer de comportement, mais en même temps, le code est devenu laid et a acquis beaucoup de bruit inutile.
Alternativement, le problème peut être résolu en convertissant l'instance
DateTime
origine en
DateTimeImmutable
:
$startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = DateTimeImmutable::createFromMutable($startedAt)->add(new DateInterval('PT3M'));
Pourquoi ne pas utiliser
DateTimeImmutable
dès le début?
Utilisation sans compromis de DateTimeImmutable
Au lieu d'appliquer manuellement des méthodes de sécurité pour éviter des modifications inattendues lors du passage d'objets de date / heure, utilisez
DateTimeImmutable
, qui encapsule les méthodes, rendant votre code plus fiable.
$startedAt = new DateTimeImmutable('2019-06-30 10:00:00'); $finishedAt = $startedAt->add(new DateInterval('PT3M')); var_dump($startedAt->format('Ymd H:i:s'));
Dans la plupart des cas, le concept d'une date est considéré comme une valeur, nous comparons les dates par leurs valeurs, et lorsque nous changeons la date, elle devient une date différente. Tout cela correspond parfaitement au concept d'
objet de
valeur , et l'une des caractéristiques importantes des objets de valeur est qu'ils sont immuables.
Style de codage détaillé
L'immutabilité vous oblige à réaffecter explicitement un objet
DateTimeImmutable
chaque fois que vous interagissez avec lui, car il ne modifie jamais sa valeur, mais renvoie une copie. Après de nombreuses années de travail avec DateTime et parce que la mutabilité est la valeur par défaut dans de nombreux langages de programmation impératifs, il est difficile de se débarrasser de l'habitude de l'utiliser et de se conformer au nouveau style d'écriture de code qui favorise le remappage:
$this->expiresAt = $this->expiresAt->modify('+1 week');
Les outils d'analyse statistique, tels que
PHPStan et l'
une de ses extensions , peuvent nous avertir si nous omettons l'affectation et utilisons incorrectement
DateTimeImmutable
.
Cependant, un tel biais cognitif vers la variabilité est supprimé lorsque nous effectuons des opérations arithmétiques sur les valeurs des primitives, par exemple:
$a + 3;
. En soi, cela est perçu comme une déclaration dénuée de sens qui manque clairement de réaffectation:
$a = $a + 3;
ou
$A += 3;
. Ce serait cool d'utiliser quelque chose comme ça dans le cas d'objets de valeur, non?
Certains langages de programmation ont un sucre syntaxique appelé surcharge d'
opérateur , qui vous permet d'implémenter des opérateurs dans des types et des classes définis par l'utilisateur afin qu'ils se comportent comme des types de données primitifs. Cela ne me dérangerait pas si PHP empruntait cette astuce à un autre langage de programmation, et nous pourrions écrire comme suit:
$this->expiresAt += '1 week';
Calculs ponctuels
Certaines personnes soutiennent qu'en termes de performances, il est préférable d'utiliser
DateTime
, car les calculs sont effectués dans la même zone d'exécution. Ceci est cependant acceptable si vous n'avez pas besoin d'effectuer des centaines d'opérations et que vous vous souvenez que les liens vers les anciens objets
DateTimeImmutable
seront collectés par le garbage collector, dans la plupart des cas, dans la pratique, la consommation de mémoire ne sera pas un problème.
Bibliothèques de date / heure
Carbon est une bibliothèque extrêmement populaire qui étend l'API Date / Heure en PHP, en ajoutant un ensemble de fonctionnalités riche. Plus précisément, il étend l'API de la classe mutable
DateTime
, dont l'utilisation est contraire au sujet de cet article.
Par conséquent, si vous aimez travailler avec Carbon mais préférez l'immuabilité, je vous suggère de vous familiariser avec
Chronos . Il s'agit d'une bibliothèque autonome, qui était à l'origine basée sur Carbon, en accordant une attention particulière à la fourniture d'objets de date / heure par défaut immuables, mais elle comprend également des options modifiables en cas de besoin.
Modifié (07/05/2019): Il s'est avéré que Carbon a une option date / heure immuable, ce qui est un gros plus. Cependant, la raison pour laquelle je préfère Chronos est que, contrairement à Carbon, il encourage et promeut l'immuabilité par défaut, à la fois dans le code et dans la documentation, et ce sont des facteurs décisifs dans le contexte de cet article.
Dernière pensée
DateTimeImmutable
été introduit pour la première fois dans l'ancien PHP 5.5, mais à ma grande surprise, de nombreux développeurs le découvrent tout à l'heure. Utilisez
DateTimeImmutable
par défaut dans la mesure du possible, mais gardez à l'esprit certains des compromis dont j'ai parlé qui, je pense, sont plus une question d'habitude et de changement de mentalité.
C’est tout. Par tradition, nous attendons vos commentaires, amis.