API smart_str¶
Это может показаться странным, но язык C предлагает практически ничего для работы со строками (построение, конкатенация, уменьшение, расширение, преобразование и т.д…). C — это низкоуровневый язык общего назначения, который можно использовать для построения API, решающих более специфичные задачи, такие как построение строк.
Note
Очевидно, вы все поняли, что мы говорим о строках ASCII, то есть о байтах. Unicode здесь ни при чём.
smart_str в PHP — это API, который поможет вам строить строки и особенно конкатенировать фрагменты байтов в
строки. Этот API располагается рядом со специальными printf()-API PHP и
zend_string, чтобы помочь в управлении строками.
smart_str против smart_string¶
Вот две структуры:
typedef struct {
char *c;
size_t len;
size_t a;
} smart_string;
typedef struct {
zend_string *s;
size_t a;
} smart_str;
Как видите, одна будет работать с традиционными C-строками (как char*/size_t), а другая будет использовать
специфичную для PHP структуру zend_string.
Мы рассмотрим подробнее вторую: smart_str, которая работает с zend_strings. Оба API
совершенно одинаковы, просто заметьте, что один (тот, который мы рассмотрим здесь) начинается с smart_str_**(),
а другой — с smart_string_***(). Не путайте их!
API smart_str подробно описан в Zend/zend_smart_str.h (а также в
соответствующем .c файле).
Warning
smart_str не следует путать с smart_string.
Базовое использование API¶
Пока всё хорошо, этот API действительно легко использовать. Вы в основном выделяете smart_str на стеке и
передаёте указатель на него функциям API smart_str_***(), которые управляют встроенным zend_string за вас.
Вы строите свою строку, используете её, а затем освобождаете. Ничего особенно сложного тут нет, правда?
Встроенный zend_string будет выделен либо
постоянно, либо в рамках запроса, это зависит от последнего
расширенного параметра API, который вы используете:
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);
Мы также можем использовать встроенный zend_string независимо от 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() возвращает предварительно выделенную пустую строку, если smart_str.s равно NULL.
В противном случае она добавляет завершающий байт NUL и обрезает выделенную память до размера строки.
Здесь мы использовали простой API, расширенный заканчивается на _ex() и позволяет указать, нужно ли вам
постоянное или привязанное к запросу выделение памяти для нижележащего zend_string. Пример:
smart_str my_str = {0};
smart_str_appends_ex(&my_str, "Hello world", 1); /* 1 means persistent allocation */
Затем, в зависимости от того, что вы хотите добавить, вы используете нужный вызов API. Если вы добавляете
классическую C-строку, можете использовать smart_str_appends(smart_str *dst, const char *src). Если вы работаете
с бинарной строкой и, таким образом, знаете её длину, используйте
smart_str_appendl(smart_str *dst, const char *src, size_t len).
Менее специфичная smart_str_append(smart_str *dest, const zend_string *src) просто добавляет zend_string в
вашу строку smart_str. А если вы работаете с другими smart_str, используйте
smart_str_append_smart_str(smart_str *dst, const smart_str *src), чтобы объединить их вместе.
Специфичные приёмы smart_str¶
Никогда не забывайте завершать вашу строку вызовом
smart_str_0(). Это ставит символ NUL в конце встроенной строки и делает её совместимой с функциями строк libc.Никогда не забывайте освобождать вашу строку с помощью
smart_str_free(), когда закончили с ней работать.Используйте
smart_str_extract(), чтобы получить самостоятельныйzend_stringпосле завершения построения строки. Это берёт на себя вызовsmart_str_0()и оптимизацию выделений памяти. В этом случае вызыватьsmart_str_free()не нужно.Вы можете позже делиться самостоятельным
zend_stringв других местах, работая с его счётчиком ссылок. Пожалуйста, посетите главу, посвящённую zend_string, чтобы узнать об этом больше.Вы можете поиграть с выделениями памяти
smart_str. Посмотрите наsmart_str_alloc()и похожие функции.smart_strактивно используется в самом сердце PHP. Например, специальные функции printf() PHP внутренне используют буферsmart_str.smart_str— это определённо простая структура, которую вам нужно освоить.