Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 85 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,19 @@ composer require okapi/code-transformer

## 📖 List of contents

- [Create a kernel](#create-a-kernel)
- [Create a transformer](#create-a-transformer)
- [Initialize the kernel](#initialize-the-kernel)
- [Create a Kernel](#create-a-kernel)
- [Create a Transformer](#create-a-transformer)
- [Target Class](#target-class)
- [Initialize the Kernel](#initialize-the-kernel)
- [Target Class (transformed)](#target-class-transformed)
- [Result](#result)
- [Limitations](#limitations)
- [How it works](#how-it-works)
- [Testing](#testing)



## Create a kernel
## Create a Kernel

```php
<?php
Expand All @@ -82,11 +86,19 @@ class Kernel extends CodeTransformerKernel
StringTransformer::class,
UnPrivateTransformer::class,
];

// Define the settings of the kernel from the "protected" properties

// The directory where the transformed source code will be stored
protected ?string $cacheDir = __DIR__ . '/var/cache';

// The cache file mode
protected ?int $cacheFileMode = 0777;
}
```


## Create a transformer
## Create a Transformer

```php
// String Transformer
Expand Down Expand Up @@ -127,7 +139,7 @@ class StringTransformer extends Transformer
&& $node->getStringContentsText() === 'Hello World!'
) {
// Replace it with 'Hello from Code Transformer!'
// Edit method accepts a Token class
// Edit method accepts a Token or Node class
$code->edit(
$node->children,
"'Hello from Code Transformer!'",
Expand Down Expand Up @@ -184,59 +196,43 @@ class UnPrivateTransformer extends Transformer
```


## Initialize the kernel
## Target Class

```php
// Initialize the kernel early in the application lifecycle

<?php

use MyKernel;

require_once __DIR__ . '/vendor/autoload.php';
class MyTargetClass
{
private string $myPrivateProperty = "You can't get me!";

$kernel = new MyKernel(
// The directory where the transformed source code will be stored
cacheDir: __DIR__ . '/var/cache',

// The cache file mode
cacheFileMode: 0777,
);
private function myPrivateMethod(): void
{
echo 'Hello World!';
}
}
```


## Result
## Initialize the Kernel

```php
<?php

// Just use your classes as usual
$myTargetClass = new MyTargetClass();
// Initialize the kernel early in the application lifecycle
// Preferably after the autoloader is registered

$myTargetClass->myPrivateProperty; // You can't get me!
$myTargetClass->myPrivateMethod(); // Hello from Code Transformer!
```
<?php

use MyKernel;

```php
// MyTargetClass.php
require_once __DIR__ . '/vendor/autoload.php';

<?php
// Initialize the Code Transformer Kernel
$kernel = MyKernel::init();
```

class MyTargetClass
{
private string $myPrivateProperty = "You can't get me!";

private function myPrivateMethod(): void
{
echo 'Hello World!';
}
}
```
## Target Class (transformed)

```php
// MyTargetClass.php (transformed)

<?php

class MyTargetClass
Expand All @@ -252,6 +248,20 @@ $iAmAppended = true;
```


## Result

```php
<?php

// Just use your classes as usual
$myTargetClass = new MyTargetClass();

$myTargetClass->myPrivateProperty; // You can't get me!
$myTargetClass->myPrivateMethod(); // Hello from Code Transformer!
```



# Limitations

- Normally xdebug will point to the original source code, not the transformed
Expand All @@ -263,36 +273,47 @@ $iAmAppended = true;

# How it works

- The `Kernel` registers multiple services
- The `CodeTransformerKernel` registers multiple services

- The `TransformerContainer` service stores the list of transformers and their configuration
- The `TransformerManager` service stores the list of transformers and their
configuration

- The `CacheStateManager` service manages the cache state

- The `StreamFilter` service registers a [PHP Stream Filter](https://www.php.net/manual/wrappers.php.php#wrappers.php.filter)
- The `StreamFilter` service registers a
[PHP Stream Filter](https://www.php.net/manual/wrappers.php.php#wrappers.php.filter)
which allows to modify the source code before it is loaded by PHP

- The `AutoloadInterceptor` service overloads the Composer autoloader, which handles the loading of classes
- The `AutoloadInterceptor` service overloads the Composer autoloader,
which handles the loading of classes


## General workflow when a class is loaded

- The `AutoloadInterceptor` service intercepts the loading of a class
- It expects a class file path

- The `TransformerContainer` matches the class name with the list of transformer target classes
- The `TransformerMatcher` matches the class name with the list of transformer
target classes

- If the class is matched, query the cache state to see if the transformed
source code is already cached

- If the class is matched, we query the cache state to see if the transformed source code is already cached
- Check if the cache is valid:
- Modification time of the caching process is less than the modification time of the source file or the transformers
- Modification time of the caching process is less than the modification
time of the source file or the transformers
- Check if the cache file, the source file and the transformers exist
- Check if the number of transformers is the same as the number of transformers in the cache
- If the cache is valid, we load the transformed source code from the cache
- If not, we convert the class file path to a stream filter path
- Check if the number of transformers is the same as the number of
transformers in the cache

- If the cache is valid, load the transformed source code from the cache
- If not, return a stream filter path to the `AutoloadInterceptor` service

- The `StreamFilter` modifies the source code by applying the matching transformers
- If the modified source code is different from the original source code, we cache the transformed source code
- If not, we cache it anyway, but without a cached source file path, so that the transformation process is not repeated
- The `StreamFilter` modifies the source code by applying the matching
transformers
- If the modified source code is different from the original source code,
cache the transformed source code
- If not, cache it anyway, but without a cached source file path,
so that the transformation process is not repeated



Expand All @@ -310,6 +331,15 @@ Give a ⭐ if this project helped you!



## 🙏 Thanks

- Big thanks to [lisachenko](https://github.com/lisachenko) for their pioneering
work on the [Go! Aspect-Oriented Framework for PHP](https://github.com/goaop/framework).
This project drew inspiration from their innovative approach and served as a
foundation for this project.



## 📝 License

Copyright © 2023 [Valentin Wotschel](https://github.com/WalterWoshid).<br>
Expand Down
32 changes: 0 additions & 32 deletions TODO.md

This file was deleted.

8 changes: 6 additions & 2 deletions src/CodeTransformerKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ abstract class CodeTransformerKernel
#[Inject]
private Options $options;

/** @internal */
#[Inject]
protected TransformerManager $transformerManager;

Expand All @@ -44,7 +45,10 @@ abstract class CodeTransformerKernel
private AutoloadInterceptor $autoloadInterceptor;

/**
* Make the constructor public to allow the DI container to instantiate the class.
* Make the constructor public to allow the DI container to instantiate the
* class.
*
* @internal
*/
public function __construct() {}

Expand All @@ -70,7 +74,7 @@ public function __construct() {}

/**
* Enable debug mode. This will disable the cache.
* <br><b>Default:</b> false<br>
* <br><b>Default:</b> {@link false}<br>
*
* @var bool
*/
Expand Down