orientado objetos metodos magicos __call php magic-methods

php - objetos - public function__ get($ k){ return this->$ k



PHP__get y__set métodos mágicos (7)

Del manual de PHP :

  • __set () se ejecuta al escribir datos en propiedades inaccesibles.
  • __get () se utiliza para leer datos de propiedades inaccesibles.

Esto solo se requiere para leer / escribir propiedades inaccesibles . Sin embargo, su propiedad es pública, lo que significa que es accesible. Cambiar el modificador de acceso a protegido resuelve el problema.

A menos que esté completamente equivocado, los métodos __get y __set se supone que permiten la sobrecarga de → get y set .

Por ejemplo, las siguientes declaraciones deben invocar el método __get :

echo $foo->bar; $var = $foo->bar;

Y lo siguiente debería usar el método __set :

$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 llamadas die() allí muestra que no está llegando a nada.

Por ahora, acabo de decir que lo __get y estoy usando __get manualmente 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é esto no funciona.

Esto se ejecuta en php 5.3.3 .


El mejor uso de los métodos de configuración / obtención de magia con los modos de configuración / obtención personalizados predefinidos, como se muestra en el ejemplo a continuación. 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 incluso se nota la diferencia. El siguiente ejemplo también valida la matriz de datos contra los setters predefinidos.

"Los métodos mágicos no son sustitutos de getters y setters. Simplemente te permiten manejar llamadas a métodos o acceso a propiedades que de lo contrario generarían un error".

Es por eso que debemos usar ambos.

EJEMPLO 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


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.

Eliminando public $bar; de tu clase debería corregir esto.


Para ampliar la respuesta de Berry, establecer el nivel de acceso protegido permite que __get y __set se usen con propiedades explícitamente declaradas (cuando se accede fuera de la clase, al menos) y la velocidad sea considerablemente más lenta, citaré un comentario de otra pregunta sobre este tema y hacer un caso para usarlo de todos modos:

Estoy de acuerdo en que __get es más lento para una función de obtención 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 loops. - Melsi 23 de noviembre de 2012 a las 22:32 Mejor práctica: PHP Magic Methods __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 todavía puede acceder a una propiedad con este método 10.000 veces, contando el tiempo para la iteración de bucle, en aproximadamente 1/100 de segundo. Es considerablemente más lento en comparación con los métodos reales de obtención y establecimiento definidos, y eso es una subestimación, pero en el gran esquema de cosas, incluso 5 veces más lento nunca es realmente lento.

El tiempo de computación de la operación todavía es insignificante y no vale la pena considerarlo en el 99% de las aplicaciones del mundo real. El único momento en el que realmente se debe evitar 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 malo si no pueden permitirse tirar unos 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 pagarle a una granja de 1,000 servidores con esa línea de texto. El usuario final nunca va a tocar sus dedos preguntándose qué es lo que lleva tanto tiempo cargar la página porque el acceso a la propiedad de la aplicación demora una millonésima de segundo.

Lo digo como un desarrollador que proviene de un entorno 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 de ahorro del desarrollador de PHP para incluso llamar a su versión de propiedades "propiedades". Además, la extensión de Visual Studio para PHP es compatible con 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á usar sus getters y setters al acceder a propiedades dentro de la definición de clase y clases ampliadas. Una mejor solución es una clase base e interfaz para extender otras clases, por lo que puede copiar las pocas líneas de código de la clase base en la clase implementadora. Estoy haciendo un poco más con la clase base de mi proyecto, así que no tengo una interfaz para proporcionar ahora, pero aquí está la definición de clase reducida no probada con obtención de propiedades mágicas y configuración usando 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.


Recomiendo 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, se asegura de que no puede acceder a las variables de otra manera (tenga en cuenta que $values está protegido), para evitar colisiones.


Suelta la public $bar; declaración y debería funcionar como se esperaba.


__get , __set , __call y __callStatic se invocan cuando el método o la propiedad son inaccesibles. Su $bar es público y, por lo tanto, no es inaccesible.

Consulte la sección sobre Sobrecarga de propiedad en el manual:

  • __set() se ejecuta al escribir datos en propiedades inaccesibles.
  • __get() se utiliza para leer datos de propiedades inaccesibles.

Los métodos mágicos no son sustitutos de getters y setters. Simplemente le permiten manejar llamadas a métodos o acceso a propiedades que de lo contrario generarían un error. Como tal, hay mucho más relacionado con el manejo de errores. También tenga en cuenta que son considerablemente más lentas que el uso apropiado de getter y setter o llamadas a métodos directos.