12 KiB
12 KiB
Classes/AGENTS.md
Scope: PHP backend components (Controllers, EventListeners, DataHandling, Utils) Parent: ../AGENTS.md
📋 Overview
PHP backend implementation for TYPO3 CKEditor Image extension. Components:
Controllers
- SelectImageController - Image browser wizard, file selection, image info API
- ImageRenderingController - Image rendering and processing for frontend
- ImageLinkRenderingController - Link-wrapped image rendering
EventListeners
- RteConfigurationListener - PSR-14 event for RTE configuration injection
DataHandling
- RteImagesDbHook - Database hooks for image magic reference handling
- RteImageSoftReferenceParser - Soft reference parsing for RTE images
Backend Components
- RteImagePreviewRenderer - Backend preview rendering
Utilities
- ProcessedFilesHandler - File processing and manipulation utilities
🏗️ Architecture Patterns
TYPO3 Patterns
- FAL (File Abstraction Layer): All file operations via ResourceFactory
- PSR-7 Request/Response: HTTP message interfaces for controllers
- PSR-14 Events: Event-driven configuration and hooks
- Dependency Injection: Constructor-based DI (TYPO3 v13+)
- Service Configuration:
Configuration/Services.yamlfor DI registration
File Structure
Classes/
├── Backend/
│ └── Preview/
│ └── RteImagePreviewRenderer.php
├── Controller/
│ ├── ImageLinkRenderingController.php
│ ├── ImageRenderingController.php
│ └── SelectImageController.php
├── DataHandling/
│ └── SoftReference/
│ └── RteImageSoftReferenceParser.php
├── Database/
│ └── RteImagesDbHook.php
├── EventListener/
│ └── RteConfigurationListener.php
└── Utils/
└── ProcessedFilesHandler.php
🔧 Build & Tests
# PHP-specific quality checks
make lint # All linters (syntax + PHPStan + Rector + style)
composer ci:test:php:lint # PHP syntax check
composer ci:test:php:phpstan # Static analysis
composer ci:test:php:rector # Rector modernization check
composer ci:test:php:cgl # Code style check
# Fixes
make format # Auto-fix code style
composer ci:cgl # Alternative: fix style
composer ci:rector # Apply Rector changes
# Full CI
make ci # Complete pipeline
📝 Code Style
Required Patterns
1. Strict Types (Always First)
<?php
declare(strict_types=1);
2. File Header (Auto-managed by PHP-CS-Fixer)
/**
* This file is part of the package netresearch/rte-ckeditor-image.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/
3. Import Order
- Classes first
- Functions second
- Constants third
- One blank line before namespace
4. Type Hints
- All parameters must have type hints
- All return types must be declared
- Use nullable types
?Typewhen appropriate - Use union types
Type1|Type2for PHP 8+
5. Property Types
private ResourceFactory $resourceFactory; // Required type declaration
private readonly ResourceFactory $factory; // Readonly for immutability
6. Alignment
$config = [
'short' => 'value', // Align on =>
'longer' => 'another',
];
🔒 Security
FAL (File Abstraction Layer)
- Always use FAL: Never direct file system access
- ResourceFactory: For retrieving files by UID
- File validation: Check isDeleted(), isMissing()
- ProcessedFile: Use process() for image manipulation
// ✅ Good: FAL usage
$file = $this->resourceFactory->getFileObject($id);
if ($file->isDeleted() || $file->isMissing()) {
throw new \Exception('File not found');
}
// ❌ Bad: Direct file access
$file = file_get_contents('/var/www/uploads/' . $filename);
Input Validation
- Type cast superglobals:
(int)($request->getQueryParams()['id'] ?? 0) - Validate before use: Check ranges, formats, existence
- Exit on error: Use HTTP status codes with
HttpUtility::HTTP_STATUS_*
XSS Prevention
- Fluid templates: Auto-escaping enabled by default
- JSON responses: Use
JsonResponseclass - Localization: Via
LocalizationUtility::translate()
✅ PR/Commit Checklist
PHP-Specific Checks
- ✅ Strict types:
declare(strict_types=1);in all files - ✅ Type hints: All parameters and return types declared
- ✅ PHPStan: Zero errors (
composer ci:test:php:phpstan) - ✅ Code style: PSR-12/PER-CS2.0 compliant (
make format) - ✅ Rector: No modernization suggestions (
composer ci:test:php:rector) - ✅ FAL usage: No direct file system access
- ✅ DI pattern: Constructor injection, no
new ClassName() - ✅ PSR-7: Request/Response for controllers
- ✅ Documentation: PHPDoc for public methods
🎓 Good vs Bad Examples
✅ Good: Controller Pattern
<?php
declare(strict_types=1);
namespace Netresearch\RteCKEditorImage\Controller;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Resource\ResourceFactory;
final class SelectImageController
{
public function __construct(
private readonly ResourceFactory $resourceFactory
) {
}
public function infoAction(ServerRequestInterface $request): ResponseInterface
{
$fileUid = (int)($request->getQueryParams()['fileId'] ?? 0);
if ($fileUid <= 0) {
return new JsonResponse(['error' => 'Invalid file ID'], 400);
}
$file = $this->resourceFactory->getFileObject($fileUid);
return new JsonResponse([
'uid' => $file->getUid(),
'width' => $file->getProperty('width'),
'height' => $file->getProperty('height'),
]);
}
}
❌ Bad: Anti-patterns
<?php
// ❌ Missing strict types
namespace Netresearch\RteCKEditorImage\Controller;
// ❌ Missing PSR-7 types
class SelectImageController
{
// ❌ No constructor DI
public function infoAction($request)
{
// ❌ Direct superglobal access
$fileUid = $_GET['fileId'];
// ❌ No DI - manual instantiation
$factory = new ResourceFactory();
// ❌ No type safety, no validation
$file = $factory->getFileObject($fileUid);
// ❌ Manual JSON encoding
header('Content-Type: application/json');
echo json_encode(['uid' => $file->getUid()]);
exit;
}
}
✅ Good: EventListener Pattern
<?php
declare(strict_types=1);
namespace Netresearch\RteCKEditorImage\EventListener;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\RteCKEditor\Form\Element\Event\AfterPrepareConfigurationForEditorEvent;
final class RteConfigurationListener
{
public function __construct(
private readonly UriBuilder $uriBuilder
) {
}
public function __invoke(AfterPrepareConfigurationForEditorEvent $event): void
{
$configuration = $event->getConfiguration();
$configuration['style']['typo3image'] = [
'routeUrl' => (string)$this->uriBuilder->buildUriFromRoute('rteckeditorimage_wizard_select_image'),
];
$event->setConfiguration($configuration);
}
}
❌ Bad: EventListener Anti-pattern
<?php
namespace Netresearch\RteCKEditorImage\EventListener;
class RteConfigurationListener
{
// ❌ Wrong signature - not invokable
public function handle($event)
{
// ❌ Manual instantiation instead of DI
$uriBuilder = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(UriBuilder::class);
// ❌ Array access without type safety
$config = $event->getConfiguration();
$config['style']['typo3image']['routeUrl'] = $uriBuilder->buildUriFromRoute('rteckeditorimage_wizard_select_image');
$event->setConfiguration($config);
}
}
✅ Good: FAL Usage
protected function getImage(int $id): File
{
try {
$file = $this->resourceFactory->getFileObject($id);
if ($file->isDeleted() || $file->isMissing()) {
throw new FileNotFoundException('File not found or deleted', 1234567890);
}
} catch (\Exception $e) {
throw new FileNotFoundException('Could not load file', 1234567891, $e);
}
return $file;
}
❌ Bad: Direct File Access
// ❌ Multiple issues
protected function getImage($id) // Missing return type, no type hint
{
// ❌ Direct file system access, bypassing FAL
$path = '/var/www/html/fileadmin/' . $id;
// ❌ No validation, no error handling
if (file_exists($path)) {
return file_get_contents($path);
}
return null; // ❌ Should throw exception or return typed null
}
🆘 When Stuck
Documentation
- API Reference: docs/API/Controllers.md - Controller APIs
- Event Listeners: docs/API/EventListeners.md - PSR-14 events
- Data Handling: docs/API/DataHandling.md - Database hooks
- Architecture: docs/Architecture/Overview.md - System design
TYPO3 Resources
- FAL Documentation: https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Fal/Index.html
- PSR-14 Events: https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Events/Index.html
- Dependency Injection: https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/DependencyInjection/Index.html
- Controllers: https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Backend/Controllers/Index.html
Common Issues
- ResourceFactory errors: Check file exists, not deleted, proper UID
- DI not working: Verify
Configuration/Services.yamlregistration - PHPStan errors: Update baseline:
composer ci:test:php:phpstan:baseline - Type errors: Enable strict_types, add all type hints
📐 House Rules
Controllers
- Extend framework controllers: ElementBrowserController for browsers
- Final by default: Use
final classunless inheritance required - PSR-7 types: ServerRequestInterface → ResponseInterface
- JSON responses: Use
JsonResponseclass - Validation first: Validate all input parameters at method start
EventListeners
- Invokable: Use
__invoke()method signature - Event type hints: Type-hint specific event classes
- Immutability aware: Get, modify, set configuration/state
- Final classes: Event listeners should be final
DataHandling
- Soft references: Implement soft reference parsing for data integrity
- Database hooks: Use for maintaining referential integrity
- Transaction safety: Consider rollback scenarios
Dependencies
- Constructor injection: All dependencies via constructor
- Readonly properties: Use
readonlyfor immutable dependencies - Interface over implementation: Depend on interfaces when available
- GeneralUtility::makeInstance: Only for factories or when DI unavailable
Error Handling
- Type-specific exceptions: Use TYPO3 exception hierarchy
- HTTP status codes: Via HttpUtility constants
- Meaningful messages: Include context in exception messages
- Log important errors: Use TYPO3 logging framework
Testing
- Functional tests: For controllers, database operations
- Unit tests: For utilities, isolated logic
- Mock FAL: Use TYPO3 testing framework FAL mocks
- Test location:
Tests/Functional/andTests/Unit/
🔗 Related
- Resources/AGENTS.md - JavaScript/CKEditor integration
- Tests/AGENTS.md - Testing patterns
- Configuration/Services.yaml - DI container configuration
- docs/API/ - Complete API documentation