Для справки:
Tokenizer (лексер) предоставляет интерфейс для анализа кода. Таким образом, можно писать утилиты без необходимости работы с языковой спецификацией.
Tokenizer, начиная с версии php >= 4.3 включен в сборку php по-умолчанию.
Какие задачи можно решать с помощью tokenizr'а?
Да самые разные, связанные с анализом и модификацией кода.
Самый простой пример приведен на php.net — по удалению комментариев:
Как видно из кода — мы получаем массив токенов и, в зависимости от их типа, оставляем или пропускаем.
Так можно решить задачки и поинтереснее — например на основе php-файлов сгенерировать карту классов проекта для автозагрузки.
Для получения списка классов из файла я написал вот такую функцию:
А потом подумал и написал небольшую утилиту, которая генерирует автозагрузчик на основании файлов проекта.
Она анализирует в указанной папке все файлы с расширением "*.php" и строит карту классов (с учетом неймспейсов, конечно), на основании которой потом генерируется автозагрузчик.
Найти ее можно на github.com
На днях вспомнил, как когда-то ковырялся с расширением runkit. Из его возможностей меня особенно заинтересовало переопределение стандартных функций и песочница, в которой можно было бы запретить использование тех или иных функций.
И сейчас я подумал, а можно ли реализовать подобный функционал, без использования этого расширения. Оказалось, что tokenizer вполне может помочь в этом деле.
Так родилась библиотечка Runtime, с помощью которой, можно во время выполнения скрипта запретить использование любых стандартных функций, или переопределить их.
Приведу примеры работы:
Как использовать эти возможности — дело личное. Но использовать это нужно аккуратно.
Я провел небольшое исследование и остался доволен результатом.
Касательно runtime — сложно сказать, где его можно применить, а где нет. Но сама библиотека наглядно демонстрирует работу tokenizer'а и его возможностей.
Tokenizer (лексер) предоставляет интерфейс для анализа кода. Таким образом, можно писать утилиты без необходимости работы с языковой спецификацией.
Tokenizer, начиная с версии php >= 4.3 включен в сборку php по-умолчанию.
Какие задачи можно решать с помощью tokenizr'а?
Да самые разные, связанные с анализом и модификацией кода.
Удаление комментариев из кода
Самый простой пример приведен на php.net — по удалению комментариев:
<?php function strip_comments($fileName) { $source = file_get_contents($fileName); // получаем все метки файла $tokens = token_get_all($source); $result = ''; foreach ($tokens as $token) { if (!is_array($token)) { // простая 1-буквенная лексема $result .= $token; } else { // токен-массив list($id, $value) = $token; switch ($id) { case T_COMMENT: case T_DOC_COMMENT: // комментарии пропускаем break; default: // все остальное -> оставляем "как есть" $result .= $value; break; } } } return $result; } ?>
Как видно из кода — мы получаем массив токенов и, в зависимости от их типа, оставляем или пропускаем.
Так можно решить задачки и поинтереснее — например на основе php-файлов сгенерировать карту классов проекта для автозагрузки.
Получение списка классов из файла
Для получения списка классов из файла я написал вот такую функцию:
<?php function getClasses($fileName) { $result = array(); $content = file_get_contents($fileName); $tokens = token_get_all($content); $waitingClassName = false; $waitingNamespace = false; $waitingNamespaceSeparator = false; $namespace = array(); for ($i = 0, $c = count($tokens); $i < $c; $i++) { if (is_array($tokens[$i])) { list($id, $value) = $tokens[$i]; switch ($id) { case T_NAMESPACE: $waitingNamespace = true; $waitingNamespaceSeparator = false; $namespace = array(); break; case T_CLASS: case T_INTERFACE: $waitingClassName = true; break; case T_STRING: if ($waitingNamespace) { $namespace[] = $value; $waitingNamespace = false; $waitingNamespaceSeparator = true; } elseif ($waitingClassName) { if (!empty($namespace)) { $value = sprintf('%s\\%s', implode('\\', $namespace), $value); } $result[] = $value; $waitingClassName = false; } break; case T_NS_SEPARATOR: if ($waitingNamespaceSeparator && !$waitingNamespace && !empty($namespace)) { $waitingNamespace = true; $waitingNamespaceSeparator = false; } break; } } else { if (($waitingNamespace || $waitingNamespaceSeparator) && ($tokens[$i] == '{' || $tokens[$i] == ';')) { $waitingNamespace = false; $waitingNamespaceSeparator = false; } } } return $result; } ?>
А потом подумал и написал небольшую утилиту, которая генерирует автозагрузчик на основании файлов проекта.
Она анализирует в указанной папке все файлы с расширением "*.php" и строит карту классов (с учетом неймспейсов, конечно), на основании которой потом генерируется автозагрузчик.
Найти ее можно на github.com
Отключение и переопределение стандартных функций
На днях вспомнил, как когда-то ковырялся с расширением runkit. Из его возможностей меня особенно заинтересовало переопределение стандартных функций и песочница, в которой можно было бы запретить использование тех или иных функций.
И сейчас я подумал, а можно ли реализовать подобный функционал, без использования этого расширения. Оказалось, что tokenizer вполне может помочь в этом деле.
Так родилась библиотечка Runtime, с помощью которой, можно во время выполнения скрипта запретить использование любых стандартных функций, или переопределить их.
Приведу примеры работы:
<?php use Dm\Runtime; $code = <<<CODE <?php echo str_replace( 0, 1, 100 ); ?> CODE; // Вывалит Exception, с в котором сообщается, что использование str_replace запрещено Runtime::code($code) ->disableFunction('str_replace') ->execute(); ?>
<?php use Dm\Runtime; $code = <<<CODE <?php echo str_replace( 0, 1, 100 ); ?> CODE; // Выведет 000, вместо 111 Runtime::code($code) ->overrideFunction('str_replace', function ($search, $replace, $subject) { // меняем 1ый и 2й аргументы местами echo str_replace($replace, $search, $subject); }) ->execute(); ?>
Как использовать эти возможности — дело личное. Но использовать это нужно аккуратно.
Я провел небольшое исследование и остался доволен результатом.
Касательно runtime — сложно сказать, где его можно применить, а где нет. Но сама библиотека наглядно демонстрирует работу tokenizer'а и его возможностей.
