Seeing PeachPie enables us to compile PHP code into .NET assemblies, wouldn’t it also be great to manage it as a package or a set of packages depending on each other? In this article, we outline some of the common practices and possibilities of building deployable packages with PeachPie using a .NET Core SDK or Microsoft Visual Studio.
PeachPie is a compiler that consumes your PHP code (usually an entire PHP project) and builds a single DLL file containing all the classes, interfaces, functions and script files, compiled into a cross-platform, managed and optimized binary file. It is very similar to the Roslyn compiler platform, on which it is built, but rather than consuming C# code, it consumes PHP instead.
First things first; the msbuild file is a project file that will describe our single package, component or application. In it, we specify the output name, **version**, path to the private key (.snk file), the set of files you would like to compile (such as *.php), dependencies on other projects or packages and what compiler will be used. Our general project file with the
.msbuildproj extension will look like the following snippet:
<Compile Include="**/*.php" />
<DotNetCliToolReference Include="Peachpie.Compiler.Tools" Version="0.9.0-CI00897" />
<PackageReference Include="Peachpie.NET.Sdk" Version="0.9.0-CI00897" PrivateAssets="Build" />
<PackageReference Include="Peachpie.Library.Graphics" Version="0.9.0-CI00897" />
Once you put such a file into the folder with your PHP project, commands like
dotnet build will start to work.
IDE: Visual Studio or Visual Studio Code
Of course we all use some development environment in addition to the command line. The good news is that the project file can be loaded into Visual Studio 2017 (with minor limitations). The only thing you have to do is to run
dotnet restore once before you open the project:
This is necessary, because at this point, the PeachPie SDK is not yet downloaded to your machine and Visual Studio wouldn’t be able to load the project without it. The command
dotnet restore downloads the
Peachpie.NET.Sdk package, which contains other necessary msbuild targets needed for the IDE.
In case of Visual Studio Code, the procedure is the same as with any other C# (CoreCLR) project. Refer to the VSCode docs in order to configure the
tasks.json, will instruct VSCode to run
dotnet build while
launch.json runs the compiled app through the
coreclr launch type. See the sample project in the blog for a demo configuration.
If you enjoy working in VS Code, you can also make use of our practical PeachPie for VS Code extension, which simplifies some of the basic processes and provides syntax error underlining, the option of debugging PHP code and various tooltips.
Building the DLL
By default, the project is built as a class library, which is essentially a standard
.dll file. To build the project as a DLL file, either specify the MSBuild property
library or do not specify anything (
library is the default type).
dotnet build on the command line. In Visual Studio / VSCode press
Using our sample MSBuild project file above, you should now get a dll file in the
bin/Debug/netstandard2.0/ subfolder. You can download the “ILSpy” tool in order to decompile the DLL and see what is inside. You will find that all the PHP code has been compiled into bytecode.
Refer to our Wiki for more MSBuild compilation options.
Signing the assembly
Now we can build the DLL file out of our PHP code. In order to deploy it or make it a part of other projects, we need some versioning and definitely a public key. PeachPie compiler allows you to do this, starting with version
0.9.0-CI00897. The signed assembly is required when building a release package and in case you are referencing the assembly from other signed projects.
To keep things short, the signing is achieved using a private key (an .snk file), which only you as the author/company have. The private key gives your assembly a unique public key, so all your users/customers can be sure the assembly originates from you. Also, it makes referencing assemblies safe, since no one can replace your assembly with his own one, which may potentially contain dangerous code; if they would attempt to do so, the runtime would throw an exception during the assembly load.
The following MSBuild properties are responsible for the assembly signature:
<PublicSign Condition="’$(OS)’ != ‘Windows_NT’">true</PublicSign>
<Version>1.2.3-preview1</Version> <!– or preferably `VersionPrefix` and `VersionSuffix` properties –>
mykey.snk in Visual Studio, for example, or by using the
sn utility, as explained here.
Build the project to see the difference. Your DLL file is now versioned and signed. You can verify this easily in ILSpy:
Building an EXE
A nice option PeachPie provides you with is to create an executable file from your PHP code. In essence, this is virtually the same as a DLL, but somewhere in its metadata there is a so called entry point specified, which is something that the runtime runs right after executing the file. PeachPie compiler allows you to build EXEs using the following MSBuild properties (the same as when compiling a C# app to EXE):
<TargetFramework>has to be
netcoreapp2.0or newer (or
<OutputType>must be set to
<StartupObject>is set to a file name (e.g. “program.php”), global function name (e.g. “main”), or a static method name (e.g. “MyApp::run”). If not specified, the first file in the list of all the compiled files is used as your startup object.
Building a NuGet
Building a NuGet out of PHP code is actually quite simple with PeachPie. Just run
dotnet pack and MSBuild makes sure that your project is built, creating a
.nupkg file from the provided MSBuild properties (version, name, PackageId, …). Refer to the Microsoft docs for more information.
Furthermore, you can set an additional MSBuild property,
true in order to create a NuGet package when building.
The project can be bound to your build server and/or package server (VSTS, MyGet.org or whatever you prefer). These
.nupkg files may be deployed manually or automatically to nuget.org.
NuGet package content files (usually used for images, resources or other non-compiled files) have another use in case of PHP projects. It is important to realize that a lot of PHP projects rely on being deployed together with PHP sources – in other words, they rely on reading these files from the disk drive. In case your PHP project expects its source files to be there, and uses functions like
fread or similar, the files targeted by such functions have to be included as content files into your package. Otherwise, your package distribution will be completely sourceless, which can actually be great (the package is smaller and you aren’t exposing your sources).
Add content files into your package by adding the following to your project file:
<Content Include="**/*.js;**/*.css;**/*.png;**/*.svg;**/*.gif;**/*.blade.php"; />
In the sample above, we include html files, images and blade PHP files into the package. When the package is used, these files will be extracted from the package to the directory of the project that uses the package.
Decomposing into packages, referencing other PHP DLLs
Now it is time to clean up the PHP project. PHP projects usually consist of a single folder structure containing all the code. Despite the fact that it is somehow structured and separated into plugins, themes and components, the whole project is distributed as a bunch of files, sometimes with a
/vendor folder along with these composer packages.
PeachPie, on the other hand, is built as a compiler that is closer to the C# compiler. It is natural to decompose the application into smaller projects/packages, and make references/dependencies between them. The simplest scenario is to compile stand-alone PHP components into separate packages and to reference them from your PHP application. The direction of the reference is important, since references cannot be circular and the referenced project does not see the symbols (classes, functions, scripts) from the other project.
Usually, we would first compile the standalone components into separate packages. Then, we exclude the source of these components from the main project and add references to the packages instead. After this, we separate the plugins and themes from the main project as well. However, plugins and themes will be referencing the main app (the other way than the components).
The diagram above describes a PHP project separated into three general categories; firstly, the core app in the middle that references 3rd party components and libraries (like composer packages, .NET libraries, etc.). With PeachPie, these components can be compiled separately into their own packages. The app itself is then referenced by all its plugins and themes. Again, in this way plugins and themes can be compiled separately, depending on a pre-compiled version of your APP. We’ll demonstrate this in a future post using WordPress as an example.
In this way, every separate package references what it needs. The folder structure of the project can remain just as it was before with regular PHP, but the project files define the dependencies and source files for each component/package.
Want to test it out yourself? Try a simple package of PHP’s FPDF library on our GitHub repo. In there, we have a C# console app that uses a signed and compiled PHP library, built as a NuGet package.