
This article describes the most naive and simplest approach to creating a PHP extension using Kotlin Native. I pay attention that not
on , but
with use .
It is rather a tutorial describing the problems that arose when crossing the grass with the hedgehog and the ways to solve them. Revelations will not, but maybe someone will come in handy.
So, if interested, then welcome under cat.
The task is to write an extension with one function `hello ($ name)`, which accepts a string and prints `Hello, $ name!`.
We will decide naively, as the name says.
- Let's write a function on Kotlin
- Let's compile in shared library
- In the classical way (in C) we will write an extension that will redirect the function call to this library.
It seems simple, but a rake has already lurked in the thick grass:
- There are a number of examples of using C-libraries in Kotlin, but I couldn’t find anything adequate about how to use the functions of the Kotlin-library in C (well, maybe I was looking bad for something)
- The kotlinc compiler on the first run loads dependencies. But bad luck - my Linux virtualka in the corporate cloud. Without even a theoretical opportunity to reach the Internet.
- These rakes are most likely from the lack of experience in C, but still I will tell you how I was notably troll linker.
Everything happened on Red Hat Enterprise Linux Server release 7.5.Go!
First, install ... No, not the Kotlin compiler. First, install the JDK. So like this.
Then install the Kotlin compiler. Simply download the
archive from the githaba and unzip it, for example, to your home directory.
In an ideal world, when you first start, he downloads all the dependencies himself, but, in the absence of the Internet, we act as follows (secret knowledge is obtained in a weak way from the staff of JetBrains):
- Create any simplest script on Kotlin so that it is something to slip to the compiler in the next step
- Run $ KOTLIN_HOME / bin / kotlinc SimpleScript.kt, wait a bit and press CTRL + C - this is to create a folder structure in the home directory
- We look in the $ KOTLIN_HOME / konan / konan.properties file, in the section with our architecture, and look for the item:
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
- Go to a special repository and download all of the above.
- We add all this to ~ / .konan / cache (remember, the Linux home directory is the tilde)
Now when you first start the compiler will use these distributions and will not work on the Internet.
Consider that the dependencies are not very small in size and, after their installation, my home directory has become heavier at 3.4GB. For those who have a separate volume for the home, it can be critical.
After that, using the standard package manager, install php and its corresponding php-devel.
At this preparatory activities are completed.
Since the task is not to tell about writing PHP extensions, we will manage as short as possible.
Let's start with Kotlin
hellokt.kt fun kt_print(string:String){ println("Hello, $string!!!") }
In various manuals and tutorials, either kotlinc or konanc are used as compilers, which is somewhat confusing. So - this is the same thing. Here is proof:
Compile
# $KOTLINC_HOME/kotlinc -opt ./hellokt.kt -o hellokt -produce dynamic
With the -opt key, the library is smaller.
-produce dynamic tells the compiler what to do with the
shared library for the current platform.
After execution, we will have two files: libhellokt.so and hellokt_api.h. Library and header file for it. Please note that prefixes and suffixes are generated automatically and we cannot influence them (probably).
In our case, this is the header file.
#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 { void (*DisposeStablePointer)(hellokt_KNativePtr ptr); void (*DisposeString)(const char* string); hellokt_KBoolean (*IsInstance)(hellokt_KNativePtr ref, const hellokt_KType* type); struct { struct { void (*kt_print)(const char* string); } root; } kotlin; } hellokt_ExportedSymbols; extern hellokt_ExportedSymbols* hellokt_symbols(void); #ifdef __cplusplus } #endif #endif
Access to our kt_print function will follow this path.
hellokt_symbols()->kotlin.root.kt_print(char *);
I will tell about classes and packages below - there are nuances.The library is ready, go to C
config.m4 (
how to create it )
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
The first line is required!
The three lines of the comments seen above are as three lines using PHP_ARG_WITH () and PHP_ARG_ENABLE ().
...
You can choose the extension into php.
Note that in PHP_ADD_LIBRARY_WITH_PATH the first argument specifies the name of the library. Not libhellokt, but hellokt. Two hours ditched until found why ld can not find the library. (here is the promised story about linker bullying).
Now, actually, the extension code itself
hello.c #include "php.h"
There are a lot of articles about writing PHP extensions, and there is documentation, so I’ll
limit myself to a reference to
using zend_parse_parameters - it will be
relevant .
Well, then everything is on the way:
# $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!!!
Bonus, pro classes and packages
Suppose we wanted to do it by Fenshui and blind such a code.
package hello.kt; public class HelloKt { fun kt_print(string: String) { println("Hello, $string!!!") } }
The usual method call here is not enough - first you have to create a class and pass it with the first argument to the called function.
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);
What's next? Rejecting the shared library, minimizing the use of C, and what the hell is not joking, a mini framework is such a thing. Wish me good luck! :)