Merge pull request #8470 from ankitjain28may/master
[civicrm-core.git] / CRM / Utils / Type.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2016 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2016
32 */
33 class CRM_Utils_Type {
34 const
35 T_INT = 1,
36 T_STRING = 2,
37 T_ENUM = 2,
38 T_DATE = 4,
39 T_TIME = 8,
40 T_BOOLEAN = 16,
41 T_TEXT = 32,
42 T_LONGTEXT = 32,
43 T_BLOB = 64,
44 T_TIMESTAMP = 256,
45 T_FLOAT = 512,
46 T_MONEY = 1024,
47 T_EMAIL = 2048,
48 T_URL = 4096,
49 T_CCNUM = 8192,
50 T_MEDIUMBLOB = 16384;
51
52 // @todo What's the point of these constants? Backwards compatibility?
53 const
54 TWO = 2,
55 FOUR = 4,
56 SIX = 6,
57 EIGHT = 8,
58 TWELVE = 12,
59 SIXTEEN = 16,
60 TWENTY = 20,
61 MEDIUM = 20,
62 THIRTY = 30,
63 BIG = 30,
64 FORTYFIVE = 45,
65 HUGE = 45;
66
67 /**
68 * Gets the string representation for a data type.
69 *
70 * @param int $type
71 * Integer number identifying the data type.
72 *
73 * @return string
74 * String identifying the data type, e.g. 'Int' or 'String'.
75 */
76 public static function typeToString($type) {
77 // @todo Use constants in the case statements, e.g. "case T_INT:".
78 // @todo return directly, instead of assigning a value.
79 // @todo Use a lookup array, as a property or as a local variable.
80 switch ($type) {
81 case 1:
82 $string = 'Int';
83 break;
84
85 case 2:
86 $string = 'String';
87 break;
88
89 case 3:
90 $string = 'Enum';
91 break;
92
93 case 4:
94 $string = 'Date';
95 break;
96
97 case 8:
98 $string = 'Time';
99 break;
100
101 case 16:
102 $string = 'Boolean';
103 break;
104
105 case 32:
106 $string = 'Text';
107 break;
108
109 case 64:
110 $string = 'Blob';
111 break;
112
113 // CRM-10404
114 case 12:
115 case 256:
116 $string = 'Timestamp';
117 break;
118
119 case 512:
120 $string = 'Float';
121 break;
122
123 case 1024:
124 $string = 'Money';
125 break;
126
127 case 2048:
128 $string = 'Date';
129 break;
130
131 case 4096:
132 $string = 'Email';
133 break;
134
135 case 16384:
136 $string = 'Mediumblob';
137 break;
138 }
139
140 return (isset($string)) ? $string : "";
141 }
142
143 /**
144 * Helper function to call escape on arrays
145 *
146 * @see escape
147 */
148 public static function escapeAll($data, $type, $abort = TRUE) {
149 foreach ($data as $key => $value) {
150 $data[$key] = CRM_Utils_Type::escape($value, $type, $abort);
151 }
152 return $data;
153 }
154
155 /**
156 * Helper function to call validate on arrays
157 *
158 * @see validate
159 */
160 public static function validateAll($data, $type, $abort = TRUE) {
161 foreach ($data as $key => $value) {
162 $data[$key] = CRM_Utils_Type::validate($value, $type, $abort);
163 }
164 return $data;
165 }
166
167 /**
168 * Verify that a variable is of a given type, and apply a bit of processing.
169 *
170 * @param mixed $data
171 * The value to be verified/escaped.
172 * @param string $type
173 * The type to verify against.
174 * @param bool $abort
175 * If TRUE, the operation will CRM_Core_Error::fatal() on invalid data.
176 *
177 * @return mixed
178 * The data, escaped if necessary.
179 */
180 public static function escape($data, $type, $abort = TRUE) {
181 switch ($type) {
182 case 'Integer':
183 case 'Int':
184 if (CRM_Utils_Rule::integer($data)) {
185 return (int) $data;
186 }
187 break;
188
189 case 'Positive':
190 if (CRM_Utils_Rule::positiveInteger($data)) {
191 return (int) $data;
192 }
193 break;
194
195 // CRM-8925 for custom fields of this type
196 case 'Country':
197 case 'StateProvince':
198 // Handle multivalued data in delimited or array format
199 if (is_array($data) || (strpos($data, CRM_Core_DAO::VALUE_SEPARATOR) !== FALSE)) {
200 $valid = TRUE;
201 foreach (CRM_Utils_Array::explodePadded($data) as $item) {
202 if (!CRM_Utils_Rule::positiveInteger($item)) {
203 $valid = FALSE;
204 }
205 }
206 if ($valid) {
207 return $data;
208 }
209 }
210 elseif (CRM_Utils_Rule::positiveInteger($data)) {
211 return (int) $data;
212 }
213 break;
214
215 case 'File':
216 if (CRM_Utils_Rule::positiveInteger($data)) {
217 return (int) $data;
218 }
219 break;
220
221 case 'Link':
222 if (CRM_Utils_Rule::url($data = trim($data))) {
223 return $data;
224 }
225 break;
226
227 case 'Boolean':
228 if (CRM_Utils_Rule::boolean($data)) {
229 return $data;
230 }
231 break;
232
233 case 'Float':
234 case 'Money':
235 if (CRM_Utils_Rule::numeric($data)) {
236 return $data;
237 }
238 break;
239
240 case 'String':
241 case 'Memo':
242 case 'Text':
243 return CRM_Core_DAO::escapeString($data);
244
245 case 'Date':
246 case 'Timestamp':
247 // a null date or timestamp is valid
248 if (strlen(trim($data)) == 0) {
249 return trim($data);
250 }
251
252 if ((preg_match('/^\d{8}$/', $data) ||
253 preg_match('/^\d{14}$/', $data)
254 ) &&
255 CRM_Utils_Rule::mysqlDate($data)
256 ) {
257 return $data;
258 }
259 break;
260
261 case 'ContactReference':
262 if (strlen(trim($data)) == 0) {
263 return trim($data);
264 }
265
266 if (CRM_Utils_Rule::validContact($data)) {
267 return (int) $data;
268 }
269 break;
270
271 case 'MysqlColumnNameOrAlias':
272 if (CRM_Utils_Rule::mysqlColumnNameOrAlias($data)) {
273 $data = str_replace('`', '', $data);
274 $parts = explode('.', $data);
275 $data = '`' . implode('`.`', $parts) . '`';
276
277 return $data;
278 }
279 break;
280
281 case 'MysqlOrderByDirection':
282 if (CRM_Utils_Rule::mysqlOrderByDirection($data)) {
283 return strtolower($data);
284 }
285 break;
286
287 case 'MysqlOrderBy':
288 if (CRM_Utils_Rule::mysqlOrderBy($data)) {
289 $parts = explode(',', $data);
290 foreach ($parts as &$part) {
291 $part = preg_replace_callback('/^(?:(?:((?:`[\w-]{1,64}`|[\w-]{1,64}))(?:\.))?(`[\w-]{1,64}`|[\w-]{1,64})(?: (asc|desc))?)$/i', array('CRM_Utils_Type', 'mysqlOrderByCallback'), trim($part));
292 }
293 return implode(', ', $parts);
294 }
295 break;
296
297 default:
298 CRM_Core_Error::fatal(
299 $type . " is not a recognised (camel cased) data type."
300 );
301 break;
302 }
303
304 // @todo Use exceptions instead of CRM_Core_Error::fatal().
305 if ($abort) {
306 $data = htmlentities($data);
307 CRM_Core_Error::fatal("$data is not of the type $type");
308 }
309 return NULL;
310 }
311
312 /**
313 * Verify that a variable is of a given type.
314 *
315 * @param mixed $data
316 * The value to validate.
317 * @param string $type
318 * The type to validate against.
319 * @param bool $abort
320 * If TRUE, the operation will CRM_Core_Error::fatal() on invalid data.
321 * @name string $name
322 * The name of the attribute
323 *
324 * @return mixed
325 * The data, escaped if necessary
326 */
327 public static function validate($data, $type, $abort = TRUE, $name = 'One of parameters ') {
328 switch ($type) {
329 case 'Integer':
330 case 'Int':
331 if (CRM_Utils_Rule::integer($data)) {
332 return (int) $data;
333 }
334 break;
335
336 case 'Positive':
337 if (CRM_Utils_Rule::positiveInteger($data)) {
338 return (int) $data;
339 }
340 break;
341
342 case 'Boolean':
343 if (CRM_Utils_Rule::boolean($data)) {
344 return $data;
345 }
346 break;
347
348 case 'Float':
349 case 'Money':
350 if (CRM_Utils_Rule::numeric($data)) {
351 return $data;
352 }
353 break;
354
355 case 'Text':
356 case 'String':
357 case 'Link':
358 case 'Memo':
359 return $data;
360
361 case 'Date':
362 // a null date is valid
363 if (strlen(trim($data)) == 0) {
364 return trim($data);
365 }
366
367 if (preg_match('/^\d{8}$/', $data) &&
368 CRM_Utils_Rule::mysqlDate($data)
369 ) {
370 return $data;
371 }
372 break;
373
374 case 'Timestamp':
375 // a null timestamp is valid
376 if (strlen(trim($data)) == 0) {
377 return trim($data);
378 }
379
380 if ((preg_match('/^\d{14}$/', $data) ||
381 preg_match('/^\d{8}$/', $data)
382 ) &&
383 CRM_Utils_Rule::mysqlDate($data)
384 ) {
385 return $data;
386 }
387 break;
388
389 case 'ContactReference':
390 // null is valid
391 if (strlen(trim($data)) == 0) {
392 return trim($data);
393 }
394
395 if (CRM_Utils_Rule::validContact($data)) {
396 return $data;
397 }
398 break;
399
400 case 'MysqlColumnNameOrAlias':
401 if (CRM_Utils_Rule::mysqlColumnNameOrAlias($data)) {
402 return $data;
403 }
404 break;
405
406 case 'MysqlOrderByDirection':
407 if (CRM_Utils_Rule::mysqlOrderByDirection($data)) {
408 return strtolower($data);
409 }
410 break;
411
412 case 'MysqlOrderBy':
413 if (CRM_Utils_Rule::mysqlOrderBy($data)) {
414 return $data;
415 }
416 break;
417
418 default:
419 CRM_Core_Error::fatal("Cannot recognize $type for $data");
420 break;
421 }
422
423 if ($abort) {
424 $data = htmlentities($data);
425 CRM_Core_Error::fatal("$name (value: $data) is not of the type $type");
426 }
427
428 return NULL;
429 }
430
431 /**
432 * preg_replace_callback for MysqlOrderBy escape.
433 */
434 public static function mysqlOrderByCallback($matches) {
435 $output = '';
436 $matches = str_replace('`', '', $matches);
437
438 // Table name.
439 if (isset($matches[1]) && $matches[1]) {
440 $output .= '`' . $matches[1] . '`.';
441 }
442
443 // Column name.
444 if (isset($matches[2]) && $matches[2]) {
445 $output .= '`' . $matches[2] . '`';
446 }
447
448 // Sort order.
449 if (isset($matches[3]) && $matches[3]) {
450 $output .= ' ' . $matches[3];
451 }
452
453 return $output;
454 }
455
456 }