It is my fervent wish that this file guide every AI coding agent working with code in this repository.
Any distilled, agent-facing documentation for this package - how it works
internally and the rationale behind key design decisions - lives in docs/.
Consult it before non-trivial changes; it is the source of truth from which the
public manual is distilled.
The object model is a self-explanatory builder, but three areas are trap-rich and
expensive to reconstruct: type/nullability handling, the name/namespace model,
and the reflection<->source round-trip. Read docs/internals.md before touching them.
docs/upgrading.md records, per released version, every change visible to
users of the package (renames, removals, deprecations, signatures, behavior,
configuration), each with its commit hash; internal changes are not in it. It
is the place to look up the history of a public symbol. Do not write to it; it
is extended at release time from the git history.
Nette PHP Generator programmatically generates PHP code (classes, functions, namespaces, whole files) with support for modern features (property hooks, enums, attributes, asymmetric visibility), plus loading existing code back into the object model.
- PHP Version: 8.2 - 8.6 (v5.0)
- Package:
nette/php-generator
# Run all tests
vendor/bin/tester tests -s # or: composer tester
vendor/bin/tester tests/Printer -s # one topic folder or a single .phpt
# Static analysis
composer phpstan- Every file starts with
declare(strict_types=1);; tabs; braces on the next line for functions/methods; Nette Coding Standard. - Tests are Nette Tester
.phptgrouped by topic undertests/(ClassLike/,Members/,Factory/,Extractor/,Printer/,PhpFile/,Dumper/,Helpers/); many compare against a.expectfile in the topic'sexpected/subfolder via thesameFile()helper (keeps test files clean and makes output changes visible in diffs).
- The type is a
Nette\Utils\Typeobject and nullability lives inside it -setNullable()(which throws without a type), anulldefault and anullinitial value are folded into the type by the setters (applyNullable()), one-way.getType()returns the object, the declaration string is(string). Printing normalizes the spelling (int|nullprints as?int). InFactory, callsetType()only aftersetInitialized(), or a transient null folds in. - The type API is duplicated across five classes, not shared -
Constanthas no nullable API,EnumType::setType()stores a plain string and skips validation (the backing type is checked only byvalidate()at print time). Account for these deliberate differences before "unifying" them. getName()returns the SHORT name andgetFullName()the FQN - the opposite ofReflectionClass::getName().PhpNamespaceis the resolution engine (resolveName/simplifyName/simplifyType), with separate alias tables per symbol kind.- Names inside bodies, attribute args, and dumped values survive only as invisible
/*(n*/comment tags (Helpers::tagName, resolved bysimplifyTaggedNamesat print time); an untagged FQN prints verbatim and is never simplified. - Loading code has two paths:
Factory(reflection only, no I/O) andExtractor(nikic/php-parser, reached viawithBodiesor thefromCode()path, and throws if the parser is absent - fail-fast, never silent degradation). self/parent/staticare kept verbatim (they're keywords). SoClassManipulator::inheritMethod()/inheritProperty()/implement()copy a member into a different class without rewriting those references - a relocation trap. Extracted bodies are likewise relative to their original namespace.- User-facing how-to (builder API, body placeholders, Printer configuration, property hooks, asymmetric visibility, arrow functions) is manual material and lives in the public web docs, not here.