Fixes #149
[akelos.git] / lib / Ak.php
blob537d5fcd199986740cd0598ac7fffa49bcf40e56
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4 // +----------------------------------------------------------------------+
5 // | Akelos Framework - http://www.akelos.org |
6 // +----------------------------------------------------------------------+
7 // | Copyright (c) 2002-2006, Akelos Media, S.L. & Bermi Ferrer Martinez |
8 // | Released under the GNU Lesser General Public License, see LICENSE.txt|
9 // +----------------------------------------------------------------------+
11 /**
12 * @package ActiveSupport
13 * @subpackage Base
14 * @author Bermi Ferrer <bermi a.t akelos c.om>
15 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
16 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
19 /**
20 * Define LOG Levels
21 * @todo Log events
23 define('AK_LOG_NOTICE', 0);
24 define('AK_LOG_WARNING', 1);
25 define('AK_LOG_ERROR', 2);
27 defined('AK_FRAMEWORK_LANGUAGE') ? null : define('AK_FRAMEWORK_LANGUAGE', 'en');
28 defined('AK_DEV_MODE') ? null : define('AK_DEV_MODE', false);
29 defined('AK_AUTOMATIC_CONFIG_VARS_ENCRYPTION') ? null : define('AK_AUTOMATIC_CONFIG_VARS_ENCRYPTION', false);
32 /**
33 * Akelos Framework static functions
35 * Ak contains all the Akelos Framework static functions. This
36 * class acts like a name space to avoid naming collisions
37 * when PHP gets new functions into its core. And also to provide
38 * additional functionality to existing PHP functions mantaining the same interface
40 * @author Bermi Ferrer <bermi at akelos com>
41 * @copyright Copyright (c) 2002-2005, Akelos Media, S.L. http://www.akelos.org
42 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
44 class Ak
47 /**
48 * Gets an instance of AkDbAdapter
50 * Whenever a database connection is required you can get a
51 * reference to the default database connection by doing:
53 * $db =& Ak::db(); // get an adodb instance
55 * AdoDB manual can be found at http://phplens.com/adodb/
57 * @access public
58 * @param string $dns A string containing Data Source Name (information
59 * regarding database connection)
60 * http://phplens.com/adodb/code.initialization.html#dsnsupport
61 * @static
62 * @return resource Php AdoDb instance.
64 function &db($dsn = null)
66 require_once(AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkDbAdapter.php');
67 return AkDbAdapter::getInstance($dsn);
70 /**
71 * @param string $message
72 * @param [OPTIONAL] $fatal triggers even in production-mode
74 function deprecateWarning($message, $fatal=false)
76 if (!$fatal && AK_ENVIRONMENT == 'production'){
77 return;
79 if (is_array($message)){
80 trigger_error(Ak::t("DEPRECATED WARNING: ".array_shift($message),$message), E_USER_NOTICE);
81 } else {
82 trigger_error(Ak::t("DEPRECATED WARNING: ".$message), E_USER_NOTICE);
86 /**
87 * Gets a cache object singleton instance
89 function &cache()
91 static $cache;
92 if(!isset($cache)){
93 require_once(AK_LIB_DIR.DS.'AkCache.php');
94 $cache = new AkCache();
96 return $cache;
100 function toUrl($options, $set_routes = false)
102 static $Map;
103 if(empty($Map)){
104 if($set_routes){
105 $Map = $options;
106 return;
107 }else{
108 require_once(AK_LIB_DIR.DS.'AkRouter.php');
109 $Map = new AkRouter();
110 if(is_file(AK_ROUTES_MAPPING_FILE)){
111 include(AK_ROUTES_MAPPING_FILE);
115 return $Map->toUrl($options);
120 * Translate strings to the current locale.
122 * When using Ak::t(), try to put entire sentences and strings
123 * in one Ak::t() call.
124 * This makes it easier for translators. HTML markup within
125 * translation strings
126 * is acceptable, if necessary. The suggested syntax for a
127 * link embedded
128 * within a translation string is:
130 * @access public
131 * @static
132 * @param string $string A string containing the English string to
133 * translate.
134 * @param array $args An associative array of replacements to make after
135 * translation. Incidences of any key in this array
136 * are replaced with the corresponding value.
137 * @return string The translated string.
139 function t($string, $args = null, $controller = null)
141 static $framework_dictionary = array(), $lang, $_dev_shutdown = true;
143 if(AK_AUTOMATICALLY_UPDATE_LANGUAGE_FILES && !empty($string) && is_string($string)){
144 require_once(AK_LIB_DIR.DS.'AkLocaleManager.php');
145 // This adds used strings to a stack for storing new entries on the locale file after shutdown
146 AkLocaleManager::getUsedLanguageEntries($string, $controller);
147 if($_dev_shutdown){
148 register_shutdown_function(array('AkLocaleManager','updateLocaleFiles'));
149 $_dev_shutdown = false;
153 if(!isset($lang)){
154 if(!empty($_SESSION['lang'])){
155 $lang = $_SESSION['lang'];
156 }else{
157 $lang = Ak::lang();
159 if(is_file(AK_CONFIG_DIR.DS.'locales'.DS.$lang.'.php')){
160 require(AK_CONFIG_DIR.DS.'locales'.DS.$lang.'.php');
161 $framework_dictionary = array_merge($framework_dictionary,$dictionary);
163 if(!defined('AK_LOCALE')){
164 define('AK_LOCALE', $lang);
166 if(!empty($locale) && is_array($locale)){
167 Ak::locale(null, $lang, $locale);
171 if(!empty($string) && is_array($string)){
172 if(!empty($string[$lang])){
173 return $string[$lang];
175 $try_whith_lang = $args !== false && empty($string[$lang]) ? Ak::base_lang() : $lang;
176 if(empty($string[$try_whith_lang]) && $args !== false){
177 foreach (Ak::langs() as $try_whith_lang){
178 if(!empty($string[$try_whith_lang])){
179 return $string[$try_whith_lang];
183 return @$string[$try_whith_lang];
186 if(isset($controller) && !isset($framework_dictionary[$controller.'_dictionary']) && is_file(AK_APP_DIR.DS.'locales'.DS.$controller.DS.$lang.'.php')){
187 require(AK_APP_DIR.DS.'locales'.DS.$controller.DS.$lang.'.php');
188 $framework_dictionary[$controller.'_dictionary'] = (array)$dictionary;
191 if(isset($controller) && isset($framework_dictionary[$controller.'_dictionary'][$string])){
192 $string = $framework_dictionary[$controller.'_dictionary'][$string];
193 }else {
194 $string = isset($framework_dictionary[$string]) ? $framework_dictionary[$string] : $string;
197 if(isset($args) && is_array($args)){
198 $string = @str_replace(array_keys($args), array_values($args),$string);
202 * @todo Prepare for multiple locales by inspecting AK_DEFAULT_LOCALE
205 return $string;
211 * Gets information about current locale from the locale settings on config/locales/LOCALE.php
213 * This are common settings on the locale file:
214 * 'description' // Locale description Example. Spanish
215 * 'charset' // 'ISO-8859-1';
216 * 'date_time_format' // '%d/%m/%Y %H:%i:%s';
217 * 'date_format' // '%d/%m/%Y';
218 * 'long_date_format' // '%d/%m/%Y';
219 * 'time_format' // '%H:%i';
220 * 'long_time_format' // '%H:%i:%s';
222 function locale($locale_setting, $locale = null)
224 static $settings;
226 // We initiate the locale settings
227 Ak::t('Akelos');
229 $locale = empty($locale) ? (defined('AK_LOCALE') ? AK_LOCALE : (Ak::t('Akelos') && Ak::locale($locale_setting))) : $locale;
231 if (empty($settings[$locale])) {
232 if(func_num_args() != 3){ // First time we ask for something using this locale so we will load locale details
233 $requested_locale = $locale;
234 if(@include(AK_CONFIG_DIR.DS.'locales'.DS.Ak::sanitize_include($requested_locale).'.php')){
235 $locale = !empty($locale) && is_array($locale) ? $locale : array();
236 Ak::locale(null, $requested_locale, $locale);
237 return Ak::locale($locale_setting, $requested_locale);
239 }else{
240 $settings[$locale] = func_get_arg(2);
241 if(isset($settings[$locale]['charset'])){
242 defined('AK_CHARSET') ? null : (define('AK_CHARSET',$settings[$locale]['charset']) && @ini_set('default_charset', AK_CHARSET));
247 return isset($settings[$locale][$locale_setting]) ? $settings[$locale][$locale_setting] : false;
251 function lang($set_language = null)
253 static $lang;
254 $lang = empty($set_language) ? (empty($lang) ? AK_FRAMEWORK_LANGUAGE : $lang) : $set_language;
255 return $lang;
259 function get_url_locale($set_locale = null)
261 static $locale;
262 if(!empty($locale)){
263 return $locale;
265 $locale = empty($set_locale) ? '' : $set_locale;
266 return $locale;
271 function langs()
273 static $langs;
274 if(!empty($lang)){
275 return $lang;
277 $lang = Ak::lang();
278 if(defined('AK_APP_LOCALES')){
279 $langs = array_diff(explode(',',AK_APP_LOCALES.','),array(''));
281 $langs = empty($langs) ? array($lang) : $langs;
282 return $langs;
285 function base_lang()
287 return array_shift(Ak::langs());
292 function dir($path, $options = array())
294 $result = array();
296 $path = rtrim($path, '/\\');
297 $default_options = array(
298 'files' => true,
299 'dirs' => true,
300 'recurse' => false,
303 $options = array_merge($default_options, $options);
305 if(is_file($path)){
306 $result = array($path);
307 }elseif(is_dir($path)){
308 if ($id_dir = opendir($path)){
309 while (false !== ($file = readdir($id_dir))){
310 if ($file != "." && $file != ".." && $file != '.svn'){
311 if(!empty($options['files']) && !is_dir($path.DS.$file)){
312 $result[] = $file;
313 }elseif(!empty($options['dirs'])){
314 $result[][$file] = !empty($options['recurse']) ? Ak::dir($path.DS.$file, $options) : $file;
318 closedir($id_dir);
322 return array_reverse($result);
326 function file_put_contents($file_name, $content, $options = array())
329 $default_options = array(
330 'ftp' => defined('AK_UPLOAD_FILES_USING_FTP') && AK_UPLOAD_FILES_USING_FTP,
331 'base_path' => AK_BASE_DIR,
333 $options = array_merge($default_options, $options);
335 if(!function_exists('file_put_contents')){
336 include_once(AK_CONTRIB_DIR.DS.'pear'.DS.'PHP'.DS.'Compat.php');
337 PHP_Compat::loadFunction(array('file_put_contents'));
340 $file_name = trim(str_replace($options['base_path'], '',$file_name),DS);
342 if($options['ftp']){
343 require_once(AK_LIB_DIR.DS.'AkFtp.php');
344 $file_name = trim(str_replace(array(DS,'//'),array('/','/'),$file_name),'/');
345 if(!AkFtp::is_dir(dirname($file_name))){
346 AkFtp::make_dir(dirname($file_name));
349 return AkFtp::put_contents($file_name, $content);
350 }else{
351 if(!is_dir(dirname($options['base_path'].DS.$file_name))){
352 Ak::make_dir(dirname($options['base_path'].DS.$file_name), $options);
355 if(!$result = file_put_contents($options['base_path'].DS.$file_name, $content)){
356 if(!empty($content)){
357 Ak::trace("Please change file/dir permissions or enable FTP file handling by".
358 " setting the following on your config/".AK_ENVIRONMENT.".php file \n<pre>define('AK_UPLOAD_FILES_USING_FTP', true);\n".
359 "define('AK_READ_FILES_USING_FTP', false);\n".
360 "define('AK_DELETE_FILES_USING_FTP', true);\n".
361 "define('AK_FTP_PATH', 'ftp://username:password@example.com/path_to_the_framework');\n".
362 "define('AK_FTP_AUTO_DISCONNECT', true);\n</pre>");
365 return $result;
370 function file_get_contents($file_name, $options = array())
372 $default_options = array(
373 'ftp' => defined('AK_READ_FILES_USING_FTP') && AK_READ_FILES_USING_FTP,
374 'base_path' => AK_BASE_DIR,
376 $options = array_merge($default_options, $options);
378 $file_name = trim(str_replace($options['base_path'], '',$file_name),DS);
379 if($options['ftp']){
380 require_once(AK_LIB_DIR.DS.'AkFtp.php');
381 $file_name = trim(str_replace(array(DS,'//'),array('/','/'),$file_name),'/');
382 return AkFtp::get_contents($file_name);
383 }else{
384 return file_get_contents($options['base_path'].DS.$file_name);
389 * @todo Optimize this code (dirty add-on to log command line interpreter results)
391 function file_add_contents($file_name, $content, $options = array())
393 $original_content = @Ak::file_get_contents($file_name, $options);
394 return Ak::file_put_contents($file_name, $original_content.$content, $options);
397 function file_delete($file_name, $options = array())
399 $default_options = array(
400 'ftp' => defined('AK_DELETE_FILES_USING_FTP') && AK_DELETE_FILES_USING_FTP,
401 'base_path' => AK_BASE_DIR,
403 $options = array_merge($default_options, $options);
405 $file_name = trim(str_replace($options['base_path'], '',$file_name),DS);
406 if($options['ftp']){
407 require_once(AK_LIB_DIR.DS.'AkFtp.php');
408 $file_name = trim(str_replace(array(DS,'//'),array('/','/'),$file_name),'/');
409 return AkFtp::delete($file_name, true);
410 }else{
411 return unlink($options['base_path'].DS.$file_name);
415 function directory_delete($dir_name, $options = array())
417 $default_options = array(
418 'ftp' => defined('AK_DELETE_FILES_USING_FTP') && AK_DELETE_FILES_USING_FTP,
419 'base_path' => AK_BASE_DIR,
421 $options = array_merge($default_options, $options);
423 $sucess = true;
424 $dir_name = Ak::_getRestrictedPath($dir_name, $options);
426 if(empty($dir_name)){
427 return false;
430 if($options['ftp']){
431 require_once(AK_LIB_DIR.DS.'AkFtp.php');
432 return AkFtp::delete($dir_name);
433 }else{
434 $items = glob($options['base_path'].DS.$dir_name."/*");
435 $hidden_items = glob($options['base_path'].DS.$dir_name."/.*");
436 $fs_items = $items || $hidden_items ? array_merge((array)$items, (array)$hidden_items) : false;
437 if($fs_items){
438 $items_to_delete = array('directories'=>array(), 'files'=>array());
439 foreach($fs_items as $fs_item) {
440 if($fs_item[strlen($fs_item)-1] != '.'){
441 $items_to_delete[ (is_dir($fs_item) ? 'directories' : 'files') ][] = $fs_item;
444 foreach ($items_to_delete['files'] as $file){
445 Ak::file_delete($file, $options);
447 foreach ($items_to_delete['directories'] as $directory){
448 $sucess = $sucess ? Ak::directory_delete($directory, $options) : $sucess;
451 return $sucess ? @rmdir($options['base_path'].DS.$dir_name) : $sucess;
455 function make_dir($path, $options = array())
457 $default_options = array(
458 'ftp' => defined('AK_UPLOAD_FILES_USING_FTP') && AK_UPLOAD_FILES_USING_FTP,
459 'base_path' => AK_BASE_DIR
461 $options = array_merge($default_options, $options);
463 $path = trim(str_replace($options['base_path'], '',$path),DS);
464 if($options['ftp']){
465 require_once(AK_LIB_DIR.DS.'AkFtp.php');
466 $path = trim(str_replace(array(DS,'//'),array('/','/'),$path),'/');
467 return AkFtp::make_dir($path);
468 }else{
469 $path = $options['base_path'].DS.$path;
470 if (!file_exists($path)){
471 Ak::make_dir(dirname($path), $options);
472 return mkdir($path);
475 return false;
479 * This static method will copy recursively all the files or directories from one
480 * path within an Akelos application to another.
482 * It uses current installation settings, so it can perform copies via the filesystem or via FTP
484 function copy($origin, $target, $options = array())
486 $default_options = array(
487 'ftp' => defined('AK_UPLOAD_FILES_USING_FTP') && AK_UPLOAD_FILES_USING_FTP,
488 'base_path' => AK_BASE_DIR,
490 $options = array_merge($default_options, $options);
492 $sucess = true;
494 $origin = Ak::_getRestrictedPath($origin, $options);
495 $target = Ak::_getRestrictedPath($target, $options);
497 if(empty($origin) || empty($target)){
498 return false;
501 if($options['ftp']){
502 require_once(AK_LIB_DIR.DS.'AkFtp.php');
504 $destination = str_replace($origin, $target, $origin);
505 if(is_file($options['base_path'].DS.$origin)){
506 return Ak::file_put_contents($options['base_path'].DS.$destination, Ak::file_get_contents($options['base_path'].DS.$origin, $options), $options);
508 Ak::make_dir($options['base_path'].DS.$destination);
509 if($fs_items = glob($options['base_path'].DS.$origin."/*")){
510 $items_to_copy = array('directories'=>array(), 'files'=>array());
511 foreach($fs_items as $fs_item) {
512 $items_to_copy[ (is_dir($fs_item) ? 'directories' : 'files') ][] = $fs_item;
514 foreach ($items_to_copy['files'] as $file){
515 $destination = str_replace($origin, $target, $file);
516 $sucess = $sucess ? Ak::file_put_contents($destination, Ak::file_get_contents($file, $options), $options) : $sucess;
518 foreach ($items_to_copy['directories'] as $directory){
519 $destination = str_replace($origin, $target, $directory);
520 $sucess = $sucess ? Ak::copy($directory, $destination, $options) : $sucess;
523 return $sucess;
527 * Returns a path restricting it to a base location
529 * This is used by Akelos to prevent functions namespaced under Ak
530 * from writing out of the Akelos base directory for security reasons.
532 function _getRestrictedPath($path, $options = array())
534 $default_options = array(
535 'ftp' => false,
536 'base_path' => AK_BASE_DIR,
538 $options = array_merge($default_options, $options);
540 $path = str_replace('..','', rtrim($path,'\\/. '));
541 $path = trim(str_replace($options['base_path'], '',$path),DS);
543 if($options['ftp']){
544 $path = trim(str_replace(array(DS,'//'),array('/','/'), $path),'/');
547 return $path;
552 * Perform a web request
554 * @param string $url URL we are going to request.
555 * @param array $options Options for current request.
556 * Options are:
557 * * referer: URL that will be set as referer url. Default is current url
558 * * params: Parameter for the request. Can be an array of key=>values or a url params string like key=value&key2=value2
559 * * method: In case params are given the will be requested using get method by default. Specify post if get is not what you need.
560 * @return string
562 function url_get_contents($url, $options = array())
564 include_once(AK_LIB_DIR.DS.'AkHttpClient.php');
565 $Client =& new AkHttpClient();
566 $method = empty($options['method']) ? 'get' : strtolower($options['method']);
567 if(empty($method) || !in_array($method, array('get','post','put','delete'))){
568 trigger_error(Ak::t('Invalid HTTP method %method', array('%method'=>$options['method'])), E_USER_ERROR);
570 return $Client->$method($url, $options);
575 * Trace helper function for development purposes
577 * @access public
578 * @static
579 * @param string $text Helper text
580 * @param string $line Helper line
581 * @param string $file Helper file
582 * @return echoes result to screen
584 function trace($text = null, $line = null, $file = null)
586 static $counter = 0;
587 if(!AK_DEBUG){
588 //return;
591 $line = isset($line) ? "Line: $line".(AK_CLI?"\n":"<br />") : "";
592 $file = isset($file) ? "File: $file".(AK_CLI?"\n":"<br />") : "";
594 if(!isset($text)){
595 $counter++;
596 $text = '';
597 }else {
598 $text = AK_CLI?'---> '.$text:'<b>---&gt;</b>'.$text;
601 echo AK_CLI?"----------------\n$line $file $text\n----------------\n":"<hr /><div>$line $file $text</div><hr />\n";
609 * Outputs debug info given a PHP resource (vars, objects,
610 * arrays...)
612 * @access public
613 * @static
614 * @param mixed $data Data to debug. It can be an object, array,
615 * resource..
616 * @return void Prints debug info.
618 function debug ($data, $_functions=0)
620 if(!AK_DEBUG && !AK_DEV_MODE){
621 return;
624 if($_functions!=0) {
625 $sf=1;
626 } else {
627 $sf=0 ;
629 if(is_object($data) && method_exists($data, 'debug')){
630 echo AK_CLI ?
631 "\n------------------------------------\nEntering on ".get_class($data)." debug() method\n\n":
632 "<hr /><h2>Entering on ".get_class($data)." debug() method</h2>";
633 if(!empty($data->__activeRecordObject)){
634 $data->toString(true);
636 $data->debug();
637 return ;
639 if (isset ($data)) {
640 if (is_array($data) || is_object($data)) {
642 if (count ($data)) {
643 echo AK_CLI ? "/--\n" : "<ol>\n";
644 while (list ($key,$value) = each ($data)) {
645 $type=gettype($value);
646 if ($type=="array" || $type == "object") {
647 ob_start();
648 Ak::debug ($value,$sf);
649 $lines = explode("\n",ob_get_clean()."\n");
650 foreach ($lines as $line){
651 echo "\t".$line."\n";
653 } elseif (eregi ("function", $type)) {
654 if ($sf) {
655 AK_CLI ? printf ("\t* (%s) %s:\n",$type, $key, $value) :
656 printf ("<li>(%s) <b>%s</b> </li>\n",$type, $key, $value);
658 } else {
659 if (!$value) {
660 $value="(none)";
662 AK_CLI ? printf ("\t* (%s) %s = %s\n",$type, $key, $value) :
663 printf ("<li>(%s) <b>%s</b> = %s</li>\n",$type, $key, $value);
666 echo AK_CLI ? "\n--/\n" : "</ol>fin.\n";
667 } else {
668 echo "(empty)";
679 * Gets information about given object
681 * @access public
682 * @static
683 * @uses Ak::get_this_object_methods
684 * @uses Ak::get_this_object_attributes
685 * @param object &$object Object to get info from
686 * @param boolean $include_inherited_info By setting this to true, parent Object properties
687 * and methods will be included.
688 * @return string html output with Object info
690 function get_object_info($object, $include_inherited_info = false)
692 $object_name = get_class($object);
693 $methods = $include_inherited_info ? get_class_methods($object) : Ak::get_this_object_methods($object);
694 $vars = $include_inherited_info ? get_class_vars($object_name) : Ak::get_this_object_attributes($object);
695 $var_desc = '';
696 if(is_array($vars)){
697 $var_desc = '<ul>';
698 foreach ($vars as $varname=>$var_value){
699 $var_desc .= "<li>$varname = $var_value (". gettype($var_value) .")</li>\n";
701 $var_desc .= "</ul>";
703 return Ak::t('Object <b>%object_name</b> information:<hr> <b>object Vars:</b><br>%var_desc <hr> <b>object Methods:</b><br><ul><li>%methods</li></ul>',array('%object_name'=>$object_name,'%var_desc'=>$var_desc,'%methods'=>join("();</li>\n<li>",$methods) .'();'));
710 * Gets selected object methods.
712 * WARNING: Inherited methods are not returned by this
713 * function. You can fetch them by using PHP native function
714 * get_class_methods
716 * @access public
717 * @static
718 * @see get_this_object_attributes
719 * @see get_object_info
720 * @param object &$object Object to inspect
721 * @return array Returns an array with selected object methods. It
722 * does not return inherited methods
724 function get_this_object_methods($object)
726 $array1 = get_class_methods($object);
727 if($parent_object = get_parent_class($object)){
728 $array2 = get_class_methods($parent_object);
729 $array3 = array_diff($array1, $array2);
730 }else{
731 $array3 = $array1;
733 return array_values((array)$array3);
740 * Get selected objects default attributes
742 * WARNING: Inherited attributes are not returned by this
743 * function. You can fetch them by using PHP native function
744 * get_class_vars
746 * @access public
747 * @static
748 * @see get_this_object_methods
749 * @see get_object_info
750 * @param object &$object Object to inspect
751 * @return void Returns an array with selected object attributes.
752 * It does not return inherited attributes
754 function get_this_object_attributes($object)
756 $object = get_class($object);
757 $array1 = get_class_vars($object);
758 if($parent_object = get_parent_class($object)){
759 $array2 = get_class_vars($parent_object);
760 $array3 = array_diff_assoc($array1, $array2);
761 }else{
762 $array3 = $array1;
764 return (array)$array3;
769 function &getLogger()
771 static $Logger;
772 if(empty($Logger)){
773 require_once(AK_LIB_DIR.DS.'AkLogger.php');
774 $Logger = new AkLogger();
776 $return =& $Logger;
777 return $Logger;
781 function get_constants()
783 $constants = get_defined_constants();
784 $keys = array_keys($constants);
785 foreach ($keys as $k){
786 if(substr($k,0,3) != 'AK_'){
787 unset($constants[$k]);
790 return $constants;
795 * @todo Use timezone time
797 function time()
799 return time()+(defined('AK_TIME_DIFERENCE') ? AK_TIME_DIFERENCE*3600 : 0);
802 function gmt_time()
804 return Ak::time()+(AK_TIME_DIFERENCE_FROM_GMT*3600);
809 * Gets a timestamp for input date provided in one of this formats: "year-month-day hour:min:sec", "year-month-day", "hour:min:sec"
811 function getTimestamp($iso_date_or_hour = null)
813 if(empty($iso_date_or_hour)){
814 return Ak::time();
816 if (!preg_match("/^
817 ([0-9]{4})[-\/\.]? # year
818 ([0-9]{1,2})[-\/\.]? # month
819 ([0-9]{1,2})[ -]? # day
821 ([0-9]{1,2}):? # hour
822 ([0-9]{2}):? # minute
823 ([0-9\.]{0,4}) # seconds
824 )?/x", ($iso_date_or_hour), $rr)){
825 if (preg_match("|^(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", ($iso_date_or_hour), $rr)){
826 return empty($rr[0]) ? Ak::time() : mktime($rr[2],$rr[3],$rr[4]);
828 }else{
829 if($rr[1]>=2038 || $rr[1]<=1970){
830 require_once(AK_CONTRIB_DIR.DS.'adodb'.DS.'adodb-time.inc.php');
831 return isset($rr[5]) ? adodb_mktime($rr[5],$rr[6],(int)$rr[7],$rr[2],$rr[3],$rr[1]) : adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
832 }else{
833 return isset($rr[5]) ? mktime($rr[5],$rr[6],(int)$rr[7],$rr[2],$rr[3],$rr[1]) : mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
836 trigger_error(Ak::t('Invalid ISO date. You must supply date in one of the following formats: "year-month-day hour:min:sec", "year-month-day", "hour:min:sec"'));
837 return false;
841 * Return formatted date.
843 * You can supply a format as defined at http://php.net/date
845 * Default date is in ISO format
847 function getDate($timestamp = null, $format = null)
849 $timestamp = !isset($timestamp) ? Ak::time() : $timestamp;
850 $use_adodb = $timestamp <= -3600 || $timestamp >= 2147468400;
851 if($use_adodb){
852 require_once(AK_CONTRIB_DIR.DS.'adodb'.DS.'adodb-time.inc.php');
854 if(!isset($format)){
855 return $use_adodb ? adodb_date('Y-m-d H:i:s', $timestamp) : date('Y-m-d H:i:s', $timestamp);
856 }elseif (!empty($format)){
857 return $use_adodb ? adodb_date($format, $timestamp) : date($format, $timestamp);
859 trigger_error(Ak::t('You must supply a valid UNIX timetamp. You can get the timestamp by calling Ak::getTimestamp("2006-09-27 20:45:57")'));
860 return false;
865 * mail function substitute. Uses the PEAR::Mail() function API.
867 * Messaging subsystem for user communication. See PEAR::Mail() function in PHP
868 * documentation for information.
870 * User must declare any of these variables for specify the outgoing method. Currently,
871 * only Sendmail and STMP methods are available . Variables
872 * for using any of these methods are:
874 * AK_SENDMAIL = 0
875 * AK_SMTP = 1
877 * For future upgrades, you must define which constants must be declared and add
878 * the functionality.
880 * NOTE: If messaging method is SMTP, you must declare in config file (/config/config.php)
881 * the outgoing SMTP server and the authentication pair user/password as constants
882 * AK_SMTP_SERVER, AK_SMTP_USER and AK_SMTP_PASSWORD, respectively.
885 * @param $from
887 * User who sends the mail.
889 * @param $to
891 * Receiver, or receivers of the mail.
893 * The formatting of this string must comply with RFC 2822. Some examples are:
895 * user@example.com
896 * user@example.com, anotheruser@example.com
897 * User <user@example.com>
898 * User <user@example.com>, Another User <anotheruser@example.com>
900 * @param $subject
902 * Subject of the email to be sent. This must not contain any newline
903 * characters, or the mail may not be sent properly.
905 * @param $body
907 * Message to be sent.
909 * @param additional_headers (optional)
911 * Array to be inserted at the end of the email header.
913 * This is typically used to add extra headers (Bcc) in an associative array, where the
914 * array key is the header name (i.e., 'Bcc'), and the array value is the header value
915 * (i.e., 'test'). The header produced from those values would be 'Bcc: test'.
917 * @return boolean whether message has been sent or not.
920 function mail ($from, $to, $subject, $body, $additional_headers = array())
922 require_once(AK_CONTRIB_DIR.DS.'pear'.DS.'Mail.php');
924 static $mail_connector;
926 if(!isset($mail_connector)){
927 if (defined('AK_SENDMAIL')) {
928 // Using Sendmail daemon without parameters.
929 $mail_connector = Mail::factory('sendmail');
930 } else if (defined('AK_SMTP') && AK_SMTP) {
931 // Using external SMTP server.
932 $params['host'] = AK_SMTP_SERVER;
933 $params['username'] = AK_SMTP_USER;
934 $params['password'] = AK_SMTP_PASSWORD;
936 $mail_connector = Mail::factory('smtp', $params);
937 } else {
938 // Using PHP mail() function thru PEAR. Factory without parameters.
939 $mail_connector = Mail::factory('mail');
943 $recipients['To'] = $to;
945 if (!empty($additional_headers)) {
946 foreach ($additional_headers as $k=>$v) {
948 if (strtolower($k)=='cc' || strtolower($k)=='cc:') {
949 $recipients['cc'] = $v;
950 unset($additional_headers['cc']);
953 if (strtolower($k)=='bcc' || strtolower($k)=='bcc:') {
954 $recipients['bcc'] = $v;
955 unset($additional_headers['bcc']);
960 $headers['From'] = $from;
961 $headers['Subject'] = $subject;
962 $headers['Content-Type'] = empty($headers['Content-Type']) ? 'text/plain; charset='.Ak::locale('charset').'; format=flowed' : $headers['Content-Type'];
964 $headers = array_merge($headers, $additional_headers);
966 return $mail_connector->send($recipients, $headers, $body) == true;
971 * @todo move this out of here and use Pear Benchmark instead
973 function profile($message = '')
975 static $profiler;
976 if(AK_DEV_MODE && AK_ENABLE_PROFILER){
977 if(!isset($profiler)){
978 @require_once(AK_LIB_DIR.DS.'AkProfiler.php');
979 $profiler = new AkProfiler();
980 $profiler->init();
981 register_shutdown_function(array(&$profiler,'showReport'));
982 }else {
983 $profiler->setFlag($message);
990 * Gets the size of given element. Counts arrays, returns numbers, string length or executes size() method on given object
992 function size($element)
994 if(is_array($element)){
995 return count($element);
996 }elseif (is_numeric($element) && !is_string($element)){
997 return $element;
998 }elseif (is_string($element)){
999 return strlen($element);
1000 }elseif (is_object($element) && method_exists($element,'size')){
1001 return $element->size();
1002 }else{
1003 return 0;
1009 * Select is a function for selecting items from double depth array.
1010 * This is useful when you just need some fields for generating
1011 * tables, select lists with only desired fields.
1013 * $People = array(
1014 * array('name'=>'Jose','email'=>'jose@example.com','address'=>'Colon, 52'),
1015 * array('name'=>'Alicia','email'=>'alicia@example.com','address'=>'Mayor, 45'),
1016 * array('name'=>'Hilario','email'=>'hilario@example.com','address'=>'Carlet, 78'),
1017 * array('name'=>'Bermi','email'=>'bermi@example.com','address'=>'Vilanova, 33'),
1018 * );
1020 * $people_for_table_generation = Ak::select($People,'name','email');
1022 * Now $people_for_table_generation will hold an array with
1023 * array (
1024 * array ('name' => 'Jose','email' => 'jose@example.com'),
1025 * array ('name' => 'Alicia','email' => 'alicia@example.com'),
1026 * array ('name' => 'Hilario','email' => 'hilario@example.com'),
1027 * array ('name' => 'Bermi','email' => 'bermi@example.com')
1028 * );
1031 function select(&$source_array)
1033 $resulting_array = array();
1034 if(!empty($source_array) && is_array($source_array) && func_num_args() > 1) {
1035 $args = array_slice(func_get_args(),1);
1036 foreach ($source_array as $source_item){
1037 $item_fields = array();
1038 foreach ($args as $arg){
1039 if(is_object($source_item) && isset($source_item->$arg)){
1040 $item_fields[$arg] = $source_item->$arg;
1041 }elseif(is_array($source_item) && isset($source_item[$arg])){
1042 $item_fields[$arg] = $source_item[$arg];
1045 if(!empty($item_fields)){
1046 $resulting_array[] = $item_fields;
1050 return $resulting_array;
1053 function collect(&$source_array, $key_index, $value_index)
1055 $resulting_array = array();
1056 if(!empty($source_array) && is_array($source_array)) {
1057 foreach ($source_array as $source_item){
1058 if(is_object($source_item)){
1059 $resulting_array[@$source_item->$key_index] = @$source_item->$value_index;
1060 }elseif(is_array($source_item)){
1061 $resulting_array[@$source_item[$key_index]] = @$source_item[$value_index];
1065 return $resulting_array;
1068 function delete($source_array, $attributes_to_delete_from_array)
1070 $resulting_array = (array)$source_array;
1071 $args = array_slice(func_get_args(),1);
1072 $args = count($args) == 1 ? Ak::toArray($args[0]) : $args;
1073 foreach ($args as $arg){
1074 unset($resulting_array[$arg]);
1076 return $resulting_array;
1079 function &singleton($class_name, &$arguments)
1081 static $instances;
1082 if(!isset($instances[$class_name])) {
1083 if(is_object($arguments)){
1084 $instances[$class_name] =& new $class_name($arguments);
1085 }else{
1086 if(Ak::size($arguments) > 0){
1087 eval("\$instances[\$class_name] =& new \$class_name(".var_export($arguments, true)."); ");
1088 }else{
1089 $instances[$class_name] =& new $class_name();
1092 $instances[$class_name]->__singleton_id = md5(microtime().rand(1000,9000));
1094 return $instances[$class_name];
1098 function xml_to_array ($xml_data)
1100 $xml_parser = xml_parser_create ();
1101 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);
1102 xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 1);
1103 xml_parse_into_struct ($xml_parser, $xml_data, $vals, $index);
1104 xml_parser_free ($xml_parser);
1105 $params = array();
1106 $ptrs[0] = & $params;
1107 foreach ($vals as $xml_elem) {
1108 $level = $xml_elem['level'] - 1;
1109 switch ($xml_elem['type']) {
1110 case 'open':
1111 $tag_or_id = (array_key_exists ('attributes', $xml_elem)) ? @$xml_elem['attributes']['ID'] : $xml_elem['tag'];
1112 $ptrs[$level][$tag_or_id][] = array ();
1113 $ptrs[$level+1] = & $ptrs[$level][$tag_or_id][count($ptrs[$level][$tag_or_id])-1];
1114 break;
1115 case 'complete':
1116 $ptrs[$level][$xml_elem['tag']] = (isset ($xml_elem['value'])) ? $xml_elem['value'] : '';
1117 break;
1120 return ($params);
1123 function array_to_xml($array, $header = "<?xml version=\"1.0\"?>\r\n", $parent = 'EMPTY_TAG')
1125 static $_tags = array();
1126 $xml = $header;
1127 foreach ($array as $key => $value) {
1128 $key = is_numeric($key) ? $parent : $key;
1129 $value = is_array($value) ? "\r\n".xmlFromArray($value, '', $key) : $value;
1130 $_tags[$key] = $key;
1131 $xml .= sprintf("<%s>%s</%s>\r\n", $key, $value, $key);
1132 $parent = $key;
1134 foreach ($_tags as $_tag){
1135 $xml = str_replace(array("<$_tag>\r\n<$_tag>","</$_tag>\r\n</$_tag>"),array("<$_tag>","</$_tag>"),$xml);
1137 return $xml;
1141 function encrypt($data, $key = 'Ak3los-m3D1a')
1143 srand((double)microtime() *1000000);
1144 $k2 = md5(rand(0, 32000));
1145 $c = 0;
1146 $m = '';
1147 for ($i = 0 ; $i < strlen($data) ; $i++) {
1148 if ($c == strlen($k2)) $c = 0;
1149 $m.= substr($k2, $c, 1) .(substr($data, $i, 1) ^substr($k2, $c, 1));
1150 $c++;
1152 $k = md5($key);
1153 $c = 0;
1154 $t = $m;
1155 $m = '';
1156 for ($i = 0 ; $i < strlen($t) ; $i++) {
1157 if ($c == strlen($k)) {
1158 $c = 0;
1160 $m.= substr($t, $i, 1) ^substr($k, $c, 1);
1161 $c++;
1163 return base64_encode($m);
1166 function decrypt($encrypted_data, $key = 'Ak3los-m3D1a')
1168 $t = base64_decode($encrypted_data);
1169 $k = md5($key);
1170 $c = 0;
1171 $m = '';
1172 for ($i = 0 ; $i < strlen($t) ; $i++) {
1173 if ($c == strlen($k)) $c = 0;
1174 $m.= substr($t, $i, 1) ^substr($k, $c, 1);
1175 $c++;
1177 $t = $m;
1178 $m = '';
1179 for ($i = 0 ; $i < strlen($t) ; $i++) {
1180 $d = substr($t, $i, 1);
1181 $i++;
1182 $m.= (substr($t, $i, 1) ^$d);
1184 return $m;
1188 function blowfishEncrypt($data, $key = 'Ak3los-m3D1a')
1190 $key = substr($key,0,56);
1191 require_once(AK_CONTRIB_DIR.DS.'pear'.DS.'Crypt'.DS.'Blowfish.php');
1192 $Blowfish =& Ak::singleton('Crypt_Blowfish', $key);
1193 $Blowfish->setKey($key);
1194 return $Blowfish->encrypt(base64_encode($data));
1197 function blowfishDecrypt($encrypted_data, $key = 'Ak3los-m3D1a')
1199 $key = substr($key,0,56);
1200 require_once(AK_CONTRIB_DIR.DS.'pear'.DS.'Crypt'.DS.'Blowfish.php');
1201 $Blowfish =& Ak::singleton('Crypt_Blowfish', $key);
1202 $Blowfish->setKey($key);
1203 return base64_decode($Blowfish->decrypt($encrypted_data));
1207 function randomString($max_length = 8)
1209 $randomString = '';
1210 srand((double)microtime()*1000000);
1211 for($i=0;$i<$max_length;$i++){
1212 $randnumber = rand(48,120);
1213 while (($randnumber >= 58 && $randnumber <= 64) || ($randnumber >= 91 && $randnumber <= 96)){
1214 $randnumber = rand(48,120);
1216 $randomString .= chr($randnumber);
1218 return $randomString;
1222 function compress($data, $format = 'gzip')
1224 $key = Ak::randomString(15);
1225 $compressed_file = AK_TMP_DIR.DS.'d'.$key;
1226 $uncompressed_file = AK_TMP_DIR.DS.'s'.$key;
1227 if(Ak::file_put_contents($uncompressed_file, $data, array('base_path'=>AK_TMP_DIR))){
1228 $compressed = gzopen($compressed_file,'w9');
1229 $uncompressed = fopen($uncompressed_file, 'rb');
1230 while(!feof($uncompressed)){
1231 $string = fread($uncompressed, 1024*512);
1232 gzwrite($compressed, $string, strlen($string));
1234 fclose($uncompressed);
1235 gzclose($compressed);
1236 }else{
1237 trigger_error(Ak::t('Could not write to temporary directory for generating compressed file using Ak::compress(). Please provide write access to %dirname', array('%dirname'=>AK_TMP_DIR)), E_USER_ERROR);
1239 $result = Ak::file_get_contents($compressed_file, array('base_path'=>AK_TMP_DIR));
1240 return $result;
1243 function uncompress($compressed_data, $format = 'gzip')
1245 $key = Ak::randomString(15);
1246 $compressed_file = AK_TMP_DIR.DS.'s'.$key;
1247 $uncompressed_file = AK_TMP_DIR.DS.'d'.$key;
1249 if(Ak::file_put_contents($compressed_file, $compressed_data, array('base_path'=>AK_TMP_DIR))){
1250 $compressed = gzopen($compressed_file, "r");
1251 $uncompressed = fopen($uncompressed_file, "w");
1252 while(!gzeof($compressed)){
1253 $string = gzread($compressed, 4096);
1254 fwrite($uncompressed, $string, strlen($string));
1256 gzclose($compressed);
1257 fclose($uncompressed);
1258 }else{
1259 trigger_error(Ak::t('Could not write to temporary directory for generating uncompressing file using Ak::uncompress(). Please provide write access to %dirname', array('%dirname'=>AK_TMP_DIR)), E_USER_ERROR);
1261 $result = Ak::file_get_contents($uncompressed_file, array('base_path'=>AK_TMP_DIR));
1262 return $result;
1266 function unzip($file_to_unzip, $destination_folder)
1268 require_once(AK_LIB_DIR.DS.'AkZip.php');
1269 $ArchiveZip =& new AkZip($file_to_unzip);
1270 $ArchiveZip->extract(array('add_path'=>str_replace(DS,'/',$destination_folder)));
1274 function decompress($compressed_data, $format = 'gzip')
1276 return Ak::uncompress($compressed_data, $format);
1280 function handleStaticCall()
1282 if (AK_PHP5) {
1283 trigger_error(Ak::t('Static calls emulation is not supported by PHP5 < 5.4'));
1284 die();
1286 $static_call = array_slice(debug_backtrace(),1,1);
1287 return call_user_func_array(array(new $static_call[0]['class'](),$static_call[0]['function']),$static_call[0]['args']);
1292 * Gets an array or a comma separated list of models. Then it includes its
1293 * respective files and returns an array of available models.
1295 * @return array available models
1297 function import()
1299 $args = func_get_args();
1300 $args = is_array($args[0]) ? $args[0] : (func_num_args() > 1 ? $args : Ak::stringToArray($args[0]));
1301 $models = array();
1302 foreach ($args as $arg){
1303 $model_name = AkInflector::camelize($arg);
1304 if (class_exists($model_name)){
1305 $models[] = $model_name;
1306 continue;
1308 $model = AkInflector::toModelFilename($model_name);
1309 if (file_exists($model)){
1310 $models[] = $model_name;
1311 include_once($model);
1312 continue;
1314 // Shouldn't we trigger an user-error?: Unknown Model or could not find the Model
1317 return $models;
1320 function uses()
1322 $args = func_get_args();
1323 return call_user_func_array(array('Ak','import'),$args);
1326 function stringToArray($string)
1328 $args = $string;
1329 if(count($args) == 1 && !is_array($args)){
1330 (array)$args = array_unique(array_map('trim',array_diff(explode(',',strtr($args.',',';|-',',,,')),array(''))));
1332 return $args;
1336 function toArray()
1338 $args = func_get_args();
1339 return is_array($args[0]) ? $args[0] : (func_num_args() === 1 ? Ak::stringToArray($args[0]) : $args);
1343 * Returns an array including only the elements with provided keys.
1345 * This is useful to limit the parameters of an array used by a method.
1347 * This utility can be used for modifying arrays which is useful for securing record creation/updating.
1349 * If you have this code on a controller
1351 * $this->user->setAttributes($this->params['user']);
1353 * and your users table has a column named is_admin. All it would take to a malicious user is to modify the page html to add the need field and gain admin privileges.
1355 * You could avoid by using the new Ak::pick method which will return and array with desired keys.
1357 * $this->user->setAttributes(Ak::pick('name,email', $this->params['user']));
1360 function pick($keys, $source_array)
1362 $result = array();
1363 foreach (Ak::toArray($keys) as $k){
1364 $result[$k] = isset($source_array[$k]) ? $source_array[$k] : null;
1366 return $result;
1371 * Includes PHP functions that are not available on current PHP version
1373 function compat($function_name)
1375 ak_compat($function_name);
1380 * The Akelos Framework has an standardized way to convert between formats.
1381 * You can find available converters on AkConverters
1383 * Usage Example: In order to convert from HTML to RTF you just need to call.
1384 * $rtf = Ak::convert('html','rtf', $my_html_file, array('font_size'=> 24));
1386 * Where the last option is an array of options for selected converter.
1388 * Previous example is the same as.
1390 * $rtf = Ak::convert(array('from'=>'html','to'=>'rtf', 'source' => $my_html_file, 'font_size'=> 24));
1392 * In order to create converters, you just need to name them "SourceFormatName + To + DestinationFormatName".
1393 * Whenever you need to call the, you need to specify the "path" option where your converter is located.
1394 * The only thing you converter must implement is a convert function. Passes options will be made available
1395 * as attributes on the converter.
1396 * If your converter needs to prepare something before the convert method is called, you just need to implement
1397 * a "init" method. You can avoid this by inspecting passed attributes to your constructor
1399 function convert()
1401 $args = func_get_args();
1402 $number_of_arguments = func_num_args();
1403 if($number_of_arguments > 1){
1404 $options = array();
1405 if($number_of_arguments > 3 && is_array($args[$number_of_arguments-1])){
1406 $options = array_pop($args);
1408 $options['from'] = $args[0];
1409 $options['to'] = $args[1];
1410 $options['source'] = $args[2];
1411 }else{
1412 $options = $args;
1415 $options['class_prefix'] = empty($options['class_prefix']) && empty($options['path']) ? 'Ak' : $options['class_prefix'];
1416 $options['path'] = rtrim(empty($options['path']) ? AK_LIB_DIR.DS.'AkConverters' : $options['path'], DS."\t ");
1418 $converter_class_name = $options['class_prefix'].AkInflector::camelize($options['from']).'To'.AkInflector::camelize($options['to']);
1419 if(!class_exists($converter_class_name)){
1420 $file_name = $options['path'].DS.$converter_class_name.'.php';
1421 if(!file_exists($file_name)){
1422 if(defined('AK_REMOTE_CONVERTER_URI')){
1423 require_once(AK_LIB_DIR.DS.'AkConverters'.DS.'AkRemoteConverter.php');
1424 $result = AkRemoteConverter::convert($options['from'], $options['to'], $options['source']);
1425 if($result !== false){
1426 return $result;
1429 trigger_error(Ak::t('Could not locate %from to %to converter on %file_name',array('%from'=>$options['from'],'%to'=>$options['to'],'%file_name'=>$file_name)),E_USER_NOTICE);
1430 return false;
1432 require_once($file_name);
1434 if(!class_exists($converter_class_name)){
1435 trigger_error(Ak::t('Could not load %converter_class_name converter class',array('%converter_class_name'=>$converter_class_name)),E_USER_NOTICE);
1436 return false;
1439 $converter = new $converter_class_name($options);
1440 foreach ($options as $option=>$value){
1441 $option[0] != '_' ? $converter->$option = $value : null;
1444 if(method_exists($converter, 'init')){
1445 $converter->init();
1447 return $converter->convert();
1452 * Converts given string to UTF-8
1454 * @param string $text
1455 * @param string $input_string_encoding
1456 * @return string UTF-8 encoded string
1458 function utf8($text, $input_string_encoding = null)
1460 $input_string_encoding = empty($input_string_encoding) ? Ak::encoding() : $input_string_encoding;
1461 require_once(AK_LIB_DIR.DS.'AkCharset.php');
1462 $Charset =& Ak::singleton('AkCharset',$text);
1463 return $Charset->RecodeString($text,'UTF-8',$input_string_encoding);
1466 function recode($text, $output_string_encoding = null, $input_string_encoding = null)
1468 $input_string_encoding = empty($input_string_encoding) ? Ak::encoding() : $input_string_encoding;
1469 require_once(AK_LIB_DIR.DS.'AkCharset.php');
1470 $Charset =& Ak::singleton('AkCharset',$text);
1471 return $Charset->RecodeString($text,$output_string_encoding,$input_string_encoding);
1474 function encoding()
1476 static $encoding;
1477 if(empty($encoding)){
1478 // This will force system language settings
1479 Ak::t('Akelos');
1480 $encoding = Ak::locale('charset', Ak::lang());
1481 $encoding = empty($encoding) ? 'UTF-8' : $encoding;
1483 return $encoding;
1487 * Get the encoding in which current user is sending the request
1489 function userEncoding()
1491 static $encoding;
1493 if(!isset($encoding)){
1494 $encoding = Ak::encoding();
1495 if(!empty($_SERVER['HTTP_ACCEPT_CHARSET'])){
1496 $accepted_charsets = array_map('strtoupper', array_diff(explode(';',str_replace(',',';',$_SERVER['HTTP_ACCEPT_CHARSET']).';'), array('')));
1497 if(!in_array($encoding,$accepted_charsets)){
1498 $encoding = array_shift($accepted_charsets);
1502 return $encoding;
1506 * strlen for UTF-8 strings
1507 * Taken from anpaza at mail dot ru post at http://php.net/strlen
1509 function strlen_utf8($str)
1511 $i = $count = 0;
1512 $len = strlen ($str);
1513 while ($i < $len){
1514 $chr = ord ($str[$i]);
1515 $count++;
1516 $i++;
1517 if ($i >= $len){
1518 break;
1520 if ($chr & 0x80){
1521 $chr <<= 1;
1522 while ($chr & 0x80){
1523 $i++;
1524 $chr <<= 1;
1528 return $count;
1532 * Convert an arbitrary PHP value into a JSON representation string.
1534 * For AJAX driven pages, JSON can come in handy – you can return send JavaScript objects
1535 * directly from your actions.
1537 function toJson($php_value)
1539 require_once(AK_VENDOR_DIR.DS.'pear'.DS.'Services'.DS.'JSON.php');
1540 $use = 0;
1541 $json =& Ak::singleton('Services_JSON', $use);
1542 return $json->encode($php_value);
1546 * Converts a JSON representation string into a PHP value.
1548 function fromJson($json_string)
1550 require_once(AK_VENDOR_DIR.DS.'pear'.DS.'Services'.DS.'JSON.php');
1551 $use = 0;
1552 $json =& Ak::singleton('Services_JSON', $use);
1553 return $json->decode($json_string);
1556 function &memory_cache($key, &$value)
1558 static $memory, $md5;
1559 if($value === false){
1560 // remove the object from cache
1561 $memory[$key] = null;
1562 $md5[$key] = null;
1563 }elseif($value === true){
1564 //check if the object is on cache or unaltered
1565 $result = !empty($memory[$key]) ? $md5[$key] == Ak::getStatusKey($memory[$key]) : false;
1566 return $result;
1567 }elseif ($value === null){
1568 //get the object
1569 return $memory[$key];
1570 }else{
1571 //set the object
1572 $md5[$key] = Ak::getStatusKey($value);
1573 $memory[$key] =& $value;
1576 return $value;
1579 function getStatusKey($element)
1581 if(AK_PHP5){
1582 $element = clone($element);
1584 if(isset($element->___status_key)){
1585 unset($element->___status_key);
1587 return md5(serialize($element));
1590 function logObjectForModifications(&$object)
1592 $object->___status_key = empty($object->___status_key) ? Ak::getStatusKey($object) : $object->___status_key;
1593 return $object->___status_key;
1596 function resetObjectModificationsWacther(&$object)
1598 unset($object->___status_key);
1601 function objectHasBeenModified(&$object)
1603 if(isset($object->___status_key)){
1604 $old_status = $object->___status_key;
1605 $new_key = Ak::getStatusKey($object);
1606 return $old_status != $new_key;
1607 }else{
1608 Ak::logObjectForModifications($object);
1609 return false;
1611 return true;
1614 function &call_user_func_array($function_name, $parameters)
1616 if(AK_PHP5){
1617 $result = call_user_func_array($function_name, $parameters);
1618 return $result;
1620 $user_function_name = is_string($function_name) ? $function_name : (is_object($function_name[0]) ? '$function_name[0]->'.$function_name[1] : $function_name[0].'::'.$function_name[1]);
1621 $arguments = array();
1622 $argument_keys = array_keys($parameters);
1623 foreach($argument_keys as $k){
1624 $arguments[] = '$parameters['.$argument_keys[$k].']';
1626 eval('$_result =& '.$user_function_name.'('.implode($arguments, ', ').');');
1627 // Dirty hack for avoiding pass by reference warnings.
1628 $result =& $_result;
1629 return $result;
1633 function &array_sort_by($array, $key = null, $direction = 'asc')
1635 $array_copy = $sorted_array = array();
1636 foreach (array_keys($array) as $k) {
1637 $array_copy[$k] =& $array[$k][$key];
1640 natcasesort($array_copy);
1641 if(strtolower($direction) == 'desc'){
1642 $array_copy = array_reverse($array_copy, true);
1645 foreach (array_keys($array_copy) as $k){
1646 $sorted_array[$k] =& $array[$k];
1649 return $sorted_array;
1652 function mime_content_type($file)
1654 static $mime_types;
1655 ak_compat('mime_content_type');
1656 empty($mime_types) ? require(AK_LIB_DIR.DS.'utils'.DS.'mime_types.php') : null;
1657 $file_extension = substr($file,strrpos($file,'.')+1);
1658 return !empty($mime_types[$file_extension]) ? $mime_types[$file_extension] : false;
1661 function stream($path, $buffer_size = 4096)
1663 ob_implicit_flush();
1664 $len = empty($buffer_size) ? 4096 : $buffer_size;
1665 $fp = fopen($path, "rb");
1666 while (!feof($fp)) {
1667 echo fread($fp, $len);
1671 function _nextPermutation($p, $size)
1673 for ($i = $size - 1; isset($p[$i]) && isset($p[$i+1]) && $p[$i] >= $p[$i+1]; --$i) { }
1674 if ($i == -1) { return false; }
1675 for ($j = $size; $p[$j] <= $p[$i]; --$j) { }
1676 $tmp = $p[$i]; $p[$i] = $p[$j]; $p[$j] = $tmp;
1677 for (++$i, $j = $size; $i < $j; ++$i, --$j) {
1678 $tmp = $p[$i]; $p[$i] = $p[$j]; $p[$j] = $tmp;
1680 return $p;
1684 * Returns all the possible permutations of given array
1686 function permute($array, $join_with = false)
1688 $size = count($array) - 1;
1689 $perm = range(0, $size);
1690 $j = 0;
1691 do {
1692 foreach ($perm as $i) {
1693 $perms[$j][] = $array[$i];
1695 } while ($perm = Ak::_nextPermutation($perm, $size) AND ++$j);
1697 if($join_with){
1698 foreach ($perms as $perm){
1699 $joined_perm[] = join(' ',$perm);
1701 return $joined_perm;
1703 return $perms;
1707 * Generates a Universally Unique IDentifier, version 4.
1709 * RFC 4122 (http://www.ietf.org/rfc/rfc4122.txt) defines a special type of Globally
1710 * Unique IDentifiers (GUID), as well as several methods for producing them. One
1711 * such method, described in section 4.4, is based on truly random or pseudo-random
1712 * number generators, and is therefore implementable in a language like PHP.
1714 * We choose to produce pseudo-random numbers with the Mersenne Twister, and to always
1715 * limit single generated numbers to 16 bits (ie. the decimal value 65535). That is
1716 * because, even on 32-bit systems, PHP's RAND_MAX will often be the maximum *signed*
1717 * value, with only the equivalent of 31 significant bits. Producing two 16-bit random
1718 * numbers to make up a 32-bit one is less efficient, but guarantees that all 32 bits
1719 * are random.
1721 * The algorithm for version 4 UUIDs (ie. those based on random number generators)
1722 * states that all 128 bits separated into the various fields (32 bits, 16 bits, 16 bits,
1723 * 8 bits and 8 bits, 48 bits) should be random, except : (a) the version number should
1724 * be the last 4 bits in the 3rd field, and (b) bits 6 and 7 of the 4th field should
1725 * be 01. We try to conform to that definition as efficiently as possible, generating
1726 * smaller values where possible, and minimizing the number of base conversions.
1728 * @copyright Copyright (c) CFD Labs, 2006. This function may be used freely for
1729 * any purpose ; it is distributed without any form of warranty whatsoever.
1730 * @author David Holmes <dholmes@cfdsoftware.net>
1732 * @return string A UUID, made up of 32 hex digits and 4 hyphens.
1734 function uuid()
1737 // The field names refer to RFC 4122 section 4.1.2
1738 return sprintf('%04x%04x-%04x-%03x4-%04x-%04x%04x%04x',
1739 mt_rand(0, 65535), mt_rand(0, 65535), // 32 bits for "time_low"
1740 mt_rand(0, 65535), // 16 bits for "time_mid"
1741 mt_rand(0, 4095), // 12 bits before the 0100 of (version) 4 for "time_hi_and_version"
1742 bindec(substr_replace(sprintf('%016b', mt_rand(0, 65535)), '01', 6, 2)),
1743 // 8 bits, the last two of which (positions 6 and 7) are 01, for "clk_seq_hi_res"
1744 // (hence, the 2nd hex digit after the 3rd hyphen can only be 1, 5, 9 or d)
1745 // 8 bits for "clk_seq_low"
1746 mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535) // 48 bits for "node"
1751 function test($test_case_name, $use_sessions = false)
1753 ak_test($test_case_name, $use_sessions);
1757 * Use this function for securing includes. This way you can prevent file inclusion attacks
1759 function sanitize_include($include, $mode = 'normal')
1761 $rules = array(
1762 'paranoid' => '/([^A-Z^a-z^0-9^_^-^ ]+)/',
1763 'high' => '/([^A-Z^a-z^0-9^_^-^ ^\/^\\\^:]+)/',
1764 'normal' => '/([^A-Z^a-z^0-9^_^-^ ^\.^\/^\\\]+)/'
1766 $mode = array_key_exists($mode,$rules) ? $mode : 'normal';
1767 return preg_replace($rules[$mode],'',$include);
1771 * Returns a PHP Object from an API resource
1774 function client_api($resource, $options = array())
1776 $default_options = array(
1777 'protocol' => 'xml_rpc',
1778 'build' => true
1780 $options = array_merge($default_options, $options);
1782 require(AK_LIB_DIR.DS.'AkActionWebService'.DS.'AkActionWebServiceClient.php');
1783 $Client =& new AkActionWebServiceClient($options['protocol']);
1784 $Client->init($resource, $options);
1785 return $Client;
1790 * Cross PHP version replacement for html_entity_decode. Emulates PHP5 behaviour on PHP4 on UTF-8 entities
1792 function html_entity_decode($html, $translation_table_or_quote_style = null)
1794 if(AK_PHP5){
1795 return html_entity_decode($html,empty($translation_table_or_quote_style) ? ENT_QUOTES : $translation_table_or_quote_style,'UTF-8');
1797 require_once(AK_LIB_DIR.DS.'AkCharset.php');
1798 $html = preg_replace('~&#x([0-9a-f]+);~ei', 'AkCharset::_CharToUtf8(hexdec("\\1"))', $html);
1799 $html = preg_replace('~&#([0-9]+);~e', 'AkCharset::_CharToUtf8("\\1")', $html);
1800 if(empty($translation_table_or_quote_style)){
1801 $translation_table_or_quote_style = get_html_translation_table(HTML_ENTITIES);
1802 $translation_table_or_quote_style = array_flip($translation_table_or_quote_style);
1804 return strtr($html, $translation_table_or_quote_style);
1808 * Loads the plugins found at app/vendor/plugins
1810 function &loadPlugins()
1812 require_once(AK_LIB_DIR.DS.'AkPlugin.php');
1813 $PluginManager =& new AkPluginLoader();
1814 $PluginManager->loadPlugins();
1815 return $PluginManager;
1820 // Now some static functions that are needed by the whole framework
1822 function translate($string, $args = null, $controller = null)
1824 return Ak::t($string, $args, $controller);
1828 function ak_test($test_case_name, $use_sessions = false)
1830 if(!defined('ALL_TESTS_CALL')){
1831 $use_sessions ? @session_start() : null;
1832 $test = &new $test_case_name();
1833 if (defined('AK_CLI') && AK_CLI || TextReporter::inCli() || (defined('AK_CONSOLE_MODE') && AK_CONSOLE_MODE) || (defined('AK_WEB_REQUEST') && !AK_WEB_REQUEST)) {
1834 $test->run(new TextReporter());
1835 }else{
1836 $test->run(new HtmlReporter());
1841 function ak_compat($function_name)
1843 if(!function_exists($function_name)){
1844 require_once(AK_VENDOR_DIR.DS.'pear'.DS.'PHP'.DS.'Compat'.DS.'Function'.DS.$function_name.'.php');
1848 function ak_generate_mock($name)
1850 static $Mock;
1851 if(empty($Mock)){
1852 $Mock = new Mock();
1854 $Mock->generate($name);
1858 * This function sets a constant and returns it's value. If constant has been already defined it
1859 * will reutrn its original value.
1861 * Returns null in case the constant does not exist
1863 * @param string $name
1864 * @param mixed $value
1866 function ak_define($name, $value = null)
1868 $name = strtoupper($name);
1869 $name = substr($name,0,3) == 'AK_' ? $name : 'AK_'.$name;
1870 return defined($name) ? constant($name) : (is_null($value) ? null : (define($name, $value) ? $value : null));
1874 * PHP4 triggers "Only variable references should be returned by reference" error when
1875 * a method that should return an object reference returns a boolean/array
1877 * The old method was to use a global variables, but it can lead into hard to debug bugs.
1879 * Now you'll need to use the following technique if you whant to build functions that
1880 * can return Object references or TRUE/FALSE.
1882 * $result = false;
1883 * return $result;
1887 * Globals are deprecated. Used ak_false, ak_true and ak_array instead
1889 * @deprecated
1891 $GLOBALS['false'] = false;
1892 $GLOBALS['true'] = true;
1895 AK_PHP5 || function_exists('clone') ? null : eval('function clone($object){return $object;}');
1897 Ak::profile('Ak.php class included'.__FILE__);