ext-psi icon indicating copy to clipboard operation
ext-psi copied to clipboard

PHP System Interface (aka FFI)

ext-psi

Join the chat at https://gitter.im/m6w6/ext-psi Build Status

PSI is a PHP extension, which provides a foreign function interface through libffi and/or libjit.

The acronym PSI may be read as:

  • PHP System Interface

WARNING:
This is heavy WIP.

Features

  • structs, unions, enums and typedefs
  • numeric and boolean expressions
  • scalar constants
  • vararg calls

Installing

WARNING:
This is heavy WIP. Installation only works from a source checkout with php-src@master on x86_64-linux yet. Sort of works on x64_64-darwin, too, roughly.

PECL

Not implemented yet.

This extension is distributed through PECL and can be installed with PEAR's pecl command:

pecl install psi

PHARext

Not implemented yet.

Watch out for PECL replicates and pharext packages attached to releases.

Checkout

This extension is hosted at Github with a personal mirror available.

git clone github.com:m6w6/ext-psi

cd ext-psi

/path/to/phpize
./configure --with-php-config=/path/to/php-config
make
sudo make install

Configuring PSI at build time

PSI supports the following configure switches:

--enable-psi

Enable PHP System Interface support.

This is only relevant for an in-tree build. Use --enable-psi to include the PSI extension in the build.

--with-psi-libjit

Path to libjit.

WARNING:
We currently rely on a patched libjit, because of an apparent bug in how libjit creates closures, which still needs to be verified, though. See https://github.com/m6w6/libjit for the preliminary patch.

--with-psi-libffi

Path to libffi.

Configuring PSI at runtime

psi.engine

The backend that PSI should use as FFI, either jit for libjit or ffi for libffi. Defaults to "ffi".

psi.directory

A colon separated list of directories to scan for *.psi files. Defaults to "psi.d".

psi.blacklist.decls

A comma separated list of C function declarations to ignore.

psi.blacklist.vars

A comma separated list of C variable declarations to ignore.

PSI files

PSI files are augmented C header files. So, everything usually found in C headers including comments, preprocessor directives, typedefs, structs, unions, enums, function and variable declarations should work.

That means, that you can just include C headers in the usual way:

#include <stdlib.h>

Dynamic Libraries

PSI needs to know which library it should dlopen unless the declared functions are provided by the standard C library. Therefore the PSI preprocessor understands a special pragma:

#pragma lib "crypt"

Constants

const int num\ZERO = 0;
const string pwd\TOKEN = "4WlOjXGL";

Constants must have namespaced identifiers and are registered as userland constants.

C enums and preprocessor macros with numerical or string expressions are automatically discovered and registered as constants.

Implementations

Implementations are the userland visible interfaces to the foreign function interface.

function str\error(int $num) : string {
    let errnum = intval($num);
    return strerror(errnum) as to_string(strerror);
}

Each implementation refers to exactly one declared foreign function referenced in its return statement. Each native function, on the other hand, may have any number of implementations.

Complete example

#ifdef __linux__
/* needed for setkey() in stdlib.h */
# pragma lib "crypt"
#endif

/* for free() */
#include <stdlib.h>
 
#pragma lib "idn"
#include <idna.h>

function idn\utf8_to_ascii(string $host, string &$result, int $flags = 0) : int {
    // there must be a `let` statement for each
    // declared argument of the called function

    // setup a pointer to NULL
	let buffer = &NULL;

	// setup a string pointer to $host
	let host = strval($host);

	// assing the integer value of $flags
	let flags = intval($flags);

	// the function to call is referenced in
	// the return statement, along with the
	// necessary cast how to interpret the
	// returned native value
	return idna_to_ascii_8z(host, buffer, flags) 
	           as to_int(idna_to_ascii_8z);

	// by-ref vars might receive output values
	// through `set` statments, which also
	// require a cast how to marshal the
	// native data as PHP value
	set $result = to_string(*buffer);

	// after the buffer has been marshaled
	// for the PHP engine, we have to free
	// the buffer to avoid a memory leak
	free free(*buffer);
	// note that in this example we omit the
	// declaration of the free() function called
	// in our `free` statement for brevity
}

function idn\strerror(int $rc) : string {
	return to_string(idna_strerror);
	let rc = intval($rc);
}

Userland

$rc = idn\utf8_to_ascii("flöte.de", $result);
printf("<%s>\n", $result);
printf("%s\n", idn\strerror($rc));

$rc = idn\utf8_to_ascii("…asdf….de", $result, IDNA_USE_STD3_ASCII_RULES);
printf("<%s>\n", $result);
printf("%s\n", idn\strerror($rc));

Output

<xn--flte-6qa.de>
Success
<>
Non-digit/letter/hyphen in input

License

ext-psi is licensed under the 2-Clause-BSD license, which can be found in the accompanying LICENSE file.

Contributing

All forms of contribution are welcome! Please see the bundled CONTRIBUTING note for the general principles followed.

The list of past and current contributors is maintained in THANKS.