Passed
Push — master ( 028295...075f5a )
by Magnar Ovedal
03:02
created

CharacterClass::getMessage()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 50
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 28
nc 5
nop 0
dl 0
loc 50
ccs 26
cts 26
cp 1
crap 5
rs 9.1608
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\Password;
9
use Stadly\PasswordPolice\Policy;
10
11
abstract class CharacterClass implements RuleInterface
12
{
13
    /**
14
     * @var string Characters matched by the rule.
15
     */
16
    protected $characters;
17
18
    /**
19
     * @var int Minimum number of characters matching the rule.
20
     */
21
    protected $min;
22
23
    /**
24
     * @var int|null Maximum number of characters matching the rule.
25
     */
26
    protected $max;
27
28
    /**
29
     * @param string $characters Characters matched by the rule.
30
     * @param int $min Minimum number of characters matching the rule.
31
     * @param int|null $max Maximum number of characters matching the rule.
32
     */
33 16
    public function __construct(string $characters, int $min = 1, ?int $max = null)
34
    {
35 16
        if ($characters === '') {
36 2
            throw new InvalidArgumentException('At least one character must be specified.');
37
        }
38 14
        if ($min < 0) {
39 2
            throw new InvalidArgumentException('Min cannot be negative.');
40
        }
41 12
        if ($max !== null && $max < $min) {
42 2
            throw new InvalidArgumentException('Max cannot be smaller than min.');
43
        }
44 10
        if ($min === 0 && $max === null) {
45 2
            throw new InvalidArgumentException('Min cannot be zero when max is unconstrained.');
46
        }
47
48 8
        $this->characters = $characters;
49 8
        $this->min = $min;
50 8
        $this->max = $max;
51 8
    }
52
53
    /**
54
     * @return string Characters matched by the rule.
55
     */
56 2
    public function getCharacters(): string
57
    {
58 2
        return $this->characters;
59
    }
60
61
    /**
62
     * @return int Minimum number of characters matching the rule.
63
     */
64 3
    public function getMin(): int
65
    {
66 3
        return $this->min;
67
    }
68
69
    /**
70
     * @return int|null Maximum number of characters matching the rule.
71
     */
72 3
    public function getMax(): ?int
73
    {
74 3
        return $this->max;
75
    }
76
77
    /**
78
     * Check whether a password is in compliance with the rule.
79
     *
80
     * @param Password|string $password Password to check.
81
     * @return bool Whether the password is in compliance with the rule.
82
     */
83 12
    public function test($password): bool
84
    {
85 12
        $count = $this->getNoncompliantCount((string)$password);
86
87 12
        return $count === null;
88
    }
89
90
    /**
91
     * Enforce that a password is in compliance with the rule.
92
     *
93
     * @param Password|string $password Password that must adhere to the rule.
94
     * @throws RuleException If the password does not adhrere to the rule.
95
     */
96 6
    public function enforce($password): void
97
    {
98 6
        $count = $this->getNoncompliantCount((string)$password);
99
100 6
        if ($count !== null) {
101 3
            throw new RuleException($this, $this->getMessage());
102
        }
103 3
    }
104
105
    /**
106
     * @param string $password Password to count characters in.
107
     * @return int Number of characters matching the rule if not in compliance with the rule.
108
     */
109 18
    private function getNoncompliantCount(string $password): ?int
110
    {
111 18
        $count = $this->getCount($password);
112
113 18
        if ($count < $this->min) {
114 6
            return $count;
115
        }
116
117 12
        if (null !== $this->max && $this->max < $count) {
118 3
            return $count;
119
        }
120
121 9
        return null;
122
    }
123
124
    /**
125
     * @param string $password Password to count characters in.
126
     * @return int Number of characters matching the rule.
127
     */
128 18
    private function getCount(string $password): int
129
    {
130 18
        $escapedCharacters = preg_quote($this->characters);
131 18
        $count = preg_match_all('{['.$escapedCharacters.']}u', $password);
132 18
        assert(false !== $count);
133
134 18
        return $count;
135
    }
136
}
137