Реализация классов в PHP5 экстеншене, часть 2, свойства класса и this
Как создать простейший класс, без особого функционала, было рассмотрено в предыдущей статье. Сейчас приступим к облагораживанию нашего первого класса, а именно, добавим свойства класса, поработаем с указателем this, ну и с сопутствующими вещами.
Я думаю, понятно, что создавать класс и не использовать в нем свойств (properties), не имеет особого смысла, поэтому расширим функционал нашего класса, добавив в него, несколько свойств и несколько методов.
Добавление новых свойств в класс, осуществляется вызовом функций zend_declare_property_X, где X, может быть string, long, null и т.д. данные функции определены в заголовочном файле zend_api.h, к примеру определим два свойства класса
Тут необходимо учитывать тот факт, что это инициализация - класса, но мы помним, что при создании инстанса класса, создаётся неявный объект this, и те default значения которые мы задаем при объявлении свойств, могут иметь другие значения уже непосредственно при работает с объектом класса. Таким образом если, смотреть PHP код, то выше написанное можно привести к виду:
Так, но что нам делать, чтобы работать с данными свойствами внутри экстеншена, тут нам приходят на помощь, заботливо предусмотренные функции zend_read_property и zend_update_property_X (что такое X, смотрим выше). Но для начала определим ещё один метод, к примеру set (только не путаем наши методы, с методами __get и __set, о них позже)
Изменения в таблице методов:
И непосредственно сам метод, на нем мы остановимся по подробнее. getThis() - макрос, возвращающий объект класса.
Наверно необходимо пояснить, что есть ещё магические макросы TSRMLS_CC и TSRMLS_DC, по сути в них передаётся все состояние PHP (точки входов классов, глобальные таблицы переменных, состояние компилятора, экзекутора и т.д. т.е. практически все, чем оперирует ZE при выполнении скрипта). Обращаться к внутренним объектам, нужно с помощью макросов EG(имя свойства) - executor globals, CG(имя свойства) - compiler globals, и т.д. Зачем надо этот макрос, да правильно для поточной безопасности параметров TSRM (Thread Safety, чего-то там
)
PHP_METHOD - в неявном виде определяет макрос TSRMLS_DC, поэтому нет необходимости в его явном определении, однако, если вдруг вам задумается определить какую-нибудь произвольную функцию, в которой вы хотите обращаться к внутренним объектам ZE, то необходимо объявлять функции следующим способом:
вызывать-же такую функции необходимо my_function("Test" TSRMLS_CC). Есть два макроса (TSRMLS_CC,TSRMLS_DC и TSRMLS_C,TSRMLS_D), до меня не сразу дошло зачем дублировать, но после мучительной минутной паузы оказалось все просто, экстеншен может быть скомпилирован как tsrm так и нет, если не использовать макрос или использовать макрос TSRMLS_D|TSRMLS_С, то функцию необходимо определять static void my_function(char* MyVar , TSRMLS_D) и вот эта ',' портит все, вам в зависимости от типа компиляции php необходимо будет убирать ',' или ставить.
Вернёмся к нашему методу. Опуская объявление переменных, обращаем внимание на функцию zend_parse_parameters - смысл её понятен из названия, она парсит параметры функции, "sl" - в данном случае говорит о том, что функция должна быть вызвана с двумя обязательными параметрами (строковым - "s" и целым - "l"). Далее мы возвращаемся к волшебным функциям zend_update_property_X, ни чем особо не отличающимся,от zend_declare_property_Х за исключение второго параметра, в которым мы передаём This, того объекта, свойства которого мы хотим изменить, первый параметр, это наш zend_class_entry.
Собственно для полноты картины, хотелось бы и получать значения свойств класса внутри нашего экстеншена, модифицируем немного наш метод get
Ну вот, как пользоваться и определять свойства класса, можно считать разобрали. Откомпилируем и подключим эекстеншен. Небольшой тестовый пример покажет чего мы добились
Если рассмотреть, то что мы сделали, то в PHP это выглядит так:
Далее можно рассмотреть, хранение с классом, произвольных C структур, а также, переопределение создания класса, методов __set, __get, __clone и т.д. Если будет интересно.
Файлы проекта в архиве:
foo 2008.03.07 16.03.rar 6 Кб
Как создать простейший класс, без особого функционала, было рассмотрено в предыдущей статье. Сейчас приступим к облагораживанию нашего первого класса, а именно, добавим свойства класса, поработаем с указателем this, ну и с сопутствующими вещами.
Я думаю, понятно, что создавать класс и не использовать в нем свойств (properties), не имеет особого смысла, поэтому расширим функционал нашего класса, добавив в него, несколько свойств и несколько методов.
Добавление новых свойств в класс, осуществляется вызовом функций zend_declare_property_X, где X, может быть string, long, null и т.д. данные функции определены в заголовочном файле zend_api.h, к примеру определим два свойства класса
PHP:
zend_declare_property_string(g_ClassEntry,"var",3,"Hello CFoo",ZEND_ACC_PRIVATE TSRMLS_CC);
zend_declare_property_long(g_ClassEntry,"long_var",8,54321,ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_long(g_ClassEntry,"static_var",10,5,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC TSRMLS_CC);
PHP:
class CFoo
{
public $var = "Hello CFoo";
static public $static_var = 10;
}
Изменения в таблице методов:
PHP:
zend_function_entry class_methods[] = {
PHP_ME(CFoo,__construct,NULL, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
PHP_ME(CFoo,get,NULL, 0)
PHP_ME(CFoo,set,NULL, 0)
{NULL, NULL, NULL}
};
Наверно необходимо пояснить, что есть ещё магические макросы TSRMLS_CC и TSRMLS_DC, по сути в них передаётся все состояние PHP (точки входов классов, глобальные таблицы переменных, состояние компилятора, экзекутора и т.д. т.е. практически все, чем оперирует ZE при выполнении скрипта). Обращаться к внутренним объектам, нужно с помощью макросов EG(имя свойства) - executor globals, CG(имя свойства) - compiler globals, и т.д. Зачем надо этот макрос, да правильно для поточной безопасности параметров TSRM (Thread Safety, чего-то там

PHP_METHOD - в неявном виде определяет макрос TSRMLS_DC, поэтому нет необходимости в его явном определении, однако, если вдруг вам задумается определить какую-нибудь произвольную функцию, в которой вы хотите обращаться к внутренним объектам ZE, то необходимо объявлять функции следующим способом:
PHP:
static void my_function(char* MyVar TSRMLS_DC)
{
....
}
Вернёмся к нашему методу. Опуская объявление переменных, обращаем внимание на функцию zend_parse_parameters - смысл её понятен из названия, она парсит параметры функции, "sl" - в данном случае говорит о том, что функция должна быть вызвана с двумя обязательными параметрами (строковым - "s" и целым - "l"). Далее мы возвращаемся к волшебным функциям zend_update_property_X, ни чем особо не отличающимся,от zend_declare_property_Х за исключение второго параметра, в которым мы передаём This, того объекта, свойства которого мы хотим изменить, первый параметр, это наш zend_class_entry.
PHP:
PHP_METHOD(CFoo,set)
{
char* value = NULL;
int value_len = 0;
long long_value = 0;
zval* This = getThis();
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"sl",&value,&value_len,&long_value) == SUCCESS)
{
zend_update_property_string(Z_OBJCE_P(This),This,"var",3,value TSRMLS_CC);
zend_update_property_long(Z_OBJCE_P(This),This,"long_var",8,long_value TSRMLS_CC);
RETURN_BOOL(1);
}
RETURN_FALSE;
}
PHP:
PHP_METHOD(CFoo,get)
{
zval* var = NULL;
zval* This = getThis();
var = zend_read_property(Z_OBJCE_P(This),This,"var",3,1 TSRMLS_CC);
RETURN_ZVAL(var,NULL,NULL);
}

PHP:
<?php
$c = new CFoo;
$c->set("Set Value",12345);
echo $c->get() . " - ".CFoo::$static_var . " - " . $c->long_var;
?>
PHP:
class CFoo
{
private $var = "Hello CFoo";
public $long_var = 54321;
static public $static_var = 5;
public function __construct() {;}
public function get()
{
return $this->var;
}
public function set($Value,$Long_value)
{
$this->var = $Value;
$this->long_var = $Long_value;
return TRUE;
}
}
Файлы проекта в архиве:
foo 2008.03.07 16.03.rar 6 Кб