Passed
Push — master ( 2cbba2...4e31d5 )
by Magnar Ovedal
03:51
created

Change::getViolation()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

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

123
    private function getMessage(Date $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...
124
    {
125 5
        $translator = Policy::getTranslator();
126 5
        $locale = $translator->getLocale();
127
128 5
        $min = CarbonInterval::instance($constraint->getMin());
129 5
        $min->locale($locale);
130 5
        if ($constraint->getMax() === null) {
131 2
            $max = null;
132
        } else {
133 3
            $max = CarbonInterval::instance($constraint->getMax());
134 3
            $max->locale($locale);
135
        }
136
137 5
        if ($constraint->getMax() === null) {
138 2
            return $translator->trans(
139 2
                'Must be at least %interval% between password changes.',
140 2
                ['%interval%' => $min]
141
            );
142
        }
143
144 3
        if (0 === Interval::compare(new DateInterval('PT0S'), $min)) {
145 1
            return $translator->trans(
146 1
                'Must be at most %interval% between password changes.',
147 1
                ['%interval%' => $max]
148
            );
149
        }
150
151 2
        if (Interval::compare($constraint->getMin(), $constraint->getMax()) === 0) {
152 1
            return $translator->trans(
153 1
                'Must be exactly %interval% between password changes.',
154 1
                ['%interval%' => $min]
155
            );
156
        }
157
158 1
        return $translator->trans(
159 1
            'Must be between %min% and %max% between password changes.',
160 1
            ['%min%' => $min, '%max%' => $max]
161
        );
162
    }
163
}
164