|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +This is a Symfony project. Check `composer.json` for the exact Symfony/PHP version |
| 4 | +in use, and read `symfony.lock` to see which recipes ran. Don't assume Doctrine, |
| 5 | +Twig, API Platform, Messenger, or Lock are installed unless one of those says so. |
| 6 | + |
| 7 | +## Ask before generating |
| 8 | + |
| 9 | +If the task doesn't specify, ask rather than guess: |
| 10 | + |
| 11 | +- Persistence: Doctrine ORM, Doctrine ODM, or none? |
| 12 | +- Interface: server-rendered (Twig), API (Serializer, maybe API Platform), or both? |
| 13 | +- Auth: SecurityBundle, and which authenticator? |
| 14 | + |
| 15 | +If you can't ask (no interactive channel), state the assumption you're making and |
| 16 | +pick the smallest option (e.g. no persistence layer) rather than scaffolding a |
| 17 | +full stack nobody asked for. |
| 18 | + |
| 19 | +## Adding features: Flex, not hand-wiring |
| 20 | + |
| 21 | +Install new capabilities with `composer require <package>` (e.g. `symfony/lock`, |
| 22 | +`symfony/messenger`, `orm-pack`) and let the Flex recipe register the bundle and |
| 23 | +generate its config. Don't hand-edit `config/bundles.php` or hand-write a bundle's |
| 24 | +base config; that's what the recipe is for. Don't skip a good-fit component just |
| 25 | +because it isn't installed yet; installing it is one command. |
| 26 | + |
| 27 | +## Conventions |
| 28 | + |
| 29 | +Follow https://symfony.com/doc/current/best_practices.html to write idiomatic |
| 30 | +Symfony: |
| 31 | + |
| 32 | +- Use PHP attributes for framework metadata, and not only on controllers: |
| 33 | + `#[Route]`, `#[MapRequestPayload]`, `#[IsGranted]` on actions, `#[Assert\...]` |
| 34 | + on properties, `#[AsCommand]`, `#[AsEventListener]`, `#[AsMessageHandler]`, and |
| 35 | + `#[AsAlias]` / `#[AsTaggedItem]` / `#[Autoconfigure]` on services. No YAML or |
| 36 | + XML routing. |
| 37 | +- Rely on autowiring and autoconfiguration. Type-hint constructor arguments and |
| 38 | + let the container resolve them. Where a type-hint can't express it, stay in the |
| 39 | + class with `#[Autowire]` (parameters, env vars, expressions) or `#[Target]` (one |
| 40 | + of several implementations of an interface). A YAML service definition is the |
| 41 | + last resort, not the first. |
| 42 | +- Controllers extend `AbstractController`, stay thin, and delegate to services. |
| 43 | +- Use the framework for what it already does: Form for server-rendered forms, |
| 44 | + Validator for validation, Serializer for JSON, Messenger for async work, |
| 45 | + Security (voters, authenticators) for access control, Twig `path()`/`url()` |
| 46 | + instead of hardcoded URLs. |
| 47 | +- Before hand-writing infrastructure (locks, queues, caches, HTTP clients, |
| 48 | + mailers, schedulers) or reaching for a third-party library, check whether a |
| 49 | + Symfony component covers it. It usually does. |
| 50 | + |
| 51 | +Three specifics worth spelling out, because they are easy to get wrong: |
| 52 | + |
| 53 | +- Bind request data with `#[MapRequestPayload]` / `#[MapQueryString]` on action |
| 54 | + arguments, which wires up Serializer and Validator for you, instead of calling |
| 55 | + `json_decode()` or `SerializerInterface` by hand. If neither package is |
| 56 | + installed yet, `composer require` them rather than falling back to manual |
| 57 | + parsing. |
| 58 | +- Use constructor property promotion, and `readonly` for DTOs and value objects. |
| 59 | + Don't mark a service `readonly` if it might become `lazy: true`: a lazy proxy |
| 60 | + can't extend a `readonly` class. |
| 61 | +- Use `symfony/lock` (`LockFactory`) for mutual exclusion. A hand-built flag or |
| 62 | + lock file looks fine in review and is usually wrong under concurrency. |
| 63 | + |
| 64 | +## Everyday workflow |
| 65 | + |
| 66 | +- Run the app with `symfony serve -d`, and commands with `symfony console ...` |
| 67 | + (or `bin/console` when the Symfony CLI isn't available). |
| 68 | +- When something fails, read `var/log/dev.log` and the web profiler |
| 69 | + (`/_profiler`) before changing code. |
| 70 | +- If `maker-bundle` is installed, prefer `bin/console make:*` with every argument |
| 71 | + passed up front and `--no-interaction` where supported: makers prompt on a |
| 72 | + terminal by default, which hangs a non-interactive shell. If a maker still |
| 73 | + needs interactive input, hand-write the code instead. |
| 74 | +- If Doctrine ORM is installed, schema changes go through migrations |
| 75 | + (`bin/console make:migration`, then `doctrine:migrations:migrate`), never |
| 76 | + `doctrine:schema:update` or hand-written SQL. |
| 77 | +- `.env` is committed and holds defaults only. Real secrets belong in `.env.local` |
| 78 | + (git-ignored) or the secrets vault (`bin/console secrets:set`), read via |
| 79 | + `%env(...)%`. |
| 80 | + |
| 81 | +## Testing |
| 82 | + |
| 83 | +Install `symfony/test-pack` if it isn't already. Functional/HTTP tests extend |
| 84 | +`WebTestCase`; service-level tests extend `KernelTestCase`. Run |
| 85 | +`php bin/phpunit` (falls back to `vendor/bin/phpunit`). A feature isn't done |
| 86 | +until it has a test that exercises it the way a caller would, an HTTP request for |
| 87 | +a controller or a service call for a service, not just "it didn't throw." |
| 88 | + |
| 89 | +## Code style |
| 90 | + |
| 91 | +Symfony's coding standard, the `@Symfony` php-cs-fixer ruleset (a PSR-12-derived |
| 92 | +superset). Run `vendor/bin/php-cs-fixer fix` if `friendsofphp/php-cs-fixer` is |
| 93 | +installed; it isn't part of the skeleton by default. |
| 94 | + |
| 95 | +## Discover, don't guess |
| 96 | + |
| 97 | +Framework APIs change between versions and your training data may be stale. Look |
| 98 | +things up in the project instead of relying on memory: |
| 99 | + |
| 100 | +- `bin/console about`: versions, environment, paths. |
| 101 | +- `bin/console debug:router`, `debug:container`, `debug:autowiring <name>`, |
| 102 | + `debug:config <bundle>`, `config:dump-reference <bundle>`: what exists and how |
| 103 | + it is configured. |
| 104 | +- `bin/console lint:container`, plus `lint:twig templates/` and |
| 105 | + `lint:yaml config/` where those packages are installed: validate before running. |
| 106 | +- Read the installed source and docblocks under `vendor/`. |
| 107 | +- Docs: https://symfony.com/doc/current/ (switch to the version matching |
| 108 | + `composer.json` if it differs). |
0 commit comments