zend engine что это

Разрабатываем модуль PHP на C++. Часть 1: экскурс в мир Zend Engine 2

Доброго времени суток, уважаемое Харбасообщество!

Под катом изложена некоторая информация касательно написания расширений для PHP c использованием C++, почерпнутая мной из различных источников (по большей части англоязычных) и ковыряния исходников Zend Engine 2 во время разработки одного модуля для собственных нужд. Так как объем ее достаточно велик, далее я старался быть краток.

Маленький дисклеймер: содержимое статьи не есть истина в первой инстанции, не основывается на официальной документации (а есть ли она?) и является моим субъективным взглядом на ZE 2. Тем не менее, в свое время я был бы рад найти нечто подобное на просторах Рунета, дабы сэкономить время на начальных этапах разработки.

Внутренний мир Zend Engine 2

Основные типы данных

Zend Engine 2 написан на C. Это сильно повлияло на его внутреннюю экосистему. В отсутствие класс-объектной парадигмы внутри ZE 2 расплодились глобальные переменные, свободные функции и подобия пользовательских типов данных — структуры. На все случаи жизни существуют свои комбинации простых и составных типов данных и процедур, предназначенных для их обработки.

Наиболее часто встречается структура zval (zend-значение?). Структура является представлением PHP-переменной по обратную сторону от userspace (под userspace здесь и далее будем понимать код, написанный на PHP, выполнением которого и занимается ZE, следовательно, обратная сторона userspace — это код на C).
PHP является языком со слабой динамической типизацией и автоматическим управлением памятью, переменные в этом языке могут менять свой тип на протяжении своего жизненного цикла и не требуют явного удаления программистом после того, как необходимость в них отпадает (сборщик мусора самостоятельно позаботится об этом). За эти прихоти отчасти отдуваться приходится и zval`у. На данный момент (PHP 5.3.3) эта структура определена следующим образом (zend.h):

Что же мы здесь видим? Как это не удивительно, zend-значение (zval) не является непосредственно значением переменной. Значение переменной хранится в поле value, тип которого zvalue_value (ниже). Тип значения, хранящегося в value, определяется полем type и может быть одним из следующих (zend.h):

Ага, вот они — те самые 8 типов данных PHP, которые люди так часто не могут перечислить на собеседованиях! Плюс два отдельно стоящих значения IS_CONSTANT и IS_CONSTANT_ARRAY.

Структура также содержит информацию о количестве ссылок на данную переменную (refcount__gc) и флаг, определяющий, является ли эта переменная ссылкой (is_ref__gc). Эта информация нужна для организации эффективной работы с памятью внутри ZE. Например следующая ситуация в userspace:

приведет к созданию объекта zval val, имеющего тип IS_LONG (все целочисленные в userspace соответствуют C long по другую сторону от userspace), установке у него is_ref_gc в 0 и refcount_gc в 1 (первая строка) и регистрации символа «foo» (об этом в следующих сериях) в текущей таблице символов процесса (на самом деле просто ассоциативный массив имен переменных и их значений) с val в качестве значения. На второй же строке новый экземпляр zval создан не будет. У только что помещенного в таблицу символов val`а будет увеличен на 1 счетчик ссылок, и символ «bar» будет зарегистрирован в таблице символов с этим же самым val`ом. За счет этого будет сокращено количество необходимых выделений памяти для создания новых переменных.
Когда же интерпретатор встретит код:

Теперь посмотрим на тип zvalue_value (zend.h):

Вот мы и добрались до объектов. Структура zend_object_value предназначена для представления userspace-переменных, содержащими в себе объекты. А что из себя представляют объект в класс-объектной парадигме? Объект — это симбиоз данных и методов по их обработке. Теперь давайте посмотрим на структуру zend_object_value (zend_type.h):

Структура представляет собой объединение некоторого целочисленного идентификатора (handle) и еще одной структуры (zend_object_handlers *handlers), содержащей в себе указатели на функции, которые будут вызываться движком ZE 2 при наступлении тех или иных событий, связанных с объектом. К таким событиям относятся: приравнивание новой переменной значения переменной, содержащей в себе объект (zend_object_add_ref_t add_ref), выход за пределы области видимости, инициализация другим значением или вызов unset для переменной, содержащей в себе объект (zend_object_del_ref_t del_ref), клонирование объекта вызовом __clone ( zend_object_clone_obj_t clone_obj), обращение к свойству объекта (zend_object_read_property_t read_property), запись свойства объекта (zend_object_write_property_t write_property) и т.д. Сама структура zend_object_handlers выглядит следующим образом (zend_object_handlers.h):

а прочитать о ней подробно можно здесь.

Отвлеклись, вернемся к zend_object_value. Итак, что в ней содержится помимо указателей на функции-обработчики событий? А ничего! Если некое подобие попытки определения поведения объекта мы увидели в _zend_object_handlers, то кроме какого-то странного идентификатора (handle) никаких данных, специфичных для конкретного экземпляра в ней не наблюдается. Но идентификатор сам по себе (сферический в вакууме) смысла не имеет. Значит должно быть какое-то хранилище однородных сущностей, где этот идентификатор будет отличать одну сущность от другой.
Таким хранилищем в ZE является Zend Object Storage. Ключами в нем являются дескрипторы объектов (zend_object_value.handle), а значениями… Да, да, вы уже наверное догадались — еще один вид структур — zend_object (zend.h):

Здесь из разрозненных кусочков уже начинает складываться картина маслом. HashTable *properties — вот где можно держать данные, специфичные для конкретного экземпляра. Поле properties — это стандартный для ZE ассоциативный массив, ключами которого должны являться имена полей класса текущего объект, а значениями — текущие значения полей (свойств) этого объекта.

Итак, на данный момент мы имеем следующие возможности для работы с объектами — умеем переопределять стандартное поведение объекта в тех или иных ситуация (за счет переопределения соответствующих функций в zend_object_handlers) и можем хранить данные в полях экземпляров, записывая их в HashTable *properties, связанную с текущим объектом. Чего-то не хватает… Ах да, а как же добавлять пользовательское поведение объекту (создавать новые методы)? Так как методы — это нечто общее для всех объектов одного класса, их логично было бы разместить в некоторой структуре, разделяемый доступ к которой был бы у всех объектов класса. Такой структурой является zend_class_entry (zend.h):

Назначение структуры zend_class_entry — представлять общие аспекты всех объектов одного класса. zend_class_entry фактически и есть сам класс. Структура, как видите, не маленькая и рассматривать назначение каждого из ее полей не есть задача этой статьи. Остановим наше внимание на полях, которые я пометил комментариями.

const struct _zend_function_entry *builtin_functions — указатель на массив структур _zend_function_entry. Не трудно догадаться, что это и есть методы нашего будущего класса. Так как он помечен модификатором const, изменять элементы этого массива (т.е. переопределять методы инстанцированного объекта класса) не возможно (в отличие от zend_object_value.handlers).

Поля, начиная с constructor по unserialize_func, являются магическими методами PHP (не трудно догадаться, что unserialize_func — это __wakeup, а serialize_func — это __sleep, остальные же методы имеют схожую мнемонику).

В процессе создания собственного расширения можно как добавлять записи в builtin_functions, так и переопределять магические методы будущего класса.

Последним, но не по значению, героем этого экскурса в увлекательный мир структур ZE, будет структура, которая занимается представлением самого модуля расширения — zend_module_entry (zend_modules.h):

Так как в общем случае расширение может реализовывать не один, а сразу несколько классов, или же наоборот — экспортировать одни лишь функции (на ООП свет клином не сошелся, как говорится), рассмотрим, как это повлияло на дизайн вышеупомянутой структуры:

Иерархия типов данных

В предыдущем пункте мы рассмотрели путь от рядового zval до генерала zend_module_entry. Умело оперируя этими типами, можно создать свое расширение PHP и организовать слаженную фабрику по производству объектов. На самом деле, модуль расширения PHP похож на школу по подготовке кадров для userspace. Сначала ее нужно построить (вызвать PHP_MINIT_FUNCTION) и зарегистрировать на бирже труда (объявив в PHP_MINIT_FUNCTION экспортируемые классы или функции) в качестве рекрутерского агенства с определенной направленностью, а затем по первому запросу на получение сотрудника (нового экземпляра класса) запускать цикл по подготовке бойца (создания объекта). Подготовка заключается в выделении памяти под создаваемый объект, связывании его со специфическими обработчиками событий (zend_object_handlers) и собственным классом (zend_class_entry), в котором содержатся методы будущего объекта, и регистрации объекта в Zend Object Storage с последующим присвоением ему уникального идентификатора. Такая подготовка обычно помещается в функцию имя_расширения_objects_new и связывается с полем zend_class_entry.create_object.

Схематически, структуру расширения можно изобразить в следующем виде:

zend engine что это. Смотреть фото zend engine что это. Смотреть картинку zend engine что это. Картинка про zend engine что это. Фото zend engine что это

А чтобы более наглядно представить себе иерархию типов данных в расширении приведу следующую схемку:

zend engine что это. Смотреть фото zend engine что это. Смотреть картинку zend engine что это. Картинка про zend engine что это. Фото zend engine что это

Заключение

В статье получилось много текста и совсем мало кода, но без экскурса в мир типов данных ZE 2 пытаться понять назначение вызовов тех или иных функций было бы достаточно сложно. В следующей части я приведу объяснение первых шагов, которые нужно выполнить, чтобы создать свой модуль расширения PHP, но перед этим затрону тему работы с zval`ами и управления выделением памятью.

Источник

Введение в Zend Framework

Update (2014): Это статья 2007 года, которая, к моему удивлению, до сих пор пользуется спросом. По этой причине я обновил её в соответствии с новыми правилами оформления постов на Хабре, и добавил подсветку синтаксиса для примеров кода. Если кому-то захочется что-то добавить или исправить, исходник текста с хабра-разметкой выложен в открытый доступ: gist.github.com/dreikanter/2b4ee996d7a775e707d7

Аннотация от переводчика

PHP — один из самых широко распространенных языков разработки веб-приложений и при этом один из самых спорных. Я очень часто видел негативное отношение к этой технологии, да и недостатки, провоцирующие это отношение — не для кого не являются секретом. Тем не менее, PHP активно эволюционирует и во многих отношениях постепенно становитс лучше. Одним из серьезных шагов его развития, на мой взгляд, является появление MVC-фреймворков, призванных систематизировать процесс разработки веб-приложений и приучить к порядку разработчиков, которым зачастую здорово не хватает силы воли, чтобы при всей предоставляемой языком свободе, сохранить грамотную и красивую инфраструктуру разрабатываемого ПО (сразу уточню, что последнее утверждение субъективно и основано исключительно на виденном мной коде различных программных решений).

В последнее время я активно заинтересовался архитектурой MVC и сделал свою собственную реализацию фреймворка на этой концепции для PHP4. Совсем недавно на глаза попался первый официальный релиз Zend Framework, о котором я давно слышал, но все руки не доходили с ним поиграть. Для PHP существуют и другие подобный библиотеки, но в данном случае привлек бренд.

Вначале я испытал некоторое разочарование в официальной документации, которая оказалась хорошим reference-ом, но просто никаким tutorial-ом и поэтому на роль вводного материала не подошла. Но почти сразу нашлась неплохая статья, в которой автор пошагово разбирает процесс создания веб-приложения. Материала достаточно, чтобы разобравшись в нем можно было начать осмысленно ориентироваться в мане от Zend. Текст ориентирован на программистов, имеющих некоторый опыт в программировании веб-приложений на PHP, но не знакомых с Zend Framework.

Статья довольно объемная, поэтому я решил разделить ее на две части, дабы не перегружать никому мозг. Ниже я привожу первую часть ее перевода, в которой в общих чертах освещена концепция MVC и разобрана структура веб-приложения, реализованного на Zend Framework. Если материал заинтересует хабрачитателей, я опубликую продолжение.

В завершение введения, небольшой дисклаймер. Уважаемые любители Ruby on Rails и Python on Django, я уважаю вашу религию 🙂 Убедительная просьба не развивать в комментариях тему о том, какой фреймворк или язык лучше. Это совсем не относящаяся к топику данного поста тема. Мне и, вероятно, многим сейчас будет гораздо интереснее узнать об опыте реальных разработок ПО на Zend.

Материал рассчитан на то, чтобы дать общее представление об использовании Zend Framework для создания простейших приложений с использованием баз данных. Приведенные примеры были протестированы на Zend Framework версии 1.0.0. Скорее всего, они будут работать и с более поздними версиями, но не с более ранними.

Архитектура Модель-Вид-Контроллер

