smart_str API¶
That may seem strange, but the C language offers nearly nothing to play with strings (build, concatenate, shrink, expand, transform, etc…). C is a low level general purpose language one can use to build APIs to deal with more specific tasks, such as string constructions.
Note
Obviously you all got that we talk about ASCII strings, aka bytes. No Unicode in there.
PHP’s smart_str
is an API that will help you build strings and especially concatenate chunks of bytes into strings.
This API seats next to PHP’s special printf() APIs and zend_string to
help with strings management.
smart_str VS smart_string¶
Here are the two structures:
typedef struct {
char *c;
size_t len;
size_t a;
} smart_string;
typedef struct {
zend_string *s;
size_t a;
} smart_str;
Like you can see, one will work with traditional C strings (as char*/size_t
) and the other will make use of the
PHP’s specific zend_string
structure.
We will detail the latter: smart_str
, that works with zend_strings. Both APIs are exactly the
same, simply note that one (the one we’ll detail here) starts by smart_str_**()
and the other by
smart_string_***()
. Don’t confuse !
The smart_str
API is detailed into Zend/zend_smart_str.h (also the .c
file).
Warning
smart_str
is not to be confused with smart_string
.
Basic API usage¶
So far so good, that API is really easy to manage. You basically stack-allocate a smart_str
, and pass its pointer to
smart_str_***()
API functions that manage the embedded zend_string
for you. You build your string, use it, and
then you free it. Nothing very strong in there right ?
The embedded zend_string
will be allocated whether
permanently or request-bound, that depends on the last extended API
parameter you’ll use:
smart_str my_str = {0};
smart_str_appends(&my_str, "Hello, you are using PHP version ");
smart_str_appends(&my_str, PHP_VERSION);
smart_str_appendc(&my_str, '\n');
smart_str_appends(&my_str, "You are using ");
smart_str_append_unsigned(&my_str, zend_hash_num_elements(CG(function_table)));
smart_str_appends(&my_str, " PHP functions");
smart_str_0(&my_str);
/* Use my_str now */
PHPWRITE(ZSTR_VAL(my_str.s), ZSTR_LEN(my_str.s));
/* Don't forget to release/free it */
smart_str_free(&my_str);
We can also use the embedded zend_string
independently of the smart_str
:
smart_str my_str = {0};
smart_str_appends(&my_str, "Hello, you are using PHP version ");
smart_str_appends(&my_str, PHP_VERSION);
zend_string *str = smart_str_extract(my_str);
RETURN_STR(str);
/* We must not free my_str in this case */
smart_str_extract()
returns a pre-allocated empty string if smart_str.s
is NULL
. Otherwise, it adds a trailing NUL byte and trims the allocated
memory to the string size.
We used here the simple API, the extended one ends with _ex()
, and allows you to tell if you want a persistent or
a request-bound allocation for the underlying zend_string
. Example:
smart_str my_str = {0};
smart_str_appends_ex(&my_str, "Hello world", 1); /* 1 means persistent allocation */
Then, depending on what you want to append, you’ll use the right API call. If you append a classical C string, you can
use smart_str_appends(smart_str *dst, const char *src)
. If you make use of a binary string, and thus know its
length, then use smart_str_appendl(smart_str *dst, const char *src, size_t len)
.
The less specific smart_str_append(smart_str *dest, const zend_string *src)
simply appends a zend_string
to
your smart_str
string. And if you come to play with others smart_str
, use
smart_str_append_smart_str(smart_str *dst, const smart_str *src)
to combine them together.
smart_str specific tricks¶
Never forget to finish your string with a call to
smart_str_0()
. That puts a NUL char at the end of the embed string and make it compatible with libc string functions.Never forget to free your string, with
smart_str_free()
, once you’re done with it.Use
smart_str_extract()
to get a standalonezend_string
when you have finished building the string. This takes care of callingsmart_str_0()
, and of optimizing allocations. In this case, callingsmart_str_free()
is not necessary.You can share the standalone
zend_string
later elsewhere playing with its reference counter. Please, visit the zend_string dedicated chapter to know more about it.You can play with
smart_str
allocations. Look atsmart_str_alloc()
and friends.smart_str
is heavily used into PHP’s heart. For example, PHP’s specific printf() functions internally use asmart_str
buffer.smart_str
is definitely an easy structure you need to master.