Реализация классов в PHP5 экстеншене, часть 2, свойства класса и this

Ирокез

бессмертный пони
Команда форума
Партнер клуба
Реализация классов в PHP5 экстеншене, часть 2, свойства класса и this

Как создать простейший класс, без особого функционала, было рассмотрено в предыдущей статье. Сейчас приступим к облагораживанию нашего первого класса, а именно, добавим свойства класса, поработаем с указателем 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);
Тут необходимо учитывать тот факт, что это инициализация - класса, но мы помним, что при создании инстанса класса, создаётся неявный объект this, и те default значения которые мы задаем при объявлении свойств, могут иметь другие значения уже непосредственно при работает с объектом класса. Таким образом если, смотреть PHP код, то выше написанное можно привести к виду:
PHP:
	class CFoo
	{
		public 		$var = "Hello CFoo";
		static public 	$static_var = 10;
	}
Так, но что нам делать, чтобы работать с данными свойствами внутри экстеншена, тут нам приходят на помощь, заботливо предусмотренные функции zend_read_property и zend_update_property_X (что такое X, смотрим выше). Но для начала определим ещё один метод, к примеру set (только не путаем наши методы, с методами __get и __set, о них позже)

Изменения в таблице методов:
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}
};
И непосредственно сам метод, на нем мы остановимся по подробнее. getThis() - макрос, возвращающий объект класса.
Наверно необходимо пояснить, что есть ещё магические макросы 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)
	{
		....
	}
вызывать-же такую функции необходимо 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.
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;
	}
Собственно для полноты картины, хотелось бы и получать значения свойств класса внутри нашего экстеншена, модифицируем немного наш метод get
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 это выглядит так:

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;
	}
}
Далее можно рассмотреть, хранение с классом, произвольных C структур, а также, переопределение создания класса, методов __set, __get, __clone и т.д. Если будет интересно.

Файлы проекта в архиве:


foo 2008.03.07 16.03.rar 6 Кб
 

Redjik

Джедай-мастер
Думаю в избранное надо всю серию перекинуть, я кончено в закладки добавил, но мало ли =)
 

Adelf

Administrator
Команда форума
Ирокез
Причем если бы ты написал правильно, это было бы за гранью.
Минёт? :)
 

Вурдалак

Продвинутый новичок
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:
class CFoo
{
    private $var = 'Hello CFoo';
    public $long_var = 54321;
    public $static_var = 5;
}
 

Ирокез

бессмертный пони
Команда форума
Партнер клуба
По-моему, тут достаточно сильное несоответствие между кодами. Правильнее, наверное, будет что-то типа
PHP:
class CFoo
{
    private $var = 'Hello CFoo';
    public $long_var = 54321;
    public $static_var = 5;
}
Не совсем так, формально переменную PHP класса можно объявить и в методе, в этом случае она будет публичная
PHP:
class Foo {
public function a() { $this-> long_var = 123; }
}
понятное дело это не очень хорошо.... но как-бы да, небольшое несоответствие есть. в окончательном варианте класс правильный
 

Ragazzo

TDD interested
Ирокез
почему не выложить на хабр например?там будет удобнее читать, чем тут по топикам скакать, м?или уже там выкладывал?
 
  • Like
Реакции: Dovg

Ирокез

бессмертный пони
Команда форума
Партнер клуба
Ирокез
почему не выложить на хабр например?там будет удобнее читать, чем тут по топикам скакать, м?или уже там выкладывал?
НЛО написало, что это кросс-пост и забанило, это пару лет назад было, я и забил на хабр
 
Сверху