DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art)

1,323 views
1,280 views

Published on

Published in: Technology, News & Politics
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,323
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
2
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art)

  1. 1. Расширяем PHPдоклад для тех, кто скучает по <?php asm mov ax,bx; ?>
  2. 2. Зачем понадобилось расширение?Имеется собственный движок для разработки сайтови систем автоматизации бизнеса (PHP+MySQL+JS).Движок используется с 2006 года.Используется собственный шаблонизатор,к которому мы очень привыкли. Вопрос «Почему не использовали [SMARTY|XSLT|…]» оставим за рамками доклада
  3. 3. Как выглядит шаблонизатор?Пример шаблона: Результат:<ul> <ul> {:list} <li> первый пункт </li> <li> {!item} </li> <li> второй пункт </li> {:/list} </ul></ul>Подача данных в шаблон:Array( “list” => Array ( Array ( “item” => ”первый пункт” ), Array ( “item” => ”второй пункт” ))); Вопрос о достоинствах и недостатках шаблонизатора оставим за рамками доклада
  4. 4. Как это обрабатывалось в PHP?Примерно такой конструкцией:$result = preg_replace(”/ {([?~:])([a-zA-Z0-9_]*)(([<>!=]{1,2}) ([a-zA-Z0-9_]*)){0,1}}(.*){1/2} /iseU”, ”ProcessBlock(‘$0′, ‘$1′, ‘$2′, ‘$4′, ‘$5′, ‘$6′, $vars_arr) ”, $result); Регулярное выражение с eval’ом
  5. 5. И что, это работало?Да!За пять лет на этом движке было созданопорядка двухсот проектов.И все было бы хорошо, если бы на некоторых проектахнас не стала подводить производительность парсера.В определенных условиях он потребляет серьезноеколичество ресурсов – памяти и процессора. Что же нам делать???
  6. 6. Что же нам делать?Первой идеей было создание компилирующегошаблонизатора. Шаблон должен трансформироватьсяв HTML, перемешанный с простыми управляющимиконструкциями PHP (циклы, условия, вывод).На вход такому скрипту-шаблону подаются данные,скрипт выполняется, получаем HTML-страницу.Не получилось – шаблонизатор является управляющим(возможен вызов программных модулей и их методовиз шаблона), шаблоны могут каскадироваться. Скорее всего, задача решаема – но из наших программистов не решил ее никто.
  7. 7. Какие проблемы былис компилирующим шаблонизатором?Схема парсинга: Шаблон Шаблон Шаблон программного страницы интерфейса модуля Тело Класс в PHP Метод в классе в PHP страницы Скорее всего, задача решаема – но из наших программистов не решил ее никто.
  8. 8. А напишем расширение!Вторая идея – написать на C++ расширение, которое будетсливать шаблон с данными.И это сработало!На пути нам встретились три основные проблемы. На самом деле, их было значительно больше.
  9. 9. Первая проблема: как в недрах PHP представленыассоциативные массивы…Данные, хранящиеся в ассоциативных массивах в PHP,становятся в C++ объектами HashTable.Работать с ними можно при помощи набора функций –довольно неудобных (начать с названий – как вам нравитсяzend_hash_internal_pointer_reset_ex?).Работа с многоэтажными массивами превращаетсяв сложное занятие.А у нас в движке данные как раз хранятся в многоэтажныхмассивах. Каждый уровень вложенности (метод, цикл) –это новый уровень вложенности массива. В принципе, можно нас обвинить в том, что мы сами себе придумали проблему.
  10. 10. Как решили?За один проход разворачиваем всю иерархиюассоциативных массивов в два обычных массива –один с ключами, второй со значениями.Ключи при этом собираем в одну строку.$arr = Array ( “first” => 1, “second” => Array ( Array ( “number” => 2 )));Keys = [ "first ", "second0number" ];Values = [ "1", "2" ];Заодно преобразуем и ключи, и значения в строки. Поиск значения с любого «этажа» - чистое удовольствие!
  11. 11. Вторая проблема: как вызвать функцию, содержащуюся в PHP-скрипте, из расширения?zval *function, *itemname, *retval;zval **params[1]; пусть itemname – параметр функции functionMAKE_STD_ZVAL ( function );MAKE_STD_ZVAL ( itemname );ZVAL_STRING ( function, “MyPHPFunction“, 1);ZVAL_STRING ( itemname, “значение“, 1); нигде не определенный параметрparams[0] = &itemname;if ( call_user_function_ex ( CG (function_table), NULL, function, &retval, 1, params, 0,NULL TSRMLS_CC ) == FAILURE ) непонятный макрос zend_error (E_ERROR, “Function call failed“);else { if ( Z_TYPE_P (retval) == IS_STRING ) { … тут мы можем обработать возвращенное значение, используямакросы Z_STRLEN_P ( retval ) и Z_STRVAL_P ( retval ) Этот код работает только внутри функции, вызываемой из PHP: ZEND_FUNCTION(Func)
  12. 12. Как решили?Документация тут не поможет… лезем в исходники PHP.Оказывается, function_table – это параметр, передаваемыйв ZEND_FUNCTION.За макросом TSRMLS_CC скрывается переменная tsrm_ls,тоже являющаяся одним из стандартных параметров дляфункций, объявленных при помощи ZEND_FUNCTION.Остается передать эти параметры в нашу innerCPPFunction:char *result = innerCPPFunction ( CG ( function_table ), tsrm_ls );char *innerCPPFunction ( HashTable *function_table, void ***tsrm_ls ){…} innerCPPFunction – это функция, куда мы захотели вынести вызов функции PHP
  13. 13. Передача выполнения между PHP и расширениемСхема парсинга: Шаблон Шаблон Шаблон программного страницы интерфейса модуля Тело Класс в PHP Метод в классе в PHP страницыPHP => extension => PHP => extenstion => PHP => extension Скорее всего, задача решаема – но из наших программистов не решил ее никто.
  14. 14. Третья проблема: глобальные переменныеГлобальные переменные (например, общие массивы ключейи значений), объявленные в расширении, сохраняют своизначения между вызовами различных функций из PHP.Приходится о них заботиться. Учитывая, что схема парсингавыглядит так: сначала из PHP вызывается функциярасширения, которая сливает данные с шаблоном верхнегоуровня, а затем она вызывает функции из PHP, которыеинициализируют содержащиеся в странице программныемодули (и цикл повторяется). То есть, выполнение переходитиз PHP в расширение, затем обратно в PHP, затем сновав расширение. При втором вызове как раз важно не забытьо том, что глобальные переменные первым вызовомуже проинициализированы. Ура, расширение работает!
  15. 15. Что выиграли в плане производительности?Парсинг при помощи регулярных 100%выраженийПарсинг при помощи расширения PHP 53%XMLXSLT 36%«чистый» PHP (компилирующий 3%шаблонизатор) парсинг при помощи регулярных выражений принят за 100%
  16. 16. Выводы?Конечно, «чистый» PHP побеждает с чудовищным отрывом.Но проблема создания компилирующего шаблонизаторадля движка с управляющим синтаксисом шаблона все ещеждет своего героя…Поэтому в итоге было принято решение использоватьв новой версии нашего продукта шаблонизацию XSLT:дополнительный плюс состоит в том, что этот язык являетсястандартом, с которым некоторые разработчики ужезнакомы, а не собственным «эксклюзивным» решением.А расширение PHP, реализующее парсер «старого»синтаксиса, помогло нам улучшить производительностьранее написанных программных систем, уже работающиху наших клиентов. А еще теперь мы знаем, как сделать <?php asm mov ax,bx; ?>
  17. 17. Спасибо за внимание! Вопросы?Также можно обсудить в ЖЖ:http://serge-index.livejournal.com

×