Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

🐘 Generates neat PHP code for you. Supports new PHP 8.4 features.

License

NotificationsYou must be signed in to change notification settings

nette/php-generator

Repository files navigation

Nette PHP Generator

Latest Stable VersionDownloads this Month

 

Are you looking for a tool to generate PHP code forclasses,functions, or completePHP files?

✅ Supports all the latest PHP features likeproperty hooks,enums,attributes, etc.
✅ Allows you to easily modifyexisting classes
✅ Output compliant withPSR-12 / PER coding style
✅ Highly mature, stable, and widely used library

 

Installation

Download and install the library using theComposer tool:

composer require nette/php-generator

PhpGenerator 4.1 is compatible with PHP 8.0 to 8.4. Documentation can be found on thelibrary's website.

 

Do you like PHP Generator? Are you looking forward to the new features?

Buy me a coffee

Thank you!

 

Classes

Let's start with an example of creating a class usingClassType:

$class =newNette\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;

This will return:

/** * Class description * Second line * * @property-read Nette\Forms\Form $form */finalclass Demoextends ParentClassimplements Countable{}

To generate the code, you can also use a so-called printer, which, unlikeecho $class, can befurther configured:

$printer =newNette\PhpGenerator\Printer;echo$printer->printClass($class);

You can add constants (classConstant) and properties (classProperty):

$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'

This will generate:

finalprotectedconstintID =123;/** @var int[] */privatestatic$items = [1,2,3];public ?array$list =null;

And you can addmethods:

$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 = []

The result is:

/** * Count it. */finalprotectedfunctioncount(array &$items = []): ?int{returncount($items ?:$this->items);}

Promoted parameters introduced in PHP 8.0 can be passed to the constructor:

$method =$class->addMethod('__construct');$method->addPromotedParameter('name');$method->addPromotedParameter('args', [])->setPrivate();

The result is:

publicfunction__construct(public$name,private$args = [],) {}

Readonly properties and classes be marked using thesetReadOnly() function.


If an added property, constant, method, or parameter already exists, an exception is thrown.

Class members can be removed usingremoveProperty(),removeConstant(),removeMethod(), orremoveParameter().

You can also add existingMethod,Property, orConstant objects to the class:

$method =newNette\PhpGenerator\Method('getHandle');$property =newNette\PhpGenerator\Property('handle');$const =newNette\PhpGenerator\Constant('ROLE');$class = (newNette\PhpGenerator\ClassType('Demo'))->addMember($method)->addMember($property)->addMember($const);

You can also clone existing methods, properties, and constants under a different name usingcloneWithName():

$methodCount =$class->getMethod('count');$methodRecount =$methodCount->cloneWithName('recount');$class->addMember($methodRecount);

 

Interfaces or Traits

You can create interfaces and traits (classesInterfaceType andTraitType):

$interface =newNette\PhpGenerator\InterfaceType('MyInterface');$trait =newNette\PhpGenerator\TraitType('MyTrait');

Using a trait:

$class =newNette\PhpGenerator\ClassType('Demo');$class->addTrait('SmartObject');$class->addTrait('MyTrait')->addResolution('sayHello as protected')->addComment('@use MyTrait<Foo>');echo$class;

The result is:

class Demo{use SmartObject;/** @use MyTrait<Foo> */use MyTrait {sayHelloasprotected;}}

 

Enums

You can easily create enums introduced in PHP 8.1 like this (classEnumType):

$enum =newNette\PhpGenerator\EnumType('Suit');$enum->addCase('Clubs');$enum->addCase('Diamonds');$enum->addCase('Hearts');$enum->addCase('Spades');echo$enum;

The result is:

enum Suit{case Clubs;case Diamonds;case Hearts;case Spades;}

You can also define scalar equivalents and create a "backed" enum:

$enum->addCase('Clubs','');$enum->addCase('Diamonds','');

For eachcase, you can add a comment orattributes usingaddComment() oraddAttribute().

 

Anonymous Classes

Passnull as the name, and you have an anonymous class:

$class =newNette\PhpGenerator\ClassType(null);$class->addMethod('__construct')->addParameter('foo');echo'$obj = new class ($val)' .$class .';';

