60 votes

Propriétés PHP en lecture seule ?

En utilisant les classes DOM de PHP (DOMNode, DOMEElement, etc), j'ai remarqué qu'elles possèdent des propriétés en lecture seule. Par exemple, je peux lire la propriété $nodeName d'un DOMNode, mais je ne peux pas y écrire (si je le fais, PHP envoie une erreur fatale).

Comment puis-je créer mes propres propriétés en lecture seule en PHP ?

10 votes

Il semble que "readonly" soit un mot clé spécial qui ne peut être utilisé qu'avec des classes compilées en PHP. C'est dommage, car "readonly public" serait un excellent moyen d'éviter d'utiliser __get() et __set().

2 votes

Ceci a été considéré comme un RFC en 2014 ( wiki.php.net/rfc/readonly_properties ), mais il a été retiré après un certain nombre de contestations ( markmail.org/message/7l3ci3sboma2nlzq ). J'aurais aimé voir readonly comme mot-clé pour les propriétés, rendrait la vie beaucoup plus facile au lieu de définir constamment des getters ou d'utiliser le modèle Proxy

0 votes

44voto

too much php Points 27983

Vous pouvez le faire comme ça :

class Example {
    private $__readOnly = 'hello world';
    function __get($name) {
        if($name === 'readOnly')
            return $this->__readOnly;
        user_error("Invalid property: " . __CLASS__ . "->$name");
    }
    function __set($name, $value) {
        user_error("Can't set property: " . __CLASS__ . "->$name");
    }
}

Ne l'utilisez que lorsque vous en avez vraiment besoin - il est plus lent que l'accès normal à la propriété. En PHP, il est préférable d'adopter une politique consistant à n'utiliser que les méthodes setter pour modifier une propriété depuis l'extérieur.

6 votes

En effet ! La méthode __get est extrêmement lente. J'avais une classe de configuration qui utilisait quelque chose comme ça, de sorte que chaque variable privée pouvait être accédée mais pas modifiée. Quand j'ai passé mon code dans un profileur, j'ai été choqué par le temps qu'il consommait :( Vraiment triste. J'ai souhaité que php ait l'attribut readonly.

0 votes

Sans __get Si l'on veut que le système soit efficace, il ne peut être mis en œuvre qu'en interne (dans une extension).

0 votes

La chose étrange est qu'ils les documentent comme readonly public au lieu de privé.

17voto

Jsowa Points 4130

Depuis PHP 8.1, il existe des propriétés natives en lecture seule.

Documentation

Vous ne pouvez initialiser une propriété en lecture seule qu'une seule fois lors de la déclaration de la propriété.

class Test {
    public readonly string $prop;

    public function __construct(string $prop) {
        $this->prop = $prop;
    }
}

--

class Test {
    public function __construct(
        public readonly string $prop,
    ) {}
}

Essayer de modifier la propriété en lecture seule provoquera l'erreur suivante :

Error: Cannot modify readonly property Test::$prop

0 votes

La plus grande différence entre le nouveau mot-clé readonly et le mot-clé const sera visible au niveau de l'héritage. Avant la version 8.1, la constante n'était pas héritée et n'était donc accessible qu'à sa propre classe. Toute classe héritée devait à nouveau implémenter cette constante ou utiliser le mot-clé parent pour appeler la classe mère et n'était pas en mesure de définir une valeur différente lors de l'initialisation.

12voto

Matt Points 41

Mais les propriétés privées exposées uniquement à l'aide de __get() ne sont pas visibles pour les fonctions qui énumèrent les membres d'un objet - json_encode() par exemple.

Je passe régulièrement des objets PHP à Javascript en utilisant json_encode() car cela semble être un bon moyen de passer des structures complexes avec beaucoup de données provenant d'une base de données. Je dois utiliser des propriétés publiques dans ces objets pour que ces données soient transmises au Javascript qui les utilise, mais cela signifie que ces propriétés doivent être publiques (et donc courir le risque qu'un autre programmeur qui n'est pas sur la même longueur d'onde (ou probablement moi-même après une mauvaise nuit) puisse les modifier directement). Si je les rends privées et utilise __get() et __set(), alors json_encode() ne les voit pas.

Ne serait-il pas agréable d'avoir un mot-clé d'accessibilité "en lecture seule" ?

0 votes

Si une variable n'est pas destinée à être éditée directement bien qu'elle soit public les programmeurs PHP utilisent souvent la convention $pleaseTouch contre $_doNotTouch pour signaler si une propriété donnée doit être utilisée en externe ou non.

5 votes

Une classe qui implémente JsonSerializable peut avoir une propriété personnalisée à encoder en la définissant via l'interface jsonSerialize méthode. Vous pouvez y afficher les propriétés privées que vous souhaitez encoder.

6voto

Je vois que vous avez déjà obtenu votre réponse, mais pour ceux qui cherchent encore.. :

Il suffit de déclarer toutes les variables "en lecture seule" comme étant privées ou protégées et d'utiliser la méthode magique __get() comme ceci :

/**
 * This is used to fetch readonly variables, you can not read the registry
 * instance reference through here.
 * 
 * @param string $var
 * @return bool|string|array
 */
public function __get($var)
{
    return ($var != "instance" && isset($this->$var)) ? $this->$var : false;
}

Comme vous pouvez le voir, j'ai également protégé la variable $this->instance car cette méthode permettra aux utilisateurs de lire toutes les variables déclarées. Pour bloquer plusieurs variables, utilisez un tableau avec in_array().

6voto

Voici un moyen de rendre toutes les propriétés de votre classe en lecture seule depuis l'extérieur, les classes héritées ont un accès en écriture ;-).

class Test {
    protected $foo;
    protected $bar;

    public function __construct($foo, $bar) {
        $this->foo = $foo;
        $this->bar = $bar;
    }

/**
 * All property accessible from outside but readonly
 * if property does not exist return null
 *
 * @param string $name
 *
 * @return mixed|null
 */
    public function __get ($name) {
        return $this->$name ?? null;
    }

/**
 * __set trap, property not writeable
 *
 * @param string $name
 * @param mixed $value
 *
 * @return mixed
 */
    function __set ($name, $value) {
        return $value;
    }
}

testé en php7

0 votes

Vos propriétés doivent être inaccessibles pour que cela fonctionne, vous devez donc modifier les éléments suivants public à private o protected .

0 votes

@story avez-vous testé ? parce que je l'ai fait et ça marche, $foo et $bar sont lisibles de l'extérieur mais ne sont pas inscriptibles (sauf dans la classe et la classe héritée). Je ne sais pas si c'est documenté mais de l'extérieur __get et __set trap ont la priorité. l'utilité de public ici est pour l'introspection de l'IDE.

0 votes

Oui, je l'ai testé. Vous pouvez obtenir et définir très bien, mais cela ne passera pas par vos méthodes magiques. stackoverflow.com/questions/4713680/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X