Important
This section is under heavy development and will be updated soon.
Darken PHP compiles your code into a polyfill and a compiled file. The polyfill is a namespaced file that loads the compiled file and renders it. The compiled file contains your original code but with some modifications to make it work with the polyfill.
sequenceDiagram participant Original Code participant Compiled Code participant Polyfill Original Code-->>Compiled Code: Compile Original Code Compiled Code-->>Polyfill: Inlclude Compiled Code in Polyfill
This is what the compile process looks like:
flowchart TD A[Original File] --> B B(Build Process) --> C & D C[Compile] D[Polyfill]
The runtime is loaded in the polyfill, and the compiled file is rendered by the polyfill. This is what the process looks like:
flowchart TD A[Runtime] --> B B[Polyfill] --> | render | C C[Compiled File]
To understand how Darken works, we need to understand the concept of compiling.
All components (or pages, which are technically just components) use anonymous classes. This allows you to put logic into the class but also directly use it in the template.
Example File: pages/test.php
$foo = new class {
public string $bar = 'baz';
public function largeBar(): string
{
return strtoupper($this->bar);
}
};
?>
<h1><?= $foo->largeBar(); ?></h1>
Since these files are not namespaced, we need to create a file in the .build
directory that is resolvable by a namespaced file and loads the original code. The namespaced file is what we call the polyfill file, and the original file will be compiled—this is called the compiled file. This means the above example file pages/test.php
will be compiled to:
.build/pages/test.php
with namespace Build\pages\test
, class name test
.build/pages/test.compiled.php
This is what the polyfill file looks like:
<?php
namespace Build\pages;
class test extends \Darken\Code\Runtime
{
public function renderFilePath(): string
{
return dirname(__FILE__) . DIRECTORY_SEPARATOR . 'test.compiled.php';
}
}
When you now open the namespaced polyfill and render it:
<?= (new Build\pages\test())->render(); ?>
render()
is defined in the ↱ Darken\Code\Runtime and invokes and handles the definedrenderFilePath()
method
The polyfill will be loaded and the compiled file (which contains your original code) will be invoked and rendered. It's important to understand that during the compile process, the original file will be modified, and the ↱ Darken\Code\Runtime class will be injected into the class constructor. Since it's loaded in the ↱ Darken\Code\Runtime class, we also have access to this object, which allows us to transfer data between the polyfill and the compiled file.
So the compiled code from above would look something like this:
$foo = new class($this) {
public function __construct(\Darken\Code\Runtime $runtime)
{
$this->runtime = $runtime;
}
public function bar(): string
{
return 'baz';
}
};
?>
<h1><?= $foo->bar(); ?></h1>
This is where the only magic happens. Since you cannot have a constructor in an anonymous class and directly run this object without creating it first, Darken does this during the compile step for you. This is, of course, only needed when you have to interact with the runtime object—for example, getting the router parameters, using Dependency Injection, collecting query params, or handling other runtime-related tasks. Therefore, PHP attributes can be used to describe a property or class, and Darken will inject the correct code during build time.
## Example with Constructor Parameters
To automatically inject a constructor parameter into a class attribute, you can use the ↱ Darken\Attributes\ConstructorParam attribute. This ensures that the class generates a constructor where you have to enter a valid, correctly typed parameter:
$obj = new class {
#[\Darken\Attributes\ConstructorParam]
public string $bar;
};
?>
<h1><?= $obj->bar; ?></h1>
This will resolve the constructor parameter bar
from the runtime object and inject it into the class attribute, with perfectly typed properties. To understand how this works, let's take a look at the polyfill code for the above example:
<?php
namespace Build\pages\sub;
class Test extends \Darken\Code\Runtime
{
public function __construct(string $bar)
{
$this->setData('constructorParams', 'bar', $title);
}
public function renderFilePath(): string
{
return dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Test.compiled.php';
}
}
Now the compiled code will inject the constructor parameter in the constructor. This looks like this (simplified):
$obj = new class($this)
{
#[\Darken\Attributes\ConstructorParam]
public string $bar;
public function __construct(\Darken\Code\Runtime $runtime)
{
$this->bar = $runtime->getData('constructorParams', 'bar');
}
};
The methods getData
is provided by the ↱ Darken\Code\Runtime class and allow communication between the polyfill and the compiled file, since they share the same ↱ Darken\Code\Runtime class.