Completed
Push — master ( d9a437...4f5efd )
by Jared
06:32
created

Errors::getTranslator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
/**
4
 * @author Jared King <[email protected]>
5
 *
6
 * @see http://jaredtking.com
7
 *
8
 * @copyright 2015 Jared King
9
 * @license MIT
10
 */
11
12
namespace Pulsar;
13
14
use ArrayAccess;
15
use ArrayIterator;
16
use Countable;
17
use IteratorAggregate;
18
use Pulsar\Interfaces\TranslatorInterface;
19
20
/**
21
 * Holds error messages generated by models, like validation errors.
22
 */
23
class Errors implements IteratorAggregate, Countable, ArrayAccess
24
{
25
    /**
26
     * @var array
27
     */
28
    private static $messages = [
29
        'pulsar.validation.alpha' => '{{field_name}} only allows letters',
30
        'pulsar.validation.alpha_numeric' => '{{field_name}} only allows letters and numbers',
31
        'pulsar.validation.alpha_dash' => '{{field_name}} only allows letters and dashes',
32
        'pulsar.validation.boolean' => '{{field_name}} must be yes or no',
33
        'pulsar.validation.custom' => '{{field_name}} validation failed',
34
        'pulsar.validation.email' => '{{field_name}} must be a valid email address',
35
        'pulsar.validation.enum' => '{{field_name}} must be one of the allowed values',
36
        'pulsar.validation.date' => '{{field_name}} must be a date',
37
        'pulsar.validation.failed' => '{{field_name}} is invalid',
38
        'pulsar.validation.ip' => '{{field_name}} only allows valid IP addresses',
39
        'pulsar.validation.matching' => '{{field_name}} must match',
40
        'pulsar.validation.numeric' => '{{field_name}} only allows numbers',
41
        'pulsar.validation.password' => '{{field_name}} must meet the password requirements',
42
        'pulsar.validation.password_php' => '{{field_name}} must meet the password requirements',
43
        'pulsar.validation.range' => '{{field_name}} must be within the allowed range',
44
        'pulsar.validation.required' => '{{field_name}} is missing',
45
        'pulsar.validation.string' => '{{field_name}} must be a string of the proper length',
46
        'pulsar.validation.time_zone' => '{{field_name}} only allows valid time zones',
47
        'pulsar.validation.timestamp' => '{{field_name}} only allows timestamps',
48
        'pulsar.validation.unique' => 'The {{field_name}} you chose has already been taken. Please try a different {{field_name}}.',
49
        'pulsar.validation.url' => '{{field_name}} only allows valid URLs',
50
    ];
51
52
    /**
53
     * @var TranslatorInterface|null
54
     */
55
    private static $translator;
56
57
    /**
58
     * @var array
59
     */
60
    private $stack = [];
61
62
    /**
63
     * Sets the global translator instance.
64
     */
65
    public static function setTranslator(TranslatorInterface $translator)
66
    {
67
        self::$translator = $translator;
68
    }
69
70
    /**
71
     * Clears the global translator instance.
72
     */
73
    public static function clearTranslator(): void
74
    {
75
        self::$translator = null;
76
    }
77
78
    /**
79
     * Gets the translator.
80
     */
81
    public function getTranslator(): ?TranslatorInterface
82
    {
83
        return self::$translator;
84
    }
85
86
    /**
87
     * Adds an error message to the stack.
88
     *
89
     * @param $error
90
     *
91
     * @return $this
92
     */
93
    public function add($error, array $parameters = [])
94
    {
95
        $this->stack[] = [
96
            'error' => $error,
97
            'params' => $parameters,
98
        ];
99
100
        return $this;
101
    }
102
103
    /**
104
     * Gets all of the errors on the stack and also performs translation if enabled.
105
     *
106
     * @param string|bool $locale optional locale
107
     *
108
     * @return array error messages
109
     */
110
    public function all($locale = false): array
111
    {
112
        $messages = [];
113
        foreach ($this->stack as $error) {
114
            $messages[] = $this->parse($error['error'], $locale, $error['params']);
0 ignored issues
show
Bug introduced by
It seems like $locale defined by parameter $locale on line 110 can also be of type boolean; however, Pulsar\Errors::parse() does only seem to accept string|false, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
115
        }
116
117
        return $messages;
118
    }
119
120
    /**
121
     * Gets an error for a specific parameter on the stack.
122
     *
123
     * @param string $value value we are searching for
124
     * @param string $param parameter name
125
     */
126
    public function find($value, $param = 'field'): ?array
127
    {
128
        foreach ($this->stack as $error) {
129
            $stackValue = $error['params'][$param] ?? null;
130
            if ($stackValue !== $value) {
131
                continue;
132
            }
133
134
            if (!isset($error['message'])) {
135
                $error['message'] = $this->parse($error['error'], false, $error['params']);
136
            }
137
138
            return $error;
139
        }
140
141
        return null;
142
    }
143
144
    /**
145
     * Checks if an error exists with a specific parameter on the stack.
146
     *
147
     * @param string $value value we are searching for
148
     * @param string $param parameter name
149
     */
150
    public function has($value, $param = 'field'): bool
151
    {
152
        return null !== $this->find($value, $param);
153
    }
154
155
    /**
156
     * Clears the error stack.
157
     *
158
     * @return $this
159
     */
160
    public function clear()
161
    {
162
        $this->stack = [];
163
164
        return $this;
165
    }
166
167
    /**
168
     * Formats an incoming error message.
169
     *
170
     * @param array|string $error
171
     */
172
    private function sanitize($error): array
173
    {
174
        if (!is_array($error)) {
175
            $error = ['error' => $error];
176
        }
177
178
        if (!isset($error['params'])) {
179
            $error['params'] = [];
180
        }
181
182
        return $error;
183
    }
184
185
    /**
186
     * Parses an error message before displaying it.
187
     *
188
     * @param string       $error
189
     * @param string|false $locale
190
     */
191
    private function parse($error, $locale, array $parameters): string
192
    {
193
        // try to supply a fallback message in case
194
        // the user does not have one specified
195
        $fallback = self::$messages[$error] ?? null;
196
197
        if (!self::$translator) {
198
            return $error;
199
        }
200
201
        return $this->getTranslator()->translate($error, $parameters, $locale, $fallback);
0 ignored issues
show
Bug introduced by
It seems like $locale defined by parameter $locale on line 191 can also be of type false; however, Pulsar\Interfaces\TranslatorInterface::translate() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
202
    }
203
204
    //////////////////////////
205
    // IteratorAggregate Interface
206
    //////////////////////////
207
208
    public function getIterator()
209
    {
210
        return new ArrayIterator($this->stack);
211
    }
212
213
    //////////////////////////
214
    // Countable Interface
215
    //////////////////////////
216
217
    /**
218
     * Get total number of models matching query.
219
     *
220
     * @return int
221
     */
222
    public function count()
223
    {
224
        return count($this->stack);
225
    }
226
227
    /////////////////////////////
228
    // ArrayAccess Interface
229
    /////////////////////////////
230
231
    public function offsetExists($offset)
232
    {
233
        return isset($this->stack[$offset]);
234
    }
235
236
    public function offsetGet($offset)
237
    {
238
        if (!$this->offsetExists($offset)) {
239
            throw new \OutOfBoundsException("$offset does not exist on this ErrorStack");
240
        }
241
242
        $error = $this->stack[$offset];
243
        if (!isset($error['message'])) {
244
            $error['message'] = $this->parse($error['error'], false, $error['params']);
245
        }
246
247
        return $error;
248
    }
249
250
    public function offsetSet($offset, $error)
251
    {
252
        if (!is_numeric($offset)) {
253
            throw new \Exception('Can only perform set on numeric indices');
254
        }
255
256
        $this->stack[$offset] = $this->sanitize($error);
257
    }
258
259
    public function offsetUnset($offset)
260
    {
261
        unset($this->stack[$offset]);
262
    }
263
}
264