diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ccb6546..94659d9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -45,7 +45,7 @@ jobs: SYMFONY_REQUIRE: ${{ matrix.symfony }} uses: ramsey/composer-install@v2 - name: Run test suite on PHP ${{ matrix.php }} and Symfony ${{ matrix.symfony }} - run: vendor/bin/simple-phpunit + run: vendor/bin/phpunit - name: Run ECS run: vendor/bin/ecs - name: Run PHPStan diff --git a/Makefile b/Makefile index 8533e96..c136be8 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,6 @@ phpstan: ## PHP Unit tests phpunit: - vendor/bin/simple-phpunit + vendor/bin/phpunit diff --git a/composer.json b/composer.json index ed105e4..0abade2 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,6 @@ "symfony/translation": "^6.4|^7.0", "symfony/form": "^6.4|^7.0", "symfony/stopwatch": "^6.4|^7.0", - "symfony/test-pack": "^1.1.0", "symfony/orm-pack": "^2.4.1" }, "require-dev": { @@ -38,7 +37,10 @@ "doctrine/doctrine-bundle": "^2.5.5", "whatwedo/php-coding-standard": "^1.0", "symfony/twig-bundle": "^6.4|^7.0", - "phpstan/phpstan": "^1.7" + "phpstan/phpstan": "^1.7", + "phpunit/phpunit": "^10", + "symfony/test-pack": "^1.0", + "slevomat/coding-standard": "8.22.1" }, "autoload": { "psr-4": { diff --git a/phpstan.neon b/phpstan.neon index 5f14b09..63aadba 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,5 +3,3 @@ parameters: paths: - src - tests - bootstrapFiles: - - vendor/bin/.phpunit/phpunit-9.6-0/vendor/autoload.php diff --git a/src/Formatter/AbstractFormatter.php b/src/Formatter/AbstractFormatter.php index eda7949..3acc133 100644 --- a/src/Formatter/AbstractFormatter.php +++ b/src/Formatter/AbstractFormatter.php @@ -8,6 +8,8 @@ abstract class AbstractFormatter implements FormatterInterface { + public const OPT_HTML_SAFE = 'html_safe'; + /** * @var array */ @@ -34,7 +36,18 @@ public function processOptions(array $options = []): void $this->options = $resolver->resolve($options); } + public function isHtmlSafe(): bool + { + return (bool) ($this->options[self::OPT_HTML_SAFE] ?? false); + } + + protected function escapeHTML(string $value): string + { + return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); + } + protected function configureOptions(OptionsResolver $resolver): void { + $resolver->setDefault(self::OPT_HTML_SAFE, false); } } diff --git a/src/Formatter/BadgeFormatter.php b/src/Formatter/BadgeFormatter.php index 0a187ca..e852538 100644 --- a/src/Formatter/BadgeFormatter.php +++ b/src/Formatter/BadgeFormatter.php @@ -26,7 +26,7 @@ public function getHtml(mixed $value): string { $this->processOptions(($this->options[self::OPT_CONFIGURATION])($value, $this->options)); return $this->twig->render($this->options[self::OPT_TEMPLATE], [ - 'value' => $this->getString($value), + 'value' => $this->escapeHTML($this->getString($value)), 'type' => $this->options[self::OPT_TYPE], 'background_color_class' => $this->options[self::OPT_BACKGROUND_COLOR_CLASS], 'background_color_hex' => $this->options[self::OPT_BACKGROUND_COLOR_HEX], @@ -43,6 +43,7 @@ protected function configureOptions(OptionsResolver $resolver): void self::OPT_TYPE => 'neutral', self::OPT_LINK => null, self::OPT_CONFIGURATION => static fn (mixed $value, array $options): array => $options, + self::OPT_HTML_SAFE => true, ]); $resolver->setAllowedTypes(self::OPT_BACKGROUND_COLOR_CLASS, ['string', 'null']); $resolver->setAllowedTypes(self::OPT_BACKGROUND_COLOR_HEX, ['string', 'null']); diff --git a/src/Formatter/CollectionFormatter.php b/src/Formatter/CollectionFormatter.php index 325c39b..10f2a8f 100644 --- a/src/Formatter/CollectionFormatter.php +++ b/src/Formatter/CollectionFormatter.php @@ -5,6 +5,7 @@ namespace araise\CoreBundle\Formatter; use Doctrine\Common\Collections\Collection; +use Symfony\Component\OptionsResolver\OptionsResolver; class CollectionFormatter extends AbstractFormatter { @@ -29,9 +30,15 @@ public function getHtml(mixed $value): string } $str = ''; } + + protected function configureOptions(OptionsResolver $resolver): void + { + parent::configureOptions($resolver); + $resolver->setDefault(self::OPT_HTML_SAFE, true); + } } diff --git a/src/Formatter/EmailFormatter.php b/src/Formatter/EmailFormatter.php index 6fc1a36..06ccef8 100644 --- a/src/Formatter/EmailFormatter.php +++ b/src/Formatter/EmailFormatter.php @@ -27,9 +27,9 @@ public function getHtml(mixed $value): string return $value ? sprintf( '%s', - $value, - $title, - $value + $this->escapeHTML($value), + $this->escapeHTML($title), + $this->escapeHTML($value) ) : ''; } } diff --git a/src/Formatter/FormatterInterface.php b/src/Formatter/FormatterInterface.php index 175e89c..4126e9f 100644 --- a/src/Formatter/FormatterInterface.php +++ b/src/Formatter/FormatterInterface.php @@ -17,4 +17,6 @@ public function getHtml(mixed $value): string; * @param array $options */ public function processOptions(array $options = []): void; + + public function isHtmlSafe(): bool; } diff --git a/src/Formatter/Nl2brFormatter.php b/src/Formatter/Nl2brFormatter.php index 0b387a5..1b8bad4 100644 --- a/src/Formatter/Nl2brFormatter.php +++ b/src/Formatter/Nl2brFormatter.php @@ -4,6 +4,8 @@ namespace araise\CoreBundle\Formatter; +use Symfony\Component\OptionsResolver\OptionsResolver; + class Nl2brFormatter extends AbstractFormatter { public function getString(mixed $value): string @@ -13,6 +15,12 @@ public function getString(mixed $value): string public function getHtml(mixed $value): string { - return nl2br($value); + return nl2br($this->escapeHTML($value)); + } + + protected function configureOptions(OptionsResolver $resolver): void + { + parent::configureOptions($resolver); + $resolver->setDefault(self::OPT_HTML_SAFE, true); } } diff --git a/src/Formatter/TwigFormatter.php b/src/Formatter/TwigFormatter.php index c28b5e5..307ca89 100644 --- a/src/Formatter/TwigFormatter.php +++ b/src/Formatter/TwigFormatter.php @@ -57,5 +57,6 @@ protected function configureOptions(OptionsResolver $resolver): void { $resolver->setRequired(self::OPT_TEMPLATE); $resolver->setAllowedTypes(self::OPT_TEMPLATE, 'string'); + $resolver->setDefault(self::OPT_HTML_SAFE, true); } } diff --git a/src/Formatter/WysiwygFormatter.php b/src/Formatter/WysiwygFormatter.php index 6d80723..78006a6 100644 --- a/src/Formatter/WysiwygFormatter.php +++ b/src/Formatter/WysiwygFormatter.php @@ -29,6 +29,8 @@ namespace araise\CoreBundle\Formatter; +use Symfony\Component\OptionsResolver\OptionsResolver; + class WysiwygFormatter extends AbstractFormatter { public function getString(mixed $value): string @@ -42,4 +44,10 @@ public function getHtml(mixed $value): string return $value ? sprintf('
%s
', $value) : ''; } + + protected function configureOptions(OptionsResolver $resolver): void + { + parent::configureOptions($resolver); + $resolver->setDefault(self::OPT_HTML_SAFE, true); + } } diff --git a/src/Twig/CoreExtension.php b/src/Twig/CoreExtension.php new file mode 100644 index 0000000..eb15cf6 --- /dev/null +++ b/src/Twig/CoreExtension.php @@ -0,0 +1,68 @@ +isHtmlSafe(...) + ), + ]; + } + + public function isHtmlSafe(mixed $object): bool + { + if (!method_exists($object, 'getOption')) { + return false; + } + $formatter = $object->getOption('formatter'); + $formatterOptions = $object->getOption('formatter_options'); + + if (is_string($formatter)) { + $formatterObj = $this->formatterManager->getFormatter($formatter); + $formatterObj->processOptions($formatterOptions); + return $formatterObj->isHtmlSafe(); + } + + return false; + } +} diff --git a/tests/AbstractFormatterTest.php b/tests/AbstractFormatterTestBase.php similarity index 87% rename from tests/AbstractFormatterTest.php rename to tests/AbstractFormatterTestBase.php index 0a86031..0d72bfa 100644 --- a/tests/AbstractFormatterTest.php +++ b/tests/AbstractFormatterTestBase.php @@ -8,7 +8,7 @@ use araise\CoreBundle\Manager\FormatterManager; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; -abstract class AbstractFormatterTest extends KernelTestCase +abstract class AbstractFormatterTestBase extends KernelTestCase { protected function getFormatter(string $formatterClass): FormatterInterface { diff --git a/tests/BadgeFormatterTest.php b/tests/BadgeFormatterTest.php index 278894d..4f1e356 100644 --- a/tests/BadgeFormatterTest.php +++ b/tests/BadgeFormatterTest.php @@ -4,7 +4,7 @@ use araise\CoreBundle\Formatter\BadgeFormatter; -class BadgeFormatterTest extends AbstractFormatterTest +class BadgeFormatterTest extends AbstractFormatterTestBase { public function testGetHtml(): void { diff --git a/tests/BooleanFormatterTest.php b/tests/BooleanFormatterTest.php index a9e2dd2..bafc9ff 100644 --- a/tests/BooleanFormatterTest.php +++ b/tests/BooleanFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\BooleanFormatter; -class BooleanFormatterTest extends AbstractFormatterTest +class BooleanFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/CollectionFormatterTest.php b/tests/CollectionFormatterTest.php index dd8e0ed..22e0086 100644 --- a/tests/CollectionFormatterTest.php +++ b/tests/CollectionFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\CollectionFormatter; -class CollectionFormatterTest extends AbstractFormatterTest +class CollectionFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/CountryAlpha2FormatterTest.php b/tests/CountryAlpha2FormatterTest.php index bc1bd4e..a66a7ed 100644 --- a/tests/CountryAlpha2FormatterTest.php +++ b/tests/CountryAlpha2FormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\CountryAlpha2Formatter; -class CountryAlpha2FormatterTest extends AbstractFormatterTest +class CountryAlpha2FormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/CountryAlpha3FormatterTest.php b/tests/CountryAlpha3FormatterTest.php index cd24072..a6720f0 100644 --- a/tests/CountryAlpha3FormatterTest.php +++ b/tests/CountryAlpha3FormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\CountryAlpha3Formatter; -class CountryAlpha3FormatterTest extends AbstractFormatterTest +class CountryAlpha3FormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/DateFormatterTest.php b/tests/DateFormatterTest.php index 3883bb0..b0a070e 100644 --- a/tests/DateFormatterTest.php +++ b/tests/DateFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\DateFormatter; -class DateFormatterTest extends AbstractFormatterTest +class DateFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/DateTimeFormatterTest.php b/tests/DateTimeFormatterTest.php index d73e030..25b6068 100644 --- a/tests/DateTimeFormatterTest.php +++ b/tests/DateTimeFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\DateTimeFormatter; -class DateTimeFormatterTest extends AbstractFormatterTest +class DateTimeFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/DefaultFormatterTest.php b/tests/DefaultFormatterTest.php index b3a135e..d691e44 100644 --- a/tests/DefaultFormatterTest.php +++ b/tests/DefaultFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\DefaultFormatter; -class DefaultFormatterTest extends AbstractFormatterTest +class DefaultFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/EmailFormatterTest.php b/tests/EmailFormatterTest.php index 6ceff83..49c67f0 100644 --- a/tests/EmailFormatterTest.php +++ b/tests/EmailFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\EmailFormatter; -class EmailFormatterTest extends AbstractFormatterTest +class EmailFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/EnumFormatterTest.php b/tests/EnumFormatterTest.php index 66a1973..7bf740d 100644 --- a/tests/EnumFormatterTest.php +++ b/tests/EnumFormatterTest.php @@ -7,7 +7,7 @@ use araise\CoreBundle\Formatter\EnumFormatter; use araise\CoreBundle\Tests\App\Enum\TestEnum; -class EnumFormatterTest extends AbstractFormatterTest +class EnumFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/InfoFormatterTest.php b/tests/InfoFormatterTest.php index 698997f..d75109a 100644 --- a/tests/InfoFormatterTest.php +++ b/tests/InfoFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\InfoFormatter; -class InfoFormatterTest extends AbstractFormatterTest +class InfoFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/MoneyFormatterTest.php b/tests/MoneyFormatterTest.php index 34af378..2a4c188 100644 --- a/tests/MoneyFormatterTest.php +++ b/tests/MoneyFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\MoneyFormatter; -class MoneyFormatterTest extends AbstractFormatterTest +class MoneyFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/Nl2brFormatterTest.php b/tests/Nl2brFormatterTest.php index e025eca..6fc36e9 100644 --- a/tests/Nl2brFormatterTest.php +++ b/tests/Nl2brFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\Nl2brFormatter; -class Nl2brFormatterTest extends AbstractFormatterTest +class Nl2brFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/PercentageFormatterTest.php b/tests/PercentageFormatterTest.php index 282e09a..2d57728 100644 --- a/tests/PercentageFormatterTest.php +++ b/tests/PercentageFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\PercentageFormatter; -class PercentageFormatterTest extends AbstractFormatterTest +class PercentageFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/TranslationFormatterTest.php b/tests/TranslationFormatterTest.php index e607088..1161264 100644 --- a/tests/TranslationFormatterTest.php +++ b/tests/TranslationFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\TranslationFormatter; -class TranslationFormatterTest extends AbstractFormatterTest +class TranslationFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/TwigFormatterTest.php b/tests/TwigFormatterTest.php index d962f92..5d8f1fb 100644 --- a/tests/TwigFormatterTest.php +++ b/tests/TwigFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\TwigFormatter; -class TwigFormatterTest extends AbstractFormatterTest +class TwigFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void { diff --git a/tests/Util/StringConverterTest.php b/tests/Util/StringConverterTest.php index c110945..2eda2df 100644 --- a/tests/Util/StringConverterTest.php +++ b/tests/Util/StringConverterTest.php @@ -5,13 +5,12 @@ namespace araise\CoreBundle\Tests\Util; use araise\CoreBundle\Util\StringConverter; +use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class StringConverterTest extends KernelTestCase { - /** - * @dataProvider dataProvider - */ + #[DataProvider('dataProvider')] public function testString(mixed $input, string $expected): void { $output = StringConverter::toString($input); @@ -23,7 +22,7 @@ public function testString(mixed $input, string $expected): void /** * @return array> */ - public function dataProvider(): array + public static function dataProvider(): array { $objectPlain = new \stdClass(); $objectStringify = new class() { diff --git a/tests/WysiwygFormatterTest.php b/tests/WysiwygFormatterTest.php index 9ebcbff..c1bb6fe 100644 --- a/tests/WysiwygFormatterTest.php +++ b/tests/WysiwygFormatterTest.php @@ -6,7 +6,7 @@ use araise\CoreBundle\Formatter\WysiwygFormatter; -class WysiwygFormatterTest extends AbstractFormatterTest +class WysiwygFormatterTest extends AbstractFormatterTestBase { public function testFormatter(): void {