Cet article est traduit, adresse originale : https://stitcher.io/blog/php-81-readonly-properties
PHP 8.1 : Propriétés en lecture seule
Depuis de nombreuses années, j'utilise PHP pour écrire des objets et des valeurs de transfert de données Les objets deviennent très faciles. Prenons l'exemple d'un DTO en PHP 5.6 :
class BlogData { /** @var string */ private $title; /** @var Status */ private $status; /** @var \DateTimeImmutable|null */ private $publishedAt; /** * @param string $title * @param Status $status * @param \DateTimeImmutable|null $publishedAt */ public function __construct( $title, $status, $publishedAt = null ) { $this->title = $title; $this->status = $status; $this->publishedAt = $publishedAt; } /** * @return string */ public function getTitle() { return $this->title; } /** * @return Status */ public function getStatus() { return $this->status; } /** * @return \DateTimeImmutable|null */ public function getPublishedAt() { return $this->publishedAt; } }
et comparez-le à l'équivalent en PHP 8.0 :
class BlogData { public function __construct( private string $title, private Status $status, private ?DateTimeImmutable $publishedAt = null, ) {} public function getTitle(): string { return $this->title; } public function getStatus(): Status { return $this->status; } public function getPublishedAt(): ?DateTimeImmutable { return $this->publishedAt; } }
C'est assez différent, même si je pense qu'il y a quand même un gros problème : tous ces getters. Personnellement, je ne les utilise plus depuis PHP 8.0 et ses propriétés améliorées. Je préfère simplement utiliser des propriétés publiques plutôt que d'ajouter des getters :
class BlogData { public function __construct( public string $title, public Status $status, public ?DateTimeImmutable $publishedAt = null, ) {} }
Les puristes orientés objet n'aiment pas cette approche : l'état interne d'un objet ne doit pas être directement exposé et ne peut certainement pas être modifié de l'extérieur.
Dans notre projet chez Spatie, nous avons une règle de guide de style interne selon laquelle les DTO et VO avec des propriétés publiques ne doivent pas être modifiés en externe ; une pratique qui semble bien fonctionner et nous le faisons depuis longtemps, aucun problème rencontré.
Cependant, oui ; je suis d’accord, il serait préférable que le langage garantisse que les propriétés publiques ne soient pas du tout annulées. Eh bien, PHP 8.1 a résolu tous ces problèmes en introduisant le mot-clé en lecture seule :
class BlogData { public function __construct( public readonly string $title, public readonly Status $status, public readonly ?DateTimeImmutable $publishedAt = null, ) {} }
Ce mot-clé fait essentiellement ce que son nom suggère : une fois qu'une propriété est définie, elle ne peut plus être remplacée :
$blog = new BlogData( title: 'PHP 8.1: readonly properties', status: Status::PUBLISHED, publishedAt: now() ); $blog->title = 'Another title'; Error: Cannot modify readonly property BlogData::$title
Sachant que lorsqu'un objet est construit, il ne peut plus changer, offre un niveau de certitude et de tranquillité d'esprit lors de l'écriture du code : une série de changements de données imprévus ne peut tout simplement pas se reproduire.
Bien sûr, vous souhaitez toujours pouvoir copier les données vers le nouvel objet, et éventuellement modifier certaines propriétés au cours du processus. Nous verrons plus loin dans cet article comment procéder à l'aide de propriétés en lecture seule. Tout d’abord, examinons-les de plus près.
Voulez-vous en savoir plus sur PHP 8.1 ? Il existe un chemin vers PHP 8.1. Pendant les 10 prochains jours, vous recevrez un e-mail quotidien concernant une fonctionnalité nouvelle et existante de PHP 8.1, vous serez alors automatiquement désabonné afin de ne pas recevoir de spam ou d'e-mails de suivi. Abonnez-vous maintenant !
#Propriétés d'entrée uniquement
Les propriétés en lecture seule ne peuvent être utilisées qu'en combinaison avec des propriétés typées :
class BlogData { public readonly string $title; public readonly $mixed; }
Cependant, vous pouvez utiliser Mixed comme indice de type :
class BlogData { public readonly string $title; public readonly mixed $mixed; }
La raison de cette limitation est en omettant le type de propriété, null PHP définira automatiquement la valeur de la propriété si aucune valeur explicite n'est fournie dans le constructeur. Ce comportement, combiné à la lecture seule, peut entraîner une confusion inutile.
#Propriétés normales et propriétés promues
Vous avez vu des exemples des deux : la lecture seule peut être ajoutée sur les propriétés normales et les propriétés promues :
class BlogData { public readonly string $title; public function __construct( public readonly Status $status, ) {} }
#Aucune valeur par défaut
Les propriétés en lecture seule ne peuvent pas avoir de valeurs par défaut :
class BlogData { public readonly string $title = 'Readonly properties'; }
Autrement dit, à moins qu'il ne s'agisse de propriétés promues :
class BlogData { public function __construct( public readonly string $title = 'Readonly properties', ) {} }
Il est permis de promouvoir des propriétés car la valeur par défaut d'une propriété promue n'est pas utilisée comme valeur par défaut de la propriété de classe, mais s'applique uniquement aux paramètres du constructeur . Dans les coulisses, le code ci-dessus se traduira par :
class BlogData { public readonly string $title; public function __construct( string $title = 'Readonly properties', ) { $this->title = $title; } }
Vous pouvez voir comment la propriété réelle ne se voit pas attribuer de valeur par défaut. À propos, la raison pour laquelle les valeurs par défaut des propriétés en lecture seule ne sont pas autorisées est qu'elles ne sont pas différentes des constantes de cette forme.
#legacy
Aucune modification autorisée pour l'indicateur en lecture seule lors de l'héritage :
class Foo { public readonly int $prop; } class Bar extends Foo { public int $prop; }
Cette règle va dans les deux sens : l'indicateur en lecture seule ne peut pas être ajouté ou supprimé pendant l'héritage.
#La désactivation n'est pas autorisée
Une fois qu'une propriété en lecture seule est définie, vous ne pouvez pas la modifier, ni même la supprimer :
$foo = new Foo('value'); unset($foo->prop);
#Réflexion
Il existe une nouvelle méthode, ainsi qu'un indicateur . ReflectionProperty::isReadOnly()ReflectionProperty::IS_READONLY
#Clone
Donc, si vous ne pouvez pas modifier les propriétés en lecture seule et que vous ne pouvez pas les supprimer, comment créer une copie d'un DTO ou d'un VO et modifier certaines de ses données ? Vous ne pouvez pas les cloner car vous ne pourrez pas écraser leurs valeurs. Il y avait en fait une idée de cloner avec une construction qui permettrait ce comportement à l'avenir, mais cela ne résoudrait pas notre problème pour le moment.
Eh bien, si vous comptez sur un peu de magie de réflexion, vous pouvez copier un objet avec une propriété en lecture seule modifiée. En créant un objet sans appeler son constructeur (cela peut être fait en utilisant la réflexion), puis en copiant manuellement chaque propriété - en écrasant parfois sa valeur - vous pouvez réellement "cloner" un objet et modifier ses propriétés en lecture seule.
J'ai fait un petit package pour ce faire et ça ressemble à ceci :
class BlogData { use Cloneable; public function __construct( public readonly string $title, ) {} } $dataA = new BlogData('Title'); $dataB = $dataA->with(title: 'Another title');
J'ai en fait écrit un article de blog dédié expliquant les mécanismes derrière tout cela que vous pouvez lire ici.
Il s’agit donc de propriétés en lecture seule. Je pense qu'ils constituent une fonctionnalité intéressante si vous travaillez sur un projet qui traite de nombreux DTO et VO et vous oblige à gérer soigneusement le flux de données dans tout votre code. Les objets immuables avec des propriétés en lecture seule sont très utiles à cet égard.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!