Skip to content
PHP System Interface (aka FFI)
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
psi.d #pragma lib Dec 10, 2018
scripts travis Dec 4, 2018
tests fix test Dec 10, 2018
travis travis: touch Sep 8, 2017
.gitattributes sqlite test Feb 12, 2016
.travis.yml travis Dec 4, 2018
BUGS flush Oct 14, 2015
CREDITS flush Oct 14, 2015
EXPERIMENTAL flush Oct 14, 2015
LICENSE flush Oct 14, 2015
THANKS flush Oct 14, 2015
TODO CPP builtins Dec 10, 2018
package.xml fix package.xml Dec 4, 2018


Join the chat at 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

This is heavy WIP.


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


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.


Not implemented yet.

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

pecl install psi


Not implemented yet.

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


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

git clone

cd ext-psi

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

Configuring PSI at build time

PSI supports the following configure switches:


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.


Path to libjit.

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 for the preliminary patch.


Path to libffi.

Configuring PSI at runtime


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

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


A comma separated list of C function declarations to ignore.


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"


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 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"

/* 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);


$rc = idn\utf8_to_ascii("flö", $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));


Non-digit/letter/hyphen in input


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


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.

You can’t perform that action at this time.