The result is:

$obj =newclass ($val) {publicfunction__construct($foo){}};

 

Global Functions

The code for functions is generated by the classGlobalFunction:

$function =newNette\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);

The result is:

functionfoo($a,$b){return$a +$b;}

 

Anonymous Functions

The code for anonymous functions is generated by the classClosure:

$closure =newNette\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);

The result is:

function ($a,$b)use (&$c) {return$a +$b;}

 

Short Arrow Functions

You can also output a short anonymous function using the printer:

$closure =newNette\PhpGenerator\Closure;$closure->setBody('$a + $b');$closure->addParameter('a');$closure->addParameter('b');echo (newNette\PhpGenerator\Printer)->printArrowFunction($closure);

The result is:

fn($a,$b) =>$a +$b

 

Method and Function Signatures

Methods are represented by the classMethod. You can set visibility, return value, add comments,attributes, etc.:

$method =$class->addMethod('count')->addComment('Count it.')->setFinal()->setProtected()->setReturnType('?int');

Individual parameters are represented by the classParameter. Again, you can set all conceivable properties:

$method->addParameter('items', [])// $items = []->setReference()// &$items = []->setType('array');// array &$items = []// function count(&$items = [])

To define the so-called variadics parameters (or also the splat, spread, ellipsis, unpacking or three dots operator), usesetVariadic():

$method =$class->addMethod('count');$method->setVariadic(true);$method->addParameter('items');

This generates:

functioncount(...$items){}

 

Method and Function Bodies

The body can be passed all at once to thesetBody() method or gradually (line by line) by repeatedly callingaddBody():

$function =newNette\PhpGenerator\GlobalFunction('foo');$function->addBody('$a = rand(10, 20);');$function->addBody('return $a;');echo$function;

The result is:

functionfoo(){$a =rand(10,20);return$a;}

You can use special placeholders for easy variable insertion.

Simple placeholders?

$str ='any string';$num =3;$function =newNette\PhpGenerator\GlobalFunction('foo');$function->addBody('return substr(?, ?);', [$str,$num]);echo$function;

The result is:

functionfoo(){returnsubstr('any string',3);}

Placeholder for variadic...?

$items = [1,2,3];$function =newNette\PhpGenerator\GlobalFunction('foo');$function->setBody('myfunc(...?);', [$items]);echo$function;

The result is:

functionfoo(){myfunc(1,2,3);}

You can also use named parameters for PHP 8 with...?:

$items = ['foo' =>1,'bar' =>true];$function->setBody('myfunc(...?:);', [$items]);// myfunc(foo: 1, bar: true);

The placeholder is escaped with a backslash\?

$num =3;$function =newNette\PhpGenerator\GlobalFunction('foo');$function->addParameter('a');$function->addBody('return $a \? 10 : ?;', [$num]);echo$function;

The result is:

functionfoo($a){return$a ?10 :3;}

 

Printer and PSR Compliance

ThePrinter class is used for generating PHP code:

$class =newNette\PhpGenerator\ClassType('Demo');// ...$printer =newNette\PhpGenerator\Printer;echo$printer->printClass($class);// same as: echo $class

It can generate code for all other elements, offering methods likeprintFunction(),printNamespace(), etc.

There's also thePsrPrinter class, which outputs in accordance with PSR-2 / PSR-12 / PER coding style:

$printer =newNette\PhpGenerator\PsrPrinter;echo$printer->printClass($class);

Need custom behavior? Create your own version by inheriting thePrinter class. You can reconfigure these variables:

