diff --git a/tip_dannih_zval/upravlenie_pamyatyu.md b/tip_dannih_zval/upravlenie_pamyatyu.md index 9eba047..eb7b812 100644 --- a/tip_dannih_zval/upravlenie_pamyatyu.md +++ b/tip_dannih_zval/upravlenie_pamyatyu.md @@ -187,7 +187,7 @@ ALLOC_PERMANENT_ZVAL(zv_ptr); zval *zv_ptr = emalloc(sizeof(zval)); ``` -Причина этого в том, что циклическому сборщику необходимо хранить дополнительную информацию в zval-е, по этому структура, которая должна быть создана это не `zval`, а `zval_gc_info`: +Причина этого в том, что циклическому сборщику необходимо хранить дополнительную информацию в zval-е, поэтому структура, которая должна быть создана это не `zval`, а `zval_gc_info`: ```с typedef struct _zval_gc_info { zval z; @@ -200,21 +200,21 @@ typedef struct _zval_gc_info { Макросы `ALLOC_*` создают `zval_gc_info` и инициализируют его дополнительный член, но затем его значение может быть прозрачно использовано так же как и zval (так как эта структура включает в себя zval как свой первый параметр). -После того как память под zval была выделена его нужно инициализировать. Для этой цели есть два макроса. Первый макро `INIT_PZVAL`, он устанавливает `refcount=1` и `is_ref=0` но оставляет значение неинициализированным: +После того как память под zval была выделена его нужно инициализировать. Для этой цели есть два макроса. Первый макрос `INIT_PZVAL`, он устанавливает `refcount=1` и `is_ref=0` но оставляет значение неинициализированным: ```php zval *zv_ptr; ALLOC_ZVAL(zv_ptr); INIT_PZVAL(zv_ptr); /* zv_ptr has garbage type+value here */ ``` -Второй макро `INIT_ZVAL` также устанавливает `refcount=1` и `is_ref=0`, но в дополнение к этому устанавливает тип zval-а в `IS_NULL`: +Второй макрос `INIT_ZVAL` также устанавливает `refcount=1` и `is_ref=0`, но в дополнение к этому устанавливает тип zval-а в `IS_NULL`: ```php zval *zv_ptr; ALLOC_ZVAL(zv_ptr); INIT_ZVAL(*zv_ptr); /* zv_ptr has type=IS_NULL here */ ``` -`INIT_PZVAL()` может принимать указатель `zval*` (по этому буква `P` используется в его названии), а `INIT_ZVAL()` на вход принимает только zval. При передаче zval* во второй макро он должен быть сначала разыменован (dereferenced). +`INIT_PZVAL()` может принимать указатель `zval*` (поэтому буква `P` используется в его названии), а `INIT_ZVAL()` на вход принимает только zval. При передаче zval* во второй макрос он должен быть сначала разыменован (dereferenced). Так как это весьма распространенная задача: сначала выделить память, а затем инициализировать zval, для её решения существуют 2 макроса, которые объединяют оба шага: ```php @@ -240,7 +240,7 @@ Z_SET_REFCOUNT(zv_ptr, 1) /* Установить refcount-у определен Как и с другими `Z_` макросами здесь доступны варианты макросов без суффиксов и с суффиксами `_P` и `_PP`, которые принимают `zval`, `zval*` и `zval**` соответственно. -Чаще всего вы будуте использовать макро `Z_ADDREF_P()`. Вот небольшой пример: +Чаще всего вы будуте использовать макрос `Z_ADDREF_P()`. Вот небольшой пример: ```php zval *zv_ptr; MAKE_STD_ZVAL(zv_ptr); @@ -252,7 +252,7 @@ Z_ADDREF_P(zv_ptr); ``` Этот код дважды вставляет число 42 в массив: сначала с индексом `0`, затем с ключом `num`, таким образом, один zval будет использован в 2 местах. После того как с помощью `MAKE_STD_ZVAL()` выделена память и инициализирован новый zval его значение refcount равно 1. Чтобы использовать тот же zval в 2 местах нужно задать refcount-у значение 2, счетчик увеличивается при помощи `Z_ADDREF_P()`. -Дополнительный макро `Z_DELREF_P()` используется реже, так как обычно недостаточно уменьшить значение счетчика ссылок, но и нужно проверить, что он не равен нули и, при необходимости, удалить zval и освободить память: +Дополнительный макрос `Z_DELREF_P()` используется реже, так как обычно недостаточно уменьшить значение счетчика ссылок, но и нужно проверить, что он не равен нулю и, при необходимости, удалить zval и освободить память: ```c Z_DELREF_P(zv_ptr); if (Z_REFCOUNT_P(zv_ptr) == 0) { @@ -262,13 +262,13 @@ if (Z_REFCOUNT_P(zv_ptr) == 0) { ``` -Макро `zval_dtor()` принимает на вход `zval*` и уничтожает его значение. Если это строка, то будет освобождена область памяти, занимаемая ею, если это массив — будет удалена хештаблица и освобождена память, если это объект или ресурс, то счетчик ссылок будет уменьшен (что также может привести к удалению и освобождению памяти). +Макрос `zval_dtor()` принимает на вход `zval*` и уничтожает его значение. Если это строка, то будет освобождена область памяти, занимаемая ею, если это массив — будет удалена хештаблица и освобождена память, если это объект или ресурс, то счетчик ссылок будет уменьшен (что также может привести к удалению и освобождению памяти). -Но вместо того чтобы писать код подобный приведенному выше и самостоятельно проверять значение refcount вам следует использовать макро `zval_ptr_dtor()`: +Но вместо того чтобы писать код подобный приведенному выше и самостоятельно проверять значение refcount вам следует использовать макрос `zval_ptr_dtor()`: ```c zval_ptr_dtor(&zv_ptr); ``` -Он принимает на вход `zval**` (по историческим причинам он также может принимать и `zval*`), уменьшает счетчик ссылок и проверяет должен ли этот zval быть уничтожен. Но в отличии от кода написанного вручную этот макро умеет находить циклические ссылки. Вот соответствующая часть его реализации: +Он принимает на вход `zval**` (по историческим причинам он также может принимать и `zval*`), уменьшает счетчик ссылок и проверяет должен ли этот zval быть уничтожен. Но в отличии от кода написанного вручную этот макрос умеет находить циклические ссылки. Вот соответствующая часть его реализации: ```c static zend_always_inline void i_zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC TSRMLS_DC) { @@ -286,13 +286,13 @@ static zend_always_inline void i_zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC } } ``` -`Z_DELREF_P()` возвращает новое значение refcount после его уменьшения, по этому код `!Z_DELREF_P(zval_ptr)` аналогичен коду, который сначала удаляет ссылку `Z_DELREF_P(zval_ptr)`, а затем проверяет значение счетчика `Z_REFCOUNT_P(zval_ptr) == 0`. +`Z_DELREF_P()` возвращает новое значение refcount после его уменьшения, поэтому код `!Z_DELREF_P(zval_ptr)` аналогичен коду, который сначала удаляет ссылку `Z_DELREF_P(zval_ptr)`, а затем проверяет значение счетчика `Z_REFCOUNT_P(zval_ptr) == 0`. -Кроме таких ожидаемых действий как `zval_dtor()` и `efree()` этот макро вызывает два `GC_*` макроса, которые обрабатывают циклические ссылки, и проверяет утверждение что не освобождена память переменной `&EG(uninitialized_zval)` (это магический zval используемый движком). +Кроме таких ожидаемых действий как `zval_dtor()` и `efree()` этот макрос вызывает два `GC_*` макроса, которые обрабатывают циклические ссылки, и проверяет утверждение что не освобождена память переменной `&EG(uninitialized_zval)` (это магический zval используемый движком). Кроме того, этот код устанавливает `is_ref=0` в случае если осталась только одна ссылка на zval. Оставлять в таком случае `is_ref=1` нельзя так как концепция `&`-ссылок в PHP имеет смысл только тогда, когда два и более владельца делят один zval. -Нексколько советов по использованию этих макросов. Вам никогда не следует использовать `Z_DELREF_P()` (он применим только тогда, когда вы можете гарантировать, что zval не должен быть уничтожен и не является корнем циклической ссылки). Вместо него, когда вы хотите уменьшить счетчик ссылок, вам следует использовать макро `zval_ptr_dtor()`. Макро `zval_dtor()` обычно используется с временными, находящимися в стэке zval-ами: +Нексколько советов по использованию этих макросов. Вам никогда не следует использовать `Z_DELREF_P()` (он применим только тогда, когда вы можете гарантировать, что zval не должен быть уничтожен и не является корнем циклической ссылки). Вместо него, когда вы хотите уменьшить счетчик ссылок, вам следует использовать макрос `zval_ptr_dtor()`. Макрос `zval_dtor()` обычно используется с временными, находящимися в стэке zval-ами: ```c zval zv; INIT_ZVAL(zv); @@ -306,7 +306,7 @@ zval_dtor(&zv); ##Копирование zval-ов -Механизм копирования-при-записи позволяет избавиться от большого числа операция копирования zval-ов, но иногда такие операции нужны, например, если вы хотите изменить значение zval-а или переместить его в другое место хранилища. +Механизм копирования-при-записи позволяет избавиться от большого числа операций копирования zval-ов, но иногда такие операции нужны, например, если вы хотите изменить значение zval-а или переместить его в другое место хранилища. PHP предоставляет большое число копирующих макросов для разных ситуаций, самый простой: `ZVAL_COPY_VALUE()`, он просто копирует значение и тип zval-а: ```c @@ -327,20 +327,20 @@ zval_copy_ctor(zv_dest); ``` `zval_copy_ctor() ` сделает полную копию zval-а, i.e. то есть указатель на строку `char*` также будет скопирован. Если это массив, то его `HashTable*` будет скопирован, если это объект или ресурс, то их внутренние счетчики ссылок будут увеличены. -Мы пока не рассмотрели инициализацию счетчика ссылок и флага `is_ref`. Это может быть сделано с испольщованием макро `INIT_PZVAL()` или использованием `MAKE_STD_ZVAL()` вместо `ALLOC_ZVAL()`. Другая альтернатива — использование `INIT_PZVAL_COPY()` вместо `ZVAL_COPY_VALUE()`, который объединяет копирование и инициализацию refcount/is_ref: +Мы пока не рассмотрели инициализацию счетчика ссылок и флага `is_ref`. Это может быть сделано с испольщованием макроса `INIT_PZVAL()` или использованием `MAKE_STD_ZVAL()` вместо `ALLOC_ZVAL()`. Другая альтернатива — использование `INIT_PZVAL_COPY()` вместо `ZVAL_COPY_VALUE()`, который объединяет копирование и инициализацию refcount/is_ref: ```c zval *zv_dest; ALLOC_ZVAL(zv_dest); INIT_PZVAL_COPY(zv_dest, zv_src); zval_copy_ctor(zv_dest); ``` -Так как комбинация `INIT_PZVAL_COPY()` и `zval_copy_ctor()` очень частая, оба они объединены в макро `MAKE_COPY_ZVAL()`: +Так как комбинация `INIT_PZVAL_COPY()` и `zval_copy_ctor()` очень частая, оба они объединены в макрос `MAKE_COPY_ZVAL()`: ```c zval *zv_dest; ALLOC_ZVAL(zv_dest); MAKE_COPY_ZVAL(&zv_src, zv_dest); ``` -Этот макро имеет необычную сигнатуру, в нем изменен порядок аргументов (целевой zval теперь идет вторым аргументом, а не первым) и также требуется чтобы источник передавался как `zval**`. Это всего лишь исторический артефакт и он не имеет какого-то технического смысла. +Этот макрос имеет необычную сигнатуру, в нем изменен порядок аргументов (целевой zval теперь идет вторым аргументом, а не первым) и также требуется чтобы источник передавался как `zval**`. Это всего лишь исторический артефакт и он не имеет какого-то технического смысла. Кроме этих базовых макросов есть несколько более сложных. Один из самых важных — `ZVAL_ZVAL`, он особенно часто используется при возвращении значения из функции. Он имеет такую сигнатуру: ```c @@ -353,7 +353,7 @@ ZVAL_ZVAL(zv_dest, zv_src, 0, 0); /* equivalent to: */ ZVAL_COPY_VALUE(zv_dest, zv_src) ``` -В этом случае `ZVAL_ZVAL()` ведет себя также как вызов `ZVAL_COPY_VALUE()`. Так что использование этого макро с аргументами 0, 0 не имеет никакого смысла. Гораздо полезнее вариант `copy=1`, `dtor=0`: +В этом случае `ZVAL_ZVAL()` ведет себя также как вызов `ZVAL_COPY_VALUE()`. Так что использование этого макроса с аргументами 0, 0 не имеет никакого смысла. Гораздо полезнее вариант `copy=1`, `dtor=0`: ```c ZVAL_ZVAL(zv_dest, zv_src, 1, 0); /* equivalent to: */ @@ -397,7 +397,7 @@ zval_ptr_dtor(&zv_src); } while (0) ``` -Если счетчик ссылок равен 1, то `SEPARATE_ZVAL()` не будет делать ничего. Если refcount больше единицы, то этот макро удалит одну ссылку из старого zval-а, скопирует её в новый zval и присвоит этот новый zval указателю `*ppzv`. Учтите, что макро принимает на вход `zval**` и модифицирует `zval*` на который он указывает. +Если счетчик ссылок равен 1, то `SEPARATE_ZVAL()` не будет делать ничего. Если refcount больше единицы, то этот макрос удалит одну ссылку из старого zval-а, скопирует её в новый zval и присвоит этот новый zval указателю `*ppzv`. Учтите, что макрос принимает на вход `zval**` и модифицирует `zval*` на который он указывает. Как это может пригодиться на практике? Предположим, вы хотите модифицировать элемент массива, например, $array[42]. Чтобы сделать это, сначала вам нужно извлечь из `zval**` указатель на хранящееся значение `zval*`. Учитывая подсчет ссылок вы не можете модифицировать его напрямую (так как он может быть совместно использован в нескольких местах), поэтому его сначала нужно разделить. При разделении в случае если refcount равен единице останется старое значение, иначе — будет осуществлено копирование. Во втором случае новый zval будет присвоен `*ppzv`, который в этом случае станет хранилищем массива. @@ -413,7 +413,7 @@ Z_UNSET_ISREF_P(zv_ptr) /* Set is_ref=0 */ Z_SET_ISREF_TO_P(zv_ptr, 1) /* Same as Z_SET_ISREF_P(zv_ptr) */ Z_SET_ISREF_TO_P(zv_ptr, 0) /* Same as Z_UNSET_ISREF_P(zv_ptr) */ ``` -Эти макросы также доступны в вариантах без суффикса и с суффиксами `_P` и `_PP` для обработки `zval*` и `zval**` соответственно. Кроме того, существует еще старый макро `PZVAL_IS_REF()`, являющийся синонимом `Z_ISREF_P()`. +Эти макросы также доступны в вариантах без суффикса и с суффиксами `_P` и `_PP` для обработки `zval*` и `zval**` соответственно. Кроме того, существует еще старый макрос `PZVAL_IS_REF()`, являющийся синонимом `Z_ISREF_P()`. Используя их PHP предоставляет еще два варианта `SEPARATE_ZVAL()`: ```c @@ -428,9 +428,9 @@ Z_SET_ISREF_TO_P(zv_ptr, 0) /* Same as Z_UNSET_ISREF_P(zv_ptr) */ Z_SET_ISREF_PP((ppzv)); \ } ``` -Макро `SEPARATE_ZVAL_IF_NOT_REF()` обычно используется когда zval модифицируется в соответствии с копированием-при-записи. `SEPARATE_ZVAL_TO_MAKE_IS_REF()` используется если вы хотите сделать zval ссылкой (например при присвоении по ссылке или передачи ссылки как аргумент функции). Последний макро часто используется движком PHP и реже в коде расширений. +Макрос `SEPARATE_ZVAL_IF_NOT_REF()` обычно используется когда zval модифицируется в соответствии с копированием-при-записи. `SEPARATE_ZVAL_TO_MAKE_IS_REF()` используется если вы хотите сделать zval ссылкой (например при присвоении по ссылке или передачи ссылки как аргумент функции). Последний макрос часто используется движком PHP и реже в коде расширений. -Есть еще один макро в семейсиве `SEPARATE`, который работает не так как другие: +Есть еще один макрос в семейсиве `SEPARATE`, который работает не так как другие: ```c #define SEPARATE_ARG_IF_REF(varptr) \ if (PZVAL_IS_REF(varptr)) { \ @@ -442,6 +442,6 @@ Z_SET_ISREF_TO_P(zv_ptr, 0) /* Same as Z_UNSET_ISREF_P(zv_ptr) */ Z_ADDREF_P(varptr); \ } ``` -Во-первых, этот макро принимает на вход `zval*`, а не `zval**`. По этому макро не сможет изменить zval, который он разделяет. Кроме того этот макро увеличивает refcount за вас, `SEPARATE_ZVAL` этого не делает. +Во-первых, этот макрос принимает на вход `zval*`, а не `zval**`. Поэтому макрос не сможет изменить zval, который он разделяет. Кроме того этот макрос увеличивает refcount за вас, `SEPARATE_ZVAL` этого не делает. -И еще один дополнительный макро `SEPARATE_ZVAL_IF_NO_REF()`. На этот раз разделение происходит если zval **является** ссылкой. Он в основном используется для того, чтобы убедиться, что аргумент переданный в функцию это значение, а не ссылка. +И еще один дополнительный макрос `SEPARATE_ZVAL_IF_NO_REF()`. На этот раз разделение происходит если zval **является** ссылкой. Он в основном используется для того, чтобы убедиться, что аргумент переданный в функцию это значение, а не ссылка.