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. No additional options are
383: * available or required for this validation method.
384: * @param array $host Host information
385: * @return string|true true if the value is valid, a string with an error
386: * message otherwise.
387: */
388: public static function numeric ( $val, $data, $opts, $host ) {
389: $cfg = Validate::_extend( $opts, null, array(
390: 'message' => "This input must be given as a number"
391: ) );
392:
393: $common = Validate::_common( $val, $cfg );
394: if ( $common !== null ) {
395: return $common;
396: }
397:
398: return ! is_numeric( $val ) ?
399: $cfg['message'] :
400: true;
401: }
402:
403: /**
404: * Check for a numeric input and that it is greater than a given value.
405: *
406: * @param string $val The value to check for validity
407: * @param string[] $data The full data set submitted
408: * @param int|array $opts Validation options. The additional option of
409: * `min` is available for this method to indicate the minimum value. If
410: * only the default validation options are required, this parameter can
411: * be given as an integer value, which will be used as the minimum value.
412: * @param array $host Host information
413: * @return string|true true if the value is valid, a string with an error
414: * message otherwise.
415: */
416: public static function minNum ( $val, $data, $opts, $host ) {
417: // `numeric` will do the common validation for us
418: $numeric = Validate::numeric( $val, $data, $opts, $host );
419: if ( $numeric !== true ) {
420: return $numeric;
421: }
422:
423: $min = is_array($opts) ? $opts['min'] : $opts;
424: $cfg = Validate::_extend( $opts, 'min', array(
425: 'message' => "Number is too small, must be ".$min." or larger"
426: ) );
427:
428: return $val < $min ?
429: $cfg['message'] :
430: true;
431: }
432:
433: /**
434: * Check for a numeric input and that it is less than a given value.
435: *
436: * @param string $val The value to check for validity
437: * @param string[] $data The full data set submitted
438: * @param int|array $opts Validation options. The additional option of
439: * `max` is available for this method to indicate the maximum value. If
440: * only the default validation options are required, this parameter can
441: * be given as an integer value, which will be used as the maximum value.
442: * @param array $host Host information
443: * @return string|true true if the value is valid, a string with an error
444: * message otherwise.
445: */
446: public static function maxNum ( $val, $data, $opts, $host ) {
447: // `numeric` will do the common validation for us
448: $numeric = Validate::numeric( $val, $data, $opts, $host );
449: if ( $numeric !== true ) {
450: return $numeric;
451: }
452:
453: $max = is_array($opts) ? $opts['max'] : $opts;
454: $cfg = Validate::_extend( $opts, 'min', array(
455: 'message' => "Number is too large, must be ".$max." or smaller"
456: ) );
457:
458: return $val > $max ?
459: $cfg['message'] :
460: true;
461: }
462:
463:
464: /**
465: * Check for a numeric input and that it is both greater and smaller than
466: * given numbers.
467: *
468: * @param string $val The value to check for validity
469: * @param string[] $data The full data set submitted
470: * @param int|array $opts Validation options. The additional options of
471: * `min` and `max` are available for this method to indicate the minimum
472: * and maximum values, respectively.
473: * @param array $host Host information
474: * @return string|true true if the value is valid, a string with an error
475: * message otherwise.
476: */
477: public static function minMaxNum ( $val, $data, $opts, $host ) {
478: $numeric = Validate::numeric( $val, $data, $opts, $host );
479: if ( $numeric !== true ) {
480: return $numeric;
481: }
482:
483: $min = Validate::minNum( $val, $data, $opts, $host );
484: if ( $min !== true ) {
485: return $min;
486: }
487:
488: return Validate::maxNum( $val, $data, $opts, $host );
489: }
490:
491:
492:
493: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
494: * String validation methods
495: */
496:
497: /**
498: * Validate an input as an e-mail address.
499: *
500: * @param string $val The value to check for validity
501: * @param string[] $data The full data set submitted
502: * @param array $opts Validation options. No additional options are
503: * available or required for this validation method.
504: * @param array $host Host information
505: * @return string|true true if the value is valid, a string with an error
506: * message otherwise.
507: */
508: public static function email( $val, $data, $opts, $host ) {
509: $cfg = Validate::_extend( $opts, null, array(
510: 'message' => "Please enter a valid e-mail address"
511: ) );
512:
513: $common = Validate::_common( $val, $cfg );
514: if ( $common !== null ) {
515: return $common;
516: }
517:
518: return filter_var($val, FILTER_VALIDATE_EMAIL) !== false ?
519: true :
520: $cfg['message'];
521: }
522:
523:
524: /**
525: * Validate a string has a minimum length.
526: *
527: * @param string $val The value to check for validity
528: * @param string[] $data The full data set submitted
529: * @param int|array $opts Validation options. The additional option of
530: * `min` is available for this method to indicate the minimum string
531: * length. If only the default validation options are required, this
532: * parameter can be given as an integer value, which will be used as the
533: * minimum string length.
534: * @param array $host Host information
535: * @return string|true true if the value is valid, a string with an error
536: * message otherwise.
537: */
538: public static function minLen( $val, $data, $opts, $host ) {
539: $min = is_array($opts) ? $opts['min'] : $opts;
540: $cfg = Validate::_extend( $opts, null, array(
541: 'message' => "The input is too short. ".$min." characters required (".($min-strlen($val))." more required)"
542: ) );
543:
544: $common = Validate::_common( $val, $cfg );
545: if ( $common !== null ) {
546: return $common;
547: }
548:
549: return strlen( $val ) < $min ?
550: $cfg['message'] :
551: true;
552: }
553:
554:
555: /**
556: * Validate a string does not exceed a maximum length.
557: *
558: * @param string $val The value to check for validity
559: * @param string[] $data The full data set submitted
560: * @param int|array $opts Validation options. The additional option of
561: * `max` is available for this method to indicate the maximum string
562: * length. If only the default validation options are required, this
563: * parameter can be given as an integer value, which will be used as the
564: * maximum string length.
565: * @param array $host Host information
566: * @return string|true true if the value is valid, a string with an error
567: * message otherwise.
568: */
569: public static function maxLen( $val, $data, $opts, $host ) {
570: $max = is_array($opts) ? $opts['max'] : $opts;
571: $cfg = Validate::_extend( $opts, null, array(
572: 'message' => "The input is ".(strlen($val)-$max)." characters too long"
573: ) );
574:
575: $common = Validate::_common( $val, $cfg );
576: if ( $common !== null ) {
577: return $common;
578: }
579:
580: return strlen( $val ) > $max ?
581: $cfg['message'] :
582: true;
583: }
584:
585: /**
586: * Require a string with a certain minimum or maximum number of characters.
587: *
588: * @param string $val The value to check for validity
589: * @param string[] $data The full data set submitted
590: * @param int|array $opts Validation options. The additional options of
591: * `min` and `max` are available for this method to indicate the minimum
592: * and maximum string lengths, respectively.
593: * @param array $host Host information
594: * @return string|true true if the value is valid, a string with an error
595: * message otherwise.
596: */
597: public static function minMaxLen( $val, $data, $opts, $host ) {
598: $min = Validate::minLen( $val, $data, $opts, $host );
599: if ( $min !== true ) {
600: return $min;
601: }
602:
603: return Validate::maxLen( $val, $data, $opts, $host );
604: }
605:
606:
607: /**
608: * Validate as an IP address.
609: *
610: * @param string $val The value to check for validity
611: * @param string[] $data The full data set submitted
612: * @param array $opts Validation options. No additional options are
613: * available or required for this validation method.
614: * @param array $host Host information
615: * @return string|true true if the value is valid, a string with an error
616: * message otherwise.
617: */
618: public static function ip( $val, $data, $opts, $host ) {
619: $cfg = Validate::_extend( $opts, null, array(
620: 'message' => "Please enter a valid IP address"
621: ) );
622:
623: $common = Validate::_common( $val, $cfg );
624: if ( $common !== null ) {
625: return $common;
626: }
627:
628: return filter_var($val, FILTER_VALIDATE_IP) === false ?
629: $cfg['message'] :
630: true;
631: }
632:
633:
634: /**
635: * Validate as an URL address.
636: *
637: * @param string $val The value to check for validity
638: * @param string[] $data The full data set submitted
639: * @param array $opts Validation options. No additional options are
640: * available or required for this validation method.
641: * @param array $host Host information
642: * @return string|true true if the value is valid, a string with an error
643: * message otherwise.
644: */
645: public static function url( $val, $data, $opts, $host ) {
646: $cfg = Validate::_extend( $opts, null, array(
647: 'message' => "Please enter a valid URL"
648: ) );
649:
650: $common = Validate::_common( $val, $cfg );
651: if ( $common !== null ) {
652: return $common;
653: }
654:
655: return filter_var($val, FILTER_VALIDATE_URL) === false ?
656: $cfg['message'] :
657: true;
658: }
659:
660:
661: /**
662: * Check if string could contain an XSS attack string
663: *
664: * @param string $val The value to check for validity
665: * @param string[] $data The full data set submitted
666: * @param int|array $opts Validation options. The additional options of
667: * `db` - database connection object, `table` - database table to use and
668: * `column` - the column to check this value against as value, are also
669: * available. These options are not required and if not given are
670: * automatically derived from the Editor and Field instances.
671: * @param array $host Host information
672: * @return string|true true if the value is valid, a string with an error
673: * message otherwise.
674: */
675: public static function xss ( $val, $data, $opts, $host ) {
676: $cfg = Validate::_extend( $opts, null, array(
677: 'message' => 'This field contains potentially unsafe data',
678: 'db' => null,
679: 'table' => null,
680: 'field' => null
681: ) );
682:
683: $common = Validate::_common( $val, $cfg );
684: if ( $common !== null ) {
685: return $common;
686: }
687:
688: $editor = $host['editor'];
689: $field = $host['field'];
690:
691: return $field->xssSafety( $val ) != $val ?
692: $cfg['message'] :
693: true;
694: }
695:
696:
697: /**
698: * Confirm that the value submitted is in a list of allowable values
699: *
700: * @param string $val The value to check for validity
701: * @param string[] $data The full data set submitted
702: * @param int|array $opts Validation options. The additional options of
703: * `db` - database connection object, `table` - database table to use and
704: * `column` - the column to check this value against as value, are also
705: * available. These options are not required and if not given are
706: * automatically derived from the Editor and Field instances.
707: * @param array $host Host information
708: * @return string|true true if the value is valid, a string with an error
709: * message otherwise.
710: */
711: public static function values( $val, $data, $opts, $host ) {
712: $cfg = Validate::_extend( $opts, null, array(
713: 'empty' => null,
714: 'message' => 'This value is not valid',
715: 'db' => null,
716: 'table' => null,
717: 'field' => null,
718: 'valid' => array()
719: ) );
720:
721: $common = Validate::_common( $val, $cfg );
722: if ( $common !== null ) {
723: return $common;
724: }
725:
726: // Allow local values to be defined - for example null
727: return in_array($val, $cfg['valid']) ?
728: true :
729: $cfg['message'];
730: }
731:
732:
733: /**
734: * Check if there are any tags in the submitted value
735: *
736: * @param string $val The value to check for validity
737: * @param string[] $data The full data set submitted
738: * @param int|array $opts Validation options. The additional options of
739: * `db` - database connection object, `table` - database table to use and
740: * `column` - the column to check this value against as value, are also
741: * available. These options are not required and if not given are
742: * automatically derived from the Editor and Field instances.
743: * @param array $host Host information
744: * @return string|true true if the value is valid, a string with an error
745: * message otherwise.
746: */
747: public static function noTags ( $val, $data, $opts, $host ) {
748: $cfg = Validate::_extend( $opts, null, array(
749: 'message' => 'This field may not contain HTML',
750: 'db' => null,
751: 'table' => null,
752: 'field' => null
753: ) );
754:
755: $common = Validate::_common( $val, $cfg );
756: if ( $common !== null ) {
757: return $common;
758: }
759:
760: return strip_tags( $val ) != $val ?
761: $cfg['message'] :
762: true;
763: }
764:
765:
766:
767: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
768: * Date validation methods
769: */
770:
771: /**
772: * Check that a valid date input is given
773: *
774: * @param string $val The value to check for validity
775: * @param string[] $data The full data set submitted
776: * @param array|string $opts If given as a string, then $opts is the date
777: * format to check the validity of. If given as an array, then the
778: * date format is in the 'format' parameter, and the return error
779: * message in the 'message' parameter.
780: * @param array $host Host information
781: * @return string|true true if the value is valid, a string with an error
782: * message otherwise.
783: */
784: public static function dateFormat( $val, $data, $opts, $host ) {
785: $cfg = Validate::_extend( $opts, 'format', array(
786: 'message' => "Date is not in the expected format"
787: ) );
788:
789: $common = Validate::_common( $val, $cfg );
790: if ( $common !== null ) {
791: return $common;
792: }
793:
794: $date = \DateTime::createFromFormat( $cfg['format'], $val) ;
795:
796: return $date && $date->format( $cfg['format'] ) == $val ?
797: true :
798: $cfg['message'];
799: }
800:
801:
802: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
803: * Database validation methods
804: */
805:
806: /**
807: * Check that the given value is unique in the database
808: *
809: * @param string $val The value to check for validity
810: * @param string[] $data The full data set submitted
811: * @param int|array $opts Validation options. The additional options of
812: * `db` - database connection object, `table` - database table to use and
813: * `column` - the column to check this value against as value, are also
814: * available. These options are not required and if not given are
815: * automatically derived from the Editor and Field instances.
816: * @param array $host Host information
817: * @return string|true true if the value is valid, a string with an error
818: * message otherwise.
819: */
820: public static function unique( $val, $data, $opts, $host ) {
821: $cfg = Validate::_extend( $opts, null, array(
822: 'message' => 'This field must have a unique value',
823: 'db' => null,
824: 'table' => null,
825: 'field' => null
826: ) );
827:
828: $common = Validate::_common( $val, $cfg );
829: if ( $common !== null ) {
830: return $common;
831: }
832:
833: $editor = $host['editor'];
834: $field = $host['field'];
835:
836: $db = $cfg['db'] ?
837: $cfg['db'] :
838: $host['db'];
839:
840: $table = $cfg['table'] ?
841: $cfg['table'] :
842: $editor->table(); // Returns an array, but `select` will take an array
843:
844: $column = $cfg['field'] ?
845: $cfg['field'] :
846: $field->dbField();
847:
848: $query = $db
849: ->query( 'select', $table )
850: ->get( $column )
851: ->where( $column, $val );
852:
853: // If doing an edit, then we need to also discount the current row,
854: // since it is of course already validly unique
855: if ( $host['action'] === 'edit' ) {
856: $query->where( $editor->pkey(), $host['id'], '!=' );
857: }
858:
859: $res = $query->exec();
860:
861: return $res->count() === 0 ?
862: true :
863: $cfg['message'];
864: }
865:
866: /**
867: * Check that the given value is a value that is available in a database -
868: * i.e. a join primary key. This will attempt to automatically use the table
869: * name and value column from the field's `options` method (under the
870: * assumption that it will typically be used with a joined field), but the
871: * table and field can also be specified via the options.
872: *
873: * @param string $val The value to check for validity
874: * @param string[] $data The full data set submitted
875: * @param int|array $opts Validation options. The additional options of
876: * `db` - database connection object, `table` - database table to use and
877: * `column` - the column to check this value against as value, are also
878: * available. These options are not required and if not given are
879: * automatically derived from the Editor and Field instances.
880: * @param array $host Host information
881: * @return string|true true if the value is valid, a string with an error
882: * message otherwise.
883: */
884: public static function dbValues( $val, $data, $opts, $host ) {
885: $cfg = Validate::_extend( $opts, null, array(
886: 'empty' => null,
887: 'message' => 'This value is not valid',
888: 'db' => null,
889: 'table' => null,
890: 'field' => null,
891: 'valid' => array()
892: ) );
893:
894: $common = Validate::_common( $val, $cfg );
895: if ( $common !== null ) {
896: return $common;
897: }
898:
899: // Allow local values to be defined - for example null
900: if ( in_array($val, $cfg['valid']) ) {
901: return true;
902: }
903:
904: $editor = $host['editor'];
905: $field = $host['field'];
906:
907: $db = $cfg['db'] ? $cfg['db'] : $host['db'];
908: $table = $cfg['table'] ? $cfg['table'] : $field->optsTable();
909: $column = $cfg['field'] ? $cfg['field'] : $field->optsValue();
910:
911: if ( ! $table ) {
912: throw new \Exception('Table for database value check is not defined for field '.$field->name());
913: }
914:
915: if ( ! $column ) {
916: throw new \Exception('Value column for database value check is not defined for field '.$field->name());
917: }
918:
919: // Try / catch in case the submitted value can't be represented as the
920: // database type (e.g. an empty string as an integer)
921: try {
922: $count = $db
923: ->query( 'select', $table )
924: ->get( $column )
925: ->where( $column, $val )
926: ->exec()
927: ->count();
928:
929: return $count === 0 ?
930: $cfg['message'] :
931: true;
932: }
933: catch (\Exception $e) {
934: return $cfg['message'];
935: }
936: }
937: }
938:
939: