Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/backwards-compatibility-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: "Setup PHP, with composer and extensions"
uses: "shivammathur/setup-php@v2"
with:
php-version: "8.3"
php-version: "8.4"
coverage: "none"

- name: "Install tool"
Expand Down
45 changes: 18 additions & 27 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
php-version:
- "8.1"
- "8.2"

dependencies:
- "highest"
Expand Down Expand Up @@ -56,21 +56,17 @@ jobs:
strategy:
matrix:
php-version:
- "8.1"
- "8.2"
- "8.3"
- "8.4"

dependencies:
- "lowest"
- "highest"

symfony:
- "~6.4.0"
- "~7.0.0"

exclude:
- php-version: "8.1"
symfony: "~7.0.0"
- "~7.4.0"

steps:
- name: "Checkout"
Expand All @@ -82,7 +78,10 @@ jobs:
coverage: "none"
extensions: "${{ env.PHP_EXTENSIONS }}"
php-version: "${{ matrix.php-version }}"
tools: "composer-require-checker, composer-unused, flex"
tools: "flex, composer-dependency-analyser"

- name: "Remove require-dev section"
run: "composer config --unset require-dev"

- name: "Install composer dependencies"
uses: "ramsey/composer-install@v3"
Expand All @@ -91,11 +90,8 @@ jobs:
env:
SYMFONY_REQUIRE: "${{ matrix.symfony }}"

- name: "Run maglnet/composer-require-checker"
run: "composer-require-checker check --config-file=$(pwd)/composer-require-checker.json"

- name: "Run composer-unused/composer-unused"
run: "composer-unused"
- name: "Run dependency analysis"
run: "composer-dependency-analyser"

static-code-analysis:
name: "Static Code Analysis"
Expand All @@ -105,20 +101,16 @@ jobs:
strategy:
matrix:
php-version:
- "8.1"
- "8.2"
- "8.3"
- "8.4"

dependencies:
- "highest"

symfony:
- "~6.4.0"
- "~7.0.0"

exclude:
- php-version: "8.1"
symfony: "~7.0.0"
- "~7.4.0"

steps:
- name: "Checkout"
Expand All @@ -140,7 +132,7 @@ jobs:
SYMFONY_REQUIRE: "${{ matrix.symfony }}"

- name: "Static analysis"
run: "vendor/bin/psalm --php-version=${{ matrix.php-version }}"
run: "vendor/bin/phpstan analyse"

unit-tests:
name: "Unit tests"
Expand All @@ -150,21 +142,17 @@ jobs:
strategy:
matrix:
php-version:
- "8.1"
- "8.2"
- "8.3"
- "8.4"

dependencies:
- "lowest"
- "highest"

symfony:
- "~6.4.0"
- "~7.0.0"

exclude:
- php-version: "8.1"
symfony: "~7.0.0"
- "~7.4.0"

steps:
- name: "Checkout"
Expand Down Expand Up @@ -196,7 +184,7 @@ jobs:
strategy:
matrix:
php-version:
- "8.3"
- "8.4"

dependencies:
- "highest"
Expand All @@ -211,11 +199,14 @@ jobs:
coverage: "pcov"
extensions: "${{ env.PHP_EXTENSIONS }}"
php-version: "${{ matrix.php-version }}"
tools: "flex"

- name: "Install composer dependencies"
uses: "ramsey/composer-install@v3"
with:
dependency-versions: "${{ matrix.dependencies }}"
env:
SYMFONY_REQUIRE: "~7.4.0"

