Tutorial: Advanced Interoperability Between PHP and C# with Peachpie

Peachpie is a compiler of the PHP language to .NET. The project is still in progress, but there is a plethora of interesting usecases for the compiler. This article is a tutorial for some of the more advanced interoperability options between PHP and C# using Peachpie.

Building a DLL from PHP

Using the compiler, we can build a regular .NET DLL from the PHP code; not only is this DLL compatible with the .NET Framework, .NETCore and Mono, but we can also use this compiled DLL in C# projects.

For the purposes of this demonstration, we created a SimpleDog class and goodboy global function in PHP. Notice the well-known PHPDoc annotation @param that we use to mark the function parameter’s type, which means that the compiled methods have a specific CLI type (this will be however configurable for backward compatibility reasons).

Notice that Peachpie already supports some dynamic constructs that are much easier to write in PHP (line 35 and 36). To be more precise, specifically reading a field on a dynamic object and then calling a method by its name dynamically. This would require using a reflection or dynamic in C#. Peachpie, however, handles it by its own way allowing us to take advantage of the simple PHP syntax.

Using Peachpie, we now compile the code to a library (as a DLL file).

peach.exe /target:dll /debug+ SimpleDog.php

This creates SimpleDog.dll managed file including all the debugging information, which will come in handy once we start using it in Visual Studio and running our hybrid project.

Creating a C# App

We can create any kind of C# app, ranging from a simple console application through to a portable one. In order to make use of our PHP library, add references to SimpleDog.dll, pchpcor.dll and pchplib.dll.

simpledog_references

Deriving from a PHP class

The first notable aspect here is that we can in fact declare a C# class that is derived from a PHP class. At this point there is no extra work we have to do; the PHP class appears in the C# context and is available for us to use as it would be written in C# as well.

/// <summary>
/// C# class inheriting from PHP class.
/// </summary>
class Dachshund : SimpleDog {
    /// <summary>
    /// C# constructor calling base PHP constructor.
    /// </summary>
    /// <param name="ctx">Special parameter referring to runtime PHP context.</param>
    /// <param name="color">Our dashund color.</param>
    public Dachshund(Context ctx, string color)
        : base(ctx, "Dachshund", color, 1.3) {   // base .ctor, subject of change in future
        __construct("Dachshund", color, 1.3);    // in PHP, we have to call base ctor explicitly; subject of change in future API
    }

    public override void Jump() {
        Console.Write("nope");  // I don't
    }

    public override void Woof() {
        Console.Write("Yip yip");
    }
}

Notice that we have to call __construct explicitly, since in PHP the base constructors are not called automatically. Nonetheless, this is subject to change in future versions of Peachpie when you call a constructor from C#.

Creating a Context

Pchp.Core.Context is an object representing the PHP runtime context. All operations with PHP objects require its instance. Context allows us to simulate PHP script inclusion or calls to a global PHP function. It also manages references to compiled scripts, which means that it extracts information about SimpleDog.dll and its declarations inside.

// Load SimpleDog.dll into the PHP application context.
// This adds compiled script files into the context, so they can be included below.
Context.AddScriptReference(typeof(SimpleDog).Assembly);

Context is an important object hosting all the PHP quirks. We will create one for our demo purposes. Note that Context is not thread-safe.

var context = Context.CreateConsole()

The assembly contains compiled script files and function declarations. We have to simulate the PHP inclusion mechanism, to load the declaration properly. To do so, we will include the compiled PHP file SimpleDog.php (for which we do not need the source code, since it was compiled into DLL so the original source code is only needed for debugging or to see the source code while stepping through).

// Call the global code in SimpleDog.php,
// including declaration of contained functions.
context.Include(context.RootPath, "SimpleDog.php");

Instantiating a PHP object

Since a PHP class is compiled to .NET respecting the C# conventions, it is possible to simply instantiate it in C#.

new SimpleDog(context, "Labrador", "White", 5.3);

Notice the first parameter – context – which is required to pass to every PHP compiled object.

Calling a global function

Because C# does not have global functions but PHP does, Peachpie disposes of a special API on Context.

context.Call("goodboy", new Dachshund(context, "pink"), "Woof");

This will dynamically call the function goodboy with the given arguments. If you experiment a little, you will see that you can put breakpoints inside the C# and PHP code and step between these two languages. The function performs several dynamic operations that would not be as simple to perform in C#. For instance, a call to a method by its name given as a parameter or even without specifying the type of dog argument at all. All of this is determined dynamically at runtime.

Conclusion

In this article, we shortly demonstrated some of the more advanced interoperability possibilities that Peachpie compiler enables. What stands out is that the compiled code fully respects the C# conventions, and is compatible with the .NET Framework, .NETCore and Mono.

Download the complete build of Peachpie and SimpleDog from
https://github.com/iolevel/peachpie/releases/tag/2016-07-24-2

We are looking forward to seeing similar experiments performed on Linux or mobile platforms running .NETCore!

Share this article:Share on FacebookShare on VKPin on PinterestShare on Google+Tweet about this on TwitterShare on LinkedInShare on Reddit
Loading Facebook Comments ...

Leave a Reply