Passed
Push — master ( 2e118b...53f704 )
by Magnar Ovedal
02:51
created

LowerCase::getMessage()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 35
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 21
nc 5
nop 1
dl 0
loc 35
ccs 19
cts 19
cp 1
crap 5
rs 9.2728
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stadly\PasswordPolice\Rule;
6
7
use InvalidArgumentException;
8
use Stadly\PasswordPolice\Rule;
9
use Stadly\PasswordPolice\RuleException;
10
use Symfony\Component\Translation\Translator;
11
12
final class LowerCase implements Rule
13
{
14
    /**
15
     * @var int Minimum number of lower case letters in password.
16
     */
17
    private $min;
18
19
    /**
20
     * @var int|null Maximum number of lower case letters in password.
21
     */
22
    private $max;
23
24 7
    public function __construct(int $min, ?int $max = null)
25
    {
26 7
        if ($min < 0) {
27 1
            throw new InvalidArgumentException('Min cannot be negative.');
28
        }
29 6
        if ($max !== null && $max < $min) {
30 1
            throw new InvalidArgumentException('Max cannot be smaller than min.');
31
        }
32 5
        if ($min === 0 && $max === null) {
33 1
            throw new InvalidArgumentException('Min cannot be zero when max is unconstrained.');
34
        }
35
36 4
        $this->min = $min;
37 4
        $this->max = $max;
38 4
    }
39
40 1
    public function getMin(): int
41
    {
42 1
        return $this->min;
43
    }
44
45 1
    public function getMax(): ?int
46
    {
47 1
        return $this->max;
48
    }
49
50 6
    public function test(string $password): bool
51
    {
52 6
        if ($this->getCount($password) < $this->min) {
53 2
            return false;
54
        }
55
56 4
        if (null !== $this->max && $this->max < $this->getCount($password)) {
57 1
            return false;
58
        }
59
60 3
        return true;
61
    }
62
63
    /**
64
     * @throws RuleException If the rule cannot be enforced.
65
     */
66 2
    public function enforce(string $password, Translator $translator): void
67
    {
68 2
        if (!$this->test($password)) {
69 1
            throw new RuleException($this, $this->getMessage($translator));
70
        }
71 1
    }
72
73 5
    public function getMessage(Translator $translator): string
74
    {
75 5
        if ($this->getMax() === null) {
76 1
            return $translator->transChoice(
77
                'There must be at least one lower case character.|'.
78 1
                'There must be at least %count% lower case characters.',
79 1
                $this->getMin()
80
            );
81
        }
82
83 4
        if ($this->getMax() === 0) {
84 1
            return $translator->trans(
85 1
                'There must be no lower case characters.'
86
            );
87
        }
88
89 3
        if ($this->getMin() === 0) {
90 1
            return $translator->transChoice(
91
                'There must be at most one lower case character.|'.
92 1
                'There must be at most %count% lower case characters.',
93 1
                $this->getMax()
94
            );
95
        }
96
97 2
        if ($this->getMin() === $this->getMax()) {
98 1
            return $translator->transChoice(
99
                'There must be exactly one lower case character.|'.
100 1
                'There must be exactly %count% lower case characters.',
101 1
                $this->getMin()
102
            );
103
        }
104
105 1
        return $translator->trans(
106 1
            'There must be between %min% and %max% lower case characters.',
107 1
            ['%min%' => $this->getMin(), '%max%' => $this->getMax()]
108
        );
109
    }
110
111 8
    private function getCount(string $password): int
112
    {
113 8
        $upperCase = mb_strtoupper($password);
114
115 8
        $passwordCharacters = $this->splitString($password);
116 8
        $upperCaseCharacters = $this->splitString($upperCase);
117 8
        assert(count($passwordCharacters) === count($upperCaseCharacters));
118
119 8
        $count = 0;
120 8
        for ($i = count($passwordCharacters)-1; $i >= 0; --$i) {
121 8
            if ($passwordCharacters[$i] !== $upperCaseCharacters[$i]) {
122 5
                ++$count;
123
            }
124
        }
125
126 8
        return $count;
127
    }
128
129 8
    private function splitString(string $string): array
130
    {
131 8
        $characters = preg_split('{}u', $string, -1, PREG_SPLIT_NO_EMPTY);
132 8
        assert($characters !== false);
133
134 8
        return $characters;
135
    }
136
}
137