- name: "Collect code coverage with pcov and phpunit/phpunit"
run: "vendor/bin/phpunit --coverage-clover=.build/logs/clover.xml"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
/composer.lock
/build/
/.phpunit.cache/
.claude/settings.local.json
51 changes: 51 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Symfony bundle (`setono/tag-bag-bundle`) that integrates the [setono/tag-bag](https://github.com/Setono/tag-bag) library into Symfony. It enables injecting dynamic HTML/JS tags (e.g., tracking scripts) into pages via a session-backed tag bag. Tags added during a request cycle are persisted to the session and restored on subsequent page loads.

## Tools

Use `jq` (not `python3`) for JSON parsing in shell commands.

## Commands

```bash
composer phpunit # Run all tests
composer analyse # Static analysis (PHPStan, level max)
composer check-style # Check coding standards (ECS with Sylius coding standard)
composer fix-style # Auto-fix coding standards
composer rector # Run Rector (PHP 8.2 level)

# Run a single test file
vendor/bin/phpunit tests/EventListener/RestoreTagBagSubscriberTest.php

# Run a single test method
vendor/bin/phpunit --filter testMethodName
```

## Architecture

### Request Lifecycle (the core mechanism)

1. **Restore** (`RestoreTagBagSubscriber`, priority 7 on `kernel.request`) — restores tags from session on each main request. Priority is below Symfony's `FirewallListener` (8) but above 0.
2. **Use** — application code adds tags to the `TagBagInterface` service during the request.
3. **Render** — Twig functions (`setono_tag_bag_render_head()`, `_body_begin()`, `_body_end()`, `_section()`, `_all()`) output tags in templates.
4. **Store** (`StoreTagBagSubscriber`, priority -900 on `kernel.response`) — persists unrendered tags to session. Priority is above Symfony's `SessionListener` (-1000) but below 0.

### Key Components

- **`SessionStorage`** — implements `StorageInterface` from tag-bag lib; stores/restores serialized tag data via Symfony sessions.
- **`TwigRenderer`** — renders `TemplateTag` instances with `templateType === 'twig'` using the Twig environment.
- **`RegisterRenderersPass`** — compiler pass that collects all services tagged `setono_tag_bag.renderer` into the composite renderer. Custom renderers are auto-tagged via `RendererInterface` autoconfiguration.
- **`Configuration`** — single config option: `setono_tag_bag.renderer.twig` (bool, defaults to true if TwigBundle is available).

### Service Definitions

XML-based service definitions in `src/Resources/config/`. The main `services.xml` imports from `services/` subdirectory (tag_bag, storage, renderer, event_listener, twig, generator). The Twig renderer integration is conditionally loaded.

## CI Matrix

Tests run against PHP 8.2–8.4, Symfony 6.4 and 7.4, with both lowest and highest dependency versions. CI also runs composer-dependency-analyser, PHPStan, ECS, and Rector checks.
10 changes: 10 additions & 0 deletions composer-dependency-analyser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

use ShipMonk\ComposerDependencyAnalyser\Config\Configuration;

return (new Configuration())
->addPathToExclude(__DIR__ . '/tests')
->ignoreUnknownClasses(['Symfony\Bundle\TwigBundle\TwigBundle'])
;
5 changes: 0 additions & 5 deletions composer-require-checker.json

This file was deleted.

38 changes: 17 additions & 21 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,22 @@
}
],
"require": {
"php": ">=8.1",
"setono/tag-bag": "^2.3",
"symfony/config": "^6.4 || ^7.0",
"symfony/dependency-injection": "^6.4 || ^7.0",
"symfony/event-dispatcher": "^6.4 || ^7.0",
"symfony/http-foundation": "^6.4 || ^7.0",
"symfony/http-kernel": "^6.4 || ^7.0",
"twig/twig": "^2.0 || ^3.0",
"php": ">=8.2",
"setono/tag-bag": "^2.5",
"symfony/config": "^6.4 || ^7.4",
"symfony/dependency-injection": "^6.4 || ^7.4",
"symfony/event-dispatcher": "^6.4 || ^7.4",
"symfony/http-foundation": "^6.4 || ^7.4",
"symfony/http-kernel": "^6.4 || ^7.4",
"twig/twig": "^3.0",
"webmozart/assert": "^1.11"
},
"require-dev": {
"matthiasnoback/symfony-dependency-injection-test": "^4.3 || ^5.1",
"nyholm/symfony-bundle-test": "^2.0 || ^3.0",
"phpspec/prophecy-phpunit": "^2.0",
"phpunit/phpunit": "^10.5.20",
"psalm/plugin-phpunit": "^0.19",
"psalm/plugin-symfony": "^5.1",
"setono/code-quality-pack": "^2.7",
"symfony/twig-bundle": "^6.4 || ^7.0"
},
"conflict": {
"vimeo/psalm": "5.24.0"
"matthiasnoback/symfony-dependency-injection-test": "^5.1 || ^6.0",
"nyholm/symfony-bundle-test": "^3.0",
"phpstan/phpstan-symfony": "^2.0",
"setono/code-quality-pack": "^3.3",
"symfony/twig-bundle": "^6.4 || ^7.4"
},
"prefer-stable": true,
"autoload": {
Expand All @@ -47,12 +41,14 @@
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": false,
"ergebnis/composer-normalize": true
"ergebnis/composer-normalize": true,
"infection/extension-installer": true,
"phpstan/extension-installer": true
},
"sort-packages": true
},
"scripts": {
"analyse": "psalm",
"analyse": "phpstan analyse",
"check-style": "ecs check",
"fix-style": "ecs check --fix",
"phpunit": "phpunit",
Expand Down
5 changes: 5 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
parameters:
level: max
paths:
- src
- tests
24 changes: 0 additions & 24 deletions psalm.xml

This file was deleted.

2 changes: 1 addition & 1 deletion rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
]);

$rectorConfig->sets([
LevelSetList::UP_TO_PHP_81
LevelSetList::UP_TO_PHP_82
]);
};
4 changes: 2 additions & 2 deletions src/EventListener/RestoreTagBagSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;

final class RestoreTagBagSubscriber implements EventSubscriberInterface
final readonly class RestoreTagBagSubscriber implements EventSubscriberInterface
{
public function __construct(private readonly TagBagInterface $tagBag)
public function __construct(private TagBagInterface $tagBag)
{
}

Expand Down
4 changes: 2 additions & 2 deletions src/EventListener/StoreTagBagSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

final class StoreTagBagSubscriber implements EventSubscriberInterface
final readonly class StoreTagBagSubscriber implements EventSubscriberInterface
{
public function __construct(private readonly TagBagInterface $tagBag)
public function __construct(private TagBagInterface $tagBag)
{
}

Expand Down
4 changes: 2 additions & 2 deletions src/Renderer/TwigRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
use Twig\Environment;
use Webmozart\Assert\Assert;

final class TwigRenderer implements RendererInterface
final readonly class TwigRenderer implements RendererInterface
{
public function __construct(private readonly Environment $environment)
public function __construct(private Environment $environment)
{
}

Expand Down
4 changes: 2 additions & 2 deletions src/Storage/SessionStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

final class SessionStorage implements StorageInterface
final readonly class SessionStorage implements StorageInterface
{
public function __construct(private readonly RequestStack $requestStack)
public function __construct(private RequestStack $requestStack)
{
}

Expand Down
4 changes: 2 additions & 2 deletions src/Twig/Runtime.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
use Setono\TagBag\TagBagInterface;
use Twig\Extension\RuntimeExtensionInterface;

final class Runtime implements RuntimeExtensionInterface
final readonly class Runtime implements RuntimeExtensionInterface
{
public function __construct(private readonly TagBagInterface $tagBag)
public function __construct(private TagBagInterface $tagBag)
{
}

Expand Down
2 changes: 1 addition & 1 deletion tests/DependencyInjection/SetonoTagBagExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ public function it_can_load(): void
{
$this->load();

$this->assertTrue(true);
$this->expectNotToPerformAssertions();
}
}
Loading
Loading