PHP-приложение, написанное традиционным способом, может представлять собой некое подобие следующего примера:

В ходе жизненного цикла приложений подобного типа, трудоемкость их поддержки становится крайне высока, т. к. меняющиеся требования заказчика приводят к необходимости вносить большое количество изменений («заплаток») в исходный код. Это делает его плохо структурированным и трудно читаемым. Один из методов для повышения гибкости приложения состоит в разделении кода на три категории:

Системные требования

У Zend Framework следующие требования:

Где скачать Zend Framework

Zend Framework доступен для свободного скачивания в виде ZIP или TAR.GZ архивов по адресу framework.zend.com/download/stable.

Структура директорий

На данный момент в Zend Framework жестко не стандартизирована структура директорий приложения, но в официальной документации рекомендуется использовать общепринятую схему. Эта схема основана на том, что пользователь, как предполагается, имеет полный доступ к конфигурированию сервера Apache. Мы же будем использовать немного видоизмененный подход, чтобы смягчить данные требования и сделать Zend Framework более применимым в условиях широко распространенного разделяемого хостинга.

Для начала, создайте директорию zf-tutorial в корневом каталоге вашего тестового веб-сайта. Это будет означать, что URL этой директории примет вид localhost/zf-tutorial (адрес может варьироваться, в зависимости от настроек вашего сервера).

После этого дополнительно создайте следующую структуру каталогов для хранения файлов веб-приложения:

Начальная загрузка

zf-tutorial/application /.htaccess, zf-tutorial/library/.htaccess:

Файл начальной загрузки index.php

Файл начальной загрузки zf-tutorial/index.php содержит следующий код:

Рассмотрим код подробнее.

Данные строки обеспечивают вывод интерпретатором всех сообщений о происходящих ошибках (подразумевается, что в конфигурационном файле php.ini параметру display_errors задано значение on ). Вторая строка кода задает часовой пояс, согласно стандартному требованию PHP 5.1+. Приведенное в примере значение параметра следует заменить на соответствующее вашему географическому положению.

Файл Zend/Loader.php содержит класс Zend_Loader со статическими функциями, позволяющими подключать любой класс из Zend Framework.

Далее нам понадобится сообщить первичному контроллеру о местоположении директории с другими контроллерами:

Учитывая, что наша программа предназначена для образовательных целей и функционирует на тестовой системе, можно разрешить все исключения, которые могут происходить при ее работе. По-умолчанию, первичный контроллер будет перехватывать их все до одного и хранить в свойстве _exceptions своего объекта (данному объекту соответствует англоязычный термин Response object). Этот объект используется для хранения всей информации, возвращаемой при обращении к заданному URL, что включает HTTP-заголовок, содержимое веб-страницы и массив произошедших исключений.

Первичный контроллер автоматически выдает заголовок и контент страницы непосредственно перед окончанием своей работы. Тех, кто ранее не встречался с архитектурой веб-приложений, подобной Zend Framework, может привести в некоторое недоумение тот факт, что для отображения сообщений об исключительных ситуациях, их (исключения) необходимо повторно возбуждать. Эта особенность становится актуальной для серверов, работающих в своем штатном режиме, когда вывод сообщений об ошибках на страницах неуместен.

В итоге, после всех выполненных приготовлений, наше приложение может быть запущено:

Это говорит о том, что мы еще не до конца сформировали наше веб-приложение. и прежде чем приступить к делу, стоит уяснить, в чем конкретно состоит задача.

Веб-сайт

Предположим, что мы хотим создать простую базу данных для хранения информации о компакт-дисках. На главной странице будет отображен список дисков в нашей коллекции, а так же будет предоставлена возможность добавлять новые CD, редактировать и удалять ненужные записи. Для хранения данных, используем базу со схемой, приведенной ниже:

FieldnameTypeNull?Notes
idIntegerNoPrimary key, Autoincrement
artistVarchar(100)No
titleVarchar(100)No

Необходимые страницы

Для реализации перечисленных выше функций, нам понадобятся следующие страницы:

Организация страниц

Прежде чем приступить к созданию файлов, следует понять, как принято организовывать их в Zend Framework. Согласно принятой терминологии, каждая страница веб-приложения определяется термином «действие».

