Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Symfony Conventions

ASecurity

Consolidated Symfony conventions (Symfony 6.4 / 7.x): attribute routing, controllers as services, autowiring/DI, Form types, Validation constraints, Voters for authorization, the Serializer/DTO contract, and Messenger. Apply when writing or reviewing Symfony backend code. Activated automatically by symfony-plugin/stack.md as a convention skill for the development phase. Apply when: writing or reviewing Symfony controllers, services, forms, validators, voters, and serialization. Do NOT use t...

35 stars
0 votes
0 copies
0 views
Added 9/22/2026
developmentgophptestingapifrontendbackendsecurity

Works with

cliapi

Security Analysis

A100/100

Scanned 9/22/2026

Install to Claude Code

$npx -y skills add AratKruglik/claude-sdlc --skill symfony-conventions --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Symfony Conventions?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Symfony Conventions
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/aratkruglik-symfony-conventions/badge)](https://www.skillsdirectory.com/skills/aratkruglik-symfony-conventions)

More formats (shields.io, HTML) on the badges page.

Download with Pro
Files
SKILL.md
---
name: symfony-conventions
description: |
  Consolidated Symfony conventions (Symfony 6.4 / 7.x): attribute routing, controllers as services, autowiring/DI, Form types, Validation constraints, Voters for authorization, the Serializer/DTO contract, and Messenger. Apply when writing or reviewing Symfony backend code.
  Activated automatically by symfony-plugin/stack.md as a convention skill for the development phase.

  Apply when: writing or reviewing Symfony controllers, services, forms, validators, voters, and serialization.

  Do NOT use this skill for:
  - PHP language idioms (strict_types, enums, readonly) — see php-foundation:php-conventions.
  - Doctrine queries / entities — see symfony-plugin:doctrine-patterns.
  - Testing — injected by the QA phase (WebTestCase/KernelTestCase) + php-foundation:php-testing.
user-invocable: false
paths: ["src/**", "config/**", "templates/**"]
---

# Symfony Conventions (6.4 / 7.x)

This skill encodes the conventions used across modern Symfony projects. Apply alongside `php-foundation:php-conventions` (language idioms) when implementing features.

## 1. Attribute routing

Routes are declared with `#[Route]` attributes on controller actions — not YAML/XML for new code.

```php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/subscriptions', name: 'subscription_')]
final class SubscriptionController extends AbstractController
{
    #[Route('/{id}', name: 'show', methods: ['GET'], requirements: ['id' => '\d+'])]
    public function show(int $id): Response
    {
        // ...
    }
}
```

A class-level `#[Route]` sets a path prefix and name prefix. Always set `methods:` explicitly. Use `requirements:` to constrain route params.

## 2. Controllers as services + thin actions

- Extend `AbstractController` (gives `render()`, `json()`, `denyAccessUnlessGranted()`, `getUser()`).
- A controller with `#[Route]` is automatically registered as a service with autowired action arguments.
- Keep actions thin: authorize → map/validate input → call a service → return a response. Business logic lives in services.

```php
public function __construct(
    private readonly SubscriptionManager $subscriptions,
) {
}

#[Route('', name: 'store', methods: ['POST'])]
public function store(#[MapRequestPayload] CreateSubscriptionRequest $request): Response
{
    $this->denyAccessUnlessGranted('SUBSCRIPTION_CREATE');

    $subscription = $this->subscriptions->create($this->getUser(), $request);

    return $this->redirectToRoute('subscription_show', ['id' => $subscription->getId()]);
}
```

`#[MapRequestPayload]` deserializes + validates the request body into a DTO automatically.

## 3. Dependency injection / autowiring

- **Constructor injection only.** Never property injection or `@required` setters for new code.
- Services are autowired + autoconfigured by default (`config/services.yaml` `_defaults: autowire: true, autoconfigure: true`).
- Inject scalars/parameters with `#[Autowire]`:

```php
public function __construct(
    private readonly HttpClientInterface $http,
    #[Autowire('%env(STRIPE_SECRET)%')] private readonly string $stripeSecret,
) {
}
```

- Bind/alias an interface to an implementation in `services.yaml` only when autowiring can't resolve it. Avoid manual service definitions otherwise.

## 4. Form types

Use Form types for HTML form workflows; constructor-inject collaborators.

```php
namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

final class SubscriptionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('plan', ChoiceType::class, ['choices' => Plan::cases()])
            ->add('startsAt', DateType::class, ['required' => false]);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults(['data_class' => CreateSubscriptionRequest::class]);
    }
}
```

For JSON APIs prefer `#[MapRequestPayload]` DTOs over Form types.

## 5. Validation — constraints on DTOs/entities

**Always** declare validation with Constraint attributes; never validate inline in the controller.

```php
namespace App\Dto;

use Symfony\Component\Validator\Constraints as Assert;

final class CreateSubscriptionRequest
{
    public function __construct(
        #[Assert\NotBlank]
        #[Assert\Choice(['basic', 'pro', 'enterprise'])]
        public readonly string $plan,

        #[Assert\GreaterThanOrEqual('today')]
        public readonly ?\DateTimeImmutable $startsAt = null,
    ) {
    }
}
```

`#[MapRequestPayload]` runs validation automatically and returns 422 on failure. Otherwise call the `ValidatorInterface` in the service and use `#[Valid]` for nested objects.

## 6. Authorization — Voters

Centralize authorization in Voters; never inline role checks.

```php
namespace App\Security\Voter;

use App\Entity\Subscription;
use App\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;

final class SubscriptionVoter extends Voter
{
    public const VIEW = 'SUBSCRIPTION_VIEW';
    public const EDIT = 'SUBSCRIPTION_EDIT';

    protected function supports(string $attribute, mixed $subject): bool
    {
        return in_array($attribute, [self::VIEW, self::EDIT], true)
            && $subject instanceof Subscription;
    }

    protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
    {
        $user = $token->getUser();
        if (!$user instanceof User) {
            return false;
        }

        return $subject->getOwner() === $user || $user->isAdmin();
    }
}
```

Use via `#[IsGranted('SUBSCRIPTION_EDIT', subject: 'subscription')]` on the action, or `$this->denyAccessUnlessGranted(...)`. Coarse, path-based rules go in `security.yaml` `access_control`.

**Never** inline `if (in_array('ROLE_ADMIN', $user->getRoles()))` — that bypasses the authorization layer and is untestable.

## 7. Serializer / DTO contract

Never serialize a raw entity with all fields. Use serialization groups or dedicated DTOs.

```php
use Symfony\Component\Serializer\Attribute\Groups;

class Subscription
{
    #[Groups(['subscription:read'])]
    private int $id;

    #[Groups(['subscription:read'])]
    private string $plan;

    // no #[Groups] on stripeCustomerId → never serialized
}

// in the controller:
return $this->json($subscription, context: ['groups' => 'subscription:read']);
```

Document the exposed shape (DTO/group) so a SPA frontend agent can consume it.

## 8. Messenger — async work

Offload slow/side-effectful work to the message bus.

```php
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

final readonly class SendSubscriptionReceipt
{
    public function __construct(public int $subscriptionId) {}
}

#[AsMessageHandler]
final class SendSubscriptionReceiptHandler
{
    public function __construct(private readonly Mailer $mailer) {}

    public function __invoke(SendSubscriptionReceipt $message): void
    {
        // ...
    }
}
```

Dispatch with `$this->bus->dispatch(new SendSubscriptionReceipt($id))`. Configure transports (`async`) in `config/packages/messenger.yaml`.

## 9. Configuration

- Service config in `config/services.yaml`; package config in `config/packages/*.yaml`.
- Env values via `%env(...)%` (typed: `%env(int:...)%`, `%env(bool:...)%`). Secrets via the Secrets Vault (`bin/console secrets:set`). Never hardcode.
- Bind app parameters under `parameters:` and inject with `#[Autowire('%param%')]`.

## 10. Anti-patterns to avoid

| Don't | Do |
|---|---|
| YAML/XML routing for new controllers | `#[Route]` attributes |
| Property/setter injection | Constructor injection |
| Inline `$validator->validate()` in controllers | Constraint attributes + `#[MapRequestPayload]` / `#[Valid]` |
| `in_array('ROLE_X', $user->getRoles())` | Voter + `#[IsGranted]` |
| `return $this->json($entity)` (all fields) | Serialization groups or a DTO |
| Business logic in the controller | Service class |
| Hardcoded secrets in config | `%env(...)%` / Secrets Vault |

## 11. Checklist before completing development phase

- [ ] All new routes use `#[Route]` with explicit `methods:`
- [ ] Controllers extend `AbstractController` and are thin; logic in services
- [ ] All collaborators injected via constructor
- [ ] All input validated via Constraint attributes (no inline validation)
- [ ] Authorization via Voters / `access_control` (no inline role checks)
- [ ] Serialized output uses groups/DTOs (no raw-entity exposure)
- [ ] No hardcoded secrets — `%env(...)%` only
- [ ] `php bin/console lint:container` passes
- [ ] `vendor/bin/php-cs-fixer fix` clean

Attribution

AratKruglikAratKruglik
View sourceMore from AratKruglik →
SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Related Skills

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

284072 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2192 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

9881 votes

Pentest

PTES-aligned adversarial security audit for backend, frontend, and mobile applications. Produces a CVSS-scored Hacker Report with verified PoCs and phased remediation.

5491 votes
View all in development →