A menos que esté completamente equivocado, se supone que los métodos __get
y __set
permiten la sobrecarga de → get
y set
.
Por ejemplo, las siguientes declaraciones deben invocar el __get
método:
echo $foo->bar;
$var = $foo->bar;
Y lo siguiente debería usar el __set
método:
$foo->bar = 'test';
Esto no funcionaba en mi código y es reproducible con este simple ejemplo:
class foo {
public $bar;
public function __get($name) {
echo "Get:$name";
return $this->$name;
}
public function __set($name, $value) {
echo "Set:$name to $value";
$this->$name = $value;
}
}
$foo = new foo();
echo $foo->bar;
$foo->bar = 'test';
echo "[$foo->bar]";
Esto solo da como resultado:
[test]
Poner algunas die()
llamadas allí muestra que no lo está haciendo en absoluto.
Por ahora, acabo de decir que se joda, y estoy usando manualmente __get
donde se necesita por ahora, pero eso no es muy dinámico y requiere conocimiento de que el código 'sobrecargado' de hecho no se llama a menos que se llame específicamente. Me gustaría saber si se supone que esto no funciona de la manera que he entendido que debería o por qué no funciona.
Esto sigue funcionando php 5.3.3
.
fuente
node
no es una propiedad de $ foo sino una propiedad dedoesNotExist
. Entonces, a menos que 'doesNotExist' sea un objeto (que implemente __set o tenga una propiedad pública llamada nodo), no funcionará.Recomendaría usar una matriz para almacenar todos los valores a través de
__set()
.class foo { protected $values = array(); public function __get( $key ) { return $this->values[ $key ]; } public function __set( $key, $value ) { $this->values[ $key ] = $value; } }
De esta forma te aseguras de no poder acceder a las variables de otra forma (ten en cuenta que
$values
está protegido), para evitar colisiones.fuente
Del manual de PHP :
Esto solo se solicita en propiedades inaccesibles de lectura / escritura . Sin embargo, su propiedad es pública, lo que significa que es accesible. Cambiar el modificador de acceso a protegido resuelve el problema.
fuente
Para ampliar la respuesta de Berry, que establecer el nivel de acceso en protected permite que __get y __set se usen con propiedades declaradas explícitamente (cuando se accede fuera de la clase, al menos) y la velocidad es considerablemente más lenta, citaré un comentario de otra pregunta sobre este tema y exponga su uso de todos modos:
Estoy de acuerdo en que __get es más lento para una función get personalizada (haciendo las mismas cosas), este es 0.0124455 el tiempo para __get () y este 0.0024445 es para get () personalizado después de 10000 bucles. - Melsi 23 de noviembre de 2012 a las 22:32 Mejores prácticas: Métodos PHP Magic __set y __get
Según las pruebas de Melsi, considerablemente más lento es aproximadamente 5 veces más lento. Eso es definitivamente considerablemente más lento, pero también tenga en cuenta que las pruebas muestran que aún puede acceder a una propiedad con este método 10,000 veces, contando el tiempo para la iteración del ciclo, en aproximadamente 1/100 de segundo. Es considerablemente más lento en comparación con los métodos get y set definidos, y eso es quedarse corto, pero en el gran esquema de las cosas, incluso 5 veces más lento nunca es realmente lento.
El tiempo de cálculo de la operación sigue siendo insignificante y no vale la pena considerarlo en el 99% de las aplicaciones del mundo real. El único momento en que realmente debería evitarse es cuando en realidad va a acceder a las propiedades más de 10,000 veces en una sola solicitud. Los sitios de alto tráfico están haciendo algo realmente mal si no pueden permitirse lanzar algunos servidores más para mantener sus aplicaciones en funcionamiento. Un anuncio de texto de una sola línea en el pie de página de un sitio de alto tráfico donde la tasa de acceso se convierte en un problema probablemente podría pagar por una granja de 1,000 servidores con esa línea de texto. El usuario final nunca va a tocar sus dedos preguntándose qué está tardando tanto en cargar la página porque el acceso a la propiedad de su aplicación tarda una millonésima de segundo.
Digo esto hablando como un desarrollador con experiencia en .NET, pero los métodos invisibles de obtención y configuración para el consumidor no son una invención de .NET. Simplemente no son propiedades sin ellos, y estos métodos mágicos son la gracia salvadora del desarrollador de PHP para incluso llamar "propiedades" a su versión de propiedades. Además, la extensión de Visual Studio para PHP admite intellisense con propiedades protegidas, con ese truco en mente, creo. Creo que con suficientes desarrolladores usando los métodos mágicos __get y __set de esta manera, los desarrolladores de PHP ajustarían el tiempo de ejecución para atender a la comunidad de desarrolladores.
Editar: En teoría, las propiedades protegidas parecían funcionar en la mayoría de las situaciones. En la práctica, resulta que muchas veces querrá utilizar sus captadores y definidores al acceder a propiedades dentro de la definición de clase y las clases extendidas. Una mejor solución es una clase base y una interfaz para cuando se extienden otras clases, por lo que puede copiar las pocas líneas de código de la clase base a la clase de implementación. Estoy haciendo un poco más con la clase base de mi proyecto, por lo que no tengo una interfaz para proporcionar en este momento, pero aquí está la definición de clase simplificada sin probar con la propiedad mágica obteniendo y configurando usando la reflexión para eliminar y mover las propiedades a una matriz protegida:
/** Base class with magic property __get() and __set() support for defined properties. */ class Component { /** Gets the properties of the class stored after removing the original * definitions to trigger magic __get() and __set() methods when accessed. */ protected $properties = array(); /** Provides property get support. Add a case for the property name to * expand (no break;) or replace (break;) the default get method. When * overriding, call parent::__get($name) first and return if not null, * then be sure to check that the property is in the overriding class * before doing anything, and to implement the default get routine. */ public function __get($name) { $caller = array_shift(debug_backtrace()); $max_access = ReflectionProperty::IS_PUBLIC; if (is_subclass_of($caller['class'], get_class($this))) $max_access = ReflectionProperty::IS_PROTECTED; if ($caller['class'] == get_class($this)) $max_access = ReflectionProperty::IS_PRIVATE; if (!empty($this->properties[$name]) && $this->properties[$name]->class == get_class() && $this->properties[$name]->access <= $max_access) switch ($name) { default: return $this->properties[$name]->value; } } /** Provides property set support. Add a case for the property name to * expand (no break;) or replace (break;) the default set method. When * overriding, call parent::__set($name, $value) first, then be sure to * check that the property is in the overriding class before doing anything, * and to implement the default set routine. */ public function __set($name, $value) { $caller = array_shift(debug_backtrace()); $max_access = ReflectionProperty::IS_PUBLIC; if (is_subclass_of($caller['class'], get_class($this))) $max_access = ReflectionProperty::IS_PROTECTED; if ($caller['class'] == get_class($this)) $max_access = ReflectionProperty::IS_PRIVATE; if (!empty($this->properties[$name]) && $this->properties[$name]->class == get_class() && $this->properties[$name]->access <= $max_access) switch ($name) { default: $this->properties[$name]->value = $value; } } /** Constructor for the Component. Call first when overriding. */ function __construct() { // Removing and moving properties to $properties property for magic // __get() and __set() support. $reflected_class = new ReflectionClass($this); $properties = array(); foreach ($reflected_class->getProperties() as $property) { if ($property->isStatic()) { continue; } $properties[$property->name] = (object)array( 'name' => $property->name, 'value' => $property->value , 'access' => $property->getModifier(), 'class' => get_class($this)); unset($this->{$property->name}); } $this->properties = $properties; } }
Mis disculpas si hay algún error en el código.
fuente
Es porque $ bar es una propiedad pública.
$foo->bar = 'test';
No es necesario llamar al método mágico cuando se ejecuta lo anterior.
Eliminar
public $bar;
de tu clase debería corregir esto.fuente
Utilice mejor los métodos de configuración / obtención mágicos con métodos predefinidos de configuración / obtención personalizados como en el ejemplo siguiente. De esta forma puedes combinar lo mejor de dos mundos. En términos de velocidad, estoy de acuerdo en que son un poco más lentos, pero ¿puedes sentir la diferencia? El siguiente ejemplo también valida la matriz de datos con los establecedores predefinidos.
Es por eso que debemos usar ambos.
EJEMPLO DE ARTÍCULO DE CLASE
/* * Item class */ class Item{ private $data = array(); function __construct($options=""){ //set default to none $this->setNewDataClass($options); //calling function } private function setNewDataClass($options){ foreach ($options as $key => $value) { $method = 'set'.ucfirst($key); //capitalize first letter of the key to preserve camel case convention naming if(is_callable(array($this, $method))){ //use seters setMethod() to set value for this data[key]; $this->$method($value); //execute the setters function }else{ $this->data[$key] = $value; //create new set data[key] = value without seeters; } } } private function setNameOfTheItem($value){ // no filter $this->data['name'] = strtoupper($value); //assign the value return $this->data['name']; // return the value - optional } private function setWeight($value){ //use some kind of filter if($value >= "100"){ $value = "this item is too heavy - sorry - exceeded weight of maximum 99 kg [setters filter]"; } $this->data['weight'] = strtoupper($value); //asign the value return $this->data['weight']; // return the value - optional } function __set($key, $value){ $method = 'set'.ucfirst($key); //capitalize first letter of the key to preserv camell case convention naming if(is_callable(array($this, $method))){ //use seters setMethod() to set value for this data[key]; $this->$method($value); //execute the seeter function }else{ $this->data[$key] = $value; //create new set data[key] = value without seeters; } } function __get($key){ return $this->data[$key]; } function dump(){ var_dump($this); } }
INDEX.PHP
$data = array( 'nameOfTheItem' => 'tv', 'weight' => '1000', 'size' => '10x20x30' ); $item = new Item($data); $item->dump(); $item->somethingThatDoNotExists = 0; // this key (key, value) will trigger magic function __set() without any control or check of the input, $item->weight = 99; // this key will trigger predefined setter function of a class - setWeight($value) - value is valid, $item->dump(); $item->weight = 111; // this key will trigger predefined setter function of a class - setWeight($value) - value invalid - will generate warning. $item->dump(); // display object info
SALIDA
object(Item)[1] private 'data' => array (size=3) 'name' => string 'TV' (length=2) 'weight' => string 'THIS ITEM IS TOO HEAVY - SORRY - EXIDED WEIGHT OF MAXIMUM 99 KG [SETTERS FILTER]' (length=80) 'size' => string '10x20x30' (length=8) object(Item)[1] private 'data' => array (size=4) 'name' => string 'TV' (length=2) 'weight' => string '99' (length=2) 'size' => string '10x20x30' (length=8) 'somethingThatDoNotExists' => int 0 object(Item)[1] private 'data' => array (size=4) 'name' => string 'TV' (length=2) 'weight' => string 'THIS ITEM IS TOO HEAVY - SORRY - EXIDED WEIGHT OF MAXIMUM 99 KG [SETTERS FILTER]' (length=80) 'size' => string '10x20x30' (length=8) 'somethingThatDoNotExists' => int 0
fuente
Suelta la
public $bar;
declaración y debería funcionar como se esperaba.fuente
Intenta con:
__GET($k){ return $this->$k; } _SET($k,$v){ return $this->$k = $v; }
fuente