Pull to refresh

Практическое применение

Reading time4 min
Views2.2K
Теперь я расскажу о том как можно связать кодогенерацию с творчеством.
Для того, чтобы разобраться в кодогенерации — давайте с ней экспериментировать.

Структура


Давайте создадим 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()
{
    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
(!@unlink($dir.'/'.$obj)) full_delete($dir.'/'.$obj, true);
    }
    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$spos1);
        $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.
Tags:
Hubs:
Total votes 25: ↑20 and ↓5+15
Comments47

Articles