murtukov/php-code-generator

生成PHP代码的库

v0.1.6 2021-05-04 16:39 UTC

This package is auto-updated.

Last update: 2024-09-22 01:05:29 UTC


README

生成PHP 7.4代码的库

Scrutinizer Code Quality Code Coverage Build Status Code Intelligence Status MIT license

安装

composer require murtukov/php-code-generator

组件

文件

use Murtukov\PHPCodeGenerator\PhpFile;

$file = PhpFile::new()->setNamespace('App\Generator');

$class = $file->createClass('MyClass');
$class->setExtends('App\My\BaseClass')
    ->addImplements(Traversable::class, JsonSerializable::class)
    ->setFinal()
    ->addDocBlock("This file was generated and shouldn't be modified")
    ->addConstructor();

echo $file;

结果

<?php

namespace App\Generator;

use App\My\BaseClass;

/**
 * This file was generated and shouldn't be modified
 */
final class MyClass extends BaseClass implements Traversable, JsonSerializable
{
    public function __construct()
    {
    }
}

use Murtukov\PHPCodeGenerator\Comment;
use Murtukov\PHPCodeGenerator\Literal;
use Murtukov\PHPCodeGenerator\Method;
use Murtukov\PHPCodeGenerator\Modifier;
use Murtukov\PHPCodeGenerator\PhpClass;

$class = PhpClass::new('Stringifier')
    ->addConst('KNOWN_TYPES', ['DYNAMIC', 'STATIC'], Modifier::PRIVATE)
    ->addProperty('errors', Modifier::PRIVATE, '', [])
    ->addImplements(JsonSerializable::class, ArrayAccess::class)
    ->setExtends(Exception::class)
    ->setFinal()
    ->addDocBlock('This is just a test class.');

$class->emptyLine();

$class->createConstructor()
    ->append('parent::__construct(...func_get_args())');

# Create a method separately
$method = Method::new('getErrors', Modifier::PUBLIC, 'array')
    ->append(Comment::slash('Add here your content...'))
    ->append('return ', new Literal('[]'))
;

$class->append($method);

echo $class;

结果

/**
 * This is just a test class.
 */
final class Stringifier extends Exception implements JsonSerializable, ArrayAccess
{
    private const KNOWN_TYPES = ['DYNAMIC', 'STATIC'];
    private $errors = [];
    
    public function __construct()
    {
        parent::__construct(...func_get_args());
    }
    
    public function getErrors(): array
    {
        // Add here your content...
        return [];
    }
}

接口

use Murtukov\PHPCodeGenerator\BlockInterface;
use Murtukov\PHPCodeGenerator\ConverterInterface;
use Murtukov\PHPCodeGenerator\Method;
use Murtukov\PHPCodeGenerator\Modifier;
use Murtukov\PHPCodeGenerator\PhpInterface;


$interface = PhpInterface::new('StringifierInterface')
    ->addExtends(BlockInterface::class, ConverterInterface::class)
    ->addConst('NAME', 'MyInterface')
    ->addConst('TYPE', 'Component')
    ->emptyLine()
    ->addSignature('parse', 'string');

$interface->emptyLine();

$signature = $interface->createSignature('stringify', 'string');
$signature->addArgument('escapeSlashes', 'bool', false);
$signature->addDocBlock('Convert value to string.');

$interface->emptyLine();

$dumpMethod = Method::new('dump', Modifier::PUBLIC, 'string');
$interface->addSignatureFromMethod($dumpMethod);

echo $interface;

结果

interface StringifierInterface extends BlockInterface, ConverterInterface
{
    public const NAME = 'MyInterface';
    public const TYPE = 'Component';
    
    public function parse(): string;

    /**
     * Convert value to string.
     */
    public function stringify(bool $escapeSlashes = false): string;

    public function dump(): string;
}

特性

use Murtukov\PHPCodeGenerator\Comment;
use Murtukov\PHPCodeGenerator\Literal;
use Murtukov\PHPCodeGenerator\Method;
use Murtukov\PHPCodeGenerator\Modifier;
use Murtukov\PHPCodeGenerator\PhpTrait;

