Socket/vendor/nette/php-generator/readme.md
2026-01-28 23:48:20 +08:00

579 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Nette PHP Generator
===================
[![Downloads this Month](https://img.shields.io/packagist/dm/nette/php-generator.svg)](https://packagist.org/packages/nette/php-generator)
[![Build Status](https://travis-ci.org/nette/php-generator.svg?branch=master)](https://travis-ci.org/nette/php-generator)
[![Coverage Status](https://coveralls.io/repos/github/nette/php-generator/badge.svg?branch=master&v=1)](https://coveralls.io/github/nette/php-generator?branch=master)
[![Latest Stable Version](https://poser.pugx.org/nette/php-generator/v/stable)](https://github.com/nette/php-generator/releases)
[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/php-generator/blob/master/license.md)
Introduction
------------
Generate PHP code, classes, namespaces etc. with a simple programmatical API.
Documentation can be found on the [website](https://doc.nette.org/php-generator).
If you like Nette, **[please make a donation now](https://nette.org/donate)**. Thank you!
Installation
------------
The recommended way to install is via Composer:
```
composer require nette/php-generator
```
- PhpGenerator 3.2 3.4 is compatible with PHP 7.1 to 7.4
- PhpGenerator 3.1 is compatible with PHP 7.1 to 7.3
- PhpGenerator 3.0 is compatible with PHP 7.0 to 7.3
- PhpGenerator 2.6 is compatible with PHP 5.6 to 7.3
Usage
-----
Usage is very easy. Let's start with a straightforward example of generating class:
```php
$class = new Nette\PhpGenerator\ClassType('Demo');
$class
->setFinal()
->setExtends(ParentClass::class)
->addImplement(Countable::class)
->addTrait(Nette\SmartObject::class)
->addComment("Description of class.\nSecond line\n")
->addComment('@property-read Nette\Forms\Form $form');
// to generate PHP code simply cast to string or use echo:
echo $class;
// or use printer:
$printer = new Nette\PhpGenerator\Printer;
echo $printer->printClass($class);
```
It will render this result:
```php
/**
* Description of class.
* Second line
*
* @property-read Nette\Forms\Form $form
*/
final class Demo extends ParentClass implements Countable
{
use Nette\SmartObject;
}
```
We can add constants and properties:
```php
$class->addConstant('ID', 123);
$class->addProperty('items', [1, 2, 3])
->setPrivate()
->setStatic()
->addComment('@var int[]');
```
It generates:
```php
const ID = 123;
/** @var int[] */
private static $items = [1, 2, 3];
```
And we can add methods with parameters:
```php
$method = $class->addMethod('count')
->addComment('Count it.')
->addComment('@return int')
->setFinal()
->setProtected()
->setBody('return count($items ?: $this->items);');
$method->addParameter('items', []) // $items = []
->setReference() // &$items = []
->setType('array'); // array &$items = []
```
It results in:
```php
/**
* Count it.
* @return int
*/
final protected function count(array &$items = [])
{
return count($items ?: $this->items);
}
```
If the property, constant, method or parameter already exist, it will be overwritten.
Members can be removed using `removeProperty()`, `removeConstant()`, `removeMethod()` or `removeParameter()`.
PHP Generator supports all new PHP 7.3 and 7.4 features:
```php
use Nette\PhpGenerator\Type;
$class = new Nette\PhpGenerator\ClassType('Demo');
$class->addConstant('ID', 123)
->setPrivate(); // constant visiblity
$class->addProperty('items')
->setType(Type::ARRAY) // typed properites
->setNullable()
->setInitialized();
$method = $class->addMethod('getValue')
->setReturnType(Type::INT) // method return type
->setReturnNullable() // nullable return type
->setBody('return count($this->items);');
$method->addParameter('id')
->setType(Type::ARRAY) // scalar type hint
->setNullable(); // nullable type hint
echo $class;
```
Result:
```php
class Demo
{
private const ID = 123;
public ?array $items = null;
public function getValue(?int $id): ?int
{
return count($this->items);
}
}
```
You can also add existing `Method`, `Property` or `Constant` objects to the class:
```php
$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);
```
You can clone existing methods, properties and constants with a different name using `cloneWithName()`:
```php
$methodCount = $class->getMethod('count');
$methodRecount = $methodCount->cloneWithName('recount');
$class->addMember($methodRecount);
```
Tabs versus spaces
------------------
The generated code uses tabs for indentation. If you want to have the output compatible with PSR-2 or PSR-12, use `PsrPrinter`:
```php
$class = new Nette\PhpGenerator\ClassType('Demo');
// ...
$printer = new Nette\PhpGenerator\PsrPrinter;
echo $printer->printClass($class); // 4 spaces indentation
```
It can be used also for functions, closures, namespaces etc.
Literals
--------
You can pass any PHP code to property or parameter default values via `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;
```
Result:
```php
class Demo
{
public $foo = Iterator::SELF_FIRST;
public function bar($id = 1 + 2)
{
}
}
```
Interface or Trait
------------------
```php
$class = new Nette\PhpGenerator\ClassType('DemoInterface');
$class->setInterface();
// or $class->setTrait();
```
Trait Resolutions and Visibility
--------------------------------
```php
$class = new Nette\PhpGenerator\ClassType('Demo');
$class->addTrait('SmartObject', ['sayHello as protected']);
echo $class;
```
Result:
```php
class Demo
{
use SmartObject {
sayHello as protected;
}
}
```
Anonymous Class
---------------
```php
$class = new Nette\PhpGenerator\ClassType(null);
$class->addMethod('__construct')
->addParameter('foo');
echo '$obj = new class ($val) ' . $class . ';';
```
Result:
```php
$obj = new class ($val) {
public function __construct($foo)
{
}
};
```
Global Function
---------------
Code of function:
```php
$function = new Nette\PhpGenerator\GlobalFunction('foo');
$function->setBody('return $a + $b;');
$function->addParameter('a');
$function->addParameter('b');
echo $function;
// or use PsrPrinter for output compatible with PSR-2 / PSR-12
// echo (new Nette\PhpGenerator\PsrPrinter)->printFunction($function);
```
Result:
```php
function foo($a, $b)
{
return $a + $b;
}
```
Closure
-------
Code of closure:
```php
$closure = new Nette\PhpGenerator\Closure;
$closure->setBody('return $a + $b;');
$closure->addParameter('a');
$closure->addParameter('b');
$closure->addUse('c')
->setReference();
echo $closure;
// or use PsrPrinter for output compatible with PSR-2 / PSR-12
// echo (new Nette\PhpGenerator\PsrPrinter)->printClosure($closure);
```
Result:
```php
function ($a, $b) use (&$c) {
return $a + $b;
}
```
Arrow function
--------------
You can also print closure as arrow function using printer:
```php
$closure = new Nette\PhpGenerator\Closure;
$closure->setBody('return $a + $b;');
$closure->addParameter('a');
$closure->addParameter('b');
// or use PsrPrinter for output compatible with PSR-2 / PSR-12
echo (new Nette\PhpGenerator\Printer)->printArrowFunction($closure);
```
Result:
```php
fn ($a, $b) => $a + $b;
```
Method and Function Body Generator
----------------------------------
You can use special placeholders for handy way to generate method or function body.
Simple placeholders:
```php
$str = 'any string';
$num = 3;
$function = new Nette\PhpGenerator\GlobalFunction('foo');
$function->addBody('return strlen(?, ?);', [$str, $num]);
echo $function;
```
Result:
```php
function foo()
{
return strlen('any string', 3);
}
```
Variadic placeholder:
```php
$items = [1, 2, 3];
$function = new Nette\PhpGenerator\GlobalFunction('foo');
$function->setBody('myfunc(...?);', [$items]);
echo $function;
```
Result:
```php
function foo()
{
myfunc(1, 2, 3);
}
```
Escape placeholder using slash:
```php
$num = 3;
$function = new Nette\PhpGenerator\GlobalFunction('foo');
$function->addParameter('a');
$function->addBody('return $a \? 10 : ?;', [$num]);
echo $function;
```
Result:
```php
function foo($a)
{
return $a ? 10 : 3;
}
```
Namespace
---------
Classes, traits and interfaces (hereinafter classes) can be grouped into namespaces:
```php
$namespace = new Nette\PhpGenerator\PhpNamespace('Foo');
$class = $namespace->addClass('Task');
$interface = $namespace->addInterface('Countable');
$trait = $namespace->addTrait('NameAware');
// or
$class = new Nette\PhpGenerator\ClassType('Task');
$namespace->add($class);
```
If the class already exists, it will be overwritten.
You can define use-statements:
```php
$namespace->addUse(Http\Request::class); // use Http\Request;
$namespace->addUse(Http\Request::class, 'HttpReq'); // use Http\Request as HttpReq;
```
**IMPORTANT NOTE:** when the class is part of the namespace, it is rendered slightly differently: all types (ie. type hints, return types, parent class name,
implemented interfaces and used traits) are automatically *resolved* (unless you turn it off, see below).
It means that you have to **use full class names** in definitions and they will be replaced
with aliases (according to the use-statements) or fully qualified names in the resulting code:
```php
$namespace = new Nette\PhpGenerator\PhpNamespace('Foo');
$namespace->addUse('Bar\AliasedClass');
$class = $namespace->addClass('Demo');
$class->addImplement('Foo\A') // it will resolve to A
->addTrait('Bar\AliasedClass'); // it will resolve to AliasedClass
$method = $class->addMethod('method');
$method->addComment('@return ' . $namespace->unresolveName('Foo\D')); // in comments resolve manually
$method->addParameter('arg')
->setType('Bar\OtherClass'); // it will resolve to \Bar\OtherClass
echo $namespace;
// or use PsrPrinter for output compatible with PSR-2 / PSR-12
// echo (new Nette\PhpGenerator\PsrPrinter)->printNamespace($namespace);
```
Result:
```php
namespace Foo;
use Bar\AliasedClass;
class Demo implements A
{
use AliasedClass;
/**
* @return D
*/
public function method(\Bar\OtherClass $arg)
{
}
}
```
Auto-resolving can be turned off this way:
```php
$printer = new Nette\PhpGenerator\Printer; // or PsrPrinter
$printer->setTypeResolving(false);
echo $printer->printNamespace($namespace);
```
PHP Files
---------
PHP files can contains multiple classes, namespaces and comments:
```php
$file = new Nette\PhpGenerator\PhpFile;
$file->addComment('This file is auto-generated.');
$file->setStrictTypes(); // adds declare(strict_types=1)
$namespace = $file->addNamespace('Foo');
$class = $namespace->addClass('A');
$class->addMethod('hello');
echo $file;
// or use PsrPrinter for output compatible with PSR-2 / PSR-12
// echo (new Nette\PhpGenerator\PsrPrinter)->printFile($file);
```
Result:
```php
<?php
/**
* This file is auto-generated.
*/
declare(strict_types=1);
namespace Foo;
class A
{
public function hello()
{
}
}
```
Generate using Reflection
-------------------------
Another common use case is to create class or method based on existing ones:
```php
$class = Nette\PhpGenerator\ClassType::from(PDO::class);
$function = Nette\PhpGenerator\GlobalFunction::from('trim');
$closure = Nette\PhpGenerator\Closure::from(
function (stdClass $a, $b = null) {}
);
```
Method bodies are empty by default. If you want to load them as well, use this way
(it requires `nikic/php-parser` to be installed):
```php
$class = Nette\PhpGenerator\ClassType::withBodiesFrom(MyClass::class);
$function = Nette\PhpGenerator\GlobalFunction::withBodyFrom('dump');
```
Variables dumper
----------------
The Dumper returns a parsable PHP string representation of a variable. It provides a better function that you can use instead of `var_export()`
with more readable output.
```php
$dumper = new Nette\PhpGenerator\Dumper;
$var = ['a', 'b', 123];
echo $dumper->dump($var); // prints ['a', 'b', 123]
```