扩展PHP和Kotlin本机。 第一部分,天真

本文讨论了使用Kotlin Native创建PHP扩展的最幼稚和最简单的方法。 我提请您注意不是使用而是使用的事实。

它是一种教程,描述了刺猬与刺猬杂交时出现的问题以及解决方法。 不会有任何启示,但也许有人会派上用场。

所以,如果有兴趣的话,欢迎来猫。

目的是编写带有一个函数`hello($ name)`的扩展,该扩展函数接受一个字符串并输出`Hello,$ name!`。

顾名思义,我们将天真地决定。

  1. 让我们在Kotlin上编写一个函数
  2. 在共享库中编译
  3. 以经典方式(在C中),我们编写了一个扩展,该扩展会将函数调用重定向到该库

看起来很简单,但是耙子已经潜伏在茂密的草丛中:

  1. 有很多在Kotlin中使用C库的示例,但是我找不到足够的东西来使用C中的Kotlin库的功能(嗯,也许我看上去很糟糕,实际上是什么)
  2. kotlinc编译器在首次启动时会加载依赖项。 但是不幸的是我在公司云中的Linux虚拟机。 没有甚至没有理论上的访问互联网的能力。
  3. 缺乏C经验的人很有可能会因此而陷入困境,但是我将告诉您链接程序如何显着诱骗我。

一切都发生在Red Hat Enterprise Linux Server 7.5版上。

走吧


首先,请安装...否,不是Kotlin编译器。 首先,安装JDK。 像那样
然后安装Kotlin编译器。 只需从github下载存档,然后将其解压缩到例如主目录即可。

在理想的世界中,他从一开始就亲自下载了所有依赖项,但是,在没有Internet的情况下,我们按以下步骤进行操作(秘密知识是从JetBrains员工那里获得的)。

  • 我们创建任何简单的Kotlin脚本,以便下一步可以在编译器上添加一些内容
  • 我们启动$ KOTLIN_HOME / bin / kotlinc SimpleScript.kt,稍等一下然后按CTRL + C-这是在主目录中创建一个文件夹结构
  • 我们在具有我们的体系结构的部分中,查看文件$ KOTLIN_HOME / konan / konan.properties,并找到以下项目:

dependencies.linux_x64 = \ clang-llvm-5.0.0-linux-x86-64 \ target-gcc-toolchain-3-linux-x86-64 \ libffi-3.2.1-2-linux-x86-64 

  • 我们转到一个特殊的存储库,并下载以上所有内容。
  • 我们将所有这些添加到〜/ .konan / cache中(我提醒您Linux主目录是波浪号)

现在,在第一次开始时,编译器将使用这些发行版,并且不会进入Internet。

请注意,依赖项的大小不是很小,安装后,我的主目录变得重3.4 GB。 对于那些需要单独完成作业的人来说,这很关键。

之后,使用标准的软件包管理器,安装php和相应的php-devel。
至此,准备措施已经结束。

由于不值得讨论编写PHP扩展,因此我们将以尽可能短的代码来解决。

让我们从Kotlin开始


hellokt.kt

 fun kt_print(string:String){ println("Hello, $string!!!") } 

在各种手册和教程中,kotlinc或konanc被用作编译器,这有些令人困惑。 所以-这是同一回事。 这是证明:


编译中

 # $KOTLINC_HOME/kotlinc -opt ./hellokt.kt -o hellokt -produce dynamic 

使用-opt 开关,库较小。 -produce dynamic告诉编译器为当前平台做一个共享库

执行后,我们将有两个文件:libhellokt.so和hellokt_api.h。 库和头文件。 请注意,前缀和后缀是自动生成的,我们不能(可能)影响它们。

