murtukov / php-code-generator
生成PHP代码的库
Requires
- php: >=7.4
- ext-json: *
Requires (Dev)
- phpstan/phpstan: ^0.12.58
- phpunit/phpunit: ^9.4.3
README
生成PHP 7.4代码的库
安装
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 = '%';