И снова я тут…
Продолжаем разбираться с интерфейсами и методами, и сегодня мы поговорим о реализации интерфейсных методов в классах. Сегодня мы познакомимся с тем, как реализовать на уровне PHP‐расширения код аналогичный этому:
interface IWorld { public function say(); } class Hello implements IWorld { public function say() { printf("Hello world"); } }
Чтож, приступим…
За основу возьмем предыдущий исходник и модифицируем его.
Для начала объявим точки входа в интерфейс и класс, метод‐реализацию Hello: say, и таблицы методов:
//точки входа в интерфейс и класс static zend_class_entry *iworldEntry; static zend_class_entry *helloEntry; //декларация метода Hello::say PHP_METHOD(Hello, say); //таблица функций для интерфейса IWorld static const zend_function_entry iworld_methods[] = { PHP_ABSTRACT_ME(IWorld, say, NULL) PHP_FE_END }; //таблица функций для класса Hello static const zend_function_entry hello_methods[] = { PHP_ME(Hello, say, NULL, ZEND_ACC_PUBLIC) PHP_FE_END };
Как уже говорилось ранее — интерфейсы в понимании пшп — это просто классы, и из этого вытекает что и методы в интерфейсах такие же как и в классах, то‐есть — абстрактные. Макрос PHP_ABSTRACT_ME отличается от PHP_ME только тем, что в нем не описываются доступность метода (public, private, protected) — по умолчанию оно объявляет метод с флагами ZEND_ACC_PUBLIC | ZEND_ACC_ABSTRACT, но если сильно хочется — можно дергать макрос ZEND_FENTRY напрямую -, но вот только как оно работает — хз, ибо в в него не передается имя класса для которого объявлен метод. Чудесный PHP с чудесной документацией которой нет :)
Далее переходим к модификации MINIT‐функции. В конечном итоге она получилась примерно такой:
static PHP_MINIT_FUNCTION(hello_world) { //инициализируем интерефейс IWorld с таблицей методов iworld_methods zend_class_entry ie; INIT_CLASS_ENTRY(ie, "IWorld", iworld_methods); iworldEntry = zend_register_internal_interface(&ie); //инициализируем класс Hello с таблицей методов hello_methods zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Hello", hello_methods); //делаем класс не абстрактным ce.ce_flags ^= ZEND_ACC_ABSTRACT; helloEntry = zend_register_internal_class(&ce); //реализуем интерфейс IWorld zend_class_implements(&*helloEntry, 1, iworldEntry); }
Как видим, от предыдущего примера отличий кардинальных нету за исключением того, что при инициализации интерфейса мы указываем таблицу методов, и то, что внезапным образом класс который реализует интерфейс становится абстрактным по непонятным мне причинам.
Я изначально попытался не реализовывать метод Hello: say, собрал расширение, запустил, и увидел SEGFAULT… Так что можно сделать вывод: в расширениях PHP нельзя не реализовывать методы, не будет тут fatal error, тут будет SEGFAULT. Но это наверное очевидно все‐таки.
В общем то, все готово, остается написать только саму реализацию метода Hello: say:
PHP_METHOD(Hello, say) { printf("Hello World "); }
Собрать и запустить:
popsul@xx:~/projects/php_ext$ php -r '$a = new Hello(); $a->say(); var_dump($a instanceof IWorld);' Hello World bool(true) popsul@xx:~/projects/php_ext$ php -r 'class A implements IWorld {}' PHP Fatal error: Class A contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (IWorld::say) in Command line code on line 1 PHP Stack trace: PHP 1. {main}() Command line code:0
Задача выполнена!
Вместо заключения
Исходник можно взять тут, и если вас еще интересует эта серия статей — возвращайтесь через пару дней, напишу еще чего‐нибудь :)