(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

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

Источник: habrahabr
olshevskiy87

Вы когда-нибудь задумывались о том, как расширить ядро PHP? Что нужно для того, чтобы создать новое ключевое слово или даже разработать новый синтаксис? Если у вас есть базовые знания языка C, то проблем с созданием небольших изменений возникнуть не должно. Да, я понимаю, что это может быть немного бессмысленно, но неважно - забавно ведь.

Давайте создадим альтернативный способ определения класса. Самый простой способ определения, разрешённый в PHP, выглядит следующим образом:

<?php class ClassName {}
Мы можем упростить синтаксис и заменить фигурные скобки на точку с запятой.

<?php class ClassName;
Если вы попытаетесь выполнить этот код, то он, очевидно, выдаст ошибку. Не проблема, мы можем это исправить.

На первом шаге необходимо установить программное обеспечение.

$ 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
Введите код для тестирования.

<?php class FooBar; $a = new FooBar; $a->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".

<ST_IN_SCRIPTING>"class" { return T_CLASS; }
Продублируйте этот блок и измените class на cls.

<ST_IN_SCRIPTING>"cls" { return T_CLASS; } <ST_IN_SCRIPTING>"class" { return T_CLASS; }
Дело сделано. Скомпилируйте код и можете использовать ключевое слово "cls" вместо "class".

Не правда ли забавно? Надеюсь, вам было приятно также, как и мне. Интересуйтесь, исследуйте.

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 21.05.2013 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
DevExpress / Universal Subscription
Inventory 9
erwin Data Modeler Standard Edition r9.7 - Product plus 1 Year Enterprise Maintenance Commercial
VMware Workstation Pro 12 for Linux and Windows, ESD
Microsoft 365 Apps for business (corporate)
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование в AutoCAD
СУБД Oracle "с нуля"
OS Linux для начинающих. Новости + статьи + обзоры + ссылки
Windows и Office: новости и советы
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100