내가 완전히 착각하지 않는 한 __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 년 11 월 23 일 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';
위를 실행할 때 매직 메서드를 호출 할 필요가 없습니다.
public $bar;
클래스에서 삭제 하면이 문제가 해결됩니다.
답변
아래 예에서와 같이 미리 정의 된 사용자 지정 설정 / 가져 오기 메서드와 함께 매직 설정 / 가져 오기 메서드를 가장 잘 사용합니다. 이렇게하면 두 세계의 장점을 결합 할 수 있습니다. 속도면에서 나는 그들이 조금 느리다는 데 동의하지만 차이를 느낄 수도 있습니다. 아래 예제는 미리 정의 된 setter에 대해 데이터 배열의 유효성도 검사합니다.
“매직 메서드는 getter 및 setter를 대체하지 않습니다. 오류가 발생할 수있는 메서드 호출 또는 속성 액세스를 처리 할 수 있도록합니다.”
이것이 우리가 둘 다 사용해야하는 이유입니다.
클래스 항목 예
/*
* 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
산출
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
답변
public $bar;
선언을 삭제하면 예상대로 작동합니다.