Теперь я расскажу о том как можно связать кодогенерацию с творчеством.
Для того, чтобы разобраться в кодогенерации — давайте с ней экспериментировать.
Давайте создадим PHP-проект и добавим там 3 директории:
1. source — директория, в которую будем складывать исходные коды.
2. gen — директория со скриптами кодогенерации.
3. result — директория с результатами.
И один файл в корень: make.php — в нем будут определяться правила кодогенерации, запускаться процесс кодогенерации.
Кодогенерацию будет запускать файл make.php, в котором будут инструкции о том, как производить данный процесс.
В общем виде это будет выглядеть так:
Инструкции make.php + Исходные коды из директории source -> Обработка скриптами из директории gen -> Генерация результата в директорию result.
Для начала давайте реализуем просто копирование данных из source в result без обработки. Я буду читать содержимое каждого файла и записывать его для того, чтобы в будущем можно было реализовать обработку этих данных.
Перед тем как совершать генерацию из source в result необходимо очищать result, для чего создадим clear.php в директории gen со следующим содержимым:
}
closedir($dh);
if ($delete_me) @rmdir($dir);
}
Здесь я использовал функцию full_delete, которую нашел на php.net и привел почти без изменений.
Давайте теперь продумаем обработку. Создадим файл index.php в директории source и напишем там что-то вроде:
1. Вы сам понимаете где их поставить.
2. Хабр их съедает.
Теперь давайте создадим фильтр в файле gen/filter.php:
И можно записывать инструкции в make.php:
Мы сделали реализовали некую концепцию, которая пока еще бесполезна, но имеет определенный потенциал.
Давайте усовершенствуем фильтр!
Давайте поэкспериментируем с нашей системой. Мне очень не хватает в PHP лямбда-выражений. Можно реализовать некое их подобие в качестве эксперимента.
Мне всегда не нравилось, что для использования функции usort — надо создавать отдельную функцию, а использование create_function совсем не радовало. Давайте реализуем некий ограниченный лямбда-синтаксис.
Я не использую символ ` (он реализует упрощенный доступ к функции shell_exec), поэтому легко могу легко пожертвовать его ради лямбда-выражений. Экспериментальный синтаксис лямбда-выражений:
Пример:
Для обработки таких конструкций напишем функцию filter_lambda в файле gen/filter.php:
И теперь усовершенствуем функцию filter:
Теперь мы можем написать в файле source/index.php:
Выполнить make.php и получим result/index.php:
Лябмда-выражения в PHP, к сожалению, отсутствуют, и даже этот способ не совершенен — нельзя использовать свои переменные (из окружающей среды) в лямбда-выражении.
Этот довольно простой эксперимент показывает нам то, как можно добавить свои условности в исходный код и из простого создавать сложное. Данный эксперимент далек от совершенства: в идеале надо было бы использовать синтаксический разбор и т.п., но опыт дает нам ощутить силу предварительной обработки кода и кодогенерации.
Предлагаю для закрепления материала реализовать такие интересные задачи как:
1. Попробуйте сделать рекурсивную обработку php файлов из директории source, чтобы не писать каждый файл в make.php.
2. Попробуйте реализовать условную генерацию (как в Си) — аналоги #define, #ifdef, #elif, #endif.
Для того, чтобы разобраться в кодогенерации — давайте с ней экспериментировать.
Структура
Давайте создадим PHP-проект и добавим там 3 директории:
1. source — директория, в которую будем складывать исходные коды.
2. gen — директория со скриптами кодогенерации.
3. result — директория с результатами.
И один файл в корень: make.php — в нем будут определяться правила кодогенерации, запускаться процесс кодогенерации.
Процесс
Кодогенерацию будет запускать файл make.php, в котором будут инструкции о том, как производить данный процесс.
В общем виде это будет выглядеть так:
Инструкции make.php + Исходные коды из директории source -> Обработка скриптами из директории gen -> Генерация результата в директорию result.
Начнем
Для начала давайте реализуем просто копирование данных из source в result без обработки. Я буду читать содержимое каждого файла и записывать его для того, чтобы в будущем можно было реализовать обработку этих данных.
Перед тем как совершать генерацию из source в result необходимо очищать result, для чего создадим clear.php в директории gen со следующим содержимым:
function clear()(!@unlink($dir.'/'.$obj)) full_delete($dir.'/'.$obj, true);
{
full_delete(dirname(__FILE__).'/../result', false);
}
function full_delete($dir, $delete_me)
{
if (!$dh = @opendir($dir)) return;
while (false !== ($obj = readdir($dh))) {
if ($obj=='.' || $obj=='..') continue;
if
}
closedir($dh);
if ($delete_me) @rmdir($dir);
}
Здесь я использовал функцию full_delete, которую нашел на php.net и привел почти без изменений.
Давайте теперь продумаем обработку. Создадим файл index.php в директории source и напишем там что-то вроде:
echo 'Hello world!';Теги PHP я не привожу по двум причинам:
1. Вы сам понимаете где их поставить.
2. Хабр их съедает.
Теперь давайте создадим фильтр в файле gen/filter.php:
function filter($input)
{
$output = $input;
return $output;
}
И можно записывать инструкции в make.php:
require 'gen/clear.php';
require 'gen/filter.php';
clear();
$index = file_get_contents('source/index.php');
file_put_contents('result/index.php', filter($index));
Что вышло
Мы сделали реализовали некую концепцию, которая пока еще бесполезна, но имеет определенный потенциал.
Давайте усовершенствуем фильтр!
Препроцессор
Давайте поэкспериментируем с нашей системой. Мне очень не хватает в PHP лямбда-выражений. Можно реализовать некое их подобие в качестве эксперимента.
Мне всегда не нравилось, что для использования функции usort — надо создавать отдельную функцию, а использование create_function совсем не радовало. Давайте реализуем некий ограниченный лямбда-синтаксис.
Я не использую символ ` (он реализует упрощенный доступ к функции shell_exec), поэтому легко могу легко пожертвовать его ради лямбда-выражений. Экспериментальный синтаксис лямбда-выражений:
`список аргументов -> действия`
Пример:
`$var1, $var2 -> $var1 + $var2`
Для обработки таких конструкций напишем функцию filter_lambda в файле gen/filter.php:
$uid = 0;
function filter_lambda($input)
{
global $uid;
$additional_code = '';
while (($spos = strpos($input, '`'))!==false)
{
$epos = strpos($input, '`', $spos + 1);
$expr = substr($input, $spos + 1, $epos — $spos — 1);
$divider_pos = strpos($expr, '->');
$vars = trim(substr($expr, 0, $divider_pos));
$body = trim(substr($expr, $divider_pos + 2));
$additional_code .= «function lambda_$uid($vars){return ($body);}\n»;
$input = substr($input, 0, $spos)."'lambda_$uid'".substr($input, $epos+1);
$uid++;
}
return ($additional_code != ''? "<?php\n$additional_code?>": '').$input;
}
И теперь усовершенствуем функцию filter:
function filter($input)
{
$output = $input;
$output = filter_lambda($output);
return $output;
}
Результат
Теперь мы можем написать в файле source/index.php:
$numbers = array('much longer', 'short', 'longer');
usort($numbers, `$value1, $value2 -> strlen($value1) > strlen($value2)`);
foreach ($numbers as $key => $value)
{
echo "$key: $value<br/>";
}
Выполнить make.php и получим result/index.php:
<?php
function lambda_0($value1, $value2){return (strlen($value1) > strlen($value2));}
?><?php
$numbers = array('much longer', 'short', 'longer');
usort($numbers, 'lambda_0');
foreach ($numbers as $key => $value)
{
echo "$key: $value<br/>";
}
?>
Лябмда-выражения в PHP, к сожалению, отсутствуют, и даже этот способ не совершенен — нельзя использовать свои переменные (из окружающей среды) в лямбда-выражении.
Заключение
Этот довольно простой эксперимент показывает нам то, как можно добавить свои условности в исходный код и из простого создавать сложное. Данный эксперимент далек от совершенства: в идеале надо было бы использовать синтаксический разбор и т.п., но опыт дает нам ощутить силу предварительной обработки кода и кодогенерации.
Шагаем дальше
Предлагаю для закрепления материала реализовать такие интересные задачи как:
1. Попробуйте сделать рекурсивную обработку php файлов из директории source, чтобы не писать каждый файл в make.php.
2. Попробуйте реализовать условную генерацию (как в Си) — аналоги #define, #ifdef, #elif, #endif.