okapi/code-transformer 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

okapi/code-transformer

Composer 安装命令:

composer require okapi/code-transformer

包简介

PHP Code Transformer is a PHP library that allows you to modify and transform the source code of a loaded PHP class.

README 文档

README

PHP Code Transformer

License: MIT Twitter: @WalterWoshid PHP: >=8.1 Packagist Build

Coverage - PHP 8.1 Coverage - PHP 8.2

PHP Code Transformer is a PHP library that allows you to modify and transform the source code of a loaded PHP class.

Installation

composer require okapi/code-transformer

Usage

📖 List of contents

Create a Kernel

<?php

use Okapi\CodeTransformer\CodeTransformerKernel;

// Extend from the "CodeTransformerKernel" class
class Kernel extends CodeTransformerKernel
{
    // Define a list of transformer classes
    protected array $transformers = [
        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

// String Transformer

<?php

use Okapi\CodeTransformer\Transformer;
use Okapi\CodeTransformer\Transformer\Code;

// Extend from the "Transformer" class
class StringTransformer extends Transformer
{
    // Define the target class(es)
    public function getTargetClass(): string|array
    {
        // You can specify a single class or an array of classes
        // You can also use wildcards, see https://github.com/okapi-web/php-wildcards
        return MyTargetClass::class;
    }
    
    // The "transform" method will be called when the target class is loaded
    // Here you can modify the source code of the target class(es)
    public function transform(Code $code): void
    {
        // I recommend using the Microsoft\PhpParser library to parse the source
        // code. It's already included in the dependencies of this package and
        // the "$code->getSourceFileNode()" property contains the parsed source code.
        
        // But you can also use any other library or manually parse the source
        // code with basic PHP string functions and "$code->getOriginalSource()"

        $sourceFileNode = $code->getSourceFileNode();

        // Iterate over all nodes
        foreach ($sourceFileNode->getDescendantNodes() as $node) {
            // Find 'Hello World!' string
            if ($node instanceof StringLiteral
                && $node->getStringContentsText() === 'Hello World!'
            ) {
                // Replace it with 'Hello from Code Transformer!'
                // Edit method accepts a Token or Node class
                $code->edit(
                    $node->children,
                    "'Hello from Code Transformer!'",
                );
                
                // You can also manually edit the source code
                $code->editAt(
                    $node->getStartPosition() + 1,
                    $node->getWidth() - 2,
                    "Hello from Code Transformer!",
                );

                // Append a new line of code
                $code->append('$iAmAppended = true;');
            }
        }
    }
}
// UnPrivate Transformer

<?php

namespace Okapi\CodeTransformer\Tests\Stubs\Transformer;

use Microsoft\PhpParser\TokenKind;
use Okapi\CodeTransformer\Transformer;
use Okapi\CodeTransformer\Transformer\Code;

// Replace all "private" keywords with "public"
class UnPrivateTransformer extends Transformer
{
    public function getTargetClass(): string|array
    {
        return MyTargetClass::class;
    }

    public function transform(Code $code): void
    {
        $sourceFileNode = $code->getSourceFileNode();

        // Iterate over all tokens
        foreach ($sourceFileNode->getDescendantTokens() as $token) {
            // Find "private" keyword
            if ($token->kind === TokenKind::PrivateKeyword) {
                // Replace it with "public"
                $code->edit($token, 'public');
            }
        }
    }
}

Target Class

<?php

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

    private function myPrivateMethod(): void
    {
        echo 'Hello World!';
    }
}

Initialize the Kernel

// Initialize the kernel early in the application lifecycle
// Preferably after the autoloader is registered

<?php

use MyKernel;

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

// Initialize the Code Transformer Kernel
$kernel = MyKernel::init();

Target Class (transformed)

<?php

class MyTargetClass
{
    public string $myPrivateProperty = "You can't get me!";
    
    public function myPrivateMethod(): void
    {
        echo 'Hello from Code Transformer!';
    }
}
$iAmAppended = true;

Result

<?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 one. The problem with this is if you add or remove a line of code, xdebug will point to the wrong line, so try to keep the number of lines the same as the original source code.

How it works

  • The CodeTransformerKernel registers multiple services

    • 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 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

General workflow when a class is loaded

  • The AutoloadInterceptor service intercepts the loading of a class

  • 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

    • 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
      • 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, 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, 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

Testing

  • Run composer run-script test
    or
  • Run composer run-script test-coverage

Show your support

Give a ⭐ if this project helped you!

🙏 Thanks

📝 License

Copyright © 2023 Valentin Wotschel.
This project is MIT licensed.

okapi/code-transformer 适用场景与选型建议

okapi/code-transformer 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 14.25k 次下载、GitHub Stars 达 8, 最近一次更新时间为 2023 年 02 月 23 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「php」 「code」 「transformer」 「php-code」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 okapi/code-transformer 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 okapi/code-transformer 我们能提供哪些服务?
定制开发 / 二次开发

基于 okapi/code-transformer 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 14.25k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 9
  • 点击次数: 24
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 8
  • Watchers: 0
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2023-02-23