Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message
Php Bundle Code
symkit/sitemap-bundle
cursor
abstract-bundle
dependency-injection
strict-types
phpstan
symfony
readonly-classes
code-style

Strict PHP code style for Symfony bundle source code

Install
php artisan boost:add-skill symkit/sitemap-bundle

Save this content to: .cursor/rules/php-bundle-code.mdc

# PHP Bundle Code Style

## Mandatory Header

Every PHP file starts with `declare(strict_types=1)`.

## Classes

- `final readonly class` for: services, handlers, processors, voters, listeners, DTOs
- Exceptions: Doctrine entities (not `readonly`), bundle class (not `final`), documented inheritance
- Constructor injection only with promoted properties (`private readonly`)

## AbstractBundle Pattern

The bundle class extends `AbstractBundle` (never `Bundle`):

```php
class VendorNameBundle extends AbstractBundle
{
    public function configure(DefinitionConfigurator $definition): void { ... }

    /** @param array{key: type, ...} $config */
    public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void { ... }

public function prependExtension(ContainerConfigurator $container, ContainerBuilder $builder): void { ... }
}
```

- `loadExtension()` receives processed config: always add `@param array{...}` PHPDoc for PHPStan level 9
- No separate `Configuration.php` or `*Extension.php` — everything in the bundle class
- Use `$this->getPath()` for paths, never `\dirname(__DIR__, N)`

## Typing

- All parameters, return types, and properties must be typed
- No `mixed` except with a comment justifying why
- PHPStan level 9: use `@param array{key: type}` for shaped arrays (especially `loadExtension()`)
- Doctrine collections: `/** @var Collection<int, Entity> */`
- Repository returns: `@return Entity[]` or exact union type, never `mixed[]`

## Language Features

- PHP 8 attributes for routing/validation: `#[Route]`, `#[Assert\...]`. Never `@annotations`.
- **No attributes for service/tag registration** (`#[AsEventListener]`, `#[AutoconfigureTag]`, `#[AsTwigComponent]`, etc.) — in a bundle, all service registration goes through `loadExtension()` or XML config (see symfony-bundle rule).
- Native function prefix: `\count()`, `\sprintf()`, `\in_array()`, `\array_map()`
- `match` over `switch` when applicable
- Early return to reduce nesting

## Dependency Injection

- Constructor injection only. No setter, no property injection.
- Never `new Service()` — always DI
- No hardcoded parameters — use `configure()` + env vars
- Prefix service IDs and interface aliases with bundle alias
- `registerForAutoconfiguration()` for custom interfaces

## Forbidden

- `Bundle` with `DependencyInjection/Extension.php` and `Configuration.php` (use `AbstractBundle`)
- `new Configuration()` in extension classes
- Business logic in controllers or entities
- `@phpstan-ignore` without explanatory comment
- Implicit `void` return types
- Anonymous classes for services (untestable)

## After Every Change

Run `make cs-fix` then `make phpstan` to validate style and types.

PHP Bundle Code Style

Mandatory Header

Every PHP file starts with declare(strict_types=1).

Classes

  • final readonly class for: services, handlers, processors, voters, listeners, DTOs
  • Exceptions: Doctrine entities (not readonly), bundle class (not final), documented inheritance
  • Constructor injection only with promoted properties (private readonly)

AbstractBundle Pattern

The bundle class extends AbstractBundle (never Bundle):

class VendorNameBundle extends AbstractBundle
{
    public function configure(DefinitionConfigurator $definition): void { ... }

    /** @param array{key: type, ...} $config */
    public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void { ... }

public function prependExtension(ContainerConfigurator $container, ContainerBuilder $builder): void { ... }
}
  • loadExtension() receives processed config: always add @param array{...} PHPDoc for PHPStan level 9
  • No separate Configuration.php or *Extension.php — everything in the bundle class
  • Use $this->getPath() for paths, never \dirname(__DIR__, N)

Typing

  • All parameters, return types, and properties must be typed
  • No mixed except with a comment justifying why
  • PHPStan level 9: use @param array{key: type} for shaped arrays (especially loadExtension())
  • Doctrine collections: /** @var Collection<int, Entity> */
  • Repository returns: @return Entity[] or exact union type, never mixed[]

Language Features

  • PHP 8 attributes for routing/validation: #[Route], #[Assert\...]. Never @annotations.
  • No attributes for service/tag registration (#[AsEventListener], #[AutoconfigureTag], #[AsTwigComponent], etc.) — in a bundle, all service registration goes through loadExtension() or XML config (see symfony-bundle rule).
  • Native function prefix: \count(), \sprintf(), \in_array(), \array_map()
  • match over switch when applicable
  • Early return to reduce nesting

Dependency Injection

  • Constructor injection only. No setter, no property injection.
  • Never new Service() — always DI
  • No hardcoded parameters — use configure() + env vars
  • Prefix service IDs and interface aliases with bundle alias
  • registerForAutoconfiguration() for custom interfaces

Forbidden

  • Bundle with DependencyInjection/Extension.php and Configuration.php (use AbstractBundle)
  • new Configuration() in extension classes
  • Business logic in controllers or entities
  • @phpstan-ignore without explanatory comment
  • Implicit void return types
  • Anonymous classes for services (untestable)

After Every Change

Run make cs-fix then make phpstan to validate style and types.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle