The Symfony PHP framework
MIT License
28134
1145
9030

Symfony version(s) affected

6.2

Description

hi all,

i have a problem with the iban validation constraint. it fails to validate an iban from Togo with the following format: TG53TG0090604310346500400070

How to reproduce

try to validate this iban: TG53TG0090604310346500400070 (I have a similar iban and I got this from here https://ibanapi.com/country/TG)

Possible Solution

No response

Additional Context

No response

Description
Currently, when creating a new Symfony command and using the choice question, like this :

return $io->choice($question, $choices, $default);

It returns the value. But I want the key ! It could be great to give the choice to send key or value with a booleen parameter, like this :

return $io->choice($question, $choices, $default, false);

Example

$choices = [
            0 => 'red',
            1 => 'blue',
            2 => 'orange'
        ];

        return $io->choice('Choose a color', $choices, 0); // Returns the value (red, blur or orange)
        return $io->choice('Choose a color', $choices, 0, false); // Returns the key (0, 1 or 2)

Description

Currently, all form themes are located into symfony/twig-bridge. This has some major drawbacks:

  • Download all themes when you only want to use one
  • No autoconfiguration from Flex
  • Impossible to create theme specific FormType
  • Testing is complicated

Example

I propose to extract all form themes except form_div_layout.html.twig and form_table_layout.html.twig into the symfony/ux repository or a dedicated one (symfony/form-theme, symfony/ui ?) and split each themes into individual packages

  • symfony/ux-theme-bootstrap
  • symfony/ux-theme-tailwind
  • symfony/ux-theme-foundation
  • etc ...

Here is an exaustive list of benefits that this could bring:

  • Require only the desire form theme (composer req symfony/ux-theme-bootstrap)
  • Recipes from Flex (autoconfiguring twig.form_themes or webpack.config.js, update packages.json etc ...)
  • Custom FormType (SwitchType for Bootstrap)
  • Simplify maintenance (split tests which are a mess, see #49775)
  • New form themes from the community

Description

Current state

I recently create a bundle which provide a form theme for Flowbite and when came time to set up tests, I digged into Symfony internal to understand how form theme testing is done.

And ho boy! What a mess 😱!

  1. A lot of code is duplicate internaly across all theme testing classes.
  2. It's not possible for external packages to benefits from thoses classes because they are internal and located in the Tests folder
  3. I had to duplicate a lot of code portions in my turn

For example, take a look to the setUp method of all thoses classes:

This setUp part can be definitely improve! The same goes for all render* methods.

Example

I propose to introduce a new abstract class Symfony\Component\Form\Test\FormLayoutTestCase which will extend from Symfony\Component\Form\Test\FormIntegrationTestCase:

abstract class FormLayoutTestCase extends FormIntegrationTestCase
{
    use RuntimeLoaderProvider;

    protected FormRendererInterface $renderer;

    protected function setUp(): void
    {
        parent::setUp();

        $loader = new FilesystemLoader($this->getTemplatePaths());

        $environment = new Environment($loader, ['strict_variables' => true]);
        $environment->setExtensions($this->getTwigExtensions());

        foreach ($this->getTwigGlobals() as [$name, $value]) {
            $environment->addGlobal($name, $value);
        }

        $rendererEngine = new TwigRendererEngine($this->getThemes(), $environment);
        $this->renderer = new FormRenderer($rendererEngine, $this->createMock(CsrfTokenManagerInterface::class));
        $this->registerTwigRuntimeLoader($environment, $this->renderer);
    }

    protected function assertMatchesXpath(string $html, string $expression, int $count = 1): void
    {
        $dom = new \DOMDocument('UTF-8');
        try {
            $dom->loadXML('<root>'.$html.'</root>');
        } catch (\Exception $e) {
            $this->fail(sprintf(
                "Failed loading HTML:\n\n%s\n\nError: %s",
                $html,
                $e->getMessage()
            ));
        }
        $xpath = new \DOMXPath($dom);
        $nodeList = $xpath->evaluate('/root'.$expression);

        if ($nodeList->length != $count) {
            $dom->formatOutput = true;
            $this->fail(sprintf(
                "Failed asserting that \n\n%s\n\nmatches exactly %s. Matches %s in \n\n%s",
                $expression,
                1 == $count ? 'once' : $count.' times',
                1 == $nodeList->length ? 'once' : $nodeList->length.' times',
                // strip away <root> and </root>
                substr($dom->saveHTML(), 6, -8)
            ));
        } else {
            $this->addToAssertionCount(1);
        }
    }

    abstract protected function getTemplatePaths(): array;

    abstract protected function getTwigExtensions(): array;

    protected function getTwigGlobals(): array
    {
        return [];
    }

    abstract protected function getThemes(): array;

    protected function renderForm(FormView $view, array $vars = []): string
    {
        return $this->renderer->renderBlock($view, 'form', $vars);
    }

    protected function renderLabel(FormView $view, $label = null, array $vars = []): string
    {
        if (null !== $label) {
            $vars += ['label' => $label];
        }

        return $this->renderer->searchAndRenderBlock($view, 'label', $vars);
    }

    protected function renderHelp(FormView $view): string
    {
        return $this->renderer->searchAndRenderBlock($view, 'help');
    }

    protected function renderErrors(FormView $view): string
    {
        return $this->renderer->searchAndRenderBlock($view, 'errors');
    }

    protected function renderWidget(FormView $view, array $vars = []): string
    {
        return $this->renderer->searchAndRenderBlock($view, 'widget', $vars);
    }

    protected function renderRow(FormView $view, array $vars = []): string
    {
        return $this->renderer->searchAndRenderBlock($view, 'row', $vars);
    }

    protected function renderRest(FormView $view, array $vars = []): string
    {
        return $this->renderer->searchAndRenderBlock($view, 'rest', $vars);
    }

    protected function renderStart(FormView $view, array $vars = []): string
    {
        return $this->renderer->renderBlock($view, 'form_start', $vars);
    }

    protected function renderEnd(FormView $view, array $vars = []): string
    {
        return $this->renderer->renderBlock($view, 'form_end', $vars);
    }

    protected function setTheme(FormView $view, array $themes, $useDefaultThemes = true): void
    {
        $this->renderer->setTheme($view, $themes, $useDefaultThemes);
    }
}

With this new class, external packages will benefit from a lighter configuration and internal classes will be more concise.

final class FlowbiteLayoutTest extends FormLayoutTestCase
{
    protected function getTemplatePaths(): array
    {
        return [
            __DIR__.'/../vendor/symfony/twig-bridge/Resources/views/Form',
            __DIR__.'/../templates/form',
        ];
    }

    protected function getTwigExtensions(): array
    {
        return [
            new TranslationExtension(new StubTranslator()),
            new FormExtension(),
        ];
    }

    protected function getThemes(): array
    {
        return [
            'default.html.twig',
        ];
    }

   // tests
}

Symfony version(s) affected

6.2

Description

For a project i need to run 2 container for the same project with different environment.

But even if the .env is different in both container. The $_ENV in symfony is the same when it come from symfony console .
All work as except in http.

How to reproduce

declare 2 service of the same symfony app in the docker-compose with different volume
first
volumes:
- ./:/var/www/html:rw,cached
second
volumes:
- ./:/var/www/html:rw,cached
- ./.env.1:/var/www/html/.env:rw,cached
- ./.var_site:/var/www/html/var

This make a different .env in each container and a different var folder for both.

When i look the file in the container the .env file is not the same

But when dump $_ENV in symfony by entering the application by console i still get the first one in both container.
If you run in http all work as except.

Possible Solution

No response

Additional Context

No response

Description

With the depreciation of ExtraBundle on object security, I can't have several attributes

Example in my use I check that the user has the right role and also the right company

#[Security('is_granted("ROLE_TECHNICIEN"), message: "Vous devez etre un tech")]
#[Security('client.getSociete() == societeService.getSociete()', message: "Ce client qui n'est pas dans votre societe.")]

The main problem is that it cannot be multiple compared to the old one.

The workaround and to have a generic message.
Why not allow this attribute multiple times?

Example

No response

Symfony version(s) affected

3.4.18

Description

Hi everybody. Hoping you can help me.

For security reasons, I deactivated the profiler completely from my yml configuration file.
image

Unfortunately, since I made this modification, my application has become very slow.

I added traces in my controller by noting execution times, with or without profiler.

For example for the construction of the form, I am at 738 ms with the profiler and 6616 ms without the profiler.

I thank you in advance.
Best regards

How to reproduce

by activating or deactivating the profiler

Possible Solution

No response

Additional Context

No response

Symfony version(s) affected

5.4.* and 6.2.*

Description

Because IpUtils::checkIp4 and checkIp6 generate the same cache key, an IpV4 address is getting reported as invalid when it is first checked via the checkIp6 method and then with the checkIp4 method again (same applies for v6 getting checked by v4 method).

How to reproduce

use Symfony\Component\HttpFoundation\IpUtils;

$requestIp = '127.0.0.1';
$subnet = '127.0.0.1/8';

$isV4 = IpUtils::checkIp4($requestIp, $subnet);
$isV6 = IpUtils::checkIp6($requestIp, $subnet);

var_dump([
    'isV4' => $isV4, // true
    'isV6' => $isV6, // true
]);

but when called in reverse:

use Symfony\Component\HttpFoundation\IpUtils;

$requestIp = '127.0.0.1';
$subnet = '127.0.0.1/8';

$isV6 = IpUtils::checkIp6($requestIp, $subnet);
$isV4 = IpUtils::checkIp4($requestIp, $subnet);

var_dump([
    'isV4' => $isV4, // false
    'isV6' => $isV6, // false
]);

Possible Solution

The simplest solution is to change how the cache key is computed and add a "-v4" and "-v6" suffix.

public static function checkIp4(string $requestIp, string $ip): bool
{
$cacheKey = $requestIp.'-'.$ip;
if (isset(self::$checkedIps[$cacheKey])) {
return self::$checkedIps[$cacheKey];
}

public static function checkIp6(string $requestIp, string $ip): bool
{
$cacheKey = $requestIp.'-'.$ip;
if (isset(self::$checkedIps[$cacheKey])) {
return self::$checkedIps[$cacheKey];
}

Additional Context

This problem does not occur when the user uses the generic IpUtils::checkIp method. This is because it only checks v4 and v6 with their respective methods.

If someone can confirm that I did not make a mistake and this is a real bug, I will submit a PR ASAP. Please confirm which branch the PR should be based from.

Symfony version(s) affected

6.2.x

Description

Once a Symfony console app gets custom completion it's no longer possible to use default file system completions for redirects. Note that this does not duplicate #43607 because completion for redirects should never be processed by Symfony.

How to reproduce

bin/console CMD > # tab, tab

Possible Solution

We can use the same approach as in Systemd project.
systemd/systemd#12089

Remaining tasks

  • Fix Bash completion - #49762
  • Fix ZSH completion (native support)
  • Fix Fish completion (native support)

Description

Hi,

in #49518 i realized we have 2 ways of handling null as a controller result

  1. json encode as null + http 200
  2. no content + http 204

i figured having a controller utility builtin creates better autocompletion due the use of $this->... already

considering #49518, this allows doing:

#[Serialize(201)]
endpoint(): mixed {
    if ($someSpecialCase) {
        return $this->noContent(); // 204
    }

    return $someOtherSpecialCase ? ['data' => true] : null; // 201
}

Example

No response

Since #46279, we add the "container.exclude" to auto-discovered classes that are excluded by manual config, but we forgot to add it to classes excluded by those attributes.

Help Wanted 🙏

Symfony version(s) affected: 5.4-dev

Description
See #43594 (comment) for details.

How to reproduce

When trying to autocomplete

bin/console lint:twig |

the argument is a file path, it should be completed by the native filesystem method.

Possible Solution

Described by @wouterj
#43594 (comment)

Additional context

Symfony must prevent native filesystem completion, it cannot implicit (like "nothing suggested by Symfony"), see #43594 (comment) for reasons.

Symfony version(s) affected

6.1

Description

Monolog 3.2 has made the Logger class final (see Seldaek/monolog@3cba75e ). This might lead to deprecation notices when initializing the Symfony logger

How to reproduce

Manually add Monolog ^3.2 to your composer dependencies
Initialize the Symfony logger in your code, e.g. a CLI command. When the logger is initalized for the first time (with a clean cache), you will get an error message that looks like this:

User Deprecated: The "Monolog\Logger" class is considered final. It may change without further notice as of its next major version You should not extend it from "Symfony\Bridge\Monolog\Logger". {"exception":"[object] (ErrorException(code: 0): User Deprecated: The "Monolog\Logger" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Bridge\Monolog\Logger". at /var/www/vendor/symfony/error-handler/DebugClassLoader.php:330)"} []

Possible Solution

A short-term fix would be to restrict the possible versions of Monolog to any version below 3.2 in the composer.json file of monolog-bridge

The long-term fix would be to change Symfony\Bridge\Monolog\Logger as a facade for a Monolog\Logger instance, possibly wrapping a logger and implementing the Psr\Log\LoggerInterface and Monolog\ResettableInterface for backwards compatibility.

Additional Context

No response

Description

The dd() helper is really useful for us developers
However, every time i am debugging a json api call, or when i do a view-source: or curl
I have to replace my dd() with var_dump() + die(), because the output starts with a bunch of static javascript.

Example

A simple solution to this is begin the output with a html comment like this:

<!-- var_dump($data) -->

This way the dumped data is always in a developer friendly version at the top of the file.

Screenshot of the "issue" that im trying to solve :)

image

Here is the short version of the code:

        $contents = scandir($directory_location);
        while(count($contents) > 0) {
            $elem = array_shift($contents);
            dump($elem); //<-- this line
        }

It would appear the dump line is causing the issue. If I comment the dump() line then there is no problem.

Is there a certain kind of string that would cause this problem? Or is there a max limit on dumps?

uzwprfk

Symfony version(s) affected

6.2.6

Description

This is a follow up of #41771, which already reported the problem. The solution offered there was to use an alternative runtime template but I think the Symfony one should work out of the box with PHARs.

Related Box issues:

Bundling a standard Symfony app into a PHAR using the default runtime component results in:

PHP Warning:  Constant  already defined in /home/imper/projects/phartest/build/app.phar on line 16
PHP Fatal error:  Uncaught TypeError: Invalid return value: callable object expected, "int" returned from "./build/app.phar". in phar:///home/imper/projects/phartest/build/app.phar/vendor/autoload_runtime.php:12
Stack trace:
#0 phar:///home/imper/projects/phartest/build/app.phar/bin/console(10): require_once()
#1 /home/imper/projects/phartest/build/app.phar(14): require('...')
#2 {main}
  thrown in phar:///home/imper/projects/phartest/build/app.phar/vendor/autoload_runtime.php on line 12

How to reproduce

Checkout box-project/box#886 and execute make e2e_symfony_runtime.

Alternatively:

  • Create a brand new standard Symfony app
  • Get your hand on a Box binary
  • Create the following Box config:
{
    "files-bin": [
        ".env.local.php",
        "src/Controller/.gitignore"
    ],
    "directories": [
        "config",
        "var"
    ],
    "force-autodiscovery": true,
    "check-requirements": false,
    "exclude-composer-files": false
}

The TL:DR; from this config is: nothing really fancy, include somehow the Symfony files that cannot be inferred by Composer.

And then, run:

$ box compile
$ ./bin/console.phar

Possible Solution

The solution suggested in #41771 is in my opinion not satisfactory. Indeed:

  • this requires a number of non-trivial steps in order to make it work
  • creating a new runtime template which actually no longer really fits the runtime component. As a consequence you do end up with something working for the PHAR, but outside the PHAR, you console script is no longer working with the runtime context. So you might as well just ditch the runtime component there IMO, it is simpler to fallback to the old console script.

I have checked the script and the issue is that somehow:

// autoload_runtime.php
$app = require $_SERVER['SCRIPT_FILENAME'];

ends up with $app = 1, but I just don't get why, it requires the file correctly and the return value should be the callable (even when doing step by step debugging, you end up doing the right path). It's as if the callable was suddenly magically mapped to 1 in the context of the PHAR.

Additional Context

No response

Symfony version(s) affected

4.4.40

Description

Since last symfony/messenger update, facing MySql error when starting consuming messenger queues.

In DoctrineReceiver.php line 63:
                                                                               
  An exception occurred while executing 'DELETE FROM messenger_messages WHERE delivered_at = ?' with params ["9999-12-31"]:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'messenger_messages' doesn't exist                                               
                                                                               
In AbstractMySQLDriver.php line 61:
                                                                               
An exception occurred while executing 'DELETE FROM messenger_messages WHERE delivered_at = ?' with params ["9999-12-31"]:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'messenger_messages' doesn't exist                                               
                                                                               
In Exception.php line 18: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'messenger_messages' doesn't exist                                               
                                                                               

In PDOStatement.php line 117: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'messenger_messages' doesn't exist

According the documentation, the table messenger_messages is automatically created when the transport is used. In my case, I use this transport only for failure messages.

This bug

How to reproduce

Configure a doctrine transport only for failure messages and a other one (redis, amqp..) for success ones. Start consuming your messages with supervisor.

Possible Solution

In the Symfony\Component\Messenger\Transport\Doctrine\Connection class, for each operation on doctrine table, check this table is created : check table messenger_messages;

Additional Context

No response

Symfony version(s) affected

6.1.0

Description

I have two files for my two locales:

header+intl-icu.en.xlf
header+intl-icu.uk.xlf

They were extracted by these commands:

php bin/console translation:extract --force en --clean
php bin/console translation:extract --force uk --clean

All content of these files translated at crowdin. When i pull translations from the crowdin the header+intl-icu.uk.xlf with UK locale updated and all OK:

label.login

Увійти

But header+intl-icu.en.xlf file with EN locale just modified but source and target was the same like before:

label.login

__label.login

And I start to dig in...

Crowdin translations managed by

crowdin-translation-provider/CrowdinProvider.php

When I try to pull translations by the command

php bin/console translation:pull crowdin --force --intl-icu

This flow managed by the function in CrowdinProvider.php:

public function read(array $domains, array $locales): TranslatorBag

There is a peace of code that executes changing for translation files for different locales:

foreach ($locales as $locale) {
if ($locale !== $this->defaultLocale) {
$response = $this->exportProjectTranslations($locale, $fileId);
} else {
$response = $this->downloadSourceFile($fileId);
}

  $responses[] = [$response, $locale, $domain];

}

So in my case if locale === uk used function

$this->exportProjectTranslations($locale, $fileId)

And if locale === en function

$this->downloadSourceFile($fileId);

If I remove this check for the defaultLocale locale all my translations from the crowdin start to work fine. And I don't know what to do next. Looks like it is a native behavior of the crowdin-translation-provider. But for me, it looks like a bug.
Thanks for any support or kick in direction of what to do with it. I am stuck.

How to reproduce

Configuration:

framework:
default_locale: '%app.default_locale%'
set_locale_from_accept_language: true
set_content_language_from_locale: true
enabled_locales: '%app.locales_array%'
translator:
default_path: '%kernel.project_dir%/translations'
fallbacks:
- '%app.default_locale%'
providers:
crowdin:
dsn: '%env(CROWDIN_DSN)%'
locales: '%app.locales_array%'
domains: ['header', 'REGISTRATION_AND_LOGIN', 'RESET_PASSWORD', 'USER_ACCOUNT']

parameters:
app.locales_array: ['en', 'uk']
app.supported_locales: 'en|uk'
app.default_locale: 'en'
project_name: '%env(PROJECT_NAME)%'
no_avatar_image: 'build/images_dummy/no_avatar.png'

Exctract translations:

php bin/console translation:extract --force en --clean
php bin/console translation:extract --force uk --clean

Export to the crowdin:

php bin/console translation:push crowdin --force

Translate it and pull back to the project:

php bin/console translation:pull crowdin --force --intl-icu

Look in translations folder: file with default locale (EN) the same. The UK locale file OK.

Possible Solution

Removing check for defaultLocale at CrowdinProvider.php in the read function.

Additional Context

No response

Description

I am currently working on a form theme for Flowbite and I would like to know if you are interested in integrating it as an official theme in Symfony.

WDYT?

Description

https://github.com/scrapy/cssselect (the python code that was ported to create the CssSelector component) has added support for them in their 1.1.0 release.

Example

No response