Примечание переводчика. Небольшое пояснение терминологии. Англоязычным оригиналом используемого в данной статье термина «действие» является слово action. Приведенный перевод на данный момент еще не является устоявшимся понятием, и в некоторых текстах о модели MVC вместо него попадаются иные определения. Например, так же известна прямая (и режущая слух) транслитерация — «экшен».

В Zend Framework так же используются модули для группировки контроллеров, но наше приложение не настолько велико, чтобы имело смысл их применять.

Учитывая, что данная статья ориентирована на первое знакомство с Zend Framework, мы не будем пока касаться таких нюансов работы веб-приложения, как авторизация пользователей. Ограничимся реализацией основной функциональности, а именно — набором перечисленных выше страниц. Учитывая, что все они относятся к работе с альбомами, можно отнести их к одному контроллеру. Мы используем стандартный контроллер, а его действия будут выглядеть следующим образом:

СтраницаКонтроллерДействие
Начальная страницаIndexIndex
Добавить альбомIndexAdd
Редактировать альбомIndexEdit
Удалить альбомIndexDelete

Настройка контроллера

В данном макете контроллера каждое действие будет выводить свое имя. Это можно протестировать, задавая приведенные ниже URL:

URLDisplayed text
localhost/zf-tutorialin IndexController::indexAction()
localhost/zf-tutorial/index/addin IndexController::addAction()
localhost/zf-tutorial/index/editin IndexController::editAction()
localhost/zf-tutorial/index/deletein IndexController::deleteAction()

Теперь у нас имеется работающий класс-роутер с набором действий, соответствующих каждой странице веб-приложения. При возникновении каких-либо проблем в процессе его создания, обратитесь к разделу «Устранение неполадок» в конце статьи.

А теперь пришло время добавить в наше приложение новый вид.

Простейший вариант использования Zend_View выглядит как показано ниже:

Легко можно понять, что если бы мы захотели поместить приведенный код в каждую функцию-действие, это привело бы к его многократному дублированию, что не явилось бы красивым решением. Вместо этого, мы выполним инициализацию вида отдельно, а из функций-действий затем будем только обращаться к нему.

После рендеринга, сгенерированный контент веб-страницы сохраняется в объекте Response, который, как было упомянуто ранее, служит для группировки HTTP заголовков, содержимого страницы и сгенерированных в ходе работы скрипта исключительных ситуаций. в итоге, первичный контроллер автоматически отсылает веб-клиенту заголовок страницы и ее содержательную часть.

Итак, для того, чтобы добавить вид в наше приложение, все, что нам потребуется, — создать несколько файлов с тестовым кодом. Никаких принципиальных изменений внутри контроллера не понадобится, но для большей наглядности мы выполним небольшую корректировку выводимого им текста.

Изменения внутри класса IndexController выделены жирным шрифтом.

Теперь нам понадобится добавить четыре файла для видов нашего веб-приложения.

При тестирование каждого из действий, должны быть отображены выделенные заголовки каждой из страниц.

Общий HTML-код

(Обратите внимание на изменения, добавленный в код.)

Файлы видов так же понадобится откорректировать.

Стилевое оформление

Ссылку на CSS-фалй понадобится добавить в секцию файла header.phtml :

В завершении, понадобится создать, собственно, сам стилевой файл.

Это заставит страницы выглядеть несколько приятнее.

Источник

Разрабатываем модуль PHP на C++. Часть 1: экскурс в мир Zend Engine 2

Доброго времени суток, уважаемое Харбасообщество!

Под катом изложена некоторая информация касательно написания расширений для PHP c использованием C++, почерпнутая мной из различных источников (по большей части англоязычных) и ковыряния исходников Zend Engine 2 во время разработки одного модуля для собственных нужд. Так как объем ее достаточно велик, далее я старался быть краток.

Маленький дисклеймер: содержимое статьи не есть истина в первой инстанции, не основывается на официальной документации (а есть ли она?) и является моим субъективным взглядом на ZE 2. Тем не менее, в свое время я был бы рад найти нечто подобное на просторах Рунета, дабы сэкономить время на начальных этапах разработки.

Внутренний мир Zend Engine 2

Основные типы данных

Zend Engine 2 написан на C. Это сильно повлияло на его внутреннюю экосистему. В отсутствие класс-объектной парадигмы внутри ZE 2 расплодились глобальные переменные, свободные функции и подобия пользовательских типов данных — структуры. На все случаи жизни существуют свои комбинации простых и составных типов данных и процедур, предназначенных для их обработки.

