1
1
Registering and using PHP functions
2
2
===================================
3
3
4
- The main goal of a PHP extension is to register new PHP functions for userland. PHP functions are complex to fully
5
- understand their mechanics that hook very deep into the Zend Engine, but fortunately we don't need this knowledge
4
+ The main goal of a PHP extension is to register new PHP functions for userland. PHP functions are complex to fully
5
+ understand their mechanics that hook very deep into the Zend Engine, but fortunately we don't need this knowledge
6
6
for our chapter, as the PHP extension mechanism provides many ways to abstract a lot such a complexity.
7
7
8
- Registering and using new PHP functions in an extension is an easy step. Deeply understanding the big picture is
9
- however pretty more complex. A first step :doc: `to the zend_function chapter<../internal_types/functions> ` could help
8
+ Registering and using new PHP functions in an extension is an easy step. Deeply understanding the big picture is
9
+ however pretty more complex. A first step :doc: `to the zend_function chapter<../internal_types/functions> ` could help
10
10
then.
11
11
12
- Obviously, you'll need to master :doc: `types<../internal_types> `, especially :doc: `zvals<../internal_types/zvals> ` and
12
+ Obviously, you'll need to master :doc: `types<../internal_types> `, especially :doc: `zvals<../internal_types/zvals> ` and
13
13
:doc: `memory management<../memory_management> ` here. Also, know your :doc: `hooks<../extensions_design/php_lifecycle> `.
14
14
15
15
zend_function_entry structure
16
16
*****************************
17
17
18
- Not to be confused with :doc: `the zend_function structure<../internal_types/functions> `, ``zend_function_entry `` is
18
+ Not to be confused with :doc: `the zend_function structure<../internal_types/functions> `, ``zend_function_entry `` is
19
19
used to register functions against the engine while in an extension.
20
20
Here it is::
21
21
@@ -44,7 +44,7 @@ return value is passed to our handler as a parameter.
44
44
Arguments. The ``arg_info `` variable is about declaring the API arguments our function will accept. Here again,
45
45
that part can be tricky to deeply understand, but we don't need to get too deep and we'll once more use macros to
46
46
abstract and ease arguments declaration. What you should know is that you are not required to declare any arguments
47
- here for the function to work, but it is highly recommanded. We'll get back to that. Arguments are an array of
47
+ here for the function to work, but it is highly recommanded. We'll get back to that. Arguments are an array of
48
48
``arg_info ``, and thus its size is passed as ``num_args ``.
49
49
50
50
Then come ``flags ``. We won't detail flags in this chapter. Those are used internally, you'll find some details in the
@@ -53,9 +53,9 @@ dedicated :doc:`zend_function<../internal_types/functions>` chapter.
53
53
Registering PHP functions
54
54
*************************
55
55
56
- PHP functions are registered into the engine when the extension gets loaded. An extension may declare a function vector
57
- into the extension structure. Functions declared by extensions are called "internal" functions, and at the opposite of
58
- "user" functions (functions declared and used using PHP userland) they don't get unregistered at the end of the
56
+ PHP functions are registered into the engine when the extension gets loaded. An extension may declare a function vector
57
+ into the extension structure. Functions declared by extensions are called "internal" functions, and at the opposite of
58
+ "user" functions (functions declared and used using PHP userland) they don't get unregistered at the end of the
59
59
current request: they are permanent.
60
60
61
61
As a reminder, here is the PHP extension structure shorten for readability::
@@ -129,16 +129,16 @@ Here is the same code, but with the macros expanded, so that you can have a look
129
129
(uint32_t) (sizeof(((void *)0))/sizeof(struct _zend_internal_arg_info)-1), 0 },
130
130
}
131
131
132
- Notice how ``PHP_FUNCTION() `` expanded to a C symbol beginning by ``zif_ ``. *'zif' * stands for
133
- *Zend Internal Function *, it is added to the name of your function to prevent symbol name collisions in the compilation
134
- of PHP and its modules. Thus, our ``fahrenheit_to_celsius() `` PHP function uses a C handler named
135
- ``zif_fahrenheit_to_celsius() ``. It is the same for nearly every PHP function. If you look for "zif_var_dump", you'll
132
+ Notice how ``PHP_FUNCTION() `` expanded to a C symbol beginning by ``zif_ ``. *'zif' * stands for
133
+ *Zend Internal Function *, it is added to the name of your function to prevent symbol name collisions in the compilation
134
+ of PHP and its modules. Thus, our ``fahrenheit_to_celsius() `` PHP function uses a C handler named
135
+ ``zif_fahrenheit_to_celsius() ``. It is the same for nearly every PHP function. If you look for "zif_var_dump", you'll
136
136
read the PHP ``var_dump() `` source code function, etc...
137
137
138
138
Declaring function arguments
139
139
****************************
140
140
141
- So far so good, if :doc: `you compile<../build_system/building_extensions> ` the extension and load it into PHP, you can
141
+ So far so good, if :doc: `you compile<../build_system/building_extensions> ` the extension and load it into PHP, you can
142
142
see with reflection that the function is present::
143
143
144
144
> ~/php/bin/php -dextension=pib.so --re pib
@@ -149,10 +149,10 @@ see with reflection that the function is present::
149
149
}
150
150
}
151
151
152
- But its arguments are missing. If we want to publish a ``fahrenheit_to_celsius($fahrenheit) `` function signature, we
152
+ But its arguments are missing. If we want to publish a ``fahrenheit_to_celsius($fahrenheit) `` function signature, we
153
153
need one mandatory argument.
154
154
155
- What you must know is that argument declaration has nothing to do with the function internal work. That means that this
155
+ What you must know is that argument declaration has nothing to do with the function internal work. That means that this
156
156
function could have worked if we would have written its body now. Even with no declared arguments.
157
157
158
158
.. note :: Declaring arguments is not mandatory but highly recommanded. Arguments are used by the reflection API to get
@@ -207,7 +207,7 @@ This bunch of macros allow you to deal with every use-case.
207
207
*'arginfo_[function name]' * pattern.
208
208
209
209
So back to our ``fahrenheit_to_celsius() `` function, we declare a simple return by value function (very classical
210
- use-case), with one argument called ``fahrenheit ``, not passed by reference (here again, very traditionnal ).
210
+ use-case), with one argument called ``fahrenheit ``, not passed by reference (here again, very traditional ).
211
211
212
212
That created the ``arginfo_fahrenheit_to_celsius `` symbol of type ``zend_internal_arg_info[] `` (a vector, or an array,
213
213
that is the same), and we must now use that back into our function declaration to attach it some args::
@@ -231,15 +231,15 @@ Ok. Here is a PHP function like you use it and declare it with the PHP language
231
231
232
232
function fahrenheit_to_celsius($fahrenheit)
233
233
{
234
- return 9/5 * $fahrenheit + 32;
234
+ return 5/9 * ( $fahrenheit - 32) ;
235
235
}
236
236
237
237
This is an easy function so that you understand things.
238
238
Here is what it looks like when programmed in C::
239
239
240
- PHP_FUNCTION(celsius_to_fahrenheit )
240
+ PHP_FUNCTION(fahrenheit_to_celsius )
241
241
{
242
- /* code to go here */
242
+ /* code to go here */
243
243
}
244
244
245
245
Macro expanded, that gives::
@@ -249,7 +249,7 @@ Macro expanded, that gives::
249
249
/* code to go here */
250
250
}
251
251
252
- Take a break and think about the majors differences.
252
+ Take a break and think about the major differences.
253
253
254
254
First strange thing, in C, the function is not expected to return anything. That's a ``void `` declared function, you
255
255
can't here in C return something. But we notice we receive an argument called ``return_value `` of type ``zval * ``,
@@ -289,18 +289,18 @@ float as an integer, and give it to you.
289
289
290
290
Let's see that function::
291
291
292
- PHP_FUNCTION(celsius_to_fahrenheit )
292
+ PHP_FUNCTION(fahrenheit_to_celsius )
293
293
{
294
- double c ;
294
+ double f ;
295
295
296
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &c ) == FAILURE) {
297
- return;
298
- }
296
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &f ) == FAILURE) {
297
+ return;
298
+ }
299
299
300
- /* continue */
300
+ /* continue */
301
301
}
302
302
303
- We want to be given a double on the c variable. We then call ``zend_parse_parameters() ``.
303
+ We want to be given a double on the f variable. We then call ``zend_parse_parameters() ``.
304
304
305
305
The first argument is the number of arguments the runtime have been given. ``ZEND_NUM_ARGS() `` is a macro that tells
306
306
us, we then use it to tell zpp() how many arguments to read.
@@ -330,18 +330,18 @@ So far so good, we received a double. Let's now perform the math operations and
330
330
331
331
static double php_fahrenheit_to_celsius(double f)
332
332
{
333
- return ((double)5/9) * (double)(f - 32);
333
+ return ((double)5/9) * (double)(f - 32);
334
334
}
335
335
336
336
PHP_FUNCTION(fahrenheit_to_celsius)
337
337
{
338
- double f;
338
+ double f;
339
339
340
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &f) == FAILURE) {
341
- return;
342
- }
340
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &f) == FAILURE) {
341
+ return;
342
+ }
343
343
344
- RETURN_DOUBLE(php_fahrenheit_to_celsius(f));
344
+ RETURN_DOUBLE(php_fahrenheit_to_celsius(f));
345
345
}
346
346
347
347
Returning values should be easy to you, as you know :doc: `how zvals work <../internal_types/zvals >`. You must fill-in
@@ -386,10 +386,10 @@ Let's add the opposite function: ``celsius_to_fahrenheit($celsius)``::
386
386
387
387
PHP_FUNCTION(celsius_to_fahrenheit)
388
388
{
389
- double c;
389
+ double c;
390
390
391
391
if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &c) == FAILURE) {
392
- return;
392
+ return;
393
393
}
394
394
395
395
RETURN_DOUBLE(php_celsius_to_fahrenheit(c));
@@ -427,7 +427,7 @@ Now a more complex use case, we show it in PHP before implementing it as a C ext
427
427
That example helps us introduce **constants **.
428
428
429
429
Constants are easy to manage in extensions, like they are in their userland counter-part. Constants are persistent,
430
- most often, that means that they should persist their value accross requests. If you are aware of
430
+ most often, that means that they should persist their value across requests. If you are aware of
431
431
:doc: `the PHP lifecycle<./php_lifecycle> `, you should have guessed that ``MINIT() `` is the right stage to register
432
432
constants against the engine.
433
433
@@ -465,7 +465,7 @@ API and macros are located into
465
465
zend_constants.h> `_.
466
466
467
467
The flags are mixed *OR * operation between ``CONST_CS `` (case-sensitive constant, what we want), and
468
- ``CONST_PERSISTENT `` (a persistent constant, accross requests, what we want as well).
468
+ ``CONST_PERSISTENT `` (a persistent constant, across requests, what we want as well).
469
469
470
470
Now our ``temperature_converter($temp, $type = TEMP_CONVERTER_TO_CELSIUS) `` function in C::
471
471
@@ -515,9 +515,9 @@ Remember to well look at `README.PARAMETER_PARSING_API <https://github.com/php/p
515
515
ef4b2fc283ddaf9bd692015f1db6dad52171c3ce/README.PARAMETER_PARSING_API> `_. It's not a hard API, you must familiarize
516
516
with it.
517
517
518
- We use *"d|l" * as arguments to ``zend_parse_parameters() ``. One double and optionnaly (the pipe *"|" *) one long. Take
519
- care, if the optionnal argument is not provided at runtime (what ``ZEND_NUM_ARGS() `` tells us about, as a reminder),
520
- then the ``&mode `` variable won't be touched by zpp(). That's why we provide a default value of
518
+ We use *"d|l" * as arguments to ``zend_parse_parameters() ``. One double and optionaly (the pipe *"|" *) one long. Take
519
+ care, if the optional argument is not provided at runtime (what ``ZEND_NUM_ARGS() `` tells us about, as a reminder),
520
+ then the ``&mode `` variable won't be touched by zpp(). That's why we provide a default value of
521
521
``TEMP_CONVERTER_TO_CELSIUS `` to that variable.
522
522
523
523
Then we use ``strpprintf() `` to build a :doc: `zend_string <../internal_types/strings/zend_strings >`, and return it into
@@ -590,14 +590,14 @@ remember one reason why we sometimes use the C language over PHP.
590
590
Managing references
591
591
*******************
592
592
593
- Now let's go to play with PHP references. You've learnt from :doc: `the zval chapter <../internal_types/zvals >` that
594
- references are a special trick used into the engine. As a reminder, a reference (by that we mean a ``&$php_reference ``)
593
+ Now let's go to play with PHP references. You've learnt from :doc: `the zval chapter <../internal_types/zvals >` that
594
+ references are a special trick used into the engine. As a reminder, a reference (by that we mean a ``&$php_reference ``)
595
595
is a heap allocated ``zval `` stored into a ``zval `` container. Haha.
596
596
597
- So, it is not very hard to deal with those into PHP functions, as soon as you remember what references are, and what
597
+ So, it is not very hard to deal with those into PHP functions, as soon as you remember what references are, and what
598
598
they're designed to.
599
599
600
- If your function accept a parameter as a reference, you must declare that in arguments signature and be passed a
600
+ If your function accept a parameter as a reference, you must declare that in arguments signature and be passed a
601
601
reference from your ``zend_parse_parameter() `` call. Let's see that like always, with a PHP example first:
602
602
603
603
.. code-block::php
@@ -616,9 +616,9 @@ So now in C, first we must change our ``arg_info``::
616
616
*1 *, passed in the ``ZEND_ARG_INFO() `` macro tells the engine that argument must be passed by reference.
617
617
618
618
Then, when we receive the argument, we use the *"z" * argument type, to tell that we want to be given it as a ``zval * ``.
619
- As we did hint the engine about the fact that it should pass us a reference, we'll be given a reference into that zval,
620
- aka it will be of type ``IS_REFERENCE ``. We just need to dereference it (that is to fetch the zval stored into the
621
- zval), and modify it as-is, as the expected behavior of references is that you must modify the value carried by the
619
+ As we did hint the engine about the fact that it should pass us a reference, we'll be given a reference into that zval,
620
+ aka it will be of type ``IS_REFERENCE ``. We just need to dereference it (that is to fetch the zval stored into the
621
+ zval), and modify it as-is, as the expected behavior of references is that you must modify the value carried by the
622
622
reference::
623
623
624
624
PHP_FUNCTION(fahrenheit_to_celsius)
0 commit comments