PHP __get 및 __set 매직 메서드
내가 완전히 착각하지 않는 한 __get
및 __set
메서드는 → get
및 set
.
예를 들어, 다음 명령문은 __get
메소드를 호출해야합니다 .
echo $foo->bar;
$var = $foo->bar;
그리고 다음은 __set
방법을 사용해야합니다 .
$foo->bar = 'test';
이것은 내 코드에서 작동하지 않았으며 다음 간단한 예제로 재현 할 수 있습니다.
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]";
결과는 다음과 같습니다.
[test]
die()
거기에 몇 가지 전화를 걸면 전혀 타격을 입지 않는다는 것을 보여줍니다.
지금은 그냥 나사를 조이고 __get
지금은 필요한 곳에 수동으로 사용 하고 있지만 이는 매우 동적이 아니며 특별히 호출되지 않는 한 '오버로드 된'코드가 실제로 호출되지 않는다는 지식이 필요합니다. 이것이 내가 이해 한 방식으로 작동하지 않는지 또는 왜 작동하지 않는지 알고 싶습니다.
이것은에서 실행 중입니다 php 5.3.3
.
__get
, __set
, __call
및 __callStatic
메소드 나 속성에 액세스 할 수없는 경우 호출됩니다. 귀하 $bar
는 공개되어 있으므로 액세스 할 수 없습니다.
매뉴얼의 속성 오버로딩 섹션을 참조하십시오 .
__set()
액세스 할 수없는 속성에 데이터를 쓸 때 실행됩니다.__get()
액세스 할 수없는 속성에서 데이터를 읽는 데 사용됩니다.
매직 메서드는 게터 및 세터를 대체하지 않습니다. 이를 통해 오류가 발생할 수있는 메서드 호출 또는 속성 액세스를 처리 할 수 있습니다. 따라서 오류 처리와 더 관련이 있습니다. 또한 적절한 getter 및 setter 또는 직접 메서드 호출을 사용하는 것보다 상당히 느립니다.
을 통해 모든 값을 저장하기 위해 배열을 사용하는 것이 좋습니다 __set()
.
class foo {
protected $values = array();
public function __get( $key )
{
return $this->values[ $key ];
}
public function __set( $key, $value )
{
$this->values[ $key ] = $value;
}
}
이렇게하면 $values
충돌을 방지하기 위해 다른 방법 ( 보호됨에 유의)으로 변수에 액세스 할 수 없습니다 .
보내는 사람 PHP 매뉴얼 :
- __set ()은 액세스 할 수없는 속성에 데이터를 쓸 때 실행됩니다.
- __get ()은 액세스 할 수없는 속성에서 데이터를 읽는 데 사용됩니다.
액세스 할 수없는 속성 읽기 / 쓰기에서만 호출됩니다 . 그러나 귀하의 자산은 공용이므로 액세스 할 수 있습니다. 액세스 수정자를 protected로 변경하면 문제가 해결됩니다.
Berry의 답변을 확장하기 위해 액세스 수준을 protected로 설정하면 __get 및 __set을 명시 적으로 선언 된 속성 (적어도 클래스 외부에서 액세스 할 때)과 함께 사용할 수 있고 속도가 상당히 느려진다는 점에서 다른 질문에서 의견을 인용하겠습니다. 이 주제에 대해 어쨌든 그것을 사용하는 사례를 만드십시오.
나는 __get이 사용자 정의 get 함수 (동일한 일을 수행)보다 느리다는 데 동의합니다. 이것은 __get ()의 시간 인 0.0124455이고이 0.0024445는 10000 루프 이후의 사용자 정의 get ()을위한 것입니다. – Melsi 11 월 23 일 '12 : 22 : 32 모범 사례 : PHP 매직 메서드 __set 및 __get
Melsi의 테스트에 따르면 상당히 느린 것은 약 5 배 느립니다. 이는 확실히 상당히 느리지 만 테스트에서이 메서드를 사용하여 약 1/100 초 내에 루프 반복 시간을 계산하여 10,000 번 여전히 속성에 액세스 할 수 있음을 보여줍니다. 정의 된 실제 get 및 set 메서드에 비해 상당히 느리며 이는 과소 표현이지만, 대대적 인 계획에서는 5 배 더 느리더라도 실제로는 느리지 않습니다.
작업의 컴퓨팅 시간은 여전히 미미하며 실제 응용 프로그램의 99 %에서 고려할 가치가 없습니다. 실제로 피해야하는 유일한 경우는 단일 요청으로 10,000 번 이상 속성에 실제로 액세스 할 때입니다. 트래픽이 많은 사이트는 애플리케이션을 계속 실행하기 위해 몇 대의 서버를 추가로 투입 할 여유가 없다면 정말 잘못된 일을하고 있습니다. 액세스 속도가 문제가되는 트래픽이 많은 사이트의 바닥 글에 한 줄 텍스트 광고는 해당 텍스트 줄을 사용하는 1,000 대의 서버 팜에 비용을 지불 할 수 있습니다. 최종 사용자는 애플리케이션의 속성 액세스가 백만 분의 1 초에 걸리기 때문에 페이지를로드하는 데 너무 오래 걸리는 것이 무엇인지 궁금해하지 않을 것입니다.
나는 이것이 .NET의 배경에서 온 개발자라고 말하지만, 소비자에게 보이지 않는 get 및 set 메서드는 .NET의 발명이 아닙니다. 그것들은 단순히 그것들이없는 속성이 아니며, 이러한 마법의 방법은 심지어 속성의 버전을 "속성"이라고 부르는 것에 대한 PHP의 개발자의 은혜입니다. 또한 PHP 용 Visual Studio 확장은 보호 된 속성을 사용하여 인텔리 젠스를 지원하므로 이러한 트릭을 염두에두고 있습니다. 이런 식으로 매직 __get 및 __set 메서드를 사용하는 개발자가 충분하면 PHP 개발자가 개발자 커뮤니티에 맞게 실행 시간을 조정할 수 있다고 생각합니다.
편집 : 이론적으로 보호 된 재산은 대부분의 상황에서 작동하는 것처럼 보였습니다. 실제로, 클래스 정의 및 확장 클래스 내의 속성에 액세스 할 때 getter 및 setter를 사용하려는 경우가 많습니다. 더 나은 솔루션은 다른 클래스를 확장 할 때의 기본 클래스와 인터페이스이므로 기본 클래스에서 구현 클래스로 몇 줄의 코드 만 복사하면됩니다. 내 프로젝트의 기본 클래스로 좀 더 많은 작업을하고 있으므로 지금은 제공 할 인터페이스가 없지만 여기에는 속성을 제거하고 이동하기 위해 리플렉션을 사용하여 설정하고 마법 속성을 가져 오는 테스트되지 않은 제거 된 클래스 정의가 있습니다. 보호 된 어레이 :
/** 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;
}
}
코드에 버그가 있으면 사과드립니다.
$ bar가 공공 자산이기 때문입니다.
$foo->bar = 'test';
There is no need to call the magic method when running the above.
Deleting public $bar;
from your class should correct this.
Best use magic set/get methods with predefined custom set/get Methods as in example below. This way you can combine best of two worlds. In terms of speed I agree that they are a bit slower but can you even feel the difference. Example below also validate the data array against predefined setters.
"The magic methods are not substitutes for getters and setters. They just allow you to handle method calls or property access that would otherwise result in an error."
This is why we should use both.
CLASS ITEM EXAMPLE
/*
* 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
OUTPUT
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
Drop the public $bar;
declaration and it should work as expected.
Intenta con:
__GET($k){
return $this->$k;
}
_SET($k,$v){
return $this->$k = $v;
}
참고URL : https://stackoverflow.com/questions/4713680/php-get-and-set-magic-methods
'development' 카테고리의 다른 글
didRegisterForRemoteNotificationsWithDeviceToken이 호출되지 않는 이유 (0) | 2020.09.23 |
---|---|
.htaccess에서 upload_max_filesize를 설정하는 방법은 무엇입니까? (0) | 2020.09.23 |
Rails 콘솔에서 비밀번호 재설정 고안 (0) | 2020.09.23 |
Chrome 확장 프로그램 개발을 위해 WebStorm을 어떻게 사용합니까? (0) | 2020.09.23 |
Cygwin에 Pip-3.2 설치 (0) | 2020.09.23 |