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 $opt->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: return strlen( $val ) < $min ?
636: $opts->message() :
637: true;
638: };
639: }
640:
641:
642: /**
643: * Validate a string does not exceed a maximum length.
644: *
645: * @param string $val The value to check for validity
646: * @param string[] $data The full data set submitted
647: * @param int|array $opts Validation options. The additional option of
648: * `max` is available for this method to indicate the maximum string
649: * length. If only the default validation options are required, this
650: * parameter can be given as an integer value, which will be used as the
651: * maximum string length.
652: * @param array $host Host information
653: * @return string|true true if the value is valid, a string with an error
654: * message otherwise.
655: */
656: public static function maxLen( $max, $cfg=null ) {
657: $opts = ValidateOptions::select( $cfg );
658:
659: return function ( $val, $data, $field, $host ) use ( $max, $opts ) {
660: $common = Validate::_common( $val, $opts );
661:
662: if ( $common !== null ) {
663: return $common === false ?
664: $opts->message() :
665: $common;
666: }
667:
668: return strlen( $val ) > $max ?
669: $opts->message() :
670: true;
671: };
672: }
673:
674: /**
675: * Require a string with a certain minimum or maximum number of characters.
676: *
677: * @param string $val The value to check for validity
678: * @param string[] $data The full data set submitted
679: * @param int|array $opts Validation options. The additional options of
680: * `min` and `max` are available for this method to indicate the minimum
681: * and maximum string lengths, respectively.
682: * @param array $host Host information
683: * @return string|true true if the value is valid, a string with an error
684: * message otherwise.
685: */
686: public static function minMaxLen( $min, $max, $cfg=null ) {
687: $opts = ValidateOptions::select( $cfg );
688:
689: return function ( $val, $data, $field, $host ) use ( $opts, $min, $max ) {
690: $common = Validate::_common( $val, $opts );
691:
692: if ( $common !== null ) {
693: return $common === false ?
694: $opts->message() :
695: $common;
696: }
697:
698: $fn = Validate::minLen( $min, $opts );
699: $res = $fn( $val, $data, $field, $host );
700:
701: if ( $res !== true ) {
702: return $res;
703: }
704:
705: $fn = Validate::maxLen( $max, $opts );
706: $res = $fn( $val, $data, $field, $host );
707:
708: if ( $res !== true ) {
709: return $res;
710: }
711:
712: return true;
713: };
714: }
715:
716:
717: /**
718: * Validate as an IP address.
719: *
720: * @param string $val The value to check for validity
721: * @param string[] $data The full data set submitted
722: * @param array $opts Validation options. No additional options are
723: * available or required for this validation method.
724: * @param array $host Host information
725: * @return string|true true if the value is valid, a string with an error
726: * message otherwise.
727: */
728: public static function ip( $cfg=null ) {
729: $opts = ValidateOptions::select( $cfg );
730:
731: return function ( $val, $data, $field, $host ) use ( $opts ) {
732: $common = Validate::_common( $val, $opts );
733:
734: if ( $common !== null ) {
735: return $common === false ?
736: $opts->message() :
737: $common;
738: }
739:
740: return filter_var($val, FILTER_VALIDATE_IP) !== false ?
741: true :
742: $opts->message();
743: };
744: }
745:
746:
747: /**
748: * Validate as an URL address.
749: *
750: * @param string $val The value to check for validity
751: * @param string[] $data The full data set submitted
752: * @param array $opts Validation options. No additional options are
753: * available or required for this validation method.
754: * @param array $host Host information
755: * @return string|true true if the value is valid, a string with an error
756: * message otherwise.
757: */
758: public static function url( $cfg=null ) {
759: $opts = ValidateOptions::select( $cfg );
760:
761: return function ( $val, $data, $field, $host ) use ( $opts ) {
762: $common = Validate::_common( $val, $opts );
763:
764: if ( $common !== null ) {
765: return $common === false ?
766: $opts->message() :
767: $common;
768: }
769:
770: return filter_var($val, FILTER_VALIDATE_URL) !== false ?
771: true :
772: $opts->message();
773: };
774: }
775:
776:
777: /**
778: * Check if string could contain an XSS attack string
779: *
780: * @param string $val The value to check for validity
781: * @param string[] $data The full data set submitted
782: * @param int|array $opts Validation options. The additional options of
783: * `db` - database connection object, `table` - database table to use and
784: * `column` - the column to check this value against as value, are also
785: * available. These options are not required and if not given are
786: * automatically derived from the Editor and Field instances.
787: * @param array $host Host information
788: * @return string|true true if the value is valid, a string with an error
789: * message otherwise.
790: */
791: public static function xss ( $cfg=null ) {
792: $opts = ValidateOptions::select( $cfg );
793:
794: return function ( $val, $data, $field, $host ) use ( $opts ) {
795: $common = Validate::_common( $val, $opts );
796:
797: if ( $common !== null ) {
798: return $common === false ?
799: $opts->message() :
800: $common;
801: }
802:
803: return $field->xssSafety( $val ) != $val ?
804: $opts->message() :
805: true;
806: };
807: }
808:
809:
810: /**
811: * Confirm that the value submitted is in a list of allowable values
812: *
813: * @param string $val The value to check for validity
814: * @param string[] $data The full data set submitted
815: * @param int|array $opts Validation options. The additional options of
816: * `db` - database connection object, `table` - database table to use and
817: * `column` - the column to check this value against as value, are also
818: * available. These options are not required and if not given are
819: * automatically derived from the Editor and Field instances.
820: * @param array $host Host information
821: * @return string|true true if the value is valid, a string with an error
822: * message otherwise.
823: */
824: public static function values( $values, $cfg=null ) {
825: $opts = ValidateOptions::select( $cfg );
826:
827: return function ( $val, $data, $field, $host ) use ( $values, $opts ) {
828: $common = Validate::_common( $val, $opts );
829:
830: if ( $common !== null ) {
831: return $common === false ?
832: $opts->message() :
833: $common;
834: }
835:
836: return in_array($val, $values) ?
837: true :
838: $opts->message();
839: };
840: }
841:
842:
843: /**
844: * Check if there are any tags in the submitted value
845: *
846: * @param string $val The value to check for validity
847: * @param string[] $data The full data set submitted
848: * @param int|array $opts Validation options. The additional options of
849: * `db` - database connection object, `table` - database table to use and
850: * `column` - the column to check this value against as value, are also
851: * available. These options are not required and if not given are
852: * automatically derived from the Editor and Field instances.
853: * @param array $host Host information
854: * @return string|true true if the value is valid, a string with an error
855: * message otherwise.
856: */
857: public static function noTags ( $cfg=null ) {
858: $opts = ValidateOptions::select( $cfg );
859:
860: return function ( $val, $data, $field, $host ) use ( $opts ) {
861: $common = Validate::_common( $val, $opts );
862:
863: if ( $common !== null ) {
864: return $common === false ?
865: $opts->message() :
866: $common;
867: }
868:
869: return strip_tags( $val ) != $val ?
870: $opts->message() :
871: true;
872: };
873: }
874:
875:
876:
877: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
878: * Date validation methods
879: */
880:
881: /**
882: * Check that a valid date input is given
883: *
884: * @param string $val The value to check for validity
885: * @param string[] $data The full data set submitted
886: * @param array|string $opts If given as a string, then $opts is the date
887: * format to check the validity of. If given as an array, then the
888: * date format is in the 'format' parameter, and the return error
889: * message in the 'message' parameter.
890: * @param array $host Host information
891: * @return string|true true if the value is valid, a string with an error
892: * message otherwise.
893: */
894: public static function dateFormat( $format, $cfg=null ) {
895: $opts = ValidateOptions::select( $cfg );
896:
897: return function ( $val, $data, $field, $host ) use ( $format, $opts ) {
898: $common = Validate::_common( $val, $opts );
899:
900: if ( $common !== null ) {
901: return $common === false ?
902: $opts->message() :
903: $common;
904: }
905:
906: $date = \DateTime::createFromFormat( $format, $val) ;
907:
908: return $date && $date->format( $format ) == $val ?
909: true :
910: $opts->message();
911: };
912: }
913:
914:
915: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
916: * Database validation methods
917: */
918:
919: /**
920: * Check that the given value is unique in the database
921: *
922: * @param string $val The value to check for validity
923: * @param string[] $data The full data set submitted
924: * @param int|array $opts Validation options. The additional options of
925: * `db` - database connection object, `table` - database table to use and
926: * `column` - the column to check this value against as value, are also
927: * available. These options are not required and if not given are
928: * automatically derived from the Editor and Field instances.
929: * @param array $host Host information
930: * @return string|true true if the value is valid, a string with an error
931: * message otherwise.
932: */
933: public static function unique( $cfg=null, $column=null, $table=null, $db=null ) {
934: $opts = ValidateOptions::select( $cfg );
935:
936: return function ( $val, $data, $field, $host ) use ( $opts, $column, $table, $db ) {
937: $common = Validate::_common( $val, $opts );
938:
939: if ( $common !== null ) {
940: return $common === false ?
941: $opts->message() :
942: $common;
943: }
944:
945: $editor = $host['editor'];
946:
947: if ( ! $db ) {
948: $db = $host['db'];
949: }
950:
951: if ( ! $table ) {
952: $table = $editor->table(); // Returns an array, but `select` will take an array
953: }
954:
955: if ( ! $column ) {
956: $column = $field->dbField();
957: }
958:
959: $query = $db
960: ->query( 'select', $table )
961: ->get( $column )
962: ->where( $column, $val );
963:
964: // If doing an edit, then we need to also discount the current row,
965: // since it is of course already validly unique
966: if ( $host['action'] === 'edit' ) {
967: $cond = $editor->pkeyToArray( $host['id'], true );
968: $query->where( $cond, null, '!=' );
969: }
970:
971: $res = $query->exec();
972:
973: return $res->count() === 0 ?
974: true :
975: $opts->message();
976: };
977: }
978:
979: /**
980: * Check that the given value is a value that is available in a database -
981: * i.e. a join primary key. This will attempt to automatically use the table
982: * name and value column from the field's `options` method (under the
983: * assumption that it will typically be used with a joined field), but the
984: * table and field can also be specified via the options.
985: *
986: * @param string $val The value to check for validity
987: * @param string[] $data The full data set submitted
988: * @param int|array $opts Validation options. The additional options of
989: * `db` - database connection object, `table` - database table to use and
990: * `column` - the column to check this value against as value, are also
991: * available. These options are not required and if not given are
992: * automatically derived from the Editor and Field instances.
993: * @param array $host Host information
994: * @return string|true true if the value is valid, a string with an error
995: * message otherwise.
996: */
997: public static function dbValues( $cfg=null, $column=null, $table=null, $db=null, $values=array() ) {
998: $opts = ValidateOptions::select( $cfg );
999:
1000: return function ( $val, $data, $field, $host ) use ( $opts, $column, $table, $db, $values ) {
1001: $common = Validate::_common( $val, $opts );
1002:
1003: if ( $common !== null ) {
1004: return $common === false ?
1005: $opts->message() :
1006: $common;
1007: }
1008:
1009: // Allow local values to be defined - for example null
1010: if ( in_array($val, $values) ) {
1011: return true;
1012: }
1013:
1014: $editor = $host['editor'];
1015: $options = $field->options();
1016:
1017: if ( ! $db ) {
1018: $db = $host['db'];
1019: }
1020:
1021: if ( ! $table ) {
1022: $table = $options->table(); // Returns an array, but `select` will take an array
1023: }
1024:
1025: if ( ! $column ) {
1026: $column = $options->value();
1027: }
1028:
1029: if ( ! $table ) {
1030: throw new \Exception('Table for database value check is not defined for field '.$field->name());
1031: }
1032:
1033: if ( ! $column ) {
1034: throw new \Exception('Value column for database value check is not defined for field '.$field->name());
1035: }
1036:
1037: // Try / catch in case the submitted value can't be represented as the
1038: // database type (e.g. an empty string as an integer)
1039: try {
1040: $count = $db
1041: ->query( 'select', $table )
1042: ->get( $column )
1043: ->where( $column, $val )
1044: ->exec()
1045: ->count();
1046:
1047: return $count === 0 ?
1048: $opts->message() :
1049: true;
1050: }
1051: catch (\Exception $e) {
1052: return $opts->message();
1053: }
1054: };
1055: }
1056:
1057:
1058:
1059: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1060: * File validation methods
1061: */
1062: static function fileExtensions ( $extensions, $msg="This file type cannot be uploaded." ) {
1063: return function ( $file ) use ( $extensions, $msg ) {
1064: $extn = pathinfo($file['name'], PATHINFO_EXTENSION);
1065:
1066: for ( $i=0, $ien=count($extensions) ; $i<$ien ; $i++ ) {
1067: if ( strtolower( $extn ) === strtolower( $extensions[$i] ) ) {
1068: return true;
1069: }
1070: }
1071:
1072: return $msg;
1073: };
1074: }
1075:
1076: static function fileSize ( $size, $msg="Uploaded file is too large." ) {
1077: return function ( $file ) use ( $size, $msg ) {
1078: return $file['size'] > $size ?
1079: $msg :
1080: true;
1081: };
1082: }
1083:
1084:
1085: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1086: * Internal functions
1087: * These legacy methods are for backwards compatibility with the old way of
1088: * using the validation methods. They basically do argument swapping.
1089: */
1090:
1091: /**
1092: * @internal
1093: */
1094: static function noneLegacy( $legacyOpts ) {
1095: return Validate::none();
1096: }
1097:
1098: /**
1099: * @internal
1100: */
1101: static function basicLegacy( $legacyOpts ) {
1102: $cfg = Validate::_extend( $legacyOpts, null, array() );
1103: $opts = Validate::_commonLegacy( $cfg );
1104:
1105: return Validate::required( $opts );
1106: }
1107:
1108: /**
1109: * @internal
1110: */
1111: static function requiredLegacy( $legacyOpts ) {
1112: $cfg = Validate::_extend( $legacyOpts, null, array(
1113: 'message' => "This field is required."
1114: ) );
1115: $opts = Validate::_commonLegacy( $cfg );
1116:
1117: return Validate::required( $opts );
1118: }
1119:
1120: /**
1121: * @internal
1122: */
1123: static function notEmptyLegacy( $legacyOpts ) {
1124: $cfg = Validate::_extend( $legacyOpts, null, array(
1125: 'message' => "This field is required."
1126: ) );
1127: $opts = Validate::_commonLegacy( $cfg );
1128:
1129: return Validate::notEmpty( $opts );
1130: }
1131:
1132: /**
1133: * @internal
1134: */
1135: static function booleanLegacy( $legacyOpts ) {
1136: $cfg = Validate::_extend( $legacyOpts, null, array(
1137: 'message' => "Please enter true or false."
1138: ) );
1139: $opts = Validate::_commonLegacy( $cfg );
1140:
1141: return Validate::notEmpty( $opts );
1142: }
1143:
1144: /**
1145: * @internal
1146: */
1147: static function numericLegacy( $legacyOpts ) {
1148: $cfg = Validate::_extend( $legacyOpts, null, array(
1149: 'message' => "This input must be given as a number."
1150: ) );
1151: $opts = Validate::_commonLegacy( $cfg );
1152:
1153: return isset( $legacyOpts['decimal'] ) ?
1154: Validate::numeric( $legacyOpts['decimal'], $opts ) :
1155: Validate::numeric( '.', $opts );
1156: }
1157:
1158: /**
1159: * @internal
1160: */
1161: static function minNumLegacy( $legacyOpts ) {
1162: $min = is_array($legacyOpts) ? $legacyOpts['min'] : $legacyOpts;
1163: $cfg = Validate::_extend( $legacyOpts, null, array(
1164: 'message' => "Number is too small, must be ".$min." or larger."
1165: ) );
1166: $opts = Validate::_commonLegacy( $cfg );
1167:
1168: return isset( $legacyOpts['decimal'] ) ?
1169: Validate::minNum( $min, $legacyOpts['decimal'], $opts ) :
1170: Validate::minNum( $min, '.', $opts );
1171: }
1172:
1173: /**
1174: * @internal
1175: */
1176: static function maxNumLegacy( $legacyOpts ) {
1177: $max = is_array($legacyOpts) ? $legacyOpts['max'] : $legacyOpts;
1178: $cfg = Validate::_extend( $legacyOpts, null, array(
1179: 'message' => "Number is too large, must be ".$max." or smaller."
1180: ) );
1181: $opts = Validate::_commonLegacy( $cfg );
1182:
1183: return isset( $legacyOpts['decimal'] ) ?
1184: Validate::maxNum( $max, $legacyOpts['decimal'], $opts ) :
1185: Validate::maxNum( $max, '.', $opts );
1186: }
1187:
1188: /**
1189: * @internal
1190: */
1191: static function minMaxNumLegacy( $legacyOpts ) {
1192: $min = $legacyOpts['min'];
1193: $max = $legacyOpts['max'];
1194: $cfg = Validate::_extend( $legacyOpts, null, array() );
1195: $opts = Validate::_commonLegacy( $cfg );
1196:
1197: return isset( $legacyOpts['decimal'] ) ?
1198: Validate::minMaxNum( $min, $max, $legacyOpts['decimal'], $opts ) :
1199: Validate::minMaxNum( $min, $max, '.', $opts );
1200: }
1201:
1202: /**
1203: * @internal
1204: */
1205: static function emailLegacy( $legacyOpts ) {
1206: $cfg = Validate::_extend( $legacyOpts, null, array(
1207: 'message' => "Please enter a valid e-mail address."
1208: ) );
1209: $opts = Validate::_commonLegacy( $cfg );
1210:
1211: return Validate::email( $opts );
1212: }
1213:
1214: /**
1215: * @internal
1216: */
1217: static function minLenLegacy( $legacyOpts ) {
1218: $min = is_array($legacyOpts) ? $legacyOpts['min'] : $legacyOpts;
1219: $cfg = Validate::_extend( $legacyOpts, null, array(
1220: 'message' => "The input is too short. ".$min." characters required."
1221: ) );
1222: $opts = Validate::_commonLegacy( $cfg );
1223:
1224: return Validate::minLen( $min, $opts );
1225: }
1226:
1227: /**
1228: * @internal
1229: */
1230: static function maxLenLegacy( $legacyOpts ) {
1231: $max = is_array($legacyOpts) ? $legacyOpts['max'] : $legacyOpts;
1232: $cfg = Validate::_extend( $legacyOpts, null, array(
1233: 'message' => "The input is too long. ".$max." characters maximum."
1234: ) );
1235: $opts = Validate::_commonLegacy( $cfg );
1236:
1237: return Validate::maxLen( $max, $opts );
1238: }
1239:
1240: /**
1241: * @internal
1242: */
1243: static function minMaxLenLegacy( $legacyOpts ) {
1244: $min = $legacyOpts['min'];
1245: $max = $legacyOpts['max'];
1246: $cfg = Validate::_extend( $legacyOpts, null, array() );
1247: $opts = Validate::_commonLegacy( $cfg );
1248:
1249: return Validate::minMaxLen( $min, $max, $opts );
1250: }
1251:
1252: /**
1253: * @internal
1254: */
1255: static function ipLegacy( $legacyOpts ) {
1256: $cfg = Validate::_extend( $legacyOpts, null, array(
1257: 'message' => "Please enter a valid IP address."
1258: ) );
1259: $opts = Validate::_commonLegacy( $cfg );
1260:
1261: return Validate::ip( $opts );
1262: }
1263:
1264: /**
1265: * @internal
1266: */
1267: static function urlLegacy( $legacyOpts ) {
1268: $cfg = Validate::_extend( $legacyOpts, null, array(
1269: 'message' => "Please enter a valid URL."
1270: ) );
1271: $opts = Validate::_commonLegacy( $cfg );
1272:
1273: return Validate::url( $opts );
1274: }
1275:
1276: /**
1277: * @internal
1278: */
1279: static function xssLegacy( $legacyOpts ) {
1280: $cfg = Validate::_extend( $legacyOpts, null, array(
1281: 'message' => "This field contains potentially unsafe data."
1282: ) );
1283: $opts = Validate::_commonLegacy( $cfg );
1284:
1285: return Validate::xss( $opts );
1286: }
1287:
1288: /**
1289: * @internal
1290: */
1291: static function valuesLegacy( $legacyOpts ) {
1292: $values = isset($legacyOpts['valid']) ? $legacyOpts['valid'] : $legacyOpts;
1293: $cfg = Validate::_extend( $legacyOpts, null, array(
1294: 'message' => "This value is not valid."
1295: ) );
1296: $opts = Validate::_commonLegacy( $cfg );
1297:
1298: return Validate::values( $values, $opts );
1299: }
1300:
1301: /**
1302: * @internal
1303: */
1304: static function noTagsLegacy( $legacyOpts ) {
1305: $cfg = Validate::_extend( $legacyOpts, null, array(
1306: 'message' => "This field may not contain HTML."
1307: ) );
1308: $opts = Validate::_commonLegacy( $cfg );
1309:
1310: return Validate::noTags( $opts );
1311: }
1312:
1313: /**
1314: * @internal
1315: */
1316: static function dateFormatLegacy( $legacyOpts ) {
1317: $format = is_array($legacyOpts) ? $legacyOpts['format'] : $legacyOpts;
1318: $cfg = Validate::_extend( $legacyOpts, null, array(
1319: 'message' => "Date is not in the expected format."
1320: ) );
1321: $opts = Validate::_commonLegacy( $cfg );
1322:
1323: return Validate::dateFormat( $format, $opts );
1324: }
1325:
1326: /**
1327: * @internal
1328: */
1329: static function uniqueLegacy( $legacyOpts ) {
1330: $table = isset( $legacyOpts['table'] ) ? $legacyOpts['table'] : null;
1331: $column = isset( $legacyOpts['column'] ) ? $legacyOpts['column'] : null;
1332: $db = isset( $legacyOpts['db'] ) ? $legacyOpts['db'] : null;
1333: $cfg = Validate::_extend( $legacyOpts, null, array(
1334: 'message' => "This field must have a unique value."
1335: ) );
1336: $opts = Validate::_commonLegacy( $cfg );
1337:
1338: return Validate::unique( $opts, $column, $table, $db );
1339: }
1340:
1341: /**
1342: * @internal
1343: */
1344: static function dbValuesLegacy( $legacyOpts ) {
1345: $table = isset( $legacyOpts['table'] ) ? $legacyOpts['table'] : null;
1346: $column = isset( $legacyOpts['column'] ) ? $legacyOpts['column'] : null;
1347: $db = isset( $legacyOpts['db'] ) ? $legacyOpts['db'] : null;
1348: $values = isset( $legacyOpts['values'] ) ? $legacyOpts['values'] : array();
1349: $cfg = Validate::_extend( $legacyOpts, null, array(
1350: 'message' => "This value is not valid."
1351: ) );
1352: $opts = Validate::_commonLegacy( $cfg );
1353:
1354: return Validate::dbValues( $opts, $column, $table, $db, $values );
1355: }
1356: }
1357:
1358: