Oc-windows.ru

IT Новости из мира ПК
2 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

List php extension

Пишем PHP extension

А давайте сегодня взглянем на PHP немного с другой точки зрения, и напишем к нему расширение. Так как на эту тему уже были публикации на Хабре (здесь и здесь), то не будем углубляться в причины того, для чего это может оказаться полезным и для чего может быть использовано на практике. Эта статья расскажет, как собирать простые расширения под Windows с использованием Visual C++ и под Debian с использованием GCC. Также я постараюсь немного осветить работу с PHP-массивами внутри расширений и провести сравнение производительности алгоритма, написанного на native PHP и использующего код, написанный на C.

Компиляция под Win32

Итак, начнем с Windows. Как известно, разработчики PHP используют Visual C++ 9 или Visual Studio 2008 для компиляции своего детища под Windows. Поэтому мы будем использовать Visual Studio 2008, бесплатная Express версия тоже подойдет, как впрочем, наверное, и более поздние и ранние версии студии.

Что нам потребуется:

  • Скомпилированные бинарники PHP 5.3, которые можно взять здесь,
  • Исходники PHP 5.3, которые можно скачать с сайта или вытянуть из общедоступного SVN,
  • Желание поэкспериментировать и немного терпения.

Для начала создадим проект типа Win32 Console Application и выберем DLL в Application type. Теперь нам придется настроить все зависимости и пути для линковщика:

  • Щелкните правой кнопкой мыши в Solution Explorer’e и выберите Properties > C/C++ > General > Additional Include Directories. Сюда мы добавим директории, в которых лежат распакованные исходники и заголовочные файлы PHP. Конкретно нужны будут:

    Теперь добавим preprocessor definitions, которые нужны для корректного выбора платформы и компиляции модуля. Выбираем Configuration Properties > C/C++ > Preprocessor > Preprocessor Definitions, и добавляем туда следующее:

    Затем укажем линковщику где можно найти необходимые библиотеки. Выбираем Configuration Properties > Linker > General > Additional Library Directories. Там выбираем директорию div из бинарников PHP. Должно получиться что-то такое: «D:Program Filesphp-5.3.6-Win32-VC9-x86dev».

    Теперь укажем конкретную либу для линковщика. Идем в Configuration Properties > Linker > Input > Additional Dependencies, и вписываем туда php5ts.lib, которая находится в той самой dev директории, которую мы указали в предыдущем шаге.

    Для избегания некоторых проблем компиляции, добавим директиву /FORCE:MULTIPLE в Configuration Properties > Linker > Command Line. Подробнее о ней можно прочитать на сайте MSDN.

  • И, наконец, можно указать, куда сохранять скомпилированную dll. Для этого перейдем в Configuration Properties > Linker > General > Output Filename и укажем там путь к папке ext установленного PHP. Должно получиться что-нибудь такое: «D:Program Filesphp-5.3.6-Win32-VC9-x86ext$(ProjectName).dll».

Найдем в проекте файл stdafx.h и заменим его содержимое на следующее:

Если вы попытаетесь скомпилировать проект на данном этапе, вы получите ошибку, говорящую о том, что отсутствует mainconfig.w32.h. Его можно получить либо запустив скрипт mainconfigure.bat, либо можно выдернуть его из исходников, например версии PHP 5.2. При этом не забываем отредактировать в этом файле все пути и раскомментировать директиву «#define HAVE_SOCKLEN_T». Теперь проект должен скомпилироваться без ошибок.

Теперь давайте напишем hello world, добавим в наш cpp файл следующее:

Теперь подключим этот модуль в PHP и попробуем запустить что-нибудь такое:
На что мы должны получить ответ «hello habr».

Компиляция под *nix

В *nix’ах все оказалось как всегда проще. Я покажу на примере Debian, думаю, что под другими системами процесс не будет отличаться.
Нам потребуется:

  • Иметь установленный PHP на машине,
  • Иметь установленный PHP-dev. Для этого нужно выполнить всего одну команду:

Давайте создадим где-нибудь директорию для нашего расширения. Ну например /test. Там создадим два пустых файла:

Первый нужен для магической компиляции расширения, а во втором будет его исходный код. В config.m4 напишем следующее:

Внутри test.c добавьте

И после этой сроки скопируйте содержимое cpp-файла из Windows-версии.
Теперь идем в консоль и:

На этом все. Теперь можно открыть php.ini, добавить там свое расширение:

И проверить его работоспособность командой

Обработка аргументов и возвращаемые значения

Для начала посмотрим, как можно принимать аргументы:

Третий параметр указывает ожидаемый тип (здесь можно просмотреть все варианты), в данном случае это char* или int. Также по ссылке можно найти варианты комбинирования типов и указания количества аргументов. Все следующие параметры являются переменными, в которые будут записаны переданные значения. При передаче строки передается сама строка и ее длина.
Если количество аргументов, переданных в вашу функцию, не совпадает, будет выброшен E_WARNING, при этом вы можете возвратить какое-либо значение, например, сообщение об ошибке.

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

Для добавления значений в массив необходимо использовать функции, зависящие от того, какой индекс и значение добавляется в массив. Например:

Полный список функций можно найти здесь

Если кого-то заинтересует, я могу в следующей статье рассмотреть пример работы с объектами (классический пример расширения на объектах — mysqli). Тут есть очень хорошая статья на эту тему.

Производительность

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

У меня получилась такая реализация, сильно не пинайте за код, я все-таки больше пишу на PHP, чем на C:

Этот код выдает следующий результат:

А теперь давайте сравним скорость выполнения этого кода и аналогичного на native PHP:

Сравнивать я буду время выполнения обоих решений с помощью функции microtime. Возьмем строку в 100 символов, строку в 5000 символов, и строку в 69000 символов (я взял книгу A Message from the Sea, написанную Чарльзом Диккенсом, надеюсь, что он мне это простит), и для каждого варианта прогоним оба решения по несколько тысяч раз. Результаты приведены в таблице ниже. Тестирование проводилось на моем не самом сильном домашнем ноутбуке и VDS с Debian на борту, и да, я отчетливо понимаю, что результаты могут зависеть от конфигурации, от версии операционной системы, PHP, атмосферного давления и направления ветра, но я хотел показать лишь примерные цифры.
Полный код тестового скрипта можно скачать здесь. Исходники и бинарники самих расширений можно скачать здесь (win) и здесь (nix).

Читать еще:  If not null php
Кол-во итерацийPHP code / Win32PHP code / DebianPHP extension / Win32PHP extension / DebianWin32 выигрышDebian выигрыш
1. Строка 100 символов100000084.7566 сек72.5617 сек8.4750 сек4.4175 секв 10 разв 16.43 раз
2. Строка 5000 символов1000039.1012 сек31.7541 сек0.5001 сек0.134 секв 78.19 разв 236.98 раз
3. Строка 69000 символов100052.3378 сек44.0647 сек0.4875 сек0.0763 секв 107.36 разв 577.51 раз

Выводы

Если судить о производительности модуля по сравнению с интерпретируемым кодом, то мы видим, что ощутимые результаты можно получить на больших объемах данных и на малых количествах итераций. То есть, для часто использующихся, однако, не очень ресурсоемких алгоритмов не имеет смысла вынесение их в компилируемый код. Но для алгоритмов, работающих с большими объемами данных, это может иметь практический смысл. Также, опираясь на мои измерения, можно заметить, что результаты работы PHP-кода сравнимы на разных системах (напомню, что это были две разные машины), а вот результаты работы расширения очень сильно отличаются. Из этого лично я делаю вывод, что существуют какие-то особенности компиляции, которые мне не известны. Впрочем, я сильно сомневаюсь, что кто-то использует Windows-сервера для PHP-проектов. Хотя я также очень сомневаюсь, что кто-то прямо сейчас побежит переписывать что-то на С, эта статья все-таки больше just for fun, чем руководство к действию. Просто я хотел показать, что написать PHP extension очень просто, и иногда может быть очень полезно.

How do I enable PHP extensions and loaders?

The standard term for a PHP configuration file is php.ini. However, DreamHost uses the term «phprc» file instead. This acts the same way – only the name is different. In this article, only the term phprc is used.

Loaders and Extensions

Loaders

PHP Loaders are extensions to PHP which load special types of PHP files that have been encoded with an encoding product such as ionCube Loader.

