ATK Framework PHP Cross Reference Developer Tools

Source: /atk/atktools.inc - 2149 lines - 63551 bytes - Summary - Text - Print

Description: This file is part of the Achievo ATK distribution. Detailed copyright and licensing information can be found in the doc/COPYRIGHT and doc/LICENSE files which should be included in the distribution.

   1  <?php
   2  
   3    /**
   4     * This file is part of the Achievo ATK distribution.
   5     * Detailed copyright and licensing information can be found
   6     * in the doc/COPYRIGHT and doc/LICENSE files which should be
   7     * included in the distribution.
   8     *
   9     * @package atk
  10     *
  11     * This file contains a set of general-purpose utility functions.
  12     * @todo Move all these functions to relevant classes.
  13     * @todo Document all of the functions
  14     *
  15     * @copyright (c)2000-2007 Ibuildings.nl BV
  16     * @copyright (c)2000-2007 Ivo Jansch
  17     * @license http://www.achievo.org/atk/licensing ATK Open Source License
  18     *
  19     * @version $Revision: 6776 $
  20     * $Id: atktools.inc 6776 2010-03-12 14:18:22Z peter $
  21     */
  22  
  23    /**
  24     * Converts applicable characters to html entities so they aren't
  25     * interpreted by the browser.
  26     */
  27    define("DEBUG_HTML", 1);
  28  
  29    /**
  30     * Wraps the text into html bold tags in order to make warnings more
  31     * clearly visible.
  32     */
  33    define("DEBUG_WARNING", 2);
  34  
  35    /**
  36     * Hides the debug unless in level 2.
  37     * This should be used for debug you only really want to see if you
  38     * are developing (like deprecation warnings).
  39     */
  40    define("DEBUG_NOTICE", 4);
  41  
  42    /**
  43     * Error message.
  44     */
  45    define("DEBUG_ERROR", 8);
  46  
  47    /**
  48     * Function atkErrorHandler
  49     * This function catches PHP parse errors etc, and passes
  50     * them to atkerror(), so errors can be mailed and output
  51     * can be regulated.
  52     * This funtion must be registered with set_error_handler("atkErrorHandler");
  53     *
  54     * @param $errtype: One of the PHP errortypes (E_PARSE, E_USER_ERROR, etc)
  55                        (See http://www.php.net/manual/en/function.error-reporting.php)
  56     * @param $errstr: Error text
  57     * @param $errfile: The php file in which the error occured.
  58     * @param $errline: The line in the file on which the error occured.
  59     */
  60    function atkErrorHandler($errtype, $errstr, $errfile, $errline)
  61    {
  62      // probably suppressed error using the @ operator, simply ignore
  63      if (error_reporting() == 0)
  64      {
  65        return;
  66      }
  67  
  68      $errortype = array (
  69        E_ERROR              => "Error",
  70        E_WARNING            => "Warning",
  71        E_PARSE              => "Parsing Error",
  72        E_NOTICE             => "Notice",
  73        E_CORE_ERROR         => "Core Error",
  74        E_CORE_WARNING       => "Core Warning",
  75        E_COMPILE_ERROR      => "Compile Error",
  76        E_COMPILE_WARNING    => "Compile Warning",
  77        E_USER_ERROR         => "User Error",
  78        E_USER_WARNING       => "User Warning",
  79        E_USER_NOTICE        => "User Notice",
  80        E_STRICT             => "Strict Notice"
  81      );
  82  
  83      // E_RECOVERABLE_ERROR is available since 5.2.0
  84      if (defined('E_RECOVERABLE_ERROR'))
  85      {
  86        $errortype[E_RECOVERABLE_ERROR] = "Recoverable Error";
  87      }
  88  
  89      // E_DEPRECATED / E_USER_DEPRECATED are available since 5.3.0
  90      if (defined('E_DEPRECATED'))
  91      {
  92        $errortype[E_DEPRECATED] = "Deprecated";
  93        $errortype[E_USER_DEPRECATED] = "User Deprecated";
  94      }
  95  
  96      // Translate the given errortype into a string
  97      $errortypestring = $errortype[$errtype];
  98  
  99      if ($errtype == E_STRICT)
 100      {
 101          // ignore strict notices for now, there is too much stuff that needs to be fixed
 102          return;
 103      }
 104      else if ($errtype == E_NOTICE)
 105      {
 106        // Just show notices
 107        atkdebug("[$errortypestring] $errstr in $errfile (line $errline)", DEBUG_NOTICE);
 108        return;
 109      }
 110      else if (defined('E_DEPRECATED') && ($errtype & (E_DEPRECATED|E_USER_DEPRECATED)) > 0)
 111      {
 112        // Just show deprecation warnings in the debug log, but don't influence the program flow
 113        atkdebug("[$errortypestring] $errstr in $errfile (line $errline)", DEBUG_NOTICE);
 114        return;
 115      }
 116  
 117      else if (($errtype & (E_WARNING|E_USER_WARNING)) > 0)
 118      {
 119        // This is something we should pay attention to, but we don't need to die.
 120        atkerror("[$errortypestring] $errstr in $errfile (line $errline)");
 121        return;
 122      }
 123      else
 124      {
 125        atkerror("[$errortypestring] $errstr in $errfile (line $errline)");
 126  
 127        // we must die. we can't even output anything anymore..
 128        // we can do something with the info though.
 129        handleError();
 130        atkOutput::getInstance()->outputFlush();
 131        die;
 132      }
 133    }
 134  
 135    /**
 136     * Default ATK exception handler, handles uncaught exceptions and calls atkHalt.
 137     *
 138     * @param Exception $exception uncaught exception
 139     */
 140    function atkExceptionHandler(Exception $exception)
 141    {
 142      atkdebug($exception->getMessage(), DEBUG_ERROR);
 143      atkdebug("Trace:<br/>".nl2br($exception->getTraceAsString()), DEBUG_ERROR);
 144      atkhalt("Uncaught exception: " . $exception->getMessage(), 'critical');
 145    }
 146  
 147    /**
 148     * @deprecated Use atkhalt instead.
 149     */
 150    function halt($msg,$level="warning")
 151    {
 152      return atkhalt($msg, $level);
 153    }
 154  
 155    /**
 156     * Function atkhalt
 157     * Halts on critical errors and also on warnings if specified in the config file.
 158     * @param string $msg   The message to be displayed
 159     * @param string $level The level of the error,
 160     *                      ("critical"|"warning" (default))
 161     * @return bool false if something goes horribly wrong
 162     */
 163    function atkhalt($msg,$level="warning")
 164    {
 165      if ($level == $GLOBALS['config_halt_on_error']||$level == "critical")
 166      {
 167        if($level == "warning")
 168        {
 169          $level_color="#0000ff";
 170        }
 171        else
 172        {
 173          // critical
 174          $level_color="#ff0000";
 175        }
 176        
 177        if (php_sapi_name() == 'cli')
 178        {
 179          $res = atktext($level,"atk").': '.$msg."\n";
 180        }
 181        else
 182        {
 183          $res  ="<html>";
 184          $res .='<body bgcolor="#ffffff" color="#000000">';
 185          $res .="<font color=\"$level_color\"><b>".atktext($level,"atk")."</b></font>: $msg.<br />\n";
 186        }
 187        
 188        atkOutput::getInstance()->output($res);
 189        atkOutput::getInstance()->outputFlush();
 190        exit("Halted...\n");
 191      }
 192      else
 193      {
 194        atkerror("$msg");
 195      }
 196      return false;
 197    }
 198  
 199    /**
 200     * @deprecated Use atkDebugger::getMicroTime()
 201     * @return int the microtime
 202     */
 203    function getmicrotime()
 204    {
 205      atkimport("atk.utils.atkdebugger");
 206      return atkDebugger::getMicroTime();
 207    }
 208  
 209    /**
 210     * @deprecated Use atkDebugger::elapsed();
 211     *
 212     * @return string elapsed time in microseconds
 213     */
 214    function elapsed()
 215    {
 216      atkimport("atk.utils.atkdebugger");
 217      return atkDebugger::elapsed();
 218    }
 219  
 220    /**
 221     * Function atkdebug
 222     *
 223     * Adds debug text to the debug log
 224     * @param String $txt The text that will be added to the log
 225     * @param Integer $flags An optional combination of DEBUG_ flags
 226     */
 227    function atkdebug($txt, $flags = 0)
 228    {
 229      global $g_debug_msg;
 230      $level = atkconfig("debug");
 231      if ($level>=0)
 232      {
 233        if (hasFlag($flags, DEBUG_HTML))
 234          $txt = atk_htmlentities($txt);
 235        if (hasFlag($flags, DEBUG_WARNING))
 236          $txt = "<b>" . $txt . "</b>";
 237  
 238        $line = atkGetTimingInfo().$txt;
 239        atkWriteLog($line);
 240  
 241        if (hasFlag($flags, DEBUG_ERROR))
 242        {
 243          $line = '<span class="atkDebugError">'.$line.'</span>';
 244        }
 245  
 246        if ($level>2)
 247        {
 248          atkimport("atk.utils.atkdebugger");
 249          if (!atkDebugger::addStatement($line))
 250          {
 251            $g_debug_msg[]=$line;
 252          }
 253        }
 254        else if (!hasFlag($flags,DEBUG_NOTICE))
 255        {
 256          $g_debug_msg[]=$line;
 257        }
 258      }
 259      else if ($level>-1) // at 0 we still collect the info so we
 260                          // have it in error reports. At -1, we don't collect
 261      {
 262        $g_debug_msg[]=$txt;
 263      }
 264    }
 265  
 266    /**
 267     * Send a notice to the debug log.
 268     * A notice doesn't get show unless your debug level is 3 or higher.
 269     *
 270     * @param String $txt The text that will be added to the log
 271     */
 272    function atknotice($txt)
 273    {
 274      atkdebug($txt,DEBUG_NOTICE);
 275    }
 276  
 277    /**
 278     * Send a warning to the debug log.
 279     * A warning gets shown more prominently than a normal debug line.
 280     * However it does not trigger a mailreport
 281     * or anything else that an atkerror triggers.
 282     *
 283     * @param unknown_type $txt
 284     * @return unknown
 285     */
 286    function atkwarning($txt)
 287    {
 288      atkdebug($txt,DEBUG_WARNING);
 289    }
 290  
 291    function atkGetTimingInfo()
 292    {
 293      return "[".elapsed().(atkconfig('debug')>0 && function_exists("memory_get_usage")?" / ".sprintf("%02.02f", (memory_get_usage()/1024/1024))."MB":"")."] ";
 294    }
 295  
 296    /**
 297     * Like atkdebug, this displays a message at the bottom of the screen.
 298     * The difference is, that this is also displayed when debugging is turned
 299     * off.
 300     *
 301     * If errorreporting by email is turned on, the errormessages are also handled (from atkOutput)
 302     *
 303     * @param string $txt the text to display
 304     */
 305    function atkerror($txt)
 306    {
 307      global $g_error_msg;
 308      $g_error_msg[]="[".elapsed()."] ".$txt;
 309  
 310      atkdebug($txt, DEBUG_ERROR);
 311  
 312      if (function_exists('debug_backtrace'))
 313        atkdebug("Trace:".atk_get_trace(), DEBUG_ERROR);
 314        
 315      if (atkconfig('throw_exception_on_error'))
 316      {
 317        throw new Exception($txt);
 318      }
 319    }
 320  
 321    /**
 322     * Returns a trace-route from all functions where-through the code has been executed
 323     *
 324     * @param string $format (html|plaintext)
 325     * @return string Backtrace in html or plaintext format
 326     */
 327    function atkGetTrace($format="html")
 328    {
 329      // Return if the debug_backtrace function doesn't exist
 330      if(!function_exists("debug_backtrace"))
 331        return "Incorrect php-version for atk_get_trace()";
 332  
 333      // Get the debug backtrace
 334      $traceArr = debug_backtrace();
 335  
 336      // Remove the call of atk_get_trace
 337      array_shift($traceArr);
 338  
 339      // Start with an empty result;
 340      $ret = "";
 341  
 342      $theSpacer = "";
 343  
 344      // Loop through all items found in the backtrace
 345      for($i=0, $_i = count($traceArr); $i < $_i; $i++)
 346      //for($i=count($traceArr)-1; $i >= 0; $i--)
 347      {
 348        // Skip this item in the backtrace if empty
 349        if (empty($traceArr[$i]))
 350          continue;
 351  
 352        // Don't display an atkerror statement itself.
 353        if ($traceArr[$i]["function"]=="atkerror")
 354          continue;
 355  
 356        // Read the source location
 357        if (isset($traceArr[$i]["file"]))
 358          $location = $traceArr[$i]["file"] . (isset($traceArr[$i]["line"]) ? sprintf(", line %d", $traceArr[$i]["line"]) : "[Unknown line]");
 359        else
 360          $location = "[PHP KERNEL]";
 361  
 362        // Read the statement
 363        if (isset($traceArr[$i]["class"]))
 364        {
 365          $statement = $traceArr[$i]["class"];
 366          if (isset($traceArr[$i]["type"]))
 367            $statement .= $traceArr[$i]["type"];
 368        }
 369        else
 370        {
 371          $statement = "";
 372        }
 373        $statement .= $traceArr[$i]["function"];
 374  
 375        // Initialize the functionParamArr array
 376        $functionParamArr = array();
 377  
 378        // Parse any arguments into the array
 379        if(isset($traceArr[$i]["args"]))
 380        {
 381          foreach($traceArr[$i]["args"] as $val)
 382          {
 383            if(is_array($val))
 384            {
 385              $valArr = array();
 386              foreach($val as $name=>$value)
 387              {
 388                if(is_numeric($name))
 389                  $valArr[] = $name;
 390                else
 391                {
 392                  if (is_object($value))
 393                    $valArr[] = sprintf("%s=Object(%s)", $name, get_class($value));
 394                  else
 395                    $valArr[] = $name."=".$value;
 396                }
 397              }
 398              $stringval = "array(".implode(", ", $valArr).")";
 399            }
 400            else if (is_null($val)) $stringval = 'null';
 401            else if (is_object($val)) $stringval = sprintf("Object(%s)", get_class($val));
 402            else if (is_bool($val)) $stringval = $val ? 'true' : 'false';
 403            else
 404            {
 405              if(strlen($val.$theSpacer) > 103)
 406                $stringval = '"'.substr($val, 0, 100-strlen($theSpacer)).'"...';
 407              else
 408                $stringval = '"'.$val.'"';
 409            }
 410            $functionParamArr[] = $theSpacer."  ".$stringval;
 411          }
 412        }
 413        $functionParams = implode(",\n", $functionParamArr);
 414  
 415        $ret .= $theSpacer."@".$location."\n";
 416        $ret .= $theSpacer.$statement;
 417        $ret .= (strlen($functionParams)?"\n".$theSpacer."(\n".$functionParams."\n".$theSpacer.")":"()")."\n";
 418  
 419        // Add indentation
 420        $theSpacer .= "  ";
 421      }
 422  
 423      // If html format should be used, replace the html special chars with html entities and put the backtrace within preformat tags.
 424      if ($format=="html")
 425        $ret = "<pre>" . htmlspecialchars($ret) . "</pre>";
 426  
 427      // Return the generated trace
 428      return $ret;
 429    }
 430  
 431    /**
 432     * @deprecated Use atkGetTrace instead
 433     */
 434    function atk_get_trace($format="html")
 435    {
 436      return atkGetTrace($format);
 437    }
 438  
 439    /**
 440     * Writes info to a given file.
 441     * Useful for writing to any log files.
 442     * @param String $text text to write to the logfile
 443     * @param String $file the file name
 444     */
 445    function atkWriteToFile($text, $file="")
 446    {
 447      $fp = @fopen($file, "a");
 448      if ($fp)
 449      {
 450        fwrite($fp, $text."\n");
 451        fclose($fp);
 452      }
 453    }
 454  
 455    /**
 456     * Writes info to the optional debug logfile.
 457     * Please notice this feature will heavily decrease the performance
 458     * and should therefore only be used for debugging and development
 459     * purposes.
 460     * @param String $text text to write to the logfile
 461     */
 462    function atkWriteLog($text)
 463    {
 464      if (atkconfig("debug") > 0 && atkconfig("debuglog"))
 465      {
 466        atkWriteToFile($text, atkconfig("debuglog"));
 467      }
 468    }
 469  
 470  
 471    /**
 472     * Replaces the [vars] with the values from the language files
 473     * Please note that it is important, for performance reasons,
 474     * that you pass along the module where the language files can be found
 475     * @param mixed $string           string or array of strings containing the name(s) of the string to return
 476     *                                when an array of strings is passed, the second will be the fallback if
 477     *                                the first one isn't found, and so forth
 478     * @param String $module          module in which the language file should be looked for,
 479     *                                defaults to core module with fallback to ATK
 480     * @param String $node            the node to which the string belongs
 481     * @param String $lng             ISO 639-1 language code, defaults to config variable
 482     * @param String $firstfallback   the first module to check as part of the fallback
 483     * @param boolean $nodefaulttext  if true, then it doesn't return a default text
 484     *                                when it can't find a translation
 485     * @return String the string from the languagefile
 486     * @deprecated Use atktext instead
 487     */
 488    function text($string, $node="", $module="", $lng="", $firstfallback="", $nodefaulttext=false)
 489    {
 490      atkdebug("Call to deprecated text() function",DEBUG_WARNING);
 491      atkimport("atk.atklanguage");
 492      return atkLanguage::text($string, $module, $node, $lng, $firstfallback, $nodefaulttext);
 493    }
 494  
 495    /**
 496     * Replaces the [vars] with the values from the language files
 497     * Please note that it is important, for performance reasons,
 498     * that you pass along the module where the language files can be found
 499     * @param mixed $string           string or array of strings containing the name(s) of the string to return
 500     *                                when an array of strings is passed, the second will be the fallback if
 501     *                                the first one isn't found, and so forth
 502     * @param String $module          module in which the language file should be looked for,
 503     *                                defaults to core module with fallback to ATK
 504     * @param String $node            the node to which the string belongs
 505     * @param String $lng             ISO 639-1 language code, defaults to config variable
 506     * @param String $firstfallback   the first module to check as part of the fallback
 507     * @param boolean $nodefaulttext  if true, then it doesn't return a default text
 508     *                                when it can't find a translation
 509     * @param boolean $modulefallback Wether or not to use all the modules of the application in the fallback,
 510     *                                when looking for strings
 511     * @return String the string from the languagefile
 512     */
 513    function atktext($string, $module="",$node="", $lng="", $firstfallback="", $nodefaulttext=false, $modulefallback=false)
 514    {
 515      atkimport("atk.atklanguage");
 516      return atkLanguage::text($string, $module, $node, $lng, $firstfallback, $nodefaulttext,$modulefallback);
 517    }
 518  
 519    /**
 520     * @deprecated Use atkSession::newLevel() instead
 521     */
 522    function session_level($sessionstatus=SESSION_DEFAULT, $levelskip=null)
 523    {
 524      return atkSessionManager::newLevel($sessionstatus, $levelskip);
 525    }
 526  
 527    /**
 528     * @deprecated Use atkSessionManager::formState instead.
 529     */
 530    function session_form($sessionstatus=SESSION_DEFAULT, $returnbehaviour=null, $fieldprefix='')
 531    {
 532      return atkSessionManager::formState($sessionstatus, $returnbehaviour, $fieldprefix);
 533    }
 534  
 535    /**
 536     * @deprecated Use atkSessionManager::sessionVars() instead.
 537     */
 538    function session_vars($sessionstatus=SESSION_DEFAULT, $levelskip=null, $url="")
 539    {
 540      return atkSessionManager::sessionVars($sessionstatus, $levelskip, $url);
 541    }
 542  
 543    /**
 544     * @deprecated use atkSessionManager::sessionUrl() instead.
 545     */
 546    function session_url($url, $sessionstatus=SESSION_DEFAULT, $levelskip=null)
 547    {
 548      return atkSessionManager::sessionUrl($url, $sessionstatus, $levelskip);
 549  
 550    }
 551  
 552    /**
 553     * @deprecated use atkHref or atkSessionManager::href instead.
 554     */
 555    function href($url,$name="",$sessionstatus=SESSION_DEFAULT, $saveform=false, $extraprops="")
 556    {
 557      return atkSessionManager::href($url, $name, $sessionstatus, $saveform, $extraprops);
 558    }
 559  
 560    /**
 561     * Convenience wrapper for atkSessionManager::href().
 562     * @see atkSessionManager::href
 563     */
 564    function atkHref($url, $name="", $sessionstatus=SESSION_DEFAULT, $saveform=false, $extraprops="")
 565    {
 566      return atkSessionManager::href($url, $name, $sessionstatus, $saveform, $extraprops);
 567    }
 568  
 569  
 570    /**
 571     * Same as array_merge from php, but without duplicates..
 572     * Supports unlimited number of arrays as arguments.
 573     *
 574     * @param Array $array1 the first array
 575     * @param Array $array2 the second array
 576     * @return Array The result of the merge between $array1 and $array2
 577     */
 578    function atk_array_merge($array1, $array2)
 579    {
 580      $res = Array();
 581  
 582      $arrays = func_get_args();
 583      for ($i = 0, $_i = count($arrays); $i < $_i; $i++)
 584      {
 585        for ($j = 0, $_j = count($arrays[$i]); $j < $_j; $j++)
 586        {
 587          if (!in_array($arrays[$i][$j], $res))
 588          {
 589            $res[] = $arrays[$i][$j];
 590          }
 591        }
 592      }
 593  
 594      return $res;
 595    }
 596  
 597    /**
 598     * Same as array_merge_recursive from PHP but without duplicates.
 599     * Supports unlimited number of arrays as arguments.
 600     *
 601     * @param array $array1 first array
 602     * @param array $array2 second array
 603     *
 604     * @return array merged arrays
 605     */
 606    function atk_array_merge_recursive($array1, $array2)
 607    {
 608      $arrays = func_get_args();
 609  
 610      $result = array();
 611  
 612      foreach ($arrays as $array)
 613      {
 614        foreach ($array as $key => $value)
 615        {
 616          if (isset($result[$key]) && is_array($result[$key]) && is_array($value))
 617          {
 618            $result[$key] = atk_array_merge_recursive($result[$key], $value);
 619          }
 620          else
 621          {
 622            $result[$key] = $value;
 623          }
 624        }
 625      }
 626  
 627      return $result;
 628    }
 629  
 630    /**
 631     * Same as array_merge from php, but this function preserves key=>index
 632     * association in case of numerical indexes. Supports unlimited number
 633     * of arrays as arguments.
 634     *
 635     * @param Array $array unlimited number of arrays
 636     * @return Array The result of the merge between the given arrays
 637     */
 638    function atk_array_merge_keys()
 639    {
 640      $args = func_get_args();
 641      $result = array();
 642      foreach($args as $array)
 643      {
 644        foreach($array as $key=>$value)
 645        {
 646          $result[$key] = $value;
 647        }
 648      }
 649      return $result;
 650    }
 651  
 652    /**
 653     * Since php triggers an error if you perform an in_array on an
 654     * uninitialised array, we provide a small wrapper that performs
 655     * an is_array on the haystack first, just to make sure the user
 656     * doesn't get an error message.
 657     *
 658     * @param mixed   $needle   The value to search for.
 659     * @param Array   $haystack The array to search.
 660     * @param boolean $strict   If true, type must match.
 661     * @return bool wether or not the value is in the array
 662     */
 663    function atk_in_array($needle, $haystack, $strict=false)
 664    {
 665      return (is_array($haystack)&&in_array($needle, $haystack, $strict));
 666    }
 667  
 668    /**
 669     * Function dataSetContains
 670     *
 671     * Checks if a value is in a Array
 672     * @param array $set  the array
 673     * @param var $key    the key in the array as in $array[$key]
 674     * @param var $value  the value we are looking for
 675     * @return bool wether or not the value is in the array
 676     */
 677    function dataSetContains($set, $key, $value)
 678    {
 679      for ($i=0;$i<count($set);$i++)
 680      {
 681        if ($set[$i][$key]==$value) return true;
 682      }
 683      return false;
 684    }
 685  
 686    /**
 687     * Strips ' or  " from the begin and end of a string (only if they are
 688     * on both sides, e.g. foo' remains foo' but 'bar' becomes bar.
 689     * @param string $string the string we need to strip
 690     * @return string the stripped string
 691     */
 692    function stripQuotes($string)
 693    {
 694      $temp = trim($string);
 695      if (substr($temp,0,1)=="'" && substr($temp,-1)=="'") return substr($temp,1,-1);
 696      if (substr($temp,0,1)=='"' && substr($temp,-1)=='"') return substr($temp,1,-1);
 697      return $string;
 698    }
 699  
 700    /**
 701     * Translates a string like id='3' into Array("id"=>3)
 702     * @param string $pair the string which is to be decoded
 703     * @return array the decoded array
 704     */
 705    function decodeKeyValuePair($pair)
 706    {
 707        $operators = array("==", "!=", "<>", ">=", "<=", "=", "<", ">");
 708  
 709        static $s_regex = null;
 710        if ($s_regex === null)
 711        {
 712          $s_regex = '/'.implode('|', array_map('preg_quote', $operators)).'/';
 713        }
 714  
 715      list($key, $value) = preg_split($s_regex, $pair);
 716  
 717      return array($key => stripQuotes($value));
 718    }
 719  
 720    /**
 721     * Translates a string like id='3 AND name='joe'' into Array("id"=>3,"name"=>"joe")
 722     * @todo we should also support <=>, >=, >, <=, <, <>
 723     * @param string $set the string to decode
 724     * @return array the decoded array
 725     */
 726    function decodeKeyValueSet($set)
 727    {
 728      $result = array();
 729      $items = explode(" AND ",$set);
 730      for ($i=0;$i<count($items);$i++)
 731      {
 732        if (strstr($items[$i], '!=') !== false)
 733        {
 734          list($key,$value) = explode("!=",$items[$i]);
 735          $result[trim($key)] = stripQuotes($value);
 736        }
 737        elseif (strstr($items[$i], '=') !== false)
 738        {
 739          list($key,$value) = explode("=",$items[$i]);
 740          $result[trim($key)] = stripQuotes($value);
 741        }
 742        elseif (stristr($items[$i], 'IS NULL') !== false)
 743        {
 744          list($key) = preg_split('/IS NULL/i', $items[$i]);
 745          $result[trim($key)] = NULL;
 746        }
 747      }
 748      return $result;
 749    }
 750  
 751  
 752    /**
 753     * Translates Array("id"=>3,"name"=>"joe") into a string like id='3 AND name='joe''
 754     * @param array $set the array to be encoded
 755     * @return string the encoded string
 756     */
 757    function encodeKeyValueSet($set)
 758    {
 759      reset($set);
 760      $items = Array();
 761      while (list($key, $value) = each($set))
 762      {
 763        $items[] = $key."=".$value;
 764      }
 765      return implode(" AND ",$items);
 766    }
 767  
 768    /**
 769     * Same as strip_slashes from php, but if the passed value is an array,
 770     * all elements of the array are stripped. Recursive function.
 771     * @param var &$var the value/array to strip the slashes of
 772     */
 773    function atk_stripslashes(&$var)
 774    {
 775      if (is_array($var))
 776      {
 777        foreach (array_keys($var) as $key)
 778        {
 779          atk_stripslashes($var[$key]);
 780        }
 781      }
 782      else
 783      {
 784        $var = stripslashes($var);
 785      }
 786    }
 787  
 788  
 789    /**
 790     * Performs stripslashes on all vars and translates:
 791     *                 something_AMDAE_other[] into something[][other]
 792     *                 something_AE_other into something[other]
 793     *                 (and a_AE_b_AE_c into a[b][c] and so on...
 794     * @param array &$vars the array to be stripped and translated
 795     */
 796    function atkDataDecode(&$vars)
 797    {
 798      $magicQuotes = get_magic_quotes_gpc();
 799  
 800      foreach (array_keys($vars) as $varname)
 801      {
 802        $value = &$vars[$varname];
 803        // We must strip all slashes from the input, since php puts slashes
 804        // in front of quotes that are passed by the url. (magic_quotes_gpc)
 805        if ($value !== NULL && $magicQuotes)
 806          atk_stripslashes($value);
 807  
 808        AE_decode($vars, $varname);
 809  
 810        if (strpos(strtoupper($varname),'_AMDAE_')>0) // Now I *know* that strpos could return 0 if _AMDAE_ *is* found
 811                                      // at the beginning of the string.. but since that's not a valid
 812                                      // encoded var, we do nothing with it.
 813        {
 814          // This string is encoded.
 815          list($dimension1,$dimension2) = explode("_AMDAE_",strtoupper($varname));
 816          if (is_array($value))
 817          {
 818            // Multidimensional thing
 819            for ($i=0;$i<count($value);$i++)
 820            {
 821              $vars[strtolower($dimension1)][$i][strtolower($dimension2)] = $value[$i];
 822            }
 823          }
 824          else
 825          {
 826            $vars[strtolower($dimension1)][strtolower($dimension2)] = $value;
 827          }
 828        }
 829      }
 830    }
 831  
 832    /**
 833     * Weird function. $dest is an associative array, that may contain
 834     * stuff like $dest["a_AE_c_AE_b"] = 3.
 835     * Now if you run this function like this:
 836     *  AE_decode($dest, "a_AE_c_AE_b");
 837     * then $dest will contain a decoded array:
 838     *  echo $dest["a"]["b"]["c"]; <- this will display 3
 839     * @param array &$dest  the array to put the decoded var in
 840     * @param string $var   the var to decode
 841     */
 842    function AE_decode(&$dest, $var)
 843    {
 844      $items = explode("_AE_", $var);
 845      if (count($items) <= 1) return;
 846  
 847      $current = &$dest;
 848      foreach ($items as $key)
 849      {
 850        $current = &$current[$key];
 851      }
 852  
 853      if (is_array($dest[$var]))
 854      {
 855        $current = atk_array_merge_recursive((array)$current, $dest[$var]);
 856      }
 857      else
 858      {
 859        $current = $dest[$var];
 860      }
 861  
 862      unset($dest[$var]);
 863    }
 864  
 865    /**
 866     * Get the [ ] Fields out of a String
 867     * @deprecated please use the atkStringParser class
 868     */
 869    function stringfields($string)
 870    {
 871      atkdebug("Warning: deprecated use of stringfields(). Use atkStringParser class instead");
 872      $tmp = "";
 873      $adding = false;
 874      $fields = array();
 875      for ($i=0;$i<strlen($string);$i++)
 876      {
 877        if ($string[$i]=="]")
 878        {
 879          $adding = false;
 880          $fields[] = $tmp;
 881          $tmp="";
 882        }
 883        else if ($string[$i]=="[")
 884        {
 885          $adding = true;
 886        }
 887        else
 888        {
 889          if ($adding) $tmp.=$string[$i];
 890        }
 891      }
 892  
 893      return $fields;
 894    }
 895  
 896    /**
 897     * Parse strings
 898     * @deprecated please use the atkStringParser class
 899     */
 900    function stringparse($string, $data,$encode=false)
 901    {
 902      atkdebug("Warning: deprecated use of stringparse(). Use atkStringParser class instead");
 903      $fields = stringfields($string);
 904      for ($i=0;$i<count($fields);$i++)
 905      {
 906        $elements = explode(".",$fields[$i]);
 907        $databin = $data;
 908        for($j=0;$j<count($elements);$j++)
 909        {
 910          if (array_key_exists($elements[$j],$databin))
 911          {
 912            $value = $databin[$elements[$j]];
 913            $databin = $databin[$elements[$j]];
 914          }
 915        }
 916        if ($encode)
 917        {
 918          $string = str_replace("[".$fields[$i]."]",rawurlencode($value),$string);
 919        }
 920        else
 921        {
 922          $string = str_replace("[".$fields[$i]."]",$value,$string);
 923        }
 924      }
 925      return $string;
 926    }
 927  
 928    /**
 929     * Safe urlencode function. Note, you can reencode already encoded strings, but
 930     * not more than 9 times!
 931     * If you encode a string more than 9 times, you won't be able to decode it
 932     * anymore
 933     *
 934     * An atkurlencoded string is normaly prefixed with '__', so atkurldecode can
 935     * determine whether the string was encoded or not. Sometimes however, if you
 936     * need to reencode part of a string (used in recordlist), you don't want the
 937     * prefix. Pass false as second parameter, and you won't get a prefix. (Note
 938     * that you can't atkurldecode that string anymore, so only use this on
 939     * substrings of already encoded strings)
 940     *
 941     * @todo Fix a problem where a string containing "_9" will be altered after encoding + decoding it.
 942     *
 943     * @param string $string  the url to encode
 944     * @param bool $pref      wether or not to use a prefix, default true
 945     * @return string the encoded url
 946     */
 947    function atkurlencode($string, $pref=true)
 948    {
 949      $string = rawurlencode($string);
 950      for ($i=8;$i>=1;$i--)
 951      {
 952        $string = str_replace("_".$i,"_".($i+1),$string);
 953      }
 954      return ($pref?"__":"").str_replace("%","_1",$string);
 955    }
 956  
 957    function atkurldecode($string)
 958    {
 959      if (substr($string,0,2)!="__") return $string;
 960      else
 961      {
 962        $string = str_replace("_1","%",substr($string,2));
 963        for ($i=1;$i<=8;$i++)
 964        {
 965          $string = str_replace("_".($i+1),"_".$i,$string);
 966        }
 967        return rawurldecode($string);
 968      }
 969    }
 970  
 971    /**
 972     * Wrap lines, add a newline after 100 characters
 973     * @param string $line the line to add new lines in
 974     * @return string the line with newlines
 975     */
 976    function _wordwrap($line)
 977    {
 978      return wordwrap($line,100,"\n",1);
 979    }
 980  
 981    /**
 982     * Send a detailed error report to the maintainer.
 983     *
 984     */
 985    function mailreport()
 986    {
 987      global $g_error_msg, $g_debug_msg;
 988      include_once(atkconfig('atkroot'). 'atk/errors/class.atkerrorhandlerbase.inc');
 989      $errorHandlerObject = atkErrorHandlerBase::get('mail', array('mailto'=>atkconfig('mailreport')));
 990      $errorHandlerObject->handle($g_error_msg, $g_debug_msg);
 991    }
 992  
 993    /**
 994     * Handle errors that occurred in ATK, available handlers from /atk/errors/ can be added to
 995     * the error_handlers config.
 996     *
 997     */
 998    function handleError()
 999    {
1000      global $g_error_msg, $g_debug_msg;
1001      include_once(atkconfig('atkroot'). 'atk/errors/class.atkerrorhandlerbase.inc');
1002      $errorHandlers = atkconfig('error_handlers', array('mail'=>array('mailto' => atkconfig('mailreport'))));
1003      foreach ($errorHandlers as $key => $value)
1004      {
1005        if (is_numeric($key))
1006          $key = $value;
1007        $errorHandlerObject = atkErrorHandlerBase::get($key, $value);
1008        $errorHandlerObject->handle($g_error_msg, $g_debug_msg);
1009      }
1010    }
1011  
1012  
1013    /**
1014     * Wrapper for escapeSQL function
1015     * @param String $string The string to escape.
1016     * @param boolean $wildcard Set to true to convert wildcard chars ('%').
1017     *                          False (default) will leave them unescaped.
1018     * @return String A SQL compatible version of the input string.
1019     */
1020    function escapeSQL($string, $wildcard=false)
1021    {
1022      $db = &atkGetDb();
1023      return $db->escapeSQL($string, $wildcard);
1024    }
1025  
1026    /**
1027     * Return the atk version number.
1028     * @return string the version number of ATK
1029     */
1030    function atkversion()
1031    {
1032      include(atkconfig("atkroot")."atk/version.inc");
1033      return $atk_version;
1034    }
1035  
1036    /**
1037     * Convenience wrapper for atkDb::getInstance()
1038     * @param String $conn The name of the connection to retrieve
1039     * @return atkDb Database connection instance
1040     */
1041    function &atkGetDb($conn='default', $reset=false, $mode="r")
1042    {
1043      atkimport("atk.db.atkdb");
1044      $db = &atkDb::getInstance($conn, $reset, $mode);
1045      return $db;
1046    }
1047  
1048    /**
1049     * Returns a url to open a popup window
1050     * @param string $target  the target of the popup
1051     * @param string $params  extra params to pass along
1052     * @param string $winName the name of the window
1053     * @param int $width      the width of the popup
1054     * @param int $height     the height of the popup
1055     * @param string $scroll  allow scrolling? (no (default)|yes)
1056     * @param string $resize  allow resizing? (no (default)|yes)
1057     * return string the url for the popup window
1058     */
1059    function atkPopup($target,$params,$winName,$width,$height,$scroll='no',$resize='no')
1060    {
1061      $url = session_url("include.php?file=".$target."&".$params, SESSION_NESTED);
1062      $popupurl ="javascript:NewWindow('".$url."','".$winName."',".$height.",".$width.",'".$scroll."','".$resize."')";
1063      return $popupurl;
1064    }
1065  
1066    /**
1067     * Adds new element to error array en $record. When
1068     * $msg is empty the multilange error string is used.
1069     * @param array &$rec var in which to add element to error array
1070     * @param var$attrib attributename or an array with attribute names
1071     * @param string $err multilanguage error string
1072     * @param string $msg optinal error string
1073     */
1074    function triggerError(&$rec, $attrib, $err, $msg="", $tab="", $label='', $module='atk')
1075    {
1076      if($msg=="")  $msg = atktext($err, $module);
1077      $rec['atkerror'][] = array( "attrib_name"=> $attrib, "err" => $err, "msg" => $msg, "tab" => $tab, "label" => $label);
1078    }
1079  
1080    /**
1081     * Adds new element to the record error array. When no message
1082     * is given the multi-language error string is used.
1083     *
1084     * @param array              $record  record
1085     * @param atkAttribute|array $attrib attribute or array of attributes
1086     * @param string             $error multi-language error string
1087     * @param string             $message error message (optional)
1088     */
1089    function atkTriggerError(&$record, &$attrib, $error, $message='')
1090    {
1091      if (is_array($attrib))
1092      {
1093        $attribName = array();
1094        $label = array();
1095  
1096        for ($i = 0; $i < count($attrib); $i++)
1097        {
1098          $attribName[$i] = $attrib[$i]->fieldName();
1099          $label[$i] = $attrib[$i]->label($record);
1100        }
1101  
1102        $tab = $attrib[0]->m_tabs[0];
1103        $message = $attrib[0]->text($error);
1104      }
1105      else
1106      {
1107        $attribName = $attrib->fieldName();
1108        $label = $attrib->label($record);
1109        $tab = $attrib->m_tabs[0];
1110        $message = $attrib->text($error);
1111      }
1112  
1113      triggerError($record, $attribName, $error, $message, $tab, $label);
1114    }
1115  
1116    /**
1117     * Adds var_export function to PHP versions older then PHP4.2
1118     * for documentation about var_export() see
1119     * http://www.php.net/manual/en/function.var-export.php
1120     */
1121    if(!function_exists("var_export"))
1122    {
1123      function var_export($a)
1124      {
1125        $result = "";
1126        switch (gettype($a))
1127        {
1128           case "array":
1129             reset($a);
1130             $result = "array(";
1131             while (list($k, $v) = each($a))
1132             {
1133               $result .= "$k => ".var_export($v).", ";
1134             }
1135             $result .= ")";
1136             break;
1137           case "string":
1138             $result = "'$a'";
1139             break;
1140           case "boolean":
1141             $result = ($a) ? "true" : "false";
1142             break;
1143           default:
1144             $result = $a;
1145             break;
1146        }
1147        return $result;
1148      }
1149    }
1150  
1151    /**
1152     * Does a var dump of an array. Makes use of atkdebug for displaying the values.
1153     *
1154     * @param $a data to be displayed
1155     * @param $d name of the data that's being displayed.
1156     */
1157    function atk_var_dump($a, $d="")
1158    {
1159      ob_start();
1160      var_dump($a);
1161      $data = ob_get_contents();
1162      atkdebug("vardump: ".($d!=""?$d." = ":"")."<pre>".$data."</pre>");
1163      ob_end_clean();
1164    }
1165  
1166    /**
1167     * This function writes data to the browser for download.
1168     * $data is the data to download.
1169     * $filename is the name the file will get when the user downloads it.
1170     * $compression can be "zip", "gzip" or "bzip", which causes the data
1171     *              to be compressed before transmission.
1172     */
1173    function exportData($data, $filename, $compression="")
1174    {
1175      $browser = getBrowserInfo();
1176      if (preg_match("/ie/i", $browser["browser"]))
1177      {
1178        $mime = "application/octetstream";
1179        $disp = 'inline';
1180      }
1181      else if (preg_match("/opera/i", $browser["browser"]))
1182      {
1183        $mime = "application/octetstream";
1184        $disp = 'attachment';
1185      }
1186      else
1187      {
1188        $mime = "application/octet-stream";
1189        $disp = 'attachment';
1190      }
1191  
1192      if($compression=="bzip")
1193      {
1194        $mime='application/x-bzip';
1195       $filename.= ".bz2";
1196      }
1197      else if($compression=="gzip")
1198      {
1199        $mime='application/x-gzip';
1200        $filename.= ".gz";
1201      }
1202      else if($compression=="zip")
1203      {
1204        $mime='application/x-zip';
1205        $filename.= ".zip";
1206      }
1207  
1208      header('Content-Type: '. $mime);
1209      header('Content-Disposition:  '.$disp.'; filename="'.$filename.'"');
1210      if(preg_match("/ie/i", $browser["browser"])) header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
1211      header('Pragma: no-cache');
1212      header('Expires: 0');
1213  
1214      // 1. as a bzipped file
1215      if($compression=="bzip")
1216      {
1217        if (@function_exists('bzcompress'))
1218        {
1219          echo bzcompress($data);
1220        }
1221      }
1222      // 2. as a gzipped file
1223      else if ($compression == 'gzip')
1224      {
1225        if (@function_exists('gzencode'))
1226        {
1227          echo gzencode($data);
1228        }
1229      }
1230      else if ($compression == 'zip')
1231      {
1232        if (@function_exists('gzcompress'))
1233        {
1234          echo gzcompress($data);
1235        }
1236      }
1237      // 3. on screen
1238      else
1239      {
1240        echo $data;
1241      }
1242      exit;
1243    }
1244  
1245    /**
1246     * This function writes a binary file to the browser for download.
1247     * @param string $file     the local filename (the file you want to open
1248     *                         on the serverside)
1249     * @param string $filename the name the file will get when the user downloads it.
1250     * @param string $mimetype the mimetype of the file
1251     * @return bool wether or not the export worked
1252     */
1253    function exportFile($file, $filename,$mimetype="", $detectmime=true)
1254    {
1255      include_once(atkconfig("atkroot")."atk/atkbrowsertools.inc");
1256      $browser = getBrowserInfo();
1257      if (preg_match("/ie/i", $browser["browser"]))
1258      {
1259        $mime = "application/octetstream";
1260        $disp = 'attachment';
1261      }
1262      else if (preg_match("/opera/i",$browser["browser"]))
1263      {
1264        $mime = "application/octetstream";
1265        $disp = 'inline';
1266      }
1267      else
1268      {
1269        $mime = "application/octet-stream";
1270        $disp = 'attachment';
1271      }
1272      if($mimetype!="") $mime=$mimetype;
1273      else if ($mimetype=="" && $detectmime && function_exists('mime_content_type'))
1274        $mime = mime_content_type($file);
1275  
1276      $fp = @fopen($file,"rb");
1277      if ($fp!=NULL)
1278      {
1279        header('Content-Type: '. $mime);
1280        header("Content-Length: ".filesize($file));
1281        header('Content-Disposition:  '.$disp.'; filename="'.$filename.'"');
1282        if(preg_match("/ie/i", $browser["browser"])) header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
1283        if (($_SERVER["SERVER_PORT"] == "443" || $_SERVER['HTTP_X_FORWARDED_PROTO' ]==='https') && preg_match("/msie/i", $_SERVER["HTTP_USER_AGENT"]))
1284        {
1285          header('Pragma: public');
1286        }
1287        else
1288        {
1289          header('Pragma: no-cache');
1290        }
1291        header('Expires: 0');
1292  
1293        header("Content-Description: File Transfer");
1294        header("Content-Transfer-Encoding: binary");
1295  
1296        fpassthru($fp);
1297        return true;
1298      }
1299      return false;
1300    }
1301  
1302    /**
1303     * Includes the file containing the specified attribute
1304     *
1305     * @param string $attribute The attribute to include in the format "module.attribute". ATK will
1306     *                          search in [moduledir]/attributes/ for the attribute file.
1307     *                          When no modulename is specified ATK will search for the attribute
1308     *                          in [atkdir]/attributes/
1309     */
1310    function useattrib($attribute)  { atkuse("attribute", $attribute); }
1311  
1312    /**
1313     * Includes the file containing the specified relation
1314     *
1315     * @param string $relation  The relation to include in the format "module.relation". ATK will
1316     *                          search in [moduledir]/relations/ for the relation file.
1317     *                          When no modulename is specified ATK will search for the relation
1318     *                          in [atkdir]/relations/
1319     */
1320    function userelation($relation) { atkuse("relation" , $relation);  }
1321    function usefilter($filter)     { atkuse("filter"   , $filter);    }
1322  
1323    /**
1324     * Returns the include file for an atk class (attribute, relation or filter)
1325     * @param string $type the type of the class (attribute|relation|filter)
1326     * @param string $name the name of the class
1327     */
1328    function atkgetinclude($type, $name)
1329    {
1330      global $config_atkroot;
1331      $a = explode(".", $name);
1332      if (count($a) == 2)
1333        $include = moduleDir(strtolower($a[0])).$type."s/class.".strtolower($a[1]).".inc";
1334      else $include = $config_atkroot."atk/".$type."s/class.".strtolower($name).".inc";
1335      return $include;
1336    }
1337  
1338    /**
1339     * Check if an atk class exists (attribute, relation or filter)
1340     * @param string $type the type of the class (attribute|relation|filter)
1341     * @param string $name the name of the class
1342     */
1343    function atkexists($type, $name)
1344    {
1345      return file_exists(atkgetinclude($type, $name));
1346    }
1347  
1348    /**
1349     * Use an atk class (attribute, relation or filter)
1350     * @param string $type the type of the class (attribute|relation|filter)
1351     * @param string $name the name of the class
1352     */
1353    function atkuse($type, $name)
1354    {
1355      include_once(atkgetinclude($type, $name));
1356    }
1357  
1358    /**
1359     * Returns the (virtual) hostname of the server.
1360     * @return string the hostname of the server
1361     */
1362    function atkHost()
1363    {
1364      $atkHost = $_SERVER["HTTP_HOST"]!=""?$_SERVER["HTTP_HOST"]:$_SERVER["SERVER_NAME"];
1365  
1366      // if we're running on our cluster environment
1367      // we seem to have a specific portid within the HTTP_HOST
1368      // If so, remove it from the hostname
1369  
1370      $dummy = explode(":", $atkHost);
1371      return $dummy[0];
1372    }
1373  
1374    /**
1375     * Returns the next unique ID for the given sequence.
1376     * NOTE: ID's are only unique for the script execution!
1377     * @param string $sequence the sequence name
1378     * @return int next unique ID for the given sequence
1379     */
1380    function getUniqueID($sequence)
1381    {
1382      static $unique = array();
1383      if (!isset($unique[$sequence]))
1384      {
1385        $unique[$sequence] = 0;
1386      }
1387      return ++$unique[$sequence];
1388    }
1389  
1390    /**
1391     * Checks if the variable $var contains the given flag ($flag).
1392     * @param string $var the variable which might contain flags
1393     * @param var $flag the flag you want to check for
1394     * @return bool result of check
1395     */
1396    function hasFlag($var, $flag)
1397    {
1398      return ($var & $flag) == $flag;
1399    }
1400  
1401    /**
1402     * Makes an url from the target var and all postvars
1403     * @param string $target the path of the file to open
1404     * @param string the url with the postvars
1405     */
1406    function makeUrlFromPostvars($target)
1407    {
1408      global $ATK_VARS;
1409  
1410      if(count($ATK_VARS ))
1411      {
1412        $url = $target."?";
1413        foreach ($ATK_VARS as $key => $val)
1414        {
1415          $url .= $key."=".rawurlencode($val)."&";
1416        }
1417        return $url;
1418      }
1419      return "";
1420    }
1421  
1422    /**
1423     * Makes an string with hidden input fields containing all posted vars
1424     * @param array $excludes array with the vars to exclude, default empty
1425     */
1426    function makeHiddenPostvars($excludes=array())
1427    {
1428      global $ATK_VARS;
1429      $str = "";
1430  
1431      if(count($ATK_VARS ))
1432      {
1433        foreach ($ATK_VARS as $key => $val)
1434        {
1435          if (!in_array($key, $excludes))
1436            $str .= "<input type='hidden' name=\"$key\" value=\"".atk_htmlentities(strval($val))."\">\n";
1437        }
1438        return $str;
1439      }
1440      return "";
1441    }
1442  
1443    /**
1444     * Returns a string representation of an action status.
1445     * @param var $status status of the action
1446     *                    (ACTION_FAILED|ACTION_SUCCESS|ACTION_CANCELLED)
1447     */
1448    function atkActionStatus($status)
1449    {
1450      switch ($status)
1451      {
1452        case ACTION_CANCELLED: return "cancelled";
1453        case ACTION_FAILED: return "failed";
1454        case ACTION_SUCCESS: return "success";
1455      }
1456    }
1457  
1458    /**
1459     * Build query string based on an array of parameters.
1460     *
1461     * @param array $params array of parameters
1462     */
1463    function buildQueryString($params, $parent="")
1464    {
1465      $query = "";
1466  
1467      foreach ($params as $key => $value)
1468      {
1469        if (!empty($query))
1470          $query .= '&';
1471  
1472        if (!empty($parent))
1473          $key = "{$parent}[{$key}]";
1474  
1475        if (!is_array($value))
1476        {
1477          $query .= "$key=".rawurlencode($value);
1478        }
1479        else
1480        {
1481          $query .= buildQueryString($value, $key);
1482        }
1483      }
1484  
1485      return $query;
1486    }
1487  
1488    /**
1489     * Generate a dispatch menu URL for use with nodes and their specific
1490     * actions.
1491     *
1492     * Note that this does not necessarily create a link to the current php
1493     * file (dispatch.php, index.php). It asks the controller which one to use.
1494     *
1495     * @param string $node the (module.)node name
1496     * @param string $action the atk action the link will perform
1497     * @param string $params: A key/value array with extra options for the url
1498     * @param string $phpfile The php file to use for dispatching, if not set we look at the theme for the dispatchfile
1499     * @return string url for the node with the action
1500     */
1501    function dispatch_url($node, $action, $params=array(),$phpfile='')
1502    {
1503      $c = &atkinstance("atk.atkcontroller");
1504      if (!$phpfile) $phpfile =  $c->getPhpFile();
1505      $url = $phpfile;
1506      $atkparams = array();
1507      if($node!="")
1508        $atkparams["atknodetype"] = $node;
1509      if($action!="")
1510        $atkparams["atkaction"] = $action;
1511      $params = array_merge($atkparams, $params);
1512  
1513      if ($params != "" && is_array($params) && count($params) > 0)
1514        $url .= '?'.buildQueryString($params);
1515  
1516      return $url;
1517    }
1518  
1519    /**
1520     * @deprecated Use atkcontroller::getPhpFile() instead.
1521     */
1522    function getDispatchFile()
1523    {
1524      $c = &atkinstance("atk.atkcontroller");
1525      return $c->getPhpFile();
1526    }
1527  
1528    /**
1529     * Generate a partial url.
1530     *
1531     * @param string $node the (module.)node name
1532     * @param string $action the atkaction
1533     * @param string $partial the partial name
1534     * @param array $params a key/value array with extra params
1535     * @param int $sessionStatus session status (default SESSION_PARTIAL)
1536     * @return string url for the partial action
1537     */
1538    function partial_url($node, $action, $partial, $params=array(), $sessionStatus=SESSION_PARTIAL)
1539    {
1540      if (!is_array($params))
1541        $params = array();
1542      $params['atkpartial'] = $partial;
1543  
1544      return session_url(dispatch_url($node, $action, $params), $sessionStatus);
1545    }
1546  
1547    /**
1548     * Writes trace file to system tmp directory
1549     * @param string $msg message to display in the trace
1550     */
1551    function atkTrace($msg="")
1552    {
1553      global $HTTP_SERVER_VARS, $HTTP_SESSION_VARS, $HTTP_GET_VARS, $HTTP_COOKIE_VARS, $HTTP_POST_VARS;
1554  
1555      $log = "\n".str_repeat("=", 5)."\n";
1556      $log.= "Trace triggered: ".$msg."\n";
1557      $log.= date("r")."\n";
1558      $log.= $HTTP_SERVER_VARS["REMOTE_ADDR"]."\n";
1559      $log.= $HTTP_SERVER_VARS["SCRIPT_URL"]."\n";
1560      $log.= "\nSessioninfo: "."session_name(): ".session_name()." session_id(): ".session_id()." SID: ".SID." REQUEST: ".$_REQUEST[session_name()]." COOKIE: ".$_COOKIE[session_name()]."\n";
1561      $log.= "\n\nHTTP_SERVER_VARS:\n";
1562      $log.= var_export($HTTP_SERVER_VARS, true);
1563      $log.= "\n\nHTTP_SESSION_VARS:\n";
1564      $log.= var_export($HTTP_SESSION_VARS, true);
1565      $log.= "\n\nHTTP_COOKIE_VARS:\n";
1566      $log.= var_export($HTTP_COOKIE_VARS, true);
1567      $log.= "\n\nHTTP_POST_VARS:\n";
1568      $log.= var_export($HTTP_POST_VARS, true);
1569      $log.= "\n\nHTTP_GET_VARS:\n";
1570      $log.= var_export($HTTP_GET_VARS, true);
1571  
1572      $log.= "\n\nSession file info:\n";
1573      $log.= var_export(stat(session_save_path()."/sess_".session_id()), true);
1574  
1575      $tmpfile = tempnam("/tmp",atkconfig("identifier")."_trace_");
1576      $fp = fopen($tmpfile,"a");
1577      fwrite($fp, $log);
1578      fclose($fp);
1579    }
1580  
1581    /**
1582     * Creates a session aware button
1583     * @param string $text       the text to display on the button
1584     * @param string $url        the url to use for the button
1585     * @param var $sessionstatus the session flags
1586     *              (SESSION_DEFAULT (default)|SESSION_NEW|SESSION_REPLACE|
1587     *               SESSION_NESTED|SESSION_BACK)
1588     * @param string $cssclass   the css class the button should get
1589     * @param bool $embeded      wether or not it's an embedded button
1590     */
1591    function atkButton($text, $url="", $sessionstatus=SESSION_DEFAULT, $embedded=true, $cssclass="")
1592    {
1593      $page = &atkPage::getInstance();
1594      $page->register_script(atkconfig("atkroot")."atk/javascript/formsubmit.js");
1595      static $cnt=0;
1596  
1597      if ($cssclass == "")
1598        $cssclass = "btn";
1599  
1600      $cssclass = ' class="'.$cssclass.'"';
1601      $script = 'atkSubmit("'.atkurlencode(session_url($url,$sessionstatus)).'")';
1602      $button = '<input type="button" name="atkbtn'.(++$cnt).'" value="'.$text.'" onClick=\''.$script.'\''.$cssclass.'>';
1603  
1604      if (!$embedded)
1605      {
1606        $res = '<form name="entryform">';
1607        $res.= session_form();
1608        $res.= $button.'</form>';
1609        return $res;
1610      }
1611      else
1612      {
1613        return $button;
1614      }
1615    }
1616  
1617    /**
1618     * Imports a file
1619     * @param string $fullclassname Name of class in atkformat (map1.map2.classfile)
1620     * @param bool   $failsafe      If $failsafe is true (default), the class is required.  Otherwise, the
1621     *                                class is included.
1622     * @param bool   $path          Whether or not it is NOT an ATK classname
1623     *                                 ("map.class"), if true it will interpret classname
1624     *                                 as: "map/class.classname.inc", default false.
1625     * @return bool whether the file we want to import was actually imported or not
1626     */
1627    function atkimport($fullclassname, $failsafe=true, $path = false)
1628    {
1629      return atkClassLoader::import($fullclassname, $failsafe, $path);
1630    }
1631  
1632    /**
1633     * Imports a Zend Framework class.
1634  
1635     * @param string $classname name of class in zend-format (starting with a Capital)
1636     */
1637    function zendimport($classname)
1638    {
1639      if (atkconfig("zend_framework_path") == null)
1640      {
1641        throw new Exception("Zend Framework path not set (".atkconfig('zend_framework_path').")!");
1642      }
1643  
1644      $current_path = getcwd();
1645      chdir(atkConfig('atkroot').atkconfig("zend_framework_path")."/");
1646  
1647      $filename = $classname.'.php';
1648  
1649      if (file_exists($filename))
1650      {
1651        require_once $filename;
1652      }
1653  
1654      chdir($current_path);
1655    }
1656  
1657    /**
1658     * Clean-up the given path.
1659     *
1660     * @param string $path
1661     * @return cleaned-up path
1662     *
1663     * @see http://nl2.php.net/manual/en/function.realpath.php (comment of 21st of September 2005)
1664     */
1665    function atkCleanPath($path)
1666    {
1667      return atkClassLoader::cleanPath($path);
1668    }
1669  
1670    /**
1671     * Converts an ATK classname ("map1.map2.classname")
1672     * to a pathname ("/map1/map2/class.classname.inc")
1673     * @param string $fullclassname  ATK classname to be converted
1674     * @param bool $class            is the file a class? defaults to true
1675     * @return string converted filename
1676     */
1677    function getClassPath($fullclassname, $class = true)
1678    {
1679      return atkClassLoader::getClassPath($fullclassname, $class);
1680    }
1681  
1682    /**
1683     * Converts a pathname ("/map1/map2/class.classname.inc")
1684     * to an ATK classname ("map1.map2.classname")
1685     * @param string $classpath pathname to be converted
1686     * @param bool $class       is the file a class? defaults to true
1687     * @return string converted filename
1688     */
1689    function getClassName($classpath, $class=true)
1690    {
1691      return atkClassLoader::getClassName($classpath, $class);
1692    }
1693  
1694    /**
1695     * Returns a new instance of a class
1696     * @param string $fullclassname the ATK classname of the class ("map1.map2.classname")
1697     * @return obj instance of the class
1698     */
1699    function atknew($fullclassname)
1700    {
1701      $args = func_get_args();
1702      array_shift($args);
1703      $args = array_values($args);
1704      return atkClassLoader::newInstanceArgs($fullclassname, $args);
1705    }
1706  
1707    /**
1708     * Return a singleton instance of the specified class.
1709     *
1710     * This works for all singletons that implement the getInstance() method.
1711     *
1712     * @param string $fullclassname the ATK classname of the class ("map1.map2.classname")
1713     * @return obj instance of the class
1714     * */
1715    function atkinstance($fullclassname, $reset=false)
1716    {
1717      return atkClassLoader::getSingletonInstance($fullclassname, $reset);
1718    }
1719  
1720    /**
1721     * Compares two assosiative multi dimensonal array's
1722     * if arrays differ, return true, otherwise it returns false
1723     * @param array $array1 original array
1724     * @param array $array2 new array
1725     * @return boolean wether or not the arrays differ
1726     */
1727    function atkArrayCompare($array1, $array2)
1728    {
1729      $difference = atkArrayDiff($array1, $array2);
1730  
1731      return !is_array($difference) ? false : true;
1732    }
1733  
1734    /**
1735     * Compares two assosiative multi dimensonal array's
1736     * if arrays differ, return differences, otherwise it returns false
1737     * @param array $array1 original array
1738     * @param array $array2 new array
1739     * @return mixed differences or false if they do not differ
1740     */
1741    function atkArrayDiff($array1, $array2)
1742    {
1743      foreach($array1 as $key => $value)
1744      {
1745        if(is_array($value))
1746        {
1747          if(!is_array($array2[$key]))
1748          {
1749            $difference[$key] = $value;
1750          }
1751          else
1752          {
1753            $new_diff = atkArrayDiff($value, $array2[$key]);
1754            if($new_diff != FALSE)
1755            {
1756              $difference[$key] = $new_diff;
1757            }
1758          }
1759        }
1760        elseif(!isset($array2[$key]) || $array2[$key] != $value)
1761        {
1762          $difference[$key] = $value;
1763        }
1764      }
1765  
1766      return !isset($difference) ? false : $difference;
1767    }
1768  
1769    /**
1770     * Recursive function that checks an array for values
1771     * because sometimes arrays will be filled with other empty
1772     * arrays and therefore still show up filled.
1773     *
1774     * WARNING: take care with using this function as it is recursive
1775     * and if you have a value linking back to it's self in one way or another,
1776     * you may spend a loooong time waiting on your application
1777     *
1778     * @param Array $array The array that
1779     * @return bool Wether or not we found anything
1780     */
1781    function atk_value_in_array($array)
1782    {
1783      if (is_array($array) && !empty($array))
1784      {
1785        foreach ($array as $key => $value)
1786        {
1787          if (is_array($value))
1788          {
1789            if (atk_value_in_array($value)) return true;
1790          }
1791          else if($value) return true;
1792        }
1793      }
1794      return false;
1795    }
1796  
1797    /**
1798     * Recursive function to look if the needle exists in the haystack
1799     *
1800     * WARNING: take care with using this function as it is recursive
1801     * and if you have a value linking back to it's self in one way or another,
1802     * you may spend a loooong time waiting on your application
1803     *
1804     * @param String $needle The value which will be searched in the haystack
1805     * @param Array $haystack Array with values
1806     * @return Boolean True if needle exists in haystack
1807     */
1808    function atk_in_array_recursive($needle, $haystack)
1809    {
1810      foreach ($haystack as $key=>$value)
1811      {
1812        if ($value==$needle) return true;
1813        else if (is_array($value))
1814        {
1815          if (atk_in_array_recursive($needle, $value)) return true;
1816        }
1817      }
1818      return false;
1819    }
1820  
1821    /**
1822     * Escapes the predefined characters
1823     *
1824     * When there are predefined characters used this function will escape them
1825     * and returns right pattern.
1826     *
1827     * @param String $pattern Raw string to be escaped
1828     * @return String Returns a pattern with the predefined pattern escaped
1829     */
1830    function escapeForRegex($pattern)
1831    {
1832      $escaped='';
1833      $escapechars = array("/","?",'"', "(", ")", "'","*",".","[","]");
1834      for ($counter = 0; $counter<strlen($pattern);$counter++)
1835      {
1836        $curchar = substr($pattern, $counter, 1);
1837        if (in_array($curchar,$escapechars))
1838        $escaped .= "\\";
1839        $escaped.=$curchar;
1840      }
1841      return $escaped;
1842    }
1843  
1844    /*
1845     * Returns the postvars
1846     * Returns a value or an array with all values
1847     */
1848    function atkGetPostVar($key="")
1849    {
1850      if(empty($key) || $key=="")
1851      {
1852        return $_REQUEST;
1853      }
1854      else
1855      {
1856        if (array_key_exists($key,$_REQUEST) && $_REQUEST[$key]!="") return $_REQUEST[$key];
1857        return "";
1858      }
1859    }
1860  
1861    /**
1862     * ATK version of the PHP htmlentities function. Works just like PHP's
1863     * htmlentities function, but falls back to atkGetCharset() instead of
1864     * PHP's default charset, if no charset is given.
1865     *
1866     * @param String $string    string to convert
1867     * @param int $quote_style  quote style (defaults to ENT_COMPAT)
1868     * @param String $charset   character set to use (default to atkGetCharset())
1869     *
1870     * @return String encoded string
1871     */
1872    function atk_htmlentities($string, $quote_style=ENT_COMPAT, $charset=null)
1873    {
1874      return atkString::htmlentities($string, $quote_style, $charset);
1875    }
1876  
1877    /**
1878     * ATK version of the PHP html_entity_decode function. Works just like PHP's
1879     * html_entity_decode function, but falls back to atkGetCharset() instead of
1880     * PHP's default charset, if no charset is given.
1881     *
1882     * @param String $string    string to convert
1883     * @param int $quote_style  quote style (defaults to ENT_COMPAT)
1884     * @param String $charset   character set to use (default to atkGetCharset())
1885     *
1886     * @return String encoded string
1887     */
1888    function atk_html_entity_decode($string, $quote_style=ENT_COMPAT, $charset=null)
1889    {
1890      return atkString::html_entity_decode($string, $quote_style, $charset);
1891    }
1892  
1893    /**
1894     * Get string length
1895     * @param string $str The string being checked for length
1896     * @return int
1897     */
1898    function atk_strlen($str)
1899    {
1900        return atkString::strlen($str);
1901    }
1902  
1903    /**
1904     * Get part of string
1905     * @param string $str The string being checked.
1906     * @param int $start The first position used in $str
1907     * @param int $length[optional] The maximum length of the returned string
1908     * @return string
1909     */
1910     function atk_substr($str,$start,$length='')
1911     {
1912       return atkString::substr($str,$start,$length);
1913     }
1914  
1915     /**
1916      *  Find position of first occurrence of string in a string
1917      * @param object $haystack The string being checked.
1918      * @param object $needle The position counted from the beginning of haystack .
1919      * @param object $offset[optional] The search offset. If it is not specified, 0 is used.
1920      * @return int|boolean
1921      */
1922     function atk_strpos($haystack,$needle,$offset=0)
1923     {
1924        return atkString::strpos($haystack,$needle,$offset);
1925     }
1926  
1927     /**
1928      * Make a string lowercase
1929      * @param string $str The string being lowercased.
1930      * @return string
1931      */
1932     function atk_strtolower($str)
1933     {
1934       return atkString::strtolower($str);
1935     }
1936  
1937    /**
1938     *
1939     * Make a string uppercase
1940     * @param string $str The string being uppercased.
1941     * @return string
1942     */
1943     function atk_strtoupper($str)
1944     {
1945       return atkString::strtoupper($str);
1946     }
1947  
1948    /**
1949     * Return the default charset, first we look if the
1950     * config_default_charset is set, else we use the
1951     * charset in the languge file;
1952     * @return string
1953     */
1954    function atkGetCharset()
1955    {
1956      return atkconfig('default_charset',atktext('charset','atk'));
1957    }
1958  
1959    /**
1960     * Looks up a value using the given key in the given array and returns
1961     * the value if found or a default value if not found.
1962     *
1963     * @param array $array Array to be searched for key
1964     * @param string $key Key for which we are looking in array
1965     * @param mixed $defaultvalue Value we will return if key was not found in array
1966     * @return mixed Value retrieved from array or default value if not found in array
1967     */
1968    function atkArrayNvl($array, $key, $defaultvalue=null)
1969    {
1970      return (isset($array[$key]) ? $array[$key] : $defaultvalue);
1971    }
1972  
1973    /**
1974     * Resolve a classname to its final classname.
1975     *
1976     * An application can overload a class with a custom version. This
1977     * method resolves the initial classname to its overloaded version
1978     * (if any).
1979     *
1980     * @param String $class The name of the class to resolve
1981     * @return String The resolved classname
1982     */
1983    function atkResolveClass($class)
1984    {
1985      atkimport("atk.utils.atkclassloader");
1986      return atkClassLoader::resolveClass($class);
1987    }
1988  
1989    /**
1990     * Returns the IP of the remote client.
1991     *
1992     * @return string ip address
1993     */
1994    function atkGetClientIp()
1995    {
1996      static $s_ip = NULL;
1997  
1998      if ($s_ip === NULL)
1999      {
2000        if (getenv("HTTP_CLIENT_IP"))
2001          $s_ip = getenv("HTTP_CLIENT_IP");
2002        elseif (getenv("HTTP_X_FORWARDED_FOR"))
2003        {
2004          $ipArray = explode(",", getenv("HTTP_X_FORWARDED_FOR"));
2005          $s_ip = $ipArray[0];
2006        }
2007        elseif (getenv("REMOTE_ADDR"))
2008          $s_ip = getenv("REMOTE_ADDR");
2009        else $s_ip = 'x.x.x.x';
2010      }
2011  
2012      return $s_ip;
2013    }
2014  
2015    /**
2016     * Function checks php version and clones the
2017     * given attribute in the right way
2018     *
2019     * @param object $attribute The attribute to clone
2020     * @return object $attr
2021     */
2022    function atkClone($attribute)
2023    {
2024      if (intval(substr(phpversion(),0,1))<5)
2025        $attr = $attribute;
2026      else
2027        $attr = clone($attribute);
2028  
2029      return $attr;
2030    }
2031  
2032    /**
2033     * Return the current script file. Like $_SERVER['PHP_SELF'], but
2034     * sanitized for security reasons
2035     *
2036     * @return String
2037     */
2038    function atkSelf()
2039    {
2040      $self = $_SERVER['PHP_SELF'];
2041      if (strpos($self, '"')!==false) $self = substr($self, 0, strpos($self, '"')); //XSS attempt
2042      return atk_htmlentities(strip_tags($self)); // just in case..
2043    }
2044  
2045    /**
2046     * ATK wrapper of the PHP iconv function. Check if iconv function is present in
2047     * the system. If yes - use it for converting string, if no - save string untouch
2048     * and make warning about it.
2049     *
2050     * @param string $in_charset  from charset
2051     * @param string $out_charset to charset
2052     * @param string $str string to convert
2053     *
2054     * @return string encoded string
2055     */
2056    function atk_iconv($in_charset, $out_charset, $str)
2057    {
2058        return atkString::iconv($in_charset,$out_charset,$str);
2059    }
2060  
2061    /**
2062     * Returns the first argument that is not null.
2063     *
2064     * @param mixed ... arguments
2065     * @return mixed first argument that is not null
2066     */
2067    function atkNvl()
2068    {
2069      for ($i = 0; $i < func_num_args(); $i++)
2070      {
2071        $arg = func_get_arg($i);
2072        if (!is_null($arg))
2073        {
2074          return $arg;
2075        }
2076      }
2077  
2078      return null;
2079    }
2080  
2081    function atkEcho($message)
2082    {
2083      if(strpos(atkSelf(),"runcron"))
2084      {
2085        echo $message;
2086      }
2087    }
2088  
2089    /**
2090     * Format date according to a format string, uses ATK's language files to translate
2091     * months, weekdays etc.
2092     *
2093     * @param $date    timestamp or date array (gotten with getdate())
2094     * @param $format  format string, compatible with PHP's date format functions
2095     * @param $weekday always include day-of-week or not
2096     *
2097     * @return string formatted date
2098     */
2099    function atkFormatDate($date, $format, $weekday=false)
2100    {
2101      static $langcache = array();
2102  
2103      if (!is_array($date))
2104      {
2105        $date = getdate($date);
2106      }
2107  
2108      /* format month */
2109      $format = str_replace("M", "%-%",   $format);
2110      $format = str_replace("F", "%=%",   $format);
2111  
2112      /* format day */
2113      $format = str_replace("D", "%&%", $format);
2114      $format = str_replace("l", "%*%", $format);
2115  
2116      if ($weekday && strpos($format, '%&%') === FALSE && strpos($format, '%*%') === FALSE)
2117      {
2118        $format = str_replace("d", "%*% d", $format);
2119        $format = str_replace("j", "%*% j", $format);
2120      }
2121  
2122      /* get date string */
2123      require_once(atkconfig('atkroot')."atk/utils/adodb-time.inc.php");
2124      $str_date = adodb_date($format, $date[0]);
2125  
2126      $month = $date['month'];
2127      $shortmonth = substr(strtolower($date["month"]), 0, 3);
2128  
2129      /* store the text calls */
2130      if(!isset($langcache[$month]))
2131      {
2132        $langcache[$month]= atktext(strtolower($month),"atk");
2133      }
2134  
2135      if(!isset($langcache[$shortmonth]))
2136      {
2137        $langcache[$shortmonth] = atktext($shortmonth);
2138      }
2139  
2140      /* replace month/week name */
2141      $str_date = str_replace("%-%", $langcache[$shortmonth], $str_date);
2142      $str_date = str_replace("%=%", $langcache[$month], $str_date);
2143      $str_date = str_replace("%*%", atktext(strtolower($date["weekday"]),"atk"), $str_date);
2144      $str_date = str_replace("%&%", atktext(substr(strtolower($date["weekday"]), 0, 3),"atk"), $str_date);
2145  
2146      /* return string */
2147      return $str_date;
2148    }
2149  ?>

title

Description

title

Description

title

Description

title

title

Body