Food for Thought: WordPress Plugin in C#

WordPress is now entirely ready to be compiled to and executed on .NET with Peachpie. With this out of the way, this article will examine one of the great usecases of this endeavor – how to write a plugin for WordPress in C#.

We are already able to compile the entire WordPress codebase into .NET and run it as an ASP.NET and ASP.NET Core application. Also, in another article, we have discussed some of the inter-operability options of making seamless invokes between compiled PHP and C# projects.

Now how about putting it all together and writing a WordPress plugin in C#? Here’s how we can go about this.

Recent Improvements

First, to make it all possible, we had to implement a few cool features into Peachpie – our open-source PHP compiler to .NET:

  • PHP graphics library – WordPress provides a simple image library with some basic image manipulation features, such as rotation, cropping or resizing. In order to run unmodified WordPress code as a .NET application, a range of imaging functions had to be implemented and most importantly – they have to perform the same exact thing as they did in PHP. This part can be tricky, but we succeeded with the help of the open-source project ImageSharp. Therefore, Peachpie now provides some image functions (and others will be implemented as needed) that do the same thing as in PHP, are fully safe and managed, and most of all – they are highly performant.
  • $_FILES – in PHP, your code accesses uploaded files through a special so-called autoglobal variable $_FILES. This differs from .NET, where all the uploaded files are available in a special Request object. Peachpie now seamlessly copies information from the ASP.NET or ASP.NET Core representation into the PHP representation so all the PHP apps that rely on $_FILES, e.g. WordPress, work unmodified. See the source code that is responsible for this feature.
  • More interoperability options! – we’ll discuss this in greater detail in another article, but essentially we have figured out a way of passing C# delegates into a PHP program. In this way, the PHP code believes the delegate is a callable object (like a Closure or a class with the magic __invoke() method) and it is able to invoke it. This can be quite amazing as you watch the call stack change while debugging and stepping through PHP and C# code seamlessly.

WordPress Plugin Architecture

The second step is to intrude the WordPress plugin mechanism. As described in the WordPress developer documentation, commonly all the PHP files with the special wp-content/plugins directory named as their parent folder are probed, their leading documentary comment is scanned and such a file can be enabled in the WordPress dashboard. Upon enabling, the file is included during the WordPress startup. The file is usually used to register some callbacks as WordPress filters or actions, and to include the rest of the plugin functionality.

Here is a basic diagram to illustrate the mechanism:

Basically, we create a small PHP file plugin.php containing only some metadata (PHPDoc comments) that WordPress parses to get the plugin name, description and other info. Then, instead of writing the actual plugin in PHP, we call our C# static method in plugin.cs that implements the plugin in C#. The C# method is coded in a C# project that is referenced during the compilation of WordPress. Thanks to the interoperability options, we can call the PHP WordPress API from C#, register WordPress callbacks as C# delegates etc.

Advantages

This option is not just about the choice of your favorite language. In C# (.NET in general) you can bypass some frequent PHP challenges, such as caching or performance considerations. Clearly, compiled C# code can be much faster and safer than the dynamic code in PHP. An experienced C# developer can make use of the C# compiler and JITter optimizations. Also imagine you can write async or multithreaded type-safe code in C# and re-use existing NuGet packages and other libraries written for .NET. Additionally, one can distribute their plugin as a compiled sourceless .NET NuGet package. Finally, we can cache data in application-wide static variables, not just in the context of a single request. And there is probably more…

The Plugin

Lets start with the dummy plugin for WordPress – plugin.php.

<?php
/*
Plugin Name:  dotNET WordPress Plugin
Description:  Basic Plugin in .NET
Version:      20171019
Author:       peachpie.io
Author URI:   https://www.peachpie.io/
License:      Apache
*/

\MyWpPlugin::Register();

Notice the last line of code – we call some MyWpPlugin::Register() method. Now we just have to implement it and make it visible to the compiled PHP code.

We continue by creating a C# project – plugin.csproj, containing a single source file – plugin.cs.

use Pchp.Core;  // Peachpie core API
public class MyWpPlugin {
  public static void Register(Context ctx) {
    // register callbacks into ctx
  }
}

The code above needs a reference to the Peachpie Runtime library (`Peachpie.Runtime`) containing the API that interoperates with the compiled PHP program. Notice the `Context ctx` parameter. The instance of `Context` is automatically passed from the PHP runtime and represents the PHP environment with all its global variables, declared PHP functions, classes etc. You can use this object to manipulate the PHP runtime and simulate PHP invokes. You can even declare new PHP functions written in C# here.

See our WordPress sample and add the following XML snippet to the msbuildproj file:

<ProjectReference Include="..\plugin\plugin.csproj" />

This causes MSBuild and Peachpie compiler to magically compile WordPress, referencing your C# plugin project. What does this mean? Everything you implement publicly in your C# code will be seamlessly available to your PHP code. You can call public methods, instantiate public classes, just as if it were coded in PHP. As a result, we just called a C# method from our dummy PHP plugin.php.

The plugin – C# part

Now it’s time to write some C# code that will be a first class citizen in the WordPress plugin ecosystem. As of now, the interoperability API is still in development, and lots of sugars are not yet implemented. This means that the code might be shortened and simplified in the future. For now, it may look like this:

use System;
use Pchp.Core;  // Peachpie core API
public class MyWpPlugin {
  public static void Register(Context ctx) {
    ctx.Call("add_filter", "the_content", new Func<string, string>(text =>
    {
      text = text.Replace("Welcome", "Greetings");
      return text;
    }));
  }
}

The piece of code above adds the WordPress filter. The function `add_filter` is a part of the WordPress API and we call it dynamically here. It has two parameters, the filter name and a callable that is called by WordPress when the filter has to be invoked. Notice that we pass a strongly typed C# delegate in place of the callable. The WordPress code does not notice it is not a PHP callable, it simply invokes as if it were any other callable in PHP (link to WordPress source code).

// THIS ACTUALLY CALLS OUR C# DELEGATE!!
$value = call_user_func_array( $the_['function'], $args );

This would work as well:

$value = $the_['function'](...$args);

The complete sample can be found at the following link:

[WORDPRESS SAMPLE]

This allows us to add any WordPress callback as a C# delegate doing anything in C#.

Future improvements

To make writing plugins in C# easier, it would be helpful to implement a C# library that defines WordPress filters and callbacks in an object-oriented way. The registration of your filter would then be reduced to something like

ctx.WpContentFilter(text => ...);

Conclusion

And there you go. A few simple steps and you are opening up the entire WordPress ecosystem to the .NET world. Hopefully the intentions of Peachpie become a little more apparent with this mechanism: the project’s objective is to combine the best of the PHP and .NET worlds. Extending the vast, rich and open-source ecosystem of WordPress with the sourceless distribution, security and performance of .NET can create a wonderful symbiosis.

Share this article:Share on FacebookShare on VKPin on PinterestShare on Google+Tweet about this on TwitterShare on LinkedInShare on Reddit
Posted on October 24, 2017, in category Information, Tutorial, tags: , , , , , ,