class MyPrinterextendsNette\PhpGenerator\Printer{// length of the line after which the line will breakpublicint$wrapLength =120;// indentation character, can be replaced with a sequence of spacespublicstring$indentation ="\t";// number of blank lines between propertiespublicint$linesBetweenProperties =0;// number of blank lines between methodspublicint$linesBetweenMethods =2;// number of blank lines between 'use statements' groups for classes, functions, and constantspublicint$linesBetweenUseTypes =0;// position of the opening curly brace for functions and methodspublicbool$bracesOnNextLine =true;// place one parameter on one line, even if it has an attribute or is supportedpublicbool$singleParameterOnOneLine =false;// omits namespaces that do not contain any class or functionpublicbool$omitEmptyNamespaces =true;// separator between the right parenthesis and return type of functions and methodspublicstring$returnTypeColon =':';}

How and why does the standardPrinter differ fromPsrPrinter? Why isn't there just one printer, thePsrPrinter, in the package?

The standardPrinter formats the code as we do throughout Nette. Since Nette was established much earlier than PSR, and also because PSR took years to deliver standards on time, sometimes even several years after introducing a new feature in PHP, it resulted in acoding standard that differs in a few minor aspects.The major difference is the use of tabs instead of spaces. We know that by using tabs in our projects, we allow for width customization, which isessential for people with visual impairments.An example of a minor difference is placing the curly brace on a separate line for functions and methods, always. The PSR recommendation seems illogical to us andleads to reduced code clarity.

 

Types

Every type or union/intersection type can be passed as a string; you can also use predefined constants for native types:

useNette\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

The same applies to thesetReturnType() method.

 

Literals

UsingLiteral, you can pass any PHP code, for example, for default property values or parameters, etc:

useNette\PhpGenerator\Literal;$class =newNette\PhpGenerator\ClassType('Demo');$class->addProperty('foo',newLiteral('Iterator::SELF_FIRST'));$class->addMethod('bar')->addParameter('id',newLiteral('1 + 2'));echo$class;

Result:

class Demo{public$foo = Iterator::SELF_FIRST;publicfunctionbar($id =1 +2){}}

You can also pass parameters toLiteral and have them formatted into valid PHP code usingplaceholders:

newLiteral('substr(?, ?)', [$a,$b]);// generates for example: substr('hello', 5);

A literal representing the creation of a new object can easily be generated using thenew method:

Literal::new(Demo::class, [$a,'foo' =>$b]);// generates for example: new Demo(10, foo: 20)

 

Attributes

With PHP 8, you can add attributes to all classes, methods, properties, constants, enum cases, functions, closures, and parameters. You can also useliterals as parameter values.

$class =newNette\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;

Result:

#[Table(name:'user', constraints: [newUniqueConstraint(name:'ean', columns: ['ean'])])]class Demo{#[Deprecated]public$list;#[Foo\Cached(mode:true)]publicfunctioncount(#[Bar]$items,) {}}

 

Property Hooks

You can also define property hooks (represented by the classPropertyHook) for get and set operations, a feature introduced in PHP 8.4:

$class =newNette\PhpGenerator\ClassType('Demo');$prop =$class->addProperty('firstName')    ->setType('string');$prop->addHook('set','strtolower($value)')    ->addParameter('value')    ->setType('string');$prop->addHook('get')->setBody('return ucfirst($this->firstName);');echo$class;

This generates:

class Demo{publicstring$firstName {        set(string$value) =>strtolower($value);        get {returnucfirst($this->firstName);        }    }}

Properties and property hooks can be abstract or final:

$class->addProperty('id')    ->setType('int')    ->addHook('get')        ->setAbstract();$class->addProperty('role')    ->setType('string')    ->addHook('set','strtolower($value)')        ->setFinal();

 

Asymmetric Visibility

PHP 8.4 introduces asymmetric visibility for properties. You can set different access levels for reading and writing.The visibility can be set using either thesetVisibility() method with two parameters, or by usingsetPublic(),setProtected(), orsetPrivate() with themode parameter that specifies whether the visibility applies to getting or setting the property. The default mode is 'get'.

$class =newNette\PhpGenerator\ClassType('Demo');$class->addProperty('name')    ->setType('string')    ->setVisibility('public','private');// public for read, private for write$class->addProperty('id')    ->setType('int')    ->setProtected('set');// protected for writeecho$class;

This generates:

class Demo{publicprivate(set)string$name;protected(set)int$id;}

 

Namespace

Classes, traits, interfaces, and enums (hereafter referred to as classes) can be grouped into namespaces represented by thePhpNamespace class:

$namespace =newNette\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 =newNette\PhpGenerator\ClassType('Task');$namespace->add($class);

If the class already exists, an exception is thrown.

You can define use clauses:

// 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');

To simplify a fully qualified class, function, or constant name based on defined aliases, use thesimplifyName method:

echo$namespace->simplifyName('Foo\Bar');// 'Bar', because 'Foo' is the current namespaceecho$namespace->simplifyName('iter\range',$namespace::NameFunction);// 'range', due to the defined use-statement

Conversely, you can convert a simplified class, function, or constant name back to a fully qualified name using theresolveName method:

echo$namespace->resolveName('Bar');// 'Foo\Bar'echo$namespace->resolveName('range',$namespace::NameFunction);// 'iter\range'

 

Class Names Resolving

When a class is part of a namespace, it's rendered slightly differently: all types (e.g., type hints, return types, parent class name, implemented interfaces, used traits, and attributes) are automaticallyresolved (unless you turn it off, see below).This means you must usefully qualified class names in definitions, and they will be replaced with aliases (based on use clauses) or fully qualified names in the resulting code:

$namespace =newNette\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\OtherClassecho$namespace;// or use the PsrPrinter for output in accordance with PSR-2 / PSR-12 / PER// echo (new Nette\PhpGenerator\PsrPrinter)->printNamespace($namespace);

Result:

namespaceFoo;useBar\AliasedClass;class Demoimplements A{use AliasedClass;/** * @return D */publicfunctionmethod(\Bar\OtherClass$arg){}}

Auto-resolving can be turned off this way:

$printer =newNette\PhpGenerator\Printer;// or PsrPrinter$printer->setTypeResolving(false);echo$printer->printNamespace($namespace);

 

PHP Files

Classes, functions, and namespaces can be grouped into PHP files represented by thePhpFile class:

$file =newNette\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);

