Fixing file upload params ($_FILES) normalization. Closes #75
[akelos.git] / lib / Ak.php
blob59e7c49a43bb57e12a11e6534a17693f4811410d
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 AdoDb database connection
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, $connection_id = null)
66 static $db, $default_connection_id;
68 // In order to retrieve a database connection we just need to provide its identifier
69 if(empty($default_connection_id)){
70 $default_connection_id = md5($dsn);
72 $connection_id = empty($connection_id) ? $default_connection_id : $connection_id;
74 if(empty($db[$connection_id])){
75 require_once(AK_CONTRIB_DIR.DS.'adodb'.DS.'adodb.inc.php');
77 if(substr($dsn, 0, 6) == 'mysql:'){
78 $dsn = substr_replace($dsn, 'mysqlt:', 0, 6);
81 if (!$db[$connection_id] = (AK_DEBUG ? NewADOConnection($dsn) : @NewADOConnection($dsn))){
82 error_reporting(E_ALL);
83 if(defined('AK_DATABASE_CONNECTION_FAILURE_CALLBACK') && function_exists(AK_DATABASE_CONNECTION_FAILURE_CALLBACK)){
84 $fn = AK_DATABASE_CONNECTION_FAILURE_CALLBACK;
85 $fn();
87 if(!AK_PHP5 && substr($dsn,0,6) == 'sqlite'){
88 echo "\nWarning, sqlite support is not available by default on PHP4.\n Check your PHP version by running \"env php -v\", and change the first line in your scripts/ so they point to a php5 binary\n\n";
90 die(Ak::t('Connection to the database failed.').' '.
91 (AK_DEBUG?preg_replace('/\/\/(\w+):(.*)@/i','//$1:******@', urldecode($dsn))."\n":''));
93 $db[$connection_id]->debug = AK_DEBUG == 2;
94 defined('AK_DATABASE_CONNECTION_AVAILABLE') ? null : define('AK_DATABASE_CONNECTION_AVAILABLE', true);
95 $dsn = '';
97 return $db[$connection_id];
103 * Gets a cache object singleton instance
105 function &cache()
107 static $cache;
108 if(!isset($cache)){
109 require_once(AK_LIB_DIR.DS.'AkCache.php');
110 $cache = new AkCache();
112 return $cache;
116 function toUrl($options, $set_routes = false)
118 static $Map;
119 if(empty($Map)){
120 if($set_routes){
121 $Map = $options;
122 return;
123 }else{
124 require_once(AK_LIB_DIR.DS.'AkRouter.php');
125 $Map = new AkRouter();
126 if(is_file(AK_ROUTES_MAPPING_FILE)){
127 include(AK_ROUTES_MAPPING_FILE);
131 return $Map->toUrl($options);
136 * Translate strings to the current locale.
138 * When using Ak::t(), try to put entire sentences and strings
139 * in one Ak::t() call.
140 * This makes it easier for translators. HTML markup within
141 * translation strings
142 * is acceptable, if necessary. The suggested syntax for a
143 * link embedded
144 * within a translation string is:
146 * @access public
147 * @static
148 * @param string $string A string containing the English string to
149 * translate.
150 * @param array $args An associative array of replacements to make after
151 * translation. Incidences of any key in this array
152 * are replaced with the corresponding value.
153 * @return string The translated string.
155 function t($string, $args = null, $controller = null)
157 static $framework_dictionary = array(), $lang, $_dev_shutdown = true;
159 if(AK_AUTOMATICALLY_UPDATE_LANGUAGE_FILES && !empty($string) && is_string($string)){
160 require_once(AK_LIB_DIR.DS.'AkLocaleManager.php');
161 // This adds used strings to a stack for storing new entries on the locale file after shutdown
162 AkLocaleManager::getUsedLanguageEntries($string, $controller);
163 if($_dev_shutdown){
164 register_shutdown_function(array('AkLocaleManager','updateLocaleFiles'));
165 $_dev_shutdown = false;
169 if(!isset($lang)){
170 if(!empty($_SESSION['lang'])){
171 $lang = $_SESSION['lang'];
172 }else{
173 $lang = Ak::lang();
175 if(is_file(AK_CONFIG_DIR.DS.'locales'.DS.$lang.'.php')){
176 require(AK_CONFIG_DIR.DS.'locales'.DS.$lang.'.php');
177 $framework_dictionary = array_merge($framework_dictionary,$dictionary);
179 if(!defined('AK_LOCALE')){
180 define('AK_LOCALE', $lang);
182 if(!empty($locale) && is_array($locale)){
183 Ak::locale(null, $lang, $locale);
187 if(!empty($string) && is_array($string)){
188 if(!empty($string[$lang])){
189 return $string[$lang];
191 $try_whith_lang = $args !== false && empty($string[$lang]) ? Ak::base_lang() : $lang;
192 if(empty($string[$try_whith_lang]) && $args !== false){
193 foreach (Ak::langs() as $try_whith_lang){
194 if(!empty($string[$try_whith_lang])){
195 return $string[$try_whith_lang];
199 return @$string[$try_whith_lang];
202 if(isset($controller) && !isset($framework_dictionary[$controller.'_dictionary']) && is_file(AK_APP_DIR.DS.'locales'.DS.$controller.DS.$lang.'.php')){
203 require(AK_APP_DIR.DS.'locales'.DS.$controller.DS.$lang.'.php');
204 $framework_dictionary[$controller.'_dictionary'] = (array)$dictionary;
207 if(isset($controller) && isset($framework_dictionary[$controller.'_dictionary'][$string])){
208 $string = $framework_dictionary[$controller.'_dictionary'][$string];
209 }else {
210 $string = isset($framework_dictionary[$string]) ? $framework_dictionary[$string] : $string;
213 if(isset($args) && is_array($args)){
214 $string = @str_replace(array_keys($args), array_values($args),$string);
218 * @todo Prepare for multiple locales by inspecting AK_DEFAULT_LOCALE
221 return $string;
227 * Gets information about current locale from the locale settings on config/locales/LOCALE.php
229 * This are common settings on the locale file:
230 * 'description' // Locale description Example. Spanish
231 * 'charset' // 'ISO-8859-1';
232 * 'date_time_format' // '%d/%m/%Y %H:%i:%s';
233 * 'date_format' // '%d/%m/%Y';
234 * 'long_date_format' // '%d/%m/%Y';
235 * 'time_format' // '%H:%i';
236 * 'long_time_format' // '%H:%i:%s';
238 function locale($locale_setting, $locale = null)
240 static $settings;
242 // We initiate the locale settings
243 Ak::t('Akelos');
245 $locale = empty($locale) ? (defined('AK_LOCALE') ? AK_LOCALE : (Ak::t('Akelos') && Ak::locale($locale_setting))) : $locale;
247 if (empty($settings[$locale])) {
248 if(func_num_args() != 3){ // First time we ask for something using this locale so we will load locale details
249 $requested_locale = $locale;
250 if(@include(AK_CONFIG_DIR.DS.'locales'.DS.Ak::sanitize_include($requested_locale).'.php')){
251 $locale = !empty($locale) && is_array($locale) ? $locale : array();
252 Ak::locale(null, $requested_locale, $locale);
253 return Ak::locale($locale_setting, $requested_locale);
255 }else{
256 $settings[$locale] = func_get_arg(2);
257 if(isset($settings[$locale]['charset'])){
258 defined('AK_CHARSET') ? null : (define('AK_CHARSET',$settings[$locale]['charset']) && @ini_set('default_charset', AK_CHARSET));
263 return isset($settings[$locale][$locale_setting]) ? $settings[$locale][$locale_setting] : false;
267 function lang($set_language = null)
269 static $lang;
270 $lang = empty($set_language) ? (empty($lang) ? AK_FRAMEWORK_LANGUAGE : $lang) : $set_language;
271 return $lang;
275 function get_url_locale($set_locale = null)
277 static $locale;
278 if(!empty($locale)){
279 return $locale;
281 $locale = empty($set_locale) ? '' : $set_locale;
282 return $locale;
287 function langs()
289 static $langs;
290 if(!empty($lang)){
291 return $lang;
293 $lang = Ak::lang();
294 if(defined('AK_APP_LOCALES')){
295 $langs = array_diff(explode(',',AK_APP_LOCALES.','),array(''));
297 $langs = empty($langs) ? array($lang) : $langs;
298 return $langs;
301 function base_lang()
303 return array_shift(Ak::langs());
308 function dir($path, $options = array())
310 $result = array();
312 $path = rtrim($path, '/\\');
313 $default_options = array(
314 'files' => true,
315 'dirs' => true,
316 'recurse' => false,
319 $options = array_merge($default_options, $options);
321 if(is_file($path)){
322 $result = array($path);
323 }elseif(is_dir($path)){
324 if ($id_dir = opendir($path)){
325 while (false !== ($file = readdir($id_dir))){
326 if ($file != "." && $file != ".." && $file != '.svn'){
327 if(!empty($options['files']) && !is_dir($path.DS.$file)){
328 $result[] = $file;
329 }elseif(!empty($options['dirs'])){
330 $result[][$file] = !empty($options['recurse']) ? Ak::dir($path.DS.$file, $options) : $file;
334 closedir($id_dir);
338 return array_reverse($result);
342 function file_put_contents($file_name, $content, $options = array())
345 $default_options = array(
346 'ftp' => defined('AK_UPLOAD_FILES_USING_FTP') && AK_UPLOAD_FILES_USING_FTP,
347 'base_path' => AK_BASE_DIR,
349 $options = array_merge($default_options, $options);
351 if(!function_exists('file_put_contents')){
352 include_once(AK_CONTRIB_DIR.DS.'pear'.DS.'PHP'.DS.'Compat.php');
353 PHP_Compat::loadFunction(array('file_put_contents'));
356 $file_name = trim(str_replace($options['base_path'], '',$file_name),DS);
358 if($options['ftp']){
359 require_once(AK_LIB_DIR.DS.'AkFtp.php');
360 $file_name = trim(str_replace(array(DS,'//'),array('/','/'),$file_name),'/');
361 if(!AkFtp::is_dir(dirname($file_name))){
362 AkFtp::make_dir(dirname($file_name));
365 return AkFtp::put_contents($file_name, $content);
366 }else{
367 if(!is_dir(dirname($options['base_path'].DS.$file_name))){
368 Ak::make_dir(dirname($options['base_path'].DS.$file_name), $options);
371 if(!$result = file_put_contents($options['base_path'].DS.$file_name, $content)){
372 if(!empty($content)){
373 Ak::trace("Please change file/dir permissions or enable FTP file handling by".
374 " setting the following on your config/".AK_ENVIRONMENT.".php file \n<pre>define('AK_UPLOAD_FILES_USING_FTP', true);\n".
375 "define('AK_READ_FILES_USING_FTP', false);\n".
376 "define('AK_DELETE_FILES_USING_FTP', true);\n".
377 "define('AK_FTP_PATH', 'ftp://username:password@example.com/path_to_the_framework');\n".
378 "define('AK_FTP_AUTO_DISCONNECT', true);\n</pre>");
381 return $result;
386 function file_get_contents($file_name, $options = array())
388 $default_options = array(
389 'ftp' => defined('AK_READ_FILES_USING_FTP') && AK_READ_FILES_USING_FTP,
390 'base_path' => AK_BASE_DIR,
392 $options = array_merge($default_options, $options);
394 $file_name = trim(str_replace($options['base_path'], '',$file_name),DS);
395 if($options['ftp']){
396 require_once(AK_LIB_DIR.DS.'AkFtp.php');
397 $file_name = trim(str_replace(array(DS,'//'),array('/','/'),$file_name),'/');
398 return AkFtp::get_contents($file_name);
399 }else{
400 return file_get_contents($options['base_path'].DS.$file_name);
405 * @todo Optimize this code (dirty add-on to log command line interpreter results)
407 function file_add_contents($file_name, $content, $options = array())
409 $original_content = @Ak::file_get_contents($file_name, $options);
410 return Ak::file_put_contents($file_name, $original_content.$content, $options);
413 function file_delete($file_name, $options = array())
415 $default_options = array(
416 'ftp' => defined('AK_DELETE_FILES_USING_FTP') && AK_DELETE_FILES_USING_FTP,
417 'base_path' => AK_BASE_DIR,
419 $options = array_merge($default_options, $options);
421 $file_name = trim(str_replace($options['base_path'], '',$file_name),DS);
422 if($options['ftp']){
423 require_once(AK_LIB_DIR.DS.'AkFtp.php');
424 $file_name = trim(str_replace(array(DS,'//'),array('/','/'),$file_name),'/');
425 return AkFtp::delete($file_name, true);
426 }else{
427 return unlink($options['base_path'].DS.$file_name);
431 function directory_delete($dir_name, $options = array())
433 $default_options = array(
434 'ftp' => defined('AK_DELETE_FILES_USING_FTP') && AK_DELETE_FILES_USING_FTP,
435 'base_path' => AK_BASE_DIR,
437 $options = array_merge($default_options, $options);
439 $sucess = true;
440 $dir_name = Ak::_getRestrictedPath($dir_name, $options);
442 if(empty($dir_name)){
443 return false;
446 if($options['ftp']){
447 require_once(AK_LIB_DIR.DS.'AkFtp.php');
448 return AkFtp::delete($dir_name);
449 }else{
450 $items = glob($options['base_path'].DS.$dir_name."/*");
451 $hidden_items = glob($options['base_path'].DS.$dir_name."/.*");
452 $fs_items = $items || $hidden_items ? array_merge((array)$items, (array)$hidden_items) : false;
453 if($fs_items){
454 $items_to_delete = array('directories'=>array(), 'files'=>array());
455 foreach($fs_items as $fs_item) {
456 if($fs_item[strlen($fs_item)-1] != '.'){
457 $items_to_delete[ (is_dir($fs_item) ? 'directories' : 'files') ][] = $fs_item;
460 foreach ($items_to_delete['files'] as $file){
461 Ak::file_delete($file, $options);
463 foreach ($items_to_delete['directories'] as $directory){
464 $sucess = $sucess ? Ak::directory_delete($directory, $options) : $sucess;
467 return $sucess ? rmdir($options['base_path'].DS.$dir_name) : $sucess;
471 function make_dir($path, $options = array())
473 $default_options = array(
474 'ftp' => defined('AK_UPLOAD_FILES_USING_FTP') && AK_UPLOAD_FILES_USING_FTP,
475 'base_path' => AK_BASE_DIR
477 $options = array_merge($default_options, $options);
479 $path = trim(str_replace($options['base_path'], '',$path),DS);
480 if($options['ftp']){
481 require_once(AK_LIB_DIR.DS.'AkFtp.php');
482 $path = trim(str_replace(array(DS,'//'),array('/','/'),$path),'/');
483 return AkFtp::make_dir($path);
484 }else{
485 $path = $options['base_path'].DS.$path;
486 if (!file_exists($path)){
487 Ak::make_dir(dirname($path), $options);
488 return mkdir($path);
491 return false;
495 * This static method will copy recursively all the files or directories from one
496 * path within an Akelos application to another.
498 * It uses current installation settings, so it can perform copies via the filesystem or via FTP
500 function copy($origin, $target, $options = array())
502 $default_options = array(
503 'ftp' => defined('AK_UPLOAD_FILES_USING_FTP') && AK_UPLOAD_FILES_USING_FTP,
504 'base_path' => AK_BASE_DIR,
506 $options = array_merge($default_options, $options);
508 $sucess = true;
510 $origin = Ak::_getRestrictedPath($origin, $options);
511 $target = Ak::_getRestrictedPath($target, $options);
513 if(empty($origin) || empty($target)){
514 return false;
517 if($options['ftp']){
518 require_once(AK_LIB_DIR.DS.'AkFtp.php');
520 $destination = str_replace($origin, $target, $origin);
521 if(is_file($options['base_path'].DS.$origin)){
522 return Ak::file_put_contents($options['base_path'].DS.$destination, Ak::file_get_contents($options['base_path'].DS.$origin, $options), $options);
524 Ak::make_dir($options['base_path'].DS.$destination);
525 if($fs_items = glob($options['base_path'].DS.$origin."/*")){
526 $items_to_copy = array('directories'=>array(), 'files'=>array());
527 foreach($fs_items as $fs_item) {
528 $items_to_copy[ (is_dir($fs_item) ? 'directories' : 'files') ][] = $fs_item;
530 foreach ($items_to_copy['files'] as $file){
531 $destination = str_replace($origin, $target, $file);
532 $sucess = $sucess ? Ak::file_put_contents($destination, Ak::file_get_contents($file, $options), $options) : $sucess;
534 foreach ($items_to_copy['directories'] as $directory){
535 $destination = str_replace($origin, $target, $directory);
536 $sucess = $sucess ? Ak::copy($directory, $destination, $options) : $sucess;
539 return $sucess;
543 * Returns a path restricting it to a base location
545 * This is used by Akelos to prevent functions namespaced under Ak
546 * from writing out of the Akelos base directory for security reasons.
548 function _getRestrictedPath($path, $options = array())
550 $default_options = array(
551 'ftp' => false,
552 'base_path' => AK_BASE_DIR,
554 $options = array_merge($default_options, $options);
556 $path = str_replace('..','', rtrim($path,'\\/. '));
557 $path = trim(str_replace($options['base_path'], '',$path),DS);
559 if($options['ftp']){
560 $path = trim(str_replace(array(DS,'//'),array('/','/'), $path),'/');
563 return $path;
568 * Perform a web request using curl
570 * @param string $url URL we are going to request.
571 * @param array $options Options for current request.
572 * Options are:
573 * * referer: URL that will be set as referer url. Default is current url
574 * * params: Parameter for the request. Can be an array of key=>values or a url params string like key=value&key2=value2
575 * * method: In case params are given the will be requested using post method by default. Specify get if post is not what you need.
576 * * browser_name: How are we going to be presented to the website. Default is 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)'
577 * @return string
579 function url_get_contents($url, $options = array())
581 ak_compat('http_build_query');
583 $default_options = array(
584 'referer' => $url,
585 'method' => 'post',
586 'timeout' => 100,
587 'params' => '',
588 'browser_name' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
591 $options = array_merge($default_options, $options);
593 $options['params'] = !empty($options['params']) ? (is_array($options['params']) ? http_build_query(array_map('urlencode', $options['params'])) : $options['params']) : '';
594 $options['method'] = strtolower($options['method']) == 'post' ? 'post' : 'get';
596 $ch = curl_init();
598 curl_setopt($ch, CURLOPT_URL, $url);
599 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
600 curl_setopt($ch, CURLOPT_USERAGENT, $options['browser_name']);
601 curl_setopt($ch, CURLOPT_HEADER, 0);
603 if(!empty($options['params']) && $options['method'] == 'post'){
604 curl_setopt($ch, CURLOPT_POST, 1);
605 curl_setopt($ch, CURLOPT_POSTFIELDS, $options['params']);
606 }elseif(!empty($options['params'])){
607 $url = trim($url,'?').'?'.trim($options['params'], '?');
610 curl_setopt ($ch, CURLOPT_REFERER, $options['referer']);
612 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
613 curl_setopt($ch, CURLOPT_TIMEOUT, $options['timeout']);
615 $result = curl_exec ($ch);
616 curl_close ($ch);
618 return $result;
623 * Trace helper function for development purposes
625 * @access public
626 * @static
627 * @param string $text Helper text
628 * @param string $line Helper line
629 * @param string $file Helper file
630 * @return echoes result to screen
632 function trace($text = null, $line = null, $file = null)
634 static $counter = 0;
635 if(!AK_DEBUG){
636 //return;
639 $line = isset($line) ? "Line: $line".(AK_CLI?"\n":"<br />") : "";
640 $file = isset($file) ? "File: $file".(AK_CLI?"\n":"<br />") : "";
642 if(!isset($text)){
643 $counter++;
644 $text = '';
645 }else {
646 $text = AK_CLI?'---> '.$text:'<b>---&gt;</b>'.$text;
649 echo AK_CLI?"----------------\n$line $file $text\n----------------\n":"<hr /><div>$line $file $text</div><hr />\n";
657 * Outputs debug info given a PHP resource (vars, objects,
658 * arrays...)
660 * @access public
661 * @static
662 * @param mixed $data Data to debug. It can be an object, array,
663 * resource..
664 * @return void Prints debug info.
666 function debug ($data, $_functions=0)
668 if(!AK_DEBUG && !AK_DEV_MODE){
669 return;
672 if($_functions!=0) {
673 $sf=1;
674 } else {
675 $sf=0 ;
677 if(is_object($data) && method_exists($data, 'debug')){
678 echo AK_CLI ?
679 "\n------------------------------------\nEntering on ".get_class($data)." debug() method\n\n":
680 "<hr /><h2>Entering on ".get_class($data)." debug() method</h2>";
681 if(!empty($data->__activeRecordObject)){
682 $data->toString(true);
684 $data->debug();
685 return ;
687 if (isset ($data)) {
688 if (is_array($data) || is_object($data)) {
690 if (count ($data)) {
691 echo AK_CLI ? "/--\n" : "<ol>\n";
692 while (list ($key,$value) = each ($data)) {
693 $type=gettype($value);
694 if ($type=="array" || $type == "object") {
695 ob_start();
696 Ak::debug ($value,$sf);
697 $lines = explode("\n",ob_get_clean()."\n");
698 foreach ($lines as $line){
699 echo "\t".$line."\n";
701 } elseif (eregi ("function", $type)) {
702 if ($sf) {
703 AK_CLI ? printf ("\t* (%s) %s:\n",$type, $key, $value) :
704 printf ("<li>(%s) <b>%s</b> </li>\n",$type, $key, $value);
706 } else {
707 if (!$value) {
708 $value="(none)";
710 AK_CLI ? printf ("\t* (%s) %s = %s\n",$type, $key, $value) :
711 printf ("<li>(%s) <b>%s</b> = %s</li>\n",$type, $key, $value);
714 echo AK_CLI ? "\n--/\n" : "</ol>fin.\n";
715 } else {
716 echo "(empty)";
727 * Gets information about given object
729 * @access public
730 * @static
731 * @uses Ak::get_this_object_methods
732 * @uses Ak::get_this_object_attributes
733 * @param object &$object Object to get info from
734 * @param boolean $include_inherited_info By setting this to true, parent Object properties
735 * and methods will be included.
736 * @return string html output with Object info
738 function get_object_info($object, $include_inherited_info = false)
740 $object_name = get_class($object);
741 $methods = $include_inherited_info ? get_class_methods($object) : Ak::get_this_object_methods($object);
742 $vars = $include_inherited_info ? get_class_vars($object_name) : Ak::get_this_object_attributes($object);
743 $var_desc = '';
744 if(is_array($vars)){
745 $var_desc = '<ul>';
746 foreach ($vars as $varname=>$var_value){
747 $var_desc .= "<li>$varname = $var_value (". gettype($var_value) .")</li>\n";
749 $var_desc .= "</ul>";
751 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) .'();'));
758 * Gets selected object methods.
760 * WARNING: Inherited methods are not returned by this
761 * function. You can fetch them by using PHP native function
762 * get_class_methods
764 * @access public
765 * @static
766 * @see get_this_object_attributes
767 * @see get_object_info
768 * @param object &$object Object to inspect
769 * @return array Returns an array with selected object methods. It
770 * does not return inherited methods
772 function get_this_object_methods($object)
774 $array1 = get_class_methods($object);
775 if($parent_object = get_parent_class($object)){
776 $array2 = get_class_methods($parent_object);
777 $array3 = array_diff($array1, $array2);
778 }else{
779 $array3 = $array1;
781 return array_values((array)$array3);
788 * Get selected objects default attributes
790 * WARNING: Inherited attributes are not returned by this
791 * function. You can fetch them by using PHP native function
792 * get_class_vars
794 * @access public
795 * @static
796 * @see get_this_object_methods
797 * @see get_object_info
798 * @param object &$object Object to inspect
799 * @return void Returns an array with selected object attributes.
800 * It does not return inherited attributes
802 function get_this_object_attributes($object)
804 $object = get_class($object);
805 $array1 = get_class_vars($object);
806 if($parent_object = get_parent_class($object)){
807 $array2 = get_class_vars($parent_object);
808 $array3 = array_diff_assoc($array1, $array2);
809 }else{
810 $array3 = $array1;
812 return (array)$array3;
817 function &getLogger()
819 static $Logger;
820 if(empty($Logger)){
821 require_once(AK_LIB_DIR.DS.'AkLogger.php');
822 $Logger =& new AkLogger();
824 return $Logger;
828 function get_constants()
830 $constants = get_defined_constants();
831 $keys = array_keys($constants);
832 foreach ($keys as $k){
833 if(substr($k,0,3) != 'AK_'){
834 unset($constants[$k]);
837 return $constants;
842 * @todo Use timezone time
844 function time()
846 return time()+(defined('AK_TIME_DIFERENCE') ? AK_TIME_DIFERENCE*3600 : 0);
849 function gmt_time()
851 return Ak::time()+(AK_TIME_DIFERENCE_FROM_GMT*3600);
856 * Gets a timestamp for input date provided in one of this formats: "year-month-day hour:min:sec", "year-month-day", "hour:min:sec"
858 function getTimestamp($iso_date_or_hour = null)
860 if(empty($iso_date_or_hour)){
861 return Ak::time();
863 if (!preg_match("/^
864 ([0-9]{4})[-\/\.]? # year
865 ([0-9]{1,2})[-\/\.]? # month
866 ([0-9]{1,2})[ -]? # day
868 ([0-9]{1,2}):? # hour
869 ([0-9]{2}):? # minute
870 ([0-9\.]{0,4}) # seconds
871 )?/x", ($iso_date_or_hour), $rr)){
872 if (preg_match("|^(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", ($iso_date_or_hour), $rr)){
873 return empty($rr[0]) ? Ak::time() : mktime($rr[2],$rr[3],$rr[4]);
875 }else{
876 if($rr[1]>=2038 || $rr[1]<=1970){
877 require_once(AK_CONTRIB_DIR.DS.'adodb'.DS.'adodb-time.inc.php');
878 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]);
879 }else{
880 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]);
883 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"'));
884 return false;
888 * Return formatted date.
890 * You can supply a format as defined at http://php.net/date
892 * Default date is in ISO format
894 function getDate($timestamp = null, $format = null)
896 $timestamp = !isset($timestamp) ? Ak::time() : $timestamp;
897 $use_adodb = $timestamp <= -3600 || $timestamp >= 2147468400;
898 if($use_adodb){
899 require_once(AK_CONTRIB_DIR.DS.'adodb'.DS.'adodb-time.inc.php');
901 if(!isset($format)){
902 return $use_adodb ? adodb_date('Y-m-d H:i:s', $timestamp) : date('Y-m-d H:i:s', $timestamp);
903 }elseif (!empty($format)){
904 return $use_adodb ? adodb_date($format, $timestamp) : date($format, $timestamp);
906 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")'));
907 return false;
912 * mail function substitute. Uses the PEAR::Mail() function API.
914 * Messaging subsystem for user communication. See PEAR::Mail() function in PHP
915 * documentation for information.
917 * User must declare any of these variables for specify the outgoing method. Currently,
918 * only Sendmail and STMP methods are available . Variables
919 * for using any of these methods are:
921 * AK_SENDMAIL = 0
922 * AK_SMTP = 1
924 * For future upgrades, you must define which constants must be declared and add
925 * the functionality.
927 * NOTE: If messaging method is SMTP, you must declare in config file (/config/config.php)
928 * the outgoing SMTP server and the authentication pair user/password as constants
929 * AK_SMTP_SERVER, AK_SMTP_USER and AK_SMTP_PASSWORD, respectively.
932 * @param $from
934 * User who sends the mail.
936 * @param $to
938 * Receiver, or receivers of the mail.
940 * The formatting of this string must comply with RFC 2822. Some examples are:
942 * user@example.com
943 * user@example.com, anotheruser@example.com
944 * User <user@example.com>
945 * User <user@example.com>, Another User <anotheruser@example.com>
947 * @param $subject
949 * Subject of the email to be sent. This must not contain any newline
950 * characters, or the mail may not be sent properly.
952 * @param $body
954 * Message to be sent.
956 * @param additional_headers (optional)
958 * Array to be inserted at the end of the email header.
960 * This is typically used to add extra headers (Bcc) in an associative array, where the
961 * array key is the header name (i.e., 'Bcc'), and the array value is the header value
962 * (i.e., 'test'). The header produced from those values would be 'Bcc: test'.
964 * @return boolean whether message has been sent or not.
968 function mail ($from, $to, $subject, $body, $additional_headers = array())
970 require_once(AK_CONTRIB_DIR.DS.'pear'.DS.'Mail.php');
972 static $mail_connector;
974 if(!isset($mail_connector)){
975 if (defined('AK_SENDMAIL')) {
976 // Using Sendmail daemon without parameters.
977 $mail_connector = Mail::factory('sendmail');
978 } else if (defined('AK_SMTP') && AK_SMTP) {
979 // Using external SMTP server.
980 $params['host'] = AK_SMTP_SERVER;
981 $params['username'] = AK_SMTP_USER;
982 $params['password'] = AK_SMTP_PASSWORD;
984 $mail_connector = Mail::factory('smtp', $params);
985 } else {
986 // Using PHP mail() function thru PEAR. Factory without parameters.
987 $mail_connector = Mail::factory('mail');
991 $recipients['To'] = $to;
993 if (!empty($additional_headers)) {
994 foreach ($additional_headers as $k=>$v) {
996 if (strtolower($k)=='cc' || strtolower($k)=='cc:') {
997 $recipients['cc'] = $v;
998 unset($additional_headers['cc']);
1001 if (strtolower($k)=='bcc' || strtolower($k)=='bcc:') {
1002 $recipients['bcc'] = $v;
1003 unset($additional_headers['bcc']);
1008 $headers['From'] = $from;
1009 $headers['Subject'] = $subject;
1010 $headers['Content-Type'] = empty($headers['Content-Type']) ? 'text/plain; charset='.Ak::locale('charset').'; format=flowed' : $headers['Content-Type'];
1012 $headers = array_merge($headers, $additional_headers);
1014 $error_code = $mail_connector->send($recipients, $headers, $body);
1016 return $error_code;
1021 * @todo move this out of here and use Pear Benchmark instead
1023 function profile($message = '')
1025 static $profiler;
1026 if(AK_DEV_MODE && AK_ENABLE_PROFILER){
1027 if(!isset($profiler)){
1028 @require_once(AK_LIB_DIR.DS.'AkProfiler.php');
1029 $profiler = new AkProfiler();
1030 $profiler->init();
1031 register_shutdown_function(array(&$profiler,'showReport'));
1032 }else {
1033 $profiler->setFlag($message);
1040 * Gets the size of given element. Counts arrays, returns numbers, string length or executes size() method on given object
1042 function size($element)
1044 if(is_array($element)){
1045 return count($element);
1046 }elseif (is_numeric($element) && !is_string($element)){
1047 return $element;
1048 }elseif (is_string($element)){
1049 return strlen($element);
1050 }elseif (is_object($element) && method_exists($element,'size')){
1051 return $element->size();
1052 }else{
1053 return 0;
1059 * Select is a function for selecting items from double depth array.
1060 * This is useful when you just need some fields for generating
1061 * tables, select lists with only desired fields.
1063 * $People = array(
1064 * array('name'=>'Jose','email'=>'jose@example.com','address'=>'Colon, 52'),
1065 * array('name'=>'Alicia','email'=>'alicia@example.com','address'=>'Mayor, 45'),
1066 * array('name'=>'Hilario','email'=>'hilario@example.com','address'=>'Carlet, 78'),
1067 * array('name'=>'Bermi','email'=>'bermi@example.com','address'=>'Vilanova, 33'),
1068 * );
1070 * $people_for_table_generation = Ak::select($People,'name','email');
1072 * Now $people_for_table_generation will hold an array with
1073 * array (
1074 * array ('name' => 'Jose','email' => 'jose@example.com'),
1075 * array ('name' => 'Alicia','email' => 'alicia@example.com'),
1076 * array ('name' => 'Hilario','email' => 'hilario@example.com'),
1077 * array ('name' => 'Bermi','email' => 'bermi@example.com')
1078 * );
1081 function select(&$source_array)
1083 $resulting_array = array();
1084 if(!empty($source_array) && is_array($source_array) && func_num_args() > 1) {
1085 $args = array_slice(func_get_args(),1);
1086 foreach ($source_array as $source_item){
1087 $item_fields = array();
1088 foreach ($args as $arg){
1089 if(is_object($source_item) && isset($source_item->$arg)){
1090 $item_fields[$arg] = $source_item->$arg;
1091 }elseif(is_array($source_item) && isset($source_item[$arg])){
1092 $item_fields[$arg] = $source_item[$arg];
1095 if(!empty($item_fields)){
1096 $resulting_array[] = $item_fields;
1100 return $resulting_array;
1103 function collect(&$source_array, $key_index, $value_index)
1105 $resulting_array = array();
1106 if(!empty($source_array) && is_array($source_array)) {
1107 foreach ($source_array as $source_item){
1108 if(is_object($source_item)){
1109 $resulting_array[@$source_item->$key_index] = @$source_item->$value_index;
1110 }elseif(is_array($source_item)){
1111 $resulting_array[@$source_item[$key_index]] = @$source_item[$value_index];
1115 return $resulting_array;
1118 function delete($source_array, $attributes_to_delete_from_array)
1120 $resulting_array = (array)$source_array;
1121 $args = array_slice(func_get_args(),1);
1122 $args = count($args) == 1 ? Ak::toArray($args[0]) : $args;
1123 foreach ($args as $arg){
1124 unset($resulting_array[$arg]);
1126 return $resulting_array;
1129 function &singleton($class_name, &$arguments)
1131 static $instances;
1132 if(!isset($instances[$class_name])) {
1133 if(is_object($arguments)){
1134 $instances[$class_name] =& new $class_name($arguments);
1135 }else{
1136 if(Ak::size($arguments) > 0){
1137 eval("\$instances[\$class_name] =& new \$class_name(".var_export($arguments, true)."); ");
1138 }else{
1139 $instances[$class_name] =& new $class_name();
1142 $instances[$class_name]->__singleton_id = md5(microtime().rand(1000,9000));
1144 return $instances[$class_name];
1148 function xml_to_array ($xml_data)
1150 $xml_parser = xml_parser_create ();
1151 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);
1152 xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 1);
1153 xml_parse_into_struct ($xml_parser, $xml_data, $vals, $index);
1154 xml_parser_free ($xml_parser);
1155 $params = array();
1156 $ptrs[0] = & $params;
1157 foreach ($vals as $xml_elem) {
1158 $level = $xml_elem['level'] - 1;
1159 switch ($xml_elem['type']) {
1160 case 'open':
1161 $tag_or_id = (array_key_exists ('attributes', $xml_elem)) ? @$xml_elem['attributes']['ID'] : $xml_elem['tag'];
1162 $ptrs[$level][$tag_or_id][] = array ();
1163 $ptrs[$level+1] = & $ptrs[$level][$tag_or_id][count($ptrs[$level][$tag_or_id])-1];
1164 break;
1165 case 'complete':
1166 $ptrs[$level][$xml_elem['tag']] = (isset ($xml_elem['value'])) ? $xml_elem['value'] : '';
1167 break;
1170 return ($params);
1173 function array_to_xml($array, $header = "<?xml version=\"1.0\"?>\r\n", $parent = 'EMPTY_TAG')
1175 static $_tags = array();
1176 $xml = $header;
1177 foreach ($array as $key => $value) {
1178 $key = is_numeric($key) ? $parent : $key;
1179 $value = is_array($value) ? "\r\n".xmlFromArray($value, '', $key) : $value;
1180 $_tags[$key] = $key;
1181 $xml .= sprintf("<%s>%s</%s>\r\n", $key, $value, $key);
1182 $parent = $key;
1184 foreach ($_tags as $_tag){
1185 $xml = str_replace(array("<$_tag>\r\n<$_tag>","</$_tag>\r\n</$_tag>"),array("<$_tag>","</$_tag>"),$xml);
1187 return $xml;
1191 function encrypt($data, $key = 'Ak3los-m3D1a')
1193 srand((double)microtime() *1000000);
1194 $k2 = md5(rand(0, 32000));
1195 $c = 0;
1196 $m = '';
1197 for ($i = 0 ; $i < strlen($data) ; $i++) {
1198 if ($c == strlen($k2)) $c = 0;
1199 $m.= substr($k2, $c, 1) .(substr($data, $i, 1) ^substr($k2, $c, 1));
1200 $c++;
1202 $k = md5($key);
1203 $c = 0;
1204 $t = $m;
1205 $m = '';
1206 for ($i = 0 ; $i < strlen($t) ; $i++) {
1207 if ($c == strlen($k)) {
1208 $c = 0;
1210 $m.= substr($t, $i, 1) ^substr($k, $c, 1);
1211 $c++;
1213 return base64_encode($m);
1216 function decrypt($encrypted_data, $key = 'Ak3los-m3D1a')
1218 $t = base64_decode($encrypted_data);
1219 $k = md5($key);
1220 $c = 0;
1221 $m = '';
1222 for ($i = 0 ; $i < strlen($t) ; $i++) {
1223 if ($c == strlen($k)) $c = 0;
1224 $m.= substr($t, $i, 1) ^substr($k, $c, 1);
1225 $c++;
1227 $t = $m;
1228 $m = '';
1229 for ($i = 0 ; $i < strlen($t) ; $i++) {
1230 $d = substr($t, $i, 1);
1231 $i++;
1232 $m.= (substr($t, $i, 1) ^$d);
1234 return $m;
1238 function blowfishEncrypt($data, $key = 'Ak3los-m3D1a')
1240 $key = substr($key,0,56);
1241 require_once(AK_CONTRIB_DIR.DS.'pear'.DS.'Crypt'.DS.'Blowfish.php');
1242 $Blowfish =& Ak::singleton('Crypt_Blowfish', $key);
1243 $Blowfish->setKey($key);
1244 return $Blowfish->encrypt(base64_encode($data));
1247 function blowfishDecrypt($encrypted_data, $key = 'Ak3los-m3D1a')
1249 $key = substr($key,0,56);
1250 require_once(AK_CONTRIB_DIR.DS.'pear'.DS.'Crypt'.DS.'Blowfish.php');
1251 $Blowfish =& Ak::singleton('Crypt_Blowfish', $key);
1252 $Blowfish->setKey($key);
1253 return base64_decode($Blowfish->decrypt($encrypted_data));
1257 function randomString($max_length = 8)
1259 $randomString = '';
1260 srand((double)microtime()*1000000);
1261 for($i=0;$i<$max_length;$i++){
1262 $randnumber = rand(48,120);
1263 while (($randnumber >= 58 && $randnumber <= 64) || ($randnumber >= 91 && $randnumber <= 96)){
1264 $randnumber = rand(48,120);
1266 $randomString .= chr($randnumber);
1268 return $randomString;
1271 function compress($data, $format = 'gzip')
1273 $key = Ak::randomString(15);
1274 $compressed_file = AK_TMP_DIR.DS.'d'.$key;
1275 $uncompressed_file = AK_TMP_DIR.DS.'s'.$key;
1277 if(@Ak::file_put_contents($uncompressed_file, $data)){
1278 $compressed = gzopen($compressed_file,'w9');
1279 $uncompressed = fopen($uncompressed_file, 'rb');
1280 while(!feof($uncompressed)){
1281 $string = fread($uncompressed, 1024*512);
1282 gzwrite($compressed, $string, strlen($string));
1284 fclose($uncompressed);
1285 gzclose($compressed);
1286 }else{
1287 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);
1289 $result = Ak::file_get_contents($compressed_file);
1290 return $result;
1293 function uncompress($compressed_data, $format = 'gzip')
1295 $key = Ak::randomString(15);
1296 $compressed_file = AK_TMP_DIR.DS.'s'.$key;
1297 $uncompressed_file = AK_TMP_DIR.DS.'d'.$key;
1299 if(@Ak::file_put_contents($compressed_file, $compressed_data)){
1300 $compressed = gzopen($compressed_file, "r");
1301 $uncompressed = fopen($uncompressed_file, "w");
1302 while(!gzeof($compressed)){
1303 $string = gzread($compressed, 4096);
1304 fwrite($uncompressed, $string, strlen($string));
1306 gzclose($compressed);
1307 fclose($uncompressed);
1308 }else{
1309 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);
1311 $result = Ak::file_get_contents($uncompressed_file);
1312 return $result;
1316 function unzip($file_to_unzip, $destination_folder)
1318 require_once(AK_LIB_DIR.DS.'AkZip.php');
1319 $ArchiveZip =& new AkZip($file_to_unzip);
1320 $ArchiveZip->extract(array('add_path'=>str_replace(DS,'/',$destination_folder)));
1324 function decompress($compressed_data, $format = 'gzip')
1326 return Ak::uncompress($compressed_data, $format);
1330 function handleStaticCall()
1332 if (AK_PHP5) {
1333 trigger_error(Ak::t('Static calls emulation is not supported by PHP5 < 5.4'));
1334 die();
1336 $static_call = array_slice(debug_backtrace(),1,1);
1337 return call_user_func_array(array(new $static_call[0]['class'](),$static_call[0]['function']),$static_call[0]['args']);
1342 * Gets an array or a comma separated list of models. Then it includes its
1343 * respective files and returns an array of available models.
1345 * @return unknown
1347 function import()
1349 $args = func_get_args();
1350 $args = is_array($args[0]) ? $args[0] : (func_num_args() > 1 ? $args : Ak::stringToArray($args[0]));
1351 $models = array();
1352 foreach ($args as $arg){
1353 $model_name = AkInflector::camelize($arg);
1354 $model = AkInflector::toModelFilename($model_name);
1355 if(file_exists($model)){
1356 $models[] = $model_name;
1357 include_once($model);
1358 }elseif (class_exists($model_name)){
1359 $models[] = $model_name;
1363 return $models;
1366 function uses()
1368 $args = func_get_args();
1369 return call_user_func_array(array('Ak','import'),$args);
1372 function stringToArray($string)
1374 $args = $string;
1375 if(count($args) == 1 && !is_array($args)){
1376 (array)$args = array_unique(array_map('trim',array_diff(explode(',',strtr($args.',',';|-',',,,')),array(''))));
1378 return $args;
1382 function toArray()
1384 $args = func_get_args();
1385 return is_array($args[0]) ? $args[0] : (func_num_args() === 1 ? Ak::stringToArray($args[0]) : $args);
1390 * Includes PHP functions that are not available on current PHP version
1392 function compat($function_name)
1394 ak_compat($function_name);
1399 * The Akelos Framework has an standardized way to convert between formats.
1400 * You can find available converters on AkConverters
1402 * Usage Example: In order to convert from HTML to RTF you just need to call.
1403 * $rtf = Ak::convert('html','rtf', $my_html_file, array('font_size'=> 24));
1405 * Where the last option is an array of options for selected converter.
1407 * Previous example is the same as.
1409 * $rtf = Ak::convert(array('from'=>'html','to'=>'rtf', 'source' => $my_html_file, 'font_size'=> 24));
1411 * In order to create converters, you just need to name them "SourceFormatName + To + DestinationFormatName".
1412 * Whenever you need to call the, you need to specify the "path" option where your converter is located.
1413 * The only thing you converter must implement is a convert function. Passes options will be made available
1414 * as attributes on the converter.
1415 * If your converter needs to prepare something before the convert method is called, you just need to implement
1416 * a "init" method. You can avoid this by inspecting passed attributes to your constructor
1418 function convert()
1420 $args = func_get_args();
1421 $number_of_arguments = func_num_args();
1422 if($number_of_arguments > 1){
1423 $options = array();
1424 if($number_of_arguments > 3 && is_array($args[$number_of_arguments-1])){
1425 $options = array_pop($args);
1427 $options['from'] = $args[0];
1428 $options['to'] = $args[1];
1429 $options['source'] = $args[2];
1430 }else{
1431 $options = $args;
1434 $options['class_prefix'] = empty($options['class_prefix']) && empty($options['path']) ? 'Ak' : $options['class_prefix'];
1435 $options['path'] = rtrim(empty($options['path']) ? AK_LIB_DIR.DS.'AkConverters' : $options['path'], DS."\t ");
1437 $converter_class_name = $options['class_prefix'].AkInflector::camelize($options['from']).'To'.AkInflector::camelize($options['to']);
1438 if(!class_exists($converter_class_name)){
1439 $file_name = $options['path'].DS.$converter_class_name.'.php';
1440 if(!file_exists($file_name)){
1441 if(defined('AK_REMOTE_CONVERTER_URI')){
1442 require_once(AK_LIB_DIR.DS.'AkConverters'.DS.'AkRemoteConverter.php');
1443 $result = AkRemoteConverter::convert($options['from'], $options['to'], $options['source']);
1444 if($result !== false){
1445 return $result;
1448 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);
1449 return false;
1451 require_once($file_name);
1453 if(!class_exists($converter_class_name)){
1454 trigger_error(Ak::t('Could not load %converter_class_name converter class',array('%converter_class_name'=>$converter_class_name)),E_USER_NOTICE);
1455 return false;
1458 $converter = new $converter_class_name($options);
1459 foreach ($options as $option=>$value){
1460 $option[0] != '_' ? $converter->$option = $value : null;
1463 if(method_exists($converter, 'init')){
1464 $converter->init();
1466 return $converter->convert();
1471 * Converts given string to UTF-8
1473 * @param string $text
1474 * @param string $input_string_encoding
1475 * @return string UTF-8 encoded string
1477 function utf8($text, $input_string_encoding = null)
1479 $input_string_encoding = empty($input_string_encoding) ? Ak::encoding() : $input_string_encoding;
1480 require_once(AK_LIB_DIR.DS.'AkCharset.php');
1481 $Charset =& Ak::singleton('AkCharset',$text);
1482 return $Charset->RecodeString($text,'UTF-8',$input_string_encoding);
1485 function recode($text, $output_string_encoding = null, $input_string_encoding = null)
1487 $input_string_encoding = empty($input_string_encoding) ? Ak::encoding() : $input_string_encoding;
1488 require_once(AK_LIB_DIR.DS.'AkCharset.php');
1489 $Charset =& Ak::singleton('AkCharset',$text);
1490 return $Charset->RecodeString($text,$output_string_encoding,$input_string_encoding);
1493 function encoding()
1495 static $encoding;
1496 if(empty($encoding)){
1497 // This will force system language settings
1498 Ak::t('Akelos');
1499 $encoding = Ak::locale('charset', Ak::lang());
1500 $encoding = empty($encoding) ? 'UTF-8' : $encoding;
1502 return $encoding;
1506 * Get the encoding in which current user is sending the request
1508 function userEncoding()
1510 static $encoding;
1512 if(!isset($encoding)){
1513 $encoding = Ak::encoding();
1514 if(!empty($_SERVER['HTTP_ACCEPT_CHARSET'])){
1515 $accepted_charsets = array_map('strtoupper', array_diff(explode(';',str_replace(',',';',$_SERVER['HTTP_ACCEPT_CHARSET']).';'), array('')));
1516 if(!in_array($encoding,$accepted_charsets)){
1517 $encoding = array_shift($accepted_charsets);
1521 return $encoding;
1525 * strlen for UTF-8 strings
1526 * Taken from anpaza at mail dot ru post at http://php.net/strlen
1528 function strlen_utf8($str)
1530 $i = $count = 0;
1531 $len = strlen ($str);
1532 while ($i < $len){
1533 $chr = ord ($str[$i]);
1534 $count++;
1535 $i++;
1536 if ($i >= $len){
1537 break;
1539 if ($chr & 0x80){
1540 $chr <<= 1;
1541 while ($chr & 0x80){
1542 $i++;
1543 $chr <<= 1;
1547 return $count;
1551 * Convert an arbitrary PHP value into a JSON representation string.
1553 * For AJAX driven pages, JSON can come in handy – you can return send JavaScript objects
1554 * directly from your actions.
1556 function toJson($php_value)
1558 require_once(AK_VENDOR_DIR.DS.'pear'.DS.'Services'.DS.'JSON.php');
1559 $use = 0;
1560 $json =& Ak::singleton('Services_JSON', $use);
1561 return $json->encode($php_value);
1565 * Converts a JSON representation string into a PHP value.
1567 function fromJson($json_string)
1569 require_once(AK_VENDOR_DIR.DS.'pear'.DS.'Services'.DS.'JSON.php');
1570 $use = 0;
1571 $json =& Ak::singleton('Services_JSON', $use);
1572 return $json->decode($json_string);
1575 function &memory_cache($key, &$value)
1577 static $memory, $md5;
1578 if($value === false){
1579 // remove the object from cache
1580 $memory[$key] = null;
1581 $md5[$key] = null;
1582 }elseif($value === true){
1583 //check if the object is on cache or unaltered
1584 $result = !empty($memory[$key]) ? $md5[$key] == Ak::getStatusKey($memory[$key]) : false;
1585 return $result;
1586 }elseif ($value === null){
1587 //get the object
1588 return $memory[$key];
1589 }else{
1590 //set the object
1591 $md5[$key] = Ak::getStatusKey($value);
1592 $memory[$key] =& $value;
1595 return $value;
1598 function getStatusKey($element)
1600 if(AK_PHP5){
1601 $element = clone($element);
1603 if(isset($element->___status_key)){
1604 unset($element->___status_key);
1606 return md5(serialize($element));
1609 function logObjectForModifications(&$object)
1611 $object->___status_key = empty($object->___status_key) ? Ak::getStatusKey($object) : $object->___status_key;
1612 return $object->___status_key;
1615 function resetObjectModificationsWacther(&$object)
1617 unset($object->___status_key);
1620 function objectHasBeenModified(&$object)
1622 if(isset($object->___status_key)){
1623 $old_status = $object->___status_key;
1624 $new_key = Ak::getStatusKey($object);
1625 return $old_status != $new_key;
1626 }else{
1627 Ak::logObjectForModifications($object);
1628 return false;
1630 return true;
1633 function &call_user_func_array($function_name, $parameters)
1635 if(AK_PHP5){
1636 $result = call_user_func_array($function_name, $parameters);
1637 return $result;
1639 $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]);
1640 $arguments = array();
1641 $argument_keys = array_keys($parameters);
1642 foreach($argument_keys as $k){
1643 $arguments[] = '$parameters['.$argument_keys[$k].']';
1645 eval('$_result =& '.$user_function_name.'('.implode($arguments, ', ').');');
1646 // Dirty hack for avoiding pass by reference warnings.
1647 $result =& $_result;
1648 return $result;
1652 function &array_sort_by($array, $key = null, $direction = 'asc')
1654 $array_copy = $sorted_array = array();
1655 foreach (array_keys($array) as $k) {
1656 $array_copy[$k] =& $array[$k][$key];
1659 natcasesort($array_copy);
1660 if(strtolower($direction) == 'desc'){
1661 $array_copy = array_reverse($array_copy, true);
1664 foreach (array_keys($array_copy) as $k){
1665 $sorted_array[$k] =& $array[$k];
1668 return $sorted_array;
1672 function mime_content_type($file)
1674 static $mime_types;
1675 ak_compat('mime_content_type');
1677 $mime = @mime_content_type($file);
1679 if (AK_OS == 'WINDOWS' && $mime == 'application/octet-stream' && is_file($file)){
1680 $mime = false;
1683 if(empty($mime)){
1684 empty($mime_types) ? require(AK_LIB_DIR.DS.'utils'.DS.'mime_types.php') : null;
1685 $file_extension = substr($file,strrpos($file,'.')+1);
1686 $mime = !empty($mime_types[$file_extension]) ? $mime_types[$file_extension] : false;
1689 return $mime;
1692 function stream($path, $buffer_size = 4096)
1694 ob_implicit_flush();
1695 $len = empty($buffer_size) ? 4096 : $buffer_size;
1696 $fp = fopen($path, "rb");
1697 while (!feof($fp)) {
1698 echo fread($fp, $len);
1702 function _nextPermutation($p, $size)
1704 for ($i = $size - 1; isset($p[$i]) && isset($p[$i+1]) && $p[$i] >= $p[$i+1]; --$i) { }
1705 if ($i == -1) { return false; }
1706 for ($j = $size; $p[$j] <= $p[$i]; --$j) { }
1707 $tmp = $p[$i]; $p[$i] = $p[$j]; $p[$j] = $tmp;
1708 for (++$i, $j = $size; $i < $j; ++$i, --$j) {
1709 $tmp = $p[$i]; $p[$i] = $p[$j]; $p[$j] = $tmp;
1711 return $p;
1715 * Returns all the possible permutations of given array
1717 function permute($array, $join_with = false)
1719 $size = count($array) - 1;
1720 $perm = range(0, $size);
1721 $j = 0;
1722 do {
1723 foreach ($perm as $i) {
1724 $perms[$j][] = $array[$i];
1726 } while ($perm = Ak::_nextPermutation($perm, $size) AND ++$j);
1728 if($join_with){
1729 foreach ($perms as $perm){
1730 $joined_perm[] = join(' ',$perm);
1732 return $joined_perm;
1734 return $perms;
1738 * Generates a Universally Unique IDentifier, version 4.
1740 * RFC 4122 (http://www.ietf.org/rfc/rfc4122.txt) defines a special type of Globally
1741 * Unique IDentifiers (GUID), as well as several methods for producing them. One
1742 * such method, described in section 4.4, is based on truly random or pseudo-random
1743 * number generators, and is therefore implementable in a language like PHP.
1745 * We choose to produce pseudo-random numbers with the Mersenne Twister, and to always
1746 * limit single generated numbers to 16 bits (ie. the decimal value 65535). That is
1747 * because, even on 32-bit systems, PHP's RAND_MAX will often be the maximum *signed*
1748 * value, with only the equivalent of 31 significant bits. Producing two 16-bit random
1749 * numbers to make up a 32-bit one is less efficient, but guarantees that all 32 bits
1750 * are random.
1752 * The algorithm for version 4 UUIDs (ie. those based on random number generators)
1753 * states that all 128 bits separated into the various fields (32 bits, 16 bits, 16 bits,
1754 * 8 bits and 8 bits, 48 bits) should be random, except : (a) the version number should
1755 * be the last 4 bits in the 3rd field, and (b) bits 6 and 7 of the 4th field should
1756 * be 01. We try to conform to that definition as efficiently as possible, generating
1757 * smaller values where possible, and minimizing the number of base conversions.
1759 * @copyright Copyright (c) CFD Labs, 2006. This function may be used freely for
1760 * any purpose ; it is distributed without any form of warranty whatsoever.
1761 * @author David Holmes <dholmes@cfdsoftware.net>
1763 * @return string A UUID, made up of 32 hex digits and 4 hyphens.
1765 function uuid()
1768 // The field names refer to RFC 4122 section 4.1.2
1769 return sprintf('%04x%04x-%04x-%03x4-%04x-%04x%04x%04x',
1770 mt_rand(0, 65535), mt_rand(0, 65535), // 32 bits for "time_low"
1771 mt_rand(0, 65535), // 16 bits for "time_mid"
1772 mt_rand(0, 4095), // 12 bits before the 0100 of (version) 4 for "time_hi_and_version"
1773 bindec(substr_replace(sprintf('%016b', mt_rand(0, 65535)), '01', 6, 2)),
1774 // 8 bits, the last two of which (positions 6 and 7) are 01, for "clk_seq_hi_res"
1775 // (hence, the 2nd hex digit after the 3rd hyphen can only be 1, 5, 9 or d)
1776 // 8 bits for "clk_seq_low"
1777 mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535) // 48 bits for "node"
1782 function test($test_case_name, $use_sessions = false)
1784 ak_test($test_case_name, $use_sessions);
1788 * Use this function for securing includes. This way you can prevent file inclusion attacks
1790 function sanitize_include($include, $mode = 'normal')
1792 $rules = array(
1793 'paranoid' => '/([^A-Z^a-z^0-9^_^-^ ]+)/',
1794 'high' => '/([^A-Z^a-z^0-9^_^-^ ^\/^\\\^:]+)/',
1795 'normal' => '/([^A-Z^a-z^0-9^_^-^ ^\.^\/^\\\]+)/'
1797 $mode = array_key_exists($mode,$rules) ? $mode : 'normal';
1798 return preg_replace($rules[$mode],'',$include);
1802 * Returns a PHP Object from an API resource
1805 function client_api($resource, $options = array())
1807 $default_options = array(
1808 'protocol' => 'xml_rpc',
1809 'build' => true
1811 $options = array_merge($default_options, $options);
1813 require(AK_LIB_DIR.DS.'AkActionWebService'.DS.'AkActionWebServiceClient.php');
1814 $Client =& new AkActionWebServiceClient($options['protocol']);
1815 $Client->init($resource, $options);
1816 return $Client;
1821 * Cross PHP version replacement for html_entity_decode. Emulates PHP5 behaviour on PHP4 on UTF-8 entities
1823 function html_entity_decode($html, $translation_table_or_quote_style = null)
1825 if(AK_PHP5){
1826 return html_entity_decode($html,empty($translation_table_or_quote_style) ? ENT_QUOTES : $translation_table_or_quote_style,'UTF-8');
1828 require_once(AK_LIB_DIR.DS.'AkCharset.php');
1829 $html = preg_replace('~&#x([0-9a-f]+);~ei', 'AkCharset::_CharToUtf8(hexdec("\\1"))', $html);
1830 $html = preg_replace('~&#([0-9]+);~e', 'AkCharset::_CharToUtf8("\\1")', $html);
1831 if(empty($translation_table_or_quote_style)){
1832 $translation_table_or_quote_style = get_html_translation_table(HTML_ENTITIES);
1833 $translation_table_or_quote_style = array_flip($translation_table_or_quote_style);
1835 return strtr($html, $translation_table_or_quote_style);
1839 * Loads the plugins found at app/vendor/plugins
1841 function &loadPlugins()
1843 require_once(AK_LIB_DIR.DS.'AkPlugin.php');
1844 $PluginManager =& new AkPluginLoader();
1845 $PluginManager->loadPlugins();
1846 return $PluginManager;
1851 // Now some static functions that are needed by the whole framework
1853 function translate($string, $args = null, $controller = null)
1855 return Ak::t($string, $args, $controller);
1859 function ak_test($test_case_name, $use_sessions = false)
1861 if(!defined('ALL_TESTS_CALL')){
1862 $use_sessions ? @session_start() : null;
1863 $test = &new $test_case_name();
1864 if (defined('AK_CLI') && AK_CLI || TextReporter::inCli() || (defined('AK_CONSOLE_MODE') && AK_CONSOLE_MODE) || (defined('AK_WEB_REQUEST') && !AK_WEB_REQUEST)) {
1865 $test->run(new TextReporter());
1866 }else{
1867 $test->run(new HtmlReporter());
1872 function ak_compat($function_name)
1874 if(!function_exists($function_name)){
1875 require_once(AK_VENDOR_DIR.DS.'pear'.DS.'PHP'.DS.'Compat'.DS.'Function'.DS.$function_name.'.php');
1879 function ak_generate_mock($name)
1881 static $Mock;
1882 if(empty($Mock)){
1883 $Mock = new Mock();
1885 $Mock->generate($name);
1889 * This function sets a constant and returns it's value. If constant has been already defined it
1890 * will reutrn its original value.
1892 * Returns null in case the constant does not exist
1894 * @param string $name
1895 * @param mixed $value
1897 function ak_define($name, $value = null)
1899 $name = strtoupper($name);
1900 $name = substr($name,0,3) == 'AK_' ? $name : 'AK_'.$name;
1901 return defined($name) ? constant($name) : (is_null($value) ? null : (define($name, $value) ? $value : null));
1905 * PHP4 triggers "Only variable references should be returned by reference" error when
1906 * a method that should return an object reference returns a boolean/array
1908 * The old method was to use a global variables, but it can lead into hard to debug bugs.
1910 * Now you'll need to use the following technique if you whant to build functions that
1911 * can return Object references or TRUE/FALSE.
1913 * $result = false;
1914 * return $result;
1918 * Globals are deprecated. Used ak_false, ak_true and ak_array instead
1920 * @deprecated
1922 $GLOBALS['false'] = false;
1923 $GLOBALS['true'] = true;
1926 AK_PHP5 || function_exists('clone') ? null : eval('function clone($object){return $object;}');
1928 Ak::profile('Ak.php class included'.__FILE__);