From 0e99f88d6d938841812bb48a6c5f3171962b5614 Mon Sep 17 00:00:00 2001 From: Manuel Reinhard Date: Sun, 23 Jun 2024 20:54:13 +0200 Subject: [PATCH] Allow to inject unsupported character replacements --- src/QrBill.php | 5 ++-- src/QrCode/QrCode.php | 24 +++++++++++++++---- tests/QrBillTest.php | 37 +++++++++++++++++++++++++++++ tests/QrCode/QrCodeTest.php | 40 +++++++++++++++++++++++++++++--- tests/TestQrBillCreatorTrait.php | 15 ++++++++++++ 5 files changed, 111 insertions(+), 10 deletions(-) diff --git a/src/QrBill.php b/src/QrBill.php index 477face..775744e 100644 --- a/src/QrBill.php +++ b/src/QrBill.php @@ -165,7 +165,7 @@ public function addAlternativeScheme(AlternativeScheme $alternativeScheme): self /** * @throws InvalidQrBillDataException */ - public function getQrCode(?string $fileFormat = null): QrCode + public function getQrCode(?string $fileFormat = null, array $unsupportedCharacterReplacements = []): QrCode { if (!$this->isValid()) { throw new InvalidQrBillDataException( @@ -175,7 +175,8 @@ public function getQrCode(?string $fileFormat = null): QrCode return QrCode::create( $this->getQrCodeContent(), - $fileFormat + $fileFormat, + $unsupportedCharacterReplacements ); } diff --git a/src/QrCode/QrCode.php b/src/QrCode/QrCode.php index af0569d..ecb58a6 100644 --- a/src/QrCode/QrCode.php +++ b/src/QrCode/QrCode.php @@ -18,6 +18,8 @@ final class QrCode public const FILE_FORMAT_PNG = 'png'; public const FILE_FORMAT_SVG = 'svg'; + public const SUPPORTED_CHARACTERS = 'a-zA-Z0-9.,;:\'"+\-\/()?*\[\]{}|`´~ !^#%&<>÷=@_$£àáâäçèéêëìíîïñòóôöùúûüýßÀÁÂÄÇÈÉÊËÌÍÎÏÒÓÔÖÙÚÛÜÑ'; + private const SUPPORTED_FILE_FORMATS = [ self::FILE_FORMAT_PNG, self::FILE_FORMAT_SVG @@ -34,17 +36,18 @@ final class QrCode /** @var array $writerOptions */ private array $writerOptions = [SvgWriter::WRITER_OPTION_FORCE_XLINK_HREF => true]; - public static function create(string $data, string $fileFormat = null): self + public static function create(string $data, string $fileFormat = null, array $unsupportedCharacterReplacements = []): self { if (null === $fileFormat) { $fileFormat = self::FILE_FORMAT_SVG; } - return new self($data, $fileFormat); + return new self($data, $fileFormat, $unsupportedCharacterReplacements); } - private function __construct(string $data, string $fileFormat) + private function __construct(string $data, string $fileFormat, array $unsupportedCharacterReplacements) { + $data = $this->replaceUnsupportedCharacters($data, $unsupportedCharacterReplacements); $data = $this->cleanUnsupportedCharacters($data); if (class_exists(ErrorCorrectionLevel\ErrorCorrectionLevelMedium::class)) { @@ -129,11 +132,22 @@ public function avoidCompactSvgs(): void } } + private function replaceUnsupportedCharacters(string $data, array $unsupportedCharacterReplacements): string + { + foreach ($unsupportedCharacterReplacements as $character => $replacement) { + if (preg_match("/([^" . self::SUPPORTED_CHARACTERS . "])/u", $character)) { + $data = str_replace($character, $replacement, $data); + } + } + + return $data; + } + private function cleanUnsupportedCharacters(string $data): string { - $pattern = '/([^a-zA-Z0-9.,;:\'"+\-\/()?*\[\]{}|`´~ !^#%&<>÷=@_$£àáâäçèéêëìíîïñòóôöùúûüýßÀÁÂÄÇÈÉÊËÌÍÎÏÒÓÔÖÙÚÛÜÑ\\n])/u'; + $supportedCharacters = self::SUPPORTED_CHARACTERS . "\\n"; - return preg_replace($pattern, '', $data); + return preg_replace("/([^$supportedCharacters])/u", '', $data); } private function setWriterByExtension(string $extension): void diff --git a/tests/QrBillTest.php b/tests/QrBillTest.php index ff95bb6..4fd8d85 100644 --- a/tests/QrBillTest.php +++ b/tests/QrBillTest.php @@ -237,6 +237,43 @@ public function testMaximumTwoAlternativeSchemesAreAllowed() $this->assertFalse($qrBill->isValid()); } + public function testItReplacesUnsupportedCharacters() + { + $qrBill = $this->createQrBill([ + 'header', + 'creditorInformationQrIban', + 'creditorWithUnsupportedCharacters', + 'paymentAmountInformation', + 'paymentReferenceQr', + ]); + + $this->assertStringContainsString( + 'Team We are the Champions!', + $qrBill->getQrCode()->getText() + ); + } + + public function testItConsidersReplacementCharacters() + { + $qrBill = $this->createQrBill([ + 'header', + 'creditorInformationQrIban', + 'creditorWithUnsupportedCharacters', + 'paymentAmountInformation', + 'paymentReferenceQr', + ]); + + $unsupportedCharacterReplacements = [ + '«' => '"', + '»' => '"', + ]; + + $this->assertStringContainsString( + 'Team "We are the Champions!"', + $qrBill->getQrCode(null, $unsupportedCharacterReplacements)->getText() + ); + } + public function testCatchInvalidData() { $this->expectException(InvalidQrBillDataException::class); diff --git a/tests/QrCode/QrCodeTest.php b/tests/QrCode/QrCodeTest.php index dd54a32..8b04fba 100644 --- a/tests/QrCode/QrCodeTest.php +++ b/tests/QrCode/QrCodeTest.php @@ -120,9 +120,43 @@ public function stringProvider() } /** - * @dataProvider invalidCharactersCodeProvider + * @dataProvider replacementCharactersProvider */ - public function testItRemovesInvalidCharacters(string $providedString, string $expectedString): void + public function testItReplacesUnsupportedCharacters(string $providedString, array $replacements, string $expectedString): void + { + $qrCode = QrCode::create($providedString, null, $replacements); + + $this->assertEquals( + $expectedString, + $qrCode->getText() + ); + } + + public function replacementCharactersProvider(): array + { + return [ + 'replaceSpecificUnsupportedCharacters' => [ + 'providedString' => '«This is a test!»', + 'replacements' => [ + '«' => '"', + '»' => '"' + ], + 'expectedString' => '"This is a test!"' + ], + 'ignoreReplacementsOfSupportedCharacters' => [ + 'providedString' => '«This is a test!»', + 'replacements' => [ + 't' => 'a', + ], + 'expectedString' => 'This is a test!' + ], + ]; + } + + /** + * @dataProvider unsupportedCharactersProvider + */ + public function testItRemovesUnsupportedCharacters(string $providedString, string $expectedString): void { $qrCode = QrCode::create($providedString); @@ -132,7 +166,7 @@ public function testItRemovesInvalidCharacters(string $providedString, string $e ); } - public function invalidCharactersCodeProvider(): array + public function unsupportedCharactersProvider(): array { return [ 'keepAllAllowedCharacters' => [ diff --git a/tests/TestQrBillCreatorTrait.php b/tests/TestQrBillCreatorTrait.php index f277938..9727dbb 100644 --- a/tests/TestQrBillCreatorTrait.php +++ b/tests/TestQrBillCreatorTrait.php @@ -218,6 +218,11 @@ public function creditor(QrBill &$qrBill) $qrBill->setCreditor($this->structuredAddress()); } + public function creditorWithUnsupportedCharacters(QrBill &$qrBill) + { + $qrBill->setCreditor($this->addressWithUnsupportedCharacters()); + } + public function creditorMediumLong(QrBill &$qrBill) { $qrBill->setCreditor($this->mediumLongAddress()); @@ -392,6 +397,16 @@ public function longAddress() ); } + public function addressWithUnsupportedCharacters() + { + return CombinedAddress::create( + 'Team «We are the Champions!»', + 'Rue examplaire 22a', + '1000 Lausanne', + 'CH' + ); + } + public function invalidAddress() { return CombinedAddress::create(