Skip to content

Commit 1f8cd96

Browse files
author
Julien Pauli
committed
Extensions skeleton
1 parent 08c240a commit 1f8cd96

File tree

1 file changed

+208
-1
lines changed

1 file changed

+208
-1
lines changed

Book/php7/extensions_design/extension_skeleton.rst

Lines changed: 208 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,211 @@ Extension skeleton
44
Here we detail what a PHP extension look like, and how to generate a skeleton using some tools. That will allow us to
55
use a skeleton code and hack into it, instead of creating every needed piece by hand from scratch.
66

7-
We'll also detail how you could/should organize your extension files.
7+
We'll also detail how you could/should organize your extension files, how the engine loads them, and basically
8+
everything you need to know about a PHP extension.
9+
10+
How the engine loads extensions
11+
*******************************
12+
13+
You remember :doc:`the chapter about building PHP extensions <../build_system/building_extensions>`, so you know how
14+
to compile/build it and install it.
15+
16+
When PHP starts, it quickly goes to parse its different INI files. If present, those later may declare extensions to
17+
load using the *"extension=some_ext.so"* line reference.
18+
PHP then collects every extension parsed from INI configuration, and will try to load them.
19+
20+
To load extensions, `libdl <https://en.wikipedia.org/wiki/Dynamic_loading>`_ and its
21+
`dlopen()/dlsym() <http://www.unix.com/man-page/All/3lib/libdl/>`_ functions are used.
22+
23+
The symbol that is looked for is the ``get_module()`` symbol, that means that you extension must export it to be loaded.
24+
This is usually the case, as if you used the skeleton script (we'll foresee it in a minute), then that later generated
25+
code using the ``ZEND_GET_MODULE(your_ext)`` macro, which looks like::
26+
27+
#define ZEND_GET_MODULE(name) \
28+
BEGIN_EXTERN_C()\
29+
ZEND_DLEXPORT zend_module_entry *get_module(void) { return &name##_module_entry; }\
30+
END_EXTERN_C()
31+
32+
Like you can see, that macro when used declares a global symbol : the get_module() function that will get called by the
33+
engine once wanting to load your extension.
34+
35+
.. note:: The source code PHP uses to load extensions is located into
36+
`ext/standard/dl.c <https://github.com/php/php-src/blob/27d681435174433c3a9b0b8325361dfa383be0a6/ext/
37+
standard/dl.c#L90>`_
38+
39+
What is an extension ?
40+
**********************
41+
42+
A PHP extension, not to be confused with a :doc:`Zend extension <zend_extensions>`, is set up by the usage of a
43+
``zend_module_entry`` structure::
44+
45+
struct _zend_module_entry {
46+
unsigned short size; /*
47+
unsigned int zend_api; * STANDARD_MODULE_HEADER
48+
unsigned char zend_debug; *
49+
unsigned char zts; */
50+
51+
const struct _zend_ini_entry *ini_entry; /* Unused */
52+
const struct _zend_module_dep *deps; /* Module dependencies */
53+
const char *name; /* Module name */
54+
const struct _zend_function_entry *functions; /* Module published functions */
55+
56+
int (*module_startup_func)(INIT_FUNC_ARGS); /*
57+
int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); *
58+
int (*request_startup_func)(INIT_FUNC_ARGS); * Lifetime functions (hooks)
59+
int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); *
60+
void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS); */
61+
62+
const char *version; /* Module version */
63+
64+
size_t globals_size; /*
65+
#ifdef ZTS *
66+
ts_rsrc_id* globals_id_ptr; *
67+
#else * Globals management
68+
void* globals_ptr; *
69+
#endif *
70+
void (*globals_ctor)(void *global); *
71+
void (*globals_dtor)(void *global); */
72+
73+
int (*post_deactivate_func)(void); /* Rarely used lifetime hook */
74+
int module_started; /* Has module been started (internal usage) */
75+
unsigned char type; /* Module type (internal usage) */
76+
void *handle; /* dlopen() returned handle */
77+
int module_number; /* module number among others */
78+
const char *build_id; /* build id, part of STANDARD_MODULE_PROPERTIES_EX */
79+
};
80+
81+
The four first parameters have already been explained in
82+
:doc:`the building extensions chapter <../build_system/building_extensions>`. They are usually filled-in using the
83+
``STANDARD_MODULE_HEADER`` macro.
84+
85+
The ``ini_entry`` vector is actually unused. You :doc:`register INI entries <ini_settings>` using special macros.
86+
87+
Then you may declare dependencies, that means that your extension could need another extension to be loaded before it,
88+
or could declare a conflict with another extensions. This is done using the ``deps`` field. In reality, this is very
89+
ucommonly used, and more generally it is a bad pactice to create dependencies accross PHP extensions.
90+
91+
After that you declare a ``name``. Nothing to say, this name is the name of your extension (which can be different from
92+
the name of its own *.so* file). Take care the name is case sensitive in most operations, we suggest you use something
93+
short, lower case, with no spaces (to make things a bit easier).
94+
95+
Then come the ``functions`` field. It is a pointer to some PHP functions that extension wants to register into
96+
the engine. We talked about that :doc:`in a dedicated chapter <php_functions>`.
97+
98+
Keeping-on come the 5 lifetime hooks. :doc:`See their dedicated chapter <php_lifecycle>`.
99+
100+
Your extension may publish a version number, as a ``char *``, using the ``version`` field. This field is only read as
101+
part of your extension informations, that is by phpinfo() or by the reflection API as
102+
``ReflectionExtension::getVersion()``.
103+
104+
We next see a lot of fields about globals. Globals management :doc:`has a dedicated chapter <globals_management>`.
105+
106+
Finally the ending fields are usually part of the ``STANDARD_MODULE_PROPERTIES`` macro and you don't have to play with
107+
them by hand. The engine will give you a ``module_number`` for its internal management, and the extension type will be
108+
set to ``MODULE_PERSISTENT``. It could be ``MODULE_TEMPORARY`` as if you extension were loaded using PHP's userland
109+
``dl()`` function, but that use-case is very uncommon, doesn't work with every SAPI and temporary extensions usually
110+
lead to many problems into the engine.
111+
112+
Generating extension skeleton with scripts
113+
******************************************
114+
115+
Now we'll see how to generate an extension skeleton so that you may start a new extension with some minimal content
116+
and structure you won't be forced to create by hand from scratch.
117+
118+
the skeleton generator script is located into
119+
`php-src/ext/ext_skel <https://github.com/php/php-src/blob/27d681435174433c3a9b0b8325361dfa383be0a6/ext/ext_skel>`_ and
120+
the structure it uses as a template is stored into
121+
`php-src/ext/skeleton <https://github.com/php/php-src/tree/27d681435174433c3a9b0b8325361dfa383be0a6/ext/skeleton>`_
122+
123+
.. note:: The script and the structure move a little bit as PHP versions move forward.
124+
125+
You can analyze those scripts to see how they work, but the basic usage is:
126+
127+
.. code-block:: shell
128+
129+
> cd /tmp
130+
/tmp> /path/to/php/ext/ext_skel --skel=/path/to/php/ext/skeleton --extname=pib
131+
[ ... generating ... ]
132+
/tmp> tree pib/
133+
pib/
134+
├── config.m4
135+
├── config.w32
136+
├── CREDITS
137+
├── EXPERIMENTAL
138+
├── php_pib.h
139+
├── pib.c
140+
├── pib.php
141+
└── tests
142+
└── 001.phpt
143+
/tmp>
144+
145+
You can see a very basic an minimal structure that got generated. You've learnt in the building extension chapter that
146+
the to-be-compiled files of your extension must be declared into *config.m4*. The skeleton only generated
147+
*<your-ext-name>.c* file. For the example, we called the extension *"pib"* so we got a *pib.c* file and we must
148+
uncomment the *--enable-pib* line in *config.m4* for it to get compiled.
149+
150+
Every C file comes with a header file (usually). Here, the structure is *php_<your-ext-name>.h* , so *php_pib.h* for
151+
us. Don't change that name, the building system expects such a naming convention for the header file.
152+
153+
You can see that a minimal test structure has been generated as well.
154+
155+
Let's open *pib.c*. In there, everything is commented out, so we won't have too many lines to write here.
156+
157+
Basically, we can see that the module symbol needed by the engine to load our extension is published here::
158+
159+
#ifdef COMPILE_DL_PIB
160+
#ifdef ZTS
161+
ZEND_TSRMLS_CACHE_DEFINE()
162+
#endif
163+
ZEND_GET_MODULE(pib)
164+
#endif
165+
166+
The ``COMPILE_DL_<YOUR-EXT-NAME>`` macro is defined if you pass *--enable-<my-ext-name>* flag to configure script. We
167+
also see that in case of ZTS mode, the TSRM local storage pointer is defined as part of ``ZEND_TSRMLS_CACHE_DEFINE()``
168+
macro.
169+
170+
After that, there is nothing more to say as everything is commented out and should be clear to you.
171+
172+
Publishing API
173+
**************
174+
175+
If we open the header file, we can see those lines::
176+
177+
#ifdef PHP_WIN32
178+
# define PHP_PIB_API __declspec(dllexport)
179+
#elif defined(__GNUC__) && __GNUC__ >= 4
180+
# define PHP_PIB_API __attribute__ ((visibility("default")))
181+
#else
182+
# define PHP_PIB_API
183+
#endif
184+
185+
Those lines define a macro named ``PHP_<EXT-NAME>_API`` (for us ``PHP_PIB_API``) and it resolves to the
186+
`GCC custom attribute <https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes>`_
187+
visibility("default").
188+
189+
In C, you can tell the linker to hide every symbol from the final object. This is what's done in PHP, for every
190+
symbol, not only static ones (which are by definition not published).
191+
192+
.. warning:: The default PHP compilation line tells the compiler to hide every symbol and not export them.
193+
194+
You should then "unhide" the symbols you'd like your extension to publish for those to be used in other extensions or
195+
other parts of the final ELF file.
196+
197+
.. note:: Remember that you can read published and hidden symbol of an ELF using ``nm`` under Unix.
198+
199+
We can't explain thoses concepts in deep here, perhaps such links could help you ?
200+
201+
* https://gcc.gnu.org/wiki/Visibility
202+
* http://www.iecc.com/linker/linker10.html
203+
* https://www.akkadia.org/drepper/dsohowto.pdf
204+
* http://www.faqs.org/docs/Linux-HOWTO/Program-Library-HOWTO.html
205+
* https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/DynamicLibraries/000-Introduction/Introduction.html
206+
207+
So basically, if you want a C symbol of yours to be publicly available to other extensions, you should declare it
208+
using the special ``PHP_PIB_API`` macro. The traditionnal use-case for that is to publish the classes symbols
209+
(``zend_class_entry*`` type) so that other extensions can hook into your own published classes and replace some of their
210+
handlers.
211+
212+
.. note:: Please, note that this only works with the traditionnal PHP. If you use
213+
:doc:`a PHP from a Linux distribution <../build_system/building_php>`, those are patched to resolve symbols
214+
at load time and not lazilly, thus this trick doesn't work.

0 commit comments

Comments
 (0)