原文:https://ollyxar.com/blog/php-c-extension
-------------------------------------------------------------
Speedup your PHP with C extension
Prelude
Nowadays we know a lot of PHP extensions like Phalcon or Swoole. But does it make sense to build them? That's the main question. To find out, we need to build at least one. So, let's do it!
Stage 1: prepare environment
To achieve the maximum performance we'll use only standard API for building our extension. That's mean that we need to learn some basics not only about C language but also about PHP API extensions.
The one (and probably only one) guide is www.phpinternalsbook.com. Unfortunately, lots of articles about PHP7 API are missing. But if you're brave enough and got some experience with C, this will not stop you on your way to build PHP7 extension.
The target for our extension will be Linux. Since I'm a web engineer, I work on Linux most of the time. But when I'm at home I prefer to use more convenient for me Windows. You might know, that if you want to build some programs for a certain platform, you need to build it on this exact platform. That's why we'll use Docker. This will allow us to work with our project on any platform you prefer.
In this example, we'll eventually build complete analog of Lumen-like routes
Clone ready to develop repository. Let's see how it's done.
Dockerfile
FROM alpine:latest
RUN apk --update --no-cache add autoconf g++ make git bison re2c \
libxml2-dev sqlite-dev pcre-dev && \
git clone https://github.com/php/php-src.git ./php-src && \
cd ./php-src && git checkout tags/php-7.3.6 && \
./buildconf --force && ./configure --prefix=/usr/local/php7 && \
make -j4 && make install
WORKDIR /src
These instructions will add all necessary dependencies for building our extension, then clone source repository of PHP, then choose PHP version 7.3.6, and finally build it with -j4
= four parallel processes just to not waste the time.
Then open cmd or powershell in the current directory and build image:
docker build -t php_ext:latest .
Stage 2: initial setup
Every time you need to build / compile your project you need to enter container using:
docker run --rm --name php_ext -v d:/projects/php_extensions/router/src:/src -it php_ext sh
.
Please make note that you have to change the directory according to your system.
This command used only once to create all necessary files for the project:
/usr/local/php7/bin/phpize && ./configure --with-php-config=/usr/local/php7/bin/php-config
This command will build your extension, clean up binaries and execute tests
make && make clean && make test && make install
Stage 3: Extension
Now let's take a closer look at the code.
First of all, since we're using regular expressions, we'll add a check for external dependency in config.m4
via AC_CHECK_HEADER
PHP_ARG_ENABLE(httprouter, for HTTPRouter support,
[ --enable-httprouter Include HTTPRouter support])
if test "$PHP_HTTPROUTER" != "no"; then
PHP_NEW_EXTENSION(httprouter, httprouter.c, $ext_shared)
CFLAGS="$CFLAGS -Wall -g"
AC_CHECK_HEADER(pcre.h, , [AC_MSG_ERROR([Couldn't find pcre.h, try installing the libpcre development/headers package])])
fi
Our extension has its own class and methods that should return self-instance. That's why in header-file we have to add a very useful template:
define RETURN_THIS() RETURN_ZVAL(getThis(), 1, 0)
Be careful with arguments in methods! Make sure that you're not assigning them to global variables. PHP engine will reduce usage and probably will reuse them next time. So once you've received parameter, copy its value to another variable. Or if you need to work with it later - better to use heap. But do not forget to free memory then! Otherwise, you'll have a memory leak.
Stage 4: comparison & conclusion
We've used scripts that run both approaches with 999 instances of Class 100 times.
Tests were made on Aser Aspire E15 (AMD A10) within docker container
Regular PHP Class | PHP Extension (C implementation) | |
---|---|---|
Time in seconds | 1.787871599197389 | 1.473563671112060 |
Overall | 1.21 times slower | 1.21 times faster |
The answer to the main question will be: you can increase the performance up to 21%. But you will probably spend much more time to implement the same functionality.