在我们的例子中,我们得到了这样的头文件。

 #ifndef KONAN_HELLOKT_H #define KONAN_HELLOKT_H #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus typedef bool hellokt_KBoolean; #else typedef _Bool hellokt_KBoolean; #endif typedef char hellokt_KByte; typedef unsigned short hellokt_KChar; typedef short hellokt_KShort; typedef int hellokt_KInt; typedef long long hellokt_KLong; typedef float hellokt_KFloat; typedef double hellokt_KDouble; typedef void* hellokt_KNativePtr; struct hellokt_KType; typedef struct hellokt_KType hellokt_KType; typedef struct { /* Service functions. */ void (*DisposeStablePointer)(hellokt_KNativePtr ptr); void (*DisposeString)(const char* string); hellokt_KBoolean (*IsInstance)(hellokt_KNativePtr ref, const hellokt_KType* type); /* User functions. */ struct { struct { void (*kt_print)(const char* string); } root; } kotlin; } hellokt_ExportedSymbols; extern hellokt_ExportedSymbols* hellokt_symbols(void); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* KONAN_HELLOKT_H */ 

访问我们的kt_print函数将以这种方式进行。

 hellokt_symbols()->kotlin.root.kt_print(char *); 

我将在下面告诉您有关类和包的信息-有细微差别。

库已准备就绪,转到C


config.m4如何创建

 PHP_ARG_ENABLE(hello, whether to enable hello support,[ --enable-hello Enable hello support]) if test "$PHP_HELLO" != "no"; then PHP_ADD_LIBRARY_WITH_PATH(hellokt, /path/to/compiled/library, HELLO_SHARED_LIBADD) PHP_NEW_EXTENSION(hello, hello.c, $ext_shared) PHP_SUBST(HELLO_SHARED_LIBADD) fi 

第一行是必需的!
除了几个注释之外,在上面的示例config.m4中看到的第一件事是使用PHP_ARG_WITH()和PHP_ARG_ENABLE()的三行代码。
...
每个扩展都应该至少提供一个扩展名,以便用户选择是否将扩展构建到PHP中。

请注意,在PHP_ADD_LIBRARY_WITH_PATH中,第一个参数是库的名称。 不是libhellokt,即hellokt。 我杀死了两个小时,直到发现ld找不到图书馆的原因。 (在这里,他是有关链接程序欺负的应许故事)。

现在,实际上,扩展代码本身

你好

 #include "php.h" //    #include "hellokt_api.h" #define PHP_MY_EXTENSION_VERSION "1.0" #define PHP_MY_EXTENSION_EXTNAME "hello" PHP_FUNCTION(hello); static zend_function_entry hello_functions[] = { PHP_FE(hello, NULL) }; zend_module_entry hello_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif PHP_MY_EXTENSION_EXTNAME, hello_functions, NULL, NULL, NULL, NULL, NULL, #if ZEND_MODULE_API_NO >= 20010901 PHP_MY_EXTENSION_VERSION, #endif STANDARD_MODULE_PROPERTIES }; ZEND_GET_MODULE(hello) PHP_FUNCTION(hello) { char * name; size_t name_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { RETURN_NULL(); } hellokt_symbols()->kotlin.root.kt_print(name); //   efree(name); } 

有很多关于编写PHP扩展的文章,并且提供了文档,因此我将只限于使用zend_parse_parameters的链接-它将就位。

好吧,那么一切都在滚动的道路上:

 # $PHP_PATH/phpize # ./configure --with-php-config=$PHP_PATH/php-config # make # $PHP_PATH/php -dextension=./modules/hello.so -r "echo hello('World');" Hello, World!!! 

关于类和包的奖金


假设我们想进行风水测试并屏蔽此代码。

 package hello.kt; public class HelloKt { fun kt_print(string: String) { println("Hello, $string!!!") } } 

这里没有办法进行常规方法调用-您首先必须创建一个类并将其第一个参数传递给被调用的函数。

 hellokt_kref_hello_kt_HelloKt helloKt = { 0 }; if(!helloKt.pinned){ helloKt = hellokt_symbols()->kotlin.root.hello.kt.HelloKt.HelloKt(); } hellokt_symbols()->kotlin.root.hello.kt.HelloKt.kt_print(helloKt, name); 

接下来是什么? 拒绝共享库,最大程度地减少了对C的使用,这简直不是在开玩笑,微型框架就是这样。 祝我好运! :)

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


All Articles