что такое модуль php
PHP модуль — это просто
Недавно мы опубликовали визард для VisualStudio, с помощью которого можно создать экстеншн в пару кликов мыши. Теперь с помощью него мы напишем наши два первых расширения: «Привет, мир» и «вытащим иконку из exe».
Сразу прошу прощение, что очень сильно задержал статью, но жизненные обстоятельства вынудили это сделать, но они исключительно уважительные.
Итак, начнем.
1. Качаем «волшебника» для VS 2008.
По ссылке из темы VS wizard: PHP extension
Устанавливаем его, это произойдет автоматически.
2. Скачиваем необходимые для сборки файлы.
Нужны лишь исходники PHP и бинарники. Скачиваем 5.2.11 версию обоих файлов
Разархивируем php-5.2.11-Win32.zip в C:\PHPDEV\php-5.2.11-Win32 и php-5.2.11.tar.bz2 в C:\PHPDEV\php-5.2.11.
3. Запускаем VS, создаем новый проект.
И вводим его название. Пути настраивать не придется 😉
После этого видим главное окно студии, смотрим, что же там в файлах.
4. Создаем функции.
Как уже замечено, то скелет полностью создан, осталось лишь написать функции и прописать их.
В проекте есть тестовая функция, раскоментируем ее.
Для справки:
1) Заголовок функции должен быть в h файле. В виде PHP_FUNCTION(имя_функции).
2) Определение — в c файле.
3) Функция должна быть прописана в function_entry test_functions в c файле. В виде PHP_FE(имя_функции, NULL).
Как написать саму функцию, я расскажу позднее. А пока ограничимся этой:
6. Продвинутое создание функций.
Разберемся, как принимать значения из функций и передавать их.
Сложность заключается в том, что функция принимает и возвращает различные значения различных типов.
Рассмотрим пример, при котором принимается строка и целое и возвращается строка.
Как можно увидеть, здесь использованы следующие конструкции:
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, строка_формата, адреса_дляполучаемых_значений)
RETURN_*;
Рассмотрим 2 таблицы, в первой указаны принимаемые PHP-типы и соответствующие им форматы и типы C.
Во второй возвращаемые значения с соответствующими конструкциями.
Дабы не утруждать себя, я прилагаю фотографии таблиц из книги, которую я всем советую прочитать.
Еще раз смотрим на примеры выше и понимаем, как все просто.
Кстати хочу обратитьт внимание, что выделение памяти ведется через e-аналоги функций c(emalloc, efree, erealloc), это нужно для того, чтобы GC PHP сам смог «прибраться».
7. Полезный пример. Вытащим иконку из exe.
Конечно можно написать это и на PHP, но работы будет больше. А тут уже есть необходимые заголовки.
Напишем код на C(писал одепт bl4de):
В файле pe.h мы видим использование кусков кода из библиотек windows: они нам помогут, а прямое их подключение невозможно, мы ведь пишем кроссплатформенное расширение, не так ли? 😉
В pe.c же пишем код. Как и понятно, мы будем оборачивать функцию void _extract_ico(char *filename, char *filenameOut).
Модульное программирование на PHP или как написать маленький портал
* /mods/ — каталог для хранения модулей
* /img/ — картинки
* /include/ — каталог вспомогательных файлов
Это что нам сейчас пока надо. Применять блоки и скины мы пока не будем. В моем портале также были другие каталоги
* /blocks/ — Тоже своего рода модули, но не выводящие сами информацию, а возвращающие заполненную переменную.
* /js/ — каталог для Java скриптов
* /theme/ — каталог выбора тем или, грубо говоря, набор скинов для сайта.
* /files/ — файлы для скачивания
ну и другие каталоги.
Также создадим файл конфигурации config.php и положим его в каталог include.
Вот примерная схема работы index.php
Теперь создадим два файла mod1.php и mod2.php и положим их в каталог mods.
Поясню немного вот эту строку
В каждый модуль желательно включать такую проверку во избежании вызова файла модуля вне самого index.php. На примере моего портала до вызова модуля у меня идет подключение в базе данных, считывание некоторых глобальных переменных и без них, ни один модуль сам по себе работать не сможет. Так что лучше всего просто запретить вызов модуля напрямую. Вызов модулей в данном случае производится через строку в виде index.php?mod=имя модуля, но тут можно применить и систему ЧПУ. Тогда URL примет вид index.php/имя модуля/
Если взять за правило, что каждый модуль обязан сам вывести шапку и низ сайта, то вам уже предоставляется свобода по выбору что и как выводить.
К примеру, наши простые модули можно модифицировать в таком варианте.
Как делать в данном и конкретном случае решать Вам. Я же просто попытался направить тех, кто начинает писать на php, а может и тех, кто уже пишет, на определенный вариант или стиль программирования.
Модульность в php-проекте
При разработке php-проекта часто возникает потребность разделить его на небольшие части — модули. Слово «модули» довольно расплывчато и в разных случаях может трактоваться по своему, но в данном случае речь идёт только о том, что модуль — это некий код, который может представлять собой единую сущность.
Программно модуль может быть набором классов, функций и файлов. Он может содержать html-код, css-стили, js-скрипты, изображения и т.п. То есть не важно как в реальности реализован модуль, главное это то, что мы можем его воспринимать как единое целое.
Хорошим примером модульности может служить CodeIgniter (1-3 версий). В нём модуль представляет собой «обертку» над классами или функциям. То есть вместо того, чтобы писать сложный код подключения, инициализации и т.п., используется универсальный загрузчик, например так:
«Loader» берёт на себя задачу по подключению файла класса и его инициализацию (и даже поддержку как singleton). Дальнейшее использование сводится к обращению к методам класса encrypt в достаточно простом варианте:
В данном примере encrypt очень простой класс, состоящий из одного файла, но гораздо чаще модуль состоит из множества классов и файлов. В этом разрезе становится очевидно, что модуль должен представлять собой уже каталог, где и будут размещаться все файлы модуля.
Структура каталогов модулей этого варианта может быть такой:
Такой подход используется например в Fuel PHP Framework при подключени модулей packages.
Главный момент здесь в том, что файл bootstrap.php выполняет роль «буфера», через который происходит «адаптация» классов модуля к php-проекту.
Приведу небольшой абстрактный пример. Например нам нужно вывести слайдер на какой-то странице сайта. Сам слайдер требует предопределенную html-разметку. Также он содержит базовые css-стили и jQuery-плагин. Модуль должен уметь не просто сгенерировать готовый html-код, но и быть управляемым, например поддерживать все параметры jQuery-плагина через php-переменные, а также дополнительные опции, вроде своих css-классов.
Понятно, что такой модуль должен состоять из каких-то php-классов или функций, но также в модуле следует разместить и все «сопутствующие» файлы: css и js, например:
Файл bootstrap.php может содержать только загрузчик классов модуля. Поэтому работа с модулем может выглядеть так:
Данный вариант хорош тем, что используется единый подход к работе с модулем. Но при этом возникает проблема для модулей, которые не имеют единой «точки входа». Например модуль представляет собой просто набор не связанных классов. Примером может послужить база данных. В зависимости от типа базы (MySQL, sqlite, pdo), будут использованы различные классы.
Обычно такие модули выделяют в специализированные каталоги, вроде «core», «database», «classes» и т.п. То есть формально это теже самые модули, но которые работают по другим «правилам». Назвать это проблемой нельзя, хотя такой подход ломает «стройность» проекта.
Но на самом деле, проблема не только в этом. На сегодняшний день существует огромное количество готовых php-проектов, решающих практически все задачи. То есть вместо того, чтобы «изобретать велосипед», можно воспользоваться уже готовой разработкой.
Чтобы оценить объем возможностей приведу как пример The PHP Package Repository, где размещены описания на почти 220 тысяч (. ) проектов. Этот репозиторий используется менеджером Composer, который довольно часто используется в php-фреймворках.
Таким образом, если мы хотим воспользоваться чужой разработкой, то возникает довольно серьезная проблема встраивания чужого проекта в свой модуль. Дело в том, что php-разработчики используют самые разные подходы из-за чего с каждым проектом приходится разбираться индивидуально.
Например, кто-то использует классы, кто-то только функции. Кто-то использует namespace, кто-то нет. Кто-то придерживается PSR-4, кто-то нет.
В итоге структура модуля получит примерно такой вид:
Каталог distr содержит дистрибутивы сторонних разработок, которые используются в данном модуле.
У такого подхода есть два серьезных минуса. Первый — если несколько модулей используют один и тот же дистрибутив, то его придётся дублировать или создавать опасную связь между модулями. Второй — если дистрибутив имеет новую версию, то в своих модулях необходимо как-то организовать их обновление. При большом количестве модулей это может оказаться проблемой.
Что получается в итоге?
Модули в php-проекте должны располагаться в отдельном каталоге и соответствовать стандарту PSR-4, что позволит упростить их использование (через автозагрузчик).
Вы можете скачать полный набор файлов. Классы простые для демонстрации, а для Composer’а я «прикрутил» парсер Parsedown. Вначале он вызывается сам по себе, а после из класса модуля.
Разработка модульного движка на PHP
Есть много разных движков на PHP, от достаточно простых, до очень тяжеловесных и громоздих, включающих практически все.
Но на мой взгляд лучший движек — расширяемый. И не такой, что к куче своих возможностей предлагает прикрутить еще и некоторые, а такой, в которого своих возможностей как-бы и нет, а есть только подлкючаемые на выбор.
Меньше полугода назад я задался вопросом создания такого движка. Первым, что нужно было написать, являлся, загрузчик. Эдакий псевдо-модуль (об этом далее), который загружает иные модули.
Давайте определимся со структурой: допустим у нас есть папка mod в которой хранятся модули.
Например /mod/staticpages/*. По стандарту загрузчика модуль должен состоять из конфигурационного файла, главного класса модуля, опционально подключаемых библиотек.
Опять же, например, есть модуль staticpages (который отвечает за обработку статических страниц), он будет состоять из файлов manifest.ini и staticpages.php.
Первый — конфигурация модуля, а второй — файл с главным классом модуля.
Для начала пусть конфиг-файл имеет такую структуру:
При обработке этого модуля открывается его манифест (будем использовать этот термин и далее), из него взымаются необходимые данные, выполняются необходимые операции.
Добавим в манифест еще один отдел:
Теперь при обработке этого модуля мы можем загрузить (точнее включить) файл с его классом и создать его экземпляр.
Сейчас алгоритм будет примерно таким:
1. Читаем все папки /mod/.
2. Если там есть manifest.ini, продолжаем.
3. Получаем значение mainclass, включаем этот файл.
4. Создаем экземпляр класса с именем devname. Именно для этого и нужно это поле.
В итоге мы получаем обьект класса StaticPages с именем $staticpages. Обьект будет являтся глобальным, для удобного взаимодействия иных модулей с ним.
Теперь в дальнейшем коде мы можем просто и быстро использовать возможности этого модуля.
Но теперь мы упираемся еще в одну проблему:
Допустим у нас есть такой запрос: «?ins=staticpage&page=info«, который, по идее, должен показать статическую страницу с именем info. Но как об этом узнает модуль Static Pages, который и должен отвечать за это?
Конечно, можно разместить обработчик в конструктор класса, типа, если ins = staticpage, но ведь на тот момент мы точно не знаем — загрузились ли иные модули, которые нужны для нормальной работы Static Pages, и вообще — стоит ли это делать?
Значит нам нужно добавить в манифест еще один отдел:
Уже после загрузки всех модулей мы запускаем второй этап: запуск методов.
В этой секции, в параметре run указан метод класа, который нужно запустить на этой стадии. Отлично, на этом этапе уже точно будут загружены все модули, и они могут спокойно взаимодействовать друг с другом.
Теперь еще одна ситуация — допустим модуль pex (который отвечает за права доступа) в этой секции только начинает загружать права доступа для текущего пользователя, а нам ну по зарез нужно ограничить показ статической страницы, а вмешиватся в код стороннего модуля совсем не хорошо.
Придется ввести еще одно понятие, а так же параметр в манифест, а именно: очередь и require.
Очередь, в нашем случае, очередь модулей (что очевидно) для выполнения некоторого действия.
А сейчас — модифицируем отдел [Run] модуля Static Pages:
При попытке выполнения метода template() для нашего модуля Static Pages загрузчик наткнется на требование сначала выполнить аналогичное действие для модуля pex, подвинет наш модуль подальше в очереди и продолжит аналогичную работу для остальных модулей.
Теперь мы можем спокойно орудовать данными, получеными pex’ом уже после его Run этапа.
Еще одной важная вещь: проверка жестко установленных модулей для работы нашего Static Pages, а так же их версий. Добавим такой отдел:
Видно, что наш модуль требует загрузчик версии не ниже 0.2 (по этому он и является «псевдо-модулем»: он имеет и манифест, и версию, и к нему можно получить доступ как к любому иному модулю, но он «жестко впаян» в систему), так же модуль для работы с БД MySQL, еще модуль lang (который будет отвечать за кодировку, формат даты и времени, и тд.), а так же модуль для постраничной навигации.
Но наши «очереди» не очень то хорошо сказываются на производительности, да и разработчикам будет утомительно указывать все модули, которые тем или иным боком зависят от него. Поэтому найболее разумным будет сделать «пару уровней» выполнения модульных методов.
Это будут новые отделы в манифесте: сначала будут выполнятся методы с [Prev], уже известный [Run], [After] и [Finish].
Для примера возьмем часть манифеста модуля pex:
Сначала, на этапе [Prev] он получает права группы, к которой принадлежит пользователь, но только после того, как модуль auth получит данные о самом пользователе. Потом он выполнит метод template(), в котором, например, проверит, можно ли текущему пользователю просматривать сайт (но и даст иным модулям сделать свою работу).
А уже после — проверит шаблон на так называемые nodesContainers, участки шаблона, для доступа к которым нужные некоторые права (ведь в пред. этапах разные модули могли добавить такие участки и они бы остались не обоработанные).
Так же не забываем про библиотеки, которые могут понадобится некоторым модулям, добавим еще один отдел:
Загрузчик проверит этот отдел, и при необходимости включит все файлы с папок.
В конце концов в index.php у нас будет примерно следующее:
Что же мы имеем в итоге?
На таком принципе можно построить сайт любой сложности используя уже существующие модули плюс написанные специально. Любой модуль может использовать любой иной, а прямое выполнение кода дает чуть ли не 100% гибкость. Нужен ajax? Создаем модуль, который подождет, пока все будет выполнено и подготовлено, а потом в последний момент отменит показ шаблона и покажет только то, что нужно.
Конечно же в такого способа есть и свои недостатки: из за постояной постройки очередей понижается быстродействие, модули могут лезть совсем не туда, куда нужно, и рядовой пользователь, который столкнется со всем этим, попробует поключить пару модулей, верно бросит все это дело.
Но с начала работы я совсем не располагаю на широкую аудиторию, и все изобратенное используется только для себя. И пока «оно» меня вполне устраивает.
Действующую модель этого способа можно посмотреть на моей страничке (ссылка есть в профиле), а исходные коды — в репрозитории GitHub’а.
Спасибо за внимание, если тема заинтересует кого-то, то напишу топик о переходе от теории к практике.
Как использовать оператор по модулю в PHP
Что делает оператор по модулю?
Знак значения, возвращаемого операцией по модулю, определяется знаком дивиденда. При делении результатом после деления двух отрицательных чисел будет положительное число. Однако это не относится к оператору по модулю. Знак делителя не влияет на конечное значение.
С плавающей точкой по модулю
Здесь значение i всегда будет целым числом.
Следует помнить, что арифметика с плавающей точкой не всегда точна из-за ограничений двоичного или десятичного представления дробей. Например, 1/3 не может быть точно представлена в десятичной форме. Вы можете продолжать писать 0.33333 …. но в какой-то момент вам придется остановиться. Вы будете приближаться к исходному значению с каждым дополнительным 3 в десятичном представлении, но значение все равно не будет точно 1/3.
Такая неточность вызывает проблемы с функцией fmod() : результаты не совсем надежны.
Вот несколько примеров функции fmod() :
Второе значение не является точным, потому что 0,2 прекрасно делится на 18,8. Это всего лишь недостаток вычислений в формате с плавающей запятой, который используется компьютерами.
Использование оператора по модулю
В этом уроке мы ограничимся целым числом по модулю, потому что оно гораздо более распространено и имеет множество приложений.
Проверка, является ли число кратным некоторому другому числу
Результат оператора по модулю равен нулю, если первое число идеально делится на второе число. Это можно использовать для проверки, является ли одно число кратным другому в данной паре номеров. Вероятно, наиболее распространенным использованием этого свойства оператора по модулю является проверка, является ли число четным или нечетным. Вот пример:
В приведенном выше примере вы можете получить список цветов от пользователя и попросить его предоставить только четное количество цветов.
В приведенном ниже примере используются аналогичные рассуждения для создания групп по 5 человек в каждой. В реальной жизни вам придется использовать дополнительный код для группировки студентов, но основная идея проверки, является ли общее количество студентов кратным 5, не меняется.
Изменение чисел, чтобы быть кратным некоторому другому числу
В приведенном выше разделе мы использовали оператор по модулю, чтобы информировать пользователей только о том, что вводить значения нужно только в определенных кратных числах. Если это невозможно, мы также можем заставить входное значение быть равным или кратным 5 или некоторому другому числу.
В нашем предыдущем примере с 28 учениками мы могли просто оставить 3 учеников и сгруппировать других учеников вместе.
Установить ограничение на вход
Как я упоминал в начале поста, в случае положительных чисел оператор по модулю вернет число от 0 до N — 1, где N — делитель. Это означает, что вы можете поставить заглушку на любой вход и выполнять некоторые операции многократно и последовательно. Вот пример:
Выполняйте какое-либо задание каждый раз в цикле
При обходе цикла мы можем проверять значение переменной, которое увеличивается с каждым проходом цикла, и выполнять определенную задачу после каждой n-й итерации. Один из примеров практического использования, который приходит на ум, — это информирование пользователей о длительном процессе. Допустим, вы вносите изменения в 1000 различных изображений с помощью PHP. Если изменения являются значительными, этот процесс займет некоторое время для обновления всех изображений.
В таких случаях у пользователей не будет возможности узнать, зависла ли программа или что-то действительно происходит. Что вы можете сделать, это сообщить о прогрессе пользователям после редактирования каждого 10-го изображения.