Php extension shared lib

Пишем расширение под PHP (7.0.7) без знаний о С/C++ и как это вообще работает

Можно ли написать свой модуль (расширение) к PHP без особых знаний, требующих большого времени изучения теории? Если умеешь программировать на самом PHP, то написать простейший код на С не составит особого труда, тем более, что PHP позволяет генерировать каркас под разрабатываемое расширение, в рамках которого потом пишешь код. Есть еще набирающий популярность зефир на хабре для этого вопроса. Данная публикация для тех, кто решил покопаться в исходниках PHP, немного посмотреть его внутренности, преследуя цель лишь поверхностного исследования. В данный момент я тот же самый исследовать без необходимых знаний. На собеседованиях по PHP часто просят написать код подсчета факториала. Вот такую функцию мы и напишем сейчас на С, которую потом можно вызывать из кода PHP. Я буду описывать действия, которые я сам делал и при этом ничего не знаю изначально по этой части. В интернете можно найти много статей по этому вопросу, большинство из них описывает информацию с использованием zval «старого» формата, но я не думаю, что будет хуже если и я еще добавлю от себя.

В PHP есть уже готовый инструмент ./ext_skel (находится в папке ext), который генерирует будущий шаблон (каркас) для расширения. Я не буду описывать все, что им генерируется и зачем (сам особо в этом ничего еще не понимаю и не знаю), а просто распишу минимальные правки, которую решат нашу задачу. Весь процесс происходит в CentOS 7.

Читайте также:  Масштабирование фона

Создаем каркас для будущего расширения mathstat, которое будет содержать функцию factorial().

 [root@localhost ext]# ./ext_skel --extname=mathstat Смотрим, что содержится в папке mathstat. [root@localhost mathstat]# ls config.m4 config.w32 CREDITS EXPERIMENTAL mathstat.c mathstat.php php_mathstat.h tests 

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

To use your new extension, you will have to execute the following steps: 1. $ cd .. 2. $ vi ext/mathstat/config.m4 3. $ ./buildconf 4. $ ./configure --[with|enable]-mathstat 5. $ make 6. $ ./sapi/cli/php -f ext/mathstat/mathstat.php 7. $ vi ext/mathstat/mathstat.c 8. $ make 

В PHP7 файла buildconf после генерации у меня нет (наверное это остатки ранних версий PHP), но я знаю, что сейчас компиляция расширений начинается с команды phpize. Она “создает” кучу файлов, среди которых есть необходимый ./configure. Напомню, что пользовательский вариант компиляции расширения состоит в последовательном выполнении следующих команд.

Phpize -> ./configure -> make -> make test -> make install 

Если сразу сделать эту последовательность команд, то make install по не ясным причинам будет ломаться и выдавать ошибку на копирование. Если кто в курсе, отпишите, в комментариях, почему так.

[root@localhost eugene]# make install Installing shared extensions: /usr/local/lib/php/extensions/no-debug-non-zts-20151012/ cp: cannot stat 'modules/*': No such file or directory make: *** [install-modules] Error 1 

Phpize создает файлы на основе описания config.m4. Это, как я понял, своеобразный декларативный способ описания того, каким будет расширение, будет ли оно подтягивать внешние исходники или нет и т.д… Поэтому просмотрев другие расширения PHP в исходниках, я просто решил его максимально упростить, чтобы минимизировать ошибки компиляций с чистого листа. Действую по принципу — ничего не хочу, «все галочки снимаю».

Открываем этот файл (config.m4) и оставляем только этот текст. Опция “—enable-mathstat” говорит о том, что это просто расширение без внешних исходников (библиотек) и который можно либо включить, либо выключить. (dnl означает комментирование строки)

dnl $Id$ PHP_ARG_ENABLE(mathstat, whether to enable mathstat support, [ --enable-mathstat Enable mathstat support]) if test "$PHP_MATHSTAT" != "no"; then PHP_NEW_EXTENSION(mathstat, mathstat.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) fi 

Перезапускаем команду phpize.

[root@localhost mathstat]# phpize Configuring for: PHP Api Version: 20151012 Zend Module Api No: 20151012 Zend Extension Api No: 320151012 [root@localhost mathstat]# ls acinclude.m4 config.guess configure EXPERIMENTAL mathstat.c php_mathstat.h aclocal.m4 config.h.in configure.in install-sh mathstat.php run-tests.php autom4te.cache config.m4 config.w32 ltmain.sh missing tests build config.sub CREDITS Makefile.global mkinstalldirs 

Далее, делаем знакомые команды:

make test — запустит один изначально созданный тест. Про эти тесты PHP я как то писал уже вкратце.

[root@localhost mathstat]# make install Installing shared extensions: /usr/local/lib/php/extensions/no-debug-non-zts-20151012/ 

В этот раз “make install” проходит, далее пробуем прописывать расширение в php.ini.

Определяем, где находится php.ini.

[root@localhost mathstat]# php --ini Configuration File (php.ini) Path: /usr/local/lib Loaded Configuration File: /usr/local/lib/php.ini Scan for additional .ini files in: (none) Additional .ini files parsed: (none) viim /usr/local/lib/php.ini extension=mathstat.so ;zend_extension = /usr/local/lib/php/extensions/no-debug-non-zts-20151012/xdebug.so [root@localhost mathstat]# systemctl restart php-fpm [root@localhost mathstat]# php -m | grep -i math mathstat 

Команда php -m (просматривает все установленные модули) говорит, что вроде бы все нормально, расширение mathstat подгрузилось.

Запускаем в текущей директории тестовый файл mathstat.php

[root@localhost mathstat]# php mathstat.php Functions available in the test extension: confirm_mathstat_compiled Congratulations! You have successfully modified ext/mathstat/config.m4. Module mathstat is now compiled into PHP. [root@localhost mathstat]# 

Отлично, что — то уже работает.

2. Начинаем реализовывать функцию factorial().

Редактируем файл mathstat.c для добавления функции factorial().

Для этого нужно добавить функцию в “список” mathstat и сделать на неё заглушку, через макрос. Делаю все по аналогии как в других расширениях.

const zend_function_entry mathstat_functions[] = < PHP_FE(confirm_mathstat_compiled, NULL) /* For testing, remove later. */ PHP_FE(factorial, NULL) PHP_FE_END /* Must be the last line in mathstat_functions[] */ >; 

Реализация функции заглушки. Делается в обертке макроса. Как он работает в итоге, пока не ясно, оставляю изучение себе на будущее. Просто делаю в аналогичном формате.

В данной случае под каждый тип возвращаемых данных, свой вариант RETURN_. Поиск в интернете покажет все возможные варианты. У нас просто целое значение. Тут вроде все просто.

Далее повторяем make clean && make && make install

[root@localhost mathstat]# make clean find . -name \*.gcno -o -name \*.gcda | xargs rm -f find . -name \*.lo -o -name \*.o | xargs rm -f find . -name \*.la -o -name \*.a | xargs rm -f find . -name \*.so | xargs rm -f find . -name .libs -a -type d|xargs rm -rf rm -f libphp.la modules/* libs/* Build complete. Don't forget to run 'make test'. [root@localhost mathstat]# make install Installing shared extensions: /usr/local/lib/php/extensions/no-debug-non-zts-20151012/ [root@localhost mathstat]# systemctl restart php-fpm [root@localhost mathstat]# systemctl status php-fpm ● php-fpm.service - The PHP FastCGI Process Manager Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; enabled; vendor preset: disabled) Active: active (running) since Thu 2016-06-16 01:12:22 EDT; 5s ago Main PID: 32625 (php-fpm) CGroup: /system.slice/php-fpm.service ├─32625 php-fpm: master process (/usr/local/etc/php-fpm.conf) ├─32626 php-fpm: pool www └─32627 php-fpm: pool www Jun 16 01:12:22 localhost.localdomain systemd[1]: Started The PHP FastCGI Process Manager. Jun 16 01:12:22 localhost.localdomain systemd[1]: Starting The PHP FastCGI Process Manager. 

Перезапуск php-fpm не показал, что что-то сломали и поэтому идем дальше и тестим наличие функции в расширении. Делаю на всякий случай, даже если компиляция прошла.

[root@localhost mathstat]# php mathstat.php Functions available in the test extension: confirm_mathstat_compiled factorial Congratulations! You have successfully modified ext/mathstat/config.m4. Module mathstat is now compiled into PHP. 

Наименование функции появилось и более того, теперь мы можем её уже вызывать из кода PHP.

[root@localhost mathstat]# php -a Interactive mode enabled php > echo factorial(1); 1000 php > 

Видно, что функция вызвалась и вернула заранее указанное значение 1000.

Научим функцию принимать аргумент и его же отдавать, для этого необходимо сделать описание аргумента функции. Смотрим аналогии в других расширениях PHP (я смотрел bcmath). Куча макросов, но формат понятен, в принципе.

ZEND_BEGIN_ARG_INFO(arginfo_factorial, 0) ZEND_ARG_INFO(0, number) ZEND_END_ARG_INFO() 

И добавляем его использование в функции. Если оставлять NULL, то умолчанию считается, что тип аргумента типа int.

