Skip to content

Commit 21e2699

Browse files
committed
Merge 4.2
2 parents fe63dde + b84a389 commit 21e2699

54 files changed

Lines changed: 1363 additions & 136 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 389 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,389 @@
1+
# Agent Instructions for API Platform Core Development
2+
3+
This document provides guidelines for AI agents working on the API Platform core codebase.
4+
5+
## Primary Responsibilities
6+
7+
Unless explicitly asked otherwise:
8+
9+
* **Focus on Test Writing:** Your main responsibility is to write functional tests for new features or bug fixes.
10+
* **No Bug Fixing:** Do not attempt to fix bugs; only write tests to expose them or verify fixes.
11+
* **Project Context:** API Platform is a PHP framework supporting both Symfony and Laravel. The user will specify which framework (defaults to Symfony).
12+
* **Fixture Handling:** Avoid altering existing fixtures to prevent unintended side effects on other tests. Create new entities/DTOs/models with unique names. Business logic is secondary; focus on framework testing.
13+
* **No Test Execution by Default:** Do not run tests unless explicitly asked (to save context, as tests produce verbose output).
14+
15+
## Running Tests (When Asked)
16+
17+
**⚠️ CRITICAL: Almost NEVER run the full test suite!**
18+
19+
- Full Behat suite takes ~10 minutes (30+ minutes without `--format=progress`)
20+
- Full PHPUnit suite is also very slow
21+
- **Best Practice:** Identify failing tests from GitHub CI, then run those specific tests locally
22+
23+
**Debugging Workflow:**
24+
1. Check GitHub CI to identify which specific test failed
25+
2. Run that exact test locally with verbose output for details
26+
3. Fix and verify only that test before pushing
27+
28+
### Symfony Tests
29+
30+
**Prerequisites:**
31+
- Clear cache when changing branches, dependencies, or `USE_SYMFONY_LISTENERS`: `rm -rf tests/Fixtures/app/var/cache/test`
32+
- Optionally warm cache: `tests/Fixtures/app/console cache:warmup`
33+
- For MongoDB tests: Set `APP_ENV=mongodb` and install MongoDB ODM: `composer require --dev doctrine/mongodb-odm-bundle`
34+
- MongoDB extension required: Ensure `mongodb` PHP extension is installed
35+
36+
**PHPUnit (Main Suite):**
37+
```bash
38+
# ALWAYS use filtering - never run all tests unnecessarily!
39+
vendor/bin/phpunit --filter testMethodName
40+
41+
# Filter by test class
42+
vendor/bin/phpunit --filter MyTestClass
43+
44+
# Filter by path (recommended)
45+
vendor/bin/phpunit tests/Functional/SomeTest.php
46+
47+
# Only if specifically needed (slow!)
48+
vendor/bin/phpunit
49+
```
50+
51+
**PHPUnit (Component-Specific):**
52+
```bash
53+
# Run tests for a specific component
54+
composer {component-name} test
55+
56+
# Examples:
57+
composer doctrine-orm test
58+
composer graphql test
59+
composer metadata test
60+
```
61+
62+
**Behat (Functional Tests):**
63+
```bash
64+
# ⚠️ IMPORTANT: Always use --format=progress unless debugging!
65+
# Without it, tests take 3x longer (30min vs 10min)
66+
67+
# Run specific scenario by line number (RECOMMENDED for debugging)
68+
vendor/bin/behat features/main/crud.feature:120 -vvv
69+
70+
# Run specific feature file with progress (for CI-like run)
71+
vendor/bin/behat features/main/crud.feature --format=progress
72+
73+
# Debug specific test (verbose, NO progress format)
74+
vendor/bin/behat features/main/crud.feature:120 -vvv
75+
76+
# Filter by tags
77+
vendor/bin/behat --tags=@pagination --format=progress
78+
79+
# With specific profile
80+
vendor/bin/behat --profile=default --format=progress
81+
vendor/bin/behat --profile=postgres --format=progress
82+
vendor/bin/behat --profile=default --tags '~@!mysql' --format=progress
83+
84+
# Only run full suite if absolutely necessary (10+ minutes)
85+
vendor/bin/behat --format=progress
86+
```
87+
88+
**MongoDB Tests:**
89+
```bash
90+
# Set MongoDB environment
91+
export APP_ENV=mongodb
92+
export MONGODB_URL=mongodb://localhost:27017
93+
94+
# Install MongoDB ODM
95+
composer require --dev doctrine/mongodb-odm-bundle
96+
97+
# Clear cache (always required when changing APP_ENV)
98+
rm -rf tests/Fixtures/app/var/cache/test
99+
100+
# Run PHPUnit tests (exclude ORM tests)
101+
vendor/bin/phpunit --exclude-group=orm
102+
103+
# Run Behat tests with MongoDB profile
104+
vendor/bin/behat --profile=mongodb-coverage --format=progress
105+
```
106+
107+
**Static Analysis:**
108+
```bash
109+
# Always run PHPStan to prevent trivial bugs
110+
# CRITICAL: PHPStan requires MongoDB extension AND MongoDB ODM bundle
111+
# Install MongoDB PHP extension first (e.g., pecl install mongodb)
112+
# Then install MongoDB ODM:
113+
composer require --dev doctrine/mongodb-odm-bundle
114+
vendor/bin/phpstan analyse --no-interaction --no-progress
115+
116+
# PHPStan will fail without both:
117+
# - mongodb PHP extension (for analyzing Document fixtures)
118+
# - doctrine/mongodb-odm-bundle package (for ODM classes)
119+
```
120+
121+
**Testing Event Listeners (vs Default Event System):**
122+
123+
API Platform has two modes for handling Symfony events:
124+
125+
1. **Default Mode (Event System):** Uses Symfony's event system with event subscribers
126+
2. **Event Listeners Mode:** Uses traditional Symfony event listeners (enabled with `USE_SYMFONY_LISTENERS=1`)
127+
128+
**When to test with event listeners:**
129+
- Set `USE_SYMFONY_LISTENERS=1` environment variable
130+
- Always clear cache after switching modes: `rm -rf tests/Fixtures/app/var/cache/test`
131+
- CI runs separate jobs for both modes to ensure compatibility
132+
133+
**Special Note - Events vs Non-Events:**
134+
The event listeners mode (`USE_SYMFONY_LISTENERS=1`) changes how API Platform hooks into Symfony's lifecycle:
135+
- **Non-events (default):** Uses event subscribers for better performance and flexibility
136+
- **Events (listeners):** Uses traditional event listeners for backward compatibility
137+
- Both modes must be tested to ensure feature compatibility
138+
- When debugging event-related issues, test both modes
139+
140+
### Laravel Tests
141+
142+
**Setup:**
143+
```bash
144+
cd src/Laravel
145+
composer run-script build
146+
```
147+
148+
**PHPUnit:**
149+
```bash
150+
# Run Laravel tests
151+
cd src/Laravel
152+
vendor/bin/phpunit
153+
154+
# Or via composer script
155+
composer run-script test
156+
157+
# Filter tests
158+
vendor/bin/phpunit --filter testMethodName
159+
```
160+
161+
**Static Analysis:**
162+
```bash
163+
cd src/Laravel
164+
./vendor/bin/phpstan analyse --no-interaction --no-progress
165+
```
166+
167+
## Project Overview
168+
169+
API Platform is a powerful, extensible, open-source PHP framework for building API-first projects. It leverages Symfony and Laravel to provide a robust foundation for creating REST and GraphQL APIs.
170+
171+
* **Language:** PHP (with strict types: `declare(strict_types=1);`)
172+
* **Frameworks:** Symfony, Laravel
173+
* **Code Quality:** PSR-12 standard, enforced via `php-cs-fixer` and `phpstan`
174+
* **Testing:** PHPUnit (functional and unit tests), Behat (legacy functional tests - **do not add new Behat tests**)
175+
176+
## Project Structure
177+
178+
The codebase is organized into components supporting both Symfony and Laravel:
179+
180+
**Core Components** (in `src/`):
181+
- `Doctrine/Common`, `Doctrine/Odm`, `Doctrine/Orm` - Doctrine integrations
182+
- `Documentation` - API documentation generation
183+
- `Elasticsearch` - Elasticsearch integration
184+
- `Graphql` - GraphQL support
185+
- `HttpCache` - HTTP caching
186+
- `Hydra` - Hydra JSON-LD vocabulary
187+
- `JsonApi` - JSON:API format support
188+
- `Hal` - HAL format support
189+
- `JsonSchema` - JSON Schema generation
190+
- `Jsonld` - JSON-LD support
191+
- `Laravel` - Laravel-specific implementation
192+
- `Metadata` - Resource metadata handling
193+
- `Openapi` - OpenAPI specification generation
194+
- `ParameterValidator` - Query parameter validation
195+
- `RamseyUuid` - UUID support
196+
- `Serializer` - Serialization layer
197+
- `State` - State management (providers/processors)
198+
- `Symfony` - Symfony-specific implementation
199+
- `Validator` - Validation support
200+
201+
### Symfony Structure
202+
203+
* **Fixtures:**
204+
* API Resources: `tests/Fixtures/TestBundle/ApiResource/`
205+
* Entities: `tests/Fixtures/TestBundle/Entity/`
206+
* Documents (MongoDB): `tests/Fixtures/TestBundle/Document/` (mirror of Entity/)
207+
* **Tests:**
208+
* Functional: `tests/Functional/`
209+
* Unit: Component-specific `src/{Component}/Tests/`
210+
* Behat features: `features/` (legacy - do not add new ones)
211+
* **Test App:** `tests/Fixtures/app/` (Symfony test application)
212+
213+
**Fixture Examples:**
214+
215+
1. **ApiResource with Static Provider (No Entity)** - `tests/Fixtures/TestBundle/ApiResource/Product.php`
216+
```php
217+
#[Get(provider: [self::class, 'provide'])]
218+
class Product
219+
{
220+
#[ApiProperty(identifier: true)]
221+
public string $code;
222+
223+
// Clever: Use a static method as provider to avoid entity persistence
224+
public static function provide()
225+
{
226+
$s = new self();
227+
$s->code = 'test';
228+
return $s;
229+
}
230+
}
231+
```
232+
233+
2. **Entity with QueryParameter Filters** - `tests/Fixtures/TestBundle/Entity/Chicken.php`
234+
```php
235+
#[ORM\Entity]
236+
#[GetCollection(
237+
parameters: [
238+
'chickenCoop' => new QueryParameter(filter: new IriFilter()),
239+
'chickenCoopId' => new QueryParameter(filter: new ExactFilter(), property: 'chickenCoop'),
240+
'name' => new QueryParameter(filter: new ExactFilter()),
241+
'namePartial' => new QueryParameter(
242+
filter: new PartialSearchFilter(),
243+
property: 'name',
244+
),
245+
'autocomplete' => new QueryParameter(
246+
filter: new FreeTextQueryFilter(new OrFilter(new ExactFilter())),
247+
properties: ['name', 'ean']
248+
),
249+
'q' => new QueryParameter(
250+
filter: new FreeTextQueryFilter(new PartialSearchFilter()),
251+
properties: ['name', 'ean']
252+
),
253+
],
254+
)]
255+
class Chicken
256+
{
257+
#[ORM\Id]
258+
#[ORM\GeneratedValue]
259+
#[ORM\Column(type: 'integer')]
260+
private ?int $id = null;
261+
262+
#[ORM\Column(type: 'string', length: 255)]
263+
private string $name;
264+
265+
// ... getters/setters
266+
}
267+
```
268+
269+
**Best Practices:**
270+
- Use static provider method pattern when you don't need database persistence
271+
- Use `Chicken.php` as reference for testing query parameters and filters on entities
272+
- Be smart: create new fixtures rather than modifying existing ones, adding a query parameter to chicken is perfectly fine as long as it doesn't alter the rest
273+
274+
### Laravel Structure
275+
276+
* **Base Directory:** `src/Laravel/`
277+
* **Tests:** `src/Laravel/Tests/`
278+
* Example tests: `EloquentTest.php`, `JsonLdTest.php`
279+
* **Fixtures/Models:** `src/Laravel/workbench/app/`
280+
* DTOs as ApiResource: `workbench/app/ApiResource/`
281+
* Eloquent Models: `workbench/app/Models/` (declared as resources with attributes)
282+
* **PHPUnit Binary:** `src/Laravel/vendor/bin/phpunit`
283+
284+
## Code Style and Conventions
285+
286+
**Critical:** Adherence to established code style is mandatory.
287+
288+
### General Rules
289+
290+
* **Standard:** PSR-12 (enforced via `.php-cs-fixer.dist.php`)
291+
* **Strict Types:** Always use `declare(strict_types=1);` at the top of PHP files
292+
* **Type Hints:** Provide type hints for all arguments and return types
293+
* **Imports:** Use `use` statements for all classes
294+
* Group by type: classes, functions, constants
295+
* Sort alphabetically within groups
296+
* **Naming Conventions:**
297+
* Classes: `PascalCase`
298+
* Methods: `camelCase`
299+
* Variables: `camelCase`
300+
* Constants: `UPPER_SNAKE_CASE`
301+
* **Visibility:** Always explicitly declare `public`, `protected`, or `private`
302+
* **Error Handling:** Use exceptions, not error codes
303+
304+
### Key PHP-CS-Fixer Rules
305+
306+
* **`@Symfony`, `@Symfony:risky`** - Comprehensive Symfony coding standards
307+
* **`@DoctrineAnnotation`** - Consistent Doctrine annotation formatting
308+
* **`strict_param`** - Strict type declarations for parameters
309+
* **`fully_qualified_strict_types`** - Fully qualified class names in type declarations
310+
* **`header_comment`** - Correct license header formatting
311+
* **`ordered_imports`** - Alphabetically sorted imports by type
312+
* **`no_superfluous_phpdoc_tags`** - Remove redundant PHPDoc tags
313+
* **`phpdoc_order`, `phpdoc_trim_consecutive_blank_line_separation`** - Consistent PHPDoc formatting
314+
315+
### Validation Commands (Context Only)
316+
317+
These commands run in CI - understand them but don't execute unless asked:
318+
319+
```bash
320+
# Code style linting
321+
vendor/bin/php-cs-fixer fix --dry-run --diff
322+
323+
# Static analysis
324+
vendor/bin/phpstan analyse
325+
326+
# Component dependency check
327+
composer check-dependencies
328+
329+
# Container linting (Symfony)
330+
tests/Fixtures/app/console lint:container
331+
```
332+
333+
## Contribution Guidelines
334+
335+
### Branching Strategy
336+
337+
* **New Features & Deprecations:** Target `main` branch
338+
* **Bug Fixes:** Target current stable branch (e.g., `4.x`)
339+
* **Legacy Code Removal:** Target `main` branch
340+
341+
### Testing Requirements
342+
343+
* **Always Add Tests:** PHPUnit functional tests are preferred
344+
* **No New Behat Tests:** Use PHPUnit instead (Behat is legacy)
345+
* **Tests Must Pass:** Ensure green CI before submitting
346+
* **Don't Modify Existing Tests:** Create new fixtures/resources instead
347+
* **Test Location:**
348+
* Symfony functional: `tests/Functional/`
349+
* Laravel functional: `src/Laravel/Tests/`
350+
* Component unit: `src/{Component}/Tests/`
351+
352+
### Code Quality
353+
354+
* **Never Break BC:** Backward compatibility is sacred (see https://symfony.com/bc)
355+
* **Update CHANGELOG:** Document changes in `CHANGELOG.md`
356+
* **Documentation PR:** Required for new features (`api-platform/docs` repository)
357+
* **Code Style:** Ensure `php-cs-fixer` and `phpstan` pass
358+
359+
### Commit Messages
360+
361+
Follow [Conventional Commits](https://www.conventionalcommits.org/):
362+
363+
* **Types:** `fix`, `feat`, `docs`, `spec`, `test`, `perf`, `ci`, `chore`
364+
* **Format:** `type(scope): description`
365+
* **Examples:**
366+
* `fix(metadata): resource identifiers from properties`
367+
* `feat(validation): introduce a number constraint`
368+
* `test(graphql): add mutation validation tests`
369+
* **Scope:** Strongly recommended (component name)
370+
* **Note:** Only first commit needs conventional format (others are squashed)
371+
372+
### PR Template
373+
374+
```markdown
375+
| Q | A
376+
| ------------- | ---
377+
| Branch? | main for features / 4.x for bug fixes
378+
| Tickets | Closes #...
379+
| License | MIT
380+
| Doc PR | api-platform/docs#... (for features)
381+
```
382+
383+
**Checklist:**
384+
- [ ] Tests added and passing
385+
- [ ] No BC breaks
386+
- [ ] CHANGELOG.md updated
387+
- [ ] Documentation PR submitted (if feature)
388+
- [ ] Conventional commit format used
389+
- [ ] Code style passes (`php-cs-fixer`, `phpstan`)

0 commit comments

Comments
 (0)