Переменные переменных. Зачем они нужны?

Кирилл Евсеев, April 25, 2011

Всем привет. Сегодня мы поговорим о переменных переменных и выясним, зачем они всё таки нужны (или не нужны).

В самом простом виде переменные переменных показаны в справке PHP. А именно –

$a = 'hello';
$$a = 'world';
echo $a . ${$a}; 

Подобная кривая конструкция выдаст на экран hello world. Другими словами, ${$a} эквивалентна вызову $hello. Не правда ли.. ужасно? Добавим сюда проблему неоднозначности, которая возникает при попытке обратиться к элементам массива (это подробно описано в справке по адресу http://ua.php.net/manual/en/language.variables.variable.php), добавим сюда возможность добавлять к имени переменной любое количество знаков доллара, о которой обычно не пишут в учебниках, и возникнет вполне закономерное недоумение – зачем это вообще нужно?

Самое главное правило, которого должен придерживаться любой разработчик в любом проекте – код пишется для людей, а не для машин. Это правило помимо негласного этикета в среде профессионалов в виде комментариев и отступов, ещё и экономически обоснованно. Дело в том что сегодня разработка ПО стремительно дешевеет (не без помощи демпинга индусов). Прямым следствием является то, что поддерживать код должно быть дешевле, чем его писать. Т.о. становится понятно, что читабельный, понятный и простой в понимании код, куда как более ценен, чем процессинг формы на Brainfuck’e (кто не в курсе, пусть насладится http://en.wikipedia.org/wiki/Brainfuck), пусть даже на Brainfuck’e этот процессинг работает на 0.0001 секунду быстрее.

Добавим сюда огромные вычислительные возможности современных серверов, которые могут прожевать мегаметры самого бездарного кода в течение секунды и станет понятно, что деньги, затрачиваемые на поддержку, должны быть минимальными. Разработчик должен иметь возможность спокойно и быстро разобраться в коде, написанном соседней командой, даже через пару лет после финального релиза проекта. Реалии PHP вполне это позволяют – всё таки это скриптовый язык, а не ассемблер.

Я тут упомянул о бездарном коде. Я вовсе не говорю о том, что нужно похоронить оптимизацию и рефакторинг. Но если неоптимизированная функция легче в чтении и понимании, чем оптимизированный аналог, который, к тому же, использует какие-нибудь недокументированные или деприкейтед трюки, то лучше о такой оптимизации забыть.

Казалось бы, на этом можно поставить жирную точку и похоронить раз и навсегда переменные переменных. Но, не будем торопиться.

Давайте представим, что у нас есть некий класс, который содержит в себе достаточно большое количество public свойств. Настолько достаточно большое, что инициализация этих свойств в конструкторе вручную представляет собой нетривиальную задачу. В реальной жизни таким классом может быть какой-нибудь научный класс, который получает большое количество значений некоторых переменых. Тогда мы можем применить неявное использование переменных переменных для быстрой инициализации.

class a
{
    public function __construct($arr)
    {
        foreach($arr as $k=>$v)
        {
            $this->$k = $v;
        }
    }
}

$arr = array();
$arr['one'] = 1;
$arr['two'] = 2;
$arr['three'] = 3;
$arr['four'] = 4;

$x = new a($arr);

echo $x->two;

То же самое можно было бы сделать и без конструктора.

class a
{
}

foreach ($arr as $k=>$v)
{
    $x->$k = $v;
}

Теперь класс а содержит четыре public свойства, переданных в конструктор при инициализации. Конечно, эту задачу можно было бы с лёгкостью реализовать и без переменных переменных –

class a
{
    public $arr = array();

    public function __construct($arr)
    {
        $this->arr = $arr;
    }
    
    public function __get($key)
    {
        if (array_key_exists($key, $this->arr))
        {
            return $this->arr[$key];
        }
    }
}

Однако, в первом случае мы используем некоторый механизм PHP, который при этом не нарушает правил ООП – просто удобная инициализация public переменных, хотя и неочевидная, и, наверняка, нуждающаяся в дополнительных проверках.

Что касается второго решения, то тут мы используем один из магических методов, который разрушает ООП подход. Конечно, в рассмотренном варианте массив объявлен со спецификатором доступа public и не играет никакой роли, как обратиться к свойству. Тем не менее, метод __get с такой же лёгкостью позволяет обращаться к закрытым переменным и это является разрушением всего ООП подхода. В cамом деле, зачем нужны private свойства если есть возможность их читать и модифицировать (__set) вне класса? Очевидно, что решение с методом __get будет работать быстрее.

Ещё одна возможность использовать переменные переменных – это вызов методов или функций, названия которых вычисляются динамически.

class a
{
    public function __construct($arr)
    {
        foreach($arr as $k=>$v)
        {
            $this->$k = $v;
        }
    }
    
    public function foo()
    {
        echo 'two';
    }
}

$arr = array();
$arr['one'] = 1;
$arr['two'] = 'foo';
$arr['three'] = 3;
$arr['four'] = 4;

$x = new a($arr);

echo $x->{$x->two}();

Ну и ещё один трюк – возможность “придумать” переменную на лету.

$var = '';
$var1 = '1';
$var2 = '2';
$var3 = '3';
$x = 'delta';
$delta123 = 5;

for($i=1; $i<=3; $i++)
{
    $x .= "$var$i";
}

echo $$x;

Резюмируем всё вышенаписанное. Переменные переменных – достаточно мощный механизм языка. В то же время, использование переменных переменных делает код трудночитаемым и неочевидным. С вероятностью в 99% можно утверждать, что любое алгоритмическое решение, которое использует переменные переменных, можно заменить на аналогичное без них. При этом не пострадают ни скорость трансляции исходного кода, ни скорость его исполнения, но при этом код будет более читабельным, понятным и очевидным.

На главный вопрос – использовать ли переменные переменных в своих программах или нет – каждый должен ответить сам. Лично я предпочитаю обходиться без них.

Всем удачи!

В тему:

1комментарий

Читабельный код – это путь к совершенству в веб-програмировании.

collaps, March 17, 2012 1:30 am Reply
Ваше имя
Ваш email*
Ваш сайт
Текст вашего комментария:

Поиск по блогу:
Подписаться:
Популярные:
Облако тегов:
Разное:
Счетчик: