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

Pspell::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\WordList;
6
7
use ErrorException;
8
use InvalidArgumentException;
9
use RuntimeException;
10
use Stadly\PasswordPolice\WordFormatter;
11
use Stadly\PasswordPolice\WordList;
12
use Traversable;
13
14
final class Pspell implements WordList
15
{
16
    /**
17
     * @var int Pspell dictionary.
18
     */
19
    private $pspell;
20
21
    /**
22
     * @var WordFormatter[] Word formatters.
23
     */
24
    private $wordFormatters;
25
26
    /**
27
     * Pspell dictionaries are case-sensitive.
28
     * Specify word formatters if tests should also be performed for the word formatted to other cases.
29
     *
30
     * @param int $pspell Pspell dictionary link, as generated by `pspell_new` and friends.
31
     * @param WordFormatter ...$wordFormatters Word formatters.
32
     */
33 2
    public function __construct(int $pspell, WordFormatter ...$wordFormatters)
34
    {
35 2
        $this->pspell = $pspell;
36 2
        $this->wordFormatters = $wordFormatters;
37 2
    }
38
39
    /**
40
     * Pspell dictionaries are case-sensitive.
41
     * Specify word formatters if tests should also be performed for the word formatted to other cases.
42
     *
43
     * @param string $locale Locale of the pspell dictionary to load. For example `en-US` or `de`.
44
     * @param WordFormatter ...$wordFormatters Word formatters.
45
     * @throws RuntimeException If the pspell dictionary could not be loaded.
46
     * @return self Pspell word list.
47
     */
48 4
    public static function fromLocale(string $locale, WordFormatter ...$wordFormatters): self
49
    {
50 4
        if (preg_match('{^[a-z]{2}(?:[-_][A-Z]{2})?$}', $locale) !== 1) {
51 2
            throw new InvalidArgumentException(sprintf('%s is not a valid locale.', $locale));
52
        }
53
54 2
        set_error_handler([self::class, 'errorHandler']);
55
        try {
56 2
            $pspell = pspell_new($locale);
57 1
        } catch (ErrorException $exception) {
58 1
            throw new RuntimeException(
59 1
                'An error occurred while loading the word list: '.$exception->getMessage(),
60 1
                /*code*/0,
61 1
                $exception
62
            );
63 1
        } finally {
64 2
            restore_error_handler();
65
        }
66
67 1
        assert($pspell !== false);
68
69 1
        return new self($pspell, ...$wordFormatters);
70
    }
71
72
    /**
73
     * {@inheritDoc}
74
     */
75 7
    public function contains(string $word): bool
76
    {
77 7
        foreach ($this->getFormattedWords($word) as $wordVariant) {
78 7
            set_error_handler([self::class, 'errorHandler']);
79
            try {
80 7
                $check = pspell_check($this->pspell, $wordVariant);
0 ignored issues
show
Bug introduced by
It seems like $wordVariant can also be of type Traversable; however, parameter $word of Stadly\PasswordPolice\WordList\pspell_check() 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

80
                $check = pspell_check($this->pspell, /** @scrutinizer ignore-type */ $wordVariant);
Loading history...
81 2
            } catch (ErrorException $exception) {
82 2
                throw new RuntimeException(
83 2
                    'An error occurred while using the word list: '.$exception->getMessage(),
84 2
                    /*code*/0,
85 2
                    $exception
86
                );
87 5
            } finally {
88 7
                restore_error_handler();
89
            }
90
91 5
            if ($check) {
92 5
                return true;
93
            }
94
        }
95
96 3
        return false;
97
    }
98
99
    /**
100
     * @param string $word Word to format.
101
     * @return Traversable<string> Formatted words. May contain duplicates.
102
     */
103 7
    private function getFormattedWords(string $word): Traversable
104
    {
105 7
        yield $word;
106
107 3
        foreach ($this->wordFormatters as $wordFormatter) {
108 2
            yield from $wordFormatter->apply([$word]);
109
        }
110 3
    }
111
112
    /**
113
     * @throws ErrorException Error converted to an exception.
114
     */
115 4
    private static function errorHandler(int $severity, string $message, string $filename, int $line): void
116
    {
117 4
        throw new ErrorException($message, /*code*/0, $severity, $filename, $line);
118
    }
119
}
120