To activate the loader, you must add a line to your phprc file. Below are links to pages that provide further instructions on installing and using the various PHP loaders:

Extensions

PHP Extensions are libraries of pre-built code. They allow you to use specific functions already created to simplify your code. The following is a current list of PHP Extensions:

Some extensions are already activated by default (such as Phar). Others are not, but can be activated by adding a line to your phprc file.

Checking if an Extension is already active

Before adding an extension, make sure it’s not already activated by default. For example, the Phar extension is already activate with any version of PHP at DreamHost. If you then add the phar.so extension to your phprc file, your site will thrown an error, so it’s important to only add an extension if needed.

You can view any currently activate extension by adding a phpinfo.php file to your site.

If an extension is already active, you’ll see its corresponding block when visiting the phpinfo.php file. For example, this shows that the Phar extension is active.

Adding an extension

Step 1 — Create a phprc file

View the following articles for instructions on how to create a phprc file.

If the file already exists, view the following articles for instructions on how to update it (depending on if you’re using an FTP client or SSH):

Step 2 — Load the extension

To load an extension, add the following line your phprc file (make sure to change ‘extension_name’ to the actual extension.):

extension = extension_name.so

For example, this is what you would add to enable the intl extension.

Below is a list of common extensions you may need to add to your phprc file.

ExtensionLine in phprc file
curlextension = curl.so
fileinfoAlready enabled by default
imagickextension = imagick.so
intlextension = intl.so
mysqliextension = mysqli.so
pharAlready enabled by default
zipextension = zip.so

Step 3 — Kill off all running php processes

Make sure to kill running php processes to ensure your new settings take effect.

Step 4 — Confirming your changes

After you’ve edited the file and killed off all PHP processes, check to confirm the extension has been enabled. You can do this by creating a phpinfo.php file. You should see the following:

If you do not see your changes have updated, try killing off your PHP processes again:

Compiling PHP extensions

Some extensions are not complied with PHP. In these instances, you must download the extension and compile it manually.

Visit the following links for a list of available extensions:

If any installation requires sudo privileges, you must upgrade to a Dedicated server.

If any installation requires you to run ‘phpize’, just specify the full path to your desired version of PHP. For example, for PHP 7.4 run the following:

You can use different versions of PHP if needed. For example.

hash.so

Starting with PHP 7.2.11, hash.so is built into PHP. If you’re using this version or higher, you should no longer call it from your phprc file.

mcrypt.so

Support for mcrypt has been removed starting with PHP 7.2.0. Any scripts that depend on mcrypt will need to be updated to use OpenSSL or Sodium instead.

list — Присваивает переменным из списка значения подобно массиву

(PHP 4, PHP 5, PHP 7)

list — Присваивает переменным из списка значения подобно массиву

Описание

Подобно array() , это не функция, а языковая конструкция. list() используется для того, чтобы присвоить списку переменных значения за одну операцию.

Читать еще:  Php encode url

Список параметров

Возвращаемые значения

Возвращает присвоенный массив.

Примеры

Пример #1 Примеры использования list()

= array( ‘кофе’ , ‘коричневый’ , ‘кофеин’ );

// Составить список всех переменных
list( $drink , $color , $power ) = $info ;
echo » $drink — $color , а $power делает его особенным.n» ;

// Составить список только некоторых из них
list( $drink , , $power ) = $info ;
echo «В $drink есть $power .n» ;

// Или только третья
list( , , $power ) = $info ;
echo «Мне нужен $power !n» ;

// list() не работает со строками
list( $bar ) = «abcde» ;
var_dump ( $bar ); // NULL
?>

Пример #2 Пример использования list()

Имя работникаЗарплата

= $pdo -> query ( «SELECT id, name, salary FROM employees» );
while (list( $id , $name , $salary ) = $result -> fetch ( PDO :: FETCH_NUM )) <
echo »

n» .
»

$id «> $name

n» .
»

$salary

n» .
«

n» ;
>

Пример #3 Использование list() с индексами массивов

list( $a , list( $b , $c )) = array( 1 , array( 2 , 3 ));

var_dump ( $a , $b , $c );

Пример #4 Использование list() с индексами массива

