Реализация классов в 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 Кб
 
Думаю в избранное надо всю серию перекинуть, я кончено в закладки добавил, но мало ли =)
 
Ирокез
Причем если бы ты написал правильно, это было бы за гранью.
Минёт? :)
 
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; }
}

понятное дело это не очень хорошо.... но как-бы да, небольшое несоответствие есть. в окончательном варианте класс правильный
 
Ирокез
почему не выложить на хабр например?там будет удобнее читать, чем тут по топикам скакать, м?или уже там выкладывал?
 
  • Like
Реакции: Dovg
Ирокез
почему не выложить на хабр например?там будет удобнее читать, чем тут по топикам скакать, м?или уже там выкладывал?

НЛО написало, что это кросс-пост и забанило, это пару лет назад было, я и забил на хабр
 
Сверху