Home
About
Search
🌐
English Română
  • A bit of PHP, Go, FFI and holiday spirit

    Citește postarea în română

    Dec 23, 2019
    Share on:

    Ah, the holiday spirit.

    Inspired by a post in Perl Advent, I decided that it would be nice to see an example done with PHP and Go. Please note that this blog is heavily inspired by the post mentioned above.

    Let’s say you want to wish upon others happy holiday using the speed of Go, but you are using PHP, what is there to be done?

    This is a great time in the PHP world to do something like this, as the newly released PHP 7.4 comes with “Foreign Function Interface” (FFI for short).

    Equipped with this new power, I installed PHP 7.4 and got to work.

    Let’s start with the super incredible Go greeting part, let’s create a file “greeting.go”:

     1package main
     2 
     3import (
     4   "fmt"
     5)
     6 
     7func main() {
     8   WishMerryChristmas();
     9}
    10 
    11func WishMerryChristmas() {
    12   fmt.Println("We wish you a Merry Christmas!");
    13}
    14
    

    Now let’s run it with:

    1$ go run greeting.go
    

    It should display:
    We wish you a Merry Christmas!

    Great stuff so far! Now this is nice and fast and everything, but it needs to be a service, let’s see how that would look like:

     1package main
     2 
     3import (
     4   "C"
     5   "fmt"
     6)
     7 
     8func main() {}
     9 
    10//export WishMerryChristmas
    11func WishMerryChristmas() {
    12   fmt.Println("We wish you a Merry Christmas!")
    13}
    14
    

    As you can see, there are several differences:

    • we also imported “C”,
    • we removed the function call from main()
    • we added a comment to export the function.

    To compile the code, run:

    1$ go build -o greeting.so -buildmode=c-shared
    

    Note that this should be run each time the Go file is modified.

    The output should be two files: “greeting.so” and “greeting.h”.

    The header file “greeting.h” file contains the types and functions definition. If you come from C world you are probably already familiar with this type of files. Normally, all we need to do now is to import the header file using FFI and use the function!

    For this I’ve created a file titled “greeting.php”:

    1<?php
    2 
    3$ffi = FFI::load("greeting.h");
    4$ffi->WishMerryChristmas();
    5
    

    Sure looks simple enough, you just have to run it with:

     1$ php greeting.php 
     2PHP Fatal error:  Uncaught FFI\ParserException: undefined C type '__SIZE_TYPE__' at line 43 in /home/claudiu/php-go/greeting.php:3
     3Stack trace:
     4#0 /home/claudiu/php-go/greeting.php(3): FFI::load()
     5#1 {main}
     6
     7Next FFI\Exception: Failed loading 'greeting.h' in /home/claudiu/php-go/greeting.php:3
     8Stack trace:
     9#0 /home/claudiu/php-go/greeting.php(3): FFI::load()
    10#1 {main}
    11  thrown in /home/claudiu/php-go/greeting.php on line 3
    12
    

    Not exactly the greeting that I was hoping for…

    After some digging, I found this one on the manual page:
    C preprocessor directives are not supported, i.e. #include, #define and CPP macros do not work.

    Because of this, we unfortunately can’t really use the header file, or at least I don’t know how.

    On the bright side, we can use FFI::cdef() which allows for function definition specification. If I lost you on the way, what I’m trying to do is just tell PHP which are the function definitions that it can use from “greeting.so”.

    The new code will become:

    1<?php
    2$ffi = FFI::cdef("
    3void WishMerryChristmas();
    4", __DIR__ . "/greeting.so");
    5
    6$ffi->WishMerryChristmas();
    7
    

    And if we run it:

    1$ php greeting.php
    2We wish you a Merry Christmas!
    3
    

    We are making great progress, the service is doing a great job!

    Adding an int parameter

    The greeting is nice and fast and all but it would be nice to be able to specify how many times to run it.

    To achieve this, I’m modifying the previous example method to specify how many times to display the greeting in the file greeting.go:

    1//export WishMerryChristmas
    2func WishMerryChristmas(number int) {
    3   for i := 0; i < number; i++ {
    4       fmt.Println("We wish you a Merry Christmas!");
    5   }
    6}
    7
    

    Run the compilation as before and everything should be fine.

    In the PHP script we need to modify the function definition. To see what we should use we can take a hint from the “greeting.h” file. The new function definition in my file is:

    1extern void WishMerryChristmas(GoInt p0);
    2
    

    “GoInt”? What magic is that? Well, if we look in the file, there are the following definitions:

    1...
    2typedef long long GoInt64;
    3...
    4typedef GoInt64 GoInt;
    5...
    6
    

    With this in mind, we can change the PHP file to:

    1<?php
    2$ffi = FFI::cdef("
    3void WishMerryChristmas(long);
    4", __DIR__ . "/greeting.so");
    5 
    6$ffi->WishMerryChristmas(3);
    7
    

    Run it and you should see:

    1$ php greeting.php 
    2We wish you a Merry Christmas!
    3We wish you a Merry Christmas!
    4We wish you a Merry Christmas!
    5
    

    Ah, it’s beginning to feel a lot like Christmas!

    Adding a string parameter

    Displaying a greeting multiple times is quite nice, but it would be nicer to add a name to it.

    The new greeting function in go will be:

    1//export WishMerryChristmas
    2func WishMerryChristmas(name string, number int) {
    3   for i := 0; i < number; i++ {
    4       fmt.Printf("We wish you a Merry Christmas, %s!\n", name);
    5   }
    6}
    7
    

    Don’t forget to compile and let’s get to the interesting part.

    Looking into the “greeting.h” file, the new function definition is:

    1extern void WishMerryChristmas(GoString p0, GoInt p1);
    

    We already got GoInt, but GoString it is a bit trickier. After several substitutions I was able to see that the structure is:

    1typedef struct { char* p; long n } GoString;
    

    It is essentially a pointer to a list of characters and a dimension.

    This means that, in the PHP file, the new definition is going to be:

    1$ffi = FFI::cdef("
    2typedef struct { char* p; long n } GoString;
    3typedef long GoInt;
    4void WishMerryChristmas(GoString p0, GoInt p1);
    5", __DIR__ . "/greeting.so");
    6
    

    p0 and p1 are optional, but I’ve added them for a closer resemblance to the header file. On the same note, GoInt is basically a long, but I left the type there for the same reason.

    Building a GoString was a bit of a challenge. The main reason is that I didn’t find a way to create a “char *” and initialize it. My alternative was to create an array of “char” and cast it, like this:

     1$name = "reader";
     2$strChar = str_split($name);
     3 
     4$c = FFI::new('char[' . count($strChar) . ']');
     5foreach ($strChar as $i => $char) {
     6   $c[$i] = $char;
     7}
     8 
     9$goStr = $ffi->new("GoString");
    10$goStr->p = FFI::cast(FFI::type('char *'), $c);
    11$goStr->n = count($strChar);
    12 
    13$ffi->WishMerryChristmas($goStr, 2);
    14
    

    And let’s try it out:

    1$ php greeting.php 
    2We wish you a Merry Christmas, reader!
    3We wish you a Merry Christmas, reader!
    4
    

    Success!

    At this point I would like to move the GoString creation in a new function, just for the sake of code clean-up.

    And the new code is:

     1$name = "reader";
     2
     3$goStr = stringToGoString($ffi->new("GoString"), $name);
     4
     5$ffi->WishMerryChristmas($goStr, 2);
     6
     7function stringToGoString($goStr, $name) {
     8    $strChar = str_split($name);
     9
    10    $c = FFI::new('char[' . count($strChar) . ']');
    11    foreach ($strChar as $i => $char) {
    12        $c[$i] = $char;
    13    }
    14    
    15    $goStr->p = FFI::cast(FFI::type('char *'), $c);
    16    $goStr->n = count($strChar);
    17
    18    return $goStr;
    19}
    20
    

    And let’s try it:

    1$ php greeting.php 
    2We wish you a Merry Christmas, ��!
    3We wish you a Merry Christmas, ��!
    4
    

    That’s not right… it seems like it’s displaying some junk memory. But why?

    Looking into the documentation for FFI::new I’ve seen a second parameter “bool $owned = TRUE”.
    Whether to create owned (i.e. managed) or unmanaged data. Managed data lives together with the returned FFI\CData object, and is released when the last reference to that object is released by regular PHP reference counting or GC. Unmanaged data should be released by calling FFI::free(), when no longer needed.

    This means that, when the function was returned, the GC is clearing the memory for the string. This is very likely a bug, but there is a very simple fix, just modify the char array creation to “false”:

    1$c = FFI::new('char[' . count($strChar) . ']', false);
    

    Let’s try it again:

    1$ php greeting.php 
    2We wish you a Merry Christmas, reader!
    3We wish you a Merry Christmas, reader!
    4
    

    And it’s working!

    Conclusion

    Maybe it’s not as easy as importing a header file when trying to run Go libraries from PHP, but with a little patience it is certainly possible! A big advantage to this is that a library built in Go, or other programming languages that allow it, can be used by a language like PHP without the need to reimplement the logic!

    And, on this last positive remark, I would like to wish you happy holidays!

Claudiu Perșoiu

Programming, technoloy and more
Read More

Recent Posts

  • Moving away from Wordpress
  • Custom path for Composer cache
  • Magento2 and the ugly truth
  • A bit of PHP, Go, FFI and holiday spirit
  • How to make use of the Xiaomi Air Conditioning Companion in Home Assistant in only 20 easy steps!
  • How I use Magento2 on my local with Docker and Docker Compose
  • About passion, programming and heating systems
  • The Books for Zend Certified Engineer 2017

PHP 49 MISCELLANEOUS 44 JAVASCRIPT 13 MAGENTO 7 MYSQL 7 BROWSERS 6 DESIGN-PATTERNS 5 LINUX-UNIX 2 WEB-STUFF 2 GO 1

PHP 35 JAVASCRIPT 14 PHP5.3 11 MAGENTO 7 PHP6 7 MYSQL 6 PHP5.4 6 ZCE 6 CERTIFICARE 5 CERTIFICATION 5 CLOSURES 4 DESIGN-PATTERNS 4 HACK 4 ANDROID 3
3D1 ADOBE-AIR2 ANDROID3 ANONYMOUS-FUNCTIONS3 BOOK1 BROWSER2 CARTE1 CERTIFICARE5 CERTIFICATION5 CERTIFIED1 CERTIFIED-DEVELOPER1 CHALLENGE1 CHM1 CLASS1 CLI2 CLOSURES4 CODE-QUALITY1 CODEIGNITER3 COLLECTIONS1 COMPOSER1 CSS1 DEBUG1 DESIGN-PATTERNS4 DEVELOPER1 DEVELOPMENT-TIME1 DOCKER1 DOCKER-COMPOSE1 DOUGLAS-CROCKFORD2 ELEPHPANT2 FACEBOOK2 FFI1 FINALLY1 FIREFOX3 GAMES1 GENERATOR1 GO1 GOOGLE1 GOOGLE-CHROME1 GOOGLE-MAPS1 HACK4 HOMEASSISTANT1 HTML2 HTML-HELP-WORKSHOP1 HTML51 HUG1 HUGO1 INFORMATION_SCHEMA1 INI1 INTERNET-EXPLORER3 IPV41 IPV61 ITERATOR2 JAVASCRIPT14 JQUERY1 LAMBDA1 LINUX1 MAGENTO7 MAGENTO22 MAP1 MINESWEEPER1 MOTIVATION1 MYSQL6 NGINX1 NODE.JS2 NOSQL1 OBSERVER3 OBSERVER-PATTERN1 OOP1 OPERA1 OPTIMIZATION1 ORACLE1 PAGESPEED1 PAIR1 PARSE_INI_FILE1 PHONEGAP2 PHP35 PHP-ELEPHANT2 PHP-FOR-ANDROID1 PHP-GTK1 PHP42 PHP53 PHP5.311 PHP5.46 PHP5.53 PHP5.61 PHP67 PHP7.41 PROGRAMMING1 REVIEW1 ROMANIAN-STEMMER2 SAFARY1 SCALAR-TYPE-HINTING1 SCHEME1 SET1 SHOPPING-CART-PRICE-RULE1 SINGLETON1 SOAP1 SPL2 SQLITE1 SSH1 STACK-TRACE1 STDERR1 STDIN1 STDOUT1 SUN1 SYMFONY2 TEST-TO-SPEECH1 TITANIUM2 TRAITS1 TTS1 UBUNTU1 UNICODE2 UTF-82 VECTOR1 WEBKIT1 WINBINDER1 WINDOWS1 WORDPRESS1 YAHOO3 YAHOO-MAPS1 YAHOO-OPEN-HACK1 YSLOW1 YUI1 ZCE6 ZCE5.31 ZEND3 ZEND-FRAMEWORK3
[A~Z][0~9]

Copyright © 2008 - 2021 CLAUDIU PERȘOIU'S BLOG. All Rights Reserved