Writing a Hello World PHP 7 Extension
August 5, 2016
Contents
- Getting Started
- Hello World
- Explaining Zend Module Entry
Getting Setup
In the folder /vagrant
you will want to create a new folder with the name of your extension. I will be calling my extension hello
and so my folder will be hello-ext
. This is just convention, and the folder name doesn’t actually matter.
$ mkdir /vagrant/hello-ext
A very basic PHP extension requires 3 files:
config.m4
src/hello.c
src/hello.h
The config.m4
extension tells PHP some basic information about your extension. The hello.c
and hello.h
files are the actual extension itself.
Actually writing the extension
Test case first
To check whether this actually works, this is a basic PHP file we using to check:
<?php
var_dump(extension_loaded('hello'));
var_dump(hello_world());
?>
At every step of the way through this process, you can re-run this file using php -f /path/to/test/file.php
. If you run this now, you’ll see:
$ php -f test.php
# bool(false)
# Fatal error: Uncaught Error: Call to undefined function hello_world() in /vagrant/test.php:5
# Stack trace:
# #0 {main}
# thrown in /vagrant/test.php on line 5
Note that I hold this file outside of the ext
folder. This file isn’t part of our extension. We will deal with proper testing at a later date.
config.m4
The first thing we should tell PHP about is the argument to enable the hello
extension.
The syntax for the config.m4
file is based on the GNU autoconf
syntax. Though, you don’t need to know it really. There will be very few lines involved, and i’ll do my best to explain them.
Comments can be left in the config.m4
file using dnl
dnl Tell PHP about the argument to enable the hello extension
PHP_ARG_ENABLE(hello, Whether to enable the hello world extension, [ --enable-hello Enable hello world])
if test "$PHP_HELLO" != "no"; then
PHP_NEW_EXTENSION(hello, src/hello.c, $ext_shared)
fi
dnl
stands for Discard to Next Line
1, including the new line at the end of the line.
Breaking PHP_ARG_ENABLE(hello, Whether to enable the hello world extension, [ --enable-hello Enable hello world])
down:
PHP_ARG_ENABLE
- Provides configure
with the options and help text seen when running ./configure --help
.
There is also a PHP_ARG_WITH
, and either ENABLE
or WITH
should be provided. The difference there is how configure
gets ran, either --with-*
or --enable-*
.
–enable-SOMETHING is used for extensions with zero dependencies
–with-SOMETHING is used for extensions with dependencies (e.g. it’s –enable-pdo, but –with-pdo-mysql as the latter may depend on libmysqlclient)
Thanks to /u/dshafik for clarification on this
Every extension should provide at least one or the other with the extension name, so that users can choose whether or not to build the extension into PHP.
By convention,
PHP_ARG_WITH()
is used for an option which takes a parameter, such as the location of a library or program required by an extension, whilePHP_ARG_ENABLE()
is used for an option which represents a simple flag.
hello
is just the name of our extension.
Whether to enable the hello world extension
Not entirely sure what this is for to be honest.
[ --enable-hello Enable hello world]
this determines the output for configure --help
.
It will end up looking something like this:
being what was inside the square brackets.
Then next bit is literally just a basic if
check to determine whether or not the the extension is enabled. By default, it will be for us.
Now we need to register the extension.
PHP_NEW_EXTENSION(hello, src/hello.c, $ext_shared)
The first parameter is the name of the extension.
The second parameter is the list of all source files which are part of the extension.
The third parameter should always be $ext_shared, a value which was determined by configure.
Taken, and adapted from the woefully outdated php guide.
There are more parameters, but so far we don’t need them, so we will leave them for a different day.
Now to the actual extension itself.
src/hello.h
This file is quite basic:
#define PHP_HELLO_EXTNAME "hello"
#define PHP_HELLO_VERSION "0.0.1"
PHP_FUNCTION(hello_world);
We are defining two directives, what this allows us to do is use PHP_HELLO_EXTNAME
in place of the actual string hello
throughout.
This is common practice, so whilst not necessary strictly in this use case, it is a good habit to have.
The first interesting line is PHP_FUNCTION(hello_world);
This is where we define the header for our function.
The body of the function will go within our hello.c
file.
src/hello.c
This is where things get a little more complicated now.
So i’ll give you the full file, and then break it down a little:
#include <php.h>
#include "hello.h"
zend_function_entry hello_functions[] = {
PHP_FE(hello_world, NULL)
PHP_FE_END
};
zend_module_entry hello_module_entry = {
STANDARD_MODULE_HEADER,
PHP_HELLO_EXTNAME,
hello_functions,
NULL,
NULL,
NULL,
NULL,
NULL,
PHP_HELLO_VERSION,
STANDARD_MODULE_PROPERTIES,
};
ZEND_GET_MODULE(hello);
PHP_FUNCTION(hello_world) {
php_printf("Hello world\
");
};
Pasting this in to your file, and then going to the terminal and running the following commands will allow this to work.
$ cd /vagrant/hello-ext
$ phpize
$ ./configure
$ make
$ sudo make install
The next thing to do is add the extension into your php.ini
file.
This will look like so:
extension=hello.so
Now, go back to your test file and run it again.
You should see something like this:
$ php -f test.php
bool(true)
Hello world
NULL
Note: Fixed typo with response above, thanks to /u/phisch90
Success.
The reason for the hanging NULL
is because we are using the printf
function, rather than returning the value.