1
1
PHP's custom printf functions
2
2
=============================
3
3
4
- You all know libc's ``printf() `` and family. This chapter will detail those many clones PHP declares and use, what's
4
+ You all know libc's ``printf() `` and family. This chapter will detail those many clones PHP declares and use, what's
5
5
their goal, why use them and when to use them.
6
6
7
- .. note :: Libc's documentation about ``printf()`` and friends
7
+ .. note :: Libc's documentation about ``printf()`` and friends
8
8
`is located here <https://www.gnu.org/software/libc/manual/html_node/Formatted-Output-Functions.html >`_
9
-
9
+
10
10
You know that those functions are useful, but sometimes don't provide enough functionalities.
11
- Also, you know that
12
- `adding format strings <https://www.gnu.org/software/libc/manual/html_node/Customizing-Printf.html >`_ to ``printf() ``
11
+ Also, you know that
12
+ `adding format strings <https://www.gnu.org/software/libc/manual/html_node/Customizing-Printf.html >`_ to ``printf() ``
13
13
family is not trivial, not portable and security risky.
14
14
15
15
PHP adds its own printf-like functions to replace libc ones and to be used by the internal developer.
16
- They will mainly add new formats, play with :doc: `zend_string<zend_strings> ` instead of
16
+ They will mainly add new formats, play with :doc: `zend_string<zend_strings> ` instead of
17
17
``char * ``, etc... Let's see them together.
18
18
19
- .. warning :: You must master your libc default ``printf()`` formats. Read
19
+ .. warning :: You must master your libc default ``printf()`` formats. Read
20
20
`their documentation here <http://www.cplusplus.com/reference/cstdio/printf/ >`_.
21
-
22
- .. note :: Those functions are added **to replace** libc ones, that means that if you use ``sprintf()`` f.e, that won't
23
- lead to libc's ``sprintf() ``, but to PHP replacement. Except the traditionnal ``printf() ``, everything else
21
+
22
+ .. note :: Those functions are added **to replace** libc ones, that means that if you use ``sprintf()`` f.e, that won't
23
+ lead to libc's ``sprintf() ``, but to PHP replacement. Except the traditional ``printf() ``, everything else
24
24
is replaced.
25
25
26
- Traditionnal use
27
- ****************
26
+ Traditional use
27
+ ***************
28
28
29
- First of all, you should not use ``sprintf() ``, as that function doesn't perform any check and allows many buffer
29
+ First of all, you should not use ``sprintf() ``, as that function doesn't perform any check and allows many buffer
30
30
overflow errors. Please, try to avoid using it.
31
31
32
32
.. warning :: Please try to avoid using ``sprintf()`` as much as possible.
@@ -36,12 +36,12 @@ Then, you have some choice.
36
36
You know your result buffer size
37
37
--------------------------------
38
38
39
- If you know your buffer size, ``snprintf() `` or ``slprintf() `` will do the job for you. There is a difference in what
39
+ If you know your buffer size, ``snprintf() `` or ``slprintf() `` will do the job for you. There is a difference in what
40
40
those functions return, but not in what those functions do.
41
41
42
- They both print according to the formats passed, and they both terminate your buffer by a ``NUL `` byte *'\\ 0' * whatever
43
- happens. However, ``snprintf() `` returns the number of characters that could have been used, whereas ``slprintf() ``
44
- returns the number of characters that have effectively been used, thus enabling to detect too-small buffers and string
42
+ They both print according to the formats passed, and they both terminate your buffer by a ``NUL `` byte *'\\ 0' * whatever
43
+ happens. However, ``snprintf() `` returns the number of characters that could have been used, whereas ``slprintf() ``
44
+ returns the number of characters that have effectively been used, thus enabling to detect too-small buffers and string
45
45
truncation. This, is not counting the final *'\\ 0' *.
46
46
47
47
Here is an example so that you fully understand::
@@ -52,12 +52,12 @@ Here is an example so that you fully understand::
52
52
53
53
r = snprintf(foo, sizeof(foo), "%s", str);
54
54
/* r = 11 here even if only 7 printable chars were written in foo */
55
-
55
+
56
56
/* foo value is now 'H' 'e' 'l' 'l' 'o' ' ' 'w' '\0' */
57
57
58
58
``snprintf() `` is not a good function to use, as it does not allows to detect an eventual string truncation.
59
- As you can see from the example above, "Hello world\\ 0" doesn't fit in an eight-byte buffer, that's obvious, but
60
- ``snprintf() `` still returns you 11, which is ``strlen("Hello world\0") ``. You have no way to detect that the string's
59
+ As you can see from the example above, "Hello world\\ 0" doesn't fit in an eight-byte buffer, that's obvious, but
60
+ ``snprintf() `` still returns you 11, which is ``strlen("Hello world\0") ``. You have no way to detect that the string's
61
61
got truncated.
62
62
63
63
Here is ``slprintf() ``::
@@ -68,10 +68,10 @@ Here is ``slprintf()``::
68
68
69
69
r = slprintf(foo, sizeof(foo), "%s", str);
70
70
/* r = 7 here , because 7 printable chars were written in foo */
71
-
71
+
72
72
/* foo value is now 'H' 'e' 'l' 'l' 'o' ' ' 'w' '\0' */
73
73
74
- With ``slprintf() ``, the result buffer ``foo `` contains the exact same string, but the returned value is now 7. 7 is
74
+ With ``slprintf() ``, the result buffer ``foo `` contains the exact same string, but the returned value is now 7. 7 is
75
75
less than the 11 chars from the *"Hello world" * string, thus you can detect that it got truncated::
76
76
77
77
if (slprintf(foo, sizeof(foo), "%s", str) < strlen(str)) {
@@ -83,7 +83,7 @@ Remember:
83
83
* Those two function always ``NUL `` terminate the string, truncation or not. Result strings are then safe C strings.
84
84
* Only ``slprintf() `` allows to detect a string truncation.
85
85
86
- Those two functions are defined in
86
+ Those two functions are defined in
87
87
`main/snprintf.c <https://github.com/php/php-src/blob/648be8600ff89e1b0e4a4ad25cebad42b53bed6d/main/snprintf.c >`_
88
88
89
89
You don't know your buffer size
@@ -95,106 +95,106 @@ Remember that **you'll have to free** the buffer by yourself !
95
95
Here is an example::
96
96
97
97
#include <time.h>
98
-
98
+
99
99
char *result;
100
100
int r;
101
101
102
102
time_t timestamp = time(NULL);
103
103
104
104
r = spprintf(&result, 0, "Here is the date: %s", asctime(localtime(×tamp)));
105
-
105
+
106
106
/* now use result that contains something like "Here is the date: Thu Jun 15 19:12:51 2017\n" */
107
-
107
+
108
108
efree(result);
109
109
110
- ``spprintf() `` returns the number of characters that've been printed into the result buffer, not counting the final
110
+ ``spprintf() `` returns the number of characters that've been printed into the result buffer, not counting the final
111
111
*'\\ 0' *, hence you know the number of bytes that got allocated for you (minus one).
112
112
113
- Please, note that the allocation is done using ZendMM (request allocation), and should thus be used as part of a
113
+ Please, note that the allocation is done using ZendMM (request allocation), and should thus be used as part of a
114
114
request and freed using ``efree() `` and not ``free() ``.
115
115
116
- .. note :: :doc:`The chapter about Zend Memory Manager <../../memory_management/zend_memory_manager>` (ZendMM) details
116
+ .. note :: :doc:`The chapter about Zend Memory Manager <../../memory_management/zend_memory_manager>` (ZendMM) details
117
117
how dynamic memory is allocated through PHP.
118
118
119
- If you want to limit the buffer size, you pass that limit as the second argument, if you pass *0 *, that means
119
+ If you want to limit the buffer size, you pass that limit as the second argument, if you pass *0 *, that means
120
120
unlimited::
121
121
122
122
#include <time.h>
123
-
123
+
124
124
char *result;
125
125
int r;
126
126
127
127
time_t timestamp = time(NULL);
128
128
129
129
/* Do not print more than 10 bytes || allocate more than 11 bytes */
130
130
r = spprintf(&result, 10, "Here is the date: %s", asctime(localtime(×tamp)));
131
-
131
+
132
132
/* r == 10 here, and 11 bytes were allocated into result */
133
-
133
+
134
134
efree(result);
135
135
136
- .. note :: Whenever possible, try not to use dynamic memory allocations. That impacts performances. If you got the
136
+ .. note :: Whenever possible, try not to use dynamic memory allocations. That impacts performances. If you got the
137
137
choice, go for the static stack allocated buffer.
138
138
139
- ``spprintf() `` is written in
139
+ ``spprintf() `` is written in
140
140
`main/spprintf.c <https://github.com/php/php-src/blob/648be8600ff89e1b0e4a4ad25cebad42b53bed6d/main/spprintf.c >`_.
141
141
142
142
What about printf() ?
143
143
---------------------
144
144
145
- If you need to ``printf() ``, aka to print formatted to the output stream, use ``php_printf() ``. That function
146
- internally uses ``spprintf() ``, and thus performs a dynamic allocation that it frees itself just after having sent it
145
+ If you need to ``printf() ``, aka to print formatted to the output stream, use ``php_printf() ``. That function
146
+ internally uses ``spprintf() ``, and thus performs a dynamic allocation that it frees itself just after having sent it
147
147
to the SAPI output, aka stdout in case of CLI, or the output buffer (CGI buffer f.e) for other SAPIs.
148
148
149
149
Special PHP printf formats
150
150
--------------------------
151
151
152
- Remember that PHP replaces most libc's ``printf() `` functions by its own of its own design. You can have a look at
153
- the argument parsing API which is easy to understand `from reading the source
152
+ Remember that PHP replaces most libc's ``printf() `` functions by its own of its own design. You can have a look at
153
+ the argument parsing API which is easy to understand `from reading the source
154
154
<https://github.com/php/php-src/blob/509f5097ab0b578adc311c720afcea8de266aadd/main/spprintf.c#L203> `_.
155
155
156
156
What that means is that arguments parsing algo has been fully rewritten, and may differ from what you're used to in libc.
157
- F.e, the libc locale is note taken care of in most cases.
157
+ F.e, the libc locale is not taken care of in most cases.
158
158
159
159
Special formats may be used, like *"%I64" * to explicitely print to an int64, or *"%I32" *.
160
160
You can also use *"%Z" * to make a zval printable (according to PHP cast rules to string), that one is a great addition.
161
161
162
162
The formatter will also recognize infinite numbers and print "INF", or "NAN" for not-a-number.
163
163
164
- If you make a mistake, and ask the formatter to print a ``NULL `` pointer, where libc will crash for sure, PHP will
164
+ If you make a mistake, and ask the formatter to print a ``NULL `` pointer, where libc will crash for sure, PHP will
165
165
return *"(null)" * as a result string.
166
166
167
- .. note :: If in a printf you see a magic *"(null)"* appearing, that means you passed a NULL pointer to one of PHP
167
+ .. note :: If in a printf you see a magic *"(null)"* appearing, that means you passed a NULL pointer to one of PHP
168
168
printf family functions.
169
169
170
170
171
171
Printf()ing into zend_strings
172
172
-----------------------------
173
173
174
- As :doc: `zend_string <zend_strings >` are a very common structure into PHP source, you may need to ``printf() `` into a
175
- ``zend_string `` instead of a traditionnal C ``char * ``. For this, use ``strpprintf() ``.
174
+ As :doc: `zend_string <zend_strings >` are a very common structure into PHP source, you may need to ``printf() `` into a
175
+ ``zend_string `` instead of a traditional C ``char * ``. For this, use ``strpprintf() ``.
176
176
177
- Tha API is ``zend_string *strpprintf(size_t max_len, const char *format, ...) `` that means that the ``zend_string `` is
178
- returned to you, and not the number of printed chars as you may expect. You can limit that number though, using the
179
- first parameter (pass 0 to mean infinite); and you must remember that the ``zend_string `` will be allocated using the
177
+ The API is ``zend_string *strpprintf(size_t max_len, const char *format, ...) `` that means that the ``zend_string `` is
178
+ returned to you, and not the number of printed chars as you may expect. You can limit that number though, using the
179
+ first parameter (pass 0 to mean infinite); and you must remember that the ``zend_string `` will be allocated using the
180
180
Zend Memory Manager, and thus bound to the current request.
181
181
182
182
Obviously, the format API is shared with the one seen above.
183
183
184
184
Here is a quick example::
185
185
186
186
zend_string *result;
187
-
187
+
188
188
result = strpprintf(0, "You are using PHP %s", PHP_VERSION);
189
-
189
+
190
190
/* Do something with result */
191
-
191
+
192
192
zend_string_release(result);
193
193
194
194
A note on ``zend_ `` API
195
195
-----------------------
196
196
197
197
You may meet ``zend_spprintf() ``, or ``zend_strpprintf() `` functions. Those are the exact same as the ones seen above.
198
198
199
- They are just here as part of the separation between the Zend Engine and PHP Core, a detail that is not important for
199
+ They are just here as part of the separation between the Zend Engine and PHP Core, a detail that is not important for
200
200
us, as into the source code, everything gets mixed together.
0 commit comments