Passed
Push — master ( d57c21...5fe13c )
by Magnar Ovedal
02:42
created

Dictionary::getWordsToCheck()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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 RuntimeException;
8
use Stadly\PasswordPolice\Policy;
9
use Stadly\PasswordPolice\Rule;
10
use Stadly\PasswordPolice\ValidationError;
11
use Stadly\PasswordPolice\WordFormatter;
12
use Stadly\PasswordPolice\WordList;
13
use Traversable;
14
15
final class Dictionary implements Rule
16
{
17
    /**
18
     * @var WordList Word list for the dictionary.
19
     */
20
    private $wordList;
21
22
    /**
23
     * @var WordFormatter[] Word formatters.
24
     */
25
    private $wordFormatters;
26
27
    /**
28
     * @var int Constraint weight.
29
     */
30
    private $weight;
31
32
    /**
33
     * @param WordList $wordList Word list for the dictionary.
34
     * @param WordFormatter[] $wordFormatters Word formatters.
35
     * @param int $weight Constraint weight.
36
     */
37 1
    public function __construct(
38
        WordList $wordList,
39
        array $wordFormatters = [],
40
        int $weight = 1
41
    ) {
42 1
        $this->wordList = $wordList;
43 1
        $this->wordFormatters = $wordFormatters;
44 1
        $this->weight = $weight;
45 1
    }
46
47
    /**
48
     * @return WordList Word list for the dictionary.
49
     */
50 1
    public function getWordList(): WordList
51
    {
52 1
        return $this->wordList;
53
    }
54
55
    /**
56
     * {@inheritDoc}
57
     */
58 9
    public function test($password, ?int $weight = 1): bool
59
    {
60 9
        if ($weight !== null && $this->weight < $weight) {
61 1
            return true;
62
        }
63
64 8
        $word = $this->getDictionaryWord((string)$password);
65
66 7
        return $word === null;
67
    }
68
69
    /**
70
     * {@inheritDoc}
71
     */
72 2
    public function validate($password): ?ValidationError
73
    {
74 2
        $word = $this->getDictionaryWord((string)$password);
75
76 2
        if ($word !== null) {
77 1
            return new ValidationError(
78 1
                $this->getMessage($word),
79 1
                $password,
80 1
                $this,
81 1
                $this->weight
82
            );
83
        }
84
85 1
        return null;
86
    }
87
88
    /**
89
     * @param string $password Password to find dictionary words in.
90
     * @return string|null Dictionary word in the password.
91
     * @throws Exception If an error occurred.
92
     */
93 10
    private function getDictionaryWord(string $password): ?string
94
    {
95 10
        foreach ($this->getFormattedWords($password) as $word) {
96
            try {
97 10
                if ($this->wordList->contains($word)) {
0 ignored issues
show
Bug introduced by
It seems like $word can also be of type Traversable; however, parameter $word of Stadly\PasswordPolice\WordList::contains() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

97
                if ($this->wordList->contains(/** @scrutinizer ignore-type */ $word)) {
Loading history...
98 9
                    return $word;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $word could return the type Traversable which is incompatible with the type-hinted return null|string. Consider adding an additional type-check to rule them out.
Loading history...
99
                }
100 1
            } catch (RuntimeException $exception) {
101 1
                throw new Exception(
102 1
                    $this,
103 1
                    'An error occurred while using the word list: '.$exception->getMessage(),
104 8
                    $exception
105
                );
106
            }
107
        }
108 7
        return null;
109
    }
110
111
    /**
112
     * @param string $word Word to format.
113
     * @return Traversable<string> Formatted words. May contain duplicates.
114
     */
115 10
    private function getFormattedWords(string $word): Traversable
116
    {
117 10
        yield $word;
118
119 7
        foreach ($this->wordFormatters as $wordFormatter) {
120 2
            yield from $wordFormatter->apply([$word]);
121
        }
122 7
    }
123
124
    /**
125
     * @param string $word Word that violates the constraint.
126
     * @return string Message explaining the violation.
127
     */
128 1
    private function getMessage(string $word): string
0 ignored issues
show
Unused Code introduced by
The parameter $word 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

128
    private function getMessage(/** @scrutinizer ignore-unused */ string $word): 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...
129
    {
130 1
        $translator = Policy::getTranslator();
131
132 1
        return $translator->trans(
133 1
            'Must not contain dictionary words.'
134
        );
135
    }
136
}
137