=')) ? clone($object) : $object; return $result; # PHP internals need a tmp var to return by ref } /** * Append a line to a logfile in log/. Date will be added to filename and line * @param $name Name of logfile * @param $line1 Line to append (varargs) */ function log($name /* ... */) { $args = func_get_args(); $line = date("Y-m-d H:i:s") . "\t" . implode("\t", array_slice($args, 1)) . "\n"; if ($fh = fopen($GLOBALS['ULTRAHOME'] . "/log/$name-" . date('Ymd'), "a")) { fputs($fh, $line); fclose($fh); } } /** * Store timings for appending to log/timer_log-* in auto_append.php */ function timerlog($label = '') { if ($GLOBALS['debug_timerlog']) { $s = $GLOBALS['ULTRATIME']; $e = gettimeofday(); $msec= ($e['sec'] - $s['sec']) * 1000 + ($e['usec'] - $s['usec']) / 1000; $GLOBALS['ULTRATIMERLOG'] .= sprintf(" %s:%d", $label, $msec); } } /** * Send error report either to browser (on devel/twin machines) or by email to .diffnotice guys * @parma $title (optional) error title, one line * @param $body (optional) error body, multiline * @param $to (optional) comma separated recipient list * @param named parameter id identifier of error. if given, only subsequent errors of same id will trigger message * @param named parameter graceperiod number of seconds within which additional errors are ignored * @param named parameter timewindow number of seconds after graceperiod within which the second error must occur */ function error($p = array(), $body = "", $to = "") { if (is_array($p)) { $title = $p['title']; $body = $p['body']; $to = $p['to']; } else { $title = $p; $p = array(); } $p += array('timewindow' => 25*3600, 'graceperiod' => 60); if (!$to) $to = strtr(trim(@file_get_contents($GLOBALS['ULTRAHOME'] . "/.diffnotice")), array("\n"=>',', ' '=>'')); if ($p['id']) { $stampfn = $GLOBALS['ULTRAHOME'] . "/tmp/errstamp_" . urlencode($p['id']); $stampage = time() - @filemtime($stampfn); if ($stampage >= $p['graceperiod'] && $stampage <= $p['graceperiod'] + $p['timewindow']) $sendalert = true; if ($stampage >= $p['graceperiod']) # constantly occurring errors should not suppress mail touch($stampfn); } else $sendalert = true; if ($sendalert) { $url = ($_SERVER['HTTPS'] ? "https://" : "http://") . $_SERVER['HTTP_HOST'] .$_SERVER['REQUEST_URI']; if (!$title) $title="Error in $url"; $body .= "\nUrl: $url\n\n\$_REQUEST: ".print_r($_REQUEST, true). "\nStack:\n" . print_r(debug_backtrace(), true) . "\n\$_SERVER: " . print_r($_SERVER, true); if (!ereg('live', $GLOBALS['ULTRASERVERTYPE'])) { echo "
$title\n$body\n"; } else { $cmd = "alert -l " . escapeshellarg($to) . " 4 " . escapeshellarg($title); EDC('exec', $cmd); if (($pipe = popen($cmd, "w"))) { fputs($pipe, $body); pclose($pipe); } } } error_log("it::error: ".$title); } /** * Same as error(), plus exit */ function fatal($title='', $body='', $to='') { it::error($title, $body, $to); exit; } /** * Return "db4" or "db2" depending on availability */ function dba_handler() { return in_array("db4", dba_handlers()) ? "db4" : "db2"; } /** * Convert a string to ASCII-only chars, map/strip ISO-8859-1 accents * @param $text Text to convert * @return mapped/stripped version of $text contains only chars [0..127] */ function toascii($text) { return strtr(strtr($text, 'ÇéâàåçêëèïîìÅÉôòûùÿøØáíóúñÑÁÂÀãÃÊËÈÍÎÏÓÔõÕÚÛÙýÝ', 'CeaaaceeeiiiAEoouuyooaiounNAAAaAEEEIIIOOoOUUUyY'), array('ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'Ä' => 'Ae', 'Ö' => 'Oe', 'Ü' => 'Ue', 'æ' => 'ae', 'Æ' => 'Ae', 'ß' => 'ss')); } /** * Convert regex for preg (adds and escapes delimiter, adds modifiers) * @param $pattern Regex to convert * @param named parameter casesensitive Regex is case sensitive (omit modifier i) * @param named parameter multiline add modifier m * @param named parameter singleline add modifier s * @param named parameter utf8 add modifier u * @param named parameter extended add modifier x * @return converted regex to use with preg */ function convertregex($pattern, $p = array()) { $pattern = preg_replace('|/|', '\/', $pattern); $modifiers = ''; if (!$p['casesensitive']) $modifiers .= 'i'; foreach (array( 'multiline' => 'm', 'singleline' => 's', 'utf8' => 'u', 'extended' => 'x', ) as $key => $mod) { if ($p[$key]) $modifiers .= $mod; } return "/$pattern/$modifiers"; } /** * Try to match string against regex. See convertregex for additional named parameters. * @param $pattern Regex to match against * @param $string String to match * @param named parameter offset_capture Set flag preg_offset_capture (returns offsets with the matches). * @param named parameter all Return every match as array instead of first match. * @return Matched string or false */ function match($pattern, $string, $p = array()) { $flags = 0; if($p['offset_capture']) $flags |= PREG_OFFSET_CAPTURE; $oldlocale = setlocale( LC_CTYPE, 0 ); setlocale(LC_CTYPE, 'de_CH'); if ($p['all']) $r = preg_match_all(it::convertregex($pattern, $p), $string, $m, $flags | PREG_PATTERN_ORDER, $p['offset']); else $r = preg_match(it::convertregex($pattern, $p), $string, $m, $flags, $p['offset']); setlocale(LC_CTYPE, $oldlocale); if (!$r) # no match $result = $p['all'] ? array() : false; else if (count($m) == 1) # no capture $result = $m[0]; else if (count($m) == 2) # one capture $result = $m[1]; else if ($p['all'] && !$p['pattern_order']) # captures, reorder pattern_order to set_order but without first element $result = call_user_func_array('array_map', array_merge(array(null), array_slice($m, 1))); else # captures, don't return first element (matched string) $result = array_slice($m, 1); return $result; } /** * Replace parts of a string matched by a pattern with according replacement string. See convertregex for named parameters. * @param $replacementes Array with patterns as keys and replacement strings as values. * @param $string String to change. * @return New string. */ function replace($replacements, $string, $p = array()) { $patterns = array(); foreach (array_keys($replacements) as $pattern) $patterns[] = it::convertregex($pattern, $p); $oldlocale = setlocale(LC_CTYPE, 0); setlocale(LC_CTYPE, 'de_CH'); $result = preg_replace($patterns, array_values($replacements), $string, isset($p['limit']) ? $p['limit'] : -1); setlocale(LC_CTYPE, $oldlocale); return $result; } /** * Extract key => value pairs from assoc array by key * @param $array array to filter * @param $keys array of keys to keep */ function filter_keys($array, $keys) { $result = array(); $keep = array_flip($keys); foreach ($array as $key => $val) if (isset($keep[$key])) $result[$key] = $val; return $result; } /** * Construct shell command, log it, execute it and return output as string. Options ONLY allowed in assoc arrays * @param varargs: scalars for cmd and arguments, assoc arrays to specify opts and opt values, "-q"=>true for opts without args * @return output of command. shell errors not detectable, see error_log in /www/server/logs */ function exec(/* ... */) { $args = func_get_args(); $parts = array(); foreach ($args as $arg) { foreach ((array)$arg as $key => $val) { if (it::match('^-', $val)) it::fatal("leading - in value"); else if ($val === true || $val === false || $val === null) $parts[] = $val ? escapeshellarg($key) : ""; else if (is_int($key)) $parts[] = escapeshellarg($val); else $parts[] = escapeshellarg($key) . " " . escapeshellarg($val); } } $cmd = join(" ", $parts); it::log('exec', $cmd); return (string)shell_exec($cmd); } } ?>