Test Failed
Push — master ( 6aac34...7ebad7 )
by Magnar Ovedal
02:23
created

Change::__construct()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
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 3
nop 2
dl 0
loc 11
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 Carbon\CarbonInterval;
12
use Stadly\Date\Interval;
13
use Stadly\PasswordPolice\Password;
14
use Stadly\PasswordPolice\Policy;
15
16
final class Change implements RuleInterface
17
{
18
    /**
19
     * @var DateInterval Minimum time between password changes.
20
     */
21
    private $min;
22
23
    /**
24
     * @var DateInterval|null Maximum time between password changes.
25
     */
26
    private $max;
27
28
    /**
29
     * @param DateInterval $min Minimum time between password changes.
30
     * @param DateInterval|null $max Maximum time between password changes.
31
     */
32 7
    public function __construct(DateInterval $min, ?DateInterval $max = null)
33
    {
34 7
        if (0 < Interval::compare(new DateInterval('PT0S'), $min)) {
35 2
            throw new InvalidArgumentException('Min cannot be negative.');
36 1
        }
37
        if ($max !== null && 0 < Interval::compare($min, $max)) {
38 1
            throw new InvalidArgumentException('Max cannot be smaller than min.');
39 1
        }
40
41
        $this->min = $min;
42 5
        $this->max = $max;
43 1
    }
44
45 4
    /**
46 1
     * @return DateInterval Minimum time between password changes.
47
     */
48
    public function getMin(): DateInterval
49
    {
50 4
        return $this->min;
51 4
    }
52 4
53
    /**
54
     * @return DateInterval|null Maximum time between password changes.
55
     */
56
    public function getMax(): ?DateInterval
57 1
    {
58
        return $this->max;
59 1
    }
60
61
    /**
62
     * Check whether a password is in compliance with the rule.
63
     *
64
     * @param Password|string $password Password to check.
65 1
     * @return bool Whether the password is in compliance with the rule.
66
     */
67 1
    public function test($password): bool
68
    {
69
        $date = $this->getNoncompliantDate($password);
70
71
        return $date === null;
72
    }
73
74
    /**
75
     * Enforce that a password is in compliance with the rule.
76 5
     *
77
     * @param Password|string $password Password that must adhere to the rule.
78 5
     * @throws RuleException If the password does not adhrere to the rule.
79
     */
80 5
    public function enforce($password): void
81
    {
82
        $date = $this->getNoncompliantDate($password);
83
84
        if ($date !== null) {
85
            throw new RuleException($this, $this->getMessage());
86
        }
87
    }
88
89 2
    /**
90
     * @param Password|string $password Password to check when was last changed.
91 2
     * @return DateTimeInterface|null When the password was last changed if not in compliance with the rule.
92
     */
93 2
    private function getNoncompliantDate($password): ?DateTimeInterface
94 1
    {
95
        $date = $this->getDate($password);
96 1
97
        if ($date !== null) {
98
            $now = new DateTimeImmutable();
99
            if ($now->sub($this->min) < $date) {
100
                return $date;
101
            }
102 7
103
            if ($this->max !== null && $date < $now->sub($this->max)) {
104 7
                return $date;
105
            }
106 7
        }
107 6
108 6
        return null;
109 2
    }
110
111
    /**
112 4
     * @param Password|string $password Password to check when was last changed.
113 1
     * @return DateTimeInterface|null When the password was last changed.
114
     */
115
    private function getDate($password): ?DateTimeInterface
116
    {
117 4
        if ($password instanceof Password) {
118
            $formerPasswords = $password->getFormerPasswords();
119
120
            if ($formerPasswords !== []) {
121
                return reset($formerPasswords)->getDate();
122
            }
123
        }
124 7
        return null;
125
    }
126 7
127 6
    /**
128
     * {@inheritDoc}
129 6
     */
130 6
    public function getMessage(): string
131
    {
132
        $translator = Policy::getTranslator();
133 1
        $locale = $translator->getLocale();
134
135
        $min = CarbonInterval::instance($this->min)->locale($locale);
136
        $max = $this->max === null ? null : CarbonInterval::instance($this->max)->locale($locale);
137
138
        if ($this->max === null) {
139 4
            return $translator->trans(
140
                'Must be at least %interval% between password changes.',
141 4
                ['%interval%' => $min]
142 4
            );
143
        }
144 4
145 4
        if (0 === Interval::compare(new DateInterval('PT0S'), $min)) {
146
            return $translator->trans(
147 4
                'Must be at most %interval% between password changes.',
148 1
                ['%interval%' => $max]
149 1
            );
150 1
        }
151
152
        if (Interval::compare($this->min, $this->max) === 0) {
153
            return $translator->trans(
154 3
                'Must be exactly %interval% between password changes.',
155 1
                ['%interval%' => $min]
156 1
            );
157 1
        }
158
159
        return $translator->trans(
160
            'Must be between %min% and %max% between password changes.',
161 2
            ['%min%' => $min, '%max%' => $max]
162 1
        );
163 1
    }
164
}
165