Knock

Самый простой способ мониторинга вашего сайта без лишних затрат

Начать бесплатно

PHP модуль на C++

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

Нативные расширения в PHP это достаточно магическая штука, как и большинство написанного на C кода. Но мы немного схитрим и воспользуемся www.php-cpp.com. С помощью этой штуки, мы будем писать расширение на C++, а не на чистом C. И это должно неплохо ускорить разработку нашего расширения.

Установка PHP-CPP очень проста. Клонируем репозиторий с github и запускаем сборку:

git clone https://github.com/CopernicaMarketingSoftware/PHP-CPP.git
cd PHP-CPP
make
sudo make install

PHP расширения компилируются в разделяемые библиотеки(.so файлы). И для загрузки этой библиотеки вам нужно указать ее в конфигурационном файле.

Перед тем как создавать расширение, давайте разберемся как вообще PHP их загружает. Когда PHP запускается, он загружает конфигурационные *.ini файлы. Затем получает названия модулей из этих файлов, пытается их загрузить и вызвать метод get_module() для каждого. А это значит, кто каждый модуль должен обязательно определить функцию get_module(). Этот метод вызывается в момент старта PHP, до начала обработки запросов. Он должен возвращать указатель на участок памяти в котором хранится информация о модуле, всех его функциях, переменных и т.д.

Структура, содержащая информацию о модуле, определена в заголовочном файле Zend engine. Это довольно сложная структура и, конечно же, нет ни какой вменяемой документации по ее использованию. К счастью, библиотека PHP-CPP избавляет нас от лишних забот, и мы можем использовать понятные классы для реализации нашего расширения.

Для начала сделаем простую заготовку для расширения:

#include <phpcpp.h>

extern "C" {
        PHPCPP_EXPORT void *get_module()
        {
                // static(!) объект Php::Extension должен остаться в памяти
                // на время работы всего процесса
                static Php::Extension simple("simple", "1.0");

                // @todo: тут мы будем добавлять наш функционал

                // возвращаем наше расширение
                return simple;
        }
}

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

#include <phpcpp.h>
#include <iostream>

void example() {
        Php::out << "I'm example" << std::endl;
}

extern "C" {
        PHPCPP_EXPORT void *get_module()  {
                static Php::Extension simple("simple", "1.0");

                // делаем нашу функцию доступной из PHP скрипта 
                simple.add<example>("example");
                
                return simple;
        }
}

И теперь, собственно, то зачем мы все это устроили. Наша задача - это наладить сбор метрик из PHP приложения, а для этого нам нужно уметь сохранять данные между запросами. В прошлый раз мы использовали apc. Теперь можем воспользоваться чуть более гибким методом - будем хранить наши счетчики как обычные сишные переменные.

#include <phpcpp.h>
#include <iostream>

int counter = 0;

void example() {
        Php::out << "I'm example" << std::endl;
}

Php::Value simple_counter() {
        counter++;

        return counter;
}

extern "C" {
        PHPCPP_EXPORT void *get_module()  {
                static Php::Extension simple("simple", "1.0");

                simple.add<example>("example");
                simple.add<simple_counter>("simple_counter");

                return simple;
        }
}

Мы добавили переменную counter и еще одну функцию simple_counter. Наша новая функция увеличивает значение переменной counter и возвращает его. Осталось все это скомпилировать и посмотреть что получилось.

Для сборки расширения нам нужен Makefile:

NAME = simple

# У вас этот будет другая папка. Узнать, где находятся
# актуальные ini файлы можно с помощью команды `php --ini`
INI_DIR	= /home/artem/.phpbrew/php/php-7.0.8/var/db

EXTENSION_DIR =	$(shell php-config --extension-dir)

EXTENSION =	${NAME}.so
INI 	  =	${NAME}.ini

COMPILER =	g++
LINKER	 =	g++

COMPILER_FLAGS		=	-Wall -c -O2 -std=c++11 -fpic -o
LINKER_FLAGS		=	-shared
LINKER_DEPENDENCIES	=	-lphpcpp

RM	=	rm -f
CP	=	cp -f
MKDIR	=	mkdir -p

SOURCES	=	$(wildcard src/*.cpp src/*/*.cpp)
OBJECTS	=	$(SOURCES:%.cpp=%.o)


all: ${OBJECTS} ${EXTENSION}


${EXTENSION}: ${OBJECTS}
			${LINKER} ${LINKER_FLAGS} -o $@ ${OBJECTS} ${LINKER_DEPENDENCIES}

${OBJECTS}:
			${COMPILER} ${COMPILER_FLAGS} $@ ${@:%.o=%.cpp}

install:
			${CP} ${EXTENSION} ${EXTENSION_DIR}
			${CP} ${INI} ${INI_DIR}

clean:
			${RM} ${EXTENSION} ${OBJECTS}

format:
			clang-format -i src/*.cpp
			clang-format -i src/*/*.cpp
			clang-format -i src/*/*.h
			clang-format -i tests/*.cpp

И кроме этого Makefile нам нужен очень простой файл конфигурации нашего расширения simple.ini:

extension=simple.so

При выполнении скрипта make install файл нашего расширения simple.so копируется в папку EXTENSION_DIR, а конфиг нашего модуля копируется в папку INI_DIR.

Теперь можем создать небольшой пример скрипта, чтобы посмотреть как будет работать наш счетчик.

<?php

echo simple_counter();

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

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