$trait = PhpTrait::new('Stringifier')
    ->addProperty('cache', Modifier::PRIVATE, 'array', [])
    ->addProperty('heap', Modifier::PROTECTED, SplHeap::class, null)
    ->addDocBlock('This is just a test trait.')
    ->emptyLine();

$constructor = Method::new('__construct')
    ->append('parent::__construct(...func_get_args())');

$method = Method::new('getErrors', Modifier::PUBLIC, 'array')
    ->append(Comment::hash('Add your content here...'))
    ->append('return ', new Literal('[]'));

$trait->append($constructor);
$trait->emptyLine();
$trait->append($method);

echo $trait;

结果

/**
 * This is just a test class.
 */
trait Stringifier
{
    private array $cache = [];
    protected ?SplHeap $heap = null;
    
    public function __construct()
    {
        parent::__construct(...func_get_args());
    }
    
    public function getErrors(): array
    {
        # Add your content here...
        return [];
    }
}

函数

use Murtukov\PHPCodeGenerator\Argument;
use Murtukov\PHPCodeGenerator\Instance;
use Murtukov\PHPCodeGenerator\Func;

$func = Func::new('myMethod', 'void');

# Crete argument and return it
$func->createArgument('arg1', SplHeap::class, null)->setNullable();

# Create argument and return function
$func->addArgument('arg2', 'string', '');

# Adding argument from object
$func->add(Argument::new('arg3'));

# Add content
$func->append('$object = ', Instance::new(stdClass::class));

echo $func;

结果

function myMethod(?SplHeap $arg1 = null, string $arg2 = '', $arg3): void
{
    $object = new stdClass();
}

方法

use Murtukov\PHPCodeGenerator\Argument;
use Murtukov\PHPCodeGenerator\Instance;
use Murtukov\PHPCodeGenerator\Method;
use Murtukov\PHPCodeGenerator\Modifier;

$method = Method::new('myMethod', Modifier::PRIVATE, 'void');

# Crete argument and return it
$method->createArgument('arg1', SplHeap::class, null)->setNullable();

# Create argument and return function
$method->addArgument('arg2', 'string', '');

# Adding argument from object
$method->add(Argument::new('arg3'));

# Add content
$method->append('$object = ', Instance::new(stdClass::class));

echo $method;

结果

private function myMethod(?SplHeap $arg1 = null, string $arg2 = '', $arg3): void
{
    $object = new stdClass();
}

构造器属性提升

$method = Method::new('__construct');

$method->addArgument('firstName', 'string', Argument::NO_PARAM, Modifier::PRIVATE);
$method->addArgument('lastName', 'string', 'Kowalski', Modifier::PRIVATE);
$method->addArgument('age', 'int', 15);

$method->signature->setMultiline();

echo $method;

结果

public function __construct(
    private string $firstName,
    private string $lastName = 'Kowalski',
    int $age = 15
) {}

从类对象创建相同的内容

$class = PhpClass::new('MyClass');

$class->createConstructor()
      ->addArgument('firstName', 'string', Argument::NO_PARAM, Modifier::PRIVATE)
      ->addArgument('lastName', 'string', 'Kowalski', Modifier::PRIVATE)
      ->addArgument('age', 'int', 15)
      ->signature->setMultiline();

修饰符也可以直接设置在参数对象上

$argument = Argument::new("firstName")->setModifier(Modifier::PUBLIC);

$constructor->add($argument);

闭包

use Murtukov\PHPCodeGenerator\Argument;
use Murtukov\PHPCodeGenerator\Loop;

# Create closure with 'array' return type
$closure = Closure::new('array');

# Create argument
$closure->addArgument('value');

# Create argument and return it
$closure->createArgument('options')
    ->setType('array')
    ->setDefaultValue([]);

# Create argument from object
$arg = Argument::new('filter', 'bool', false);
$closure->add($arg);

# Add uses of external variables
$closure->bindVar('this');
# by reference
$closure->bindVar('global', true);

# Create foreach loop
$foreach = Loop::foreach('$options as &$option')
    ->append('unset($option)');

# Append foreach as content of the closure
$closure->append($foreach);

结果

function ($value, array $options = [], bool $filter = false) use ($this, &$global): array {
    foreach ($options as &$option) {
        unset($option);
    }
}

