1: <?php
2: /**
3: * Basic Cake functionality.
4: *
5: * Core functions for including other source files, loading models and so forth.
6: *
7: * PHP 5
8: *
9: * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
10: * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
11: *
12: * Licensed under The MIT License
13: * Redistributions of files must retain the above copyright notice.
14: *
15: * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
16: * @link http://cakephp.org CakePHP(tm) Project
17: * @package Cake
18: * @since CakePHP(tm) v 0.2.9
19: * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
20: */
21:
22: /**
23: * Basic defines for timing functions.
24: */
25: define('SECOND', 1);
26: define('MINUTE', 60);
27: define('HOUR', 3600);
28: define('DAY', 86400);
29: define('WEEK', 604800);
30: define('MONTH', 2592000);
31: define('YEAR', 31536000);
32:
33: /**
34: * Loads configuration files. Receives a set of configuration files
35: * to load.
36: * Example:
37: *
38: * `config('config1', 'config2');`
39: *
40: * @return boolean Success
41: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#config
42: */
43: function config() {
44: $args = func_get_args();
45: foreach ($args as $arg) {
46: if (file_exists(APP . 'Config' . DS . $arg . '.php')) {
47: include_once(APP . 'Config' . DS . $arg . '.php');
48:
49: if (count($args) == 1) {
50: return true;
51: }
52: } else {
53: if (count($args) == 1) {
54: return false;
55: }
56: }
57: }
58: return true;
59: }
60:
61: /**
62: * Prints out debug information about given variable.
63: *
64: * Only runs if debug level is greater than zero.
65: *
66: * @param boolean $var Variable to show debug information for.
67: * @param boolean $showHtml If set to true, the method prints the debug data in a browser-friendly way.
68: * @param boolean $showFrom If set to true, the method prints from where the function was called.
69: * @link http://book.cakephp.org/2.0/en/development/debugging.html#basic-debugging
70: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#debug
71: */
72: function debug($var = false, $showHtml = null, $showFrom = true) {
73: if (Configure::read('debug') > 0) {
74: $file = '';
75: $line = '';
76: $lineInfo = '';
77: if ($showFrom) {
78: $calledFrom = debug_backtrace();
79: $file = substr(str_ireplace(ROOT, '', $calledFrom[0]['file']), 1);
80: $line = $calledFrom[0]['line'];
81: }
82: $html = <<<HTML
83: <div class="cake-debug-output">
84: %s
85: <pre class="cake-debug">
86: %s
87: </pre>
88: </div>
89: HTML;
90: $text = <<<TEXT
91: %s
92: ########## DEBUG ##########
93: %s
94: ###########################
95: TEXT;
96: $template = $html;
97: if (php_sapi_name() == 'cli' || $showHtml === false) {
98: $template = $text;
99: if ($showFrom) {
100: $lineInfo = sprintf('%s (line %s)', $file, $line);
101: }
102: }
103: if ($showHtml === null && $template !== $text) {
104: $showHtml = true;
105: }
106: $var = print_r($var, true);
107: if ($showHtml) {
108: $template = $html;
109: $var = h($var);
110: if ($showFrom) {
111: $lineInfo = sprintf('<span><strong>%s</strong> (line <strong>%s</strong>)</span>', $file, $line);
112: }
113: }
114: printf($template, $lineInfo, $var);
115: }
116: }
117:
118: if (!function_exists('sortByKey')) {
119:
120: /**
121: * Sorts given $array by key $sortby.
122: *
123: * @param array $array Array to sort
124: * @param string $sortby Sort by this key
125: * @param string $order Sort order asc/desc (ascending or descending).
126: * @param integer $type Type of sorting to perform
127: * @return mixed Sorted array
128: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#sortByKey
129: */
130: function sortByKey(&$array, $sortby, $order = 'asc', $type = SORT_NUMERIC) {
131: if (!is_array($array)) {
132: return null;
133: }
134:
135: foreach ($array as $key => $val) {
136: $sa[$key] = $val[$sortby];
137: }
138:
139: if ($order == 'asc') {
140: asort($sa, $type);
141: } else {
142: arsort($sa, $type);
143: }
144:
145: foreach ($sa as $key => $val) {
146: $out[] = $array[$key];
147: }
148: return $out;
149: }
150: }
151:
152: /**
153: * Convenience method for htmlspecialchars.
154: *
155: * @param mixed $text Text to wrap through htmlspecialchars. Also works with arrays, and objects.
156: * Arrays will be mapped and have all their elements escaped. Objects will be string cast if they
157: * implement a `__toString` method. Otherwise the class name will be used.
158: * @param boolean $double Encode existing html entities
159: * @param string $charset Character set to use when escaping. Defaults to config value in 'App.encoding' or 'UTF-8'
160: * @return string Wrapped text
161: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#h
162: */
163: function h($text, $double = true, $charset = null) {
164: if (is_array($text)) {
165: $texts = array();
166: foreach ($text as $k => $t) {
167: $texts[$k] = h($t, $double, $charset);
168: }
169: return $texts;
170: } elseif (is_object($text)) {
171: if (method_exists($text, '__toString')) {
172: $text = (string) $text;
173: } else {
174: $text = '(object)' . get_class($text);
175: }
176: }
177:
178: static $defaultCharset = false;
179: if ($defaultCharset === false) {
180: $defaultCharset = Configure::read('App.encoding');
181: if ($defaultCharset === null) {
182: $defaultCharset = 'UTF-8';
183: }
184: }
185: if (is_string($double)) {
186: $charset = $double;
187: }
188: return htmlspecialchars($text, ENT_QUOTES, ($charset) ? $charset : $defaultCharset, $double);
189: }
190:
191: /**
192: * Splits a dot syntax plugin name into its plugin and classname.
193: * If $name does not have a dot, then index 0 will be null.
194: *
195: * Commonly used like `list($plugin, $name) = pluginSplit($name);`
196: *
197: * @param string $name The name you want to plugin split.
198: * @param boolean $dotAppend Set to true if you want the plugin to have a '.' appended to it.
199: * @param string $plugin Optional default plugin to use if no plugin is found. Defaults to null.
200: * @return array Array with 2 indexes. 0 => plugin name, 1 => classname
201: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#pluginSplit
202: */
203: function pluginSplit($name, $dotAppend = false, $plugin = null) {
204: if (strpos($name, '.') !== false) {
205: $parts = explode('.', $name, 2);
206: if ($dotAppend) {
207: $parts[0] .= '.';
208: }
209: return $parts;
210: }
211: return array($plugin, $name);
212: }
213:
214: /**
215: * Print_r convenience function, which prints out <PRE> tags around
216: * the output of given array. Similar to debug().
217: *
218: * @see debug()
219: * @param array $var Variable to print out
220: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#pr
221: */
222: function pr($var) {
223: if (Configure::read('debug') > 0) {
224: echo '<pre>';
225: print_r($var);
226: echo '</pre>';
227: }
228: }
229:
230: /**
231: * Merge a group of arrays
232: *
233: * @param array First array
234: * @param array Second array
235: * @param array Third array
236: * @param array Etc...
237: * @return array All array parameters merged into one
238: * @link http://book.cakephp.org/2.0/en/development/debugging.html#am
239: */
240: function am() {
241: $r = array();
242: $args = func_get_args();
243: foreach ($args as $a) {
244: if (!is_array($a)) {
245: $a = array($a);
246: }
247: $r = array_merge($r, $a);
248: }
249: return $r;
250: }
251:
252: /**
253: * Gets an environment variable from available sources, and provides emulation
254: * for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on
255: * IIS, or SCRIPT_NAME in CGI mode). Also exposes some additional custom
256: * environment information.
257: *
258: * @param string $key Environment variable name.
259: * @return string Environment variable setting.
260: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#env
261: */
262: function env($key) {
263: if ($key === 'HTTPS') {
264: if (isset($_SERVER['HTTPS'])) {
265: return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
266: }
267: return (strpos(env('SCRIPT_URI'), 'https://') === 0);
268: }
269:
270: if ($key === 'SCRIPT_NAME') {
271: if (env('CGI_MODE') && isset($_ENV['SCRIPT_URL'])) {
272: $key = 'SCRIPT_URL';
273: }
274: }
275:
276: $val = null;
277: if (isset($_SERVER[$key])) {
278: $val = $_SERVER[$key];
279: } elseif (isset($_ENV[$key])) {
280: $val = $_ENV[$key];
281: } elseif (getenv($key) !== false) {
282: $val = getenv($key);
283: }
284:
285: if ($key === 'REMOTE_ADDR' && $val === env('SERVER_ADDR')) {
286: $addr = env('HTTP_PC_REMOTE_ADDR');
287: if ($addr !== null) {
288: $val = $addr;
289: }
290: }
291:
292: if ($val !== null) {
293: return $val;
294: }
295:
296: switch ($key) {
297: case 'SCRIPT_FILENAME':
298: if (defined('SERVER_IIS') && SERVER_IIS === true) {
299: return str_replace('\\\\', '\\', env('PATH_TRANSLATED'));
300: }
301: break;
302: case 'DOCUMENT_ROOT':
303: $name = env('SCRIPT_NAME');
304: $filename = env('SCRIPT_FILENAME');
305: $offset = 0;
306: if (!strpos($name, '.php')) {
307: $offset = 4;
308: }
309: return substr($filename, 0, strlen($filename) - (strlen($name) + $offset));
310: break;
311: case 'PHP_SELF':
312: return str_replace(env('DOCUMENT_ROOT'), '', env('SCRIPT_FILENAME'));
313: break;
314: case 'CGI_MODE':
315: return (PHP_SAPI === 'cgi');
316: break;
317: case 'HTTP_BASE':
318: $host = env('HTTP_HOST');
319: $parts = explode('.', $host);
320: $count = count($parts);
321:
322: if ($count === 1) {
323: return '.' . $host;
324: } elseif ($count === 2) {
325: return '.' . $host;
326: } elseif ($count === 3) {
327: $gTLD = array(
328: 'aero',
329: 'asia',
330: 'biz',
331: 'cat',
332: 'com',
333: 'coop',
334: 'edu',
335: 'gov',
336: 'info',
337: 'int',
338: 'jobs',
339: 'mil',
340: 'mobi',
341: 'museum',
342: 'name',
343: 'net',
344: 'org',
345: 'pro',
346: 'tel',
347: 'travel',
348: 'xxx'
349: );
350: if (in_array($parts[1], $gTLD)) {
351: return '.' . $host;
352: }
353: }
354: array_shift($parts);
355: return '.' . implode('.', $parts);
356: break;
357: }
358: return null;
359: }
360:
361: /**
362: * Reads/writes temporary data to cache files or session.
363: *
364: * @param string $path File path within /tmp to save the file.
365: * @param mixed $data The data to save to the temporary file.
366: * @param mixed $expires A valid strtotime string when the data expires.
367: * @param string $target The target of the cached data; either 'cache' or 'public'.
368: * @return mixed The contents of the temporary file.
369: * @deprecated Please use Cache::write() instead
370: */
371: function cache($path, $data = null, $expires = '+1 day', $target = 'cache') {
372: if (Configure::read('Cache.disable')) {
373: return null;
374: }
375: $now = time();
376:
377: if (!is_numeric($expires)) {
378: $expires = strtotime($expires, $now);
379: }
380:
381: switch (strtolower($target)) {
382: case 'cache':
383: $filename = CACHE . $path;
384: break;
385: case 'public':
386: $filename = WWW_ROOT . $path;
387: break;
388: case 'tmp':
389: $filename = TMP . $path;
390: break;
391: }
392: $timediff = $expires - $now;
393: $filetime = false;
394:
395: if (file_exists($filename)) {
396: $filetime = @filemtime($filename);
397: }
398:
399: if ($data === null) {
400: if (file_exists($filename) && $filetime !== false) {
401: if ($filetime + $timediff < $now) {
402: @unlink($filename);
403: } else {
404: $data = @file_get_contents($filename);
405: }
406: }
407: } elseif (is_writable(dirname($filename))) {
408: @file_put_contents($filename, $data);
409: }
410: return $data;
411: }
412:
413: /**
414: * Used to delete files in the cache directories, or clear contents of cache directories
415: *
416: * @param mixed $params As String name to be searched for deletion, if name is a directory all files in
417: * directory will be deleted. If array, names to be searched for deletion. If clearCache() without params,
418: * all files in app/tmp/cache/views will be deleted
419: * @param string $type Directory in tmp/cache defaults to view directory
420: * @param string $ext The file extension you are deleting
421: * @return true if files found and deleted false otherwise
422: */
423: function clearCache($params = null, $type = 'views', $ext = '.php') {
424: if (is_string($params) || $params === null) {
425: $params = preg_replace('/\/\//', '/', $params);
426: $cache = CACHE . $type . DS . $params;
427:
428: if (is_file($cache . $ext)) {
429: @unlink($cache . $ext);
430: return true;
431: } elseif (is_dir($cache)) {
432: $files = glob($cache . '*');
433:
434: if ($files === false) {
435: return false;
436: }
437:
438: foreach ($files as $file) {
439: if (is_file($file) && strrpos($file, DS . 'empty') !== strlen($file) - 6) {
440: @unlink($file);
441: }
442: }
443: return true;
444: } else {
445: $cache = array(
446: CACHE . $type . DS . '*' . $params . $ext,
447: CACHE . $type . DS . '*' . $params . '_*' . $ext
448: );
449: $files = array();
450: while ($search = array_shift($cache)) {
451: $results = glob($search);
452: if ($results !== false) {
453: $files = array_merge($files, $results);
454: }
455: }
456: if (empty($files)) {
457: return false;
458: }
459: foreach ($files as $file) {
460: if (is_file($file) && strrpos($file, DS . 'empty') !== strlen($file) - 6) {
461: @unlink($file);
462: }
463: }
464: return true;
465: }
466: } elseif (is_array($params)) {
467: foreach ($params as $file) {
468: clearCache($file, $type, $ext);
469: }
470: return true;
471: }
472: return false;
473: }
474:
475: /**
476: * Recursively strips slashes from all values in an array
477: *
478: * @param array $values Array of values to strip slashes
479: * @return mixed What is returned from calling stripslashes
480: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#stripslashes_deep
481: */
482: function stripslashes_deep($values) {
483: if (is_array($values)) {
484: foreach ($values as $key => $value) {
485: $values[$key] = stripslashes_deep($value);
486: }
487: } else {
488: $values = stripslashes($values);
489: }
490: return $values;
491: }
492:
493: /**
494: * Returns a translated string if one is found; Otherwise, the submitted message.
495: *
496: * @param string $singular Text to translate
497: * @param mixed $args Array with arguments or multiple arguments in function
498: * @return mixed translated string
499: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__
500: */
501: function __($singular, $args = null) {
502: if (!$singular) {
503: return;
504: }
505:
506: App::uses('I18n', 'I18n');
507: $translated = I18n::translate($singular);
508: if ($args === null) {
509: return $translated;
510: } elseif (!is_array($args)) {
511: $args = array_slice(func_get_args(), 1);
512: }
513: return vsprintf($translated, $args);
514: }
515:
516: /**
517: * Returns correct plural form of message identified by $singular and $plural for count $count.
518: * Some languages have more than one form for plural messages dependent on the count.
519: *
520: * @param string $singular Singular text to translate
521: * @param string $plural Plural text
522: * @param integer $count Count
523: * @param mixed $args Array with arguments or multiple arguments in function
524: * @return mixed plural form of translated string
525: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__n
526: */
527: function __n($singular, $plural, $count, $args = null) {
528: if (!$singular) {
529: return;
530: }
531:
532: App::uses('I18n', 'I18n');
533: $translated = I18n::translate($singular, $plural, null, 6, $count);
534: if ($args === null) {
535: return $translated;
536: } elseif (!is_array($args)) {
537: $args = array_slice(func_get_args(), 3);
538: }
539: return vsprintf($translated, $args);
540: }
541:
542: /**
543: * Allows you to override the current domain for a single message lookup.
544: *
545: * @param string $domain Domain
546: * @param string $msg String to translate
547: * @param mixed $args Array with arguments or multiple arguments in function
548: * @return translated string
549: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__d
550: */
551: function __d($domain, $msg, $args = null) {
552: if (!$msg) {
553: return;
554: }
555: App::uses('I18n', 'I18n');
556: $translated = I18n::translate($msg, null, $domain);
557: if ($args === null) {
558: return $translated;
559: } elseif (!is_array($args)) {
560: $args = array_slice(func_get_args(), 2);
561: }
562: return vsprintf($translated, $args);
563: }
564:
565: /**
566: * Allows you to override the current domain for a single plural message lookup.
567: * Returns correct plural form of message identified by $singular and $plural for count $count
568: * from domain $domain.
569: *
570: * @param string $domain Domain
571: * @param string $singular Singular string to translate
572: * @param string $plural Plural
573: * @param integer $count Count
574: * @param mixed $args Array with arguments or multiple arguments in function
575: * @return plural form of translated string
576: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__dn
577: */
578: function __dn($domain, $singular, $plural, $count, $args = null) {
579: if (!$singular) {
580: return;
581: }
582: App::uses('I18n', 'I18n');
583: $translated = I18n::translate($singular, $plural, $domain, 6, $count);
584: if ($args === null) {
585: return $translated;
586: } elseif (!is_array($args)) {
587: $args = array_slice(func_get_args(), 4);
588: }
589: return vsprintf($translated, $args);
590: }
591:
592: /**
593: * Allows you to override the current domain for a single message lookup.
594: * It also allows you to specify a category.
595: *
596: * The category argument allows a specific category of the locale settings to be used for fetching a message.
597: * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL.
598: *
599: * Note that the category must be specified with a numeric value, instead of the constant name. The values are:
600: *
601: * - LC_ALL 0
602: * - LC_COLLATE 1
603: * - LC_CTYPE 2
604: * - LC_MONETARY 3
605: * - LC_NUMERIC 4
606: * - LC_TIME 5
607: * - LC_MESSAGES 6
608: *
609: * @param string $domain Domain
610: * @param string $msg Message to translate
611: * @param integer $category Category
612: * @param mixed $args Array with arguments or multiple arguments in function
613: * @return translated string
614: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__dc
615: */
616: function __dc($domain, $msg, $category, $args = null) {
617: if (!$msg) {
618: return;
619: }
620: App::uses('I18n', 'I18n');
621: $translated = I18n::translate($msg, null, $domain, $category);
622: if ($args === null) {
623: return $translated;
624: } elseif (!is_array($args)) {
625: $args = array_slice(func_get_args(), 3);
626: }
627: return vsprintf($translated, $args);
628: }
629:
630: /**
631: * Allows you to override the current domain for a single plural message lookup.
632: * It also allows you to specify a category.
633: * Returns correct plural form of message identified by $singular and $plural for count $count
634: * from domain $domain.
635: *
636: * The category argument allows a specific category of the locale settings to be used for fetching a message.
637: * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL.
638: *
639: * Note that the category must be specified with a numeric value, instead of the constant name. The values are:
640: *
641: * - LC_ALL 0
642: * - LC_COLLATE 1
643: * - LC_CTYPE 2
644: * - LC_MONETARY 3
645: * - LC_NUMERIC 4
646: * - LC_TIME 5
647: * - LC_MESSAGES 6
648: *
649: * @param string $domain Domain
650: * @param string $singular Singular string to translate
651: * @param string $plural Plural
652: * @param integer $count Count
653: * @param integer $category Category
654: * @param mixed $args Array with arguments or multiple arguments in function
655: * @return plural form of translated string
656: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__dcn
657: */
658: function __dcn($domain, $singular, $plural, $count, $category, $args = null) {
659: if (!$singular) {
660: return;
661: }
662: App::uses('I18n', 'I18n');
663: $translated = I18n::translate($singular, $plural, $domain, $category, $count);
664: if ($args === null) {
665: return $translated;
666: } elseif (!is_array($args)) {
667: $args = array_slice(func_get_args(), 5);
668: }
669: return vsprintf($translated, $args);
670: }
671:
672: /**
673: * The category argument allows a specific category of the locale settings to be used for fetching a message.
674: * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL.
675: *
676: * Note that the category must be specified with a numeric value, instead of the constant name. The values are:
677: *
678: * - LC_ALL 0
679: * - LC_COLLATE 1
680: * - LC_CTYPE 2
681: * - LC_MONETARY 3
682: * - LC_NUMERIC 4
683: * - LC_TIME 5
684: * - LC_MESSAGES 6
685: *
686: * @param string $msg String to translate
687: * @param integer $category Category
688: * @param mixed $args Array with arguments or multiple arguments in function
689: * @return translated string
690: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__c
691: */
692: function __c($msg, $category, $args = null) {
693: if (!$msg) {
694: return;
695: }
696: App::uses('I18n', 'I18n');
697: $translated = I18n::translate($msg, null, null, $category);
698: if ($args === null) {
699: return $translated;
700: } elseif (!is_array($args)) {
701: $args = array_slice(func_get_args(), 2);
702: }
703: return vsprintf($translated, $args);
704: }
705:
706: /**
707: * Shortcut to Log::write.
708: *
709: * @param string $message Message to write to log
710: * @return void
711: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#LogError
712: */
713: function LogError($message) {
714: App::uses('CakeLog', 'Log');
715: $bad = array("\n", "\r", "\t");
716: $good = ' ';
717: CakeLog::write('error', str_replace($bad, $good, $message));
718: }
719:
720: /**
721: * Searches include path for files.
722: *
723: * @param string $file File to look for
724: * @return Full path to file if exists, otherwise false
725: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#fileExistsInPath
726: */
727: function fileExistsInPath($file) {
728: $paths = explode(PATH_SEPARATOR, ini_get('include_path'));
729: foreach ($paths as $path) {
730: $fullPath = $path . DS . $file;
731:
732: if (file_exists($fullPath)) {
733: return $fullPath;
734: } elseif (file_exists($file)) {
735: return $file;
736: }
737: }
738: return false;
739: }
740:
741: /**
742: * Convert forward slashes to underscores and removes first and last underscores in a string
743: *
744: * @param string String to convert
745: * @return string with underscore remove from start and end of string
746: * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#convertSlash
747: */
748: function convertSlash($string) {
749: $string = trim($string, '/');
750: $string = preg_replace('/\/\//', '/', $string);
751: $string = str_replace('/', '_', $string);
752: return $string;
753: }
754: