Cake/basics.php

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