箭头函数

$arrow = ArrowFunction::new([
    'name' => 'Alrik',
    'age' => 30
]);

$arrow->setStatic();

echo $arrow;

结果

static fn() => [
    'name' => 'Alrik',
    'age' => 30,
]

对象实例化

use Murtukov\PHPCodeGenerator\Instance;

# Create an instance with a single argument
$instance = Instance::new('App\Entity\DateTime', '2000-01-01');

# Add a second argument
$instance->addArgument(null);

echo $instance;

结果

new DateTime('2000-01-01', null);

您可以通过在名称前加@符号来防止类限定符缩短。

$instance = Instance::new('@App\Entity\DateTime', '2000-01-01');

结果

new App\Entity\DateTime('2000-01-01', null);

可以使用静态配置类更改@抑制符号。

use Murtukov\PHPCodeGenerator\Config;

Config::$suppressSymbol = '~';

数组

此库提供了一个将变量转换为字符串的有用工具:Utils::stringify()。它类似于var_export函数,但提供了对数组格式的更多控制。数组也可以用Collection类包装,该类内部使用Utils::stringify()

具有0键定义的数组被认为是“数字”,默认情况下,它们内联且无键地转换成字符串。

use Murtukov\PHPCodeGenerator\Utils;

echo Utils::stringify(['Test', 100, 5.67, array(), true, NULL]);

结果

['Test', 100, 5.67, [], true, null]

所有其他数组都多行转换,并且具有键。

use Murtukov\PHPCodeGenerator\Utils;

echo Utils::stringify(['name' => 'Justin', 'age' => 25]);

结果

[
    'name' => 'Justin', 
    'age' => 25
]

如果您想自定义数组如何转换,只需将它们包装在Collection类中。

use Murtukov\PHPCodeGenerator\Collection;

echo Collection::numeric(['name' => 'Justin', 'age' => 25])
    ->setMultiline();

结果

[
    'Justin',
    25,
]

assoc集合示例

use Murtukov\PHPCodeGenerator\Collection;

echo Collection::assoc(['Tim', 'Max', 'Alfred']);

结果

[
    0 => 'Tim', 
    1 => 'Max', 
    2 => 'Alfred'
]

Collection类只对其顶层数组应用其格式化规则,所有嵌套数组使用默认格式化。

use Murtukov\PHPCodeGenerator\Collection;

echo Collection::numeric(['apple', 'banana', ['strawberry', 'tomato']])
    ->setMultiline();

结果

[
    'apple',
    'banana',
    ['strawberry', 'tomato'],
]

如果...否则

use Murtukov\PHPCodeGenerator\IfElse;
use Murtukov\PHPCodeGenerator\Text;

echo IfElse::new('$name === 15')
    ->append('$names = ', "['name' => 'Timur']")
    ->createElseIf(new Text('$name === 95'))
        ->append('return null')
    ->end()
    ->createElseIf('$name === 95')
        ->append('return null')
    ->end()
    ->createElse()
        ->append('$x = 95')
        ->append('return false')
    ->end();

结果

if ($name === 15) {
    $names = ['name' => 'Timur'];  
} elseif ('\$name === 95') {
    return null;
} elseif ($name === 95) {
    return null;
} else {
    $x = 95;
    return false;
}

循环

use Murtukov\PHPCodeGenerator\Comment;
use Murtukov\PHPCodeGenerator\Loop;

echo Loop::for('$i = 1; $i < 1000; ++$i')
    ->append('$x = $i')
    ->emptyLine()
    ->append(Comment::hash('Ok, stop now...'))
    ->append('break');

echo Loop::foreach('$apples as $apple')
    ->append('$x = $apple')
    ->append('continue');


echo Loop::while('true')
    ->append('$x = $i')
    ->append('break');


echo Loop::doWhile('true')
    ->append(Comment::block('Hello, World!'));

结果

for ($i = 1; $i < 1000; ++$i) {
    $x = $i;
    
    # Ok, stop now...
    break;
}

foreach ($apples as $apple) {
    $x = $apple;
    continue;
}

while (true) {
    $x = $i;
    break;
}

