kotlin-native-php-extension
kotlin-native-php-extension copied to clipboard
Toolkit for writing PHP extensions with Kotlin Native
Kotlin Native PHP extension generator
PHP version >= 7.0
Linux
How it works
You describe PHP extension by provided DSL and run make
.
Build script do:
- Compile and run code-generator utility
- Generate template
.c
file for PHP extension - Generate
config.m4
file forautoconf
- Bind Kotlin function to php functions calls
- Generate
.kt
file with declared constants - Compile static library from
.kt
sources - Run
phpize
,configure
andmake
to produce shared PHP library from generated.c
file and the resultant Kotlin static library
Scope
Can
- Functions
- Supported arguments types:
int
,float
,string
,boolean
,mixed
,array
,null
,object
- Optional arguments
- Extension constants
- Declare and read INI-entries as String
- Call Zend C-functions from Kotlin
- Add logic to MINIT, RINIT, MSHUTDOWN and RSHUTDOWN
- Define classes with methods, properties and constants
Can't
- Arguments by reference
- Resources and callable as arguments or return value
Files
./konfigure/* Code generator, DSL, zend interop classes
./tests/* .phpt tests for example extension
Makefile Makefile
extension.kt Mandatory. Write your DSL of PHP extension here
lifecycle.kt Mandatory. Write your lifecycle hooks here
example_double.kt Example extension functions
example_strings.kt Example extension functions
Prerequisites
-
Install
php
andphp-devel
packages -
Change
PHP_HOME
andKOTLIN_HOME
variables inMakefile
PHP_HOME
must point at home PHP directory, where located./bin
and./include
directoriesKOTLIN_HOME
must point at directory, where locatedkotlinc
binaryexample:
KOTLIN_HOME=/root/kotlin-native-linux-0.7.1/bin PHP_HOME=/opt/rh/rh-php71/root/usr
Writing extension
Extension consist of two or mode .kt
files.
Mandatory one is extension.kt
, where you describe your extension with DSL.
All others contains your extension logic.
Let's make example
extension with three functions.
First, make file example.kt
, that contains realization of those functions.
fun hello(name: String, lang: String?) = "${if (lang ?: "" == "") HELLO_EN else lang} $name!!!\n"
fun helloWorld() = println("Hello $EXAMPLE_WORLD!!!")
fun multiple2(num: Double) = num * 2
NOTE! In current version of KN have a bug with unitialized
char *
. Because of this, you must handle an empty string instead of null.
First function receive String
and return String
.
Second function receive String
and return nothing (NULL
by default).
Third function receive Double
and return Double
.
Also let's add som constants and INI-setting.
Second, write DSL with description of extension.
import php.extension.dsl.*
val dsl = extension("example", "0.1") {
ini("example.count","10")
constant("EXAMPLE_WORLD", "World")
constant("EXAMPLE_LONG", 10L)
constant("HELLO_EN", "Hello")
constant("HELLO_ES", "Hola")
constant("HELLO_RU", "Привет")
function("hello", ArgumentType.PHP_STRING) {
arg(ArgumentType.PHP_STRING, "name")
arg(ArgumentType.PHP_STRING, "lang", true)
}
function("helloWorld")
function("multiple2", ArgumentType.PHP_DOUBLE) {
arg(ArgumentType.PHP_DOUBLE, "number")
}
}
fun main(args: Array<String>) = dsl.make()
NOTE! Your
.kt
files MUST contains functions with names equals to described by DSL. Also they MUST receive all described arguments of corresponding types in described order.
NOTE! Corresponding K/N constants will generated automatically. Do not declare them in
.kt
files.
Third, just run make
.
If no errors occuried then example.so
will be created inside ./modules
directory.
If you does not want run phpize
, configure
and make
after generation of C-code, just use parameter make kotlin
.
Don't forget to run make test
.
DSL description
extension(name, version){
ini(name, defaultValue)
...
externalIni(name)
...
constant(name, value)
...
function(name [, returnType = NULL]){
arg(type, name [, isOptional = false])
...
}
...
class(name){
constant(name, value)
property(name, value){
[static()]
[private() | protected()]
}
method(name [, returnType = NULL]){
arg(type, name [, isOptional = false])
...
[static()]
[private() | protected()]
[abstract() | final()]
}
}
...
}
node | parameter | type | note |
---|---|---|---|
extension |
|||
name |
String |
||
version |
String |
||
ini |
Extension INI-setting | ||
name |
String |
||
value |
String |
||
externalIni |
INI-setting that do not related to your extension, but you need it to use inside them. This needed for creation proxy retriever function. | ||
name |
String |
||
constant |
|||
name |
String |
||
value |
Any |
String, Long (or Int), Double and Boolean will be converted to corresponding PHP types. All other will be silently dropped | |
function |
|||
name |
String |
Name of resulting PHP-function. Note that you MUST provide corresponding K/N function with exact same name | |
returnType |
ArgumentType |
Optional return type. By default ArgumentType.PHP_NULL |
|
arg |
Note that argument addition order is important. After adding first optional argument, all arguments below MUST be optional | ||
type |
ArgumentType |
||
name |
String |
||
isOptional |
Boolean |
Optional flag decides that argument is optional. By default FALSE |
|
class |
|||
name |
Name of the class. You must create separate .kt file with appropriate methods realization and set package to lowercase name of the class. For example, for class MyClass you must write package myclass |
||
method |
Like a function . Additionally you can use functions static(), private(), protected(), abstract(), final() for setting modifiers. Note, that first argument of appropriate K/N functions must be obj:PhpObject . Do not declare this argument inside DSL. |
||
property |
Like a constant . Additionally you can set modifiers by static() , private() and protected() functions. |
Types
ArgumentType | Kotlin type | PHP type | C type | note |
---|---|---|---|---|
PHP_STRING |
String |
string |
char* |
|
PHP_LONG |
Long |
int |
zend_long /int64_t |
|
PHP_STRICT_LONG |
Long |
int |
zend_long /int64_t |
See description. Can't be used for return value |
PHP_DOUBLE |
Double |
float /double |
double |
|
PHP_BOOL |
Boolean |
boolean |
int |
|
PHP_NULL |
PhpMixed |
null |
zval* |
|
PHP_MIXED |
PhpMixed |
mixed |
zval* |
|
PHP_ARRAY |
PhpArray |
array |
HashTable* |
PhpArray is a wrapper class realizing Map<PhpMixed,PhpMixed> interface |
PHP_OBJECT |
PhpObject |
object |
zval* |
Hooks
You can provide Kotlin function for following hooks by editing lifecycle.kt
file
Lifecycle | Kotlin function |
---|---|
MINIT |
minit() |
MSHUTDOWN |
mshutdown() |
RINIT |
rinit() |
RSHUTDOWN |
rshutdown() |
Reference
Helper classes
Class | Realize | Description |
---|---|---|
PhpMixed |
CPointer<zval> |
Type alias for pointer to C-struct zval |
PhpArray |
MutableMap<PhpMixed, PhpMixed> |
Wrapper for C-struct HashTable . Represents methods for PHP-array manipulations |
PhpObject |
common class | Wrapper for object manipulation |
ArgumentType |
enum class | Represents possible PHP-types |
LifeCycle |
enum class | Represents possible PHP-extension lifecycle hook |
High order functions
Function | Returns | Description |
---|---|---|
getIniString(name:String) |
String |
Returns INI-setting for name. You can retrieve only those INI-settings that described by DSL directives ini and externalIni |
createPhpNull() |
PhpMixed |
Returns PhpMixed with type NULL |
arrayToHashTable(array: PhpArray) |
CPointer<HashTable> |
Convert PhpArray to pointer to C-struct HashTable . Normally you do not need to use this function. |
hashToArray(hash: CPointer<HashTable>) |
PhpArray |
Convert PhpArray to pointer to C-struct HashTable . Normally you do not need to use this function. |
phpObj(context: CPointer<zend_class_entry>, obj: PhpMixed) |
PhpObject |
Provide PhpObject . For interop purpose. |
objectToZval(obj: PhpObject) |
PhpMixed |
Convert PhpObject to PhpMixed . For interop purpose. |
Additional properties
Property | Type | Description |
---|---|---|
String.mixed |
PhpMixed |
PhpMixed representation of String |
Long.mixed |
PhpMixed |
PhpMixed representation of Long |
Double.mixed |
PhpMixed |
PhpMixed representation of Double |
Boolean.mixed |
PhpMixed |
PhpMixed representation of Boolean |
PhpArray.mixed |
PhpMixed |
PhpMixed representation of PhpArray |
PhpObject.mixed |
PhpMixed |
PhpMixed representation of PhpObject |
PhpMixed.string |
String |
String value from PhpMixed |
PhpMixed.long |
Long |
Long value from PhpMixed |
PhpMixed.double |
Double |
Double value from PhpMixed |
PhpMixed.bool |
Boolean |
Boolean value from PhpMixed |
PhpMixed.array |
PhpArray |
PhpArray value from PhpMixed |
PhpMixed.type |
ArgumentType |
Corresponding ArgumentType for PhpMixed |
Class PhpArray
PhpArray
implements interface MutableMap<PhpMixed, PhpMixed>
. Thus, it contains all the required properties and methods.
Non-standard properties and methods
Property | Type | Description |
---|---|---|
hash |
CPointer<HashTable> |
Pointer to wrapped C-struct HashTable |
mixed |
PhpMixed |
PhpMixed representation of PhpArray |
stringKeys |
Set<String> |
Set of keys converted into String . Actually in PHP numeric keys is equals to they string representation. Thus, 11 is equals to "11" |
Constructor | Description |
---|---|
PhpArray(hash: CPointer<HashTable>) |
Default constructor |
PhpArray.fromMixed(array:PhpMixed) |
Construct PhpArray from PhpMixed |
PhpArray.createEmpty(size:Int = 8) |
Construct empty PhpArray and initialize hash |
Method | Returns | Description |
---|---|---|
put(key: String, value: PhpMixed) |
PhpMixed? |
Returns old value for key or null |
put(key: Long, value: PhpMixed) |
PhpMixed? |
Returns old value for key or null |
put(value: PhpMixed) |
Unit |
Add element to array with next free numeric index |
putAll(from: Map<Long, PhpMixed>) |
Unit |
Put all elements with numeric indexes |
putAll(from: Map<String, PhpMixed>) |
Unit |
Put all elements with string keys |
putAll(from: List<PhpMixed>) |
Unit |
Sequentially add values by calling put(value: PhpMixed) |
remove(key: String) |
PhpMixed? |
Remove element and returns old value for key or null |
remove(key: Long) |
PhpMixed? |
Remove element and returns old value for key or null |
containsKey(key: String) |
Boolean |
Whether array contains element with this key |
containsKey(key: Long) |
Boolean |
Whether array contains element with this key |
get(key: String) |
PhpMixed? |
Returns corresponding value or null |
get(key: Long) |
PhpMixed? |
Returns corresponding value or null |
Class PhpObject
Property | Type | Description |
---|---|---|
context |
CPointer<zend_class_entry> |
Pointer to appropriate zend_class_entry |
obj |
PhpMixed |
Pointer to object's zval |
Constructor | Description |
---|---|
PhpObject(val context: CPointer<zend_class_entry>, val obj: PhpMixed) |
Default constructor |
Method | Returns | Description |
---|---|---|
get(name: String) |
PhpMixed |
Get property value by name |
set(name: String, value: PhpMixed) |
Unit |
Set property value by name |
getStatic(name: String) |
PhpMixed |
Get static property value by name |
setStatic(name: String, value: PhpMixed) |
Unit |
Set static property value by name |
toString() |
Unit |
Prints name of the class or "unknown object" |