Skip to content

Commit cb4aa12

Browse files
committed
added CLAUDE.md
1 parent 50b0979 commit cb4aa12

File tree

2 files changed

+330
-0
lines changed

2 files changed

+330
-0
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.gitattributes export-ignore
22
.github/ export-ignore
33
.gitignore export-ignore
4+
CLAUDE.md export-ignore
45
ncs.* export-ignore
56
phpstan*.neon export-ignore
67
tests/ export-ignore

CLAUDE.md

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
Nette PHP Generator is a library for programmatically generating PHP code - classes, functions, namespaces, and complete PHP files. It supports all modern PHP features including property hooks (PHP 8.4), enums, attributes, asymmetric visibility, and more.
8+
9+
The library provides both a builder API for constructing PHP structures and extraction capabilities for loading existing PHP code.
10+
11+
## Essential Commands
12+
13+
### Testing
14+
```bash
15+
# Run all tests
16+
composer run tester
17+
# OR
18+
vendor/bin/tester tests -s -C
19+
20+
# Run specific test file
21+
vendor/bin/tester tests/PhpGenerator/ClassType.phpt -s -C
22+
23+
# Run tests in specific directory
24+
vendor/bin/tester tests/PhpGenerator/ -s -C
25+
```
26+
27+
### Static Analysis
28+
```bash
29+
# Run PHPStan analysis
30+
composer run phpstan
31+
# OR
32+
vendor/bin/phpstan analyse
33+
```
34+
35+
### Code Style
36+
The project follows Nette Coding Standard with configuration in `ncs.php`. Uses tabs for indentation and braces on next line for functions/methods.
37+
38+
## Architecture
39+
40+
### Core Components
41+
42+
**PhpFile** (`PhpFile.php`)
43+
- Top-level container representing a complete PHP file
44+
- Manages namespaces, strict_types declaration, and file-level comments
45+
- Entry point: `PhpFile::add()` for adding namespaces/classes/functions
46+
47+
**PhpNamespace** (`PhpNamespace.php`)
48+
- Represents a namespace with use statements and contained classes/functions
49+
- Handles type resolution and name simplification based on use statements
50+
- Methods: `addClass()`, `addInterface()`, `addTrait()`, `addEnum()`, `addFunction()`
51+
52+
**ClassLike** (`ClassLike.php`)
53+
- Abstract base for class-like structures (classes, interfaces, traits, enums)
54+
- Provides common functionality for attributes and comments
55+
- Extended by ClassType, InterfaceType, TraitType, EnumType
56+
57+
**ClassType** (`ClassType.php`)
58+
- Represents class definitions with full feature support
59+
- Composes traits for properties, methods, constants, and trait usage
60+
- Supports final, abstract, readonly modifiers, extends, and implements
61+
62+
**Method** (`Method.php`) & **GlobalFunction** (`GlobalFunction.php`)
63+
- Method represents class/interface/trait methods
64+
- GlobalFunction represents standalone functions
65+
- Both use FunctionLike trait for parameters, return types, and body
66+
67+
**Property** (`Property.php`)
68+
- Represents class properties with type hints, visibility, hooks
69+
- Supports PHP 8.4 property hooks (get/set) via PropertyHook class
70+
- Supports asymmetric visibility (different get/set visibility)
71+
72+
**Printer** (`Printer.php`) & **PsrPrinter** (`PsrPrinter.php`)
73+
- Printer: Generates code following Nette Coding Standard (tabs, braces on next line)
74+
- PsrPrinter: Generates PSR-2/PSR-12/PER compliant code
75+
- Configurable via properties: `$wrapLength`, `$indentation`, `$linesBetweenMethods`, etc.
76+
- Handles type resolution when printing within namespace context
77+
78+
**Factory** (`Factory.php`) & **Extractor** (`Extractor.php`)
79+
- Factory: Creates PhpGenerator objects from existing classes/functions
80+
- Extractor: Low-level parser integration (requires nikic/php-parser)
81+
- Enable loading existing code: `ClassType::from()`, `PhpFile::fromCode()`
82+
83+
**Dumper** (`Dumper.php`)
84+
- Converts PHP values to code representation
85+
- Used internally for default values, but also available for standalone use
86+
- Better output than `var_export()`
87+
88+
### Trait Organization (`Traits/`)
89+
90+
Shared functionality is implemented via traits for composition:
91+
92+
- **FunctionLike**: Parameters, return types, body management for functions/methods
93+
- **PropertyLike**: Core property functionality (type, value, visibility)
94+
- **AttributeAware**: Attribute support for all elements
95+
- **CommentAware**: Doc comment management
96+
- **VisibilityAware**: Visibility modifiers (public/protected/private)
97+
- **ConstantsAware**: Class constant management
98+
- **MethodsAware**: Method collection management
99+
- **PropertiesAware**: Property collection management
100+
- **TraitsAware**: Trait usage management
101+
102+
This trait-based architecture allows ClassType to compose all necessary features while keeping concerns separated.
103+
104+
### Type System
105+
106+
**Type** (`Type.php`)
107+
- Constants for native types (String, Int, Array, etc.)
108+
- Helper methods for union/intersection/nullable types
109+
- Used throughout for type hints and return types
110+
111+
**Literal** (`Literal.php`)
112+
- Represents raw PHP code that should not be escaped
113+
- Used for default values, constants, expressions
114+
- Supports placeholders for value injection (see Placeholders section below)
115+
- `Literal::new()` helper for creating object instantiation literals
116+
117+
### Test Structure
118+
119+
Tests use Nette Tester with `.phpt` extension:
120+
- Located in `tests/PhpGenerator/`
121+
- Mirror source file organization
122+
- Use `test()` function for test cases (defined in `bootstrap.php`)
123+
- Use `testException()` for exception testing
124+
- Helper functions: `same()`, `sameFile()` for assertions
125+
126+
Example test pattern:
127+
```php
128+
test('description of what is being tested', function () {
129+
$class = new ClassType('Demo');
130+
// ... test code
131+
Assert::same($expected, (string) $class);
132+
});
133+
```
134+
135+
## Important Features
136+
137+
### Placeholders in Function Bodies
138+
139+
The library supports special placeholders for inserting values into method/function bodies:
140+
141+
- **`?`** - Simple placeholder for single values (strings, numbers, arrays)
142+
```php
143+
$function->addBody('return substr(?, ?);', [$str, $num]);
144+
// Generates: return substr('any string', 3);
145+
```
146+
147+
- **`...?`** - Variadic placeholder (unpacks arrays as separate arguments)
148+
```php
149+
$function->setBody('myfunc(...?);', [[1, 2, 3]]);
150+
// Generates: myfunc(1, 2, 3);
151+
```
152+
153+
- **`...?:`** - Named parameters placeholder for PHP 8+
154+
```php
155+
$function->setBody('myfunc(...?:);', [['foo' => 1, 'bar' => true]]);
156+
// Generates: myfunc(foo: 1, bar: true);
157+
```
158+
159+
- **`\?`** - Escaped placeholder (literal question mark)
160+
```php
161+
$function->addBody('return $a \? 10 : ?;', [$num]);
162+
// Generates: return $a ? 10 : 3;
163+
```
164+
165+
### Printer Configuration
166+
167+
Both `Printer` and `PsrPrinter` can be customized by extending and overriding public properties:
168+
169+
```php
170+
class MyPrinter extends Nette\PhpGenerator\Printer
171+
{
172+
public int $wrapLength = 120; // line length for wrapping
173+
public string $indentation = "\t"; // indentation character
174+
public int $linesBetweenProperties = 0; // blank lines between properties
175+
public int $linesBetweenMethods = 2; // blank lines between methods
176+
public int $linesBetweenUseTypes = 0; // blank lines between use statement groups
177+
public bool $bracesOnNextLine = true; // opening brace position for functions/methods
178+
public bool $singleParameterOnOneLine = false; // single parameter formatting
179+
public bool $omitEmptyNamespaces = true; // omit empty namespaces
180+
public string $returnTypeColon = ': '; // separator before return type
181+
}
182+
```
183+
184+
**Note:** `Printer` uses Nette Coding Standard (tabs, braces on next line), while `PsrPrinter` follows PSR-2/PSR-12/PER (spaces, braces on same line).
185+
186+
### Property Hooks (PHP 8.4)
187+
188+
Property hooks allow defining get/set operations directly on properties:
189+
190+
```php
191+
$prop = $class->addProperty('firstName')->setType('string');
192+
$prop->addHook('set', 'strtolower($value)') // Arrow function style
193+
->addParameter('value')->setType('string');
194+
$prop->addHook('get') // Block style
195+
->setBody('return ucfirst($this->firstName);');
196+
```
197+
198+
Property hooks can be marked as `abstract` or `final` using `setAbstract()` / `setFinal()`.
199+
200+
### Asymmetric Visibility (PHP 8.4)
201+
202+
Properties can have different visibility for reading vs writing:
203+
204+
```php
205+
// Using setVisibility() with two parameters
206+
$class->addProperty('name')
207+
->setVisibility('public', 'private'); // public get, private set
208+
209+
// Using modifier methods with 'get' or 'set' mode
210+
$class->addProperty('id')
211+
->setProtected('set'); // protected set, public get (default)
212+
```
213+
214+
Generates: `public private(set) string $name;`
215+
216+
### ClassManipulator
217+
218+
The `ClassManipulator` class provides advanced class manipulation:
219+
220+
- **`inheritMethod($name)`** - Copies method from parent/interface for overriding
221+
- **`inheritProperty($name)`** - Copies property from parent class
222+
- **`implement($interface)`** - Automatically implements all methods/properties from interface/abstract class
223+
224+
```php
225+
$manipulator = new Nette\PhpGenerator\ClassManipulator($class);
226+
$manipulator->implement(SomeInterface::class);
227+
// Now $class contains stub implementations of all interface methods
228+
```
229+
230+
### Arrow Functions
231+
232+
While `Closure` represents anonymous functions, you can generate arrow functions using the printer:
233+
234+
```php
235+
$closure = new Nette\PhpGenerator\Closure;
236+
$closure->setBody('$a + $b'); // Note: arrow function body without 'return'
237+
$closure->addParameter('a');
238+
$closure->addParameter('b');
239+
240+
echo (new Nette\PhpGenerator\Printer)->printArrowFunction($closure);
241+
// Generates: fn($a, $b) => $a + $b
242+
```
243+
244+
### Cloning Members
245+
246+
Methods, properties, and constants can be cloned under a different name:
247+
248+
```php
249+
$methodCount = $class->getMethod('count');
250+
$methodRecount = $methodCount->cloneWithName('recount');
251+
$class->addMember($methodRecount);
252+
```
253+
254+
## Key Patterns
255+
256+
### Builder Pattern
257+
All classes use fluent interface - methods return `$this`/`static` for chaining:
258+
```php
259+
$class->setFinal()
260+
->setExtends(ParentClass::class)
261+
->addImplement(Countable::class);
262+
```
263+
264+
### Type Resolution
265+
When a class is part of a namespace, types are automatically resolved:
266+
- Fully qualified names → simplified based on use statements
267+
- Can be disabled: `$printer->setTypeResolving(false)`
268+
- Use `$namespace->simplifyType()` for manual resolution
269+
270+
### Code Generation Flow
271+
1. Create PhpFile
272+
2. Add namespace(s)
273+
3. Add classes/interfaces/traits/enums/functions to namespace
274+
4. Add members (properties/methods/constants) to classes
275+
5. Convert to string or use Printer explicitly
276+
277+
### Validation
278+
Classes validate themselves before printing via `validate()` method. Throws `Nette\InvalidStateException` for invalid states (e.g., abstract and final simultaneously).
279+
280+
### Cloning
281+
All major classes implement `__clone()` to deep-clone contained objects (methods, properties, etc.).
282+
283+
## Common Tasks
284+
285+
### Adding New Features
286+
1. Check if feature needs new class or extends existing (e.g., PropertyHook for property hooks)
287+
2. Add tests first in `tests/PhpGenerator/`
288+
3. Update Printer to generate correct syntax
289+
4. Update Factory/Extractor if feature should be loadable from existing code
290+
5. Consider PsrPrinter compatibility
291+
292+
### Updating for New PHP Versions
293+
1. Add support to builder classes (e.g., new method/property)
294+
2. Update Printer with syntax generation
295+
3. Update Extractor to parse the feature (via php-parser)
296+
4. Add comprehensive tests including edge cases
297+
5. Update README.md with examples
298+
299+
### Test Expectations
300+
- Test files may have corresponding `.expect` files with expected output
301+
- Use `sameFile()` helper to compare against expectation files
302+
- This keeps test files clean and makes output changes visible in diffs
303+
304+
## Important Notes & Limitations
305+
306+
### Loading from Existing Code
307+
- **Requires `nikic/php-parser`** to load method/function bodies with `withBodies: true` / `withBody: true`
308+
- Without php-parser, bodies are empty but signatures are complete
309+
- Single-line comments outside method bodies are ignored when loading (library has no API for them)
310+
- Use `nikic/php-parser` directly if you need to manipulate global code or individual statements
311+
312+
### PhpFile Restrictions
313+
- **No global code allowed** - PhpFile can only contain namespaces, classes, functions
314+
- Cannot add arbitrary code like `echo 'hello'` outside functions/classes
315+
- Use `setStrictTypes()` for `declare(strict_types=1)` declaration
316+
317+
### Exception Handling
318+
- Adding duplicate members (same name) throws `Nette\InvalidStateException`
319+
- Use `addMember($member, overwrite: true)` to replace existing members
320+
- Invalid class states (e.g., abstract + final) detected by `validate()` method
321+
322+
### Removal Methods
323+
- `removeProperty($name)`, `removeConstant($name)`, `removeMethod($name)`, `removeParameter($name)`
324+
- Available on respective container classes
325+
326+
### Compatibility
327+
- **PhpGenerator 4.1+** supports PHP 8.0 to 8.4
328+
- **PhpGenerator 4.2** (current) compatible with PHP 8.1 to 8.5
329+
- Check composer.json for exact version requirements

0 commit comments

Comments
 (0)