1: <?php
2: /**
3: * This file is part of the PHPLucidFrame library.
4: * Core utility for input validation
5: *
6: * @package PHPLucidFrame\Core
7: * @since PHPLucidFrame v 1.0.0
8: * @copyright Copyright (c), PHPLucidFrame.
9: * @link http://phplucidframe.com
10: * @license http://www.opensource.org/licenses/mit-license.php MIT License
11: *
12: * This source file is subject to the MIT license that is bundled
13: * with this source code in the file LICENSE
14: */
15:
16: use LucidFrame\Core\Validation;
17:
18: /**
19: * @internal
20: * @ignore
21: *
22: * Initialize the validation messages
23: */
24: function __validation_init()
25: {
26: $validationMessages = array(
27: 'default' => "'%s' needs to be revised.",
28: 'mandatory' => "'%s' is required.",
29: 'mandatoryOne' => "'%s' must be entered/selected at least one.",
30: 'mandatoryAll' => "'%s' is required. All must be entered/selected.",
31: 'notAllowZero' => "'%s' should not be zero.",
32: 'alphaNumeric' => "'%s' should contain only letters and numbers.",
33: 'alphaNumericSpace' => "'%s' should contain only letters, numbers and spaces.",
34: 'alphaNumericDash' => "'%s' should contain only letters, numbers and dashes.",
35: 'numeric' => "'%s' should be a number.",
36: 'numericSpace' => "'%s' should contain only numbers and spaces.",
37: 'numericDash' => "'%s' should contain only numbers and dashes. It should not start or end with a dash.",
38: 'username' => "'%s' should contain only letters, numbers, periods, underscores and dashes.",
39: 'naturalNumber' => "'%s' should be a positive integer. It is not allowed zero.",
40: 'wholeNumber' => "'%s' should be a positive integer.",
41: 'integer' => "'%s' should be a positive or negative integer.",
42: 'rationalNumber' => "'%s' should be an integer or decimal.",
43: 'positiveRationalNumber' => "'%s' should be a positive integer or decimal.",
44: 'email' => "'%s' should be a valid format, e.g., username@example.com",
45: 'domain' => "'%s' should be a valid domain name with letters, numbers and dash only.",
46: 'url' => "'%s' should be a valid website address, e.g., http://www.example.com",
47: 'exactLength' => "'%s' should have exact length of %d.",
48: 'min' => "'%s' should be greater than or equal to %d.",
49: 'max' => "'%s' should be less than or equal to %d.",
50: 'minLength' => "'%s' should have at least %d letters.",
51: 'maxLength' => "'%s' should not exceed %d letters.",
52: 'between' => "'%s' should be between %d and %d.",
53: 'fileMaxSize' => "'%s' cannot exceed the maximum allowed upload size %dMB.",
54: 'fileMaxWidth' => "'%s' cannot exceed the maximum allowed width %dpx.",
55: 'fileMaxHeight' => "'%s' cannot exceed the maximum allowed height %dpx.",
56: 'fileMaxDimension' => "'%s' cannot exceed the maximum allowed dimension %dpx%dpx.",
57: 'fileExactDimension' => "'%s' should have the dimension %dx%dpx.",
58: 'fileExtension' => "'%s' must be one of the file types: %s.",
59: 'date' => "'%s' should be valid for the date format '%s'.",
60: 'time' => "'%s' should be valid for the time format '%s'.",
61: 'datetime' => "'%s' should be valid for the date/time format '%s %s'.",
62: 'unique' => "'%s' already exists. Try another one",
63: 'custom' => "'%s' should be a valid format."
64: );
65:
66: if (function_exists('__validation_messages')) {
67: $validationMessages = array_merge($validationMessages, __validation_messages());
68: }
69:
70: $i18nEnabled = function_exists('_t');
71:
72: foreach ($validationMessages as $key => $msg) {
73: $validationMessages[$key] = ($i18nEnabled) ? _t($msg) : $msg;
74: }
75:
76: Validation::set('messages', $validationMessages);
77: }
78:
79: /**
80: * Setter for Validation class properties
81: * @param string $key The property name
82: * @param mixed $value The value for the property
83: * @return void
84: */
85: function validation_set($key, $value = null)
86: {
87: Validation::set($key, $value);
88: }
89:
90: /**
91: * Getter for Validation class properties
92: * @param string $key The property name
93: * @return mixed
94: */
95: function validation_get($key)
96: {
97: return Validation::get($key);
98: }
99:
100: /**
101: * Check all inputs according to the validation rules provided
102: *
103: * @param array $validations The array of the validation rules
104: * @param array $data The optional data array (if no `value` in $validation, it will be looked up in $data)
105: * @param string $type The return form of the error message:
106: * "multi" to return all error messages occurred;
107: * "single" to return the first error message occurred
108: *
109: * @return bool
110: */
111: function validation_check($validations, $data = [], $type = Validation::TYPE_MULTI)
112: {
113: return Validation::check($validations, $data, $type);
114: }
115:
116: /**
117: * Add an external error message
118: *
119: * @param string $id HTML ID
120: * @param string $msg The error message to show
121: *
122: * @return void
123: */
124: function validation_addError($id, $msg)
125: {
126: Validation::addError($id, $msg);
127: }
128:
129: /**
130: * Checks that a string contains something other than whitespace
131: * @param mixed $value The value being checked
132: * @return boolean TRUE if the value contains something other than whitespace, FALSE otherwise
133: */
134: function validate_mandatory($value)
135: {
136: if (is_array($value) && count($value) == 0) {
137: return false; # other grouped inputs
138: }
139:
140: if (is_array($value) && isset($value['name']) && empty($value['name'])) {
141: return false; # file upload
142: }
143:
144: if (empty($value) && $value != '0') {
145: return false;
146: }
147:
148: return (is_array($value)) ? true : preg_match('/[^\s]+/', $value);
149: }
150: /**
151: * Check one of the fields is required
152: * @param array $value The array of values to check
153: * @return boolean TRUE if one of the fields are not empty, FALSE otherwise
154: */
155: function validate_mandatoryOne($value)
156: {
157: if (is_array($value)) {
158: $value = array_unique($value);
159: $empty = true;
160: foreach ($value as $v) {
161: if (preg_match('/[^\s]+/', $v)) {
162: # if one of the value is not empty
163: $empty = false;
164: }
165: }
166: return !$empty;
167: } else {
168: return preg_match('/[^\s]+/', $value);
169: }
170: }
171: /**
172: * Check all the fields are not empty
173: * @param array $value The array of values being checked
174: * @return boolean TRUE if all the fields are not empty, FALSE otherwise
175: */
176: function validate_mandatoryAll($value)
177: {
178: if (is_array($value)) {
179: $value = array_unique($value);
180: foreach ($value as $v) {
181: if (preg_match('/[\s]+/', $v)) {
182: # if one of the value is empty
183: return false;
184: }
185: }
186: return true;
187: } else {
188: return preg_match('/[^\s]+/', $value);
189: }
190: }
191: /**
192: * Check a string or number is zero or not
193: * @param string $value The value being checked
194: * @return boolean TRUE for non-zero, FALSE otherwise
195: */
196: function validate_notAllowZero($value)
197: {
198: $value = trim($value);
199:
200: return ($value == '0' || $value == 0) ? false : true;
201: }
202: /**
203: * Checks that a string contains only integer or letters
204: * @param mixed $value The value being checked
205: * @return boolean TRUE if the value contains only integer or letters, FALSE otherwise
206: */
207: function validate_alphaNumeric($value)
208: {
209: $value = trim($value);
210: if ($value == '') {
211: return true;
212: }
213:
214:
215: return preg_match('/^[A-Za-z0-9]+$/', $value);
216: }
217: /**
218: * Checks that a string contains only integer, letters or spaces
219: * @param mixed $value The value being checked
220: * @return boolean TRUE if the value contains only integer, letters or spaces, FALSE otherwise
221: */
222: function validate_alphaNumericSpace($value)
223: {
224: $value = trim($value);
225: if ($value == '') {
226: return true;
227: }
228:
229:
230: return preg_match('/^[A-Za-z0-9 ]+$/', $value);
231: }
232: /**
233: * Checks that a string contains only integer, letters or dashes
234: * @param mixed $value The value being checked
235: * @return boolean TRUE if the value contains only integer, letters or dashes, FALSE otherwise
236: */
237: function validate_alphaNumericDash($value)
238: {
239: $value = trim($value);
240: if ($value == '') {
241: return true;
242: }
243:
244:
245: return preg_match('/^[A-Za-z0-9\-]+$/', $value);
246: }
247: /**
248: * Checks if a value is numeric.
249: * @param mixed $value The value being checked
250: * @return boolean TRUE if var is a number or a numeric string, FALSE otherwise.
251: */
252: function validate_numeric($value)
253: {
254: $value = trim($value);
255: if ($value == '') {
256: return true;
257: }
258:
259:
260: return is_numeric($value);
261: }
262: /**
263: * Checks if the value contains numbers and dashes
264: * @param mixed $value The value being checked
265: * @return boolean TRUE if the value contains numbers and dashes only, FALSE otherwise
266: */
267: function validate_numericDash($value)
268: {
269: if (is_numeric($value) && strlen($value) == 1) {
270: return true;
271: }
272:
273: if (empty($value)) {
274: return true;
275: }
276:
277: return preg_match('/^([0-9])+([0-9\-])*([0-9])+$/', $value);
278: }
279: /**
280: * Checks if the value contains numbers and spaces
281: * @param string $value The value being checked
282: * @return boolean TRUE if the value contains numbers and spaces only, FALSE otherwise
283: */
284: function validate_numericSpace($value)
285: {
286: if (is_numeric($value) && strlen($value) == 1) {
287: return true;
288: }
289:
290: if (empty($value)) {
291: return true;
292: }
293:
294: return preg_match('/^[0-9 ]+$/', $value);
295: }
296: /**
297: * Checks if the value does not contain special characters
298: * @param mixed $value The value being checked
299: * @return boolean TRUE if the value does not contain special characters, FALSE otherwise
300: */
301: function validate_username($value)
302: {
303: $value = trim($value);
304: if ($value == '') {
305: return true;
306: }
307:
308:
309: return preg_match('/^([A-Za-z])+([A-Za-z0-9_\-\.])*([A-Za-z0-9])+$/', $value);
310: }
311: /**
312: * Checks if a value is a positive integer starting from 1, 2, 3, and so on. No decimal
313: * @param mixed $value The value being checked
314: * @return boolean TRUE if the value is natural number, FALSE otherwise
315: * @see http://en.wikipedia.org/wiki/Natural_number
316: * http://math.about.com/od/mathhelpandtutorials/a/Understanding-Classification-Of-Numbers.htm
317: */
318: function validate_naturalNumber($value)
319: {
320: $value = trim($value);
321: if ($value == '') {
322: return true;
323: }
324:
325: return preg_match('/^[1-9][0-9]*$/', $value);
326: }
327: /**
328: * Checks if a value is a positive integer starting from 0, 1, 2, 3, and so on. No decimal.
329: * @param mixed $value The value being checked
330: * @return boolean TRUE if the value is whole number, FALSE otherwise
331: * @see http://math.about.com/od/mathhelpandtutorials/a/Understanding-Classification-Of-Numbers.htm
332: */
333: function validate_wholeNumber($value)
334: {
335: $value = trim($value);
336: if ($value == '') {
337: return true;
338: }
339:
340: return preg_match('/^(?:0|[1-9][0-9]*)$/', $value);
341: }
342: /**
343: * Checks if a value is a positive or negative integer.
344: * @param mixed $value The value being checked
345: * @return boolean TRUE if the value is integer, FALSE otherwise
346: * @see http://math.about.com/od/mathhelpandtutorials/a/Understanding-Classification-Of-Numbers.htm
347: */
348: function validate_integer($value)
349: {
350: $value = trim($value);
351: if ($value == '') {
352: return true;
353: }
354:
355: return preg_match('/^[-]?(?:0|[1-9][0-9]*)$/', $value);
356: }
357: /**
358: * Checks if a value is an integer AND decimal.
359: * @param mixed $value The value being checked
360: * @return boolean TRUE if the value is rational number, FALSE otherwise
361: * @see http://math.about.com/od/mathhelpandtutorials/a/Understanding-Classification-Of-Numbers.htm
362: */
363: function validate_rationalNumber($value)
364: {
365: $value = trim($value);
366: if ($value == '') {
367: return true;
368: }
369:
370: return preg_match('/^[-]?[0-9]*[\.]?[0-9]+$/', $value);
371: }
372: /**
373: * Checks if a value is a positive integer AND decimal
374: * @param mixed $value The value being checked
375: * @return boolean TRUE if the value is positive rational number, FALSE otherwise
376: * @see http://math.about.com/od/mathhelpandtutorials/a/Understanding-Classification-Of-Numbers.htm
377: */
378: function validate_positiveRationalNumber($value)
379: {
380: $value = trim($value);
381: if ($value == '') {
382: return true;
383: }
384:
385: return preg_match('/^[0-9]*[\.]?[0-9]+$/', $value);
386: }
387: /**
388: * Validates for an email address.
389: * @param mixed $value The value being checked
390: * @return boolean TRUE if the value is a valid email address, FALSE otherwise
391: */
392: function validate_email($value)
393: {
394: $value = trim($value);
395: if ($value == '') {
396: return true;
397: }
398:
399: return preg_match('/^[A-Za-z0-9]([A-Za-z0-9]|_|\.|\-)*@([a-z0-9]|\.|\-)+\.[a-z]{2,4}$/', $value);
400: }
401: /**
402: * Checks if the value is a valid domain (alpha-numeric and dash only)
403: * @param mixed $value The value being checked
404: * @return boolean TRUE if the value has letters, numbers and dashes only, FALSE otherwise
405: */
406: function validate_domain($value)
407: {
408: if (empty($value)) {
409: return true;
410: }
411:
412: return preg_match('/^([a-z])+([a-z0-9\-])*([a-z0-9])+$/i', $value);
413: }
414: /**
415: * Validates for a valid absolute web address
416: * @param mixed $value The value being checked
417: * @return boolean TRUE if the value is a valid absolute web address, FALSE otherwise
418: */
419: function validate_url($value)
420: {
421: if (empty($value)) {
422: return true;
423: }
424:
425: # General regular expression for URL
426: $regExp = '/^((http|https|ftp):\/\/)?([a-z0-9\-_]+\.) {2,4}([[:alnum:]]) {2,4}([[:alnum:]\/+=%&_\.~?\-]*)$/';
427:
428: # Get host name from URL
429: preg_match("/^((http|https|ftp):\/\/)?([^\/]+)/i", $value, $matches);
430: $host = $matches[3];
431: # Checking host name
432: if (!strstr($host, "@")) {
433: if (preg_match($regExp, $value)) {
434: # Ok with general regular expression
435: # Analyze host segment of URL
436: $hostParts = explode('.', $host);
437: $domain = $hostParts[count($hostParts)-1];
438: $domainParts = explode('?', $domain);
439:
440: # Get suffix from host e.g., com, net, org, sg or info, etc...
441: $suffix = strstr($domain, '?') ? reset($domainParts) : $domain;
442:
443: # IF last segment is valid && URL not contains 4w
444: if (preg_match("/^[a-z]{2,4}$/", $suffix) && ! strstr($value, 'wwww')) {
445: return true;
446: }
447: } else {
448: # IF not OK with general regular expression
449: # Regular Expression for URL
450: $urlExp = "/^(([a-z0-9]|_|\-)+\.)+[a-z]{2,4}$/";
451:
452: # IF valid URL && URL not contains 4 w
453: if (preg_match($urlExp, $value) && ! strstr($value, 'wwww')) {
454: return true;
455: }
456: } # End of Check if URL
457: } # End of Check Host Name
458:
459: return false;
460: }
461: /**
462: * Checks that a string/array's length is equal to the specific length.
463: * @param mixed $value The value being checked
464: * @param int $length The exact length to meet
465: * @return boolean if the character length of the value meets the specified exact length, FALSE otherwise
466: */
467: function validate_exactLength($value, $length)
468: {
469: if (is_array($value)) {
470: return count($value) == $length;
471: }
472:
473: return mb_strlen($value) == $length;
474: }
475: /**
476: * Checks that a string length is greater than the specific length.
477: * @param mixed $value The value being checked
478: * @param int $min The minimum length to meet (inclusive)
479: * @return boolean if the character length of the value meets the specified minimum length, FALSE otherwise
480: */
481: function validate_minLength($value, $min)
482: {
483: return mb_strlen($value) >= $min;
484: }
485: /**
486: * Checks that a string length is less than the specific length.
487: * @param mixed $value The value being checked
488: * @param int $max The maximum length to meet (inclusive)
489: * @return boolean if the character length of the value meets the specified maximum length, FALSE otherwise
490: */
491: function validate_maxLength($value, $max)
492: {
493: $length = mb_strlen($value);
494: return ($length <= $max);
495: }
496: /**
497: * Checks that a number is greater than the specific number.
498: * @param int/float $value The value being checked
499: * @param int/float $min The minimum value to meet (inclusive)
500: * @return boolean if the value is equal to or greater than the specific minimum number, FALSE otherwise
501: */
502: function validate_min($value, $min)
503: {
504: return $value >= $min;
505: }
506: /**
507: * Checks that a number is less than the specific number.
508: * @param int/float $value The value being checked
509: * @param int/float $max The maximum value to meet (inclusive)
510: * @return boolean if the value is equal to or less than the specific maximum number, FALSE otherwise
511: */
512: function validate_max($value, $max)
513: {
514: return $value <= $max;
515: }
516: /**
517: * Checks that a number is within a specified range.
518: * @param int/float $value The value being checked
519: * @param int/float $min The minimum value in range (inclusive)
520: * @param int/float $max The maximum value in range (inclusive)
521: * @return boolean TRUE if the number is within the specified range, FALSE otherwise
522: */
523: function validate_between($value, $min, $max)
524: {
525: return $value >= $min && $value <= $max;
526: }
527: /**
528: * Used when a custom regular expression is needed.
529: * Searches the value for a match to the regular expression given in pattern.
530: * @param mixed $value The value being checked
531: * @param string $pattern The pattern to search for, as a string
532: * @return mixed `1` if the pattern matches given value, `0` if it does not, or `FALSE` if an error occurred.
533: * @see http://php.net/manual/en/function.preg-match.php
534: */
535: function validate_custom($value, $pattern)
536: {
537: if (empty($value) && $value != '0') {
538: return true;
539: }
540:
541: return preg_match($pattern, $value);
542: }
543: /**
544: * Validation of image file upload for allowed file extensions
545: * @param array $value The $_FILES array
546: * @param array $extensions The Array of file extensions such as `array('jpg', 'jpeg', 'png', 'gif')`
547: * @return boolean TRUE if the uploaded file extension is allowed according to the given extensions, FALSE otherwise
548: */
549: function validate_fileExtension($value, $extensions = array('jpg', 'jpeg', 'png', 'gif'))
550: {
551: if (!is_array($value)) {
552: return true;
553: }
554:
555: if (!file_exists($value['tmp_name'])) {
556: return true;
557: }
558:
559: if (empty($value['name'])) {
560: return true;
561: }
562:
563: $ext = explode('.', $value['name']);
564: $ext = strtolower(end($ext));
565:
566: return in_array($ext, $extensions);
567: }
568: /**
569: * Validation of maximum file upload size
570: * @param array $value The $_FILES array
571: * @param int $maxSize The maximum file size in MB
572: * @return boolean TRUE if the uploaded file does not exceed the given file size, FALSE otherwise
573: */
574: function validate_fileMaxSize($value, $maxSize = null)
575: {
576: if (!is_array($value)) {
577: return true;
578: }
579:
580: if (is_null($maxSize)) {
581: return true;
582: }
583:
584: $fileSize = $value['size'];
585: $maxSize = $maxSize * 1024 * 1024; # in bytes
586:
587: return $fileSize <= $maxSize;
588: }
589: /**
590: * Validation of image file upload for max width and max height
591: * @param array $value The $_FILES array
592: * @param int $maxWidth The maximum image width in pixels
593: * @param int $maxHeight The maximum image height in pixels
594: * @return boolean
595: * TRUE if the image uploaded dimension does not exceed the given max width and height;
596: * FALSE otherwise
597: */
598: function validate_fileMaxDimension($value, $maxWidth, $maxHeight)
599: {
600: if (!is_array($value)) {
601: return true;
602: }
603:
604: if (!file_exists($value['tmp_name'])) {
605: return true;
606: }
607:
608: list($width, $height) = getimagesize($value['tmp_name']);
609: return $width <= $maxWidth && $height <= $maxHeight;
610: }
611: /**
612: * Validation of image file upload for exact width and height
613: * @param array $value The $_FILES array
614: * @param int $width The image width in pixels
615: * @param int $height The mage height in pixels
616: * @return boolean
617: * TRUE if the image uploaded dimension same as the given max width and height;
618: * FALSE otherwise
619: */
620: function validate_fileExactDimension($value, $width, $height)
621: {
622: if (!is_array($value)) {
623: return true;
624: }
625:
626: if (!file_exists($value['tmp_name'])) {
627: return true;
628: }
629:
630: list($w, $h) = getimagesize($value['tmp_name']);
631:
632: return $w == $width && $h == $height;
633: }
634: /**
635: * Validation of image file upload for max width only
636: * @param array $value The $_FILES array
637: * @param int $maxWidth The maximum image width in pixels
638: * @return boolean
639: * TRUE if the uploaded image does not exceed the maximum width allowed;
640: * FALSE otherwise
641: */
642: function validate_fileMaxWidth($value, $maxWidth)
643: {
644: if (!is_array($value)) {
645: return true;
646: }
647:
648: if (!file_exists($value['tmp_name'])) {
649: return true;
650: }
651:
652: list($width, $height) = getimagesize($value['tmp_name']);
653:
654: return $width <= $maxWidth;
655: }
656: /**
657: * Validation of image file upload for max height only
658: * @param array $value The $_FILES array
659: * @param int $maxHeight The maximum image height in pixels
660: * @return boolean
661: * TRUE if the uploaded image does not exceed the maximum height allowed;
662: * FALSE otherwise
663: */
664: function validate_fileMaxHeight($value, $maxHeight)
665: {
666: if (!is_array($value)) {
667: return true;
668: }
669:
670: if (!file_exists($value['tmp_name'])) {
671: return true;
672: }
673:
674: list($width, $height) = getimagesize($value['tmp_name']);
675:
676: return $height <= $maxHeight;
677: }
678: /**
679: * Validation of an IP address.
680: * @param string $value The value being checked
681: * @param string $type The IP protocol version to validate against IPv4 or IPv6
682: * @return boolean TRUE on success; FALSE on failure
683: */
684: function validate_ip($value, $type = 'both')
685: {
686: $type = strtolower($value);
687: $flags = 0;
688: if ($type === 'v4' || $type === 'ipv4') {
689: $flags = FILTER_FLAG_IPV4;
690: }
691:
692: if ($type === 'v6' || $type === 'ipv6') {
693: $flags = FILTER_FLAG_IPV6;
694: }
695:
696: return (boolean)filter_var($value, FILTER_VALIDATE_IP, array('flags' => $flags));
697: }
698: /**
699: * Validation of a date which checks if the string passed is a valid date.
700: * **Allowed formats**
701: *
702: * - `d-m-y` 31-12-2014 separators can be a period, dash, forward slash, but not allow space
703: * - `m-d-y` 12-31-2014 separators can be a period, dash, forward slash, but not allow space
704: * - `y-m-d` 2014-12-31 separators can be a period, dash, forward slash, but not allow space
705: *
706: * @param string $value The date string being checked
707: * @param string $format The date format to be validated against. Default is y-m-d for 2014-12-31
708: *
709: * @return bool TRUE on success; FALSE on failure
710: */
711: function validate_date($value, $format = 'y-m-d')
712: {
713: if (empty($value)) {
714: return true;
715: }
716:
717: $value = trim($value);
718: $format = strtolower($format);
719: $separators = array('/', '-', '.');
720: $sepGroup = '([-\/.])';
721: $cleanFormat = preg_replace('/'.$sepGroup.'/', '', $format); // remove the separators from the format
722:
723: if (in_array($cleanFormat, array('dmy', 'mdy'))) {
724: $pattern = '/^([\d]{1,2})'.$sepGroup.'([\d]{1,2})'.$sepGroup.'([\d]{4})$/'; // dmy or mdy
725: } else {
726: $pattern = '/^([\d]{4})'.$sepGroup.'([\d]{1,2})'.$sepGroup.'([\d]{1,2})$/'; // ymd
727: }
728:
729: if ($pattern && preg_match_all($pattern, $value, $matches)) {
730: if ($matches[2][0] != $matches[4][0]) {
731: return false; // inconsistent separators
732: }
733:
734: if (!in_array($matches[2][0], $separators)) {
735: return false; // invalid separator
736: }
737:
738: $sep = $matches[2][0]; // the separator using
739: $dt = explode($sep, $value);
740: $format = str_split($cleanFormat);
741: $ft = array_flip($format);
742: $y = $dt[$ft['y']];
743: $m = $dt[$ft['m']];
744: $d = $dt[$ft['d']];
745:
746: return checkdate($m, $d, $y);
747: }
748:
749: return false;
750: }
751: /**
752: * Validation of a time which checks if the string passed is a valid time in 24-hour or 12-hour format or both
753: * **Allowed inputs**
754: *
755: * - 23:59 or 01:00 or 1:00
756: * - 23:59:59 or 01:00:00 or 1:00:00
757: * - 11:59am or 01:00pm or 1:00pm
758: * - 11:59 am or 01:00 pm or 1:00 PM or 1:00PM
759: * - 11:59:59am 01:00:00pm or 1:00:00pm
760: * - 11:59:59 AM 01:00:00 PM or 1:00:00PM
761: *
762: * @param string $value The time string being checked
763: * @param string $timeFormat The time format: 12, 24 or both
764: *
765: * @return bool TRUE on success; FALSE on failure
766: */
767: function validate_time($value, $timeFormat = 'both')
768: {
769: if (empty($value)) {
770: return true;
771: }
772:
773: $value = trim($value);
774: $regex = array(
775: '24' => '/^([01]?[0-9]|2[0-3]):([0-5][0-9])(:[0-5][0-9])?$/', // 24-hour format
776: '12' => '/^(0?[0-9]|1[0-2]):([0-5][0-9])(:[0-5][0-9])?\s*(am|pm)$/i' // 12-hour format
777: );
778:
779: if (!in_array($timeFormat, array('both', '12', '24'))) {
780: $timeFormat = 'both';
781: }
782:
783: if ($timeFormat === 'both') {
784: $test = $regex;
785: } else {
786: $test = array($regex[$timeFormat]);
787: }
788:
789: foreach ($test as $pattern) {
790: if (preg_match($pattern, $value)) {
791: return true;
792: }
793: }
794:
795: return false;
796: }
797: /**
798: * Validation of a date/time which checks if the string passed is a valid date and time.
799: * **Allowed date formats**
800: *
801: * - `d-m-y` 31-12-2014 separators can be a period, dash, forward slash, but not allow space
802: * - `m-d-y` 12-31-2014 separators can be a period, dash, forward slash, but not allow space
803: * - `y-m-d` 2014-12-31 separators can be a period, dash, forward slash, but not allow space
804: *
805: * @param string $value The date/time string being checked
806: * @param string $dateFormat The date format only to be validated against. Default is y-m-d for 2014-12-31.
807: * @param string $timeFormat The time format: 12, 24 or both
808: *
809: * @return bool TRUE on success; FALSE on failure
810: */
811: function validate_datetime($value, $dateFormat = 'y-m-d', $timeFormat = 'both')
812: {
813: if (empty($value)) {
814: return true;
815: }
816:
817: $value = trim($value);
818: $generalPattern = '/^([\d]{1,4}[-\/.][\d]{1,2}[-\/.][\d]{1,4})(\s+.{4,}\s*(am|pm)?)$/i';
819: if (preg_match_all($generalPattern, $value, $matches)) {
820: $date = $matches[1][0];
821: $time = $matches[2][0];
822: return validate_date($date, $dateFormat) && validate_time($time, $timeFormat);
823: } else {
824: return false;
825: }
826: }
827: /**
828: * Validation of a record uniqueness
829: *
830: * @param mixed $value The value to check for uniqueness
831: * @param string $table The table name without prefix
832: * @param string $field The field name in the table to check
833: * @param int $id The optional ID field to be excluded
834: * @return boolean TRUE if the value already exists in the table; otherwise FALSE
835: */
836: function validate_unique($value, $table, $field, $id = 0)
837: {
838: $value = strtolower($value);
839: if (empty($value)) {
840: return true;
841: }
842:
843: $qb = db_count($table)
844: ->where()
845: ->condition($field, $value);
846:
847: if ($id) {
848: $qb->condition('id !=', $id);
849: }
850:
851: return $qb->fetch() ? false : true;
852: }
853: