skills/pimcore-studio-backend-code-style/SKILL.md
Pimcore Studio PHP code style rules — strict types, class modifiers, final/readonly patterns, formatting (120 chars), named arguments, imports, PHPDoc, @throws documentation, and constructor promotion
npx skillsauth add pimcore/skills pimcore-studio-backend-code-styleInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The StudioBackendBundle uses a layered architecture: Controllers (HTTP only) -> Services (business logic) -> Hydrators (DTO creation). Events provide extension points. All APIs are OpenAPI-documented.
These code style rules apply to ALL PHP code written for Studio bundles.
Every PHP file starts with:
<?php
declare(strict_types=1);
| Class Type | Modifier | Why |
|-----------|----------|-----|
| Service implementation | final readonly class | No inheritance, immutable after construction |
| Hydrator implementation | final readonly class | Same |
| Controller | final class | Cannot be readonly (extends AbstractApiController with mutable state) |
| Response/Parameter DTO | final class or final readonly class | final class when using AdditionalAttributesTrait (has mutable array); final readonly class for parameter-only DTOs |
| Event | final class | Extends AbstractPreResponseEvent (has mutable state) |
| Interface | interface | Standard |
All classes except events get @internal in their PHPDoc:
/**
* @internal
*/
Events are public API (no @internal).
Always use constructor promotion for all injected dependencies and DTO properties:
public function __construct(
private readonly ConfigurationServiceInterface $configurationService
) {
}
Use readonly on every property that is never reassigned. Prefer final readonly class when all properties are readonly and the class has no traits with mutable state.
return in multi-statement methods.Do NOT use named arguments unless you need to skip positional parameters:
// GOOD -- skipping first positional param
$this->hydrator->hydrateKeyName(groupName: $group->getName(), keyName: $key->getName());
// BAD -- unnecessary named arguments
$this->hydrator->hydrateKeyName(keyId: null, groupName: $group->getName(), keyName: $key->getName());
// BAD -- named arguments when all are positional
$this->service->doSomething(name: $name, config: $config);
use imports. Never use FQCNs inline in code, PHPDoc, or @throws.// BAD -- redundant
/**
* @param string $name The config name
* @return ConfigurationDetail
*/
public function getConfiguration(string $name): ConfigurationDetail
// GOOD -- no PHPDoc needed when types are self-evident
public function getConfiguration(string $name): ConfigurationDetail
@throws, semantic descriptions for non-obvious parameters.@throws to document what they throw (since they have no interface).Remove redundant default values at call sites. If the method signature has $param = null and you'd pass null, omit the argument (use named args if needed to skip):
// BAD
$this->hydrator->hydrateKeyName(null, $group->getName(), $key->getName());
// GOOD
$this->hydrator->hydrateKeyName(groupName: $group->getName(), keyName: $key->getName());
@throws on interfaces only, not on implementations. Implementations inherit the contract.@throws:
// GOOD
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* @throws NotFoundHttpException
*/
// BAD
/**
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
\Exception. Import Exception via use Exception; and reference it as @throws Exception (short name, not FQCN).@throws for specific vendor exceptions that callers should be aware of (e.g., InvalidConfigurationException, QueueNotEmptyException).@throws via use statements at the top of the file -- this includes Exception itself.@throws directly on the trait methods, since traits have no interfaces.tools
UX and UI design conventions for Pimcore Studio - layout, spacing, action labels, writing style, and design principles for consistent extensions
tools
Widget system in Pimcore Studio UI - registering widgets, opening them in layout areas, WidgetManagerTabConfig, and connecting widgets to navigation
tools
How bundles consume the Pimcore Studio UI SDK - plugins, modules, DI, registries, and imports
development
TypeScript coding standards and best practices for Pimcore Studio UI - type safety, null checks, and code quality