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:
17: /**
18: * Validation methods for DataTables Editor fields.
19: *
20: * These methods will typically be applied through the {@link Field::validator}
21: * method and thus the arguments to be passed will be automatically resolved
22: * by Editor.
23: *
24: * The validation methods in this class all take three parameters:
25: *
26: * 1. Data to be validated
27: * 2. Full data from the form (this can be used with a custom validation method
28: * for dependent validation).
29: * 3. Validation configuration options.
30: *
31: * When using the `Validate` class functions with the {@link Field::validator}
32: * method, the second parameter passed into {@link Field::validator} is given
33: * to the validation functions here as the third parameter. The first and
34: * second parameters are automatically resolved by the {@link Field} class.
35: *
36: * The validation configuration options is an array of options that can be used
37: * to customise the validation - for example defining a date format for date
38: * validation. Each validation method has the option of defining its own
39: * validation options, but all validation methods provide four common options:
40: *
41: * * `{boolean} optional` - Require the field to be submitted (`false`) or not
42: * (`true` - default). When set to `true` the field does not need to be
43: * included in the list of parameters sent by the client - if set to `false`
44: * then it must be included. This option can be be particularly used in Editor
45: * as Editor will not set a value for fields which have not been submitted -
46: * giving the ability to submit just a partial list of options.
47: * * `{boolean} empty` - Allow a field to be empty, i.e. a zero length string -
48: * `''` (`true` - default) or require it to be non-zero length (`false`).
49: * * `{boolean} required` - Short-cut for `optional=false` and `empty=false`.
50: * Note that if this option is set the `optional` and `empty` parameters are
51: * automatically set and cannot be overridden by passing in different values.
52: * * `{string} message` - Error message shown should validation fail. This
53: * provides complete control over the message shown to the end user, including
54: * internationalisation (i.e. to provide a translation that is not in the
55: * English language).
56: *
57: * @example
58: * <code>
59: * // Ensure that a non-empty value is given for a field
60: * Field::inst( 'engine' )->validator( 'Validate::required' )
61: * </code>
62: *
63: * @example
64: * <code>
65: * // Don't require a field to be submitted, but if it is submitted, it
66: * // must be non-empty
67: * Field::inst( 'reg_date' )->validator( 'Validate::notEmpty' )
68: *
69: * // or (this is identical in functionality to the line above):
70: * Field::inst( 'reg_date' )->validator( 'Validate::basic', array('empty'=>true) )
71: * </code>
72: *
73: * @example
74: * <code>
75: * // Date validation
76: * Field::inst( 'reg_date' )->validator( 'Validate::date_format', 'D, d M y' )
77: * </code>
78: *
79: * @example
80: * <code>
81: * // Date validation with a custom error message
82: * Field::inst( 'reg_date' )->validator( 'Validate::date_format', array(
83: * 'format' => 'D, d M y',
84: * 'message' => "Invalid date"
85: * ) )
86: * </code>
87: *
88: * @example
89: * <code>
90: * // Require a non-empty e-mail address
91: * Field::inst( 'reg_date' )->validator( 'Validate::email', array('required'=>true) )
92: *
93: * // or (this is identical in functionality to the line above):
94: * Field::inst( 'reg_date' )->validator( 'Validate::email', array(
95: * 'empty' => false,
96: * 'optional' => false
97: * ) )
98: * </code>
99: *
100: * @example
101: * <code>
102: * // Custom validation - closure
103: * Field::inst( 'engine' )->validator( function($val, $data, $opts) {
104: * if ( ! preg_match( '/^1/', $val ) ) {
105: * return "Value <b>must</b> start with a 1";
106: * }
107: * return true;
108: * } )
109: * </code>
110: */
111: class Validate {
112: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
113: * Internal functions
114: */
115:
116: /**
117: * "Magic" method to automatically apply the required check to any of the
118: * static methods simply by adding '_required' or '_empty' to the end of the
119: * method's name, depending on if you need the field to be submitted or not.
120: *
121: * This is retained for backwards compatibility, but the validation options
122: * are now the recommended way of indicating that a field is required.
123: *
124: * @internal
125: * @param string $name Function name
126: * @param array $arguments Function arguments
127: * @return mixed|string
128: */
129: public static function __callStatic( $name, $arguments ) {
130: if ( preg_match( '/_required$/', $name ) ) {
131: if ( $arguments[0] === null || $arguments[0] === '' ) {
132: return 'This field is required';
133: }
134:
135: return call_user_func_array(
136: __NAMESPACE__.'\Validate::'.str_replace( '_required', '', $name ),
137: $arguments
138: );
139: }
140: }
141:
142:
143: /**
144: * Extend the options from the user function and the validation function
145: * with core defaults.
146: *
147: * @internal
148: */
149: public static function _extend( $userOpts, $prop, $fnOpts ) {
150: $cfg = array(
151: 'message' => 'Input not valid',
152: 'required' => false,
153: 'empty' => true,
154: 'optional' => true
155: );
156:
157: if ( ! is_array( $userOpts ) ) {
158: if ( $prop ) {
159: $cfg[ $prop ] = $userOpts;
160: }
161:
162: // Not an array, but the non-array case has been handled above, so
163: // just an empty array
164: $userOpts = array();
165: }
166:
167: // Merge into a single array - first array gets priority if there is a
168: // key clash, so user first, then function commands and finally the
169: // global options
170: $cfg = $userOpts + $fnOpts + $cfg;
171:
172: return $cfg;
173: }
174:
175:
176: /**
177: * Perform common validation using the configuration parameters
178: *
179: * @internal
180: */
181: public static function _common( $val, $cfg ) {
182: // `required` is a shortcut for optional==false && empty==false
183: $optional = $cfg['required'] ? false : $cfg['optional'];
184: $empty = $cfg['required'] ? false : $cfg['empty'];
185:
186: // Error state tests
187: if ( ! $optional && $val === null ) {
188: // Value must be given
189: return $cfg['message'];
190: }
191: else if ( $empty === false && $val === '' ) {
192: // Value must not be empty
193: return $cfg['message'];
194: }
195:
196: // Validation passed states
197: if ( $optional && $val === null ) {
198: return true;
199: }
200: else if ( $empty === true && $val === '' ) {
201: return true;
202: }
203:
204: // Have the specific validation function perform its tests
205: return null;
206: }
207:
208:
209:
210: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
211: * Basic validation
212: */
213:
214: /**
215: * No validation - all inputs are valid.
216: * @param string $val The value to check for validity
217: * @param string[] $data The full data set submitted
218: * @param array $opts Validation options. No additional options are
219: * available or required for this validation method.
220: * @return true
221: */
222: public static function none( $val, $data, $opts, $host ) {
223: return true;
224: }
225:
226:
227: /**
228: * Basic validation - this is used to perform the validation provided by the
229: * validation options only. If the validation options pass (e.g. `required`,
230: * `empty` and `optional`) then the validation will pass regardless of the
231: * actual value.
232: *
233: * Note that there are two helper short-cut methods that provide the same
234: * function as this method, but are slightly shorter:
235: *
236: * ```
237: * // Required:
238: * Validate::required()
239: *
240: * // is the same as
241: * Validate::basic( $val, $data, array(
242: * "required" => true
243: * );
244: * ```
245: *
246: * ```
247: * // Optional, but not empty if given:
248: * Validate::notEmpty()
249: *
250: * // is the same as
251: * Validate::basic( $val, $data, array(
252: * "empty" => false
253: * );
254: * ```
255: *
256: * @param string $val The value to check for validity
257: * @param string[] $data The full data set submitted
258: * @param array $opts Validation options. No additional options are
259: * available or required for this validation method.
260: * @param array $host Host information
261: * @return string|true true if the value is valid, a string with an error
262: * message otherwise.
263: */
264: static function basic( $val, $data, $opts, $host ) {
265: // Common validation with user override option
266: $cfg = Validate::_extend( $opts, null, array() );
267:
268: $common = Validate::_common( $val, $cfg );
269: if ( $common !== null ) {
270: return $common;
271: }
272:
273: return true;
274: }
275:
276:
277: /**
278: * Required field - there must be a value and it must be a non-empty value
279: *
280: * This is a helper short-cut method which is the same as:
281: *
282: * ```
283: * Validate::basic( $val, $data, array(
284: * "required" => true
285: * );
286: * ```
287: *
288: * @param string $val The value to check for validity
289: * @param string[] $data The full data set submitted
290: * @param array $opts Validation options. No additional options are
291: * available or required for this validation method.
292: * @param array $host Host information
293: * @return string|true true if the value is valid, a string with an error
294: * message otherwise.
295: */
296: static function required( $val, $data, $opts, $host ) {
297: $cfg = Validate::_extend( $opts, null, array(
298: 'message' => "This field is required",
299: 'required' => true
300: ) );
301:
302: $common = Validate::_common( $val, $cfg );
303: return $common !== null ?
304: $common :
305: true;
306: }
307:
308:
309: /**
310: * Optional field, but if given there must be a non-empty value
311: *
312: * This is a helper short-cut method which is the same as:
313: *
314: * ```
315: * Validate::basic( $val, $data, array(
316: * "empty" => false
317: * );
318: * ```
319: *
320: * @param string $val The value to check for validity
321: * @param string[] $data The full data set submitted
322: * @param array $opts Validation options. No additional options are
323: * available or required for this validation method.
324: * @param array $host Host information
325: * @return string|true true if the value is valid, a string with an error
326: * message otherwise.
327: */
328: static function notEmpty( $val, $data, $opts, $host ) {
329: // Supplying this field is optional, but if given, then it must be
330: // non-empty (user can override by passing in `empty=true` in opts
331: // at which point there is basically no validation
332: $cfg = Validate::_extend( $opts, null, array(
333: 'message' => "This field is required",
334: 'empty' => false
335: ) );
336:
337: $common = Validate::_common( $val, $cfg );
338: return $common !== null ?
339: $common :
340: true;
341: }
342:
343:
344: /**
345: * Validate an input as a boolean value.
346: *
347: * @param string $val The value to check for validity
348: * @param string[] $data The full data set submitted
349: * @param array $opts Validation options. No additional options are
350: * available or required for this validation method.
351: * @param array $host Host information
352: * @return string|true true if the value is valid, a string with an error
353: * message otherwise.
354: */
355: public static function boolean( $val, $data, $opts, $host ) {
356: $cfg = Validate::_extend( $opts, null, array(
357: 'message' => "Please enter true or false"
358: ) );
359:
360: $common = Validate::_common( $val, $cfg );
361: if ( $common !== null ) {
362: return $common;
363: }
364:
365: if ( filter_var($val, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) === false ) {
366: return $cfg['message'];
367: }
368: return true;
369: }
370:
371:
372:
373: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
374: * Number validation methods
375: */
376:
377: /**
378: * Check that any input is numeric.
379: *
380: * @param string $val The value to check for validity
381: * @param string[] $data The full data set submitted
382: * @param array $opts Validation options. Additional options:
383: * * `decimal`: is available to indicate what character should be used
384: * as the decimal
385: * @param array $host Host information
386: * @return string|true true if the value is valid, a string with an error
387: * message otherwise.
388: */
389: public static function numeric ( $val, $data, $opts, $host ) {
390: $cfg = Validate::_extend( $opts, null, array(
391: 'message' => "This input must be given as a number",
392: 'decimal' => '.'
393: ) );
394:
395: $common = Validate::_common( $val, $cfg );
396: if ( $common !== null ) {
397: return $common;
398: }
399:
400: if ( $cfg['decimal'] !== '.' ) {
401: $val = str_replace( $cfg['decimal'], '.', $val );
402: }
403:
404: return ! is_numeric( $val ) ?
405: $cfg['message'] :
406: true;
407: }
408:
409: /**
410: * Check for a numeric input and that it is greater than a given value.
411: *
412: * @param string $val The value to check for validity
413: * @param string[] $data The full data set submitted
414: * @param int|array $opts Validation options. Additional options:
415: * * `min`: indicate the minimum value. If only the default validation
416: * options are required, this parameter can be given as an integer
417: * value, which will be used as the minimum value.
418: * * `decimal`: is available to indicate what character should be used
419: * as the decimal
420: * separator (default '.').
421: * @param array $host Host information
422: * @return string|true true if the value is valid, a string with an error
423: * message otherwise.
424: */
425: public static function minNum ( $val, $data, $opts, $host ) {
426: // `numeric` will do the common validation for us
427: $numeric = Validate::numeric( $val, $data, $opts, $host );
428: if ( $numeric !== true ) {
429: return $numeric;
430: }
431:
432: $min = is_array($opts) ? $opts['min'] : $opts;
433: $cfg = Validate::_extend( $opts, 'min', array(
434: 'message' => "Number is too small, must be ".$min." or larger",
435: 'decimal' => '.'
436: ) );
437:
438: if ( $cfg['decimal'] !== '.' ) {
439: $val = str_replace( $cfg['decimal'], '.', $val );
440: }
441:
442: return $val < $min ?
443: $cfg['message'] :
444: true;
445: }
446:
447: /**
448: * Check for a numeric input and that it is less than a given value.
449: *
450: * @param string $val The value to check for validity
451: * @param string[] $data The full data set submitted
452: * @param int|array $opts Validation options.
453: * * `max`: indicate the maximum value. If only the default validation
454: * options are required, this parameter can be given as an integer
455: * value, which will be used as the maximum value.
456: * * `decimal`: is available to indicate what character should be used
457: * as the decimal
458: * @param array $host Host information
459: * @return string|true true if the value is valid, a string with an error
460: * message otherwise.
461: */
462: public static function maxNum ( $val, $data, $opts, $host ) {
463: // `numeric` will do the common validation for us
464: $numeric = Validate::numeric( $val, $data, $opts, $host );
465: if ( $numeric !== true ) {
466: return $numeric;
467: }
468:
469: $max = is_array($opts) ? $opts['max'] : $opts;
470: $cfg = Validate::_extend( $opts, 'min', array(
471: 'message' => "Number is too large, must be ".$max." or smaller",
472: 'decimal' => '.'
473: ) );
474:
475: if ( $cfg['decimal'] !== '.' ) {
476: $val = str_replace( $cfg['decimal'], '.', $val );
477: }
478:
479: return $val > $max ?
480: $cfg['message'] :
481: true;
482: }
483:
484:
485: /**
486: * Check for a numeric input and that it is both greater and smaller than
487: * given numbers.
488: *
489: * @param string $val The value to check for validity
490: * @param string[] $data The full data set submitted
491: * @param int|array $opts Validation options. Additional options:
492: * * `min`: indicate the minimum value.
493: * * `max`: indicate the maximum value.
494: * * `decimal`: is available to indicate what character should be used
495: * as the decimal
496: * @param array $host Host information
497: * @return string|true true if the value is valid, a string with an error
498: * message otherwise.
499: */
500: public static function minMaxNum ( $val, $data, $opts, $host ) {
501: $numeric = Validate::numeric( $val, $data, $opts, $host );
502: if ( $numeric !== true ) {
503: return $numeric;
504: }
505:
506: $min = Validate::minNum( $val, $data, $opts, $host );
507: if ( $min !== true ) {
508: return $min;
509: }
510:
511: return Validate::maxNum( $val, $data, $opts, $host );
512: }
513:
514:
515:
516: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
517: * String validation methods
518: */
519:
520: /**
521: * Validate an input as an e-mail address.
522: *
523: * @param string $val The value to check for validity
524: * @param string[] $data The full data set submitted
525: * @param array $opts Validation options. No additional options are
526: * available or required for this validation method.
527: * @param array $host Host information
528: * @return string|true true if the value is valid, a string with an error
529: * message otherwise.
530: */
531: public static function email( $val, $data, $opts, $host ) {
532: $cfg = Validate::_extend( $opts, null, array(
533: 'message' => "Please enter a valid e-mail address"
534: ) );
535:
536: $common = Validate::_common( $val, $cfg );
537: if ( $common !== null ) {
538: return $common;
539: }
540:
541: return filter_var($val, FILTER_VALIDATE_EMAIL) !== false ?
542: true :
543: $cfg['message'];
544: }
545:
546:
547: /**
548: * Validate a string has a minimum length.
549: *
550: * @param string $val The value to check for validity
551: * @param string[] $data The full data set submitted
552: * @param int|array $opts Validation options. The additional option of
553: * `min` is available for this method to indicate the minimum string
554: * length. If only the default validation options are required, this
555: * parameter can be given as an integer value, which will be used as the
556: * minimum string length.
557: * @param array $host Host information
558: * @return string|true true if the value is valid, a string with an error
559: * message otherwise.
560: */
561: public static function minLen( $val, $data, $opts, $host ) {
562: $min = is_array($opts) ? $opts['min'] : $opts;
563: $cfg = Validate::_extend( $opts, null, array(
564: 'message' => "The input is too short. ".$min." characters required (".($min-strlen($val))." more required)"
565: ) );
566:
567: $common = Validate::_common( $val, $cfg );
568: if ( $common !== null ) {
569: return $common;
570: }
571:
572: return strlen( $val ) < $min ?
573: $cfg['message'] :
574: true;
575: }
576:
577:
578: /**
579: * Validate a string does not exceed a maximum length.
580: *
581: * @param string $val The value to check for validity
582: * @param string[] $data The full data set submitted
583: * @param int|array $opts Validation options. The additional option of
584: * `max` is available for this method to indicate the maximum string
585: * length. If only the default validation options are required, this
586: * parameter can be given as an integer value, which will be used as the
587: * maximum string length.
588: * @param array $host Host information
589: * @return string|true true if the value is valid, a string with an error
590: * message otherwise.
591: */
592: public static function maxLen( $val, $data, $opts, $host ) {
593: $max = is_array($opts) ? $opts['max'] : $opts;
594: $cfg = Validate::_extend( $opts, null, array(
595: 'message' => "The input is ".(strlen($val)-$max)." characters too long"
596: ) );
597:
598: $common = Validate::_common( $val, $cfg );
599: if ( $common !== null ) {
600: return $common;
601: }
602:
603: return strlen( $val ) > $max ?
604: $cfg['message'] :
605: true;
606: }
607:
608: /**
609: * Require a string with a certain minimum or maximum number of characters.
610: *
611: * @param string $val The value to check for validity
612: * @param string[] $data The full data set submitted
613: * @param int|array $opts Validation options. The additional options of
614: * `min` and `max` are available for this method to indicate the minimum
615: * and maximum string lengths, respectively.
616: * @param array $host Host information
617: * @return string|true true if the value is valid, a string with an error
618: * message otherwise.
619: */
620: public static function minMaxLen( $val, $data, $opts, $host ) {
621: $min = Validate::minLen( $val, $data, $opts, $host );
622: if ( $min !== true ) {
623: return $min;
624: }
625:
626: return Validate::maxLen( $val, $data, $opts, $host );
627: }
628:
629:
630: /**
631: * Validate as an IP address.
632: *
633: * @param string $val The value to check for validity
634: * @param string[] $data The full data set submitted
635: * @param array $opts Validation options. No additional options are
636: * available or required for this validation method.
637: * @param array $host Host information
638: * @return string|true true if the value is valid, a string with an error
639: * message otherwise.
640: */
641: public static function ip( $val, $data, $opts, $host ) {
642: $cfg = Validate::_extend( $opts, null, array(
643: 'message' => "Please enter a valid IP address"
644: ) );
645:
646: $common = Validate::_common( $val, $cfg );
647: if ( $common !== null ) {
648: return $common;
649: }
650:
651: return filter_var($val, FILTER_VALIDATE_IP) === false ?
652: $cfg['message'] :
653: true;
654: }
655:
656:
657: /**
658: * Validate as an URL address.
659: *
660: * @param string $val The value to check for validity
661: * @param string[] $data The full data set submitted
662: * @param array $opts Validation options. No additional options are
663: * available or required for this validation method.
664: * @param array $host Host information
665: * @return string|true true if the value is valid, a string with an error
666: * message otherwise.
667: */
668: public static function url( $val, $data, $opts, $host ) {
669: $cfg = Validate::_extend( $opts, null, array(
670: 'message' => "Please enter a valid URL"
671: ) );
672:
673: $common = Validate::_common( $val, $cfg );
674: if ( $common !== null ) {
675: return $common;
676: }
677:
678: return filter_var($val, FILTER_VALIDATE_URL) === false ?
679: $cfg['message'] :
680: true;
681: }
682:
683:
684: /**
685: * Check if string could contain an XSS attack string
686: *
687: * @param string $val The value to check for validity
688: * @param string[] $data The full data set submitted
689: * @param int|array $opts Validation options. The additional options of
690: * `db` - database connection object, `table` - database table to use and
691: * `column` - the column to check this value against as value, are also
692: * available. These options are not required and if not given are
693: * automatically derived from the Editor and Field instances.
694: * @param array $host Host information
695: * @return string|true true if the value is valid, a string with an error
696: * message otherwise.
697: */
698: public static function xss ( $val, $data, $opts, $host ) {
699: $cfg = Validate::_extend( $opts, null, array(
700: 'message' => 'This field contains potentially unsafe data'
701: ) );
702:
703: $common = Validate::_common( $val, $cfg );
704: if ( $common !== null ) {
705: return $common;
706: }
707:
708: $field = $host['field'];
709: return $field->xssSafety( $val ) != $val ?
710: $cfg['message'] :
711: true;
712: }
713:
714:
715: /**
716: * Confirm that the value submitted is in a list of allowable values
717: *
718: * @param string $val The value to check for validity
719: * @param string[] $data The full data set submitted
720: * @param int|array $opts Validation options. The additional options of
721: * `db` - database connection object, `table` - database table to use and
722: * `column` - the column to check this value against as value, are also
723: * available. These options are not required and if not given are
724: * automatically derived from the Editor and Field instances.
725: * @param array $host Host information
726: * @return string|true true if the value is valid, a string with an error
727: * message otherwise.
728: */
729: public static function values( $val, $data, $opts, $host ) {
730: $cfg = Validate::_extend( $opts, null, array(
731: 'message' => 'This value is not valid',
732: 'valid' => array()
733: ) );
734:
735: $common = Validate::_common( $val, $cfg );
736: if ( $common !== null ) {
737: return $common;
738: }
739:
740: // Allow local values to be defined - for example null
741: return in_array($val, $cfg['valid']) ?
742: true :
743: $cfg['message'];
744: }
745:
746:
747: /**
748: * Check if there are any tags in the submitted value
749: *
750: * @param string $val The value to check for validity
751: * @param string[] $data The full data set submitted
752: * @param int|array $opts Validation options. The additional options of
753: * `db` - database connection object, `table` - database table to use and
754: * `column` - the column to check this value against as value, are also
755: * available. These options are not required and if not given are
756: * automatically derived from the Editor and Field instances.
757: * @param array $host Host information
758: * @return string|true true if the value is valid, a string with an error
759: * message otherwise.
760: */
761: public static function noTags ( $val, $data, $opts, $host ) {
762: $cfg = Validate::_extend( $opts, null, array(
763: 'message' => 'This field may not contain HTML'
764: ) );
765:
766: $common = Validate::_common( $val, $cfg );
767: if ( $common !== null ) {
768: return $common;
769: }
770:
771: return strip_tags( $val ) != $val ?
772: $cfg['message'] :
773: true;
774: }
775:
776:
777:
778: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
779: * Date validation methods
780: */
781:
782: /**
783: * Check that a valid date input is given
784: *
785: * @param string $val The value to check for validity
786: * @param string[] $data The full data set submitted
787: * @param array|string $opts If given as a string, then $opts is the date
788: * format to check the validity of. If given as an array, then the
789: * date format is in the 'format' parameter, and the return error
790: * message in the 'message' parameter.
791: * @param array $host Host information
792: * @return string|true true if the value is valid, a string with an error
793: * message otherwise.
794: */
795: public static function dateFormat( $val, $data, $opts, $host ) {
796: $cfg = Validate::_extend( $opts, 'format', array(
797: 'message' => "Date is not in the expected format"
798: ) );
799:
800: $common = Validate::_common( $val, $cfg );
801: if ( $common !== null ) {
802: return $common;
803: }
804:
805: $date = \DateTime::createFromFormat( $cfg['format'], $val) ;
806:
807: return $date && $date->format( $cfg['format'] ) == $val ?
808: true :
809: $cfg['message'];
810: }
811:
812:
813: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
814: * Database validation methods
815: */
816:
817: /**
818: * Check that the given value is unique in the database
819: *
820: * @param string $val The value to check for validity
821: * @param string[] $data The full data set submitted
822: * @param int|array $opts Validation options. The additional options of
823: * `db` - database connection object, `table` - database table to use and
824: * `column` - the column to check this value against as value, are also
825: * available. These options are not required and if not given are
826: * automatically derived from the Editor and Field instances.
827: * @param array $host Host information
828: * @return string|true true if the value is valid, a string with an error
829: * message otherwise.
830: */
831: public static function unique( $val, $data, $opts, $host ) {
832: $cfg = Validate::_extend( $opts, null, array(
833: 'message' => 'This field must have a unique value',
834: 'db' => null,
835: 'table' => null,
836: 'field' => null
837: ) );
838:
839: $common = Validate::_common( $val, $cfg );
840: if ( $common !== null ) {
841: return $common;
842: }
843:
844: $editor = $host['editor'];
845: $field = $host['field'];
846:
847: $db = $cfg['db'] ?
848: $cfg['db'] :
849: $host['db'];
850:
851: $table = $cfg['table'] ?
852: $cfg['table'] :
853: $editor->table(); // Returns an array, but `select` will take an array
854:
855: $column = $cfg['field'] ?
856: $cfg['field'] :
857: $field->dbField();
858:
859: $query = $db
860: ->query( 'select', $table )
861: ->get( $column )
862: ->where( $column, $val );
863:
864: // If doing an edit, then we need to also discount the current row,
865: // since it is of course already validly unique
866: if ( $host['action'] === 'edit' ) {
867: $cond = $editor->pkeyToArray( $host['id'], true );
868: $query->where( $cond, null, '!=' );
869: }
870:
871: $res = $query->exec();
872:
873: return $res->count() === 0 ?
874: true :
875: $cfg['message'];
876: }
877:
878: /**
879: * Check that the given value is a value that is available in a database -
880: * i.e. a join primary key. This will attempt to automatically use the table
881: * name and value column from the field's `options` method (under the
882: * assumption that it will typically be used with a joined field), but the
883: * table and field can also be specified via the options.
884: *
885: * @param string $val The value to check for validity
886: * @param string[] $data The full data set submitted
887: * @param int|array $opts Validation options. The additional options of
888: * `db` - database connection object, `table` - database table to use and
889: * `column` - the column to check this value against as value, are also
890: * available. These options are not required and if not given are
891: * automatically derived from the Editor and Field instances.
892: * @param array $host Host information
893: * @return string|true true if the value is valid, a string with an error
894: * message otherwise.
895: */
896: public static function dbValues( $val, $data, $opts, $host ) {
897: $cfg = Validate::_extend( $opts, null, array(
898: 'empty' => null,
899: 'message' => 'This value is not valid',
900: 'db' => null,
901: 'table' => null,
902: 'field' => null,
903: 'valid' => array()
904: ) );
905:
906: $common = Validate::_common( $val, $cfg );
907: if ( $common !== null ) {
908: return $common;
909: }
910:
911: // Allow local values to be defined - for example null
912: if ( in_array($val, $cfg['valid']) ) {
913: return true;
914: }
915:
916: $editor = $host['editor'];
917: $field = $host['field'];
918: $options = $field->options();
919:
920: $db = $cfg['db'] ? $cfg['db'] : $host['db'];
921: $table = $cfg['table'] ? $cfg['table'] : $options->table();
922: $column = $cfg['field'] ? $cfg['field'] : $options->value();
923:
924: if ( ! $table ) {
925: throw new \Exception('Table for database value check is not defined for field '.$field->name());
926: }
927:
928: if ( ! $column ) {
929: throw new \Exception('Value column for database value check is not defined for field '.$field->name());
930: }
931:
932: // Try / catch in case the submitted value can't be represented as the
933: // database type (e.g. an empty string as an integer)
934: try {
935: $count = $db
936: ->query( 'select', $table )
937: ->get( $column )
938: ->where( $column, $val )
939: ->exec()
940: ->count();
941:
942: return $count === 0 ?
943: $cfg['message'] :
944: true;
945: }
946: catch (\Exception $e) {
947: return $cfg['message'];
948: }
949: }
950: }
951:
952: