Using C# in PHP and Vice Versa

Interoperability is one of the most valuable use cases of PeachPie: using C# (.NET) classes, interfaces and methods right in PHP code and vice versa. Let’s take a look at individual cases and how to achieve interop with PeachPie.

Introduction

We start off assuming we already have a PHP project running on .NET. If not, follow the steps in our Getting Started guide or MSBuild docs. In this article we are working with Visual Studio 2017 or newer, but it all works on the command line, VSCode or continuous integration tooling on your favorite cloud provider as well. Everything is based on .NET tooling (no PHP needed).

tl;dr:
Samples and demo projects can be found at:

>> https://github.com/iolevel/peachpie-samples

Use C# (.NET) objects in PHP

The simplest case is to reference NuGets or C# projects from your PHP project. We will call these projects or NuGets as libraries, and you can read more about the additional features of a library here. In short, the referenced library may contain classes, methods, enums, constants and properties.

namespace Namespace1 {

  class MyCSharpClass {
    public virtual string Hello() => "Hello World!";
    public const int C1 = 123;
    public static bool B1 = true;
  }

  public enum E {
    A,
    B,
    C
  }
}

Usually we have some class with methods, properties and constants in C#, a library (DLL file) or a NuGet package. Imagine for a moment that the PHP project is yet another C# project, and treat it just like that.

1/ Add a reference to your C# project, DLL or NuGet from within your PHP project. There are more ways how to do that, e.g. editing the project file or using the Visual Studio UI.

Adding a reference to a C# project, NuGet package or DLL.

Both our projects and NuGet packages are targeting netstandard2.0, which makes them compatible with the vast array of other frameworks and platforms that run on it as well. For the full matrix of compatibility see https://docs.microsoft.com/en-us/dotnet/standard/net-standard

Referenced C# project in your PHP project, in Visual Studio.

2/ That’s pretty much it. Use the class in the same way you would as if it were written in PHP:

<?php

echo (new Namespace1\MyCSharpClass)->Hello();
echo Namespace1\MyCSharpClass::C1;
echo Namespace1\MyCSharpClass::$B1;
echo Namespace1\E::B;

Notice the uninterrupted PHP syntax, but accessing a C# (.NET) object. Why would you do that, you say? Here are several real-world scenarios:

  • your PHP app needs something that is implemented in .NET, already packaged in a NuGet or a part of your .NET stack
  • you are about to migrate a PHP website/application to .NET; compile it with PeachPie and subsequently remove PHP classes and replace them with the corresponding implementation in C#
  • you can optimize your PHP app; replace something you have in PHP with a high-performance implementation in C#
  • you want to be interesting and you are about to develop a game in PHP, using a .NET framework for 3D rendering and UI management … (haha)

Not enough? Try inheriting a .NET class or interface in PHP:

<?php

class MyPhpClass extends Namespace1\MyCSharpClass {
    function Hello() : string {
      return "Hello from PHP!";
    }
}

To give you the complete picture, here’s what happens when you “build” such a PHP project. The compiler creates a DLL assembly file that can either be executed or used by other .NET applications, including ASP.NET and ASP.NET Core web servers. The assembly contains low level instructions and metadata, and not the original source code. Using tools such as ILSpy, we can de-compile the assembly and see the instructions in a human readable form:

The PHP sample above decompiled to IL instructions.

The comments (green lines) show what C# code you would have to write to achieve the same IL generated from our PHP code. Basically, we translated PHP to IL, then to metadata, then back to IL using ILSpy and finally to C#. Just note – this is not a translation of PHP to C# – you don’t want to use PeachPie for that, nor should you strive for this goal anyway.

The PHP class MyPhpClass decompiled to IL instructions and metadata.

Use PHP functions/classes in C#

What about the opposite way? What if we compiled a PHP library or a PHP web into a DLL assembly file and we want to use it in C# – as a regular project or NuGet reference? Let’s start by adding the reference the usual way:

Adding a reference of a PHP project into a C# project.
Referencing a PHP project in a C# project.

The executable C# project targets netcoreapp2.1 and the PHP library is netstandard2.0. You can also target net461 if you want to stick with the full .NET Framework and not .NET Core.


If you start typing in C# now you’ll see that the IntelliSense knows the PHP classes already.

The PHP language has some unusual quirks and features, not very applicable to the C# syntax. There are PHP static properties that are not as static as you might think. There are also global functions and constants. And finally dynamic types.

Since the PHP program is bound to a web request, its state and static variables are also bound only to a web request. This means that the data is not static as defined in .NET – it is not application-static, but rather request-static. As a result, our runtime has to maintain the so-called Context that contains the state of the PHP program. You can have several Context‘s or just one if you want to do some crazy things.

Instantiate a PHP class in C#

using (var context = Pchp.Core.Context.CreateEmpty()) {
    var x = new MyPhpClass(context);
}

In the sample above, we create an instance of a PHP class. Notice we have to create the Context first and maintain its instance. Since the instance manages the life-cycle of the PHP program (a set of declared PHP classes, included scripts, etc.), you should keep the instance for all the other subsequent tasks.

The instance of Context is passed to every PHP constructor; in general, a PHP class needs to be bound to a Context for the purposes of outputting/echoing text, including scripts, declared classes lookup, etc.

Call a PHP method in C#

x.Hello();

Calling methods is very straight forward and you won’t notice you are actually invoking a PHP code.

Access a PHP static property in C#

Since a PHP static property is not application-static but only request-static, its value is bound to the Context. To access the value of the PHP static property, we have to use some API:

context.GetStatic<MyPhpClass._statics>().myStaticPhpProperty

Every PHP class that contains a static property has the compiler-generated nested class _statics– the container of the static property value. It contains the actual properties and you have to obtain the container’s instance from the Context by calling context.GetStatic<MyPhpClass._statics>

Include an entire PHP script in C#

This is an important part of every PHP program lifecycle – including scripts. C# does not have runtime scripts, but PHP does and you have to include them in order to simulate the life-cycle. The PHP code expects PHP classes to be properly declared (i.e. its containing script files should be included).

Pchp.Core.Context.AddScriptReference(typeof(MyPhpClass).Assembly);

First, just once within your .NET program instance, somewhere in the beginning, you have to reflect the compiled PHP scripts. This step may become unnecessary in the future, but for now it is mandatory. The compiled PHP project contains all the source scripts (.php files); these are special classes that are not accessible using C# syntax and you won’t see them in IntelliSense. They contain metadata and its global code compiled as a special static method Main, which our runtime is able to call when you perform the inclusion. The code above reflects this metadata and remembers it. 

Compiled .php scripts inside the DLL Assembly:
index.php, class-user.php, functions.php

You can then use Context’s API to perform the include, which is basically an invocation of the script’s global code.

context.Include(string.Empty, "index.php");

The first parameter is the current (relative) directory path. This is basically a shortcut for PHP’s include 'index.php'; called from within the project root.

Calling a global PHP function in C#

Once you include the script that contains a declaration of a PHP function, you can invoke it:

context.Call("test")

Passing parameters and obtaining the return value is nothing unusual, except all the value types are PhpValue. See https://docs.peachpie.io/api/ref/phpvalue/ for more details on this object. It represents PHP’s dynamic value and you can implicitly convert values between built-in .NET types and PhpValue.

Conclusion

And there you have it, the most detailed interop guide we’ve written to date. If you have any additional interoperability use cases we didn’t cover, please don’t hesitate to let us know. 

Share this article:Share on FacebookShare on VKPin on PinterestShare on Google+Tweet about this on TwitterShare on LinkedInShare on Reddit
Posted on February 5, 2019, in category Information, Tutorial, tags: , , ,