Completed
Push — master ( 0be9f6...b3ed5a )
by Jared
01:25
created

Errors::sanitize()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 3
nc 4
nop 1
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 Infuse\Locale;
18
use IteratorAggregate;
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 Locale
54
     */
55
    private static $globalLocale;
56
57
    /**
58
     * @var array
59
     */
60
    private $stack = [];
61
62
    /**
63
     * @var Locale
64
     */
65
    private $locale;
66
67
    /**
68
     * Sets the global locale instance.
69
     *
70
     * @param Locale $locale
71
     */
72
    public static function setGlobalLocale(Locale $locale)
73
    {
74
        self::$globalLocale = $locale;
75
    }
76
77
    /**
78
     * Sets the locale service.
79
     *
80
     * @param Locale $locale
81
     *
82
     * @return $this
83
     */
84
    public function setLocale(Locale $locale)
85
    {
86
        $this->locale = $locale;
87
88
        return $this;
89
    }
90
91
    /**
92
     * Gets the locale service.
93
     *
94
     * @return Locale
95
     */
96
    public function getLocale()
97
    {
98
        if (!$this->locale) {
99
            if (self::$globalLocale) {
100
                return self::$globalLocale;
101
            }
102
103
            $this->locale = new Locale();
104
        }
105
106
        return $this->locale;
107
    }
108
109
    /**
110
     * Adds an error message to the stack.
111
     *
112
     * @param $error
113
     * @param array $parameters
114
     *
115
     * @return $this
116
     */
117
    public function add($error, array $parameters = [])
118
    {
119
        $this->stack[] = [
120
            'error' => $error,
121
            'params' => $parameters,
122
        ];
123
124
        return $this;
125
    }
126
127
    /**
128
     * Gets all of the errors on the stack and also attempts
129
     * translation using the Locale class.
130
     *
131
     * @param string|bool $locale optional locale
132
     *
133
     * @return array error messages
134
     */
135
    public function all($locale = false)
136
    {
137
        $messages = [];
138
        foreach ($this->stack as $error) {
139
            $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 135 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...
140
        }
141
142
        return $messages;
143
    }
144
145
    /**
146
     * Gets an error for a specific parameter on the stack.
147
     *
148
     * @param string $value value we are searching for
149
     * @param string $param parameter name
150
     *
151
     * @return array|null
152
     */
153
    public function find($value, $param = 'field'): ?array
154
    {
155
        foreach ($this->stack as $error) {
156
            if (array_value($error['params'], $param) !== $value) {
157
                continue;
158
            }
159
160
            if (!isset($error['message'])) {
161
                $error['message'] = $this->parse($error['error'], false, $error['params']);
162
            }
163
164
            return $error;
165
        }
166
167
        return null;
168
    }
169
170
    /**
171
     * Checks if an error exists with a specific parameter on the stack.
172
     *
173
     * @param string $value value we are searching for
174
     * @param string $param parameter name
175
     *
176
     * @return bool
177
     */
178
    public function has($value, $param = 'field')
179
    {
180
        return null !== $this->find($value, $param);
181
    }
182
183
    /**
184
     * Clears the error stack.
185
     *
186
     * @return $this
187
     */
188
    public function clear()
189
    {
190
        $this->stack = [];
191
192
        return $this;
193
    }
194
195
    /**
196
     * Formats an incoming error message.
197
     *
198
     * @param array|string $error
199
     *
200
     * @return array
201
     */
202
    private function sanitize($error)
203
    {
204
        if (!is_array($error)) {
205
            $error = ['error' => $error];
206
        }
207
208
        if (!isset($error['params'])) {
209
            $error['params'] = [];
210
        }
211
212
        return $error;
213
    }
214
215
    /**
216
     * Parses an error message before displaying it.
217
     *
218
     * @param string       $error
219
     * @param string|false $locale
220
     * @param array        $parameters
221
     *
222
     * @return string
223
     */
224
    private function parse($error, $locale, array $parameters)
225
    {
226
        // try to supply a fallback message in case
227
        // the user does not have one specified
228
        $fallback = array_value(self::$messages, $error);
229
230
        return $this->getLocale()->t($error, $parameters, $locale, $fallback);
231
    }
232
233
    //////////////////////////
234
    // IteratorAggregate Interface
235
    //////////////////////////
236
237
    public function getIterator()
238
    {
239
        return new ArrayIterator($this->stack);
240
    }
241
242
    //////////////////////////
243
    // Countable Interface
244
    //////////////////////////
245
246
    /**
247
     * Get total number of models matching query.
248
     *
249
     * @return int
250
     */
251
    public function count()
252
    {
253
        return count($this->stack);
254
    }
255
256
    /////////////////////////////
257
    // ArrayAccess Interface
258
    /////////////////////////////
259
260
    public function offsetExists($offset)
261
    {
262
        return isset($this->stack[$offset]);
263
    }
264
265
    public function offsetGet($offset)
266
    {
267
        if (!$this->offsetExists($offset)) {
268
            throw new \OutOfBoundsException("$offset does not exist on this ErrorStack");
269
        }
270
271
        $error = $this->stack[$offset];
272
        if (!isset($error['message'])) {
273
            $error['message'] = $this->parse($error['error'], false, $error['params']);
274
        }
275
276
        return $error;
277
    }
278
279
    public function offsetSet($offset, $error)
280
    {
281
        if (!is_numeric($offset)) {
282
            throw new \Exception('Can only perform set on numeric indices');
283
        }
284
285
        $this->stack[$offset] = $this->sanitize($error);
286
    }
287
288
    public function offsetUnset($offset)
289
    {
290
        unset($this->stack[$offset]);
291
    }
292
}
293