Календарь на Май 2024 года: calendar2008.ru/2024/may/
Навигация
Главная »  Sap 

Хак синтаксиса PHP


Источник: habrahabr
olshevskiy87
Вы когда-нибудь задумывались о том, как расширить ядро PHP? Что нужно для того, чтобы создать новое ключевое слово или даже разработать новый синтаксис? Если у вас есть базовые знания языка C, то проблем с созданием небольших изменений возникнуть не должно. Да, я понимаю, что это может быть немного бессмысленно, но неважно - забавно ведь. Давайте создадим альтернативный способ определения класса. Самый простой способ определения, разрешённый в PHP, выглядит следующим образом:
Мы можем упростить синтаксис и заменить фигурные скобки на точку с запятой.
Если вы попытаетесь выполнить этот код, то он, очевидно, выдаст ошибку. Не проблема, мы можем это исправить.

На первом шаге необходимо установить программное обеспечение. $ sudo apt-get install bison re2c
PHP написан на C, однако парсер разработан с помощью Bison. Bison - это генератор синтаксических анализаторов. Официальный сайт определяет его, как генератор парсеров общего назначения, который преобразует помеченную контекстно-свободную грамматику в детерминированный LR или обобщенный LR (GLR) анализатор, применяя таблицы LALR-парсера (Look-Ahead LR parser - прим. пер.). Это очень мощная часть программного обеспечения, о которой можно написать целую книгу. Если захотите узнать больше, я посоветовал бы вам ознакомиться с документацией. Она не из легких, зато содержит хорошие примеры. И если вы когда-нибудь захотите создать язык программирования, то она может стать хорошей стартовой площадкой. Теперь перейдите на http://php.net и скачайте самые свежие исходники PHP. $ tar xvjf php-5.4.14.tar.bz2 $ cd php-5.4.14 $ ./configure $ cd Zend $ ls
Снимите шляпу, потому что перед вами ядро PHP. Код в этих файлах управляет подавляющим большинством веб-серверов. Давайте исследуем его. По умолчанию для файлов генератора Bison используется расширение "y". $ ls *.y zend_ini_parser.y zend_language_parser.y
Мы не хотим возиться с синтаксисом "ini", поэтому остается только "zend_language_parser.y". Откройте его вашим любимым редактором. Теперь, если поискать слово "class", то можно обнаружить следующее: %token T_CLASS "class (T_CLASS)"
Парсер любит работать с токенами. Токен класса - это "T_CLASS". Если вы поищете в тексте "T_CLASS", то найдете нечто подобное: class_entry_type: T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = 0; } / T_ABSTRACT T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } / T_TRAIT { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_TRAIT; } / T_FINAL T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_FINAL_CLASS; } ;
Перед вами четыре разных способа определения класса.
  1. класс (class)
  2. абстрактный класс (abstract class)
  3. трейт (trait)
  4. финальный (листовой, конечный) класс (final class)

В фигурных скобках вы можете видеть несколько присваиваний низкого уровня. Я могу только догадываться, зачем они нужны. Давайте не будем их трогать. Мы на верном пути, но это не совсем то, что мы ищем. Поищите фразу "class_entry_type", которая объединяет те четыре определения класса. Она приведёт вас к пункту назначения. Разобраться в этом легко, но в первый раз читать сложновато. unticked_class_declaration_statement: class_entry_type T_STRING extends_from { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } implements_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, &$3 TSRMLS_CC); } / interface_entry T_STRING { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); } interface_extends_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, NULL TSRMLS_CC); } ;
Здесь есть два объявления. Одно для класса, другое для интерфейса. Нас интересует первое. Оно начинается с "class_entry_type", которое разрешает конструкции: class / abstract class / trait / final class. Следующий элемент - это токен T_STRING. В будущем на его месте будет имя класса. "extends_from" - это группа. Этот элемент может быть преобразован в "extends T_STRING" или оставаться пустым. После этого парсер вызывает движок Zend, чтобы начать объявление класса. { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); }
Вы можете найти эту функцию в файле  zend_compiler.c . void zend_do_begin_class_declaration(const znode *class_token, znode *class_name, const znode *parent_class_name TSRMLS_DC)
Первый аргумент здесь - это токен класса "class_entry_type", второй - имя класса "T_STRING", а последний - родительский класс "extends_from". Ниже идёт группа "implements_list". Уверен, что вы знаете, зачем она нужна. Верно, для определения интерфейсов. Следующие строки образуют обязательное тело класса: открывающая фигурная скобка "{", группа "class_statement_list" и закрывающая фигурная скобка "}". Наконец, парсер сообщает движку Zend, что объявление класса окончено. { zend_do_end_class_declaration(&$1, &$3 TSRMLS_CC); }
Нам необходимо продублировать этот код, но уже без тела класса. unticked_class_declaration_statement: class_entry_type T_STRING extends_from { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } ';' { zend_do_end_class_declaration(&$1, &$3 TSRMLS_CC); } / class_entry_type T_STRING extends_from { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } implements_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, &$3 TSRMLS_CC); } / interface_entry T_STRING { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); } interface_extends_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, NULL TSRMLS_CC); } ;
Это было довольно просто, не так ли? Теперь вам остаётся лишь скомпилировать изменения. $ cd .. $ make
Первая компиляция всегда занимает некоторое время. $ vim test.php
Введите код для тестирования. bar = 10; print_r($a);
А теперь протестируйте его. $ sapi/cli/php test.php FooBar Object ( [bar] => 10 )
Отлично, у вас получилось! Давайте сделаем еще кое-что. В PHP вы объявляете класс с помощью ключевого слова "class". Как насчет того, чтобы сделать его покороче? Думаю, "cls", подойдёт. Ищем файлы лексера: $ cd Zend/ $ ls *.l zend_ini_scanner.l zend_language_scanner.l
Файл Bison оперировал токенами. Лексер позволяет вам решать как преобразовывать код в токены. Откройте zend_language_scanner.l и поищите слово "class". "class" { return T_CLASS; }
Продублируйте этот блок и измените class на cls. "cls" { return T_CLASS; } "class" { return T_CLASS; }
Дело сделано. Скомпилируйте код и можете использовать ключевое слово "cls" вместо "class". Не правда ли забавно? Надеюсь, вам было приятно также, как и мне. Интересуйтесь, исследуйте.



 

 Поздравления / 8 марта / Поэты о женщине.
 SAP вынужден уступить.
 О ПРИМЕНЕНИИ НОВЫХ ИНФОРМАЦИОННЫХ ТЕХНОЛОГИЙ В ГЕРОНТОЛОГИИ.
 IDC: Новые возможности получения конкурентных преимуществ поставщиками ПО и решений SaaS (документация, download).
 SAP - в списке лидеров "магического квадранта".


Главная »  Sap 

© 2024 Team.Furia.Ru.
Частичное копирование материалов разрешено.