web-dev-qa-db-fra.com

PHP Propriétés en lecture seule?

En utilisant les classes DOM de PHP (DOMNode, DOMEElement, etc.), j'ai remarqué qu'elles possèdent des propriétés vraiment 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 génère une erreur fatale).

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

56
mazniak

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");
    }
}

Utilisez-le uniquement lorsque vous en avez vraiment besoin - il est plus lent que l'accès normal à la propriété. Pour PHP, il est préférable d’adopter une politique qui consiste à n’utiliser que les méthodes de définition pour modifier une propriété de l’extérieur.

39
too much php

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

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 renseignées à partir d'une base de données. Je dois utiliser des propriétés publiques dans ces objets pour que ces données soient renseignées jusqu'au Javascript qui les utilise, mais cela signifie que ces propriétés doivent être publiques (et risquent donc qu'un autre programmeur ne soit pas sur la même longueur d'onde (ou probablement moi-même après une mauvaise nuit) pourrait les modifier directement). Si je les rend privé et utilise __get () et __set (), json_encode () ne les voit pas.

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

11
Matt

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

Déclarez simplement toutes les variables "en lecture seule" comme étant privées ou protégées et utilisez 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 constater, j'ai également protégé la variable d'instance $ this-> 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 ().

5
Daniel

Voici un moyen de rendre toutes les propriétés de votre classe read_only de l'extérieur, la classe héritée ayant 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

2

Pour ceux qui recherchent un moyen d'exposer vos propriétés privées/protégées pour la sérialisation, si vous choisissez d'utiliser une méthode getter pour les rendre en lecture seule, voici une façon de procéder (@Matt: pour json par exemple):

interface json_serialize {
    public function json_encode( $asJson = true );
    public function json_decode( $value );
}

class test implements json_serialize {
    public $obj = null;
    protected $num = 123;
    protected $string = 'string';
    protected $vars = array( 'array', 'array' );
    // getter
    public function __get( $name ) {
        return( $this->$name );
    }
    // json_decode
    public function json_encode( $asJson = true ) {
        $result = array();
        foreach( $this as $key => $value )
            if( is_object( $value ) ) {
                if( $value instanceof json_serialize )
                    $result[$key] = $value->json_encode( false );
                else
                    trigger_error( 'Object not encoded: ' . get_class( $this ).'::'.$key, E_USER_WARNING );
            } else
                $result[$key] = $value;
        return( $asJson ? json_encode( $result ) : $result );
    }
    // json_encode
    public function json_decode( $value ) {
        $json = json_decode( $value, true );
        foreach( $json as $key => $value ) {
            // recursively loop through each variable reset them
        }
    }
}
$test = new test();
$test->obj = new test();
echo $test->string;
echo $test->json_encode();
0
Precastic