Passed
Push — master ( eb260c...178b8b )
by Magnar Ovedal
02:45
created

ChangeOnDateRule::validate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 1
dl 0
loc 16
ccs 11
cts 11
cp 1
crap 2
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stadly\PasswordPolice\Rule;
6
7
use DateTime;
8
use DateTimeInterface;
9
use StableSort\StableSort;
10
use Stadly\PasswordPolice\Constraint\DateConstraint;
11
use Stadly\PasswordPolice\Password;
12
use Stadly\PasswordPolice\Policy;
13
use Stadly\PasswordPolice\Rule;
14
use Stadly\PasswordPolice\ValidationError;
15
16
final class ChangeOnDateRule implements Rule
17
{
18
    /**
19
     * @var DateConstraint[] Rule constraints.
20
     */
21
    private $constraints;
22
23
    /**
24
     * @param DateTimeInterface|null $min Minimum date for when the password was last changed.
25
     * @param DateTimeInterface|null $max Maximum date for when the password was last changed.
26
     * @param int $weight Constraint weight.
27
     */
28 6
    public function __construct(?DateTimeInterface $min, ?DateTimeInterface $max = null, int $weight = 1)
29
    {
30 6
        $this->addConstraint($min, $max, $weight);
31 5
    }
32
33
    /**
34
     * @param DateTimeInterface|null $min Minimum date for when the password was last changed.
35
     * @param DateTimeInterface|null $max Maximum date for when the password was last changed.
36
     * @param int $weight Constraint weight.
37
     * @return $this
38
     */
39 2
    public function addConstraint(?DateTimeInterface $min, ?DateTimeInterface $max = null, int $weight = 1): self
40
    {
41 2
        $this->constraints[] = new DateConstraint($min, $max, $weight);
42
43
        StableSort::usort($this->constraints, static function (DateConstraint $a, DateConstraint $b): int {
44 2
            return $b->getWeight() <=> $a->getWeight();
45 2
        });
46
47 2
        return $this;
48
    }
49
50
    /**
51
     * Check whether a password is in compliance with the rule.
52
     *
53
     * @param Password|string $password Password to check.
54
     * @param int|null $weight Don't consider constraints with lower weights.
55
     * @return bool Whether the password is in compliance with the rule.
56
     */
57 6
    public function test($password, ?int $weight = 1): bool
58
    {
59 6
        $date = $this->getDate($password);
60 6
        $constraint = $this->getViolation($date, $weight);
61
62 6
        return $constraint === null;
63
    }
64
65
    /**
66
     * Validate that a password is in compliance with the rule.
67
     *
68
     * @param Password|string $password Password to validate.
69
     * @return ValidationError|null Validation error describing why the password is not in compliance with the rule.
70
     */
71 5
    public function validate($password): ?ValidationError
72
    {
73 5
        $date = $this->getDate($password);
74 5
        $constraint = $this->getViolation($date);
75
76 5
        if ($constraint !== null) {
77 4
            assert($date !== null);
78 4
            return new ValidationError(
79 4
                $this->getMessage($constraint, $date),
80 4
                $password,
81 4
                $this,
82 4
                $constraint->getWeight()
83
            );
84
        }
85
86 1
        return null;
87
    }
88
89
    /**
90
     * @param DateTimeInterface|null $date When the password was last changed.
91
     * @param int|null $weight Don't consider constraints with lower weights.
92
     * @return DateConstraint|null Constraint violated by the count.
93
     */
94 11
    private function getViolation(?DateTimeInterface $date, ?int $weight = null): ?DateConstraint
95
    {
96 11
        if ($date === null) {
97 1
            return null;
98
        }
99
100 10
        foreach ($this->constraints as $constraint) {
101 10
            if ($weight !== null && $constraint->getWeight() < $weight) {
102 1
                continue;
103
            }
104 9
            if (!$constraint->test($date)) {
105 9
                return $constraint;
106
            }
107
        }
108
109 4
        return null;
110
    }
111
112
    /**
113
     * @param Password|string $password Password to check when was last changed.
114
     * @return DateTimeInterface|null When the password was last changed.
115
     */
116 11
    private function getDate($password): ?DateTimeInterface
117
    {
118 11
        if ($password instanceof Password) {
119 10
            $formerPasswords = $password->getFormerPasswords();
120
121 10
            if ($formerPasswords !== []) {
122 10
                return reset($formerPasswords)->getDate();
123
            }
124
        }
125 1
        return null;
126
    }
127
128
    /**
129
     * @param DateConstraint $constraint Constraint that is violated.
130
     * @param DateTimeInterface $date Date that violates the constraint.
131
     * @return string Message explaining the violation.
132
     */
133 4
    private function getMessage(DateConstraint $constraint, DateTimeInterface $date): string
0 ignored issues
show
Unused Code introduced by
The parameter $date 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

133
    private function getMessage(DateConstraint $constraint, /** @scrutinizer ignore-unused */ DateTimeInterface $date): 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...
134
    {
135 4
        $translator = Policy::getTranslator();
136 4
        $minString = $constraint->getMin() === null ? '' : $constraint->getMin()->format('Y-m-d H:i:s');
137 4
        $maxString = $constraint->getMax() === null ? '' : $constraint->getMax()->format('Y-m-d H:i:s');
138
139 4
        if ($constraint->getMax() === null) {
140 1
            return $translator->trans(
141 1
                'The password must have been changed on or after %date%.',
142 1
                ['%date%' => $minString]
143
            );
144
        }
145
146 3
        if ($constraint->getMin() === null) {
147 1
            return $translator->trans(
148 1
                'The password must have been changed on or before %date%.',
149 1
                ['%date%' => $maxString]
150
            );
151
        }
152
153 2
        if ($constraint->getMin()->format(DateTime::RFC3339_EXTENDED)
154 2
        === $constraint->getMax()->format(DateTime::RFC3339_EXTENDED)
155
        ) {
156 1
            return $translator->trans(
157 1
                'The password must have been changed at %date%.',
158 1
                ['%date%' => $minString]
159
            );
160
        }
161
162 1
        return $translator->trans(
163 1
            'The password must have been changed between %min% and %max%.',
164 1
            ['%min%' => $minString, '%max%' => $maxString]
165
        );
166
    }
167
}
168