do {
    /*
     * Hello, World!
     */
} while (true)

注释

use Murtukov\PHPCodeGenerator\Comment;

echo Comment::block('Hello, World!');
echo Comment::hash('Hello, World!');
echo Comment::docBlock('Hello, World!');
echo Comment::slash('Hello, World!');

结果

/*
 * Hello, World!
 */

# Hello, World!

/**
 * Hello, World!
 */

// Hello, World!

命名空间

PhpFile组件在渲染过程中自动解析所有子组件中的类限定符。

use Murtukov\PHPCodeGenerator\Collection;
use Murtukov\PHPCodeGenerator\Instance;
use Murtukov\PHPCodeGenerator\PhpFile;

$file = PhpFile::new();

$class = $file->createClass('MyClass');
$construct = $class->createConstructor();

$array = Collection::numeric()
    ->push(Instance::new('App\Service\Converter'))
    ->push(Instance::new('App\Service\Normalizer'));

$construct->append('return ', $array);

echo $file;

结果

<?php

use App\Service\Converter;
use App\Service\Normalizer;

class MyClass
{
    public function __construct()
    {
        return [new Converter(), new Normalizer()];
    }
}

但是,除非包装在特殊对象中,否则不会从标量值和数组中解析类限定符。

use Murtukov\PHPCodeGenerator\Collection;
use Murtukov\PHPCodeGenerator\Literal;
use Murtukov\PHPCodeGenerator\PhpFile;

$file = PhpFile::new();

$class = $file->createClass('MyClass');
$construct = $class->createConstructor();


$array = Collection::numeric();

# This qualifiers are not resolved automatically
$array
    ->push(Literal::new('new App\Service\Converter()'))
    ->push('new App\Service\Normalizer()');

$construct->append('return ', $array);

结果

<?php

class MyClass
{
    public function __construct()
    {
        return [new App\Service\Converter(), 'new App\Service\Normalizer()'];
    }
}

您始终可以通过调用$file->addUse()$file->addUseGroup()手动添加use语句。

$file->addUse('App\Entity\User');
$file->addUse('App\Service\UserManager', 'Manager');
$file->addUseGroups('Symfony\Validator\Converters', 'NotNull', 'Length', 'Range');

结果

use App\Entity\User;
use App\Service\UserManager as Manager;
use Symfony\Validator\Converters\{NotNull, Length, Range};

尽管此库的所有组件都实现了魔法__toString()方法,但请避免将它们连接,因为这会将它们转换为字符串标量,并且所有类限定符都将丢失。

所以,而不是连接

$method->append('return ' . Instance::new('App\MyClass'));

将部分作为单独的参数传递,因为append是一个可变参数函数

$method->append('return ', Instance::new('App\MyClass'));

文字

按提供的格式生成代码(不进行任何附加处理)

echo Literal::new('$foo = "bar";');

结果

$foo = "bar";

字符串可以包含类似于sprintf函数的占位符

$literal = Literal::new(
    '$foo = %s; %s',
    Literal::new('"bar"'),
    Literal::new('echo $foo;')
);
echo $literal;

结果

$foo = "bar"; echo $foo;

转义保留的%字符

$literal = Literal::new(
    '$foo = %s; sprintf("This value should not be quoted %%s.", %s);',
    Literal::new('"bar"'),
    Literal::new('$foo')
);
echo $literal;

结果

$foo = "bar"; sprintf("This value should not be quoted %s.", $foo);

全局配置

所有全局配置都存储在Config类的静态属性中。

缩进

默认缩进包含4个空格。您可以通过重写$indent属性来更改它

use Murtukov\PHPCodeGenerator\Config;

Config::$indent = '  ';

缩短限定符

命名空间部分所述,类限定符会自动缩短并添加到PhpFile输出的顶部。为了禁用此功能,请重写$shortenQualifiers属性

use Murtukov\PHPCodeGenerator\Config;

Config::$shortenQualifiers = false;

抑制符号

对象实例化部分所述,您可以通过在前面加@符号来抑制类限定符的缩短。为了更改此符号,请重写$suppressSymbol属性

use Murtukov\PHPCodeGenerator\Config;

Config::$suppressSymbol = '%';