A menos que eu esteja completamente enganado, os métodos __get
e __set
devem permitir a sobrecarga do → get
e set
.
Por exemplo, as seguintes instruções devem invocar o __get
método:
echo $foo->bar;
$var = $foo->bar;
E o seguinte deve usar o __set
método:
$foo->bar = 'test';
Isso não estava funcionando em meu código e é reproduzível com este exemplo simples:
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]";
Isso só resulta em:
[test]
Fazer algumas die()
ligações mostra que não está acertando nada.
Por enquanto, eu apenas disse dane-se e estou usando manualmente __get
onde é necessário por enquanto, mas isso não é muito dinâmico e requer conhecimento de que o código 'sobrecarregado' na verdade não está sendo chamado a menos que seja especificamente chamado. Gostaria de saber se isso não deve funcionar da maneira que entendi que deveria ou por que não está funcionando.
Isso está funcionando php 5.3.3
.
fonte
node
não é uma propriedade de $ foo, mas sim dedoesNotExist
. Portanto, a menos que 'doesNotExist' seja um objeto (que implementa __set ou tenha uma propriedade pública chamada node), ele não funcionará.Eu recomendo usar uma matriz para armazenar todos os valores via
__set()
.class foo { protected $values = array(); public function __get( $key ) { return $this->values[ $key ]; } public function __set( $key, $value ) { $this->values[ $key ] = $value; } }
Desta forma, você se certifica de que não pode acessar as variáveis de outra maneira (note que
$values
está protegida), para evitar colisões.fonte
Do manual do PHP :
Isso só é chamado na leitura / gravação de propriedades inacessíveis . Sua propriedade, entretanto, é pública, o que significa que está acessível. Alterar o modificador de acesso para protegido resolve o problema.
fonte
Para expandir a resposta de Berry, que definir o nível de acesso como protegido permite que __get e __set sejam usados com propriedades declaradas explicitamente (quando acessados fora da classe, pelo menos) e a velocidade sendo consideravelmente mais lenta, citarei um comentário de outra pergunta sobre este tópico e tente usá-lo de qualquer maneira:
Eu concordo que __get é mais lento para uma função get customizada (fazendo as mesmas coisas), isto é 0,0124455 o tempo para __get () e 0,0024445 é para get () customizado após 10.000 loops. - Melsi 23 de novembro de 12 às 22:32 Prática recomendada: métodos mágicos de PHP __set e __get
De acordo com os testes de Melsi, consideravelmente mais lento é cerca de 5 vezes mais lento. Isso é definitivamente consideravelmente mais lento, mas observe também que os testes mostram que você ainda pode acessar uma propriedade com esse método 10.000 vezes, contando o tempo para a iteração do loop, em cerca de 1/100 de segundo. É consideravelmente mais lento em comparação com os métodos get e set reais definidos, e isso é um eufemismo, mas no grande esquema das coisas, mesmo 5 vezes mais lento nunca é realmente lento.
O tempo de computação da operação ainda é insignificante e não vale a pena ser considerado em 99% dos aplicativos do mundo real. A única vez que isso realmente deve ser evitado é quando você realmente vai acessar as propriedades mais de 10.000 vezes em uma única solicitação. Os sites de alto tráfego estão fazendo algo realmente errado se não podem arcar com o aumento de alguns servidores para manter seus aplicativos em execução. Um anúncio de texto de uma única linha no rodapé de um site de alto tráfego, onde a taxa de acesso se torna um problema, provavelmente pagaria por um conjunto de 1.000 servidores com essa linha de texto. O usuário final nunca vai ficar batendo os dedos se perguntando por que a página está demorando tanto para carregar porque o acesso à propriedade do seu aplicativo leva um milionésimo de segundo.
Digo isso falando como um desenvolvedor vindo de .NET, mas os métodos invisíveis get e set para o consumidor não são invenção do .NET. Eles simplesmente não são propriedades sem eles, e esses métodos mágicos são a graça salvadora do desenvolvedor do PHP por até mesmo chamar sua versão de propriedades de "propriedades". Além disso, a extensão do Visual Studio para PHP oferece suporte a intellisense com propriedades protegidas, com esse truque em mente, eu acho. Eu acho que com desenvolvedores suficientes usando os métodos mágicos __get e __set dessa forma, os desenvolvedores de PHP ajustariam o tempo de execução para atender à comunidade de desenvolvedores.
Edit: Em teoria, as propriedades protegidas pareciam funcionar na maioria das situações. Na prática, acontece que muitas vezes você vai querer usar seus getters e setters ao acessar propriedades dentro da definição de classe e classes estendidas. Uma solução melhor é uma classe base e interface para quando estender outras classes, portanto, você pode apenas copiar algumas linhas de código da classe base para a classe de implementação. Estou fazendo um pouco mais com a classe base do meu projeto, então não tenho uma interface para fornecer agora, mas aqui está a definição de classe reduzida não testada com propriedade mágica obtendo e configurando usando reflexão para remover e mover as propriedades para uma 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; } }
Minhas desculpas se houver algum bug no código.
fonte
É porque $ bar é uma propriedade pública.
$foo->bar = 'test';
Não há necessidade de chamar o método mágico ao executar o acima.
Excluir
public $bar;
da sua classe deve corrigir isso.fonte
É melhor usar métodos mágicos de definir / obter com métodos de definir / obter personalizados predefinidos como no exemplo abaixo. Desta forma, você pode combinar o melhor de dois mundos. Em termos de velocidade concordo que são um pouco mais lentos, mas dá para sentir a diferença. O exemplo abaixo também valida a matriz de dados em relação aos configuradores predefinidos.
É por isso que devemos usar ambos.
EXEMPLO DE ITEM DE CLASSE
/* * 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
RESULTADO
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
fonte
Cancele a
public $bar;
declaração e ela deve funcionar conforme o esperado.fonte
Intenta con:
__GET($k){ return $this->$k; } _SET($k,$v){ return $this->$k = $v; }
fonte