Я использую __ get(), чтобы сделать некоторые из моих свойств "динамическими" (инициализировать их только по запросу). Эти "поддельные" свойства хранятся внутри свойства private array, которое я проверяю внутри __get.
В любом случае, как вы думаете, лучше ли создавать методы для каждого из этих свойств вместо того, чтобы делать это в инструкции switch?
Изменить: тесты скорости
Меня беспокоит только производительность, другие вещи, о которых упоминал @Gordon, не так важны для меня:
- ненужная добавленная сложность - на самом деле она не увеличивает мою сложность приложения.
- хрупкий неочевидный API - я хочу, чтобы мой API был "изолирован"; Документация должна сообщать другим, как ее использовать: P
Итак, вот те тесты, которые я сделал, которые заставляют меня думать, что поражение в производительности не оправдано:
Результаты для 50 000 вызовов (на PHP 5.3.9):
(t1 = магия с переключателем, t2 = геттер, t3 = магия с последующим геттер-вызовом)
Не уверен, что означает "Cum" на t3. Он не может быть кумулятивным, потому что t2 должен иметь 2K, тогда...
Код:
class B{}
class A{
protected
$props = array(
'test_obj' => false,
);
// magic
function __get($name){
if(isset($this->props[$name])){
switch($name){
case 'test_obj':
if(!($this->props[$name] instanceof B))
$this->props[$name] = new B;
break;
}
return $this->props[$name];
}
trigger_error('property doesnt exist');
}
// standard getter
public function getTestObj(){
if(!($this->props['test_obj'] instanceof B))
$this->props['test_obj'] = new B;
return $this->props['test_obj'];
}
}
class AA extends A{
// magic
function __get($name){
$getter = "get".str_replace('_', '', $name); // give me a break, its just a test :P
if(method_exists($this, $getter))
return $this->$getter();
trigger_error('property doesnt exist');
}
}
function t1(){
$obj = new A;
for($i=1;$i<50000;$i++){
$a = $obj->test_obj;
}
echo 'done.';
}
function t2(){
$obj = new A;
for($i=1;$i<50000;$i++){
$a = $obj->getTestObj();
}
echo 'done.';
}
function t3(){
$obj = new AA;
for($i=1;$i<50000;$i++){
$a = $obj->test_obj;
}
echo 'done.';
}
t1();
t2();
t3();
ps: почему я хочу использовать __get() над стандартными методами getter? единственная причина - красота апи; потому что я не вижу реальных недостатков, я думаю, это того стоит: P
Изменить: более быстрые тесты
На этот раз я использовал microtime для измерения некоторых средних значений:
PHP 5.2.4 и 5.3.0 (аналогичные результаты):
t1 - 0.12s
t2 - 0.08s
t3 - 0.24s
PHP 5.3.9, с активным xdebug, поэтому он настолько медленный:
t1 - 1.34s
t2 - 1.26s
t3- 5.06s
PHP 5.3.9 с отключенным xdebug:
t1 - 0.30
t2 - 0.25
t3 - 0.86
Другой метод:
// magic
function __get($name){
$getter = "get".str_replace('_', '', $name);
if(method_exists($this, $getter)){
$this->$name = $this->$getter(); // <-- create it
return $this->$name;
}
trigger_error('property doesnt exist');
}
Открытое свойство с запрошенным именем будет создано динамически после первого вызова __get. Это решает проблемы скорости - получение 0,1 с в PHP 5.3 (это в 12 раз быстрее, чем стандартный геттер) и проблема расширения, поднятая Гордоном. Вы можете просто переопределить getter в дочернем классе.
Недостатком является то, что свойство становится доступным для записи: (