= array( ‘кофе’ , ‘коричневый’ , ‘кофеин’ );

list( $a [ 0 ], $a [ 1 ], $a [ 2 ]) = $info ;

Выведет (сравните порядок исходных элементов с порядком, в в котором они были перезаписаны list() ):

Примечания

list() присваивает значения начиная с крайнего правого параметра. Если вы используете простые переменные, можете не беспокоиться об этом. Но если вы используете индексные массивы, вы можете ожидать, что в результате выполнения функции list() вы получите тот же порядок элементов, что и в исходном массиве: слева направо; однако это не так. Они будут присвоены в обратном порядке.

Изменение массива во время выполнения функции list() (например, использование list($a, $b) = $b) приводит к непредсказуемому поведению.

list() работает только с массивами, индексами которых являются числа и нумерация которых начинается с 0.

Смотрите также

  • each() — Возвращает текущую пару ключ/значение из массива и смещает его указатель
  • array() — Создает массив
  • extract() — Импортирует переменные из массива в текущую таблицу символов

PHP Internals Book

A look into a PHP extension and extension skeleton

A look into a PHP extension and extension skeleton¶

Here we detail what a PHP extension look like, and how to generate a skeleton using some tools. That will allow us to use a skeleton code and hack into it, instead of creating every needed piece by hand from scratch.

We’ll also detail how you could/should organize your extension files, how the engine loads them, and basically everything you need to know about a PHP extension.

How the engine loads extensions¶

You remember the chapter about building PHP extensions , so you know how to compile/build it and install it.

You may build statically compiled extensions, those are extensions that are part of PHP’s heart and melt into it. They are not represented as .so files, but as .o objects that are linked into the final PHP executable (ELF). Thus, such extensions cannot be disabled, they are part of the PHP executable body code : they are here, in, whatever you say and do. Some extensions are required to be statically built, f.e ext/core, ext/standard, ext/spl and ext/mysqlnd (non-exhaustive list).

You can find the list of statically compiled extensions by looking at the main/internal_functions.c that is generated while you compile PHP. This step is detailed in the chapter about building PHP .

Then, you may also build dynamically loaded extensions. Those are the famous extension.so files that are born at the end of the individual compilation process. Dynamically loaded extensions offer the advantage to be pluggable and unpluggable at runtime, and don’t require a recompilation of all PHP to be enabled or disabled. The drawback is that the PHP process startup time is longer when it must load .so files. But that’s a matter of milliseconds and you don’t really suffer from that.

Another drawback of dynamically loaded extensions is the extension loading order. Some extensions may require other ones to be loaded before them. Although this is not a good practice, we’ll see that PHP extension system allows you to declare dependencies to master such an order, but dependencies are usually a bad idea and should be avoided.

Last thing : PHP statically compiled extensions start before dynamically compiled ones. That means that their MINIT() is called before extensions.so files’ MINIT() .

When PHP starts, it quickly goes to parse its different INI files. If present, those later may declare extensions to load using the “extension=some_ext.so” line reference. PHP then collects every extension parsed from INI configuration, and will try to load them in the same order they’ve been added to the INI file, until some extensions declared some dependencies (which will then be loaded before).

If you use an operating system package manager, you may have noticed that packagers usually name their extension file with heading numbers, aka 00_ext.ini, 01_ext.ini etc… This is to master the order extensions will be loaded. Some uncommon extensions require a specific order to be run. We’d like to remind you that depending on other extensions to be loaded before yours is a bad idea.

To load extensions, libdl and its dlopen()/dlsym() functions are used.

The symbol that is looked for is the get_module() symbol, that means that you extension must export it to be loaded. This is usually the case, as if you used the skeleton script (we’ll foresee it in a minute), then that later generated code using the ZEND_GET_MODULE(your_ext) macro, which looks like:

Like you can see, that macro when used declares a global symbol : the get_module() function that will get called by the engine once wanting to load your extension.

The source code PHP uses to load extensions is located into ext/standard/dl.c

What is a PHP extension ?¶

A PHP extension, not to be confused with a Zend extension , is set up by the usage of a zend_module_entry structure:

The four first parameters have already been explained in the building extensions chapter . They are usually filled-in using the STANDARD_MODULE_HEADER macro.

