Как работает PHP Internals TSRMLS_FETCH
макрос делает свою работу?
В соответствии с Руководство по PHP
При разработке расширений ошибки сборки, содержащие «tsrm_ls не определен», или ошибки на этот счет проистекают из того факта, что TSRMLS не определен в текущей области видимости, чтобы исправить это, объявите функцию, принимающую TSRMLS с соответствующим макросом, если прототип рассматриваемая функция не может быть изменена, вы должны вызвать TSRMLS_FETCH в теле функции.
Я это понимаю объявив функцию для принятия TSRMLS с соответствующими макросами означает использование TSRMLS_C, TSRMLS_D, TSRMLS_CC и TSRMLS_DC либо определить вызов функции с дополнительными параметрами / аргументами.
Тем не мение, если прототип рассматриваемой функции не может быть изменен, вы должны вызвать TSRMLS_FETCH в теле функции немного смущает меня Если я посмотрю на php-src оба Вот а также Вот TSRMLS_FETCH
кажется пустой макрос.
Так что возникает вопрос: как TSRMLS_FETCH
даже работа? Что-то еще заполняет этот макрос во время компиляции?
Во-первых, я бы не стал обращать слишком много внимания на то, что говорится в руководстве по внутренним компонентам PHP. Он очень устарел, и есть большая вероятность, что он будет удален из руководства в ближайшем будущем. В настоящее время существует два веб-сайта, посвященных внутренним компонентам PHP: PHPInternalsBook.com а также PHPInternals.net (Я автор контента для последнего). Есть также несколько хороших блогов, чтобы следовать, в том числе Никита а также Жюльена.
TSRM в серии PHP 5.x был довольно агрессивным. При желании получить доступ к любому Zend Globals из функции выбор был либо между извлечением указателя памяти TLS из вызова функции (например, pthread_getspecific
, что было относительно дорого) или распространение указателя памяти TLS через параметры функции (грязный и подверженный ошибкам процесс, но более быстрый способ). TSRMLS_FETCH
упомянутый вами макрос использовался для первого подхода.
В PHP 7.x распространение указателя памяти TLS (через TSRMLS_[D|C]C?
макросы) был удален полностью (хотя их макросы все еще определены для обратной совместимости — они просто ничего не будут делать). Предпочтительный способ получить доступ к TLS TSRM — через статический кеш. Это в основном просто локальная глобальная переменная потока, используемая для хранения текущего указателя памяти TLS.
Вот соответствующие макросы:
#define TSRMLS_CACHE _tsrm_ls_cache // the TLS global variable
#define TSRMLS_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE = NULL; // define it
#define TSRMLS_CACHE_UPDATE() TSRMLS_CACHE = tsrm_get_ls_cache() // update it - i.e. calls pthread_getspecific()
#define TSRMLS_CACHE_RESET() TSRMLS_CACHE = NULL // reset it
Использование вышеупомянутых макросов требует особого внимания для надлежащего обновления статического кэша (обычно во время GINIT
, и иногда RINIT
, фазы расширения). Тем не менее, это более чистый способ предоставления доступа к указателю памяти TLS без беспорядочной передачи его через параметры функции или снижения производительности при его постоянной загрузке (через pthread_getspecific
и тому подобное).
Дополнительное чтение:
Взгляни на старые версии этого файла:
#define TSRMLS_FETCH() void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL) #define TSRMG(id, type, element) (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element) #define TSRMLS_D void ***tsrm_ls #define TSRMLS_DC , TSRMLS_D #define TSRMLS_C tsrm_ls #define TSRMLS_CC , TSRMLS_C
Кажется, в какой-то момент PHP удалил поддержку этих макросов, но оставил их пустыми, чтобы избежать необходимости разделять внешний код на две версии: одну для нового PHP и одну для старого PHP.
Этот кусок кода
#if ZEND_DEBUG
...
#else
#define TSRMLS_FETCH()
...
#endif
Делает следующее:
Если вы не находитесь в режиме отладки, меняйте каждый вызов на макрос TSRMLS_FETCH()
ни с чем.
В этом примере:
#if 0
#define TSRMLS_FETCH() printf("Bla");
#else
#define TSRMLS_FETCH()
#endif
int main(void)
{
TSRMLS_FETCH()
return 0;
}
cpp demo.c
(вывод препроцессора) возвращает:
int main(void)
{
return 0;
}