Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.77% covered (success)
96.77%
30 / 31
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
RegexUtils
96.77% covered (success)
96.77%
30 / 31
75.00% covered (warning)
75.00%
3 / 4
12
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 removeDelimiters
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
3
 getMaximumAcceptedStringLengthOfRegularExpression
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 fromPlaceholderToRegularExpression
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2namespace Apie\Core;
3
4use Apie\RegexTools\CompiledRegularExpression;
5
6final class RegexUtils
7{
8    /**
9     * @var array<string, int|null> $alreadyCalculated
10     */
11    private static array $alreadyCalculated = [];
12
13    private function __construct()
14    {
15    }
16
17    public static function removeDelimiters(string $regularExpressionWithDelimiter): string
18    {
19        $delimiter = preg_quote(substr($regularExpressionWithDelimiter, 0, 1), '#');
20        $removeStartDelimiterRegex = '#^' . $delimiter . '#u';
21        $regex = preg_replace($removeStartDelimiterRegex, '', $regularExpressionWithDelimiter);
22        $caseSensitive = true;
23        $regexModifiers = preg_match('#' . $delimiter . '[imsxADSUJXu]*$#u', $regularExpressionWithDelimiter, $matches);
24        if ($regexModifiers) {
25            $caseSensitive = strpos($matches[0], 'i') === false;
26        }
27        $removeEndDelimiterRegex = '#' . $delimiter . '[imsxADSUJXu]*$#u';
28        $result = preg_replace($removeEndDelimiterRegex, '', $regex);
29        if ($caseSensitive) {
30            return $result;
31        }
32
33        return CompiledRegularExpression::createFromRegexWithoutDelimiters($result)
34            ->toCaseInsensitive()
35            ->__toString();
36    }
37
38    public static function getMaximumAcceptedStringLengthOfRegularExpression(
39        string $regularExpression,
40        bool $removeDelimiters = true
41    ): ?int {
42        if ($removeDelimiters) {
43            $regularExpression = self::removeDelimiters($regularExpression);
44        }
45        if (!isset(self::$alreadyCalculated[$regularExpression])) {
46            $regex = CompiledRegularExpression::createFromRegexWithoutDelimiters($regularExpression);
47            // regular expression should start with ^ and end with $ to determine max length of an
48            // accepted string.
49            if (!$regex->hasStartOfRegexMarker() || !$regex->hasEndOfRegexMarker()) {
50                return self::$alreadyCalculated[$regularExpression] = null;
51            }
52            return self::$alreadyCalculated[$regularExpression] = $regex->getMaximumPossibleLength();
53        }
54
55        return self::$alreadyCalculated[$regularExpression];
56    }
57
58    public static function fromPlaceholderToRegularExpression(
59        string $translationString,
60        bool $includeStartAndEndRegex = true,
61        bool $includeAsCaptureGroup = true
62    ): string {
63        $regex = preg_replace(
64            '/:([a-zA-Z_][a-zA-Z0-9_]*)/',
65            $includeAsCaptureGroup ? '(?<$1>[^.]+)' : '[^.]+',
66            $translationString
67        );
68        if ($includeStartAndEndRegex) {
69            return '/^' . $regex . '$/';
70        }
71        return $regex;
72    }
73}