1: <?php
2: /**
3: * DataTables PHP libraries.
4: *
5: * PHP libraries for DataTables and DataTables Editor, utilising PHP 5.3+.
6: *
7: * @author SpryMedia
8: * @copyright 2012-2014 SpryMedia ( http://sprymedia.co.uk )
9: * @license http://editor.datatables.net/license DataTables Editor
10: * @link http://editor.datatables.net
11: */
12:
13: namespace DataTables\Editor;
14: if (!defined('DATATABLES')) exit();
15:
16: use DataTables\Editor\ValidateOptions;
17:
18: /**
19: * Validation methods for DataTables Editor fields.
20: *
21: * These methods will typically be applied through the {@link Field::validator}
22: * method and thus the arguments to be passed will be automatically resolved
23: * by Editor.
24: *
25: * The validation methods in this class all take three parameters:
26: *
27: * 1. Data to be validated
28: * 2. Full data from the form (this can be used with a custom validation method
29: * for dependent validation).
30: * 3. Validation configuration options.
31: *
32: * When using the `Validate` class functions with the {@link Field::validator}
33: * method, the second parameter passed into {@link Field::validator} is given
34: * to the validation functions here as the third parameter. The first and
35: * second parameters are automatically resolved by the {@link Field} class.
36: *
37: * The validation configuration options is an array of options that can be used
38: * to customise the validation - for example defining a date format for date
39: * validation. Each validation method has the option of defining its own
40: * validation options, but all validation methods provide four common options:
41: *
42: * * `{boolean} optional` - Require the field to be submitted (`false`) or not
43: * (`true` - default). When set to `true` the field does not need to be
44: * included in the list of parameters sent by the client - if set to `false`
45: * then it must be included. This option can be be particularly used in Editor
46: * as Editor will not set a value for fields which have not been submitted -
47: * giving the ability to submit just a partial list of options.
48: * * `{boolean} empty` - Allow a field to be empty, i.e. a zero length string -
49: * `''` (`true` - default) or require it to be non-zero length (`false`).
50: * * `{boolean} required` - Short-cut for `optional=false` and `empty=false`.
51: * Note that if this option is set the `optional` and `empty` parameters are
52: * automatically set and cannot be overridden by passing in different values.
53: * * `{string} message` - Error message shown should validation fail. This
54: * provides complete control over the message shown to the end user, including
55: * internationalisation (i.e. to provide a translation that is not in the
56: * English language).
57: *
58: * @example
59: * <code>
60: * // Ensure that a non-empty value is given for a field
61: * Field::inst( 'engine' )->validator( Validate::required() )
62: * </code>
63: *
64: * @example
65: * <code>
66: * // Don't require a field to be submitted, but if it is submitted, it
67: * // must be non-empty
68: * Field::inst( 'reg_date' )->validator( Validate::notEmpty() )
69: * </code>
70: *
71: * @example
72: * <code>
73: * // Date validation
74: * Field::inst( 'reg_date' )->validator( Validate::dateFormat( 'D, d M y' ) )
75: * </code>
76: *
77: * @example
78: * <code>
79: * // Date validation with a custom error message
80: * Field::inst( 'reg_date' )->validator( Validate::dateFormat( 'D, d M y',
81: * ValidateOptions::inst()
82: * ->message( 'Invalid date' )
83: * ) )
84: * </code>
85: *
86: * @example
87: * <code>
88: * // Require a non-empty e-mail address
89: * Field::inst( 'reg_date' )->validator( Validate::email( ValidateOptions::inst()
90: * ->empty( false )
91: * ) )
92: * </code>
93: *
94: * @example
95: * <code>
96: * // Custom validation - closure
97: * Field::inst( 'engine' )->validator( function($val, $data, $opts) {
98: * if ( ! preg_match( '/^1/', $val ) ) {
99: * return "Value <b>must</b> start with a 1";
100: * }
101: * return true;
102: * } )
103: * </code>
104: */
105: class Validate {
106: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
107: * Internal functions
108: */
109:
110: /**
111: * "Magic" method to automatically apply the required check to any of the
112: * static methods simply by adding '_required' or '_empty' to the end of the
113: * method's name, depending on if you need the field to be submitted or not.
114: *
115: * This is retained for backwards compatibility, but the validation options
116: * are now the recommended way of indicating that a field is required.
117: *
118: * @internal
119: * @param string $name Function name
120: * @param array $arguments Function arguments
121: * @return mixed|string
122: */
123: public static function __callStatic( $name, $arguments ) {
124: if ( preg_match( '/_required$/', $name ) ) {
125: if ( $arguments[0] === null || $arguments[0] === '' ) {
126: return 'This field is required';
127: }
128:
129: return call_user_func_array(
130: __NAMESPACE__.'\Validate::'.str_replace( '_required', '', $name ),
131: $arguments
132: );
133: }
134: }
135:
136:
137: /**
138: * Extend the options from the user function and the validation function
139: * with core defaults.
140: *
141: * @internal
142: */
143: public static function _extend( $userOpts, $prop, $fnOpts ) {
144: $cfg = array(
145: 'message' => 'Input not valid',
146: 'required' => false,
147: 'empty' => true,
148: 'optional' => true
149: );
150:
151: if ( ! is_array( $userOpts ) ) {
152: if ( $prop ) {
153: $cfg[ $prop ] = $userOpts;
154: }
155:
156: // Not an array, but the non-array case has been handled above, so
157: // just an empty array
158: $userOpts = array();
159: }
160:
161: // Merge into a single array - first array gets priority if there is a
162: // key clash, so user first, then function commands and finally the
163: // global options
164: $cfg = $userOpts + $fnOpts + $cfg;
165:
166: return $cfg;
167: }
168:
169:
170: /**
171: * Perform common validation using the configuration parameters
172: *
173: * @internal
174: */
175: public static function _common( $val, $opts ) {
176: $optional = $opts->optional();
177: $empty = $opts->allowEmpty();
178:
179: // Error state tests
180: if ( ! $optional && $val === null ) {
181: // Value must be given
182: return false;
183: }
184: else if ( $empty === false && $val === '' ) {
185: // Value must not be empty
186: return false;
187: }
188:
189: // Validation passed states
190: if ( $optional && $val === null ) {
191: return true;
192: }
193: else if ( $empty === true && $val === '' ) {
194: return true;
195: }
196:
197: // Have the specific validation function perform its tests
198: return null;
199: }
200:
201: /**
202: * Convert the old style validation parameters into ValidateOptions
203: *
204: * @internal
205: */
206: public static function _commonLegacy( $cfg ) {
207: $opts = new ValidateOptions();
208:
209: if ( is_array( $cfg ) ) {
210: // `required` is a legacy shortcut for optional=false, empty=false
211: if ( isset( $cfg['required'] ) ) {
212: $opts->optional( false );
213: $opts->allowEmpty( false );
214: }
215:
216: if ( isset( $cfg['empty'] ) ) {
217: $opts->allowEmpty( $cfg['empty'] );
218: }
219:
220: if ( isset( $cfg['message'] ) ) {
221: $opts->message( $cfg['message'] );
222: }
223:
224: if ( isset( $cfg['optional'] ) ) {
225: $opts->optional( $cfg['optional'] );
226: }
227: }
228:
229: return $opts;
230: }
231:
232:
233:
234: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
235: * Basic validation
236: */
237:
238: /**
239: * No validation - all inputs are valid.
240: * @return callable Validation function
241: */
242: public static function none() {
243: return function ( $val, $data, $field, $host ) {
244: return true;
245: };
246: }
247:
248:
249: /**
250: * Basic validation - this is used to perform the validation provided by the
251: * validation options only. If the validation options pass (e.g. `required`,
252: * `empty` and `optional`) then the validation will pass regardless of the
253: * actual value.
254: *
255: * Note that there are two helper short-cut methods that provide the same
256: * function as this method, but are slightly shorter:
257: *
258: * ```
259: * // Required:
260: * Validate::required()
261: *
262: * // is the same as
263: * Validate::basic( $val, $data, array(
264: * "required" => true
265: * );
266: * ```
267: *
268: * ```
269: * // Optional, but not empty if given:
270: * Validate::notEmpty()
271: *
272: * // is the same as
273: * Validate::basic( $val, $data, array(
274: * "empty" => false
275: * );
276: * ```
277: *
278: * @param string $val The value to check for validity
279: * @param string[] $data The full data set submitted
280: * @param array $opts Validation options. No additional options are
281: * available or required for this validation method.
282: * @param array $host Host information
283: * @return string|true true if the value is valid, a string with an error
284: * message otherwise.
285: */
286: static function basic( $cfg=null ) {
287: $opts = ValidateOptions::select( $cfg );
288:
289: return function ( $val, $data, $field, $host ) use ( $opts ) {
290: $common = Validate::_common( $val, $opts );
291:
292: return $common === false ?
293: $opts->message() :
294: true;
295: };
296: }
297:
298:
299: /**
300: * Required field - there must be a value and it must be a non-empty value
301: *
302: * This is a helper short-cut method which is the same as:
303: *
304: * ```
305: * Validate::basic( $val, $data, array(
306: * "required" => true
307: * );
308: * ```
309: *
310: * @param string $val The value to check for validity
311: * @param string[] $data The full data set submitted
312: * @param array $opts Validation options. No additional options are
313: * available or required for this validation method.
314: * @param array $host Host information
315: * @return string|true true if the value is valid, a string with an error
316: * message otherwise.
317: */
318: static function required( $cfg=null ) {
319: $opts = ValidateOptions::select( $cfg );
320: $opts->allowEmpty( false );
321: $opts->optional( false );
322:
323: return function ( $val, $data, $field, $host ) use ( $opts ) {
324: $common = Validate::_common( $val, $opts );
325:
326: return $common === false ?
327: $opts->message() :
328: true;
329: };
330: }
331:
332:
333: /**
334: * Optional field, but if given there must be a non-empty value
335: *
336: * This is a helper short-cut method which is the same as:
337: *
338: * ```
339: * Validate::basic( $val, $data, array(
340: * "empty" => false
341: * );
342: * ```
343: *
344: * @param ValidateOptions $cfg Validation options
345: * @return callable Validation function
346: */
347: static function notEmpty( $cfg=null ) {
348: $opts = ValidateOptions::select( $cfg );
349: $opts->allowEmpty( false );
350:
351: return function ( $val, $data, $field, $host ) use ( $opts ) {
352: $common = Validate::_common( $val, $opts );
353:
354: return $common === false ?
355: $opts->message() :
356: true;
357: };
358: }
359:
360:
361: /**
362: * Validate an input as a boolean value.
363: *
364: * @param string $val The value to check for validity
365: * @param string[] $data The full data set submitted
366: * @param array $opts Validation options. No additional options are
367: * available or required for this validation method.
368: * @param array $host Host information
369: * @return string|true true if the value is valid, a string with an error
370: * message otherwise.
371: */
372: public static function boolean( $cfg=null ) {
373: $opts = ValidateOptions::select( $cfg );
374:
375: return function ( $val, $data, $field, $host ) use ( $opts ) {
376: $common = Validate::_common( $val, $opts );
377:
378: if ( $common !== null ) {
379: return $common === false ?
380: $opts->message() :
381: $common;
382: }
383:
384: if ( filter_var($val, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) === null ) {
385: return $opts->message();
386: }
387: return true;
388: };
389: }
390:
391:
392:
393: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
394: * Number validation methods
395: */
396:
397: /**
398: * Check that any input is numeric.
399: *
400: * @param string $val The value to check for validity
401: * @param string[] $data The full data set submitted
402: * @param array $opts Validation options. Additional options:
403: * * `decimal`: is available to indicate what character should be used
404: * as the decimal
405: * @param array $host Host information
406: * @return string|true true if the value is valid, a string with an error
407: * message otherwise.
408: */
409: public static function numeric ( $decimal=".", $cfg=null ) {
410: $opts = ValidateOptions::select( $cfg );
411:
412: return function ( $val, $data, $field, $host ) use ( $opts, $decimal ) {
413: $common = Validate::_common( $val, $opts );
414:
415: if ( $common !== null ) {
416: return $common === false ?
417: $opts->message() :
418: $common;
419: }
420:
421: if ( $decimal !== '.' ) {
422: $val = str_replace( $decimal, '.', $val );
423: }
424:
425: return ! is_numeric( $val ) ?
426: $opts->message() :
427: true;
428: };
429: }
430:
431: /**
432: * Check for a numeric input and that it is greater than a given value.
433: *
434: * @param string $val The value to check for validity
435: * @param string[] $data The full data set submitted
436: * @param int|array $opts Validation options. Additional options:
437: * * `min`: indicate the minimum value. If only the default validation
438: * options are required, this parameter can be given as an integer
439: * value, which will be used as the minimum value.
440: * * `decimal`: is available to indicate what character should be used
441: * as the decimal
442: * separator (default '.').
443: * @param array $host Host information
444: * @return string|true true if the value is valid, a string with an error
445: * message otherwise.
446: */
447: public static function minNum ( $min, $decimal=".", $cfg=null ) {
448: $opts = ValidateOptions::select( $cfg );
449:
450: return function ( $val, $data, $field, $host ) use ( $opts, $min, $decimal ) {
451: $common = Validate::_common( $val, $opts );
452:
453: if ( $common !== null ) {
454: return $common === false ?
455: $opts->message() :
456: $common;
457: }
458:
459: $fn = Validate::numeric( $decimal, $opts );
460: $numeric = $fn( $val, $data, $field, $host );
461:
462: if ( $numeric !== true ) {
463: return $numeric;
464: }
465:
466: if ( $decimal !== '.' ) {
467: $val = str_replace( $decimal, '.', $val );
468: }
469:
470: return $val < $min ?
471: $opts->message() :
472: true;
473: };
474: }
475:
476: /**
477: * Check for a numeric input and that it is less than a given value.
478: *
479: * @param string $val The value to check for validity
480: * @param string[] $data The full data set submitted
481: * @param int|array $opts Validation options.
482: * * `max`: indicate the maximum value. If only the default validation
483: * options are required, this parameter can be given as an integer
484: * value, which will be used as the maximum value.
485: * * `decimal`: is available to indicate what character should be used
486: * as the decimal
487: * @param array $host Host information
488: * @return string|true true if the value is valid, a string with an error
489: * message otherwise.
490: */
491: public static function maxNum ( $max, $decimal=".", $cfg=null ) {
492: $opts = ValidateOptions::select( $cfg );
493:
494: return function ( $val, $data, $field, $host ) use ( $opts, $max, $decimal ) {
495: $common = Validate::_common( $val, $opts );
496:
497: if ( $common !== null ) {
498: return $common === false ?
499: $opts->message() :
500: $common;
501: }
502:
503: $fn = Validate::numeric( $decimal, $opts );
504: $numeric = $fn( $val, $data, $field, $host );
505:
506: if ( $numeric !== true ) {
507: return $numeric;
508: }
509:
510: if ( $decimal !== '.' ) {
511: $val = str_replace( $decimal, '.', $val );
512: }
513:
514: return $val > $max ?
515: $opts->message() :
516: true;
517: };
518: }
519:
520:
521: /**
522: * Check for a numeric input and that it is both greater and smaller than
523: * given numbers.
524: *
525: * @param string $val The value to check for validity
526: * @param string[] $data The full data set submitted
527: * @param int|array $opts Validation options. Additional options:
528: * * `min`: indicate the minimum value.
529: * * `max`: indicate the maximum value.
530: * * `decimal`: is available to indicate what character should be used
531: * as the decimal
532: * @param array $host Host information
533: * @return string|true true if the value is valid, a string with an error
534: * message otherwise.
535: */
536: public static function minMaxNum ( $min, $max, $decimal='.', $cfg=null ) {
537: $opts = ValidateOptions::select( $cfg );
538:
539: return function ( $val, $data, $field, $host ) use ( $opts, $min, $max, $decimal ) {
540: $common = Validate::_common( $val, $opts );
541:
542: if ( $common !== null ) {
543: return $common === false ?
544: $opts->message() :
545: $common;
546: }
547:
548: $fn = Validate::numeric( $decimal, $opts );
549: $numeric = $fn( $val, $data, $field, $host );
550:
551: if ( $numeric !== true ) {
552: return $numeric;
553: }
554:
555: $fn = Validate::minNum( $min, $decimal, $opts );
556: $numeric = $fn( $val, $data, $field, $host );
557:
558: if ( $numeric !== true ) {
559: return $numeric;
560: }
561:
562: $fn = Validate::maxNum( $max, $decimal, $opts );
563: $numeric = $fn( $val, $data, $field, $host );
564:
565: if ( $numeric !== true ) {
566: return $numeric;
567: }
568:
569: return true;
570: };
571: }
572:
573:
574:
575: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
576: * String validation methods
577: */
578:
579: /**
580: * Validate an input as an e-mail address.
581: *
582: * @param string $val The value to check for validity
583: * @param string[] $data The full data set submitted
584: * @param array $opts Validation options. No additional options are
585: * available or required for this validation method.
586: * @param array $host Host information
587: * @return string|true true if the value is valid, a string with an error
588: * message otherwise.
589: */
590: public static function email( $cfg=null ) {
591: $opts = ValidateOptions::select( $cfg );
592:
593: return function ( $val, $data, $field, $host ) use ( $opts ) {
594: $common = Validate::_common( $val, $opts );
595:
596: if ( $common !== null ) {
597: return $common === false ?
598: $opts->message() :
599: $common;
600: }
601:
602: return filter_var($val, FILTER_VALIDATE_EMAIL) !== false ?
603: true :
604: $opts->message();
605: };
606: }
607:
608:
609: /**
610: * Validate a string has a minimum length.
611: *
612: * @param string $val The value to check for validity
613: * @param string[] $data The full data set submitted
614: * @param int|array $opts Validation options. The additional option of
615: * `min` is available for this method to indicate the minimum string
616: * length. If only the default validation options are required, this
617: * parameter can be given as an integer value, which will be used as the
618: * minimum string length.
619: * @param array $host Host information
620: * @return string|true true if the value is valid, a string with an error
621: * message otherwise.
622: */
623: public static function minLen( $min, $cfg=null ) {
624: $opts = ValidateOptions::select( $cfg );
625:
626: return function ( $val, $data, $field, $host ) use ( $min, $opts ) {
627: $common = Validate::_common( $val, $opts );
628:
629: if ( $common !== null ) {
630: return $common === false ?
631: $opts->message() :
632: $common;
633: }
634:
635: $strlen = is_callable('mb_strlen') ?
636: 'mb_strlen' :
637: 'strlen';
638:
639: return $strlen( $val ) < $min ?
640: $opts->message() :
641: true;
642: };
643: }
644:
645:
646: /**
647: * Validate a string does not exceed a maximum length.
648: *
649: * @param string $val The value to check for validity
650: * @param string[] $data The full data set submitted
651: * @param int|array $opts Validation options. The additional option of
652: * `max` is available for this method to indicate the maximum string
653: * length. If only the default validation options are required, this
654: * parameter can be given as an integer value, which will be used as the
655: * maximum string length.
656: * @param array $host Host information
657: * @return string|true true if the value is valid, a string with an error
658: * message otherwise.
659: */
660: public static function maxLen( $max, $cfg=null ) {
661: $opts = ValidateOptions::select( $cfg );
662:
663: return function ( $val, $data, $field, $host ) use ( $max, $opts ) {
664: $common = Validate::_common( $val, $opts );
665:
666: if ( $common !== null ) {
667: return $common === false ?
668: $opts->message() :
669: $common;
670: }
671:
672: $strlen = is_callable('mb_strlen') ?
673: 'mb_strlen' :
674: 'strlen';
675:
676: return $strlen( $val ) > $max ?
677: $opts->message() :
678: true;
679: };
680: }
681:
682: /**
683: * Require a string with a certain minimum or maximum number of characters.
684: *
685: * @param string $val The value to check for validity
686: * @param string[] $data The full data set submitted
687: * @param int|array $opts Validation options. The additional options of
688: * `min` and `max` are available for this method to indicate the minimum
689: * and maximum string lengths, respectively.
690: * @param array $host Host information
691: * @return string|true true if the value is valid, a string with an error
692: * message otherwise.
693: */
694: public static function minMaxLen( $min, $max, $cfg=null ) {
695: $opts = ValidateOptions::select( $cfg );
696:
697: return function ( $val, $data, $field, $host ) use ( $opts, $min, $max ) {
698: $common = Validate::_common( $val, $opts );
699:
700: if ( $common !== null ) {
701: return $common === false ?
702: $opts->message() :
703: $common;
704: }
705:
706: $fn = Validate::minLen( $min, $opts );
707: $res = $fn( $val, $data, $field, $host );
708:
709: if ( $res !== true ) {
710: return $res;
711: }
712:
713: $fn = Validate::maxLen( $max, $opts );
714: $res = $fn( $val, $data, $field, $host );
715:
716: if ( $res !== true ) {
717: return $res;
718: }
719:
720: return true;
721: };
722: }
723:
724:
725: /**
726: * Validate as an IP address.
727: *
728: * @param string $val The value to check for validity
729: * @param string[] $data The full data set submitted
730: * @param array $opts Validation options. No additional options are
731: * available or required for this validation method.
732: * @param array $host Host information
733: * @return string|true true if the value is valid, a string with an error
734: * message otherwise.
735: */
736: public static function ip( $cfg=null ) {
737: $opts = ValidateOptions::select( $cfg );
738:
739: return function ( $val, $data, $field, $host ) use ( $opts ) {
740: $common = Validate::_common( $val, $opts );
741:
742: if ( $common !== null ) {
743: return $common === false ?
744: $opts->message() :
745: $common;
746: }
747:
748: return filter_var($val, FILTER_VALIDATE_IP) !== false ?
749: true :
750: $opts->message();
751: };
752: }
753:
754:
755: /**
756: * Validate as an URL address.
757: *
758: * @param string $val The value to check for validity
759: * @param string[] $data The full data set submitted
760: * @param array $opts Validation options. No additional options are
761: * available or required for this validation method.
762: * @param array $host Host information
763: * @return string|true true if the value is valid, a string with an error
764: * message otherwise.
765: */
766: public static function url( $cfg=null ) {
767: $opts = ValidateOptions::select( $cfg );
768:
769: return function ( $val, $data, $field, $host ) use ( $opts ) {
770: $common = Validate::_common( $val, $opts );
771:
772: if ( $common !== null ) {
773: return $common === false ?
774: $opts->message() :
775: $common;
776: }
777:
778: return filter_var($val, FILTER_VALIDATE_URL) !== false ?
779: true :
780: $opts->message();
781: };
782: }
783:
784:
785: /**
786: * Check if string could contain an XSS attack string
787: *
788: * @param string $val The value to check for validity
789: * @param string[] $data The full data set submitted
790: * @param int|array $opts Validation options. The additional options of
791: * `db` - database connection object, `table` - database table to use and
792: * `column` - the column to check this value against as value, are also
793: * available. These options are not required and if not given are
794: * automatically derived from the Editor and Field instances.
795: * @param array $host Host information
796: * @return string|true true if the value is valid, a string with an error
797: * message otherwise.
798: */
799: public static function xss ( $cfg=null ) {
800: $opts = ValidateOptions::select( $cfg );
801:
802: return function ( $val, $data, $field, $host ) use ( $opts ) {
803: $common = Validate::_common( $val, $opts );
804:
805: if ( $common !== null ) {
806: return $common === false ?
807: $opts->message() :
808: $common;
809: }
810:
811: return $field->xssSafety( $val ) != $val ?
812: $opts->message() :
813: true;
814: };
815: }
816:
817:
818: /**
819: * Confirm that the value submitted is in a list of allowable values
820: *
821: * @param string $val The value to check for validity
822: * @param string[] $data The full data set submitted
823: * @param int|array $opts Validation options. The additional options of
824: * `db` - database connection object, `table` - database table to use and
825: * `column` - the column to check this value against as value, are also
826: * available. These options are not required and if not given are
827: * automatically derived from the Editor and Field instances.
828: * @param array $host Host information
829: * @return string|true true if the value is valid, a string with an error
830: * message otherwise.
831: */
832: public static function values( $values, $cfg=null ) {
833: $opts = ValidateOptions::select( $cfg );
834:
835: return function ( $val, $data, $field, $host ) use ( $values, $opts ) {
836: $common = Validate::_common( $val, $opts );
837:
838: if ( $common !== null ) {
839: return $common === false ?
840: $opts->message() :
841: $common;
842: }
843:
844: return in_array($val, $values) ?
845: true :
846: $opts->message();
847: };
848: }
849:
850:
851: /**
852: * Check if there are any tags in the submitted value
853: *
854: * @param string $val The value to check for validity
855: * @param string[] $data The full data set submitted
856: * @param int|array $opts Validation options. The additional options of
857: * `db` - database connection object, `table` - database table to use and
858: * `column` - the column to check this value against as value, are also
859: * available. These options are not required and if not given are
860: * automatically derived from the Editor and Field instances.
861: * @param array $host Host information
862: * @return string|true true if the value is valid, a string with an error
863: * message otherwise.
864: */
865: public static function noTags ( $cfg=null ) {
866: $opts = ValidateOptions::select( $cfg );
867:
868: return function ( $val, $data, $field, $host ) use ( $opts ) {
869: $common = Validate::_common( $val, $opts );
870:
871: if ( $common !== null ) {
872: return $common === false ?
873: $opts->message() :
874: $common;
875: }
876:
877: return strip_tags( $val ) != $val ?
878: $opts->message() :
879: true;
880: };
881: }
882:
883:
884:
885: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
886: * Date validation methods
887: */
888:
889: /**
890: * Check that a valid date input is given
891: *
892: * @param string $val The value to check for validity
893: * @param string[] $data The full data set submitted
894: * @param array|string $opts If given as a string, then $opts is the date
895: * format to check the validity of. If given as an array, then the
896: * date format is in the 'format' parameter, and the return error
897: * message in the 'message' parameter.
898: * @param array $host Host information
899: * @return string|true true if the value is valid, a string with an error
900: * message otherwise.
901: */
902: public static function dateFormat( $format, $cfg=null ) {
903: $opts = ValidateOptions::select( $cfg );
904:
905: return function ( $val, $data, $field, $host ) use ( $format, $opts ) {
906: $common = Validate::_common( $val, $opts );
907:
908: if ( $common !== null ) {
909: return $common === false ?
910: $opts->message() :
911: $common;
912: }
913:
914: $formatCreate = substr($format, 0, 1) !== '!' ?
915: '!'.$format :
916: $format;
917:
918: $date = \DateTime::createFromFormat( $formatCreate, $val );
919:
920: return $date && $date->format( $format ) == $val ?
921: true :
922: $opts->message();
923: };
924: }
925:
926:
927: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
928: * Database validation methods
929: */
930:
931: /**
932: * Check that the given value is unique in the database
933: *
934: * @param string $val The value to check for validity
935: * @param string[] $data The full data set submitted
936: * @param int|array $opts Validation options. The additional options of
937: * `db` - database connection object, `table` - database table to use and
938: * `column` - the column to check this value against as value, are also
939: * available. These options are not required and if not given are
940: * automatically derived from the Editor and Field instances.
941: * @param array $host Host information
942: * @return string|true true if the value is valid, a string with an error
943: * message otherwise.
944: */
945: public static function unique( $cfg=null, $column=null, $table=null, $db=null ) {
946: $opts = ValidateOptions::select( $cfg );
947:
948: return function ( $val, $data, $field, $host ) use ( $opts, $column, $table, $db ) {
949: $common = Validate::_common( $val, $opts );
950:
951: if ( $common !== null ) {
952: return $common === false ?
953: $opts->message() :
954: $common;
955: }
956:
957: $editor = $host['editor'];
958:
959: if ( ! $db ) {
960: $db = $host['db'];
961: }
962:
963: if ( ! $table ) {
964: $table = $editor->table(); // Returns an array, but `select` will take an array
965: }
966:
967: if ( ! $column ) {
968: $column = $field->dbField();
969: }
970:
971: $query = $db
972: ->query( 'select', $table )
973: ->get( $column )
974: ->where( $column, $val );
975:
976: // If doing an edit, then we need to also discount the current row,
977: // since it is of course already validly unique
978: if ( $host['action'] === 'edit' ) {
979: $cond = $editor->pkeyToArray( $host['id'], true );
980: $query->where( $cond, null, '!=' );
981: }
982:
983: $res = $query->exec();
984:
985: return $res->count() === 0 ?
986: true :
987: $opts->message();
988: };
989: }
990:
991: /**
992: * Check that the given value is a value that is available in a database -
993: * i.e. a join primary key. This will attempt to automatically use the table
994: * name and value column from the field's `options` method (under the
995: * assumption that it will typically be used with a joined field), but the
996: * table and field can also be specified via the options.
997: *
998: * @param string $val The value to check for validity
999: * @param string[] $data The full data set submitted
1000: * @param int|array $opts Validation options. The additional options of
1001: * `db` - database connection object, `table` - database table to use and
1002: * `column` - the column to check this value against as value, are also
1003: * available. These options are not required and if not given are
1004: * automatically derived from the Editor and Field instances.
1005: * @param array $host Host information
1006: * @return string|true true if the value is valid, a string with an error
1007: * message otherwise.
1008: */
1009: public static function dbValues( $cfg=null, $column=null, $table=null, $db=null, $values=array() ) {
1010: $opts = ValidateOptions::select( $cfg );
1011:
1012: return function ( $val, $data, $field, $host ) use ( $opts, $column, $table, $db, $values ) {
1013: $common = Validate::_common( $val, $opts );
1014:
1015: if ( $common !== null ) {
1016: return $common === false ?
1017: $opts->message() :
1018: $common;
1019: }
1020:
1021: // Allow local values to be defined - for example null
1022: if ( in_array($val, $values) ) {
1023: return true;
1024: }
1025:
1026: $editor = $host['editor'];
1027: $options = $field->options();
1028:
1029: if ( ! $db ) {
1030: $db = $host['db'];
1031: }
1032:
1033: if ( ! $table ) {
1034: $table = $options->table(); // Returns an array, but `select` will take an array
1035: }
1036:
1037: if ( ! $column ) {
1038: $column = $options->value();
1039: }
1040:
1041: if ( ! $table ) {
1042: throw new \Exception('Table for database value check is not defined for field '.$field->name());
1043: }
1044:
1045: if ( ! $column ) {
1046: throw new \Exception('Value column for database value check is not defined for field '.$field->name());
1047: }
1048:
1049: // Try / catch in case the submitted value can't be represented as the
1050: // database type (e.g. an empty string as an integer)
1051: try {
1052: $count = $db
1053: ->query( 'select', $table )
1054: ->get( $column )
1055: ->where( $column, $val )
1056: ->exec()
1057: ->count();
1058:
1059: return $count === 0 ?
1060: $opts->message() :
1061: true;
1062: }
1063: catch (\Exception $e) {
1064: return $opts->message();
1065: }
1066: };
1067: }
1068:
1069:
1070:
1071: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1072: * File validation methods
1073: */
1074: static function fileExtensions ( $extensions, $msg="This file type cannot be uploaded." ) {
1075: return function ( $file ) use ( $extensions, $msg ) {
1076: $extn = pathinfo($file['name'], PATHINFO_EXTENSION);
1077:
1078: for ( $i=0, $ien=count($extensions) ; $i<$ien ; $i++ ) {
1079: if ( strtolower( $extn ) === strtolower( $extensions[$i] ) ) {
1080: return true;
1081: }
1082: }
1083:
1084: return $msg;
1085: };
1086: }
1087:
1088: static function fileSize ( $size, $msg="Uploaded file is too large." ) {
1089: return function ( $file ) use ( $size, $msg ) {
1090: return $file['size'] > $size ?
1091: $msg :
1092: true;
1093: };
1094: }
1095:
1096:
1097:
1098: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1099: * Mjoin validation methods
1100: */
1101: static function mjoinMinCount ( $count, $msg="Too few items." ) {
1102: return function ( $editor, $action, $values ) use ( $count, $msg ) {
1103: if ( $action === 'create' || $action === 'edit' ) {
1104: return count($values) < $count ?
1105: $msg :
1106: true;
1107: }
1108: return true;
1109: };
1110: }
1111:
1112: static function mjoinMaxCount ( $count, $msg="Too many items." ) {
1113: return function ( $editor, $action, $values ) use ( $count, $msg ) {
1114: if ( $action === 'create' || $action === 'edit' ) {
1115: return count($values) > $count ?
1116: $msg :
1117: true;
1118: }
1119: return true;
1120: };
1121: }
1122:
1123:
1124: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1125: * Internal functions
1126: * These legacy methods are for backwards compatibility with the old way of
1127: * using the validation methods. They basically do argument swapping.
1128: */
1129:
1130: /**
1131: * @internal
1132: */
1133: static function noneLegacy( $legacyOpts ) {
1134: return Validate::none();
1135: }
1136:
1137: /**
1138: * @internal
1139: */
1140: static function basicLegacy( $legacyOpts ) {
1141: $cfg = Validate::_extend( $legacyOpts, null, array() );
1142: $opts = Validate::_commonLegacy( $cfg );
1143:
1144: return Validate::required( $opts );
1145: }
1146:
1147: /**
1148: * @internal
1149: */
1150: static function requiredLegacy( $legacyOpts ) {
1151: $cfg = Validate::_extend( $legacyOpts, null, array(
1152: 'message' => "This field is required."
1153: ) );
1154: $opts = Validate::_commonLegacy( $cfg );
1155:
1156: return Validate::required( $opts );
1157: }
1158:
1159: /**
1160: * @internal
1161: */
1162: static function notEmptyLegacy( $legacyOpts ) {
1163: $cfg = Validate::_extend( $legacyOpts, null, array(
1164: 'message' => "This field is required."
1165: ) );
1166: $opts = Validate::_commonLegacy( $cfg );
1167:
1168: return Validate::notEmpty( $opts );
1169: }
1170:
1171: /**
1172: * @internal
1173: */
1174: static function booleanLegacy( $legacyOpts ) {
1175: $cfg = Validate::_extend( $legacyOpts, null, array(
1176: 'message' => "Please enter true or false."
1177: ) );
1178: $opts = Validate::_commonLegacy( $cfg );
1179:
1180: return Validate::notEmpty( $opts );
1181: }
1182:
1183: /**
1184: * @internal
1185: */
1186: static function numericLegacy( $legacyOpts ) {
1187: $cfg = Validate::_extend( $legacyOpts, null, array(
1188: 'message' => "This input must be given as a number."
1189: ) );
1190: $opts = Validate::_commonLegacy( $cfg );
1191:
1192: return isset( $legacyOpts['decimal'] ) ?
1193: Validate::numeric( $legacyOpts['decimal'], $opts ) :
1194: Validate::numeric( '.', $opts );
1195: }
1196:
1197: /**
1198: * @internal
1199: */
1200: static function minNumLegacy( $legacyOpts ) {
1201: $min = is_array($legacyOpts) ? $legacyOpts['min'] : $legacyOpts;
1202: $cfg = Validate::_extend( $legacyOpts, null, array(
1203: 'message' => "Number is too small, must be ".$min." or larger."
1204: ) );
1205: $opts = Validate::_commonLegacy( $cfg );
1206:
1207: return isset( $legacyOpts['decimal'] ) ?
1208: Validate::minNum( $min, $legacyOpts['decimal'], $opts ) :
1209: Validate::minNum( $min, '.', $opts );
1210: }
1211:
1212: /**
1213: * @internal
1214: */
1215: static function maxNumLegacy( $legacyOpts ) {
1216: $max = is_array($legacyOpts) ? $legacyOpts['max'] : $legacyOpts;
1217: $cfg = Validate::_extend( $legacyOpts, null, array(
1218: 'message' => "Number is too large, must be ".$max." or smaller."
1219: ) );
1220: $opts = Validate::_commonLegacy( $cfg );
1221:
1222: return isset( $legacyOpts['decimal'] ) ?
1223: Validate::maxNum( $max, $legacyOpts['decimal'], $opts ) :
1224: Validate::maxNum( $max, '.', $opts );
1225: }
1226:
1227: /**
1228: * @internal
1229: */
1230: static function minMaxNumLegacy( $legacyOpts ) {
1231: $min = $legacyOpts['min'];
1232: $max = $legacyOpts['max'];
1233: $cfg = Validate::_extend( $legacyOpts, null, array() );
1234: $opts = Validate::_commonLegacy( $cfg );
1235:
1236: return isset( $legacyOpts['decimal'] ) ?
1237: Validate::minMaxNum( $min, $max, $legacyOpts['decimal'], $opts ) :
1238: Validate::minMaxNum( $min, $max, '.', $opts );
1239: }
1240:
1241: /**
1242: * @internal
1243: */
1244: static function emailLegacy( $legacyOpts ) {
1245: $cfg = Validate::_extend( $legacyOpts, null, array(
1246: 'message' => "Please enter a valid e-mail address."
1247: ) );
1248: $opts = Validate::_commonLegacy( $cfg );
1249:
1250: return Validate::email( $opts );
1251: }
1252:
1253: /**
1254: * @internal
1255: */
1256: static function minLenLegacy( $legacyOpts ) {
1257: $min = is_array($legacyOpts) ? $legacyOpts['min'] : $legacyOpts;
1258: $cfg = Validate::_extend( $legacyOpts, null, array(
1259: 'message' => "The input is too short. ".$min." characters required."
1260: ) );
1261: $opts = Validate::_commonLegacy( $cfg );
1262:
1263: return Validate::minLen( $min, $opts );
1264: }
1265:
1266: /**
1267: * @internal
1268: */
1269: static function maxLenLegacy( $legacyOpts ) {
1270: $max = is_array($legacyOpts) ? $legacyOpts['max'] : $legacyOpts;
1271: $cfg = Validate::_extend( $legacyOpts, null, array(
1272: 'message' => "The input is too long. ".$max." characters maximum."
1273: ) );
1274: $opts = Validate::_commonLegacy( $cfg );
1275:
1276: return Validate::maxLen( $max, $opts );
1277: }
1278:
1279: /**
1280: * @internal
1281: */
1282: static function minMaxLenLegacy( $legacyOpts ) {
1283: $min = $legacyOpts['min'];
1284: $max = $legacyOpts['max'];
1285: $cfg = Validate::_extend( $legacyOpts, null, array() );
1286: $opts = Validate::_commonLegacy( $cfg );
1287:
1288: return Validate::minMaxLen( $min, $max, $opts );
1289: }
1290:
1291: /**
1292: * @internal
1293: */
1294: static function ipLegacy( $legacyOpts ) {
1295: $cfg = Validate::_extend( $legacyOpts, null, array(
1296: 'message' => "Please enter a valid IP address."
1297: ) );
1298: $opts = Validate::_commonLegacy( $cfg );
1299:
1300: return Validate::ip( $opts );
1301: }
1302:
1303: /**
1304: * @internal
1305: */
1306: static function urlLegacy( $legacyOpts ) {
1307: $cfg = Validate::_extend( $legacyOpts, null, array(
1308: 'message' => "Please enter a valid URL."
1309: ) );
1310: $opts = Validate::_commonLegacy( $cfg );
1311:
1312: return Validate::url( $opts );
1313: }
1314:
1315: /**
1316: * @internal
1317: */
1318: static function xssLegacy( $legacyOpts ) {
1319: $cfg = Validate::_extend( $legacyOpts, null, array(
1320: 'message' => "This field contains potentially unsafe data."
1321: ) );
1322: $opts = Validate::_commonLegacy( $cfg );
1323:
1324: return Validate::xss( $opts );
1325: }
1326:
1327: /**
1328: * @internal
1329: */
1330: static function valuesLegacy( $legacyOpts ) {
1331: $values = isset($legacyOpts['valid']) ? $legacyOpts['valid'] : $legacyOpts;
1332: $cfg = Validate::_extend( $legacyOpts, null, array(
1333: 'message' => "This value is not valid."
1334: ) );
1335: $opts = Validate::_commonLegacy( $cfg );
1336:
1337: return Validate::values( $values, $opts );
1338: }
1339:
1340: /**
1341: * @internal
1342: */
1343: static function noTagsLegacy( $legacyOpts ) {
1344: $cfg = Validate::_extend( $legacyOpts, null, array(
1345: 'message' => "This field may not contain HTML."
1346: ) );
1347: $opts = Validate::_commonLegacy( $cfg );
1348:
1349: return Validate::noTags( $opts );
1350: }
1351:
1352: /**
1353: * @internal
1354: */
1355: static function dateFormatLegacy( $legacyOpts ) {
1356: $format = is_array($legacyOpts) ? $legacyOpts['format'] : $legacyOpts;
1357: $cfg = Validate::_extend( $legacyOpts, null, array(
1358: 'message' => "Date is not in the expected format."
1359: ) );
1360: $opts = Validate::_commonLegacy( $cfg );
1361:
1362: return Validate::dateFormat( $format, $opts );
1363: }
1364:
1365: /**
1366: * @internal
1367: */
1368: static function uniqueLegacy( $legacyOpts ) {
1369: $table = isset( $legacyOpts['table'] ) ? $legacyOpts['table'] : null;
1370: $column = isset( $legacyOpts['column'] ) ? $legacyOpts['column'] : null;
1371: $db = isset( $legacyOpts['db'] ) ? $legacyOpts['db'] : null;
1372: $cfg = Validate::_extend( $legacyOpts, null, array(
1373: 'message' => "This field must have a unique value."
1374: ) );
1375: $opts = Validate::_commonLegacy( $cfg );
1376:
1377: return Validate::unique( $opts, $column, $table, $db );
1378: }
1379:
1380: /**
1381: * @internal
1382: */
1383: static function dbValuesLegacy( $legacyOpts ) {
1384: $table = isset( $legacyOpts['table'] ) ? $legacyOpts['table'] : null;
1385: $column = isset( $legacyOpts['column'] ) ? $legacyOpts['column'] : null;
1386: $db = isset( $legacyOpts['db'] ) ? $legacyOpts['db'] : null;
1387: $values = isset( $legacyOpts['values'] ) ? $legacyOpts['values'] : array();
1388: $cfg = Validate::_extend( $legacyOpts, null, array(
1389: 'message' => "This value is not valid."
1390: ) );
1391: $opts = Validate::_commonLegacy( $cfg );
1392:
1393: return Validate::dbValues( $opts, $column, $table, $db, $values );
1394: }
1395: }
1396:
1397: