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).
    Как написать саму функцию, я расскажу позднее. А пока ограничимся этой:
    PHP_FUNCTION(hello_world) {
        RETURN_STRING("Hello World", 1);
    }


    5. Сборка и запуск.
    Собираем в релизе. Собралось.
    Создаем каталог C:\PHPDEV\test
    Копируем туда php.exe и php5ts.dll из каталога.
    Копируем собранный dll под именем test.dll.
    Создаем php.ini:
    extension_dir = .
    extension = test.dll

    Создаем test.php со строкой <?=hello_world()?> и запускаем его в консоли.


    6. Продвинутое создание функций.
    Разберемся, как принимать значения из функций и передавать их.
    Сложность заключается в том, что функция принимает и возвращает различные значения различных типов.
    Рассмотрим пример, при котором принимается строка и целое и возвращается строка.
    PHP_FUNCTION(foo) {
        char* input;
        int inputLength;
        long multy;
        char* result;
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &input, &inputLength, &multy)) {
            return;
        }
        result = (char*) emalloc(strlen(input) + sizeof(long) + 3);
        sprintf(result, "%s, %ld", input, multy);
        RETURN_STRING(result, 1);
    }

    Как можно увидеть, здесь использованы следующие конструкции:
    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_FUNCTION(extract_ico) {
        char *filename;
        char *filenameOut;
        int len1, len2;
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &filename, &len1, &filenameOut, &len2)) {
            return;
        }
        _extract_ico(filename, filenameOut);
        RETURN_NULL();
    }

    Ничего сложного, как мы видим. Просто и понятно. Вот такие вот файлы получились в итоге: php_ico.c и php_ico.h.

    8. Бонус: сборка под *nix
    Бонус заключается в наличии config.m4 файла, так что для сборки надо лишь выполнить серию команд:
    phpize
    ./configure
    make
    Естественно в системе должны стоять соответствующие пакеты.
    Для ubuntu 9.10 server это делается так:
    sudo apt-get install build-essential php5 php5-dev
    Берем собранную so-библиотеку и прописываем в расширения. Готово.
    Да, *nix гораздо удобнее для веб-программиста, среди моих знакомых на винде сидит мешишинство. Многие предпочитают убунту или мак =)

    9. Литература и полезные ссылки.
    1) Джордж Шлосснейгл. Профессиональное программирование на PHP. Купить можно на books.ru, там дешевле всего из популярных магазинов имхо. Если хотите меня поддержать, то жмите эту ссылку =)
    2) Extension Writing Part I: Introduction to PHP and Zend
    3) PHP: internals:windows [PHP Wiki]

    P.S. А в следующий раз я расскажу, как встроить PHP в свое приложение на С/C++. Но не буду обещать, что это будет «на днях».
    Поделиться публикацией

    Похожие публикации

    Комментарии 17

      0
      отлично, надо будет попробовать.
        0
        Хочу обратить внимание общественности на один интересный факт — внутри пхп LONG есть, но в самом коде инты всегда знаковые.

        ip2long — вернет лонг, но ip2long+1 выдаст инт :)
        И приходиться изращаться борясь с потерей старшего бита и перегрузкой инта в два миллиарда

        Смешно правда?
          +2
          Эм, не понял немного.
          long который в C — это int который в PHP.

          И естественно IP со знаком не поместился бы в int(PHP), вот поэтому и нужно мучать старший байт. Посмотри второй пример в описании этой функции — станет понятно, почему надо извращаться.
            0
            Вот только типы данных в пхп всегда были 64 битные…
            обидно :(
        • НЛО прилетело и опубликовало эту надпись здесь
            0
            А что именно вам бы хотелось?
              0
              допустим обьект стринг, инкапсулирующий некоторые строковые функции, и адекватно работающий со встроенными функциями и операторами
              $objString = new String(«Hello»);
              $objString->append(" World");
              if( $objString->serch(«llo») )
              {
              echo «in ». $objString. " exists substring 'llo' on". $objString->indexOf('llo'). ' index';
              }
                0
                хм, ни разу не пробовал писать свои операторы. Про остальное постараюсь написать на днях. Если найду информацию про операторы, расскажу и про них)
                0
                А мне хотелось бы узнать как в случае создания своего модуля, реализовать на C работу с ассоциативными массивами и особенно (звучит ужасно, конечно, но просто нужно) как в C сделать как бы аналог PHP'шного eval'a или использовать уже существующий (что лучше).
                  0
                  В книге вопрос ро массивы раскрыт, все просто, но функций много.
                  Насчет аналога евала — это встраивание.
                    0
                    Встраивание чего и куда? (или это я до такой степени уже C забыл?)
                      0
                      Про встраивание в след. раз напишу )
                • НЛО прилетело и опубликовало эту надпись здесь
                    0
                    Пример для класса строк:
                    Это будет медленнее, чем на чистом пхп — ведь придется кастовать типы в с, потом в пхп для передачи, вызывать ф-ии из пхп через евал, кастовать обратно в с, потом кастовать обратно в пхп )

                    Либо придется переписывать стандартные строковые функции, что есть гемор. Про работу с классами можно глянуть вкниге — цель статьи была в «дать пинка к изучению», ичиттывая фактор, что в винде сложно очень создать проект.
                    • НЛО прилетело и опубликовало эту надпись здесь
                +1
                FFI в PHP просит двух вещей: молотка сверху и наковальни снизу.
                  0
                  А еще документации, которой реально нет вообще — есть только для ZE1, для ZE2 нет практически ничего, а на носу уже ZE3.

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

                Самое читаемое