Немного исправляем тело функции.

PHP_FUNCTION(factorial) < int argc = ZEND_NUM_ARGS(); long number = 0; if (zend_parse_parameters(argc, "l", &number) == FAILURE) < RETURN_LONG(0); >RETURN_LONG(number); > 

Здесь используется zend_parse_parameters, который проверяет переданные аргументы на тип используя формат в кавычках («»), затем по адресу задает принятое значение. Детали можно легко найти в интернете. Для задачи реализации факториала больших знаний пока не нужно.

Проверяем после перекомпиляции (make clean && make && make install).

[root@localhost mathstat]# php -r "echo factorial('80');"; 80[root@localhost mathstat]# php -r "echo factorial(80);"; 80[root@localhost mathstat]# 

Если передадим строку в аргументе, получим ошибку. Пока не ясно, как на самом деле все это работает до конца, но требуемая задача сделана.

[root@localhost mathstat]# php -r "echo factorial('aaaa');"; PHP Warning: factorial() expects parameter 1 to be integer, string given in Command line code on line 1 PHP Stack trace: PHP 1. () Command line code:0 PHP 2. factorial() Command line code:1 Warning: factorial() expects parameter 1 to be integer, string given in Command line code on line 1 Call Stack: 0.2040 349464 1. () Command line code:0 0.2040 349464 2. factorial() Command line code:1 

Так как тело функции вроде бы отрабатывает, реализуем теперь сам алгоритм расчета факториала. Как Вы знаете, алгоритм основан на рекурсивном вызове, сделаем тоже самое. Прописываем тело функции calculate() в этом же файле mathstat.c с последующим его вызовом.

static long calculate(long number) < if(number == 0) < return 1; >else < return number * calculate(number - 1); >> PHP_FUNCTION(factorial) < int argc = ZEND_NUM_ARGS(); long number = 0; if (zend_parse_parameters(argc, "l", &number) == FAILURE) < RETURN_LONG(0); >number = calculate(number); RETURN_LONG(number); > 

Компилируем, перезапускаем, проверяем.

[root@localhost mathstat]# php -a Interactive mode enabled php > echo factorial(1); 1 php > echo factorial(2); 2 php > echo factorial(3); 6 php > echo factorial(4); 24 php > echo factorial(5); 120 

Удивительно, но это работает. Получается, чтобы реализовать данную функцию без базовых знаний как там все устроенно в PHP, да и сам язык С/C++ не смотрелся с университета, мне понадобилось не более 3-4 часов. Весь процесс написания кода напоминает работу в каком то фреймворке для PHP. Все что нужно, это изучить архитектуру фреймворка и его API, а дальше работать в рамках его каркаса, тоже самое и здесь.

Особо большого кода по описанному варианту нет, но оставлю ссылку на github

Источник

php custom c extension with external shared lib

Solution:

You’ve included the library, but looks you’re missing the actual link option. I haven’t done this in the longest time, but I think something like the following should work.

EXTERNAL_LIB="blahLibName" LIB_LINK_CMD="-L/usr/local/lib -l$EXTERNAL_LIB" PHP_ADD_LIBRARY_WITH_PATH(externallib, lib64, PROJECTNAME_SHARED_LIBADD) PHP_EVAL_LIBLINE($LIB_LINK_CMD, PROJECTNAME_SHARED_LIBADD) 

The syntax here might not be right at all, but you definitely need the PHP_EVAL_LIBLINE.

Share solution ↓

Additional Information:

Didn’t find the answer?

Our community is visited by hundreds of web development professionals every day. Ask your question and get a quick answer for free.

Similar questions

Find the answer in similar questions on our website.

Write quick answer

Do you know the answer to this question? Write a quick response to it. With your help, we will make our community stronger.

About the technologies asked in this question

PHP

PHP (from the English Hypertext Preprocessor — hypertext preprocessor) is a scripting programming language for developing web applications. Supported by most hosting providers, it is one of the most popular tools for creating dynamic websites. The PHP scripting language has gained wide popularity due to its processing speed, simplicity, cross-platform, functionality and distribution of source codes under its own license.
https://www.php.net/

Welcome to programmierfrage.com

programmierfrage.com is a question and answer site for professional web developers, programming enthusiasts and website builders. Site created and operated by the community. Together with you, we create a free library of detailed answers to any question on programming, web development, website creation and website administration.

Get answers to specific questions

Ask about the real problem you are facing. Describe in detail what you are doing and what you want to achieve.

Help Others Solve Their Issues

Our goal is to create a strong community in which everyone will support each other. If you find a question and know the answer to it, help others with your knowledge.

Источник

Оцените статью