
- Installation and configuration tools.
- Writing the
helloWorld() function on Kotlin Native and compiling it in a shared library. - Access to this function from the C-code extension of PHP.
In this article I will talk about creating tools for writing a PHP extension without having to touch C, exclusively in K / N.
Who cares - welcome under cat.
Who is not interested to read, but just want to see - welcome to
githubAt the very beginning I want to say a big thank you to Nikolai Igotti for the prompt and high-quality answers to my, sometimes stupid and naive, questions in the Kotlin Native channel.At once I will make a reservation that I do not pretend to create a full-fledged framework (maybe later), therefore we will limit the functionality in this way:
- Creating functions that can be called from PHP code.
- Definition of constants.
- We operate with only simple PHP types:
string , boolean , int , float (and null ). No arrays, objects, resources, passes by reference, etc. - I'll tell you why below.
The specificity of the development of PHP extensions is that almost all the service code and communication with the
zend engine is written in macros. On the one hand, this makes it much easier to write extensions in C, and on the other, it makes it very difficult to do the same in all other programming languages.
With such input, the most obvious solution was to use kodoherenaria. And, given that Kotlin provides very wide possibilities for creating DSL, the process of describing the extension structure can be made simple and intuitive.
In order to build the extension library in the classical way (phpize, configure, make), you need at least two artifacts - the C extension code and the
config.m4 file.
The usage scenario will be as follows:
- With DSL we describe the extension.
- We write the implementation of functions on K / N.
- According to the description, we generate
extension.c and config.m4 . The code in extencion.c will deal with banal function call proxying. - According to the description, we generate
constants.kt , which will allow using the given constants in our functions on K / N. - Compile the K / N code into a static library.
- We collect all this in one pile and compile into the extension library.
Go!
To implement our plans, we need to get something like this structure:
(, ) 1 2 ... 1(, ) 1 2 ... 1 ...
I think that for anyone working with Kotlin, it will not be difficult to write the corresponding DSL. For the rest, there are a large number of specialized articles where this topic is revealed in much more detail than if I try to do this in the framework of this article.
The next step is to turn this DSL into necessary artifacts. To do this, we write a generator on the same K / N, compile the executable file from it and our DSL and run it - voila! The decision is not the most elegant, but nothing more simple and reliable until it came to mind.
Well, then everything is simple - we compile the library with functions and in a regular way we assemble the extension, including it there.
For ease of use, all magic with compilations is hidden in a shell script.What came out of it
An example of the description and the generated code of a simple extension described on this DSL (
for a better understanding, all arguments are given in a named form ).
konfigure.kt - DSL Extensions 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 - Implementing Functions fun hello(name: String, lang: String?) = "${if (lang ?: "" == "") HELLO_EN else lang} $name!!!\n"
Note the strange algorithm for determining the value for `lang`. This is due to a bug in the current version of K / N, which does not allow an uninitialized variable of type `char *` to be passed as an argument from C - you have to pass an empty string.
config.m4 - generated file 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_generated_constants.kt - generated file with Kotlin constants const val HELLO_EN = "Hello" const val HELLO_ES = "Hola" const val HELLO_RU = ""
example.c - generated C code file #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){
About why only simple types
Because they are one-to-one mapped to Kotlin Native types. To date, the project has implemented, in fact, interop only in one direction, i.e. call K / N functions from C. To handle complex types such as
zend_value ,
zend_class_entry or
zend_fcall_info , you need to import the appropriate structures into the K / N project and write the appropriate wrappers to work with them, and there, too, all on macros, etc ...
Jar with tar. Spoon attached.
- Kotlin Native Documentation. It seems to be there, but ... So far, the most reliable means of studying is reading the source code.
- The size of the resulting expansion is not that small. For the above example, the library is approximately 500KB.
- You can not even hope that the extensions written in K / N, fall into the library of extensions PHP. The product is obtained, so to speak, only for internal use.
What's next
Implement everything that is described in the section “About why only simple types”.
Once again the
link to the repository .
Thank you for your attention, wish me luck :)