[BUGFIX] Ensure StaticRangeMapper applies zero prefix to internal map 64/62864/3
authorOliver Hader <oliver@typo3.org>
Mon, 13 Jan 2020 14:59:43 +0000 (15:59 +0100)
committerSusanne Moog <look@susi.dev>
Tue, 14 Jan 2020 09:25:15 +0000 (10:25 +0100)
StateRangeMapper routing aspects defined like the following did not use
leading zero (0) prefixes due to internal number conversion to integer:

    month:
      type: StaticRangeMapper
      start: '01'
      end: '12'

Zero prefixes are applied in case they were explicitly given in either
start or end property for StaticRangeMapper settings.

Resolves: #87730
Releases: master, 9.5
Change-Id: I1846d9faa4843834d7175173f5ca9db5b1445ca5
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/62864
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Markus Klein <markus.klein@typo3.org>
Tested-by: Sascha Rademacher <sascha.rademacher+typo3@gmail.com>
Tested-by: Felix P. <f.pachowsky@neusta.de>
Tested-by: Susanne Moog <look@susi.dev>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Tobi Kretschmann <tobi@tobishome.de>
Reviewed-by: Felix P. <f.pachowsky@neusta.de>
Reviewed-by: Sascha Rademacher <sascha.rademacher+typo3@gmail.com>
Reviewed-by: Steffen Frese <steffenf14@gmail.com>
Reviewed-by: Susanne Moog <look@susi.dev>
typo3/sysext/core/Classes/Routing/Aspect/StaticRangeMapper.php
typo3/sysext/extbase/Tests/Unit/Routing/Aspect/StaticRangeMapperTest.php [new file with mode: 0644]

index 027cdbc..b70536d 100644 (file)
@@ -82,7 +82,7 @@ class StaticRangeMapper implements StaticMappableAspectInterface, \Countable
         $this->settings = $settings;
         $this->start = $start;
         $this->end = $end;
-        $this->range = $this->buildRange();
+        $this->range = $this->applyNumericPrefix($this->buildRange());
     }
 
     /**
@@ -144,4 +144,27 @@ class StaticRangeMapper implements StaticMappableAspectInterface, \Countable
         }
         return $range;
     }
+
+    /**
+     * @param array $range
+     * @return string[]
+     */
+    protected function applyNumericPrefix(array $range): array
+    {
+        if (!preg_match('#^\d+$#', $this->start)
+            || !preg_match('#^\d+$#', $this->end)
+            || $this->start[0] !== '0' && $this->end[0] !== '0'
+        ) {
+            return $range;
+        }
+
+        $length = strlen(max($this->start, $this->end));
+        $range = array_map(
+            function ($value) use ($length) {
+                return str_pad($value, $length, '0', STR_PAD_LEFT);
+            },
+            $range
+        );
+        return $range;
+    }
 }
diff --git a/typo3/sysext/extbase/Tests/Unit/Routing/Aspect/StaticRangeMapperTest.php b/typo3/sysext/extbase/Tests/Unit/Routing/Aspect/StaticRangeMapperTest.php
new file mode 100644 (file)
index 0000000..9a6bc97
--- /dev/null
@@ -0,0 +1,176 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Tests\Unit\Routing\Aspect;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Routing\Aspect\StaticRangeMapper;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+/**
+ * Test case
+ */
+class StaticRangeMapperTest extends UnitTestCase
+{
+    public function valueSettingsDataProvider(): array
+    {
+        return [
+            '1-3' => [
+                '1',
+                '3',
+                [
+                    'a' => null,
+                    '0' => null,
+                    '1' => '1',
+                    '2' => '2',
+                    '3' => '3',
+                    '4' => null,
+                    'z' => null,
+                ]
+            ],
+            'b-d' => [
+                'b',
+                'd',
+                [
+                    '0' => null,
+                    'a' => null,
+                    'b' => 'b',
+                    'c' => 'c',
+                    'd' => 'd',
+                    'e' => null,
+                    '9' => null,
+                ]
+            ],
+            '@-C' => [
+                '@',
+                'C',
+                [
+                    '?' => null,
+                    '@' => '@',
+                    'A' => 'A',
+                    'B' => 'B',
+                    'C' => 'C',
+                    'D' => null,
+                ]
+            ],
+            '2-11 (no zero prefix)' => [
+                '2',
+                '11',
+                [
+                    '00' => null,
+                    '01' => null,
+                    '02' => null,
+                    '03' => null,
+                    '0' => null,
+                    '1' => null,
+                    '2' => '2',
+                    '3' => '3',
+                    '10' => '10',
+                    '11' => '11',
+                    '12' => null,
+                ]
+            ],
+            '02-11 (apply zero prefix)' => [
+                '02',
+                '11',
+                [
+                    '00' => null,
+                    '01' => null,
+                    '02' => '02',
+                    '03' => '03',
+                    '10' => '10',
+                    '11' => '11',
+                    '12' => null,
+                ]
+            ],
+            '11-02 (apply zero prefix)' => [
+                '11',
+                '02',
+                [
+                    '00' => null,
+                    '01' => null,
+                    '02' => '02',
+                    '03' => '03',
+                    '10' => '10',
+                    '11' => '11',
+                    '12' => null,
+                ]
+            ],
+            '011-002 (apply zero prefix)' => [
+                '011',
+                '002',
+                [
+                    '000' => null,
+                    '001' => null,
+                    '002' => '002',
+                    '003' => '003',
+                    '010' => '010',
+                    '011' => '011',
+                    '012' => null,
+                ]
+            ],
+            '2-100 (no zero prefix)' => [
+                '2',
+                '100',
+                [
+                    '2' => '2',
+                    '02' => null,
+                    '002' => null,
+                    '100' => '100',
+                ]
+            ],
+            // use maximum char length (3) even if '02' is given
+            '02-100 (zero prefix)' => [
+                '02',
+                '100',
+                [
+                    '2' => null,
+                    '02' => null,
+                    '002' => '002',
+                    '100' => '100',
+                ]
+            ],
+            '002-100 (zero prefix)' => [
+                '002',
+                '100',
+                [
+                    '2' => null,
+                    '02' => null,
+                    '002' => '002',
+                    '100' => '100',
+                ]
+            ],
+        ];
+    }
+
+    /**
+     * @param string $start
+     * @param string $end
+     * @param array $expectations
+     *
+     * @test
+     * @dataProvider valueSettingsDataProvider
+     */
+    public function resolveDeterminesValues(string $start, string $end, array $expectations): void
+    {
+        $subject = new StaticRangeMapper([
+            'start' => $start,
+            'end' => $end,
+        ]);
+        foreach ($expectations as $value => $expectation) {
+            self::assertSame($expectation, $subject->resolve((string)$value));
+        }
+    }
+}