settings = $settings; $this->start = $start; $this->end = $end; $this->range = $this->applyNumericPrefix($this->buildRange()); } /** * {@inheritdoc} */ public function count(): int { return count($this->range); } /** * {@inheritdoc} */ public function generate(string $value): ?string { return $this->respondWhenInRange($value); } /** * {@inheritdoc} */ public function resolve(string $value): ?string { return $this->respondWhenInRange($value); } protected function respondWhenInRange(string $value): ?string { if (in_array($value, $this->range, true)) { return $value; } return null; } /** * Builds range based on given settings and ensures each item is string. * The amount of items is limited to 1000 in order to avoid brute-force * scenarios and the risk of cache-flooding. * * In case that is not enough, creating a custom and more specific mapper * is encouraged. Using high values that are not distinct exposes the site * to the risk of cache-flooding. * * @return string[] * @throws \LengthException */ protected function buildRange(): array { $range = array_map('strval', range($this->start, $this->end)); if (count($range) > 1000) { throw new \LengthException( 'Range is larger than 1000 items', 1537696771 ); } return $range; } /** * @return string[] */ protected function applyNumericPrefix(array $range): array { if (!preg_match('#^\d+$#', $this->start) || !preg_match('#^\d+$#', $this->end) || $this->start === '0' || $this->end === '0' || $this->start[0] !== '0' && $this->end[0] !== '0' ) { return $range; } $length = strlen(max($this->start, $this->end)); $range = array_map( static function ($value) use ($length) { return str_pad($value, $length, '0', STR_PAD_LEFT); }, $range ); return $range; } }