Nette PHP Generator

repository·master·Indexed 24 days ago

https://github.com/nette/php-generator

A mature library for generating PSR-compliant PHP code, including classes, functions, enums, interfaces, and traits. It supports modern PHP features such as property hooks, asymmetric visibility, attributes, and promoted parameters. The library provides tools for modeling PHP structures via ClassType, EnumType, and PhpNamespace, as well as utilities like ClassManipulator for modifying existing types and Dumper for converting variables into parseable PHP code.

Tokens
5.8K
Snippets
18
Records
46
Agent score
81%

What's inside nette/php-generator

  1. Factory vs Extractor for source loading

    master

    There are two ways to build objects from existing code, depending on whether you need access to method bodies or default values:

    1. Factory: Uses reflection only. It does not perform I/O or require nikic/php-parser. It is fast but cannot access method bodies or parameter defaults.
    2. Extractor: Parses source code using nikic/php-parser. It is required when you need to access bodies or defaults. You must explicitly opt-in using .withBodies(). If php-parser is not installed, the Extractor constructor will throw a NotSupportedException.

    Warning on Member Relocation: When using ClassManipulator::inheritMethod() or implement(), the library copies members without rewriting types or bodies. Because self, static, and parent are kept verbatim, they may silently refer to the wrong class in the new context.

  2. Use placeholders in method and function bodies

    master

    You can use placeholders in setBody() or addBody() to easily insert variables into the generated code:

    • ?: Simple placeholder for values.
    • ...?: Placeholder for variadic/unpacking (e.g., ...$items).
    • ...?:: Placeholder for named parameters (PHP 8+).
    • \?: Escaped placeholder for a literal question mark.
  3. How nullability is handled in types

    master

    Nullability in nette/php-generator is managed through three distinct, potentially conflicting representations. Developers should be aware that isNullable() may not always reflect the literal string content of a type.

    1. Type String: A literal string (e.g., 'int|null'). Note that Helpers::validateType strips a leading ? and moves it to a flag, but does not touch |null within a union.
    2. Nullable Flag: A boolean flag set via setNullable or automatically by Helpers::validateType when a ? is present.
    3. Implicit Nullability: For Parameter and Property, isNullable() returns true if the default or initial value is null, even without a ? or a flag. Return types do not support this inference.

    Critical Traps:

    • setType('int|null')->isNullable() returns false (because it relies on the flag).
    • setType('?int')->isNullable() returns true (because it sets the flag).
    • getType(asObject: true) builds a Nette\Utils\Type object from the string alone, meaning ? or flag-based nullability is lost, while |null inside a union string survives.
  4. Understand the Name and Namespace model

    master

    The library uses a specific model for class and function names that differs from standard PHP Reflection:

    • Short vs Full Names: For ClassLike objects, getName() returns the short name, while getFullName() returns the Fully Qualified Name (FQN). This is the opposite of ReflectionClass::getName().
    • Name Resolution: PhpNamespace::resolveName expands short or aliased names to an FQN. PhpNamespace::simplifyName reduces an FQN to the shortest form allowed by current use statements.
    • Type Simplification: PhpNamespace::simplifyType applies simplifyName to every name within a type string via regex.
    • Keywords: self, parent, and static are treated as keywords via Helpers::Keywords and are left untouched by resolution or simplification engines.
  5. Generate PHP Files

    master

    Use Nette\PhpGenerator\PhpFile to group namespaces, classes, and functions into a single file. You can set strict types using setStrictTypes() and add file-level comments.

    $file = new Nette\PhpGenerator\PhpFile;
    $file->addComment('This file is auto-generated.');
    $file->setStrictTypes();
    
    $class = $file->addClass('Foo\A');
    $function = $file->addFunction('Foo\foo');
    
    echo $file;
  6. Create anonymous classes

    master

    To create an anonymous class, pass null as the name to the ClassType constructor.

    $class = new Nette\PhpGenerator\ClassType(null);
    $class->addMethod('__construct')
    	->addParameter('foo');
    
    echo '$obj = new class ($val) ' . $class . ';';
  7. Generate short arrow functions

    master

    To output a short arrow function (fn() => ...), use the printArrowFunction() method of a Printer instance with a Closure object.

    $closure = new Nette\PhpGenerator\Closure;
    $closure->setBody('$a + $b');
    $closure->addParameter('a');
    $closure->addParameter('b');
    
    echo (new Nette\PhpGenerator\Printer)->printArrowFunction($closure);
  8. Define types and union/intersection types

    master

    Types can be passed as strings or using Nette\PhpGenerator\Type constants. This applies to setType() and setReturnType().

    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
  9. Add methods and parameters to a class

    master

    Use addMethod() to create a Method object. You can set return types, visibility, and bodies. Use addParameter() on the method to define parameters, including type hints, references, and default values. For PHP 8.0+ promoted parameters, use addPromotedParameter() on the method.

    $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 = []
    
    // Promoted parameters
    $method = $class->addMethod('__construct');
    $method->addPromotedParameter('name');
    $method->addPromotedParameter('args', [])
    	->setPrivate();
  10. Generate code from existing classes or code strings

    master
    You can model classes, functions, or closures based on existing ones using the from() method. If nikic/php-parser is installed, you can also load bodies. Alternatively, use fromCode() to load from a string of PHP code.