Наиболее часто встречается структура zval (zend-значение?). Структура является представлением PHP-переменной по обратную сторону от userspace (под userspace здесь и далее будем понимать код, написанный на PHP, выполнением которого и занимается ZE, следовательно, обратная сторона userspace — это код на C).
PHP является языком со слабой динамической типизацией и автоматическим управлением памятью, переменные в этом языке могут менять свой тип на протяжении своего жизненного цикла и не требуют явного удаления программистом после того, как необходимость в них отпадает (сборщик мусора самостоятельно позаботится об этом). За эти прихоти отчасти отдуваться приходится и zval`у. На данный момент (PHP 5.3.3) эта структура определена следующим образом (zend.h):

Что же мы здесь видим? Как это не удивительно, zend-значение (zval) не является непосредственно значением переменной. Значение переменной хранится в поле value, тип которого zvalue_value (ниже). Тип значения, хранящегося в value, определяется полем type и может быть одним из следующих (zend.h):

Ага, вот они — те самые 8 типов данных PHP, которые люди так часто не могут перечислить на собеседованиях! Плюс два отдельно стоящих значения IS_CONSTANT и IS_CONSTANT_ARRAY.

Структура также содержит информацию о количестве ссылок на данную переменную (refcount__gc) и флаг, определяющий, является ли эта переменная ссылкой (is_ref__gc). Эта информация нужна для организации эффективной работы с памятью внутри ZE. Например следующая ситуация в userspace:

приведет к созданию объекта zval val, имеющего тип IS_LONG (все целочисленные в userspace соответствуют C long по другую сторону от userspace), установке у него is_ref_gc в 0 и refcount_gc в 1 (первая строка) и регистрации символа «foo» (об этом в следующих сериях) в текущей таблице символов процесса (на самом деле просто ассоциативный массив имен переменных и их значений) с val в качестве значения. На второй же строке новый экземпляр zval создан не будет. У только что помещенного в таблицу символов val`а будет увеличен на 1 счетчик ссылок, и символ «bar» будет зарегистрирован в таблице символов с этим же самым val`ом. За счет этого будет сокращено количество необходимых выделений памяти для создания новых переменных.
Когда же интерпретатор встретит код:

Теперь посмотрим на тип zvalue_value (zend.h):

Вот мы и добрались до объектов. Структура zend_object_value предназначена для представления userspace-переменных, содержащими в себе объекты. А что из себя представляют объект в класс-объектной парадигме? Объект — это симбиоз данных и методов по их обработке. Теперь давайте посмотрим на структуру zend_object_value (zend_type.h):

Структура представляет собой объединение некоторого целочисленного идентификатора (handle) и еще одной структуры (zend_object_handlers *handlers), содержащей в себе указатели на функции, которые будут вызываться движком ZE 2 при наступлении тех или иных событий, связанных с объектом. К таким событиям относятся: приравнивание новой переменной значения переменной, содержащей в себе объект (zend_object_add_ref_t add_ref), выход за пределы области видимости, инициализация другим значением или вызов unset для переменной, содержащей в себе объект (zend_object_del_ref_t del_ref), клонирование объекта вызовом __clone ( zend_object_clone_obj_t clone_obj), обращение к свойству объекта (zend_object_read_property_t read_property), запись свойства объекта (zend_object_write_property_t write_property) и т.д. Сама структура zend_object_handlers выглядит следующим образом (zend_object_handlers.h):

а прочитать о ней подробно можно здесь.

Отвлеклись, вернемся к zend_object_value. Итак, что в ней содержится помимо указателей на функции-обработчики событий? А ничего! Если некое подобие попытки определения поведения объекта мы увидели в _zend_object_handlers, то кроме какого-то странного идентификатора (handle) никаких данных, специфичных для конкретного экземпляра в ней не наблюдается. Но идентификатор сам по себе (сферический в вакууме) смысла не имеет. Значит должно быть какое-то хранилище однородных сущностей, где этот идентификатор будет отличать одну сущность от другой.
Таким хранилищем в ZE является Zend Object Storage. Ключами в нем являются дескрипторы объектов (zend_object_value.handle), а значениями… Да, да, вы уже наверное догадались — еще один вид структур — zend_object (zend.h):

Здесь из разрозненных кусочков уже начинает складываться картина маслом. HashTable *properties — вот где можно держать данные, специфичные для конкретного экземпляра. Поле properties — это стандартный для ZE ассоциативный массив, ключами которого должны являться имена полей класса текущего объект, а значениями — текущие значения полей (свойств) этого объекта.

Итак, на данный момент мы имеем следующие возможности для работы с объектами — умеем переопределять стандартное поведение объекта в тех или иных ситуация (за счет переопределения соответствующих функций в zend_object_handlers) и можем хранить данные в полях экземпляров, записывая их в HashTable *properties, связанную с текущим объектом. Чего-то не хватает… Ах да, а как же добавлять пользовательское поведение объекту (создавать новые методы)? Так как методы — это нечто общее для всех объектов одного класса, их логично было бы разместить в некоторой структуре, разделяемый доступ к которой был бы у всех объектов класса. Такой структурой является zend_class_entry (zend.h):

Назначение структуры zend_class_entry — представлять общие аспекты всех объектов одного класса. zend_class_entry фактически и есть сам класс. Структура, как видите, не маленькая и рассматривать назначение каждого из ее полей не есть задача этой статьи. Остановим наше внимание на полях, которые я пометил комментариями.

const struct _zend_function_entry *builtin_functions — указатель на массив структур _zend_function_entry. Не трудно догадаться, что это и есть методы нашего будущего класса. Так как он помечен модификатором const, изменять элементы этого массива (т.е. переопределять методы инстанцированного объекта класса) не возможно (в отличие от zend_object_value.handlers).

Поля, начиная с constructor по unserialize_func, являются магическими методами PHP (не трудно догадаться, что unserialize_func — это __wakeup, а serialize_func — это __sleep, остальные же методы имеют схожую мнемонику).

В процессе создания собственного расширения можно как добавлять записи в builtin_functions, так и переопределять магические методы будущего класса.

Последним, но не по значению, героем этого экскурса в увлекательный мир структур ZE, будет структура, которая занимается представлением самого модуля расширения — zend_module_entry (zend_modules.h):

Так как в общем случае расширение может реализовывать не один, а сразу несколько классов, или же наоборот — экспортировать одни лишь функции (на ООП свет клином не сошелся, как говорится), рассмотрим, как это повлияло на дизайн вышеупомянутой структуры:

Иерархия типов данных

В предыдущем пункте мы рассмотрели путь от рядового zval до генерала zend_module_entry. Умело оперируя этими типами, можно создать свое расширение PHP и организовать слаженную фабрику по производству объектов. На самом деле, модуль расширения PHP похож на школу по подготовке кадров для userspace. Сначала ее нужно построить (вызвать PHP_MINIT_FUNCTION) и зарегистрировать на бирже труда (объявив в PHP_MINIT_FUNCTION экспортируемые классы или функции) в качестве рекрутерского агенства с определенной направленностью, а затем по первому запросу на получение сотрудника (нового экземпляра класса) запускать цикл по подготовке бойца (создания объекта). Подготовка заключается в выделении памяти под создаваемый объект, связывании его со специфическими обработчиками событий (zend_object_handlers) и собственным классом (zend_class_entry), в котором содержатся методы будущего объекта, и регистрации объекта в Zend Object Storage с последующим присвоением ему уникального идентификатора. Такая подготовка обычно помещается в функцию имя_расширения_objects_new и связывается с полем zend_class_entry.create_object.

Схематически, структуру расширения можно изобразить в следующем виде:

zend engine что это. Смотреть фото zend engine что это. Смотреть картинку zend engine что это. Картинка про zend engine что это. Фото zend engine что это

А чтобы более наглядно представить себе иерархию типов данных в расширении приведу следующую схемку:

zend engine что это. Смотреть фото zend engine что это. Смотреть картинку zend engine что это. Картинка про zend engine что это. Фото zend engine что это

Заключение

В статье получилось много текста и совсем мало кода, но без экскурса в мир типов данных ZE 2 пытаться понять назначение вызовов тех или иных функций было бы достаточно сложно. В следующей части я приведу объяснение первых шагов, которые нужно выполнить, чтобы создать свой модуль расширения PHP, но перед этим затрону тему работы с zval`ами и управления выделением памятью.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *