Implementing PHP Extensions with Zephir, CGO, and JIT: A Performance Comparison
This article explores several methods for creating PHP extensions—including native C, Zephir, php-cpp, php-x, and CGO—provides sample code, describes compilation steps, and compares their execution speed using a Fibonacci benchmark with and without PHP JIT.
The article introduces multiple approaches for developing PHP extensions: native C extensions generated by ext_skel.php , Zephir, php-cpp, php-x, and CGO. It emphasizes two main implementation modes: wrapping the Zend API directly and using CGO to embed C and Go code.
Zephir example :
<code>//Main 类
final class Zimuge {
public static function calcFibonacci(int i) {
if (i < 2) {
return i;
}
return self::calcFibonacci(i - 1) + self::calcFibonacci(i - 2);
}
}
</code>Compilation command for Zephir:
<code>zephir build</code>CGO example (Go code generating a PHP extension):
<code>package main
/*
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
static int le_go2php;
PHP_MINIT_FUNCTION(go2php) { return SUCCESS; }
PHP_MSHUTDOWN_FUNCTION(go2php) { return SUCCESS; }
PHP_RINIT_FUNCTION(go2php) { return SUCCESS; }
PHP_RSHUTDOWN_FUNCTION(go2php) { return SUCCESS; }
PHP_MINFO_FUNCTION(go2php) {
php_info_print_table_start();
php_info_print_table_header(2, "go2php support", "enabled");
php_info_print_table_end();
}
PHP_FUNCTION(go2php_print) {
zend_long a,b;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_LONG(a)
ZEND_PARSE_PARAMETERS_END();
b = calcFib(a);
RETURN_LONG(b);
}
ZEND_BEGIN_ARG_INFO(null, 0)
ZEND_END_ARG_INFO()
const zend_function_entry go2php_functions[] = {
PHP_FE(go2php_print, null)
PHP_FE_END
};
zend_module_entry go2php_module_entry = {
STANDARD_MODULE_HEADER,
"go2php",
go2php_functions,
PHP_MINIT(go2php),
PHP_MSHUTDOWN(go2php),
PHP_RINIT(go2php),
PHP_RSHUTDOWN(go2php),
PHP_MINFO(go2php),
"0.1.0",
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_GO2PHP
ZEND_GET_MODULE(go2php)
#endif
*/
import "C"
func main() {}
</code>Go implementation of the Fibonacci function used by the extension:
<code>package main
import "C"
//export calcFib
func calcFib(i int) int {
if i < 2 {
return i
}
return calcFib(i-1) + calcFib(i-2)
}
</code>Compilation and linking flags for CGO:
<code>CGO_CFLAGS="-g -I`/root/download/php8/bin/php-config --include-dir` -I`/root/download/php8/bin/php-config --include-dir`/main -I`/root/download/php8/bin/php-config --include-dir`/TSRM -I`/root/download/php8/bin/php-config --include-dir`/Zend -I`/root/download/php8/bin/php-config --include-dir`/ext -I`/root/download/php8/bin/php-config --include-dir`/ext/date/lib -DHAVE_CONFIG_H"
CGO_LDFLAGS="-Wl,--export-dynamic -Wl,--unresolved-symbols=ignore-all"
go build -p 1 -gcflags "-l" -buildmode=c-shared -o go2php.so
</code>A PHP test script runs the Fibonacci calculation using the native PHP function, the Zephir‑generated class, and the CGO‑built extension, measuring execution time with microtime(true) . Results without JIT show CGO fastest (≈0.06 s), Zephir slower (≈8.57 s), and pure PHP slowest (≈0.76 s). With PHP JIT enabled, CGO improves slightly (≈0.047 s), Zephir becomes faster (≈5.59 s), and PHP JIT runs in ≈0.11 s.
The conclusions are:
PHP JIT can noticeably improve performance.
CGO allows Go developers to write PHP extensions without abandoning PHP.
Zephir, while not the fastest, offers a relatively simple way to create PHP extensions.
Additional notes cover loading the compiled .so files via php.ini and verifying them with php -m or php --ri . The PHP version used for testing is 8.1.3.
php中文网 Courses
php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.