antonio's blog

antonio's blog


Блог о всяком разном, связанном с разработкой ПО. Пишу редко, когда есть время и желание.

Anton Dobkin
Author

Share


Tags


Разработка расширений для PHP. Данные и переменные. Часть 1

Anton DobkinAnton Dobkin

PHP - язык c динамической типизацией данных. При динамической типизации данных связывание переменной с ее типом происходит в момент присваивания значения.В переменных PHP можно хранить любые данные, они будут автоматически преобразованы в тот тип, который на данный момент необходим, также в различных участках программы переменной можно присваивать данные различных типов.

Типы данных

В Zend Engine все типы данных представлены структурой zval (Zend Value) которая имеет следующие объявление:

struct _zval_struct {  
    /* Хранимое значение */
    zvalue_value value;
    /* Счетчик ссылок */
    zend_uint refcount__gc;
    /* Тип хранимого значения в value */
    zend_uchar type;
    /* Флаг ссылки на другие данные */
    zend_uchar is_ref__gc;
};

typedef struct _zval_struct zval;  

В элементе value хранится данные (значение переменной), value - объединение, которое объявлено следующим образом:

typedef union _zvalue_value {  
    long lval;  // Элемент для хранения целочисленного значения
    double dval; // Элемент для хранения значения с плавающей точкой
    struct {
        char *val;
        int len;
    } str; // Элемент для хранения строковых данных
    HashTable *ht; // Элемент для хранения хэш-таблиц (массивов)
    zend_object_value obj; // Элемент для хранения объектов
} zvalue_value;

Это объединение позволяет хранить данные следующих типов:

Тип данныхОписание
NULL Данный тип присваивается всем неинициализированным переменным при первом использовании. В пользовательском пространстве данное значение можно присвоить переменной используя константу NULL(null). Данный тип поддерживает специальное неопределенное значение ("non-value"), которое отличается от булевого FALSE и целого 0
BOOLБулева переменная, может иметь одно из двух значений TRUE или FALSE. В пользовательском пространстве булево значение переменной можно присвоить используя константы TRUE и FALSE
LONGЦелочисленный тип. В PHP целочисленные хранятся в переменной типа signed long языка Си. Это позволяет хранить значения в диапазоне от -2147483648 до `+2147483647` (на 32-х битных системах). Если в переменную записывается число выходящие за диапазон, то тип переменной автоматически преобразуется в DOUBLE
DOUBLEЧисло с плавающей запятой. Для хранения данного типа PHP использует тип signed double языка Си. Данный тип позволяет хранить числа в диапазоне от 2.225x10^-308 до 1.798x10^308 (на 32-х битных системах). На разных системах диапазон может отличаться
STRINGТип данных предназначенный для хранения строк. Это наиболее универсальный тип данных в PHP. Для строк выделяется блок памяти достаточный для сохранения всех символов строки и указатель на выделенный участок памяти сохраняется в структуре zvalue_value. Память всегда выделяется на один байт больше требуемой, для символа завершения строки \0. Символ завершения строки всегда добавляется в конец строки, это позволяет безопасно передавать строку для обработки в бинарно не безопасные функции, чтобы в них однозначно можно было определить конец строки. Помимо указателя на строку в структуре также храниться длина строки. Это позволяет безопасно хранить в строках бинарные данные.
ARRAYМассив (вектор). Специальный тип-контейнер для хранения переменных других типов. В отличие от языка C в PHP массив может хранить переменные разных типов. В PHP массив – это комплексный набор данных связанных в структуру HashTable. Каждый элемент данной структуры содержит label и data. В пользовательском пространстве PHP для массивов label - это ассоциативный и числовой индекс элемента массива, а data - это данные (zval) которые с этим индексом связанны
OBJECTОбъект объединяет в себе все свойства ARRAY к которым добавляются методы, магические методы, свойства, область видимости, модификаторы, константы
RESSOURCEДанный тип является универсальным и предназначен для трансляции в пользовательское пространство данных которые не могут быть представлены выше перечисленными типами данных. К таким данным относятся хэндл (handle) подключения к базе данных, хэндл curl и т.д.

Zend Engine имеет в своем составе набор констант с префиксом IS_*, которые содержат числовой код типа данных из вышеперечисленной таблицы:

КонстантаТип данных код которых содержится в константе
IS_NULLNULL
IS_BOLLBOOL
IS_LONGLONG
IS_DOUBLEDOUBLE
IS_STRINGSTRING
IS_ARRAYARRAY
IS_OBJECTOBJECT
IS_RESOURCERESOURCE

Значение вышеперечисленных констант присваивается элементу type структуры zval и определяет какой тип данных храниться в элементе value структуры в данный момент.

Константы объявлены следующим образом:

#define IS_NULL     0
#define IS_LONG     1
#define IS_DOUBLE   2
#define IS_BOOL     3
#define IS_ARRAY    4
#define IS_OBJECT   5
#define IS_STRING   6
#define IS_RESOURCE 7

Пример проверки типа данных хранимых в структуре zval:

void print_type(zval *var)  
{
    switch (Z_TYPE_P(var)) {
        case IS_NULL:
            zend_printf("The variable is NULL");
            break;
        case IS_BOOL:
            zend_printf("The variable is BOOL");
            break;
        case IS_LONG:
            zend_printf("The variable is LONG");
            break;
        case IS_DOUBLE:
            zend_printf("The variable is DOUBLE");
            break;
        case IS_STRING:
            zend_printf("The variable is STRING");
            break;
    }
}

Получение типа данных (переменной)

Для получения типа данных хранимых, в данный момент в структуре zval, Zend Engine предоставляет макросы Z_TYPE_* (Z_TYPE, Z_TYPE_P, Z_TYPE_PP). Макрос Z_TYPE в качестве параметра принимает структуру zval (не косвенное указание).

Суффикс _P в имени макроса Z_TYPE говорит о том, что в качестве параметра макрос принимает указатель на структуру zval (косвенный указатель), суффикс _PP в названии макроса Z_TYPE говорит о том, что в качестве параметра макрос принимает указатель на указатель (косвенный указатель второго уровня)

Конечно не будет ошибкой если для получения типа данных структуры zval обратиться напрямую к элементу type и сравнить его значение с константами семейства IS_:

void print_var_type(zval *var)  
{
    if (var->type == IS_LONG) {
        zend_printf("The variable is of type long");
    } else {
        zend_printf("Inccorrect type of variable");
    }
}

или

void print_var_type(zval *var)  
{
    switch(var->type) {
           case IS_NULL:
                zend_printf("The variable is NULL");
                break;
             ....
           default:
                zend_printf("The variable is of type %d", var->type);
                break;
   }
}

Данный способ проверки типа данных не рекомендуется использовать, так как никто не даст гарантии, что в будущем тип данных будет храниться в структуре zval таким же образом. Использование макросов Z_TYPE дает гарантию сохранения совместимости расширений между версиями PHP.

Выше приведенные примеры должны быть переписаны следующим образом:

void print_var_type(zval *var)  
{
    if (Z_TYPE_P(var) == IS_LONG) {
        zend_printf("The variable is of type long");
    } else {
        zend_printf("Inccorrect type of variable");
    }
}
void print_var_type(zval *var)  
{
  switch(Z_TYPE_P(var)) {
     case IS_NULL:
        zend_printf("The variable is NULL");
        break;
        ....
      default:
        zend_printf("The variable is of type %d", Z_TYPE_P(var->type));
        break;
   }
}
Anton Dobkin
Author

Anton Dobkin

Comments