Passed
Push — master ( 8c700e...66c7a5 )
by Magnar Ovedal
03:24
created

LowerCase::getMessage()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 37
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 22
nc 5
nop 2
dl 0
loc 37
ccs 20
cts 20
cp 1
crap 5
rs 9.2568
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 StableSort\StableSort;
9
use Stadly\PasswordPolice\Constraint\Count;
10
use Stadly\PasswordPolice\Password;
11
use Stadly\PasswordPolice\Policy;
12
13
final class LowerCase implements RuleInterface
14
{
15
    /**
16
     * @var Count[] Rule constraints.
17
     */
18
    private $constraints;
19
20
    /**
21
     * @param int $min Minimum number of lower case letters.
22
     * @param int|null $max Maximum number of lower case letters.
23
     * @param int $weight Constraint weight.
24
     */
25 7
    public function __construct(int $min = 1, ?int $max = null, int $weight = 1)
26
    {
27 7
        $this->addConstraint($min, $max, $weight);
28 5
    }
29
30
    /**
31
     * @param int $min Minimum number of lower case letters.
32
     * @param int|null $max Maximum number of lower case letters.
33
     * @param int $weight Constraint weight.
34
     * @return $this
35
     */
36 2
    public function addConstraint(int $min = 1, ?int $max = null, int $weight = 1): self
37
    {
38 2
        $this->constraints[] = new Count($min, $max, $weight);
39
40
        StableSort::usort($this->constraints, function (Count $a, Count $b): int {
41 2
            return $b->getWeight() <=> $a->getWeight();
42 2
        });
43
44 2
        return $this;
45
    }
46
47
    /**
48
     * Check whether a password is in compliance with the rule.
49
     *
50
     * @param Password|string $password Password to check.
51
     * @return bool Whether the password is in compliance with the rule.
52
     */
53 6
    public function test($password): bool
54
    {
55 6
        $count = $this->getCount((string)$password);
56 6
        $constraint = $this->getViolation($count);
57
58 6
        return $constraint === null;
59
    }
60
61
    /**
62
     * Enforce that a password is in compliance with the rule.
63
     *
64
     * @param Password|string $password Password that must adhere to the rule.
65
     * @throws RuleException If the password does not adhrere to the rule.
66
     */
67 7
    public function enforce($password): void
68
    {
69 7
        $count = $this->getCount((string)$password);
70 7
        $constraint = $this->getViolation($count);
71
72 7
        if ($constraint !== null) {
73 6
            throw new RuleException($this, $this->getMessage($constraint, $count));
74
        }
75 1
    }
76
77
    /**
78
     * @param int $count Number of lower case characters.
79
     * @return Count|null Constraint violated by the count.
80
     */
81 13
    private function getViolation(int $count): ?Count
82
    {
83 13
        foreach ($this->constraints as $constraint) {
84 13
            if (!$constraint->test($count)) {
85 13
                return $constraint;
86
            }
87
        }
88
89 4
        return null;
90
    }
91
92
    /**
93
     * @param string $password Password to count characters in.
94
     * @return int Number of lower case characters.
95
     */
96 13
    private function getCount(string $password): int
97
    {
98 13
        $upperCase = mb_strtoupper($password);
99
100 13
        $passwordCharacters = $this->splitString($password);
101 13
        $upperCaseCharacters = $this->splitString($upperCase);
102 13
        assert(count($passwordCharacters) === count($upperCaseCharacters));
103
104 13
        $count = 0;
105 13
        for ($i = count($passwordCharacters)-1; $i >= 0; --$i) {
106 13
            if ($passwordCharacters[$i] !== $upperCaseCharacters[$i]) {
107 10
                ++$count;
108
            }
109
        }
110
111 13
        return $count;
112
    }
113
114
    /**
115
     * @param string $string String to split into individual characters.
116
     * @return string[] Array of characters.
117
     */
118 13
    private function splitString(string $string): array
119
    {
120 13
        $characters = preg_split('{}u', $string, -1, PREG_SPLIT_NO_EMPTY);
121 13
        assert($characters !== false);
122
123 13
        return $characters;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $characters returns an array which contains values of type array which are incompatible with the documented value type string.
Loading history...
124
    }
125
126
    /**
127
     * @param Count $constraint Constraint that is violated.
128
     * @param int $count Count that violates the constraint.
129
     * @return string Message explaining the violation.
130
     */
131 6
    private function getMessage(Count $constraint, int $count): string
0 ignored issues
show
Unused Code introduced by
The parameter $count is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

131
    private function getMessage(Count $constraint, /** @scrutinizer ignore-unused */ int $count): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
132
    {
133 6
        $translator = Policy::getTranslator();
134
135 6
        if ($constraint->getMax() === null) {
136 2
            return $translator->trans(
137
                'There must be at least one lower case character.|'.
138 2
                'There must be at least %count% lower case characters.',
139 2
                ['%count%' => $constraint->getMin()]
140
            );
141
        }
142
143 4
        if ($constraint->getMax() === 0) {
144 1
            return $translator->trans(
145 1
                'There must be no lower case characters.'
146
            );
147
        }
148
149 3
        if ($constraint->getMin() === 0) {
150 1
            return $translator->trans(
151
                'There must be at most one lower case character.|'.
152 1
                'There must be at most %count% lower case characters.',
153 1
                ['%count%' => $constraint->getMax()]
154
            );
155
        }
156
157 2
        if ($constraint->getMin() === $constraint->getMax()) {
158 1
            return $translator->trans(
159
                'There must be exactly one lower case character.|'.
160 1
                'There must be exactly %count% lower case characters.',
161 1
                ['%count%' => $constraint->getMin()]
162
            );
163
        }
164
165 1
        return $translator->trans(
166 1
            'There must be between %min% and %max% lower case characters.',
167 1
            ['%min%' => $constraint->getMin(), '%max%' => $constraint->getMax()]
168
        );
169
    }
170
}
171