Result:

<?php/** * This file is auto-generated. */declare(strict_types=1);namespaceFoo;class A{}functionfoo(){}

Please note: No additional code can be added to the files outside of functions and classes.

 

Generating from Existing Ones

In addition to being able to model classes and functions using the API described above, you can also have them automatically generated using existing ones:

// 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) {},);

By default, function and method bodies are empty. If you also want to load them, use this method(requires thenikic/php-parser package to be installed):

$class =Nette\PhpGenerator\ClassType::from(Foo::class, withBodies:true);$function =Nette\PhpGenerator\GlobalFunction::from('foo', withBody:true);

 

Loading from PHP Files

You can also load functions, classes, interfaces, and enums directly from a string containing PHP code. For example, to create aClassType object:

$class =Nette\PhpGenerator\ClassType::fromCode(<<<XX<?phpclass Demo{public$foo;}XX);

When loading classes from PHP code, single-line comments outside method bodies are ignored (e.g., for properties, etc.), as this library doesn't have an API to work with them.

You can also directly load an entire PHP file, which can contain any number of classes, functions, or even namespaces:

$file =Nette\PhpGenerator\PhpFile::fromCode(file_get_contents('classes.php'));

The file's initial comment andstrict_types declaration are also loaded. However, all other global code is ignored.

It requiresnikic/php-parser to be installed.

(If you need to manipulate global code in files or individual statements in method bodies, it's better to use thenikic/php-parser library directly.)

 

Class Manipulator

TheClassManipulator class provides tools for manipulating classes.

$class =newNette\PhpGenerator\ClassType('Demo');$manipulator =newNette\PhpGenerator\ClassManipulator($class);

TheinheritMethod() method copies a method from a parent class or implemented interface into your class. This allows you to override the method or extend its signature:

$method =$manipulator->inheritMethod('bar');$method->setBody('...');

TheinheritProperty() method copies a property from a parent class into your class. This is useful when you want to have the same property in your class, but possibly with a different default value:

$property =$manipulator->inheritProperty('foo');$property->setValue('new value');

Theimplement() method automatically implements all methods and properties from the given interface or abstract class:

$manipulator->implement(SomeInterface::class);// Now your class implements SomeInterface and includes all its methods

 

Variable Dumping

TheDumper class converts a variable into parseable PHP code. It provides a better and clearer output than the standardvar_export() function.

$dumper =newNette\PhpGenerator\Dumper;$var = ['a','b',123];echo$dumper->dump($var);// outputs ['a', 'b', 123]

[8]ページ先頭

©2009-2025 Movatter.jp