Issues (164)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Validator/Component.php (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace BootPress\Validator;
4
5
use BootPress\Page\Component as Page;
6
7
class Component
8
{
9
    
10
    /**
11
     * Custom validation rules that you would like to apply.
12
     * 
13
     * @var callable[]
14
     */
15
    public $rules = array();
16
    
17
    
18
    /**
19
     * Before you check if ``$this->certified()``, these are the error messages associated with each validation rule.  After ``$this->certified()``, these are all the errors we encountered (if any).  You can customize and add as you see fit.
20
     * 
21
     * @var string[]
22
     */
23
    public $errors = array(
24
        'remote' => 'Please fix this field.',
25
        'required' => 'This field is required.',
26
        'equalTo' => 'Please enter the same value again.',
27
        'notEqualTo' => 'Please enter a different value, values must not be the same.',
28
        'number' => 'Please enter a valid number.',
29
        'integer' => 'A positive or negative non-decimal number please.',
30
        'digits' => 'Please enter only digits.',
31
        'min' => 'Please enter a value greater than or equal to {0}.',
32
        'max' => 'Please enter a value less than or equal to {0}.',
33
        'range' => 'Please enter a value between {0} and {1}.',
34
        'alphaNumeric' => 'Letters, numbers, and underscores only please.',
35
        'minLength' => 'Please enter at least {0} characters.',
36
        'maxLength' => 'Please enter no more than {0} characters.',
37
        'rangeLength' => 'Please enter a value between {0} and {1} characters long.',
38
        'minWords' => 'Please enter at least {0} words.',
39
        'maxWords' => 'Please enter {0} words or less.',
40
        'rangeWords' => 'Please enter between {0} and {1} words.',
41
        'pattern' => 'Invalid format.',
42
        'date' => 'Please enter a valid date.',
43
        'email' => 'Please enter a valid email address.',
44
        'url' => 'Please enter a valid URL.',
45
        'ipv4' => 'Please enter a valid IP v4 address.',
46
        'ipv6' => 'Please enter a valid IP v6 address.',
47
        'inList' => 'Please make a valid selection.',
48
        'noWhiteSpace' => 'No white space please.',
49
    );
50
    
51
    /**
52
     * This is where we save all of the information ``$this->set()``ed for each field.
53
     * 
54
     * @var array
55
     */
56
    protected $data = array();
57
    
58
    /**
59
     * These are the user submitted values for each field.
60
     * 
61
     * @var array
62
     */
63
    protected $values = array();
64
    
65
    /**
66
     * Whether the form has been submitted or not.  Null if we don't know.
67
     * 
68
     * @var null|bool
69
     */
70
    protected $submitted = null;
71
    
72
    /**
73
     * Either false or an array of all the submitted values.
74
     * 
75
     * @var false|array
76
     */
77
    protected $certified = false;
78
    
79
    /**
80
     * The rules we reserve for validation until the end.
81
     * 
82
     * @var string[]
83
     */
84
    protected $reserved = array('default', 'required', 'equalTo', 'notEqualTo');
85
    
86
    /**
87
     * The rules we define in-house.
88
     * 
89
     * @var string[]
90
     */
91
    protected $methods = array('number', 'integer', 'digits', 'min', 'max', 'range', 'alphaNumeric', 'minLength', 'maxLength', 'rangeLength', 'minWords', 'maxWords', 'rangeWords', 'pattern', 'date', 'email', 'url', 'ipv4', 'ipv6', 'inList', 'yesNo', 'trueFalse', 'noWhiteSpace', 'singleSpace');
92
    
93
    /**
94
     * So that we have something to work with no matter what happens to ``$this->errors`` (anything can happen) public property.
95
     * 
96
     * @var string[]
97
     */
98
    protected $default_errors = array();
99
100
    /**
101
     * Creates a BootPress\Validator\Component object.
102
     * 
103
     * @param array $values The user submitted variables you want to validate.
104
     * 
105
     * ```php
106
     * $validator = new Validator($_POST);
107
     * ```
108
     */
109 19
    public function __construct(array $values = array())
110
    {
111 19
        $this->values = $values;
112 19
        $this->default_errors = $this->errors;
113 19
    }
114
115
    /**
116
     * This allows you to set individually (or all at once) the validation rules and filters for each form field.  The value of every $field you set here is automatically ``trim()``ed and returned when ``$this->submittted()``.
117
     * 
118
     * @param string $field The name of your form field.  If this is an ``array($field => $rules, $field, ...)`` then we loop through each one and call this method ourselves over and over.
119
     * 
120
     * Your $field names can be an array by adding brackets to the end ie. 'name[]'.  They can also be multi-dimensional arrays such as 'name[first]', or 'name[players][]', or 'name[parent][child]', etc.  The important thing to remember is that you must always use the exact name given here when referencing them in other methods.
121
     * 
122
     * @param string|array $rules A pipe delimited set (or an array) of rules to validate and filter the $field with.  You can also specify custom messages by making this an ``array($rule => $message, ...)``.  Parameters are comma-delimited, and placed within '**[]**' two brackets.  The available options are:
123
     *
124
     * - '**remote[rule]**' - Set ``$form->rules['rule'] = function($value){}`` to determine the validity of a submitted value.  The function should return a boolean true or false.
125
     * - '**default**' - A default value if the field is empty, or not even set.
126
     * - '**required**' - This field must have a value, and cannot be empty.
127
     * - '**equalTo[field]**' - Must match the same value as contained in the other form field.
128
     * - '**notEqualTo[field]**' - Must NOT match the same value as contained in the other form field.
129
     * - Numbers:
130
     *   - '**number**' - Must be a valid decimal number, positive or negative, integer or float, commas okay.  Defaults to 0.
131
     *   - '**integer**' - Must be a postive or negative integer number, no commas.  Defaults to 0.
132
     *   - '**digits**' - Must be a positive integer number, no commas.  Defaults to 0.
133
     *   - '**min[number]**' - Must be greater than or equal to [number].
134
     *   - '**max[number]**' - Must be less than or equal to [number].
135
     *   - '**range[min, max]**' - Must be greater than or equal to [min], and less than or equal to [max].
136
     * - Strings:
137
     *   - '**alphaNumeric**' - Alpha (a-z), numeric (0-9), and underscore (_) characters only.
138
     *   - '**minLength[integer]**' - String length must be greater than or equal to [integer].
139
     *   - '**maxLength[integer]**' - String length must be less than or equal to [integer].
140
     *   - '**rangeLength[minLength, maxLength]**' - String length must be greater than or equal to [minLength], and less than or equal to [maxLength].
141
     *   - '**minWords[integer]**' - Number of words must be greater than or equal to [integer].
142
     *   - '**maxWords[integer]**' - Number of words must be less than or equal to [integer].
143
     *   - '**rangeWords[minWords, maxWords]**' - Number of words must be greater than or equal to [minWords], and less than or equal to [maxWords].
144
     *   - '**pattern[regex]**' - Must match the supplied [ECMA Javascript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) compatible [regex].
145
     *   - '**date**' - Must be a valid looking date.  No particular format is enforced.
146
     *   - '**email**' - Must be a valid looking email.
147
     *   - '**url**' - Must be a valid looking url.
148
     *   - '**ipv4**' - Must be a valid looking ipv4 address.
149
     *   - '**ipv6**' - Must be a valid looking ipv6 address.
150
     *   - '**inList[1,2,3]**' - Must be one of a comma-separated list of acceptable values.
151
     *   - '**noWhiteSpace**' - Must contain no white space.
152
     * - Filters:
153
     *   - '**singleSpace**' - Removes any doubled-up whitespace so that you only have single spaces between words.
154
     *   - '**trueFalse**' - Returns a **1** (true) or **0** (false) integer.
155
     *   - '**yesNo**' - Returns a '**Y**' or '**N**' value.
156
     * 
157
     * ```php
158
     * $validator->set('name', 'required');
159
     * 
160
     * $validator->set('email', 'required|email');
161
     * 
162
     * $validator->set(array(
163
     *     'password' => 'required|alphaNumeric|minLength[5]|noWhiteSpace',
164
     *     'confim' => array('required', 'matches[password]'),
165
     * ));
166
     * 
167
     * $validator->set('field', array('required' => 'Do this or else.'));
168
     * ```
169
     */
170 11
    public function set($field, $rules = '')
171
    {
172 11
        if (is_array($field)) {
173 1
            foreach ($field as $name => $rules) {
174 1
                if (is_numeric($name) && is_string($rules)) {
175 1
                    $this->set($rules);
176 1
                } else {
177 1
                    $this->set($name, $rules);
178
                }
179 1
            }
180
181 1
            return;
182
        }
183 11
        $page = Page::html();
184 11
        $process = (is_array($rules)) ? $rules : array_map('trim', explode('|', $rules));
185 11
        $custom = array();
186 11
        $rules = array();
187 11
        foreach ($process as $rule => $message) {
188 11
            if (is_numeric($rule)) {
189 7
                $rule = $message;
190 7
                $message = false;
191 7
            }
192 11
            $param = true;
193 11
            if (preg_match("/(.*?)\[(.*)\]/", $rule, $match)) {
194 4
                $rule = $match[1];
195 4
                $param = $match[2];
196 4
                if (strpos($param, ',') !== false) {
197 4
                    $param = array_map('trim', explode(',', $param));
198 4
                }
199 4
            }
200 11
            if ($message) {
201 5
                $custom[$rule] = $message;
202 5
            }
203 11
            $rules[$rule] = $param;
204 11
        }
205 11
        $validate = array();
206 11
        foreach ($rules as $rule => $param) {
207
            switch ($rule) {
208 11
                case 'remote':
209 1
                    if ($value = $page->get($this->base($field))) {
210 1
                        return $page->sendJson($this->remote($value, $param));
211
                    }
212 1
                    $validate['remote'] = $page->url();
213 1
                    break;
214 11
                case 'required':
215 9
                    $validate['required'] = 'true';
216 9
                    break;
217 5
                case 'equalTo':
218 1
                    $validate['equalTo'] = '#'.$this->id($param);
219 1
                    break;
220 5
                case 'notEqualTo':
221 1
                    $validate['notEqualTo'] = '#'.$this->id($param);
222 1
                    break;
223 5
                case 'number':
224 1
                    $validate['number'] = 'true';
225 1
                    $rules['default'] = 0;
226 1
                    break;
227 5
                case 'integer':
228 1
                    $validate['integer'] = 'true';
229 1
                    $rules['default'] = 0;
230 1
                    break;
231 5
                case 'digits':
232 1
                    $validate['digits'] = 'true';
233 1
                    $rules['default'] = 0;
234 1
                    break;
235 5
                case 'min':
236 1
                    $validate['min'] = $param;
237 1
                    break;
238 5
                case 'max':
239 1
                    $validate['max'] = $param;
240 1
                    break;
241 5
                case 'range':
242 1
                    $validate['range'] = $param;
243 1
                    break;
244 5
                case 'alphaNumeric':
245 1
                    $validate['alphanumeric'] = 'true';
246 1
                    break;
247 5
                case 'minLength':
248 1
                    $validate['minlength'] = $param;
249 1
                    break;
250 5
                case 'maxLength':
251 1
                    $validate['maxlength'] = $param;
252 1
                    break;
253 5
                case 'rangeLength':
254 1
                    $validate['rangelength'] = $param;
255 1
                    break;
256 5
                case 'minWords':
257 1
                    $validate['minWords'] = $param;
258 1
                    break;
259 5
                case 'maxWords':
260 1
                    $validate['maxWords'] = $param;
261 1
                    break;
262 5
                case 'rangeWords':
263 1
                    $validate['rangeWords'] = $param;
264 1
                    break;
265 5
                case 'pattern':
266 1
                    $validate['pattern'] = $param;
267 1
                    break; // must use javascript ecma regex
268 5
                case 'date':
269 1
                    $validate['date'] = 'true';
270 1
                    break;
271 5
                case 'email':
272 1
                    $validate['email'] = 'true';
273 1
                    break;
274 5
                case 'url':
275 1
                    $validate['url'] = 'true';
276 1
                    break;
277 5
                case 'ipv4':
278 1
                    $validate['ipv4'] = 'true';
279 1
                    break;
280 5
                case 'ipv6':
281 1
                    $validate['ipv6'] = 'true';
282 1
                    break;
283 5
                case 'inList':
284 4
                    if (!is_array($param)) {
285 1
                        $param = array($param);
286 1
                        $rules[$rule] = $param;
287 1
                    }
288 4
                    $validate['inList'] = implode(',', $param);
289
                    // json string arrays do not play nicely with data-rule-... attributes
290 4
                    $page->jquery('jQuery.validator.addMethod("inList", function(value, element, params) { return this.optional(element) || $.inArray(value, params.split(",")) !== -1; }, "Please make a valid selection.");');
291 4
                    break;
292 3
                case 'noWhiteSpace':
293 1
                    $validate['nowhitespace'] = 'true';
294 1
                    break;
295 3
                case 'trueFalse':
296 1
                    $rules['default'] = 0;
297 1
                    break;
298 3
                case 'yesNo':
299 3
                    $rules['default'] = 'N';
300 3
                    break;
301
            }
302 11
        }
303 11
        foreach ($validate as $rule => $param) {
304 10
            if (is_array($param)) {
305 1
                $validate[$rule] = implode(',', $param);
306 1
            }
307 11
        }
308 11
        $messages = array();
309 11
        foreach ($rules as $rule => $param) {
310 11
            if (isset($custom[$rule]) || isset($this->errors[$rule])) {
311 10
                $error = (isset($custom[$rule])) ? $custom[$rule] : $this->errors[$rule];
312 10
                if (!isset($this->default_errors[$rule]) || $this->default_errors[$rule] != $error) {
313 5
                    $messages[$rule] = $error;
314 5
                }
315 10
            }
316 11
        }
317 11
        $indexes = array();
318 11
        sscanf($field, '%[^[][', $indexes[0]);
319 11
        $value = null;
320 11
        if ((bool) preg_match_all('/\[(.*?)\]/', $field, $matches)) {
321 2
            foreach ($matches[1] as $key) {
322 2
                if ($key !== '') {
323 1
                    $indexes[] = $key;
324 1
                }
325 2
            }
326 2
            $value = $this->reduce($this->values, $indexes);
327 11
        } elseif (isset($this->values[$field])) {
328 1
            $value = $this->values[$field];
329 1
        }
330 11
        $error = null;
331 11
        if (!is_null($value)) {
332 1
            $value = (is_array($value)) ? array_map('trim', $value) : trim($value);
333 1
            foreach ($rules as $rule => $param) {
334 1
                list($value, $error) = $this->validate($value, $rule, $param);
335 1
                if (empty($value) || !empty($error)) {
336 1
                    break;
337
                }
338 1
            }
339 1
        }
340 11
        $this->data[$field] = array(
341 11
            'id' => '#'.$this->id($field),
342 11
            'field' => $indexes,
343 11
            'rules' => $rules,
344 11
            'validate' => (!empty($validate)) ? $validate : null,
345 11
            'messages' => (!empty($messages)) ? $messages : null,
346 11
            'value' => $value,
347 11
            'error' => $error,
348
        );
349 11
    }
350
351
    /**
352
     * This method goes through all of the fields you set above, determines if the form has been sent, and picks out any errors.
353
     * 
354
     * @return false|array Returns an array of validated and filtered form values for every ``$validator->set('field')`` IF the form was submitted (ie. at least one field has it's $_GET or $_POST counterpart), AND there were no errors.
355
     * 
356
     * ```php
357
     * if ($vars = $validator->certified()) {
358
     *     // process $vars
359
     * }
360
     * ```
361
     */
362 3
    public function certified()
363
    {
364 3
        if (!is_null($this->submitted)) {
365 1
            return $this->certified; // so we don't overwrite error messages
366
        }
367 3
        $errors = array();
368 3
        $this->values = array();
369 3
        $this->submitted = false;
370 3
        foreach ($this->data as $field => $data) {
371 2
            if (!is_null($data['value'])) {
372 1
                $this->submitted = true;
373 1
            }
374 2
            if (!empty($data['value'])) {
375 1
                $this->values[$field] = $data['value'];
376 2
            } elseif (strpos($field, '[]') !== false) {
377 1
                $this->values[$field] = array();
378 1
            } else {
379 2
                $this->values[$field] = (isset($data['rules']['default'])) ? $data['rules']['default'] : '';
380
            }
381 2
            if (!is_null($data['error'])) {
382 1
                $errors[$field] = $data['error'];
383 1
            }
384 3
        }
385 3
        if ($this->submitted) {
386 1
            $submitted = array();
387 1
            foreach ($this->data as $field => $data) {
388 1
                $value =& $submitted;
389 1
                foreach ($data['field'] as $index) {
390 1
                    $value =& $value[$index];
391 1
                }
392 1
                $value = $this->values[$field];
393 1
                if (isset($data['rules']['required']) && empty($value)) {
394 1
                    $errors[$field] = $this->errorMessage('required');
395 1
                } elseif (!isset($errors[$field])) {
396 1
                    if (isset($data['rules']['equalTo']) && $value != $this->value($data['rules']['equalTo'])) {
397 1
                        $errors[$field] = $this->errorMessage('equalTo');
398 1
                    } elseif (isset($data['rules']['notEqualTo']) && $value == $this->value($data['rules']['notEqualTo'])) {
399 1
                        $errors[$field] = $this->errorMessage('notEqualTo');
400 1
                    }
401 1
                }
402 1
            }
403 1
            $this->certified = (!empty($errors)) ? false : $submitted;
404 1
        }
405 3
        $this->errors = $errors;
406
407 3
        return $this->certified;
408
    }
409
    
410
    /**
411
     * Allows you to know if a form $field has been required, or not.
412
     * 
413
     * @param string $field 
414
     * 
415
     * @return bool  Whether the field is required or not.
416
     */
417 1
    public function required($field)
418
    {
419 1
        return (isset($this->data[$field]['rules']['required'])) ? true : false;
420
    }
421
    
422
    /**
423
     * Returns the submitted value of the $field that should be used when displaying the form.
424
     * 
425
     * The array feature comes in handy when you want to save the values to your database.
426
     * 
427
     * @param string|array $field 
428
     * 
429
     * @return mixed
430
     */
431 10
    public function value($field)
432
    {
433 10
        if (is_array($field)) {
434 1
            $values = array();
435 1
            foreach ($field as $key => $value) {
436 1
                $values[$key] = $this->value($value);
437 1
            }
438
439 1
            return $values;
440
        }
441
        
442 10
        return ($this->submitted && isset($this->values[$field])) ? $this->values[$field] : null;
443
    }
444
445
    /**
446
     * Returns an error message (if any) that should be used when displaying the form.
447
     * 
448
     * @param string $field 
449
     * 
450
     * @return null|string
451
     */
452 2
    public function error($field)
453
    {
454 2
        return ($this->submitted && isset($this->errors[$field])) ? $this->errors[$field] : null;
455
    }
456
457
    /**
458
     * Returns all of the rules set up for the $field.
459
     * 
460
     * @param string $field 
461
     * 
462
     * @return string[]
463
     * 
464
     * ```php
465
     * foreach ($validator->rules($field) as $validate => $param) {
466
     *     $attributes["data-rule-{$validate}"] = htmlspecialchars($param);
467
     * }
468
     * ```
469
     */
470 11
    public function rules($field)
471
    {
472 11
        return (isset($this->data[$field]) && $validate = $this->data[$field]['validate']) ? $validate : array();
0 ignored issues
show
The variable $validate does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
473
    }
474
475
    /**
476
     * Returns a $field's rules and associated error messages.
477
     * 
478
     * @param string $field 
479
     * 
480
     * @return string[]
481
     * 
482
     * ```php
483
     * foreach ($validator->messages($field) as $rule => $message) {
484
     *     $attributes["data-msg-{$rule}"] = htmlspecialchars($message);
485
     * }
486
     * ```
487
     */
488 11
    public function messages($field)
489
    {
490 11
        return (isset($this->data[$field]) && $messages = $this->data[$field]['messages']) ? $messages : array();
0 ignored issues
show
The variable $messages does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
491
    }
492
493
    /**
494
     * Includes Jörn's jQuery Validation code that this component was meant to sync perfectly with.
495
     * 
496
     * @param string   $form    The jQuery identifier of your form.
497
     * @param string[] $options The rules and custom messages should be added to each inputs data-... attributes using ``$this->rules()`` and ``$this->messages()``.  Any other fine-tuning can be done here.  The $options values must be pre json encoded ie. quotes around strings ('"string"'), brackets for arrays ('[]'), quoted bools ('false').  The reason for this is because we cannot json_encode functions properly ('function(){}').
498
     * 
499
     * ```php
500
     * $validator->jquery('#form', array('debug'=>'true'));
501
     * ```
502
     * 
503
     * @see https://jqueryvalidation.org/validate/
504
     */
505 2
    public function jquery($form, array $options = array())
506
    {
507 2
        $page = Page::html();
508 2
        foreach ($options as $key => $value) {
509 1
            $options[$key] = $key.':'.$value;
510 2
        }
511 2
        $page->jquery('$("'.$form.'").validate({'.implode(', ', $options).'});');
512 2
        $page->link('https://cdn.jsdelivr.net/jquery.validation/1.15.0/jquery.validate.min.js');
513 2
        $page->link('https://cdn.jsdelivr.net/jquery.validation/1.15.0/additional-methods.min.js');
514 2
    }
515
516
    /**
517
     * Returns the unique id assigned to the $field.
518
     * 
519
     * @param string $field 
520
     * 
521
     * @return string
522
     */
523 11
    public function id($field)
524
    {
525 11
        static $ids = array();
526 11
        if (!isset($ids[$field])) {
527 4
            $ids[$field] = Page::html()->id($this->base($field));
528 4
        }
529
530 11
        return $ids[$field];
531
    }
532
533
    /**
534
     * Determines if the $value is a valid decimal number.  Can be positive or negative, integer or float, and commas to separate thousandths are okay.
535
     * 
536
     * @param string $value
537
     * 
538
     * @return bool
539
     */
540 1
    public static function number($value)
541
    {
542 1
        return (bool) preg_match('/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/', (string) $value);
543
    }
544
545
    /**
546
     * Deterimes if the $value is a positive or negative integer number, no commas.
547
     * 
548
     * @param string $value
549
     * 
550
     * @return bool
551
     */
552 1
    public static function integer($value)
553
    {
554 1
        return (bool) preg_match('/^-?\d+$/', $value);
555
    }
556
557
    /**
558
     * Deterimes if the $value is a positive integer number, no commas.
559
     * 
560
     * @param string $value
561
     * 
562
     * @return bool
563
     */
564 1
    public static function digits($value)
565
    {
566 1
        return (bool) preg_match('/^\d+$/', $value);
567
    }
568
569
    /**
570
     * Determines if the $value is greater than or equal to $param.
571
     * 
572
     * @param float $value 
573
     * @param float $param 
574
     * 
575
     * @return bool
576
     */
577 2
    public static function min($value, $param)
578
    {
579 2
        return is_numeric($value) ? ($value >= $param) : false;
580
    }
581
582
    /**
583
     * Determines if the $value is less than or equal to $param.
584
     * 
585
     * @param float $value 
586
     * @param float $param 
587
     * 
588
     * @return bool
589
     */
590 1
    public static function max($value, $param)
591
    {
592 1
        return is_numeric($value) ? ($value <= $param) : false;
593
    }
594
595
    /**
596
     * Determines if the $value is greater than or equal to $param[0], and less than or equal to $param[1].
597
     * 
598
     * @param float   $value 
599
     * @param float[] $param 
600
     * 
601
     * @return bool
602
     */
603 1
    public static function range($value, array $param)
604
    {
605 1
        return is_numeric($value) ? ($value >= $param[0] && $value <= $param[1]) : false;
606
    }
607
608
    /**
609
     * Deterimes if the $value has alpha (a-z), numeric (0-9), and underscore (_) characters only.
610
     * 
611
     * @param string $value
612
     * 
613
     * @return bool
614
     */
615 1
    public static function alphaNumeric($value)
616
    {
617 1
        return (bool) preg_match('/^\w+$/', $value);
618
    }
619
620
    /**
621
     * Determines if the $value's length is greater than or equal to $param.
622
     * 
623
     * @param string $value 
624
     * @param int    $param 
625
     * 
626
     * @return bool
627
     */
628 2
    public static function minLength($value, $param)
629
    {
630 2
        $length = is_array($value) ? count($value) : mb_strlen($value);
631
632 2
        return $length >= $param;
633
    }
634
635
    /**
636
     * Determines if the $value's length is less than or equal to $param.
637
     * 
638
     * @param string $value 
639
     * @param int    $param 
640
     * 
641
     * @return bool
642
     */
643 1
    public static function maxLength($value, $param)
644
    {
645 1
        $length = is_array($value) ? count($value) : mb_strlen($value);
646
647 1
        return $length <= $param;
648
    }
649
650
    /**
651
     * Determines if the $value's length is greater than or equal to $param[0], and less than or equal to $param[1].
652
     * 
653
     * @param string $value 
654
     * @param int[]  $param 
655
     * 
656
     * @return bool
657
     */
658 1
    public static function rangeLength($value, array $param)
659
    {
660 1
        $length = is_array($value) ? count($value) : mb_strlen($value);
661
662 1
        return $length >= $param[0] && $length <= $param[1];
663
    }
664
665
    /**
666
     * Determines if the number of $value's words are greater than or equal to $param.
667
     * 
668
     * @param string $value 
669
     * @param int    $param 
670
     * 
671
     * @return bool
672
     */
673 1
    public static function minWords($value, $param)
674
    {
675 1
        $count = self::numWords($value);
676
677 1
        return $count >= $param;
678
    }
679
680
    /**
681
     * Determines if the number of $value's words are less than or equal to $param.
682
     * 
683
     * @param string $value 
684
     * @param int    $param 
685
     * 
686
     * @return bool
687
     */
688 1
    public static function maxWords($value, $param)
689
    {
690 1
        $count = self::numWords($value);
691
692 1
        return $count <= $param;
693
    }
694
695
    /**
696
     * Determines if the number of $value's words are greater than or equal to $param[0], and less than or equal to $param[1].
697
     * 
698
     * @param string $value 
699
     * @param int[]  $param 
700
     * 
701
     * @return bool
702
     */
703 1
    public static function rangeWords($value, array $param)
704
    {
705 1
        $count = self::numWords($value);
706
707 1
        return $count >= $param[0] && $count <= $param[1];
708
    }
709
710
    /**
711
     * Determines if the $value matches the supplied regex ($param).
712
     * 
713
     * @param string $value 
714
     * @param string $param 
715
     * 
716
     * @return bool
717
     */
718 1
    public static function pattern($value, $param)
719
    {
720 1
        return (bool) preg_match($param, $value);
721
    }
722
723
    /**
724
     * Determines if the $value is a parseable date.
725
     * 
726
     * @param string $value 
727
     * 
728
     * @return bool
729
     */
730 1
    public static function date($value)
731
    {
732 1
        return (bool) strtotime($value);
733
    }
734
735
    /**
736
     * Determines if the $value is a valid looking email.
737
     * 
738
     * @param string $value 
739
     * 
740
     * @return bool
741
     */
742 2
    public static function email($value)
743
    {
744 2
        return (bool) preg_match('/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/', $value);
745
    }
746
747
    /**
748
     * Determines if the $value is a valid looking url.
749
     * 
750
     * @param string $value 
751
     * 
752
     * @return bool
753
     */
754 1
    public static function url($value)
755
    {
756 1
        return (bool) preg_match('_^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$_iuS', $value);
757
    }
758
759
    /**
760
     * Determines if the $value is a valid looking ipv4 address.
761
     * 
762
     * @param string $value 
763
     * 
764
     * @return bool
765
     */
766 1
    public static function ipv4($value)
767
    {
768 1
        return (bool) preg_match('/^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i', $value);
769
    }
770
771
    /**
772
     * Determines if the $value is a valid looking ipv6 address.
773
     * 
774
     * @param string $value 
775
     * 
776
     * @return bool
777
     */
778 1
    public static function ipv6($value)
779
    {
780 1
        return (bool) preg_match('/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i', $value);
781
    }
782
783
    /**
784
     * Determines if the $value exists in the $param array.
785
     * 
786
     * @param string $value 
787
     * @param array  $param 
788
     * 
789
     * @return bool
790
     */
791 2
    public static function inList($value, array $param)
792
    {
793 2
        return in_array($value, $param);
794
    }
795
796
    /**
797
     * Determines if the $value contains any white space.
798
     * 
799
     * @param string $value 
800
     * 
801
     * @return bool
802
     */
803 1
    public static function noWhiteSpace($value)
804
    {
805 1
        return (empty($value)) ? true : (bool) preg_match('/^\S+$/i', $value);
806
    }
807
808
    /**
809
     * Removes any doubled-up whitespace from the $value.
810
     * 
811
     * @param string $value 
812
     * 
813
     * @return string
814
     */
815 2
    public static function singleSpace($value)
816
    {
817 2
        return preg_replace('/\s(?=\s)/', '', $value);
818
    }
819
820
    /**
821
     * Returns a **1** (true) or **0** (false) integer depending on the $value.
822
     * 
823
     * @param mixed $value 
824
     * 
825
     * @return int
826
     */
827 1
    public static function trueFalse($value)
828
    {
829 1
        if ((is_numeric($value) && $value > 0) || strtoupper($value) == 'Y') {
830 1
            return 1;
831
        }
832
833 1
        return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 1 : 0;
834
    }
835
836
    /**
837
     * Returns a '**Y**' or '**N**' string depending on the $value.
838
     * 
839
     * @param mixed $value 
840
     * 
841
     * @return string
842
     */
843 1
    public static function yesNo($value)
844
    {
845 1
        if ((is_numeric($value) && $value > 0) || strtoupper($value) == 'Y') {
846 1
            return 'Y';
847
        }
848
849 1
        return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 'Y' : 'N';
850
    }
851
852
    /**
853
     * Returns the number of words in $value.
854
     * 
855
     * @param string $value 
856
     * 
857
     * @return integer
858
     */
859 3
    private static function numWords($value)
860
    {
861 3
        $value = preg_replace('/<.[^<>]*?>/', ' ', $value);
862 3
        $value = preg_replace('/&nbsp;|&#160;/i', ' ', $value);
863 3
        $value = preg_replace('/[.(),;:!?%#$\'\"_+=\/\-“”’]*/', '', $value);
864 3
        preg_match_all('/\b\w+\b/', $value, $words);
865
866 3
        return count($words[0]);
867
    }
868
869
     /**
870
      * A helper method to validate a $value via a callable $param.
871
      * 
872
      * @param string $value 
873
      * @param string $param 
874
      * 
875
      * @return string
876
      */
877 1
    private function remote($value, $param)
878
    {
879 1
        $value = (isset($this->rules[$param]) && is_callable($this->rules[$param])) ? $this->rules[$param]($value) : false;
880 1
        if (!is_string($value)) {
881 1
            $value = ($value) ? 'true' : 'false';
882 1
        }
883
884 1
        return $value;
885
    }
886
887
    /**
888
     * A helper method to determine the value in an array based on a string eg. 'array[value]'
889
     * 
890
     * @param mixed $array 
891
     * @param array $indexes 
892
     * @param int   $i  
893
     * 
894
     * @return mixed
895
     */
896 2
    private function reduce($array, array $indexes, $i = 0)
897
    {
898 2
        if (is_array($array) && isset($indexes[$i])) {
899 2
            return isset($array[$indexes[$i]]) ? $this->reduce($array[$indexes[$i]], $indexes, ($i + 1)) : null;
900
        }
901
902 1
        return ($array === '') ? null : $array;
903
    }
904
905
    /**
906
     * A helper method to remove any reference to an array ie. 'array[value]' would be just 'array'.
907
     * 
908
     * @param string $field 
909
     * 
910
     * @return string
911
     */
912 4
    private function base($field)
913
    {
914 4
        return ($split = strpos($field, '[')) ? substr($field, 0, $split) : $field;
915
    }
916
917
    /**
918
     * A helper method to validate a string or an array of values based on it's $rule's and $param's.
919
     * 
920
     * @param mixed  $value 
921
     * @param string $rule 
922
     * @param mixed  $param 
923
     * 
924
     * @return array The derived value, and error (if any).
925
     */
926 1
    private function validate($value, $rule, $param)
927
    {
928 1
        if (in_array($rule, $this->reserved)) {
929 1
            return array($value, null);
930
        }
931 1
        if (is_array($value) && !in_array($rule, array('minLength', 'maxLength', 'rangeLength'))) {
932 1
            $values = $value;
933 1
            $errors = array();
934 1
            foreach ($values as $key => $value) {
935 1
                list($value, $error) = $this->validate($value, $rule, $param);
936 1
                $values[$key] = $value;
937 1
                if ($error) {
938 1
                    $errors[$key] = $error;
939 1
                }
940 1
            }
941
942 1
            return array($values, !empty($errors) ? array_shift($errors) : null);
943
        }
944 1
        $error = null;
945 1
        if ($rule == 'remote') {
946 1
            if ('true' != $result = $this->remote($value, $param)) {
947 1
                $error = ($result == 'false') ? $this->errorMessage($rule, $param) : $result;
948 1
            }
949 1
        } elseif (isset($this->rules[$rule]) && is_callable($this->rules[$rule])) {
950 1
            if (!is_bool($result = $this->rules[$rule]($value, $param))) {
951 1
                $value = $result;
952 1
            }
953 1
            if ($result === false) {
954 1
                $error = $this->errorMessage($rule, $param);
955 1
            }
956 1
        } elseif (in_array($rule, $this->methods)) {
957 1
            if (!is_bool($result = self::$rule($value, $param))) {
958 1
                $value = $result;
959 1
            }
960 1
            if ($result === false) {
961 1
                $error = $this->errorMessage($rule, $param);
962 1
            }
963 1
        }
964
965 1
        return array($value, $error);
966
    }
967
968
    /**
969
     * A helper method to retrieve the associated error message when something goes wrong.
970
     * 
971
     * @param string $rule 
972
     * @param mixed  $param  
973
     * 
974
     * @return null|string
975
     */
976 1
    private function errorMessage($rule, $param = null)
977
    {
978 1
        if ($rule == 'remote' && isset($this->errors[$param])) {
979 1
            return $this->errorMessage($param);
980
        }
981 1
        if (is_null($param)) {
982 1
            $param = '';
983 1
        }
984 1
        $params = array_pad((array) $param, 2, '');
985
986 1
        return (isset($this->errors[$rule])) ? str_replace(array('{0}', '{1}'), $params, $this->errors[$rule]) : null;
987
    }
988
}
989