mirror of
https://github.com/symfony/ux.symfony.com.git
synced 2026-03-24 00:02:09 +01:00
Add PHPStan
This commit is contained in:
31
.github/workflows/phpstan.yaml
vendored
Normal file
31
.github/workflows/phpstan.yaml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: PHPStan
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
phpstan:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.5'
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ramsey/composer-install@v3
|
||||
|
||||
- name: Warmup Symfony cache for PHPStan
|
||||
run: php bin/console cache:warmup
|
||||
env:
|
||||
APP_ENV: test
|
||||
|
||||
- name: Run PHPStan
|
||||
run: vendor/bin/phpstan analyse
|
||||
17
.github/workflows/tests.yaml
vendored
17
.github/workflows/tests.yaml
vendored
@@ -1,5 +1,11 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -13,19 +19,8 @@ jobs:
|
||||
with:
|
||||
php-version: '8.5'
|
||||
|
||||
- name: Install root dependencies
|
||||
uses: ramsey/composer-install@v3
|
||||
with:
|
||||
working-directory: ${{ github.workspace }}
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ramsey/composer-install@v3
|
||||
with:
|
||||
working-directory: ux.symfony.com
|
||||
dependency-versions: 'highest'
|
||||
|
||||
- name: Importmap dependencies
|
||||
run: php bin/console importmap:install
|
||||
|
||||
- name: Build Sass assets
|
||||
run: php bin/console sass:build
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -20,3 +20,7 @@
|
||||
###> vincentlanglet/twig-cs-fixer ###
|
||||
/.twig-cs-fixer.cache
|
||||
###< vincentlanglet/twig-cs-fixer ###
|
||||
|
||||
###> phpstan/phpstan ###
|
||||
phpstan.neon
|
||||
###< phpstan/phpstan ###
|
||||
|
||||
4
AGENTS.md
Normal file
4
AGENTS.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Development Commands
|
||||
|
||||
- Run PHPStan: `symfony php vendor/bin/phpstan analyse`
|
||||
- Run PHPUnit: `symfony php vendor/bin/simple-phpunit`
|
||||
@@ -14,6 +14,7 @@
|
||||
"intervention/image": "^2.7.2",
|
||||
"kornrunner/blurhash": "^1.2.2",
|
||||
"league/commonmark": "^2.6.0",
|
||||
"phpstan/phpdoc-parser": "^2.3",
|
||||
"symfony/asset": "8.0.*",
|
||||
"symfony/asset-mapper": "8.0.*",
|
||||
"symfony/console": "8.0.*",
|
||||
@@ -69,6 +70,10 @@
|
||||
"twig/twig": "^3.22"
|
||||
},
|
||||
"require-dev": {
|
||||
"kocal/phpstan-symfony-ux": "^1.1",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-doctrine": "^2.0",
|
||||
"phpstan/phpstan-symfony": "^2.0",
|
||||
"phpunit/phpunit": "^9.6.21",
|
||||
"symfony/browser-kit": "8.0.*",
|
||||
"symfony/css-selector": "8.0.*",
|
||||
|
||||
303
composer.lock
generated
303
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "311ac68b2d9eb622b96fa52b63f36e36",
|
||||
"content-hash": "5ef3f75f2ff5431ddddb3a76deb7da4f",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/semver",
|
||||
@@ -2038,6 +2038,53 @@
|
||||
},
|
||||
"time": "2025-12-22T12:14:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpdoc-parser",
|
||||
"version": "2.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
||||
"reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/16dbf9937da8d4528ceb2145c9c7c0bd29e26374",
|
||||
"reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "^2.0",
|
||||
"nikic/php-parser": "^5.3.0",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpstan/phpstan-strict-rules": "^2.0",
|
||||
"phpunit/phpunit": "^9.6",
|
||||
"symfony/process": "^5.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PHPStan\\PhpDocParser\\": [
|
||||
"src/"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.1"
|
||||
},
|
||||
"time": "2026-01-12T11:33:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/cache",
|
||||
"version": "3.0.0",
|
||||
@@ -10163,6 +10210,63 @@
|
||||
},
|
||||
"time": "2024-11-21T13:46:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "kocal/phpstan-symfony-ux",
|
||||
"version": "v1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Kocal/phpstan-symfony-ux.git",
|
||||
"reference": "0746480622429b8b0ee2383251585213d9a87c7d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Kocal/phpstan-symfony-ux/zipball/0746480622429b8b0ee2383251585213d9a87c7d",
|
||||
"reference": "0746480622429b8b0ee2383251585213d9a87c7d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"phpstan/phpstan": "^2.1.13"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^11.1",
|
||||
"symfony/ux-live-component": "^2.0",
|
||||
"symfony/ux-twig-component": "^2.0",
|
||||
"symplify/easy-coding-standard": "^13.0"
|
||||
},
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
"extension.neon"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/deprecated-aliases.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Kocal\\PHPStanSymfonyUX\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Hugo Alliaume",
|
||||
"email": "hugo@alliau.me"
|
||||
}
|
||||
],
|
||||
"description": "PHPStan rules for Symfony UX",
|
||||
"support": {
|
||||
"issues": "https://github.com/Kocal/phpstan-symfony-ux/issues",
|
||||
"source": "https://github.com/Kocal/phpstan-symfony-ux/tree/v1.1.0"
|
||||
},
|
||||
"time": "2025-12-16T00:28:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.13.4",
|
||||
@@ -10399,6 +10503,203 @@
|
||||
},
|
||||
"time": "2022-02-21T01:04:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.35",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/72f843c7f59d3aac0b7510f5e70913a9b72a8e88",
|
||||
"reference": "72f843c7f59d3aac0b7510f5e70913a9b72a8e88",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan-shim": "*"
|
||||
},
|
||||
"bin": [
|
||||
"phpstan",
|
||||
"phpstan.phar"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"keywords": [
|
||||
"dev",
|
||||
"static analysis"
|
||||
],
|
||||
"support": {
|
||||
"docs": "https://phpstan.org/user-guide/getting-started",
|
||||
"forum": "https://github.com/phpstan/phpstan/discussions",
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"security": "https://github.com/phpstan/phpstan/security/policy",
|
||||
"source": "https://github.com/phpstan/phpstan-src"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/ondrejmirtes",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/phpstan",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-01-20T17:33:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-doctrine",
|
||||
"version": "2.0.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-doctrine.git",
|
||||
"reference": "2d2ad04a0ac14ac52e21ad47ec67a54a14355c1f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/2d2ad04a0ac14ac52e21ad47ec67a54a14355c1f",
|
||||
"reference": "2d2ad04a0ac14ac52e21ad47ec67a54a14355c1f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4 || ^8.0",
|
||||
"phpstan/phpstan": "^2.1.34"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/collections": "<1.0",
|
||||
"doctrine/common": "<2.7",
|
||||
"doctrine/mongodb-odm": "<1.2",
|
||||
"doctrine/orm": "<2.5",
|
||||
"doctrine/persistence": "<1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"cache/array-adapter": "^1.1",
|
||||
"composer/semver": "^3.3.2",
|
||||
"cweagans/composer-patches": "^1.7.3",
|
||||
"doctrine/annotations": "^2.0",
|
||||
"doctrine/collections": "^1.6 || ^2.1",
|
||||
"doctrine/common": "^2.7 || ^3.0",
|
||||
"doctrine/dbal": "^3.3.8",
|
||||
"doctrine/lexer": "^2.0 || ^3.0",
|
||||
"doctrine/mongodb-odm": "^2.4.3",
|
||||
"doctrine/orm": "^2.16.0",
|
||||
"doctrine/persistence": "^2.2.1 || ^3.2",
|
||||
"gedmo/doctrine-extensions": "^3.8",
|
||||
"nesbot/carbon": "^2.49",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0.2",
|
||||
"phpstan/phpstan-phpunit": "^2.0.8",
|
||||
"phpstan/phpstan-strict-rules": "^2.0",
|
||||
"phpunit/phpunit": "^9.6.20",
|
||||
"ramsey/uuid": "^4.2",
|
||||
"symfony/cache": "^5.4",
|
||||
"symfony/uid": "^5.4 || ^6.4 || ^7.3"
|
||||
},
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
"extension.neon",
|
||||
"rules.neon"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PHPStan\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Doctrine extensions for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-doctrine/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.13"
|
||||
},
|
||||
"time": "2026-01-18T16:15:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-symfony",
|
||||
"version": "2.0.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-symfony.git",
|
||||
"reference": "5a7ab5319a0b0d856ddbe08f67a21b00b386107f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/5a7ab5319a0b0d856ddbe08f67a21b00b386107f",
|
||||
"reference": "5a7ab5319a0b0d856ddbe08f67a21b00b386107f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-simplexml": "*",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"phpstan/phpstan": "^2.1.13"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/framework-bundle": "<3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||
"phpstan/phpstan-phpunit": "^2.0.8",
|
||||
"phpstan/phpstan-strict-rules": "^2.0",
|
||||
"phpunit/phpunit": "^9.6",
|
||||
"psr/container": "1.1.2",
|
||||
"symfony/config": "^5.4 || ^6.1",
|
||||
"symfony/console": "^5.4 || ^6.1",
|
||||
"symfony/dependency-injection": "^5.4 || ^6.1",
|
||||
"symfony/form": "^5.4 || ^6.1",
|
||||
"symfony/framework-bundle": "^5.4 || ^6.1",
|
||||
"symfony/http-foundation": "^5.4 || ^6.1",
|
||||
"symfony/messenger": "^5.4",
|
||||
"symfony/polyfill-php80": "^1.24",
|
||||
"symfony/serializer": "^5.4",
|
||||
"symfony/service-contracts": "^2.2.0"
|
||||
},
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
"extension.neon",
|
||||
"rules.neon"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PHPStan\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Lukáš Unger",
|
||||
"email": "looky.msc@gmail.com",
|
||||
"homepage": "https://lookyman.net"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Framework extensions and rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-symfony/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.10"
|
||||
},
|
||||
"time": "2026-01-20T16:40:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.32",
|
||||
|
||||
42
phpstan.dist.neon
Normal file
42
phpstan.dist.neon
Normal file
@@ -0,0 +1,42 @@
|
||||
includes:
|
||||
- vendor/phpstan/phpstan-doctrine/extension.neon
|
||||
- vendor/phpstan/phpstan-symfony/extension.neon
|
||||
- vendor/kocal/phpstan-symfony-ux/extension.neon
|
||||
- tools/phpstan/symfony-configuration.php
|
||||
|
||||
parameters:
|
||||
level: 6
|
||||
paths:
|
||||
- bin/
|
||||
- config/
|
||||
- public/
|
||||
- src/
|
||||
- tests/
|
||||
excludePaths:
|
||||
- config/reference.php
|
||||
|
||||
doctrine:
|
||||
objectManagerLoader: tools/phpstan/object-manager.php
|
||||
|
||||
ignoreErrors:
|
||||
- message: '#Method .+::test.+\(\) has no return type specified\.#'
|
||||
paths:
|
||||
- tests/
|
||||
|
||||
rules:
|
||||
# LiveComponent rules
|
||||
- Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveActionMethodsVisibilityRule
|
||||
- Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveListenerMethodsVisibilityRule
|
||||
- Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LivePropHydrationMethodsRule
|
||||
- Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LivePropModifierMethodRule
|
||||
|
||||
# TwigComponent rules
|
||||
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ClassMustBeFinalRule
|
||||
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ClassNameMustNotEndWithComponentRule
|
||||
# ExposePublicPropsMustBeFalseRule is NOT enabled as requested
|
||||
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ForbiddenAttributesPropertyRule
|
||||
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ForbiddenClassPropertyRule
|
||||
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\MethodsVisibilityRule
|
||||
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\PostMountMethodSignatureRule
|
||||
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\PreMountMethodSignatureRule
|
||||
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\PublicPropertiesMustBeCamelCaseRule
|
||||
@@ -76,6 +76,9 @@ final class SitemapController extends AbstractController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $parameters
|
||||
*/
|
||||
private function generateAbsoluteUrl(string $route, array $parameters = []): string
|
||||
{
|
||||
return $this->generateUrl($route, $parameters, UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
|
||||
@@ -57,10 +57,13 @@ class AutocompleteController extends AbstractController
|
||||
return $words[array_rand($words)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<int, Food> $foods
|
||||
*/
|
||||
private function generateEatingMessage(Collection $foods, string $name): string
|
||||
{
|
||||
$i = 0;
|
||||
$foodStrings = $foods->map(function (Food $food) use (&$i, $foods) {
|
||||
$foodStrings = $foods->map(static function (Food $food) use (&$i, $foods) {
|
||||
++$i;
|
||||
$str = $food->getName();
|
||||
|
||||
|
||||
@@ -37,11 +37,13 @@ class LiveComponentController extends AbstractController
|
||||
return $this->redirectToRoute('app_demos');
|
||||
}
|
||||
|
||||
// Permanent Redirect old URL
|
||||
if (null !== $liveDemo = $liveDemoRepository->find($demo)) {
|
||||
return $this->redirectToRoute($liveDemo->getRoute(), [], 301);
|
||||
}
|
||||
try {
|
||||
// Permanent Redirect old URL
|
||||
$liveDemo = $liveDemoRepository->find($demo);
|
||||
|
||||
throw $this->createNotFoundException(\sprintf('Live Component demo "%s" not found.', $demo));
|
||||
return $this->redirectToRoute($liveDemo->getRoute(), [], 301);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
throw $this->createNotFoundException(\sprintf('Live Component demo "%s" not found.', $demo), previous: $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +148,7 @@ class TurboController extends AbstractController
|
||||
}
|
||||
}
|
||||
|
||||
/** @var array<string> */
|
||||
private static array $messages = [
|
||||
'Hey!',
|
||||
'Hola!',
|
||||
|
||||
@@ -38,6 +38,9 @@ class Invoice
|
||||
#[Assert\Range(min: 0, max: 100)]
|
||||
private int $taxRate = 0;
|
||||
|
||||
/**
|
||||
* @var Collection<int, InvoiceItem>
|
||||
*/
|
||||
#[ORM\OneToMany(mappedBy: 'invoice', targetEntity: InvoiceItem::class, orphanRemoval: true)]
|
||||
private Collection $invoiceItems;
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ class TodoList
|
||||
#[NotBlank]
|
||||
private ?string $name = null;
|
||||
|
||||
/**
|
||||
* @var Collection<int, TodoItem>
|
||||
*/
|
||||
#[ORM\OneToMany(mappedBy: 'todoList', targetEntity: TodoItem::class, orphanRemoval: true, cascade: ['persist'])]
|
||||
#[Valid]
|
||||
private Collection $todoItems;
|
||||
|
||||
@@ -16,6 +16,9 @@ use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<array{item?: string}>
|
||||
*/
|
||||
class AddTodoItemForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
|
||||
@@ -17,6 +17,9 @@ use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<array{name?: string, animal?: string}>
|
||||
*/
|
||||
class AnimalCreationForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
|
||||
@@ -13,8 +13,12 @@ namespace App\Form;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
use Symfony\UX\Dropzone\Form\DropzoneType;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<array{file?: File}>
|
||||
*/
|
||||
class DropzoneForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
|
||||
@@ -12,12 +12,16 @@
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\Food;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\Count;
|
||||
use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
|
||||
use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<Collection<int, Food>>
|
||||
*/
|
||||
#[AsEntityAutocompleteField]
|
||||
class FoodAutocompleteField extends AbstractType
|
||||
{
|
||||
|
||||
@@ -22,6 +22,9 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfonycasts\DynamicForms\DependentField;
|
||||
use Symfonycasts\DynamicForms\DynamicFormBuilder;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<MealPlan>
|
||||
*/
|
||||
class MealPlannerForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
@@ -36,22 +39,22 @@ class MealPlannerForm extends AbstractType
|
||||
$builder
|
||||
->add('meal', EnumType::class, [
|
||||
'class' => Meal::class,
|
||||
'choice_label' => fn (Meal $meal): string => $meal->getReadable(),
|
||||
'choice_label' => static fn (Meal $meal): string => $meal->getReadable(),
|
||||
'placeholder' => 'Which meal is it?',
|
||||
'autocomplete' => true,
|
||||
])
|
||||
// see: https://github.com/SymfonyCasts/dynamic-forms
|
||||
->addDependent('mainFood', 'meal', function (DependentField $field, ?Meal $meal) {
|
||||
->addDependent('mainFood', 'meal', static function (DependentField $field, ?Meal $meal) {
|
||||
$field->add(EnumType::class, [
|
||||
'class' => Food::class,
|
||||
'placeholder' => null === $meal ? 'Select a meal first' : \sprintf('What\'s for %s?', $meal->getReadable()),
|
||||
'choices' => $meal?->getFoodChoices(),
|
||||
'choice_label' => fn (Food $food): string => $food->getReadable(),
|
||||
'choice_label' => static fn (Food $food): string => $food->getReadable(),
|
||||
'disabled' => null === $meal,
|
||||
'autocomplete' => true,
|
||||
]);
|
||||
})
|
||||
->addDependent('pizzaSize', 'mainFood', function (DependentField $field, ?Food $food) {
|
||||
->addDependent('pizzaSize', 'mainFood', static function (DependentField $field, ?Food $food) {
|
||||
if (Food::Pizza !== $food) {
|
||||
return;
|
||||
}
|
||||
@@ -59,7 +62,7 @@ class MealPlannerForm extends AbstractType
|
||||
$field->add(EnumType::class, [
|
||||
'class' => PizzaSize::class,
|
||||
'placeholder' => 'What size pizza?',
|
||||
'choice_label' => fn (PizzaSize $pizzaSize): string => $pizzaSize->getReadable(),
|
||||
'choice_label' => static fn (PizzaSize $pizzaSize): string => $pizzaSize->getReadable(),
|
||||
'required' => true,
|
||||
'autocomplete' => true,
|
||||
]);
|
||||
|
||||
@@ -20,6 +20,9 @@ use Symfony\Component\Validator\Constraints\Email;
|
||||
use Symfony\Component\Validator\Constraints\Length;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<null>
|
||||
*/
|
||||
class RegistrationFormType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
|
||||
@@ -16,6 +16,9 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<array<string, mixed>>
|
||||
*/
|
||||
class SendNotificationForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
@@ -33,6 +36,9 @@ class SendNotificationForm extends AbstractType
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function getTextChoices(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -15,6 +15,9 @@ use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<array<string, mixed>>
|
||||
*/
|
||||
class TimeForAMealForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
|
||||
@@ -16,6 +16,9 @@ use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<TodoItem>
|
||||
*/
|
||||
class TodoItemForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
|
||||
@@ -17,6 +17,9 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\UX\LiveComponent\Form\Type\LiveCollectionType;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<TodoList>
|
||||
*/
|
||||
class TodoListFormType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
|
||||
@@ -16,6 +16,9 @@ use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<array<string, mixed>>
|
||||
*/
|
||||
class TogglePasswordForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
|
||||
@@ -36,7 +36,7 @@ use Symfony\UX\TwigComponent\Attribute\PostMount;
|
||||
name: 'LiveMemory:Board',
|
||||
template: 'demos/live_memory/components/LiveMemory/Board.html.twig',
|
||||
)]
|
||||
class Board extends AbstractController
|
||||
final class Board extends AbstractController
|
||||
{
|
||||
use ComponentToolsTrait;
|
||||
use DefaultActionTrait;
|
||||
@@ -168,6 +168,9 @@ class Board extends AbstractController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, bool|string>>
|
||||
*/
|
||||
#[ExposeInTemplate('cards')]
|
||||
public function getCards(): array
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ use function Symfony\Component\String\u;
|
||||
name: 'LiveMemory:Icon',
|
||||
template: 'demos/live_memory/components/LiveMemory/Icon.html.twig',
|
||||
)]
|
||||
class Icon
|
||||
final class Icon
|
||||
{
|
||||
/**
|
||||
* Name of the icon file without extension (ex: `symfony-ux`).
|
||||
@@ -38,7 +38,7 @@ class Icon
|
||||
*/
|
||||
public ?string $label = null;
|
||||
|
||||
protected string $iconDirectory;
|
||||
private string $iconDirectory;
|
||||
|
||||
public function __construct(
|
||||
#[Autowire('%kernel.project_dir%')] string $projectDir,
|
||||
|
||||
@@ -23,7 +23,7 @@ use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
|
||||
name: 'LiveMemory:ScoreRow',
|
||||
template: 'demos/live_memory/components/LiveMemory/ScoreRow.html.twig',
|
||||
)]
|
||||
class ScoreRow
|
||||
final class ScoreRow
|
||||
{
|
||||
public string $label;
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ use Symfony\UX\TwigComponent\Attribute\PostMount;
|
||||
name: 'LiveMemory:Timer',
|
||||
template: 'demos/live_memory/components/LiveMemory/Timer.html.twig',
|
||||
)]
|
||||
class Timer
|
||||
final class Timer
|
||||
{
|
||||
use ComponentToolsTrait;
|
||||
use DefaultActionTrait;
|
||||
@@ -101,17 +101,17 @@ class Timer
|
||||
return $remainingTime <= ($this->warningThreshold * 1000);
|
||||
}
|
||||
|
||||
public function hydrateDate(?string $startedAt): ?\DateTimeImmutable
|
||||
public function hydrateDate(string $startedAt): \DateTimeImmutable
|
||||
{
|
||||
if (null === $startedAt || '' === $startedAt) {
|
||||
return null;
|
||||
if ('' === $startedAt) {
|
||||
return new \DateTimeImmutable();
|
||||
}
|
||||
|
||||
return \DateTimeImmutable::createFromFormat('U', $startedAt);
|
||||
}
|
||||
|
||||
public function dehydrateDate(?\DateTimeImmutable $startedAt): ?string
|
||||
public function dehydrateDate(\DateTimeImmutable $startedAt): string
|
||||
{
|
||||
return (string) $startedAt?->format('U');
|
||||
return (string) $startedAt->format('U');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class Game
|
||||
/**
|
||||
* Map of game card as <cardKey> => <cardValue>.
|
||||
*
|
||||
* @var list<int, string>
|
||||
* @var list<string>
|
||||
*/
|
||||
private readonly array $cards;
|
||||
|
||||
@@ -101,6 +101,9 @@ class Game
|
||||
return \count($this->cards);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{int, int}
|
||||
*/
|
||||
public function getGrid(): array
|
||||
{
|
||||
$cardCount = $this->getCardCount();
|
||||
@@ -111,6 +114,9 @@ class Game
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<int>
|
||||
*/
|
||||
public function getFlips(): array
|
||||
{
|
||||
return $this->flips;
|
||||
@@ -129,6 +135,9 @@ class Game
|
||||
$this->flips[] = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int>
|
||||
*/
|
||||
public function getMatches(): array
|
||||
{
|
||||
return $this->matches;
|
||||
@@ -150,6 +159,9 @@ class Game
|
||||
$this->matches[] = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<int>
|
||||
*/
|
||||
public function getCurrentPair(): array
|
||||
{
|
||||
$selectedPairs = $this->getSelectedPairs();
|
||||
@@ -157,6 +169,9 @@ class Game
|
||||
return array_pop($selectedPairs) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<list<int>>
|
||||
*/
|
||||
public function getSelectedPairs(): array
|
||||
{
|
||||
return array_chunk($this->getFlips(), 2);
|
||||
@@ -228,13 +243,10 @@ class Game
|
||||
|
||||
public function getTime(): int
|
||||
{
|
||||
if (null !== $start = $this->getCreatedAt()) {
|
||||
$end = ($this->getEndedAt() ?? new \DateTimeImmutable('now'));
|
||||
$start = $this->getCreatedAt();
|
||||
$end = ($this->getEndedAt() ?? new \DateTimeImmutable('now'));
|
||||
|
||||
return $end->getTimestamp() - $start->getTimestamp();
|
||||
}
|
||||
|
||||
return 5;
|
||||
return $end->getTimestamp() - $start->getTimestamp();
|
||||
}
|
||||
|
||||
public function getCreatedAt(): \DateTimeImmutable
|
||||
|
||||
@@ -23,7 +23,7 @@ final class GameCards
|
||||
*/
|
||||
public static function getCards(): array
|
||||
{
|
||||
return array_map(fn ($i) => \sprintf('%02d', $i), range(1, 16));
|
||||
return array_map(static fn ($i) => \sprintf('%02d', $i), range(1, 16));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,7 +35,7 @@ final class GameCards
|
||||
$cards = [...$cards, ...$cards];
|
||||
shuffle($cards);
|
||||
|
||||
return array_values($cards);
|
||||
return $cards;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,7 @@ final class GameLevels
|
||||
* nbCards = (level + 2) * 2
|
||||
* timeLimit = level * 20
|
||||
*
|
||||
* @var array<int, array{nbCards: int, theme: string, timeLimit: int, grid: string}>
|
||||
* @var array<int, array{0: int, 1: string, 2: int, 3: string}>
|
||||
*/
|
||||
private const LEVEL_METADATA = [
|
||||
1 => [6, 'blue', 20, '3x2'],
|
||||
|
||||
@@ -13,6 +13,9 @@ namespace App\Model;
|
||||
|
||||
class Demo
|
||||
{
|
||||
/**
|
||||
* @param list<string> $tags
|
||||
*/
|
||||
public function __construct(
|
||||
private string $identifier,
|
||||
private string $name,
|
||||
|
||||
@@ -29,6 +29,15 @@ class IconSet
|
||||
public const CATEGORY_ARCHIVE_UNMAINTAINED = 'Archive / Unmaintained';
|
||||
public const CATEGORY_UNCATEGORIZED = 'Uncategorized';
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $author
|
||||
* @param array<string, mixed> $license
|
||||
* @param list<string>|null $samples
|
||||
* @param array<string, int>|int|null $height
|
||||
* @param list<string>|null $tags
|
||||
* @param array<string, string>|null $suffixes
|
||||
* @param array<string, mixed>|null $categories
|
||||
*/
|
||||
public function __construct(
|
||||
private string $identifier,
|
||||
private string $name,
|
||||
@@ -63,11 +72,17 @@ class IconSet
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getAuthor(): array
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getLicense(): array
|
||||
{
|
||||
return $this->license;
|
||||
@@ -83,11 +98,17 @@ class IconSet
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
public function getSamples(): array
|
||||
/**
|
||||
* @return list<string>|null
|
||||
*/
|
||||
public function getSamples(): ?array
|
||||
{
|
||||
return $this->samples;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, int>|int|null
|
||||
*/
|
||||
public function getHeight(): array|int|null
|
||||
{
|
||||
return $this->height;
|
||||
@@ -103,6 +124,9 @@ class IconSet
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>|null
|
||||
*/
|
||||
public function getTags(): ?array
|
||||
{
|
||||
return $this->tags;
|
||||
@@ -113,11 +137,17 @@ class IconSet
|
||||
return $this->palette;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>|null
|
||||
*/
|
||||
public function getSuffixes(): ?array
|
||||
{
|
||||
return $this->suffixes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function getCategories(): ?array
|
||||
{
|
||||
return $this->categories;
|
||||
@@ -133,6 +163,9 @@ class IconSet
|
||||
return $this->isFavorite ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>|null
|
||||
*/
|
||||
public function getGithub(): ?array
|
||||
{
|
||||
$urls = [
|
||||
|
||||
@@ -13,6 +13,9 @@ namespace App\Model;
|
||||
|
||||
final class LiveDemo extends Demo
|
||||
{
|
||||
/**
|
||||
* @param list<string> $tags
|
||||
*/
|
||||
public function __construct(
|
||||
string $identifier,
|
||||
string $name,
|
||||
|
||||
@@ -13,6 +13,9 @@ namespace App\Model;
|
||||
|
||||
class RecipeFileTree
|
||||
{
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
*/
|
||||
private array $files = [];
|
||||
|
||||
public function __construct()
|
||||
@@ -45,11 +48,17 @@ class RecipeFileTree
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, mixed>>
|
||||
*/
|
||||
public function getFiles(): array
|
||||
{
|
||||
return $this->buildFileTree('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, mixed>>
|
||||
*/
|
||||
public function buildFileTree(string $targetDirectory): array
|
||||
{
|
||||
$files = [];
|
||||
|
||||
@@ -17,11 +17,6 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Category>
|
||||
*
|
||||
* @method Category|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method Category|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method Category[] findAll()
|
||||
* @method Category[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class CategoryRepository extends ServiceEntityRepository
|
||||
{
|
||||
|
||||
@@ -17,11 +17,6 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Chat>
|
||||
*
|
||||
* @method Chat|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method Chat|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method Chat[] findAll()
|
||||
* @method Chat[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class ChatRepository extends ServiceEntityRepository
|
||||
{
|
||||
|
||||
@@ -19,11 +19,6 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Food>
|
||||
*
|
||||
* @method Food|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method Food|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method Food[] findAll()
|
||||
* @method Food[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class FoodRepository extends ServiceEntityRepository
|
||||
{
|
||||
|
||||
@@ -17,11 +17,6 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<InvoiceItem>
|
||||
*
|
||||
* @method InvoiceItem|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method InvoiceItem|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method InvoiceItem[] findAll()
|
||||
* @method InvoiceItem[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class InvoiceItemRepository extends ServiceEntityRepository
|
||||
{
|
||||
|
||||
@@ -17,11 +17,6 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Invoice>
|
||||
*
|
||||
* @method Invoice|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method Invoice|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method Invoice[] findAll()
|
||||
* @method Invoice[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class InvoiceRepository extends ServiceEntityRepository
|
||||
{
|
||||
|
||||
@@ -17,11 +17,6 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Product>
|
||||
*
|
||||
* @method Product|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method Product|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method Product[] findAll()
|
||||
* @method Product[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class ProductRepository extends ServiceEntityRepository
|
||||
{
|
||||
|
||||
@@ -17,11 +17,6 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<TodoItem>
|
||||
*
|
||||
* @method TodoItem|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method TodoItem|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method TodoItem[] findAll()
|
||||
* @method TodoItem[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class TodoItemRepository extends ServiceEntityRepository
|
||||
{
|
||||
|
||||
@@ -17,11 +17,6 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<TodoList>
|
||||
*
|
||||
* @method TodoList|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method TodoList|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method TodoList[] findAll()
|
||||
* @method TodoList[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class TodoListRepository extends ServiceEntityRepository
|
||||
{
|
||||
|
||||
@@ -26,6 +26,9 @@ final class ChangelogProvider
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{id: int, name: string, version: string, date: string, body: string}>
|
||||
*/
|
||||
public function getChangelog(int $page = 1): array
|
||||
{
|
||||
$changelog = [];
|
||||
@@ -37,6 +40,9 @@ final class ChangelogProvider
|
||||
return $changelog;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{id: int, name: string, version: string, date: string, body: string}>
|
||||
*/
|
||||
private function getReleases(int $page = 1): array
|
||||
{
|
||||
return $this->cache->get('releases-symfony-ux-'.$page, function (CacheItemInterface $item) use ($page) {
|
||||
|
||||
@@ -17,15 +17,15 @@ use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use Symfony\UX\TwigComponent\ComponentRendererInterface;
|
||||
|
||||
final readonly class FencedCodeRenderer implements NodeRendererInterface
|
||||
final class FencedCodeRenderer implements NodeRendererInterface
|
||||
{
|
||||
public function __construct(
|
||||
private ComponentRendererInterface $componentRenderer,
|
||||
private readonly ComponentRendererInterface $componentRenderer,
|
||||
) {
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable|string|null
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): string
|
||||
{
|
||||
if (!$node instanceof FencedCode) {
|
||||
throw new \InvalidArgumentException('Block must be instance of '.FencedCode::class);
|
||||
|
||||
@@ -24,7 +24,6 @@ use League\CommonMark\Parser\MarkdownParserStateInterface;
|
||||
final class TabParser extends AbstractBlockContinueParser
|
||||
{
|
||||
private Tab $block;
|
||||
private bool $finished = false;
|
||||
|
||||
public function __construct(string $title)
|
||||
{
|
||||
|
||||
@@ -73,7 +73,7 @@ final class ToolkitPreviewParser extends AbstractBlockContinueParser
|
||||
return false;
|
||||
}
|
||||
|
||||
public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
|
||||
public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): BlockContinue
|
||||
{
|
||||
if ($cursor->isBlank()) {
|
||||
return BlockContinue::at($cursor);
|
||||
|
||||
@@ -13,6 +13,9 @@ namespace App\Service;
|
||||
|
||||
class DinoStatsService
|
||||
{
|
||||
/**
|
||||
* @var array<string, mixed>|null
|
||||
*/
|
||||
private ?array $rawData = null;
|
||||
|
||||
private const ALL_DINOS = 'all';
|
||||
@@ -32,6 +35,9 @@ class DinoStatsService
|
||||
'de3267',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public static function getAllTypes(): array
|
||||
{
|
||||
return [
|
||||
@@ -45,13 +51,18 @@ class DinoStatsService
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $types
|
||||
*
|
||||
* @return array{labels: list<string>, datasets: list<array<string, mixed>>}
|
||||
*/
|
||||
public function fetchData(int $start, int $end, array $types): array
|
||||
{
|
||||
$start = abs($start);
|
||||
$end = abs($end);
|
||||
|
||||
$steps = 10;
|
||||
$step = round(($start - $end) / $steps);
|
||||
$step = (int) round(($start - $end) / $steps);
|
||||
|
||||
$labels = [];
|
||||
for ($i = 0; $i < $steps; ++$i) {
|
||||
@@ -83,6 +94,9 @@ class DinoStatsService
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getRawData(): array
|
||||
{
|
||||
if (null === $this->rawData) {
|
||||
@@ -92,11 +106,14 @@ class DinoStatsService
|
||||
return $this->rawData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<int>
|
||||
*/
|
||||
private function getSpeciesCounts(int $start, int $steps, int $step, string $type): array
|
||||
{
|
||||
$counts = [];
|
||||
for ($i = 0; $i < $steps; ++$i) {
|
||||
$current = round($start - ($i * $step));
|
||||
$current = (int) round($start - ($i * $step));
|
||||
$counts[] = $this->countSpeciesAt($current, $type);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,14 @@ namespace App\Service;
|
||||
*/
|
||||
final class EmojiCollection implements \IteratorAggregate, \Countable
|
||||
{
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private array $emojis;
|
||||
|
||||
/**
|
||||
* @param list<string> $emojis
|
||||
*/
|
||||
public function __construct(array $emojis = [])
|
||||
{
|
||||
$this->emojis = $emojis ?: $this->loadEmojis();
|
||||
@@ -42,6 +48,9 @@ final class EmojiCollection implements \IteratorAggregate, \Countable
|
||||
return \count($this->emojis);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
private function loadEmojis(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -28,12 +28,11 @@ class IconSetRepository
|
||||
'bootstrap',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string, IconSet>
|
||||
*/
|
||||
private array $iconSets;
|
||||
|
||||
private array $terms = [
|
||||
'crypto',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private Iconify $iconify,
|
||||
) {
|
||||
@@ -77,6 +76,9 @@ class IconSetRepository
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
private static function createIconSet(string $identifier, array $data): IconSet
|
||||
{
|
||||
return new IconSet(
|
||||
|
||||
@@ -26,6 +26,9 @@ final class Iconify
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function search(string $query, ?string $prefix = null, int $limit = 32, ?int $start = null): array
|
||||
{
|
||||
return $this->http
|
||||
@@ -41,11 +44,17 @@ final class Iconify
|
||||
;
|
||||
}
|
||||
|
||||
public function collection(string $name): ?array
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function collection(string $name): array
|
||||
{
|
||||
return $this->collectionData($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function collectionStyles(string $prefix): array
|
||||
{
|
||||
$data = $this->collectionData($prefix);
|
||||
@@ -56,6 +65,9 @@ final class Iconify
|
||||
return $styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function collectionCategories(string $prefix): array
|
||||
{
|
||||
$data = $this->collectionData($prefix);
|
||||
@@ -74,6 +86,9 @@ final class Iconify
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function collectionData(string $prefix): array
|
||||
{
|
||||
return $this->cache->get('iconify-collection-'.$prefix, function (ItemInterface $item) use ($prefix) {
|
||||
@@ -87,6 +102,9 @@ final class Iconify
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function collectionIcons(string $prefix): array
|
||||
{
|
||||
$icons = [];
|
||||
@@ -105,6 +123,9 @@ final class Iconify
|
||||
return array_keys($icons);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function collections(): array
|
||||
{
|
||||
return $this->cache->get('iconify-collections', function (ItemInterface $item) {
|
||||
|
||||
@@ -29,7 +29,7 @@ class LiveDemoRepository
|
||||
publishedAt: '2024-06-07',
|
||||
tags: ['grid', 'pagination', 'loading', 'scroll'],
|
||||
longDescription: <<<EOF
|
||||
The second and final part of the **Infinite Scroll Serie**, with a new range of (lovely) T-Shirts!
|
||||
The second and final part of the **Infinite Scroll Series**, with a new range of (lovely) T-Shirts!
|
||||
Now with `automatic loading on scroll`, a new trick and amazing `loading animations`!
|
||||
EOF,
|
||||
),
|
||||
@@ -201,6 +201,9 @@ class LiveDemoRepository
|
||||
return current($demos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException if the demo is not found
|
||||
*/
|
||||
public function find(string $identifier): LiveDemo
|
||||
{
|
||||
$demos = $this->findAll();
|
||||
|
||||
@@ -23,6 +23,9 @@ class ShellLanguage extends BaseLanguage
|
||||
return 'shell';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getAliases(): array
|
||||
{
|
||||
return ['bash', 'sh'];
|
||||
|
||||
@@ -68,10 +68,7 @@ class ToolkitService
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{
|
||||
* props: array<array{name: string, type: string, description: string}>,
|
||||
* blocks: array<array{name: string, description: string}>
|
||||
* }
|
||||
* @return array<string, array{props: list<array{name: string, type: string, description: string}>, blocks: list<array{name: string, description: string}>}>
|
||||
*/
|
||||
public function extractRecipeApiReference(Recipe $recipe): array
|
||||
{
|
||||
|
||||
@@ -21,6 +21,9 @@ class UxPackageDataProvider
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getPackages(): array
|
||||
{
|
||||
$packages = $this->packageRepository->findAll();
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace App\Twig\Components;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent]
|
||||
class Alert
|
||||
final class Alert
|
||||
{
|
||||
public string $type = 'success';
|
||||
public string $message;
|
||||
@@ -24,6 +24,7 @@ class Alert
|
||||
return match ($this->type) {
|
||||
'success' => 'bi:check-circle',
|
||||
'danger' => 'bi:exclamation-circle',
|
||||
default => 'bi:info-circle',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ namespace App\Twig\Components\Badge;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
|
||||
|
||||
/**
|
||||
* @phpstan-ignore-next-line symfonyUX.twigComponent.classMustBeFinal
|
||||
*/
|
||||
#[AsTwigComponent(
|
||||
name: 'Badge',
|
||||
template: 'components/Badge.html.twig',
|
||||
@@ -29,6 +32,9 @@ class Badge
|
||||
|
||||
public string $url;
|
||||
|
||||
/**
|
||||
* @return array{icon: ?string, label: string, value: string, url: ?string}
|
||||
*/
|
||||
#[ExposeInTemplate(destruct: true)]
|
||||
public function getBadge(): array
|
||||
{
|
||||
@@ -40,22 +46,22 @@ class Badge
|
||||
];
|
||||
}
|
||||
|
||||
protected function getLabel(): string
|
||||
private function getLabel(): string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
protected function getValue(): string
|
||||
private function getValue(): string
|
||||
{
|
||||
return $this->value ?? '';
|
||||
}
|
||||
|
||||
protected function getIcon(): ?string
|
||||
private function getIcon(): string
|
||||
{
|
||||
return $this->icon;
|
||||
}
|
||||
|
||||
protected function getUrl(): ?string
|
||||
private function getUrl(): string
|
||||
{
|
||||
return $this->url ?? '';
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace App\Twig\Components;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent]
|
||||
class BootstrapModal
|
||||
final class BootstrapModal
|
||||
{
|
||||
public ?string $id = null;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,11 @@ namespace App\Twig\Components;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent('ChangelogItem')]
|
||||
class ChangelogItem
|
||||
final class ChangelogItem
|
||||
{
|
||||
/**
|
||||
* @var array{id: int, name?: string, version: string, date: string, body?: string}
|
||||
*/
|
||||
public array $item;
|
||||
|
||||
public bool $isOpen = false;
|
||||
|
||||
@@ -17,7 +17,7 @@ use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent('CodeBlock', template: 'components/Code/CodeBlock.html.twig')]
|
||||
class CodeBlock
|
||||
final class CodeBlock
|
||||
{
|
||||
public string $filename;
|
||||
public string $height = '300px';
|
||||
@@ -176,7 +176,7 @@ class CodeBlock
|
||||
};
|
||||
}
|
||||
|
||||
public function getElementId(): ?string
|
||||
public function getElementId(): string
|
||||
{
|
||||
return FilenameHelper::getElementId($this->filename);
|
||||
}
|
||||
@@ -186,6 +186,8 @@ class CodeBlock
|
||||
*
|
||||
* This allows us to inject some HTML (e.g. a <span> around use statements)
|
||||
* that will be kept raw / not highlighted.
|
||||
*
|
||||
* @return list<array{content: string, highlight: bool}>
|
||||
*/
|
||||
private function splitAndProcessSource(string $content): array
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace App\Twig\Components\Code;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent('CodeBlockInline', template: 'components/Code/CodeBlockInline.html.twig')]
|
||||
class CodeBlockInline
|
||||
final class CodeBlockInline
|
||||
{
|
||||
public string $code;
|
||||
public string $language;
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace App\Twig\Components\Code;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent('Code:CodeWithExplanationRow')]
|
||||
class CodeWithExplanationRow
|
||||
final class CodeWithExplanationRow
|
||||
{
|
||||
public string $filename;
|
||||
|
||||
|
||||
@@ -15,8 +15,11 @@ use App\Util\FilenameHelper;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent('Code:TabbedCodeBlocks')]
|
||||
class TabbedCodeBlocks
|
||||
final class TabbedCodeBlocks
|
||||
{
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public array $files = [];
|
||||
|
||||
public function getItemId(string $filename): string
|
||||
|
||||
@@ -16,7 +16,7 @@ use App\Service\LiveDemoRepository;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent('Demo:PrevNext')]
|
||||
class PrevNextDemo
|
||||
final class PrevNextDemo
|
||||
{
|
||||
public function __construct(
|
||||
private readonly LiveDemoRepository $demoRepository,
|
||||
|
||||
@@ -20,10 +20,11 @@ use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
|
||||
|
||||
#[AsLiveComponent]
|
||||
class DinoChart
|
||||
final class DinoChart
|
||||
{
|
||||
use DefaultActionTrait;
|
||||
|
||||
/** @var list<string> */
|
||||
#[LiveProp(writable: true)]
|
||||
public array $currentTypes = ['all', 'large theropod', 'small theropod'];
|
||||
|
||||
@@ -85,6 +86,9 @@ class DinoChart
|
||||
return $chart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
#[ExposeInTemplate]
|
||||
public function allTypes(): array
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
|
||||
|
||||
#[AsTwigComponent]
|
||||
class DocsLink
|
||||
final class DocsLink
|
||||
{
|
||||
public string $size = 'md';
|
||||
public string $url;
|
||||
|
||||
@@ -21,7 +21,7 @@ use Symfony\UX\LiveComponent\Attribute\LiveProp;
|
||||
use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||
|
||||
#[AsLiveComponent]
|
||||
class FoodVote extends AbstractController
|
||||
final class FoodVote extends AbstractController
|
||||
{
|
||||
use DefaultActionTrait;
|
||||
|
||||
@@ -36,7 +36,7 @@ class FoodVote extends AbstractController
|
||||
}
|
||||
|
||||
#[LiveAction]
|
||||
public function vote(#[LiveArg] string $direction)
|
||||
public function vote(#[LiveArg] string $direction): void
|
||||
{
|
||||
if ('up' === $direction) {
|
||||
$this->food->upVote();
|
||||
|
||||
@@ -18,17 +18,20 @@ use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
|
||||
|
||||
#[AsTwigComponent]
|
||||
class HomepageTerminalSwapper
|
||||
final class HomepageTerminalSwapper
|
||||
{
|
||||
public function __construct(private UxPackageRepository $packageRepository)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
#[ExposeInTemplate]
|
||||
public function getTypedStrings(): array
|
||||
{
|
||||
$strings = [];
|
||||
$packages = array_filter($this->packageRepository->findAll(), fn (UxPackage $p): bool => null !== $p->getCreateString());
|
||||
$packages = array_filter($this->packageRepository->findAll(), static fn (UxPackage $p): bool => null !== $p->getCreateString());
|
||||
|
||||
shuffle($packages);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||
use Symfony\UX\TwigComponent\Attribute\PostMount;
|
||||
|
||||
#[AsLiveComponent('Icon:IconModal')]
|
||||
class IconModal
|
||||
final class IconModal
|
||||
{
|
||||
use DefaultActionTrait;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ use Symfony\UX\LiveComponent\Attribute\LiveProp;
|
||||
use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||
|
||||
#[AsLiveComponent('Icon:IconSearch')]
|
||||
class IconSearch
|
||||
final class IconSearch
|
||||
{
|
||||
use DefaultActionTrait;
|
||||
|
||||
@@ -32,6 +32,7 @@ class IconSearch
|
||||
#[LiveProp(writable: true, url: true)]
|
||||
public ?string $set = null;
|
||||
|
||||
/** @var list<Icon> */
|
||||
private array $icons;
|
||||
|
||||
public function __construct(
|
||||
@@ -40,6 +41,9 @@ class IconSearch
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, string>>
|
||||
*/
|
||||
public function getIconSetOptionGroups(): array
|
||||
{
|
||||
$groups = [];
|
||||
@@ -66,6 +70,9 @@ class IconSearch
|
||||
return substr(md5(serialize([$this->query, $this->set])), 0, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Icon>
|
||||
*/
|
||||
public function icons(): array
|
||||
{
|
||||
return $this->icons ??= $this->searchIcons();
|
||||
@@ -76,6 +83,9 @@ class IconSearch
|
||||
return $this->set ? $this->iconSetRepository->get($this->set) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Icon>
|
||||
*/
|
||||
private function searchIcons(): array
|
||||
{
|
||||
if (!$this->query) {
|
||||
@@ -90,6 +100,6 @@ class IconSearch
|
||||
|
||||
$icons = $this->iconify->search($this->query, $this->set, self::PER_PAGE)['icons'];
|
||||
|
||||
return array_map(fn ($icon) => Icon::fromIdentifier($icon), $icons);
|
||||
return array_map(static fn ($icon) => Icon::fromIdentifier($icon), $icons);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use App\Model\Icon\IconSet;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent('Icon:IconSetCard')]
|
||||
class IconSetCard
|
||||
final class IconSetCard
|
||||
{
|
||||
public IconSet $iconSet;
|
||||
|
||||
@@ -62,6 +62,9 @@ class IconSetCard
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getSampleIcons(): array
|
||||
{
|
||||
return self::ICONSET_SAMPLES[$this->iconSet->getIdentifier()] ?? [];
|
||||
|
||||
@@ -22,7 +22,7 @@ use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||
use Symfony\UX\LiveComponent\ValidatableComponentTrait;
|
||||
|
||||
#[AsLiveComponent]
|
||||
class InlineEditFood extends AbstractController
|
||||
final class InlineEditFood extends AbstractController
|
||||
{
|
||||
use DefaultActionTrait;
|
||||
use ValidatableComponentTrait;
|
||||
@@ -46,13 +46,13 @@ class InlineEditFood extends AbstractController
|
||||
public ?string $flashMessage = null;
|
||||
|
||||
#[LiveAction]
|
||||
public function activateEditing()
|
||||
public function activateEditing(): void
|
||||
{
|
||||
$this->isEditing = true;
|
||||
}
|
||||
|
||||
#[LiveAction]
|
||||
public function save(EntityManagerInterface $entityManager)
|
||||
public function save(EntityManagerInterface $entityManager): void
|
||||
{
|
||||
// if validation fails, this throws an exception & the component re-renders
|
||||
$this->validate();
|
||||
|
||||
@@ -28,7 +28,7 @@ use Symfony\UX\LiveComponent\ValidatableComponentTrait;
|
||||
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
|
||||
|
||||
#[AsLiveComponent]
|
||||
class InvoiceCreator extends AbstractController
|
||||
final class InvoiceCreator extends AbstractController
|
||||
{
|
||||
use DefaultActionTrait;
|
||||
use ValidatableComponentTrait;
|
||||
@@ -37,6 +37,9 @@ class InvoiceCreator extends AbstractController
|
||||
#[Valid]
|
||||
public Invoice $invoice;
|
||||
|
||||
/**
|
||||
* @var list<array{productId: int|null, quantity: int, isEditing: bool}>
|
||||
*/
|
||||
#[LiveProp]
|
||||
public array $lineItems = [];
|
||||
|
||||
@@ -77,7 +80,7 @@ class InvoiceCreator extends AbstractController
|
||||
}
|
||||
|
||||
#[LiveListener('line_item:change_edit_mode')]
|
||||
public function onLineItemEditModeChange(#[LiveArg] int $key, #[LiveArg] $isEditing): void
|
||||
public function onLineItemEditModeChange(#[LiveArg] int $key, #[LiveArg] bool $isEditing): void
|
||||
{
|
||||
$this->lineItems[$key]['isEditing'] = $isEditing;
|
||||
}
|
||||
@@ -95,7 +98,7 @@ class InvoiceCreator extends AbstractController
|
||||
}
|
||||
|
||||
#[LiveAction]
|
||||
public function saveInvoice(EntityManagerInterface $entityManager)
|
||||
public function saveInvoice(EntityManagerInterface $entityManager): mixed
|
||||
{
|
||||
$this->saveFailed = true;
|
||||
$this->validate();
|
||||
@@ -145,6 +148,8 @@ class InvoiceCreator extends AbstractController
|
||||
// Keep the lineItems in sync with the invoice: new InvoiceItems may
|
||||
// not have been given the same key as the original lineItems
|
||||
$this->lineItems = $this->populateLineItems($this->invoice);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getSubtotal(): float
|
||||
@@ -183,6 +188,9 @@ class InvoiceCreator extends AbstractController
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{productId: int|null, quantity: int, isEditing: bool}>
|
||||
*/
|
||||
private function populateLineItems(Invoice $invoice): array
|
||||
{
|
||||
$lineItems = [];
|
||||
|
||||
@@ -23,7 +23,7 @@ use Symfony\UX\LiveComponent\ValidatableComponentTrait;
|
||||
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
|
||||
|
||||
#[AsLiveComponent]
|
||||
class InvoiceCreatorLineItem
|
||||
final class InvoiceCreatorLineItem
|
||||
{
|
||||
use DefaultActionTrait;
|
||||
use ValidatableComponentTrait;
|
||||
@@ -73,6 +73,9 @@ class InvoiceCreatorLineItem
|
||||
$this->changeEditMode(true, $responder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Product>
|
||||
*/
|
||||
#[ExposeInTemplate]
|
||||
public function getProducts(): array
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
namespace App\Twig\Components;
|
||||
|
||||
use App\Form\MealPlannerForm;
|
||||
use App\Model\MealPlan;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
|
||||
@@ -19,11 +20,14 @@ use Symfony\UX\LiveComponent\ComponentWithFormTrait;
|
||||
use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||
|
||||
#[AsLiveComponent]
|
||||
class MealPlanner extends AbstractController
|
||||
final class MealPlanner extends AbstractController
|
||||
{
|
||||
use ComponentWithFormTrait;
|
||||
use DefaultActionTrait;
|
||||
|
||||
/**
|
||||
* @return FormInterface<MealPlan>
|
||||
*/
|
||||
protected function instantiateForm(): FormInterface
|
||||
{
|
||||
return $this->createForm(MealPlannerForm::class);
|
||||
|
||||
@@ -23,7 +23,7 @@ use Symfony\UX\LiveComponent\LiveResponder;
|
||||
use Symfony\UX\LiveComponent\ValidatableComponentTrait;
|
||||
|
||||
#[AsLiveComponent]
|
||||
class NewCategoryForm
|
||||
final class NewCategoryForm
|
||||
{
|
||||
use ComponentToolsTrait;
|
||||
use DefaultActionTrait;
|
||||
|
||||
@@ -28,7 +28,7 @@ use Symfony\UX\LiveComponent\ValidatableComponentTrait;
|
||||
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
|
||||
|
||||
#[AsLiveComponent]
|
||||
class NewProductForm extends AbstractController
|
||||
final class NewProductForm extends AbstractController
|
||||
{
|
||||
use DefaultActionTrait;
|
||||
use ValidatableComponentTrait;
|
||||
@@ -48,6 +48,9 @@ class NewProductForm extends AbstractController
|
||||
#[NotBlank]
|
||||
public ?Category $category = null;
|
||||
|
||||
/**
|
||||
* @return list<Category>
|
||||
*/
|
||||
#[ExposeInTemplate]
|
||||
public function getCategories(): array
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ use App\Model\UxPackage;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent('PackageBox')]
|
||||
class PackageBox
|
||||
final class PackageBox
|
||||
{
|
||||
public UxPackage $package;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ use App\Service\UxPackageRepository;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent('PackageHeader', template: 'components/Package/PackageHeader.html.twig')]
|
||||
class PackageHeader
|
||||
final class PackageHeader
|
||||
{
|
||||
public UxPackage $package;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ use Symfony\UX\LiveComponent\Attribute\LiveProp;
|
||||
use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||
|
||||
#[AsLiveComponent('SearchPackages', template: 'components/Package/SearchPackages.html.twig')]
|
||||
class SearchPackages
|
||||
final class SearchPackages
|
||||
{
|
||||
use DefaultActionTrait;
|
||||
|
||||
@@ -28,6 +28,9 @@ class SearchPackages
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<\App\Model\UxPackage>
|
||||
*/
|
||||
public function getPackages(): array
|
||||
{
|
||||
return $this->packageRepo->findAll($this->query);
|
||||
|
||||
@@ -19,7 +19,7 @@ use Symfony\UX\LiveComponent\ComponentToolsTrait;
|
||||
use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||
|
||||
#[AsLiveComponent('ProductGrid')]
|
||||
class ProductGrid
|
||||
final class ProductGrid
|
||||
{
|
||||
use ComponentToolsTrait;
|
||||
use DefaultActionTrait;
|
||||
@@ -44,6 +44,9 @@ class ProductGrid
|
||||
return \count($this->emojis) > ($this->page * self::PER_PAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{id: int, emoji: string, color: string}>
|
||||
*/
|
||||
public function getItems(): array
|
||||
{
|
||||
$emojis = $this->emojis->paginate($this->page, self::PER_PAGE);
|
||||
@@ -61,6 +64,9 @@ class ProductGrid
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getColors(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -20,7 +20,7 @@ use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
|
||||
|
||||
#[AsLiveComponent('ProductGrid2')]
|
||||
class ProductGrid2
|
||||
final class ProductGrid2
|
||||
{
|
||||
use ComponentToolsTrait;
|
||||
use DefaultActionTrait;
|
||||
@@ -34,6 +34,9 @@ class ProductGrid2
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{id: int, emoji: string}>
|
||||
*/
|
||||
public function getItems(): array
|
||||
{
|
||||
$items = [];
|
||||
|
||||
@@ -21,7 +21,7 @@ use Symfony\UX\LiveComponent\ComponentWithFormTrait;
|
||||
use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||
|
||||
#[AsLiveComponent]
|
||||
class RegistrationForm extends AbstractController
|
||||
final class RegistrationForm extends AbstractController
|
||||
{
|
||||
use ComponentWithFormTrait;
|
||||
use DefaultActionTrait;
|
||||
@@ -31,6 +31,9 @@ class RegistrationForm extends AbstractController
|
||||
#[LiveProp]
|
||||
public ?string $newUserEmail = null;
|
||||
|
||||
/**
|
||||
* @phpstan-ignore missingType.generics
|
||||
*/
|
||||
protected function instantiateForm(): FormInterface
|
||||
{
|
||||
return $this->createForm(RegistrationFormType::class);
|
||||
@@ -42,7 +45,7 @@ class RegistrationForm extends AbstractController
|
||||
}
|
||||
|
||||
#[LiveAction]
|
||||
public function saveRegistration()
|
||||
public function saveRegistration(): void
|
||||
{
|
||||
$this->submitForm();
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ use App\Util\SourceCleaner;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent]
|
||||
class Terminal
|
||||
final class Terminal
|
||||
{
|
||||
public int $bottomPadding = 100;
|
||||
public string $height = 'auto';
|
||||
|
||||
@@ -21,7 +21,7 @@ use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||
use Symfony\UX\LiveComponent\LiveCollectionTrait;
|
||||
|
||||
#[AsLiveComponent]
|
||||
class TodoListForm extends AbstractController
|
||||
final class TodoListForm extends AbstractController
|
||||
{
|
||||
use DefaultActionTrait;
|
||||
use LiveCollectionTrait;
|
||||
@@ -29,6 +29,9 @@ class TodoListForm extends AbstractController
|
||||
#[LiveProp(fieldName: 'formData')]
|
||||
public ?TodoList $todoList;
|
||||
|
||||
/**
|
||||
* @return FormInterface<TodoList>
|
||||
*/
|
||||
protected function instantiateForm(): FormInterface
|
||||
{
|
||||
return $this->createForm(
|
||||
|
||||
@@ -18,16 +18,11 @@ use Symfony\UX\Toolkit\Recipe\Recipe;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
#[AsTwigComponent]
|
||||
class ComponentDoc
|
||||
final class ComponentDoc
|
||||
{
|
||||
public ToolkitKitId $kitId;
|
||||
public Recipe $component;
|
||||
|
||||
/**
|
||||
* @see https://regex101.com/r/8L2pPy/1
|
||||
*/
|
||||
private const RE_CODE_BLOCK = '/```(?P<language>[a-z]+?)\s*(?P<options>\{.+?\})?\n(?P<code>.+?)```/s';
|
||||
|
||||
public function __construct(
|
||||
private readonly ToolkitService $toolkitService,
|
||||
private readonly \Twig\Environment $twig,
|
||||
|
||||
@@ -22,7 +22,7 @@ use Symfony\UX\LiveComponent\Attribute\LiveProp;
|
||||
use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||
|
||||
#[AsLiveComponent]
|
||||
class UploadFiles
|
||||
final class UploadFiles
|
||||
{
|
||||
use DefaultActionTrait;
|
||||
|
||||
@@ -35,6 +35,7 @@ class UploadFiles
|
||||
#[LiveProp]
|
||||
public ?string $singleFileUploadError = null;
|
||||
|
||||
/** @var list<array{filename: string, size: int|false}> */
|
||||
#[LiveProp]
|
||||
public array $multipleUploadFilenames = [];
|
||||
|
||||
@@ -59,6 +60,9 @@ class UploadFiles
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0: string, 1: int|false}
|
||||
*/
|
||||
private function processFileUpload(UploadedFile $file): array
|
||||
{
|
||||
// in a real app, move this file somewhere
|
||||
@@ -82,6 +86,6 @@ class UploadFiles
|
||||
$this->singleFileUploadError = $errors->get(0)->getMessage();
|
||||
|
||||
// causes the component to re-render
|
||||
throw new UnprocessableEntityHttpException('Validation failed');
|
||||
throw new UnprocessableEntityHttpException('Validation failed.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ final class ToolkitRuntime implements RuntimeExtensionInterface
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function codeExample(string $kitId, string $recipeName, string $exampleName, array $options = [], bool $preview = true): string
|
||||
{
|
||||
$kitId = ToolkitKitId::from($kitId);
|
||||
@@ -76,11 +79,17 @@ final class ToolkitRuntime implements RuntimeExtensionInterface
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function codeDemo(string $kitId, string $recipeName, array $options = []): string
|
||||
{
|
||||
return $this->codeExample($kitId, $recipeName, 'Demo', $options + ['height' => '450px'], preview: true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function codeUsage(string $kitId, string $recipeName, array $options = []): string
|
||||
{
|
||||
return $this->codeExample($kitId, $recipeName, 'Usage', $options, preview: false);
|
||||
|
||||
@@ -37,7 +37,7 @@ class SourceCleaner
|
||||
|
||||
// Unindent all lines by 4 spaces
|
||||
$lines = explode("\n", $contents);
|
||||
$lines = array_map(function (string $line) {
|
||||
$lines = array_map(static function (string $line) {
|
||||
return substr($line, 4);
|
||||
}, $lines);
|
||||
$contents = u(implode("\n", $lines));
|
||||
@@ -50,7 +50,7 @@ class SourceCleaner
|
||||
{
|
||||
$lines = explode("\n", $content);
|
||||
|
||||
$lines = array_map(function (string $line) {
|
||||
$lines = array_map(static function (string $line) {
|
||||
$line = trim($line);
|
||||
|
||||
if (!$line) {
|
||||
@@ -129,7 +129,7 @@ class SourceCleaner
|
||||
}
|
||||
|
||||
// remove the minimum indentation from each line
|
||||
$blockLines = array_map(function (string $line) use ($leastIndentedLineCount) {
|
||||
$blockLines = array_map(static function (string $line) use ($leastIndentedLineCount) {
|
||||
return substr($line, $leastIndentedLineCount);
|
||||
}, $blockLines);
|
||||
|
||||
@@ -156,7 +156,7 @@ class SourceCleaner
|
||||
public static function removeExcessHtml(string $content): string
|
||||
{
|
||||
// remove all HTML attributes and values + whitespace around them
|
||||
$content = preg_replace_callback('/\s+[a-z0-9-]+="[^"]*"/', function ($matches) {
|
||||
$content = preg_replace_callback('/\s+[a-z0-9-]+="[^"]*"/', static function ($matches) {
|
||||
if (str_starts_with(trim($matches[0]), 'data-')) {
|
||||
return $matches[0];
|
||||
}
|
||||
@@ -167,12 +167,10 @@ class SourceCleaner
|
||||
// Find all the <div> elements without attributes
|
||||
preg_match_all('/<div>\s*(.*?)\s*<\/div>/s', $content, $matches);
|
||||
|
||||
if (isset($matches[1])) {
|
||||
// Loop through the found matches
|
||||
foreach ($matches[1] as $match) {
|
||||
// Replace the div tags without attributes with their content
|
||||
$content = preg_replace('/<div>\s*'.preg_quote($match, '/').'\s*<\/div>/s', $match, $content);
|
||||
}
|
||||
// Loop through the found matches
|
||||
foreach ($matches[1] as $match) {
|
||||
// Replace the div tags without attributes with their content
|
||||
$content = preg_replace('/<div>\s*'.preg_quote($match, '/').'\s*<\/div>/s', $match, $content);
|
||||
}
|
||||
|
||||
$lines = explode("\n", $content);
|
||||
|
||||
12
symfony.lock
12
symfony.lock
@@ -107,6 +107,18 @@
|
||||
"phar-io/version": {
|
||||
"version": "3.2.1"
|
||||
},
|
||||
"phpstan/phpstan": {
|
||||
"version": "2.1",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes-contrib",
|
||||
"branch": "main",
|
||||
"version": "1.0",
|
||||
"ref": "5e490cc197fb6bb1ae22e5abbc531ddc633b6767"
|
||||
},
|
||||
"files": [
|
||||
"phpstan.dist.neon"
|
||||
]
|
||||
},
|
||||
"phpunit/php-code-coverage": {
|
||||
"version": "9.2.15"
|
||||
},
|
||||
|
||||
@@ -25,6 +25,9 @@ class RedirectUrlTest extends WebTestCase
|
||||
$this->assertResponseRedirects($expectedUrl, $expectedStatusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{0: string, 1: string, 2: int}>
|
||||
*/
|
||||
protected static function getRedirectionTests(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -19,9 +19,14 @@ class ChangelogItemTest extends TestCase
|
||||
public function testSetItem()
|
||||
{
|
||||
$component = new ChangelogItem();
|
||||
$component->item = ['body' => 'foobar'];
|
||||
$component->item = [
|
||||
'id' => 1,
|
||||
'name' => 'Test',
|
||||
'version' => 'v1.0.0',
|
||||
'date' => '2024-01-01',
|
||||
'body' => 'foobar',
|
||||
];
|
||||
|
||||
$this->assertSame(['body' => 'foobar'], $component->item);
|
||||
$this->assertSame('foobar', $component->getContent());
|
||||
}
|
||||
|
||||
@@ -32,12 +37,19 @@ class ChangelogItemTest extends TestCase
|
||||
{
|
||||
$component = new ChangelogItem();
|
||||
$component->item = [
|
||||
'id' => 1,
|
||||
'name' => 'Test',
|
||||
'version' => 'v1.0.0',
|
||||
'date' => '2024-01-01',
|
||||
'body' => $body,
|
||||
];
|
||||
|
||||
$this->assertSame($expected, $component->getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return iterable<string, array{0: string, 1: string}>
|
||||
*/
|
||||
public static function provideContentValues(): iterable
|
||||
{
|
||||
yield 'keep_existing_h1' => [
|
||||
|
||||
@@ -13,9 +13,7 @@ use Symfony\Component\Dotenv\Dotenv;
|
||||
|
||||
require dirname(__DIR__).'/vendor/autoload.php';
|
||||
|
||||
if (method_exists(Dotenv::class, 'bootEnv')) {
|
||||
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
|
||||
}
|
||||
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
|
||||
|
||||
if ($_SERVER['APP_DEBUG']) {
|
||||
umask(0o000);
|
||||
|
||||
26
tools/phpstan/console-application.php
Normal file
26
tools/phpstan/console-application.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is used by PHPStan, see https://github.com/phpstan/phpstan-symfony#console-command-analysis.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Kernel;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
|
||||
new Dotenv()->bootEnv(__DIR__ . '/../../.env');
|
||||
|
||||
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
|
||||
|
||||
return new Application($kernel);
|
||||
30
tools/phpstan/object-manager.php
Normal file
30
tools/phpstan/object-manager.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is used by PHPStan, see https://github.com/phpstan/phpstan-doctrine
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Kernel;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
|
||||
new Dotenv()->bootEnv(__DIR__ . '/../../.env');
|
||||
|
||||
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
|
||||
$kernel->boot();
|
||||
|
||||
/** @var EntityManager $entityManager */
|
||||
$entityManager = $kernel->getContainer()->get('doctrine')->getManager();
|
||||
|
||||
return $entityManager;
|
||||
39
tools/phpstan/symfony-configuration.php
Normal file
39
tools/phpstan/symfony-configuration.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is used by PHPStan, see https://github.com/phpstan/phpstan-symfony#console-command-analysis.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__.'/../../vendor/autoload.php';
|
||||
|
||||
$env = getenv('APP_ENV') ?: 'test';
|
||||
|
||||
$xmlContainerFile = __DIR__.sprintf('/../../var/cache/%s/App_Kernel%sDebugContainer.xml', $env, ucfirst($env));
|
||||
|
||||
if (!file_exists($xmlContainerFile)) {
|
||||
throw new RuntimeException(sprintf(<<<ERROR
|
||||
PHPStan depends on the meta information the Symfony Dependency Injection that the compiler pass writes.
|
||||
The meta xml file could not be found: %s.
|
||||
|
||||
To compile the Symfony container do a cache:clear in the current env (%s) with debug: true!
|
||||
ERROR, $xmlContainerFile, $env));
|
||||
}
|
||||
|
||||
return [
|
||||
'parameters' => [
|
||||
'symfony' => [
|
||||
'containerXmlPath' => $xmlContainerFile,
|
||||
],
|
||||
],
|
||||
];
|
||||
Reference in New Issue
Block a user