Наступили выходные, прошел IT.conf_2012…
И снова я тут, и снова мы будем изучать недры PHP, и сегодня мы поговорим о классах, интерфейсах, методах классов, финализированных классах и прочих няшках.
Приступим
Для начала мы объявим список методов для класса Hello, это будут методы __construct () и say ():
PHP_METHOD(Hello, __construct); PHP_METHOD(Hello, say); static const zend_function_entry hello_methods[] = { PHP_ME(Hello, __construct, NULL, ZEND_ACC_PUBLIC) PHP_ME(Hello, say, NULL, ZEND_ACC_PUBLIC) PHP_FE_END };
Тут мы уже вместо макросов PHP_FUNCTION () и PHP_FE () используем PHP_METHOD () и PHP_ME () в таблице функций. Макрос PHP_METHOD () принимает два аргумента: первый это имя класса, а второй — собсвенно имя метода. PHP_ME () принимает те же аргументы, но он имеет третий аргумент — флаги:
- ZEND_ACC_PUBLIC — публичный метод
- ZEND_ACC_PRIVATE — приватный метод
- ZEND_ACC_PROTECTED — protected‐метод
- -------
- ZEND_ACC_STATIC — статический метод
- ZEND_ACC_ABSTRACT — абстрактный метод
- ZEND_ACC_FINAL — финализированный метод
С этим вроде бы все понятно… Перейдем к объявлению классов.
Объявление
Объявление классов происходит в MINIT‐функции (MINIT, MSHUTDOWN — методы вызывающиеся при загрузке модуля, и его выгрузке, о них поговорим потом), и объявление будет примерно таким:
static PHP_MINIT_FUNCTION(hello_world) { zend_class_entry ce; zend_class_entry ie; INIT_CLASS_ENTRY(ce, "Hello", hello_methods); INIT_CLASS_ENTRY(ie, "IWorld", NULL); zend_register_internal_interface(&ie); zend_class_implements(&ce, 1, ie); zend_class_entry *rce = zend_register_internal_class(&ce); rce->ce_flags |= ZEND_ACC_FINAL_CLASS; }
В первых двух строчках мы объявляем две точки входа в класс PHP, ce — для класса, ie — для интерфейса. В следующих двух строчках — мы инициализируем класс Hello с таблицей методов hello_methods, и интерфейс IWorld (да‐да, интерфейсы инициализируются как классы, это какая то хрень, имхо). Далее — мы регистрируем внутренний интерфейс с помощью функции zend_register_internal_interface () которая в качестве аргумента принимает ссылку на точку входа в класс (интерфейс) — &ie. После этого с помощью метода zend_class_implements () мы указываем что &ce (Hello) должен реализовывать интерфейс &ie (IWorld). Данный метод имеет >3 аргументов: первый — ссылка на класс который должен реализовывать методы, второй — количество интерфейсов которые будут реализованы, и остальные аргументы — сами интерфейсы. После того как мы указали что Hello реализует IWorld — можно зарегистрировать сам класс с помощью метода zend_register_internal_class () который в качестве аргумента принимает ссылку на точку входа в класс, и возвращает указатель на нее, с которым дальше необходимо продолжать работать.
После всех этих действий мы можем модифицировать флаги класса, к примеру — сделать класс финализированным. Для этого мы будем использовать указатель который вернула нам функция zend_register_internal_class (), поле структуры _zend_class_entry под названием ce_flags, и собственно сам флаг — ZEND_ACC_FINAL_CLASS.
Изначально у меня не получилось навешать реализацию интерфейса на класс после регистрации самого класса, но чуть подумав немного переписал MINIT‐функцию, и… Можно так:
static PHP_MINIT_FUNCTION(hello_world) { zend_class_entry ce; zend_class_entry ie; INIT_CLASS_ENTRY(ce, "Hello", hello_methods); INIT_CLASS_ENTRY(ie, "IWorld", NULL); zend_class_entry *rie = zend_register_internal_interface(&ie); zend_class_entry *rce = zend_register_internal_class(&ce); zend_class_implements(&*rce, 1, rie); rce->ce_flags |= ZEND_ACC_FINAL_CLASS; }
Кардинально ничего не изменилось, но все же…
После этого мы изменим точку входа в модуль добавив в него MINIT‐функцию:
zend_module_entry hello_world_module_entry = { STANDARD_MODULE_HEADER, "hello_world", my_functions, PHP_MINIT(hello_world), // name of the MINIT function or NULL if not applicable NULL, // name of the MSHUTDOWN function or NULL if not applicable NULL, // name of the RINIT function or NULL if not applicable NULL, // name of the RSHUTDOWN function or NULL if not applicable NULL, // name of the MINFO function or NULL if not applicable NO_VERSION_YET, STANDARD_MODULE_PROPERTIES };
И сделаем простую реализацию методов __construct () и say ():
PHP_METHOD(Hello, __construct) { /* nothing */ } PHP_METHOD(Hello, say) { php_printf("Hello world "); }
После этого собираем модуль и устанавливаем:
make && sudo make install
И проверяем:
popsul@xx:~/projects/php_ext$ php -r "$i = new Hello(); var_dump($i instanceof IWorld);" bool(true) popsul@xx:~/projects/php_ext$ php -r "class X extends Hello {}; $x = new X;" PHP Fatal error: Class X may not inherit from final class (Hello) in Command line code on line 1 popsul@xx:~/projects/php_ext$ php -r "$i = new IWorld();" PHP Fatal error: Cannot instantiate interface IWorld in Command line code on line 1 PHP Stack trace: PHP 1. {main}() Command line code:0
В конечном итоге у нас появился класс Hello, инстанс которого является instanceof IWorld, так же класс Hello является финальным и его невозможно унаследовать, а так же интерфейс у нас действительно интерфейс, и создать его инстанс невозможно.
И конечно же метод say ():
popsul@xx:~/projects/php_ext$ php -r "$i = new Hello(); $i->say();" Hello world
Заключение
Как видим, все работает и ничего сложного :)
Исходник всего этого можно взять тут -> hello_world.c
«Добра вам! Счастья! Здоровья! » © какой‐то клоун.