扩展PHP和Kotlin本机。 第二部分,自觉

第一部分摘要:


  1. 工具的安装和配置。
  2. 在Kotlin Native中编写helloWorld()函数,并将其编译为共享库。
  3. 从PHP扩展C代码访问此函数。


在本文中,我将讨论创建用于编写PHP扩展而无需接触C的工具(仅在K / N上)。

谁在乎-欢迎猫。
读的人不感兴趣,但只想看看-欢迎来到github

首先,我要对Nikolai Igotti表示感谢,感谢他们在Kotlin Native闲暇频道中为我的问题提供了快速,高质量的答案,有时是愚蠢而幼稚的。

立即保留我不假装创建完整框架的保留(也许以后),因此,我们将以这种方式限制功能:

  1. 创建可以从PHP代码调用的函数。
  2. 常量的定义。
  3. 我们仅使用简单的PHP类型进行操作: stringbooleanintfloat (和null )。 没有数组,对象,资源,按引用传输等。 -我会在下面告诉你原因。

开发PHP扩展的细节是几乎所有实用程序代码以及与zend engine通信zend engine写在宏上。 一方面,它极大地方便了用C编写扩展,另一方面,它使得在所有其他编程语言中很难做到这一点。

有了这样的介绍,最明显的解决方案是使用coderinarium。 并且,考虑到Kotlin为创建DSL提供了非常广泛的可能性,描述扩展结构的过程可以变得简单而直观。

为了以经典方式(phpize,configure,make)构建扩展库,至少需要两个工件-C中的扩展代码和config.m4文件。

使用场景将如下所示:

  1. 我们使用DSL描述扩展。
  2. 我们在K / N上编写函数的实现。
  3. 根据描述,我们生成extension.cconfig.m4extencion.c的代码将处理函数调用的普通代理。
  4. 根据描述,我们生成了constants.kt ,这使我们可以在K / N的函数中使用给定的常量。
  5. 我们将K / N代码编译到一个静态库中。
  6. 将它们放在一起并编译成扩展库。

走吧


要实施我们的计划,我们需要获得以下结构:

 (, ) 1 2 ... 1(,  ) 1 2 ... 1 ... 

我认为与Kotlin合作的任何人编写适当的DSL并不困难。 对于其余的文章,有很多专门的文章比在本文中尝试做的更详尽。

下一步是将该DSL转换为必要的工件。 为此,我们将在相同的K / N上编写一个生成器,从它和DSL编译一个可执行文件并运行它-瞧! 解决方案不是最优雅的,但是我还没有想到更简单,更可靠的解决方案。

好了,那么一切都很简单-我们用函数编译该库,并以常规方式(包括在那里)收集扩展。

为了易于使用,所有具有编译功能的魔术都隐藏在Shell脚本中。

这是怎么回事


DSL上描述的简单扩展的描述示例和生成的代码( 为了更好地理解,所有参数都以命名形式给出 )。

konfigure.kt-DSL扩展

 import php.extension.dsl.* val dsl = extension(name = "example", version = "0.1") { constant(name = "HELLO_EN", value = "Hello") constant(name = "HELLO_ES", value = "Hola") constant(name = "HELLO_RU", value = "") function(name = "hello", returnType = ArgumentType.STRING) { arg(type = ArgumentType.STRING, name = "name") arg(type = ArgumentType.STRING, name = "lang", optional = true) } } fun main(args: Array<String>) = dsl.make() 

example.kt-实现功能

 fun hello(name: String, lang: String?) = "${if (lang ?: "" == "") HELLO_EN else lang} $name!!!\n" 

注意确定lang值的奇怪算法。 这是由于当前版本的K / N中存在一个错误,该错误不允许将“ char *”类型的未初始化变量作为C的参数来传递。-您必须传递一个空字符串。

config.m4-生成的文件

 PHP_ARG_ENABLE(example, whether to enable example support,[ --enable-example Enable hello support]) if test "$PHP_EXAMPLE" != "no"; then PHP_ADD_INCLUDE(.) PHP_ADD_LIBRARY_WITH_PATH(example_kt, ., EXAMPLE_SHARED_LIBADD) PHP_SUBST(EXAMPLE_SHARED_LIBADD) PHP_NEW_EXTENSION(example, example.c, $ext_shared) fi 

example_genic_constants.kt-使用Kotlin常量生成的文件

 const val HELLO_EN = "Hello" const val HELLO_ES = "Hola" const val HELLO_RU = "" 

example.c-使用C代码生成的文件

 #include "php.h" #include "example_kt_api.h" PHP_FUNCTION(hello); static zend_function_entry example_functions[] = { PHP_FE(hello, NULL) {NULL,NULL,NULL} }; PHP_MINIT_FUNCTION(example); zend_module_entry example_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "example", example_functions, PHP_MINIT(example), NULL, NULL, NULL, NULL, #if ZEND_MODULE_API_NO >= 20010901 "0.1", #endif STANDARD_MODULE_PROPERTIES }; ZEND_GET_MODULE(example) PHP_MINIT_FUNCTION(example) { REGISTER_STRING_CONSTANT("HELLO_EN", "Hello", CONST_CS|CONST_PERSISTENT); REGISTER_STRING_CONSTANT("HELLO_ES", "Hola", CONST_CS|CONST_PERSISTENT); REGISTER_STRING_CONSTANT("HELLO_RU", "", CONST_CS|CONST_PERSISTENT); return SUCCESS; } PHP_FUNCTION(hello){ //-,      char*  K/N char *name = malloc(1); name[0] = '\0'; size_t name_len=0; char *lang = malloc(1); lang[0] = '\0'; size_t lang_len=0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &name, &name_len, &lang, &lang_len) == FAILURE) { return; } RETURN_STRING(example_kt_symbols()->kotlin.root.hello(name, lang)); } 

关于为什么只有简单类型


因为它们一对一映射到Kotlin本机类型。 迄今为止,该项目实际上仅在一个方向上实现互操作,即 从C调用K / N函数。 要处理诸如zend_valuezend_class_entryzend_fcall_info类的复杂类型,您需要将相应的结构导入K / N项目中,并编写适当的包装来使用它们,并且还有所有的宏等。

加焦油的瓶子。 附有汤匙。


  1. Kotlin本机文档。 似乎在那里,但是...到目前为止,最可靠的学习方法是阅读原始资料。
  2. 产生的扩展名的大小并不小。 对于上面的示例,获得了大约500KB的库。
  3. 您甚至不必希望用K / N编写的扩展最终会出现在PHP扩展库中。 可以说,该产品仅供内部使用。

接下来是什么


实现“关于为什么只有简单类型”一节中描述的所有内容。

再一次, 指向存储库链接

谢谢您的关注,祝我好运:)

Source: https://habr.com/ru/post/zh-CN416719/


All Articles