. ** ** it.class - static functions */ class it { /** * Create config class with static members initialized (e.g. $home). * NOTE: PHP5 ONLY * @param $p Static members to be generated in newly created class * service: Class name of created class (default: from caller path) * home: Home directory of application (default: from caller path) * site: Domain name of application (default: from caller path) * db: Database of application (default: from caller path) */ static function createconfig($p = array()) { $stack = debug_backtrace(); $filename = $stack[0]['file']; preg_match('!/www/((\w+)[^/]+)!', $filename, $parts); $p += array( 'home' => $parts[0], 'site' => $parts[1], 'db' => strtr($parts[1], ".-", "__"), 'service' => $parts[2], ); if (class_exists($p['service'] . "_tools")) $extends = "extends {$p['service']}_tools "; $code = array("class {$p['service']} $extends{"); foreach ($p as $name => $value) $code[] = "static \$$name = " . var_export($value, true) . ";"; $code[] = "}"; eval(join("\n", $code)); } /** * Clone an object and return copy, works for all PHP versions */ static 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) */ static function log($name /* ... */) { $args = func_get_args(); $line = date("Y-m-d H:i:s") . "\t" . implode("\t", array_slice($args, 1)) . "\n"; $fn = $GLOBALS['ULTRAHOME'] . "/log/$name-" . date('Ymd'); $existed = file_exists($fn); if (isset($GLOBALS['ULTRAHOME']) && ($fh = fopen($fn, "a"))) { fputs($fh, $line); fclose($fh); if (!$existed) { @chgrp($fn, "www"); @unlink($GLOBALS['ULTRAHOME'] . "/log/$name"); @symlink($fn, $GLOBALS['ULTRAHOME'] . "/log/$name"); } } } /** * Store timings for appending to log/timer_log-* in auto_append.php */ static 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 verbose error report to browser or (if display_errors is off) by email to .diffnotice gods, file owner or SERVER_ADMIN * All params optional. Single string parameter means 'title'. * @parma $p['title'] error title, one line * @param $p['body'] error body, multiline * @param $p['to'] comma separated recipient list * @param $p['id'] identifier of error. if given, only subsequent errors of same id will trigger message * @param $p['graceperiod'] number of seconds within which additional errors are ignored if id is set * @param $p['timewindow'] number of seconds after graceperiod within which the second error must occur if id is set * @param $p['backtraceskip'] number of stack levels to drop * @param $p['blockmail'] number of seconds to block mails after having sent a mail [3600] * @param $p['blockmailid'] block mail for $p['blockmail'] seconds with same id. Default: $p['to'] * @param $p['omitdebuginfo'] Do not add stack dump, locals and environment to output [false] */ static function error($p = array(), $body = null, $to = null) # $body and $to deprecated { if (!is_array($p)) $p = array('title' => $p, 'body' => $body, 'to' => $to); if ($_SERVER['REMOTE_ADDR']) $url = ($_SERVER['HTTPS'] ? "https://" : "http://") . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; else $url = $_SERVER['SCRIPT_NAME']; $gods = strtr(trim(@file_get_contents($GLOBALS['ULTRAHOME'] . "/.diffnotice")), array("\n"=>', ')); if (!$p['to']) unset($p['to']); # allow defaults to kick in $p += array( 'title' => "it::error", 'to' => $gods ? $gods : (get_current_user() ? get_current_user() : $_SERVER['SERVER_ADMIN']), 'graceperiod' => 60, 'timewindow' => 25*3600, 'backtraceskip' => 0, 'blockmail' => 3600, 'omitdebuginfo' => false, ); $p += array('blockmailid' => md5($p['to'])); @mkdir("/tmp/alertdata"); @chmod("/tmp/alertdata", 0777); $toscreen = ini_get('display_errors') || (defined("STDOUT") && posix_isatty(STDOUT)) || EDC('astwin') || EDC('asdevel'); @fseek(STDOUT, 0, SEEK_END); # Work around PHP bug 49819: posix_isatty seeks to start if ($toscreen && !it::is_live()) $GLOBALS['debug_noredir'] = 1; if (!$toscreen) # this error can only be sent by mail: find out if we want to suppress it { if (!$p['id']) $sendmail = true; else { $errstampfn = "/tmp/alertdata/errstamp_" . urlencode($p['id']); $errstampage = time() - @filemtime($errstampfn); $sendmail = $errstampage >= $p['graceperiod'] && $errstampage <= $p['graceperiod'] + $p['timewindow']; if ($errstampage > $p['graceperiod']) { @unlink($errstampfn); @touch($errstampfn); } } if ($sendmail) { $lastsentfn = "/tmp/alertdata/lastsent_" . getmyuid() . "." . $p['blockmailid']; $now = time(); clearstatcache(); $lastsenttime = @filemtime($lastsentfn); $sendmail = $now - $lastsenttime > $p['blockmail']; $lastsentdebug = "Lastsent: lastsentfn=$lastsentfn now=$now lastsenttime=$lastsenttime blockmail={$p['blockmail']} sendmail=$sendmail errstampage=$errstampage\n\n"; if ($sendmail) { @unlink($lastsentfn); @touch($lastsentfn); } } } if ($toscreen || $sendmail) { $trace = it_debug::backtrace($p['backtraceskip']); # moved in here for performance in mass error case if (strlen($p['body']) > 500000) { file_put_contents($datafn = "/tmp/alertdata/error-" . substr(md5($p['body']), 0, 2), $p['body']); $p['body'] = "Body: " . getenv('HOSTNAME') . ":$datafn"; } $body = ($p['body'] ? trim($p['body'])."\n\n" : "") . ($url && !$toscreen? "{$p['title']}\n\nUrl: $url\n\n" : "") . ($trace ? ($sendmail?"" :" ")."Trace: $trace\n\n" : ""); if ($sendmail) # we're mailing: send maximum info { $p['title'] = "Alert: " . $p['title'] . " (on " . getenv('HOSTNAME') . ")"; if (!$p['omitdebuginfo']) { unset ($p['locals']['GLOBALS'], $p['locals']['_GET'], $p['locals']['_POST'], $p['locals']['_COOKIE']); $locals = print_r($p['locals'], true); if ($trace && ($fulltrace = array_slice(debug_backtrace(), $p['backtraceskip']))) while (strlen(print_r($fulltrace, true)) > 100000) array_pop($fulltrace); $body .= "Host: " . getenv('HOSTNAME') . "\n\n"; $body .= $p['locals'] && strlen($locals) < 100000 ? "Locals: $locals\n\n" : ""; $body .= $p['id'] ? "Filter: graceperiod={$p['graceperiod']} timewindow={$p['timewindow']}\n\n" : ""; $body .= "Time: " . it::date() . "\n\n"; $body .= $lastsentdebug; $body .= $_GET ? "\$_GET: " . var_export($_GET, true) . "\n\n" : ""; $body .= $_POST ? "\$_POST: " . var_export($_POST, true) . "\n\n" : ""; $body .= $_COOKIE ? "\$_COOKIE: " . var_export($_COOKIE, true) . "\n\n" : ""; $body .= $_SERVER ? "\$_SERVER: " . var_export($_SERVER, true) . "\n\n" : ""; $body .= $fulltrace ? "Stack: " . print_r($fulltrace, true) . "\n\n" : ""; $body = it::replace(array('(pw|passw|password|secret)\] => .*' => '$1] => ********'), $body); } it::mail(array('To' => $p['to'], 'Subject' => substr($p['title'], 0, 80), 'Body' => $body) + (($cc = $GLOBALS['it_defaultconfig']['error_cc']) ? array('Cc' => $cc) : array())); } else if ($_SERVER['REMOTE_ADDR']) # toscreen mode: web echo "
{$p['title']}\n".rtrim($body)."";
else # toscreen mode: shell (outputs to stderr)
error_log($p['title'] . " in " . ($trace ? $trace : "{$p['file']}:{$p['line']} Url: $url") . " " . (EDC('verbose') ? D($p['locals']) : ""));
}
if ($_SERVER['REMOTE_ADDR'])
error_log("it::error: " . $p['title']);
if (($fh = fopen("/tmp/alertdata/alert.log", "a")))
{
fputs($fh, it::date() . " " . $p['title'] . " in " . ($trace ? $trace : "{$p['file']}:{$p['line']} Url: $url") . "\n");
fclose($fh);
@chmod("/tmp/alertdata/alert.log", 0777);
}
}
/**
* Same as it::error(), plus exit
* @see error()
*/
static function fatal($title='', $body='', $to='')
{
$p = is_array($title) ? $title : array('title' => $title, 'body' => $body, 'to' => $to);
$p['backtraceskip']++;
it::error($p);
exit(1);
}
/**
* Print message to stderr and exit with error code
*/
static function bail($message = "Bailed.\n")
{
fputs(STDERR, $message);
exit(1);
}
/**
* Check wether I am on a live server for this service. Honors 'aslive' and 'asdevel' debug vars
* @return true if servertype is 'live'
*/
static function is_live()
{
return (preg_match("/^live/", $GLOBALS['ULTRASERVERTYPE']) || !$GLOBALS['ULTRASERVERTYPE'] || EDC('aslive')) && !EDC('asdevel');
}
/**
* Check wether I am on a development server for this service. Honors 'aslive' and 'asdevel' debug vars
* @return true if servertype is 'devel'
*/
static function is_devel()
{
return (preg_match("/^devel/", $GLOBALS['ULTRASERVERTYPE']) || EDC('asdevel')) && !EDC('aslive');
}
/**
* 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]
*/
static 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 $p['casesensitive'] Regex is case sensitive (omit modifier i)
* @param $p['multiline'] add modifier m: ^ and $ match \n
* @param $p['singleline'] add modifier s: . matches \n
* @param $p['utf8'] add modifier u
* @param $p['extended'] add modifier x
* @return converted regex to use with preg
*/
static 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. Case insensitive by default.
* @param $pattern Regex to match against
* @param $string String to match
* @param $p['offset_capture'] Set flag preg_offset_capture (returns offsets with the matches).
* @param $p['all'] Return every match as array instead of first match.
* @param $p['locale'] Use given locale (default: de_CH), mainly affects handling of iso-latin chars
* @param $p contains pattern modifiers, @see convertregex()
* @return Matched string or false
*/
static function match($pattern, $string, $p = array())
{
$flags = 0;
$p += array('locale' => 'de_CH');
if($p['offset_capture'])
$flags |= PREG_OFFSET_CAPTURE;
$oldlocale = setlocale(LC_CTYPE, 0);
setlocale(LC_CTYPE, $p['locale']);
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
{
static $backtrackerror;
if (!isset($backtrackerror))
$backtrackerror = defined('PREG_BACKTRACK_LIMIT_ERROR') ? constant('PREG_BACKTRACK_LIMIT_ERROR') : 0;
if ($backtrackerror && preg_last_error() == $backtrackerror) # Silence phpversionlint.php commit hook: function_exists('preg_last_error')
it::error("Exceeded pcre.backtrack_limit of " . ini_get('pcre.backtrack_limit') . " bytes");
$result = $p['all'] ? array() : null;
}
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.
*/
static 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 or comma separated list of keys to keep
*/
static function filter_keys($array, $keys)
{
$result = array();
$keep = array_flip(is_string($keys) ? explode(",", $keys) : (array)$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.
* Keywords {keyword} are replace a la ET(), {-opts} takes an array and
* inserts options a la it_html attributes (value, true, false or null)
* @param $cmd Format string with {keywords} replace a la ET()
* @param $values (zero, one or more arrays can be passed)
* @return output of command. shell errors not detectable, see error_log in /www/server/logs
*/
static function exec(/* $cmd, $values1 = array(), ... */)
{
$args = func_get_args();
$cmd = array_shift($args);
$values = array();
# Merge values into one array
foreach ($args as $arg)
$values += (array)$arg;
#for escapeshellarg in it::_exec_quotevalue
$oldlocale = setlocale(LC_CTYPE, 0);
setlocale(LC_CTYPE, 'de_CH');
foreach (it::match('({(-?)([a-z]\w*)})', $cmd, array('all' => true)) as $tags)
{
list($tag, $option, $key) = $tags;
$parts = array();
if ($option)
{
foreach ((array)$values["-$key"] as $key => $value)
{
if ($value === true || $value === false || $value === null)
$parts[] = $value ? $key : "";
else foreach ((array)$value as $val)
$parts[] = "$key " . it::_exec_quotevalue($val);
}
}
else
{
foreach ((array)$values[$key] as $value)
$parts[] = it::_exec_quotevalue($value);
}
$replacements[$tag] = join(" ", $parts);
}
$cmd = strtr($cmd, (array)$replacements); # Use strtr behaviour of going through source string once
setlocale(LC_CTYPE, $oldlocale);
$s = gettimeofday();
$result = EDC('noexec') ? "" : (string)shell_exec($cmd);
$e = gettimeofday();
$msec= intval(($e['sec'] - $s['sec']) * 1000 + ($e['usec'] - $s['usec']) / 1000);
@it::log('exec', "$msec\t$cmd");
return $result;
}
#fails with C locale!!!
static function _exec_quotevalue($value)
{
$result = strval($value);
if (it::match('^-', $result))
it::fatal("leading - in value");
return escapeshellarg($result);
}
/**
* Convert an image to a given size and type (ensures input is an image)
* @param $p['in'] Input filename (mandatory)
* @param $p['out'] Output filename (mandatory)
* @param $p['size'] Width x height of resulting image, e.g. "160x60"
* @param $p['type'] Output file type, e.g. "jpg"
* @param $p['types'] Comma separated list of accepted input types, default "bmp,eps,gif,jp2,jpg,png,svg,tif"
* @param $p['-opts'] Custom command line options to ImageMagick convert
* @return Success of convert as true/false
*/
static function imageconvert($p)
{
$result = false;
if (!(($imagetype = @exif_imagetype($p['in'])) && ($type = image_type_to_extension($imagetype, false))))
{
if (@get_class(it_xml::create(fopen($p['in'], "r"), array('prefix' => "_imageconvert_", 'safety' => 0))) == "_imageconvert_svg")
$type = "svg"; # Accept SVG files if they are valid XML and root tag is svg
else
list(, $type) = explode(' ', strtolower(it::exec('identify 2>/dev/null {in}', $p))); # for things like eps
}
$type = strtr($type, "jpeg" => "jpg", "tiff" => "tif", "ps" => "eps", "ept" => "eps");
$p += array('type' => $type, 'types' => "bmp,eps,gif,jp2,jpg,png,svg,tif");
$p['-opts'] = array('-thumbnail' => $p['size']) + (array)$p['-opts'];
if (in_array($type, explode(',', $p['types']))) # Valid type?
$result = it::exec("convert 2>&1 -flatten -quality 75 {-opts} {in} {type}:{out}", $p) === "";
return $result;
}
/**
* Parse command line options with Usage given as template and return assoc array. Example: (like grep --help)
* Usage: myprogram.php [OPTIONS] PATTERN
* -s, --short Use short ouput
* -f, --file=FILE Use FILE for input
* -x EXTENSION Ignore EXTENSION
* Mandatory arguments from the Usage: line are returned under their (lowercased!) name.
* All non-option arguments are returned in 'args'
* Option text must be indented; if long and short option present, value is stored in long option
* Options without arguments store true or false under their key
* Options -h and --help will be handled internally by printing usage and exiting
* When printing, the usage will be de-indented so the first line starts in the first column
* Two or more blanks must be in front of option explanation
* @param $helplines Usage parsed to determine options
* @return Associative array of options
*/
static function getopt($helplines)
{
$GLOBALS['it_stdin'] = array(
'fd' => null,
'args' => array(),
'filename' => "",
'line' => 0,
);
$result = array('args' => array());
if ($indentation = it::match('^\s+', $helplines))
$helplines = it::replace(array($indentation => "\n"), $helplines);
foreach(explode("\n", trim($helplines)) as $helpline)
{
$shortoptname = $shortoptarg = $longoptname = $longoptarg = "";
foreach (explode(',', $helpline) as $optdesc)
{
$optdesc = trim($optdesc);
if ($matches = (array)it::match('^--(\w[\w-]*)(=[A-Z])?', $optdesc))
list($longoptname, $longoptarg) = $matches;
elseif ($matches = (array)it::match('^-(\w)( [A-Z])?', $optdesc))
list($shortoptname, $shortoptarg) = $matches;
}
if ($longoptname || $shortoptname)
{
if ($longoptname && $shortoptname)
$alias[$shortoptname] = $longoptname;
$witharg[$longoptname ? $longoptname : $shortoptname] = $longoptarg || $shortoptarg;
}
}
$witharg['debug'] = true;
$mandatoryargs = array();
if ($tmp = trim(it::replace(array("\n.*" => "", "^\S+\s+\S+\s*" => "", "\[.*?\]\s*" => ""), trim($helplines))))
$mandatoryargs = preg_split('/\s+/', $tmp);
foreach (array_slice($_SERVER['argv'], 1) as $arg)
{
if ($eat)
{
if (it::match('^--?\w', $arg)) # Already next option => Missing argument?
$err = true;
else
$result[array_shift($eat)] = $arg;
}
elseif ($matches = (array)it::match('^--(\w[\w-]*)(=.*)?', $arg))
{
list($optname, $val) = $matches;
if (!isset($witharg[$optname]) || isset($val) && !$witharg[$optname])
$err = true;
else if($witharg[$optname] && !$val)
$eat[] = $optname;
else
$result[$optname] = $val ? substr($val, 1) : true;
}
else if ($letters = it::match('^-(\w+)', $arg))
{
foreach (explode("\n", trim(chunk_split($letters, 1, "\n"))) as $letter)
{
$optname = $alias[$letter] ? $alias[$letter] : $letter;
if ($witharg[$optname])
$eat[] = $optname;
else if (!isset($witharg[$optname]))
$err = true;
else
$result[$optname] = true;
}
}
elseif($mandatoryargs)
$result[strtolower(array_shift($mandatoryargs))] = $arg;
else
$result['args'][] = $arg;
}
if ($err || $eat || $result['h'] || $result['help'] || $mandatoryargs)
{
fputs(($result['h'] || $result['help'] ? STDOUT : STDERR), trim($helplines) . "\n");
exit(1);
}
if ($result['debug'])
{
foreach (preg_split('/[.,]/', $result['debug']) as $ultrad)
{
$ultravar = preg_split('/[-=:]/', $ultrad);
$GLOBALS["debug_$ultravar[0]"] = isset($ultravar[1]) ? $ultravar[1] : 1;
}
}
$GLOBALS['it_stdin']['args'] = $result['args'] ? $result['args'] : array("-");
it::_stdin_next();
return $result;
}
static function _stdin_next()
{
if ($result = $GLOBALS['it_stdin']['args'])
{
$GLOBALS['it_stdin']['filename'] = array_shift($GLOBALS['it_stdin']['args']);
$GLOBALS['it_stdin']['fd'] = ($GLOBALS['it_stdin']['filename'] == "-") ? STDIN : @fopen($GLOBALS['it_stdin']['filename'], "r");
$GLOBALS['it_stdin']['line'] = 0;
}
return $result;
}
/**
* Get one line from stdin (or files given on command line) a la Perl <>.
* Note: You need to call getopt() before using this function.
* @return Line (including newline) or false on eof
*/
static function gets()
{
do {
$result = fgets($GLOBALS['it_stdin']['fd']);
} while (($result === false) && it::_stdin_next());
$GLOBALS['it_stdin']['line']++;
return $result;
}
/**
* Output formatted and localized date
* @param format optional format (default is 2007-01-02 03:04:05).
* Other formats are "date", "datetime", "time".
* Formats can be qualified with language, e.g. "date:en"
* @param stamp optional unix timestamp (default is now).
* If it contains nondigits, it is fed to strtotime
*/
static function date($format = "", $stamp = null)
{
list($name, $language) = explode(":", $format);
if ($format && !$language)
$language = T_lang();
$formats = array(
"" => "Y-m-d H:i:s",
"date" => "d.m.Y",
"datetime" => "d.m.Y H:i",
"time" => "H:i",
"date:en" => "m/d/Y",
"datetime:en" => "m/d/Y h:ia",
"time:en" => "h:ia",
);
if (!($formatstring = $formats["$name:$language"]) && !($formatstring = $formats[$name]))
$formatstring = $format;
$stamp = !isset($stamp) ? time() : (it::match('^\d+$', $stamp) ? $stamp : strtotime($stamp));
return date($formatstring, $stamp);
}
/**
* Iterate over an array, replacing every element by expression
* @param $expression The expression to apply, may contain $k for keys and $v for values
* @param $array The array to iterate over
*/
static function map($expression, $array)
{
static $cache = array();
if (!($func = $cache[$expression]))
$func = $cache[$expression] = create_function('$k,$v', "return $expression;");
foreach ($array as $k => $v)
$result[$k] = $func($k, $v);
return (array)$result;
}
/**
* Send a mail. Expects array for Header => Content pairs with Body => for the mail body.
* @return nothing useful
*/
static function mail($p)
{
$body = $p['Body'];
unset($p['Body']);
$mail = new it_mail($p);
$mail->add_body($body);
return $mail->send();
}
/**
* Reads a file and returns it as string or in one of several formats. Two params: filename and flags
* @param $filename name of file to read or - for stdin
* @param $p['keyval'] each line of the file is one tab-separated key/value pair, return assoc array
* @param $p['lines'] return file as array of lines without newline
*/
static function file_get($filename, $p = array())
{
$data = file_get_contents($filename == "-" ? "php://stdin" : $filename);
if ($p['keyval'])
{
foreach (explode("\n", rtrim($data, "\n")) as $line)
{
$arr = explode("\t", $line, 2);
$result[$arr[0]] = $arr[1];
}
}
else if ($p['lines'])
$result = explode("\n", rtrim($data, "\n"));
else
$result = $data;
return $result;
}
/**
* Write data to a file with several serialization modes
* @param $filename name of file to write or - for stdout
* @param $data data to write
* @param $p['keyval'] $data must be an assoc array and is written as tab-separated lines
* @param $p['lines'] write array of lines, appending newline
*/
static function file_put($filename, $data, $p = array())
{
if ($p['keyval'])
$data = join("", it::map('"$k\t$v\n"', $data));
else if ($p['lines'])
$data = count((array)$data) ? join("\n", (array)$data) ."\n" : "";
return ($out = fopen($filename == "-" ? "php://stdout" : $filename, "w")) && (fputs($out, $data) === strlen($data)) && fclose($out);
}
}
?>