nette / php-generator
🐘 Nette PHP Generator:为您生成整洁的PHP代码。支持PHP 8.3的新特性。
Requires
- php: 8.0 - 8.4
- nette/utils: ^3.2.9 || ^4.0
Requires (Dev)
- jetbrains/phpstorm-attributes: dev-master
- nette/tester: ^2.4
- nikic/php-parser: ^4.18 || ^5.0
- phpstan/phpstan: ^1.0
- tracy/tracy: ^2.8
Suggests
- nikic/php-parser: to use ClassType::from(withBodies: true) & ClassType::fromCode()
- dev-master / 4.1.x-dev
- v4.1.6
- v4.1.5
- v4.1.4
- v4.1.3
- v4.1.2
- v4.1.1
- v4.1.0
- v4.0.x-dev
- v4.0.9
- v4.0.8
- v4.0.7
- v4.0.6
- v4.0.5
- v4.0.3
- v4.0.2
- v4.0.1
- v4.0.0
- v3.6.x-dev
- v3.6.9
- v3.6.8
- v3.6.7
- v3.6.6
- v3.6.5
- v3.6.4
- v3.6.2
- v3.6.1
- v3.6.0
- v3.5.x-dev
- v3.5.4
- v3.5.3
- v3.5.2
- v3.5.1
- v3.5.0
- v3.4.x-dev
- v3.4.1
- v3.4.0
- v3.3.x-dev
- v3.3.4
- v3.3.3
- v3.3.2
- v3.3.1
- v3.3.0
- v3.2.x-dev
- v3.2.3
- v3.2.2
- v3.2.1
- v3.2.0
- v3.1.x-dev
- v3.1.4
- v3.1.3
- v3.1.2
- v3.1.1
- v3.1.0
- v3.0.x-dev
- v3.0.5
- v3.0.4
- v3.0.3
- v3.0.2
- v3.0.1
- v3.0.0
- v2.6.x-dev
- v2.6.4
- v2.6.3
- v2.6.2
- v2.6.1
- v2.6.0
- v2.5.x-dev
- v2.5.0
- v2.4.x-dev
- v2.4.1
- v2.4.0
- v2.3.x-dev
- v2.3.6
- v2.3.5
- v2.3.4
- v2.3.3
- v2.3.2
- v2.3.1
- v2.3.0
- 2.2.x-dev
- v2.2.3
- v2.2.2
- v2.2.1
- v2.2.0
- dev-grouped-uses
This package is auto-updated.
Last update: 2024-09-18 11:11:14 UTC
README
您是否在寻找用于生成类、函数或完整PHP文件的工具?
✅ 支持所有最新的PHP特性,如枚举、属性等。
✅ 允许您轻松修改现有类
✅ 输出符合PSR-12 / PER编码风格的代码
✅ 高度成熟、稳定且广泛使用的库
安装
使用Composer工具下载并安装库
composer require nette/php-generator
PhpGenerator 4.1与PHP 8.0至8.4兼容。文档可以在库的网站上找到:库网站。
支持我
你喜欢PHP Generator吗?你是否期待新特性?
谢谢!
类
让我们从一个使用ClassType创建类的例子开始
$class = new Nette\PhpGenerator\ClassType('Demo'); $class ->setFinal() ->setExtends(ParentClass::class) ->addImplement(Countable::class) ->addComment("Class description.\nSecond line\n") ->addComment('@property-read Nette\Forms\Form $form'); // generate code simply by typecasting to string or using echo: echo $class;
这将返回
/** * Class description * Second line * * @property-read Nette\Forms\Form $form */ final class Demo extends ParentClass implements Countable { }
要生成代码,您还可以使用所谓的打印机,它不同于echo $class
,可以进行进一步配置
$printer = new Nette\PhpGenerator\Printer; echo $printer->printClass($class);
您可以添加常量(类Constant)和属性(类Property)
$class->addConstant('ID', 123) ->setProtected() // constant visibility ->setType('int') ->setFinal(); $class->addProperty('items', [1, 2, 3]) ->setPrivate() // or setVisibility('private') ->setStatic() ->addComment('@var int[]'); $class->addProperty('list') ->setType('?array') ->setInitialized(); // outputs '= null'
这将生成
final protected const int ID = 123; /** @var int[] */ private static $items = [1, 2, 3]; public ?array $list = null;
您还可以添加方法
$method = $class->addMethod('count') ->addComment('Count it.') ->setFinal() ->setProtected() ->setReturnType('?int') // return types for methods ->setBody('return count($items ?: $this->items);'); $method->addParameter('items', []) // $items = [] ->setReference() // &$items = [] ->setType('array'); // array &$items = []
结果是
/** * Count it. */ final protected function count(array &$items = []): ?int { return count($items ?: $this->items); }
PHP 8.0中引入的推广参数可以传递给构造函数
$method = $class->addMethod('__construct'); $method->addPromotedParameter('name'); $method->addPromotedParameter('args', []) ->setPrivate();
结果是
public function __construct( public $name, private $args = [], ) { }
使用setReadOnly()
函数可以标记只读属性和类。
如果添加的属性、常量、方法或参数已存在,则会抛出异常。
可以使用removeProperty()
、removeConstant()
、removeMethod()
或removeParameter()
移除类成员。
您还可以将现有的Method
、Property
或Constant
对象添加到类中
$method = new Nette\PhpGenerator\Method('getHandle'); $property = new Nette\PhpGenerator\Property('handle'); $const = new Nette\PhpGenerator\Constant('ROLE'); $class = (new Nette\PhpGenerator\ClassType('Demo')) ->addMember($method) ->addMember($property) ->addMember($const);
您还可以使用cloneWithName()
在不同名称下克隆现有的方法、属性和常量
$methodCount = $class->getMethod('count'); $methodRecount = $methodCount->cloneWithName('recount'); $class->addMember($methodRecount);
接口或特质
您可以创建接口和特质(类InterfaceType和TraitType)
$interface = new Nette\PhpGenerator\InterfaceType('MyInterface'); $trait = new Nette\PhpGenerator\TraitType('MyTrait');
使用特质
$class = new Nette\PhpGenerator\ClassType('Demo'); $class->addTrait('SmartObject'); $class->addTrait('MyTrait') ->addResolution('sayHello as protected') ->addComment('@use MyTrait<Foo>'); echo $class;
结果是
class Demo { use SmartObject; /** @use MyTrait<Foo> */ use MyTrait { sayHello as protected; } }
枚举
您可以轻松创建PHP 8.1中引入的枚举,如下所示(类EnumType)
$enum = new Nette\PhpGenerator\EnumType('Suit'); $enum->addCase('Clubs'); $enum->addCase('Diamonds'); $enum->addCase('Hearts'); $enum->addCase('Spades'); echo $enum;
结果是
enum Suit { case Clubs; case Diamonds; case Hearts; case Spades; }
您还可以定义标量等效项并创建“支持”枚举
$enum->addCase('Clubs', '♣'); $enum->addCase('Diamonds', '♦');
对于每个案例,您可以使用addComment()
或addAttribute()
添加注释或属性。
匿名类
将名称传递为null
,您就有一个匿名类
$class = new Nette\PhpGenerator\ClassType(null); $class->addMethod('__construct') ->addParameter('foo'); echo '$obj = new class ($val) ' . $class . ';';
结果是
$obj = new class ($val) { public function __construct($foo) { } };
全局函数
函数的代码由类GlobalFunction生成
$function = new Nette\PhpGenerator\GlobalFunction('foo'); $function->setBody('return $a + $b;'); $function->addParameter('a'); $function->addParameter('b'); echo $function; // or use the PsrPrinter for output compliant with PSR-2 / PSR-12 / PER // echo (new Nette\PhpGenerator\PsrPrinter)->printFunction($function);
结果是
function foo($a, $b) { return $a + $b; }
匿名函数
匿名函数的代码由类Closure生成
$closure = new Nette\PhpGenerator\Closure; $closure->setBody('return $a + $b;'); $closure->addParameter('a'); $closure->addParameter('b'); $closure->addUse('c') ->setReference(); echo $closure; // or use the PsrPrinter for output compliant with PSR-2 / PSR-12 / PER // echo (new Nette\PhpGenerator\PsrPrinter)->printClosure($closure);
结果是
function ($a, $b) use (&$c) { return $a + $b; }
短箭头函数
您还可以使用打印器输出短匿名函数
$closure = new Nette\PhpGenerator\Closure; $closure->setBody('$a + $b'); $closure->addParameter('a'); $closure->addParameter('b'); echo (new Nette\PhpGenerator\Printer)->printArrowFunction($closure);
结果是
fn($a, $b) => $a + $b
方法和函数签名
方法由类Method表示。您可以设置可见性、返回值、添加注释、属性等。
$method = $class->addMethod('count') ->addComment('Count it.') ->setFinal() ->setProtected() ->setReturnType('?int');
单个参数由类Parameter表示。同样,您可以设置所有可想象到的属性
$method->addParameter('items', []) // $items = [] ->setReference() // &$items = [] ->setType('array'); // array &$items = [] // function count(&$items = [])
要定义所谓的变长参数(或也称为splat、spread、省略号、解包或三点运算符),请使用setVariadic()
$method = $class->addMethod('count'); $method->setVariadic(true); $method->addParameter('items');
这将生成
function count(...$items) { }
方法和函数体
可以将整个体一次传递给setBody()
方法,或者逐行(逐行)通过重复调用addBody()
来逐步传递
$function = new Nette\PhpGenerator\GlobalFunction('foo'); $function->addBody('$a = rand(10, 20);'); $function->addBody('return $a;'); echo $function;
结果是
function foo() { $a = rand(10, 20); return $a; }
您可以使用特殊占位符轻松插入变量。
简单占位符?
$str = 'any string'; $num = 3; $function = new Nette\PhpGenerator\GlobalFunction('foo'); $function->addBody('return substr(?, ?);', [$str, $num]); echo $function;
结果是
function foo() { return substr('any string', 3); }
变长占位符...?
$items = [1, 2, 3]; $function = new Nette\PhpGenerator\GlobalFunction('foo'); $function->setBody('myfunc(...?);', [$items]); echo $function;
结果是
function foo() { myfunc(1, 2, 3); }
您还可以使用PHP 8的命名参数...?:
$items = ['foo' => 1, 'bar' => true]; $function->setBody('myfunc(...?:);', [$items]); // myfunc(foo: 1, bar: true);
占位符使用反斜杠\?
转义
$num = 3; $function = new Nette\PhpGenerator\GlobalFunction('foo'); $function->addParameter('a'); $function->addBody('return $a \? 10 : ?;', [$num]); echo $function;
结果是
function foo($a) { return $a ? 10 : 3; }
打印器和PSR合规性
使用Printer类生成PHP代码
$class = new Nette\PhpGenerator\ClassType('Demo'); // ... $printer = new Nette\PhpGenerator\Printer; echo $printer->printClass($class); // same as: echo $class
它可以生成所有其他元素的代码,提供如printFunction()
、printNamespace()
等方法。
还有PsrPrinter
类,它按照PSR-2 / PSR-12 / PER编码风格输出
$printer = new Nette\PhpGenerator\PsrPrinter; echo $printer->printClass($class);
需要自定义行为?通过继承Printer
类创建自己的版本。您可以重新配置这些变量
class MyPrinter extends Nette\PhpGenerator\Printer { // length of the line after which the line will break public int $wrapLength = 120; // indentation character, can be replaced with a sequence of spaces public string $indentation = "\t"; // number of blank lines between properties public int $linesBetweenProperties = 0; // number of blank lines between methods public int $linesBetweenMethods = 2; // number of blank lines between 'use statements' groups for classes, functions, and constants public int $linesBetweenUseTypes = 0; // position of the opening curly brace for functions and methods public bool $bracesOnNextLine = true; // place one parameter on one line, even if it has an attribute or is supported public bool $singleParameterOnOneLine = false; // omits namespaces that do not contain any class or function public bool $omitEmptyNamespaces = true; // separator between the right parenthesis and return type of functions and methods public string $returnTypeColon = ': '; }
标准和Printer
之间的差异是什么?为什么包中没有只有一个打印器,即PsrPrinter
?
标准Printer
将代码格式化为我们在Nette中一直所做的那样。由于Nette建立的时间比PSR早得多,而且PSR花了多年时间按计划交付标准,有时甚至在PHP推出新功能后几年,这导致了一个在几个小方面有所不同的编码标准。主要区别在于使用制表符而不是空格。我们知道,在我们的项目中使用制表符,我们可以进行宽度自定义,这对有视觉障碍的人是必不可少的。一个小差异的例子是将花括号放在函数和方法的单独一行上,始终如此。PSR的建议在我们看来似乎是不合逻辑的,并且会导致代码清晰度降低。
类型
每个类型或联合/交集类型都可以作为字符串传递;您还可以使用原生类型的预定义常量
use Nette\PhpGenerator\Type; $member->setType('array'); // or Type::Array; $member->setType('?array'); // or Type::nullable(Type::Array); $member->setType('array|string'); // or Type::union(Type::Array, Type::String) $member->setType('Foo&Bar'); // or Type::intersection(Foo::class, Bar::class) $member->setType(null); // removes the type
同样也适用于setReturnType()
方法。
文字
使用Literal
,您可以传递任何PHP代码,例如,用于默认属性值或参数等
use Nette\PhpGenerator\Literal; $class = new Nette\PhpGenerator\ClassType('Demo'); $class->addProperty('foo', new Literal('Iterator::SELF_FIRST')); $class->addMethod('bar') ->addParameter('id', new Literal('1 + 2')); echo $class;
结果
class Demo { public $foo = Iterator::SELF_FIRST; public function bar($id = 1 + 2) { } }
您还可以向 Literal
传递参数,并使用占位符将它们格式化为有效的 PHP 代码,具体请参阅 占位符
new Literal('substr(?, ?)', [$a, $b]); // generates for example: substr('hello', 5);
可以使用 new
方法轻松生成表示创建新对象的字面量
Literal::new(Demo::class, [$a, 'foo' => $b]); // generates for example: new Demo(10, foo: 20)
属性
从 PHP 8 开始,您可以向所有类、方法、属性、常量、枚举情况、函数、闭包和参数添加属性。您还可以将 字面量 用作参数值。
$class = new Nette\PhpGenerator\ClassType('Demo'); $class->addAttribute('Table', [ 'name' => 'user', 'constraints' => [ Literal::new('UniqueConstraint', ['name' => 'ean', 'columns' => ['ean']]), ], ]); $class->addProperty('list') ->addAttribute('Deprecated'); $method = $class->addMethod('count') ->addAttribute('Foo\Cached', ['mode' => true]); $method->addParameter('items') ->addAttribute('Bar'); echo $class;
结果
#[Table(name: 'user', constraints: [new UniqueConstraint(name: 'ean', columns: ['ean'])])] class Demo { #[Deprecated] public $list; #[Foo\Cached(mode: true)] public function count( #[Bar] $items, ) { } }
命名空间
类、特性、接口和枚举(以下统称为类)可以组合成由 PhpNamespace 类表示的命名空间
$namespace = new Nette\PhpGenerator\PhpNamespace('Foo'); // create new classes in the namespace $class = $namespace->addClass('Task'); $interface = $namespace->addInterface('Countable'); $trait = $namespace->addTrait('NameAware'); // or insert an existing class into the namespace $class = new Nette\PhpGenerator\ClassType('Task'); $namespace->add($class);
如果类已经存在,则会抛出异常。
您可以定义使用条款
// use Http\Request; $namespace->addUse(Http\Request::class); // use Http\Request as HttpReq; $namespace->addUse(Http\Request::class, 'HttpReq'); // use function iter\range; $namespace->addUseFunction('iter\range');
要简化基于定义的别名的完全限定类、函数或常量名称,请使用 simplifyName
方法
echo $namespace->simplifyName('Foo\Bar'); // 'Bar', because 'Foo' is the current namespace echo $namespace->simplifyName('iter\range', $namespace::NameFunction); // 'range', due to the defined use-statement
相反,您可以使用 resolveName
方法将简化的类、函数或常量名称转换回完全限定名称
echo $namespace->resolveName('Bar'); // 'Foo\Bar' echo $namespace->resolveName('range', $namespace::NameFunction); // 'iter\range'
类名称解析
当类属于命名空间时,其表示方式略有不同:所有类型(例如,类型提示、返回类型、父类名称、实现的接口、使用的特性、属性和属性)都会自动 解析(除非您将其关闭,见下文)。这意味着您必须在定义中使用 完全限定类名称,它们将在生成的代码中替换为基于使用条款的别名或完全限定名称
$namespace = new Nette\PhpGenerator\PhpNamespace('Foo'); $namespace->addUse('Bar\AliasedClass'); $class = $namespace->addClass('Demo'); $class->addImplement('Foo\A') // will be simplified to A ->addTrait('Bar\AliasedClass'); // will be simplified to AliasedClass $method = $class->addMethod('method'); $method->addComment('@return ' . $namespace->simplifyType('Foo\D')); // we manually simplify in comments $method->addParameter('arg') ->setType('Bar\OtherClass'); // will be translated to \Bar\OtherClass echo $namespace; // or use the PsrPrinter for output in accordance with PSR-2 / PSR-12 / PER // echo (new Nette\PhpGenerator\PsrPrinter)->printNamespace($namespace);
结果
namespace Foo; use Bar\AliasedClass; class Demo implements A { use AliasedClass; /** * @return D */ public function method(\Bar\OtherClass $arg) { } }
可以通过这种方式关闭自动解析
$printer = new Nette\PhpGenerator\Printer; // or PsrPrinter $printer->setTypeResolving(false); echo $printer->printNamespace($namespace);
PHP 文件
类、函数和命名空间可以组合成由 PhpFile 类表示的 PHP 文件
$file = new Nette\PhpGenerator\PhpFile; $file->addComment('This file is auto-generated.'); $file->setStrictTypes(); // adds declare(strict_types=1) $class = $file->addClass('Foo\A'); $function = $file->addFunction('Foo\foo'); // or // $namespace = $file->addNamespace('Foo'); // $class = $namespace->addClass('A'); // $function = $namespace->addFunction('foo'); echo $file; // or use the PsrPrinter for output in accordance with PSR-2 / PSR-12 / PER // echo (new Nette\PhpGenerator\PsrPrinter)->printFile($file);
结果
<?php /** * This file is auto-generated. */ declare(strict_types=1); namespace Foo; class A { } function foo() { }
请注意:除函数和类之外,文件中不能添加其他代码。
从现有内容生成
除了可以使用上述 API 模型类和函数之外,您还可以使用现有内容自动生成它们
// creates a class identical to the PDO class $class = Nette\PhpGenerator\ClassType::from(PDO::class); // creates a function identical to the trim() function $function = Nette\PhpGenerator\GlobalFunction::from('trim'); // creates a closure based on the provided one $closure = Nette\PhpGenerator\Closure::from( function (stdClass $a, $b = null) {}, );
默认情况下,函数和方法体是空的。如果您也想加载它们,请使用此方法(需要安装 nikic/php-parser
包)
$class = Nette\PhpGenerator\ClassType::from(Foo::class, withBodies: true); $function = Nette\PhpGenerator\GlobalFunction::from('foo', withBody: true);
从 PHP 文件加载
您还可以从包含 PHP 代码的字符串中直接加载函数、类、接口和枚举。例如,要创建一个 ClassType
对象
$class = Nette\PhpGenerator\ClassType::fromCode(<<<XX <?php class Demo { public $foo; } XX);
在从 PHP 代码加载类时,方法体外的单行注释将被忽略(例如,用于属性等),因为此库没有与之交互的 API。
您还可以直接加载整个 PHP 文件,该文件可以包含任何数量的类、函数,甚至是命名空间
$file = Nette\PhpGenerator\PhpFile::fromCode(file_get_contents('classes.php'));
文件的初始注释和 strict_types
声明也会被加载。但是,所有其他全局代码将被忽略。
需要安装 nikic/php-parser
。
(如果您需要在文件中操作全局代码或方法体中的单个语句,则最好直接使用 nikic/php-parser
库。)
类操作器
ClassManipulator 类提供用于操作类的工具。
$class = new Nette\PhpGenerator\ClassType('Demo'); $manipulator = new Nette\PhpGenerator\ClassManipulator($class);
inheritMethod()
方法将父类或实现的接口中的方法复制到您的类中。这允许您覆盖方法或扩展其签名
$method = $manipulator->inheritMethod('bar'); $method->setBody('...');
inheritProperty()
方法将父类中的属性复制到您的类中。当您想在类中拥有相同的属性,但可能具有不同的默认值时,这很有用
$property = $manipulator->inheritProperty('foo'); $property->setValue('new value');
implementInterface()
方法自动在您的类中实现给定接口的所有方法
$manipulator->implementInterface(SomeInterface::class); // Now your class implements SomeInterface and includes all its methods
变量转储
《Dumper》类将变量转换为可解析的PHP代码。它提供的输出比标准的《var_export()`函数》更清晰、更易于阅读。
$dumper = new Nette\PhpGenerator\Dumper; $var = ['a', 'b', 123]; echo $dumper->dump($var); // outputs ['a', 'b', 123]