The ini_entry vector is actually unused. You register INI entries using special macros.

Then you may declare dependencies, that means that your extension could need another extension to be loaded before it, or could declare a conflict with another extensions. This is done using the deps field. In reality, this is very ucommonly used, and more generally it is a bad practice to create dependencies across PHP extensions. Also, note that dependencies are

After that you declare a name . Nothing to say, this name is the name of your extension (which can be different from the name of its own .so file). Take care the name is case sensitive in most operations, we suggest you use something short, lower case, with no spaces (to make things a bit easier).

Then come the functions field. It is a pointer to some PHP functions that extension wants to register into the engine. We talked about that in a dedicated chapter .

Keeping-on come the 5 lifetime hooks. See their dedicated chapter .

Your extension may publish a version number, as a char * , using the version field. This field is only read as part of your extension information, that is by phpinfo() or by the reflection API as ReflectionExtension::getVersion() .

We next see a lot of fields about globals. Globals management has a dedicated chapter .

Finally the ending fields are usually part of the STANDARD_MODULE_PROPERTIES macro and you don’t have to play with them by hand. The engine will give you a module_number for its internal management, and the extension type will be set to MODULE_PERSISTENT . It could be MODULE_TEMPORARY as if you extension were loaded using PHP’s userland dl() function, but that use-case is very uncommon, doesn’t work with every SAPI and temporary extensions usually lead to many problems into the engine.

Generating extension skeleton with scripts¶

Now we’ll see how to generate an extension skeleton so that you may start a new extension with some minimal content and structure you won’t be forced to create by hand from scratch.

the skeleton generator script is located into php-src/ext/ext_skel and the structure it uses as a template is stored into php-src/ext/skeleton

The script and the structure move a little bit as PHP versions move forward.

You can analyze those scripts to see how they work, but the basic usage is:

You can see a very basic an minimal structure that got generated. You’ve learnt in the building extension chapter that the to-be-compiled files of your extension must be declared into config.m4. The skeleton only generated .c file. For the example, we called the extension “pib” so we got a pib.c file and we must uncomment the –enable-pib line in config.m4 for it to get compiled.

Every C file comes with a header file (usually). Here, the structure is php_ .h , so php_pib.h for us. Don’t change that name, the building system expects such a naming convention for the header file.

You can see that a minimal test structure has been generated as well.

Let’s open pib.c. In there, everything is commented out, so we won’t have too many lines to write here.

Basically, we can see that the module symbol needed by the engine to load our extension is published here:

The COMPILE_DL_ macro is defined if you pass –enable- flag to configure script. We also see that in case of ZTS mode, the TSRM local storage pointer is defined as part of ZEND_TSRMLS_CACHE_DEFINE() macro.

After that, there is nothing more to say as everything is commented out and should be clear to you.

New age of the extension skeleton generator¶

Since this commit and the extension skeleton generator had took a new style :

and here is the available options :

The new extension skeleton generator will generate skeleton with three fixed functions, you may define any others functions and change the concrete body as you want.

Remember that the new ext_skel is no longer support proto files.

Publishing API¶

If we open the header file, we can see those lines:

Those lines define a macro named PHP_ _API (for us PHP_PIB_API ) and it resolves to the GCC custom attribute visibility(“default”).

In C, you can tell the linker to hide every symbol from the final object. This is what’s done in PHP, for every symbol, not only static ones (which are by definition not published).

The default PHP compilation line tells the compiler to hide every symbol and not export them.

You should then “unhide” the symbols you’d like your extension to publish for those to be used in other extensions or other parts of the final ELF file.

Remember that you can read published and h > nm under Unix.

We can’t explain those concepts in deep here, perhaps such links could help you ?

So basically, if you want a C symbol of yours to be publicly available to other extensions, you should declare it using the special PHP_PIB_API macro. The traditional use-case for that is to publish the > zend_class_entry* type) so that other extensions can hook into your own published classes and replace some of their handlers.

Please, note that this only works with the traditional PHP. If you use a PHP from a Linux distribution , those are patched to resolve symbols at load time and not lazilly, thus this trick doesn’t work.

The repository for this book is available on GitHub.


The PHP internals book is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Ссылка на основную публикацию
Adblock
detector
×
×