<?php
/*
**	$Id$
**
**	ITools - the Internet Tools Library
**
**	Copyright (C) 1995-2006 by the ITools Authors.
**	This program is free software; you can redistribute it and/or
**	modify it under the terms of either the GNU General Public License
**	or the GNU Lesser General Public License, as published by the Free
**	Software Foundation. See http://www.gnu.org/licenses/ for details.
**
**	it.class - static functions
*/

class it
{

/**
 * Clone an object and return copy, works for all PHP versions
 */
function &cloneobj(&$object)
{
	$result = (is_object($object) && version_compare(zend_version(), 2, '>=')) ? 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 "<pre>$title\n$body\n</pre>";
		}
		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;
}

}
?>