Pull to refresh

PHP: массивы, возвращаемые функцией

Reading time4 min
Views11K
Мне нравится PHP (если вам не нравится — пожалуйста, забудьте про этот топик. Не надо холиварить) и еще мне нравится одна штука, которая прям везде есть, а в PHP отсутствует:

superFunction(foo, bar)[2];


Что делает этот код? Правильно! Возвращает третий элемент массива, который возвращает superFunction() с аргументами foo и bar.

В PHP-синтаксисе это выглядело бы так:

superFunction($foo, $bar)[2];


Вот только этот код выдает Parse Error. «И поделом!» — раздаются уж крики ненавистников синтаксического сахара. Я предлагаю им тоже отправиться подальше от этого топика, чтобы не холиварить и не доказывать, что это не нужно (посмотрите, в каком я блоге это разместил, в конце-то концов).

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



Работает ли это?



Да, Preparser работает. После нескольких часов отладки я смог научить его не глючить, обрабатывая одно мое «большое» приложение, использующее Zend Framework и тысячу всего подряд. Из чего делаю вывод, что глючить он по-умолчанию не станет.
Работает он быстро. Да, он парсит все подключаемые (include и т.п.) фаилы и меняет там все как хочет. Но Preparser обработанный код, разумеется, аккуратненько кеширует и подключает обычным include-ом при повторном запуске скрипта, что не отнимает лишнего процессорного времени вообще. Кстати, Preparser проверяет дату модификации фаилов, так что можно не беспокоиться об очистке кеша с каждой измененной строкой.
Уже отпарсенные фаилы выполняют конструкцию func()[] без потерь производительности. Там ничего страшного не происходит вообще :)

Работает он просто. Чтобы взять и перевести ну прям всё огромное большое приложение на Preparser, надо написать 2 или там не знаю, 3 строки. Если вы программируете, меняя глобальные переменные в include-ах, то строк придется добавить чуть больше. Но вы же взрослые люди и давно не делаете таких бяк, правда?
Кстати, глобальные переменные из include-ов меняться перестанут, но команда return $smth; останется работать.
А еще: для совместимости с Zend_View во всех фаилах, заинклуженных внутри классов, продолжает работать $this.
Итак, если вы пишете при помощи Zend Framework, то все будет хорошо. Если у вас какой-то другой цивильный фреймворк без уродливостей вроде глобальных переменных — скорее всего, всё будет хорошо (я не проверял, буду рад вашим отзывам!). Ну а если нет — не знаю, попробовать все равно стоит, благо это просто. Заодно расскажете мне, в чём я не прав :)

Ах да, Препарсеру нужен PHP ветки 5.3. :)

Чо как?



Preparser работает с фаилами, заинклуженными через него, а также с фаилами, заинклуженными теми фаилами, которые заинклужены через него. Наверное, вы не поняли предыдущую фразу. Окей.

Вариант 1: поиграться


Суть такова: чтобы в php фаиле можно было использовать конструкцию func()[2], его нужно подключить через Preparser. Т.е., создаете, скажем, index.php и hooray.php:
<? // index.php

// настраиваете include_path() или прописываете путь
require_once '/library/Preparser.php';

// не обязательно, но желательно. Не забудьте создать папку и дать права!
Preparser\setCachePath('/../data/preparserCache/');

Preparser\requirePreparsed_once('hooray.php');


Теперь в hooray.php вы можете использовать самые страшные конструкции (в /tests/dereferencing.php можете найти примеры):

<? // hooray.php

assert( array('a' => 1, 'b' => 2)['a'] == 1 );

function ret_anything($lol) {
return $lol;
}

assert( ret_anything( array(1, 2, ret_anything(4), ret_anything(array(1, 2, 3))[1])[ ret_anything(array(1, 2, 3))[ret_anything(array(1, 2, 3))[1]] ]) == 2 );



Вариант 2: перевести проект на Preparser


Вот как я изменил index.php для одного крутого сайта:
<?
// поскипаны всякие штуки с константами и set_include_path()

require_once '/Preparser.php';
Preparser\setCachePath(APPLICATION_PATH . '/../data/preparserCache/');

//было: require_once 'Zend/Application.php';
Preparser\requirePreparsed_once('Zend/Application.php');

// Create application, bootstrap, and run
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);

$application->bootstrap()
->run();


Добавил две строки, а потом поменял одну. Очень сложно! Потом я открыл index.php в браузере и подождал (достаточно долго, минуту почти), пока формировался кеш. После этого все работало быстро и без сучка без задоринки.

Подводные камни?



Не знаю, какие уж там подводные камни. Работать я старался аккуратно. Глобальное пространство имен не засорял. Свежезаинклуженный фаил не получит себе в подарок кучу переменных, он вообще ни одной не получит, кроме, разве что, $this. Ну можете еще подключать свои глобальные переменные директивой global.

Я думаю, вы найдете эти камни быстрее меня — буду рад вашим отзывам!

Где взять?



А, брать эту приблуду с ГуглКода. Опенсорс. Лицензия New BSD, я не вдавался в подробности, но, кажется, вы можете использовать это как хотите и свой код при этом вам открывать не обязательно :)

Там еще есть даже не начатый мануал. Я буду рад, если кто поможет мне его написать…

Как помочь?


… да, я буду рад, если кто-нибудь поможет мне написать мануалы и прочее. Я очень хреново говорю по-английски, так что мне немного стыдно за все, что я понаписал в README.txt и HOWTOS.txt. За исправление тамошних ошибок тоже буду благодарен, но одна есть просьба: не пишите о них в комментариях здесь на хабрике, чтобы не засирать эти самые комментарии временными и малоосмысленными сообщениями. У меня есть почты разные, например, preparser@va1en0k.net

Еще я буду благодарен за тестирование. Ну и — особенно — за патчи.

Фичреквесты обязательны. Думаю, еще много чему стоило бы научить парсер php.

Спасибо за внимание!

Делал я это, разумеется, от скуки и никакой поддержки или обоснования необходимости существования не обещаю 8)
Tags:
Hubs:
+8
Comments113

Articles

Change theme settings