diff options
-rw-r--r-- | auto_prepend.php | 12 | ||||
-rw-r--r-- | it.class | 69 | ||||
-rw-r--r-- | it_dbi.class | 6 | ||||
-rw-r--r-- | it_debug.class | 6 | ||||
-rw-r--r-- | it_html.class | 6 | ||||
-rw-r--r-- | it_pipe.class | 16 | ||||
-rw-r--r-- | it_text.class | 1 | ||||
-rw-r--r-- | it_url.class | 8 | ||||
-rw-r--r-- | it_xml.class | 3 | ||||
-rwxr-xr-x | test/exec.t | 2 | ||||
-rwxr-xr-x | test/getopt.t | 3 | ||||
-rwxr-xr-x | test/it.t | 54 | ||||
-rwxr-xr-x | test/it_dbi.t | 37 | ||||
-rw-r--r-- | test/it_url.testserver.php | 2 | ||||
-rwxr-xr-x | test/it_xml.t | 4 |
15 files changed, 132 insertions, 97 deletions
diff --git a/auto_prepend.php b/auto_prepend.php index 19108fb..8d0a94e 100644 --- a/auto_prepend.php +++ b/auto_prepend.php @@ -63,8 +63,8 @@ function EDX(...$args) * Return a text in the selected language * Replaces variables of the form {var} with quoted values from argument $values * @param $label Label of text to return - * @param $language Optional value array or language string (will be sent through Q()) - * @param $values Optional value array or language string (will be sent through Q()) + * @param $language Optional value array (will be sent through Q()) or language string + * @param $values Optional value array (will be sent through Q()) or language string * @return Localized text string */ function T($label, $language = null, $values = null) @@ -177,14 +177,6 @@ function debug($text, $level=0) } /** - * Convert a htmlentities-encoded string back to normal - */ -function it_htmlentities_decode($string) -{ - return strtr($string, array_flip(get_html_translation_table(HTML_ENTITIES))); -} - -/** * Clone an object and return copy, works for all PHP versions */ function &it_clone(&$object) @@ -123,21 +123,27 @@ static function timerlog($label = '') /** * If display_errors is on or stdout is a tty, shows error in page or on stdout respectively * If display_errors is off, mails (with rate limiting) diagnostics to .diffnotice addresses or file owner or SERVER_ADMIN - * @param $p either error title or assoc array of params, see below - * @param $p['title'] error title, one line. also accepted in $p[0] (with priority). false means ignore error - * @param $p['body'] error body: multiline string or any data type (will be dumped) - * @param $p['to'] comma separated recipient list - * @param $p['id'] id of error, used with timewindow, defaults to file and line of caller - * @param $p['graceperiod'] in seconds. deprecated, see $p['timewindow'] - * @param $p['timewindow'] in secs. "5-35" means for an notice to be sent, a second error must occur 5 to 35 seconds after first err - * @param $p['blockmailid'] block mail for $p['blockmail'] seconds with same id. Default: $p['to'] - * @param $p['blockmail'] number of seconds to block mails after having sent a mail [3600] - * @param $p['backtraceskip'] number of stack levels to drop - * @param $p['skipfiles'] files to skip in backtrace - * @param $p['okstate'] give current ok label and ok state, e.g. telresult=1 for working. see failcount - * @param $p['failcount'] give number of consecutive okstate failures needed for creating an error [2] - * @param $p['omitdebuginfo'] value 1 omits long stack and var dumps, value 2 also minimal infos - * @param $p['fatal'] exit after displaying error + * + * Controlling error mail content + * @param $p either error title or assoc array of params, see below + * @param $p['title'] error title, one line. also accepted in $p[0] (with priority). false means suppress error + * @param $p['body'] error body: multiline string or any data type (will be dumped) + * @param $p['backtraceskip'] number of stack levels to drop + * @param $p['skipfiles'] files to skip in backtrace + * @param $p['omitdebuginfo'] value 1 omits long stack and var dumps, value 2 also minimal infos + * + * Controlling the sending of mails + * @param $p['to'] comma separated recipient list + * @param $p['timewindow'] in secs. "5-35" means for an mail to be sent, another err must occur 5 to 35 seconds after first one + * @param $p['id'] id of error, used with timewindow, defaults to file and line of caller + * @param $p['blockmailid'] block mail for $p['blockmail'] seconds with same id. Default: $p['to'] + * @param $p['blockmail'] number of seconds to block mails after having sent a mail [3600] + * @param $p['okstate'] give current ok label and ok state, e.g. telresult=1 for working. see failcount + * @param $p['failcount'] give number of consecutive okstate failures needed for creating an error [2] + * @param $p['fatal'] exit after displaying error + * @param $p['graceperiod'] in seconds. DEPRECATED, see $p['timewindow'] + * Use debug param 'verboseerrors' to include $p['body'] when interactive + * * @return always null (so users can return it::error() in error cases) * * TIMEWINDOW 5-35 (x = error occurs, no mail; m = error occurs, mail sent) @@ -164,6 +170,7 @@ static function error($p = array(), $extra = null) $p['title'] = $p[0] ?: $p['title']; # handle 'it_error' => "oops" that was cast to array on the way $p['title'] = grapheme_substr($p['title'], 0, 2000) ?: substr($p['title'], 0, 2000); + # support for errlike() in tests $GLOBALS['ULTRAERROR'] = $p['title']; if ($GLOBALS['ULTRANOERRORS']) return null; @@ -391,20 +398,6 @@ static function servertype($pattern) /** - * 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, - utf8_decode( 'ÇéâàåçêëèïîìÅÉôòûùÿøØáíóúñÑÁÂÀãÃÊËÈÍÎÏÓÔõÕÚÛÙýÝ'), - 'CeaaaceeeiiiAEoouuyooaiounNAAAaAEEEIIIOOoOUUUyY'), - array("\xe4" => 'ae', "\xf6" => 'oe', "\xfc" => 'ue', "\xc4" => 'Ae', "\xd6" => 'Oe', "\xdc" => 'Ue', "\xe6" => 'ae', "\xc6" => 'Ae', "\xdf" => 'ss')); -} - - -/** * Check whether an IP adress lies within a given range. Supports IPv4 and IPv6 * @param $ip IP address (192.168.42.123) * @param $cidrs IP range in CIDR notation (192.168.42.64/26) or array of ranges @@ -601,9 +594,9 @@ static function any2utf8($value, $errprefix = "") else if (is_string($value)) { if (grapheme_strlen($value) === null) - list($value, $error) = array(utf8_encode($value), utf8_encode("incorrect utf8-encoding. input=$value")); + list($value, $error) = array(it::utf8_encode($value), it::utf8_encode("incorrect utf8-encoding. input=$value")); if (preg_match('/\xc3[\x82\x83]\xc2[\x82\x83\xbc\xa9\xa4\xb6\xa8\xa2\xa0\xb4\xaa\xa7\x84\xab\xae\x9c\xaf\x96\xb2\xbb\xb9\x9f]/', $value)) - list($value, $error) = array(it::any2utf8(preg_replace_callback('/\xc3[\x82\x83]\xc2[\x82\x83\xbc\xa9\xa4\xb6\xa8\xa2\xa0\xb4\xaa\xa7\x84\xab\xae\x9c\xaf\x96\xb2\xbb\xb9\x9f]/', function($m) {return utf8_decode($m[0]);}, $value)), $errprefix ? "double utf8-encoding. input=$value" : ""); + list($value, $error) = array(it::any2utf8(preg_replace_callback('/\xc3[\x82\x83]\xc2[\x82\x83\xbc\xa9\xa4\xb6\xa8\xa2\xa0\xb4\xaa\xa7\x84\xab\xae\x9c\xaf\x96\xb2\xbb\xb9\x9f]/', function($m) {return it::utf8_decode($m[0]);}, $value)), $errprefix ? "double utf8-encoding. input=$value" : ""); if (preg_match('/\xef\xb7[\x90-\xaf]|\xef\xbf[\xbe\xbf]/', $value)) list($value, $error) = array(preg_replace('/\xef\xb7[\x90-\xaf]|\xef\xbf[\xbe\xbf]/', " ", $value), "forbidden utf-8 character. input=$value"); $value = preg_replace('/\xc2\xad/', '', $value); # Kill invisible soft hyphens @@ -909,7 +902,7 @@ static function getopt($usage, $p = array()) foreach(explode("\n", trim($usage)) as $usageline) { $shortoptname = $shortoptarg = $longoptname = $longoptarg = ""; - foreach (explode(',', $usageline) as $optdesc) + foreach (explode(',', $usageline, 2) as $optdesc) { $optdesc = trim($optdesc); if ($matches = (array)it::match('^--(\w[\w-]*)(=[A-Z])?', $optdesc)) @@ -1058,7 +1051,7 @@ static function date($format = "", $stamp = null) if (!isset($stamp)) $stamp = it::time(); else if (is_string($stamp) && !ctype_digit($stamp)) - $stamp = strtotime($stamp, it::time()); + $stamp = strtotime(it::replace(['\s(--|\+\+)\s*(\d)' => ' +\2', '\s(\+-|-\+)\s*(\d)' => ' -\2'], $stamp), it::time()); list($name, $language) = explode(":", $format); @@ -1383,4 +1376,14 @@ static function request_body() return it::any2utf8(it::file_get_contents('php://input')); } +static function utf8_decode($utf8) +{ + return UConverter::transcode($utf8, 'ISO-8859-1', 'UTF8', ['to_subst' => '?']); +} + +static function utf8_encode($latin1) +{ + return UConverter::transcode($latin1, 'UTF8', 'ISO-8859-1'); +} + } diff --git a/it_dbi.class b/it_dbi.class index b365f59..786fb90 100644 --- a/it_dbi.class +++ b/it_dbi.class @@ -19,7 +19,8 @@ ** dbi.class - UltraFlexible Database Interface 3000 */ -class it_dbi +#[AllowDynamicProperties] +class it_dbi implements Iterator { static $_global_key = 'it_dbi'; # $GLOBAL key to use for singleton @@ -176,9 +177,8 @@ static function createclass($p) if (substr($classname, 0, 4) != 'PMA_') # It is designed behaviour that an error is generated if this class already exists! { - $interface = function_exists("interface_exists") && interface_exists("Iterator", false) ? "implements Iterator" : ""; $parentname = static::$_global_key; - $code = "class $classname extends $parentname $interface + $code = "class $classname extends $parentname { function __construct(\$query = null, ...\$args) { diff --git a/it_debug.class b/it_debug.class index 3361933..91342bd 100644 --- a/it_debug.class +++ b/it_debug.class @@ -170,13 +170,13 @@ static function dump($args, $p = []) $var = array_shift($argnames); $item = gettype($arg) == 'resource' || is_array($arg) && isset($arg['GLOBALS']) || is_object($arg) && is_a($arg, "SimpleXMLElement") ? trim(print_r($arg, true)) : trim(var_export($arg, true)); - # Replace PHP 5 var_export object representation by old style + # Replace PHP 5+ var_export object representation by old readable style while (preg_match("#(.*\b)(\w+)::__set_state\(array\(([^()]+)\)\)(.*)#s", $item, $regs) && ++$iterations < 100) { list (, $head, $classname, $values, $tail) = $regs; $classname = strtolower($classname); $values = preg_replace("#'(\w+)' =>\s*([^\n]+),#", 'var \$$1 = $2;', $values); - $item = $head . "class $classname { $values }$tail"; + $item = rtrim($head, '\\') . "class $classname {" . $values . "}" . $tail; } $item = preg_replace_callback('/\b(1[2-9]\d\d\d\d\d\d\d\d)\b(.*)/', function($m) { return $m[1] . $m[2] . " # " . date('Y-m-d H:i:s', $m[1]); }, $item); @@ -211,7 +211,7 @@ static function dump($args, $p = []) if ($p['html']) return "<pre title='$title' style='color:#c00; text-align:left; background:white; border-bottom:1px solid #ddd; z-index:9999; position:relative; line-height:1'>$r</pre>\n"; else - return $ansiok || $p['short'] ? "$r\n" : "\xe2\x96\x88\xe2\x96\x8c" . preg_replace("#\n#", "\n\xe2\x96\x88 ", "$r") . "\n"; + return $ansiok || $p['short'] || EDC('edplain') ? "$r\n" : "\u{2588}\u{258c}" . preg_replace("#\n#", "\n\u{2588} ", "$r") . "\n"; } /** diff --git a/it_html.class b/it_html.class index d7e1db3..93057bd 100644 --- a/it_html.class +++ b/it_html.class @@ -59,6 +59,10 @@ class it_html var $p; # constructor params plus defaults + var $doctypes; + var $alltags; + var $hasnonewline; + /** * Create a HTML object and global functions for all methods (exlcluding * methods starting with '_') in this class plus the default tags (see below). @@ -507,7 +511,7 @@ static function U(...$args) list($u['path'], $u['query']) = explode("?", $base, 2); } - $u['host'] = preg_match('/[^-_.0-9a-z]/i', $u['host']) && function_exists('idn_to_ascii') && ($idnahost = idn_to_ascii($GLOBALS['it_html']->p['charset'] == "iso-8859-1" ? utf8_encode($u['host']) : $u['host'])) ? $idnahost : $u['host']; # Punycode hostname to include into webpage + $u['host'] = preg_match('/[^-_.0-9a-z]/i', $u['host']) && function_exists('idn_to_ascii') && ($idnahost = idn_to_ascii($GLOBALS['it_html']->p['charset'] == "iso-8859-1" ? it::utf8_encode($u['host']) : $u['host'])) ? $idnahost : $u['host']; # Punycode hostname to include into webpage $u['host'] = preg_replace_callback('/[^-_.0-9a-z\x80-\xff]/i', function($m) { return rawurlencode($m[0]); }, $u['host']); # Encode garbage chars in host # handle scheme, user (urlencoded), password, host diff --git a/it_pipe.class b/it_pipe.class index edc6dd8..0ffb7af 100644 --- a/it_pipe.class +++ b/it_pipe.class @@ -101,22 +101,6 @@ function filter($expr) } /** - * Convert pipe from utf8 to iso-latin - */ -function latin() -{ - return $this->map('utf8_decode($v)'); -} - -/** - * Convert pipe from iso-latin to uft8 - */ -function utf8() -{ - return $this->map('utf8_encode($v)'); -} - -/** * Select cols from tab-separated cols in each line and tab-joins them again. Key order relevant. */ function cut($picks) diff --git a/it_text.class b/it_text.class index 5e33395..61a6006 100644 --- a/it_text.class +++ b/it_text.class @@ -25,6 +25,7 @@ class it_text var $languages_available = array(); # Available languages var $statictext = array(); # Text array, read from php file on init var $label_to_service = array(); # which label belongs to which service - only used for debug parameter texts + var $allowedfuncs; var $p; # Constructor Parameters /** diff --git a/it_url.class b/it_url.class index b2dad3c..e2cd993 100644 --- a/it_url.class +++ b/it_url.class @@ -39,6 +39,7 @@ class it_url var $redir = 0; /* Redirect count */ var $header; /* http header */ var $errstr; /* request error string */ + var $curlinfo; static $retryable = "^(5..)$"; @@ -573,6 +574,7 @@ static function get_cache_filename($p) * @param $p['cachedir'] directory to store cache files in, @see get_cache_dir * @param $p['timeout'] timeout in seconds, default 10. fractions allowed * @param $p['maxage'] maximum age of cache entries in seconds, default 23 hours. id mandatory if given + * @param $p['randomexpire'] chance to randomly expunge an entry, 0..1 * @param $p['cleanbefore'] maximum seconds since midnight when initiating expire, default 10800 * @param $p['preprocess'] callback function (or array for methods) to change received file or array('function' => ..., 'in' => $src, 'out' => $dst, ...) with callback function plus args * @param $p['safety'] DEPRECATED. see $p['it_error'] @@ -599,7 +601,7 @@ static function get_cache($p = array()) if (!is_writable(dirname($path))) it::error("parent dir not writable: " . trim(it::exec('ls -ld {dir} 2>&1', ['dir' => dirname($path)]))); - if (($filemtime = it_url::_expired($path, $p['maxage'])) || ($p['returnheaders'] && !file_exists(("$path.headers")))) # Outdated(non-zero int) or non-existant(true)? + if (($filemtime = it_url::_expired($path, $p['maxage'], $p['randomexpire'])) || ($p['returnheaders'] && !file_exists(("$path.headers")))) # Outdated(non-zero int) or non-existant(true)? { $fileexists = $filemtime !== true; @@ -736,11 +738,11 @@ static function get_cache_contents($p) * @param $maxage Maximum age of file in seconds * @return Not expired: false | Non-existant file: true | Timestamp of expired file */ -static function _expired($path, $maxage) +static function _expired($path, $maxage, $randomexpire = 0) { if ($result = EDC('nocache') ? false : @filemtime($path)) { - if (time() - $result > $maxage) + if (time() - $result > $maxage || rand(0, 100000) <= $randomexpire * 100000) EDC('getcache', "expired", $maxage, $path); else $result = false; diff --git a/it_xml.class b/it_xml.class index 57f4555..7765054 100644 --- a/it_xml.class +++ b/it_xml.class @@ -19,6 +19,7 @@ ** it_xml.class - XML parser / object factory */ +#[AllowDynamicProperties] class it_xml { /* @@ -140,7 +141,7 @@ function _sanitize($xmldata, $isutf8 = null) # Encode non-utf8 characters in a string, leave utf8 alone static function _utf8_fix($str) { - return preg_match('/^([\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf][\x80-\xbf]|[\xf0-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf])$/', $str) ? $str : utf8_encode($str); + return preg_match('/^([\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf][\x80-\xbf]|[\xf0-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf])$/', $str) ? $str : it::utf8_encode($str); } function consume(/* $p */) diff --git a/test/exec.t b/test/exec.t index 09e16da..04850fc 100755 --- a/test/exec.t +++ b/test/exec.t @@ -70,7 +70,7 @@ foreach (["", "C", "de_CH", "de_CH.utf8"] as $locale) setlocale(LC_ALL, $locale); $arg = "preüpost"; if (it::match('utf8', $locale)) - $arg = utf8_encode($arg); + $arg = it::any2utf8($arg); is(it::exec("echo " . $arg), $arg . "\n", "exec with umlaut (locale '$locale')"); is(it::exec("echo {arg}", ['arg' => $arg]), $arg . "\n", "exec with argument and umlaut (locale '$locale')"); } diff --git a/test/getopt.t b/test/getopt.t index 77e52ae..380d0fd 100755 --- a/test/getopt.t +++ b/test/getopt.t @@ -9,6 +9,7 @@ $GLOBALS['usage'] = "Usage: doesnotexist.php [OPTIONS] POSITIONAL [VARARGS] -a,--argument=ARG the arg argument -d,--default=ARG an argument with default [defäult] -0,--zero testworthy shortarg + -c,--comma option with comma and, -minus in the description "; function getopt_ok($argv, $exp, $name) @@ -26,6 +27,7 @@ foreach (["" => "blah gnaber", " (umlaute)" => "pre üäpost"] as $variant => $t getopt_ok([$testarg, "--argument=$testarg"], $exp, "Long version with equal" . $variant); } +getopt_ok(['posargs', '-c'], ['args' => [], 'positional' => 'posargs', 'comma' => true, 'default' => 'defäult'], "comma and minus in description doesn't break shortopt"); $exp = ['args' => [], 'positional' => 'posarg', 'zero' => true, 'default' => 'defäult']; getopt_ok(['posarg', '-0'], $exp, 'short argument -0 without value'); getopt_ok(['posarg', '--zero'], $exp, 'long argument --zero without value'); @@ -33,6 +35,7 @@ getopt_ok(['posarg', '-0', 'vararg'], ['args' => ['vararg']] + $exp, "additional getopt_ok(['posarg', '--zero', 'vararg'], ['args' => ['vararg']] + $exp, "additional value after long argument --zero"); fclose(STDERR); +getopt_ok(['posargs', '-m'], false, "comma and minus in description doesn't break shortopt 2"); getopt_ok(['posarg', '--unknown'], false, "Unknown long named argument fails"); getopt_ok(['posarg', '-u'], false, "Unknown short named argument fails"); getopt_ok([], false, "Missing positional argument fails"); @@ -221,15 +221,15 @@ _match( ); _match( - utf8_decode('ö'), utf8_decode('Ö'), - utf8_decode('Ö'), + it::utf8_decode('ö'), it::utf8_decode('Ö'), + it::utf8_decode('Ö'), 'match umlaute in de_CH.latin1 case insensitive', ['utf8' => false] ); _match( - utf8_decode('aöBÜ'), utf8_decode('AÖbü'), - utf8_decode('AÖbü'), + it::utf8_decode('aöBÜ'), it::utf8_decode('AÖbü'), + it::utf8_decode('AÖbü'), "match umlaute with non-utf-8 override in p", ['utf8' => false] ); @@ -270,6 +270,12 @@ _match( ['all' => 1, 'pattern_order' => 1] ); +_match( + preg_quote('a/b!c(d\\e{f$g^h|i??**++'), 'faa/b!c(d\\e{f$g^h|i??**++ar', + 'a/b!c(d\\e{f$g^h|i??**++', + 'match with preg_quote of special characters in pattern' +); + ini_set('default_charset', 'iso-8859-1'); _match( 'aöBÜ', "AÖbü", @@ -388,7 +394,19 @@ _time("152715", it::date('', '15:27:15')); _time("yesterday", it::date('', 'yesterday')); is(it::date('date', '2011-10-25'), '25.10.2011', 'parse date string with strtotime'); -is(it::date('date', '2011-10-25 + 3 days'), '28.10.2011', 'some date arithmetic'); +is(it::date('date', '2011-10-25 + 3 days'), '28.10.2011', 'date arithmetic (add)'); +is(it::date('date', '2011-10-25 - 3 days'), '22.10.2011', 'date arithmetic (subtract)'); +is(it::date('date', '2011-10-25 +-3 days'), '22.10.2011', 'date arithmetic (negative value)'); +is(it::date('date', '2011-10-25 --3 days'), '28.10.2011', 'date arithmetic (double negative)'); +is(it::date('date', '2011-10-25 ++ 3 days'), '28.10.2011', 'date arithmetic (double positive with space)'); +is(it::date('date', '2011-10-25 -+ 3 days'), '22.10.2011', 'date arithmetic (negative positive with space)'); +is(it::date('datetime', '2011-10-25 +12 hours +30 minutes'), '25.10.2011 12:30', 'datetime arithmetic (hours minutes)'); +is(it::date('datetime', '2011-10-25 +24 hours +30 minutes'), '26.10.2011 00:30', 'datetime arithmetic (hours minutes over midnight)'); +is(it::date('datetime', '2011-10-25 8:00 +10 minutes'), '25.10.2011 08:10', 'time arithmetic (add)'); +is(it::date('datetime', '2011-10-25 8:00 -10 minutes'), '25.10.2011 07:50', 'time arithmetic (subtract)'); +is(it::date('datetime', '2011-10-25 8:00 +-10 minutes'), '25.10.2011 07:50', 'time arithmetic (negative value)'); +is(it::date('datetime', '2011-10-25 8:00 --10 minutes'), '25.10.2011 08:10', 'time arithmetic (double negative)'); +is(it::date('datetime', '2011-10-25 --1 month --3 days 8:00 +- 4 hours +-10 minutes'), '28.11.2011 03:50', 'date and time arithmetic (multiple signs)'); is(it::date('datetime', it::time()), it::date('datetime'), 'recognize int as timestamp'); is(it::date('datetime', it::time()*1.0), it::date('datetime'), 'recognize float as timestamp'); is(it::date('datetime', it::time() . ''), it::date('datetime'), 'recognize digit string as timestamp'); @@ -419,32 +437,32 @@ is(grapheme_strlen("\xc1"), null, "need grapheme_strlen side effect for any2utf8 is(it::any2utf8('Meier'), 'Meier', "it::any2utf8 ascii input"); is(it::any2utf8('Müller'), 'Müller', "it::any2utf8 utf8 input"); is(it::any2utf8('Aslı'), 'Aslı', "it::any2utf8 utf8 non-latin1 input"); -is(it::any2utf8(utf8_decode('Müller')), 'Müller', "it::any2utf8 latin1 input"); +is(it::any2utf8(it::utf8_decode('Müller')), 'Müller', "it::any2utf8 latin1 input"); is(it::any2utf8( ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'), # omit soft hyphen cause we filter it ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ', "it::any2utf8 utf8 input (exhaustive alphabet)"); is(it::any2utf8( - utf8_decode(' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ')), + it::utf8_decode(' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ')), ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ', "it::any2utf8 latin1 input (exhaustive alphabet)"); -is(it::any2utf8(utf8_encode("ü")), "ü", "it::any2utf8 double encoding"); +is(it::any2utf8(it::utf8_encode("ü")), "ü", "it::any2utf8 double encoding"); is(it::any2utf8("Meier"), "Meier", "it::any2utf8 ascii"); is(it::any2utf8("Müller"), "Müller", "it::any2utf8 utf-8 latin1"); is(it::any2utf8("Aslı"), "Aslı", "it::any2utf8 utf-8 non-latin1"); is(it::any2utf8("é»"), "é»", "it::any2utf8 utf-8 latin1 special combination"); -is(it::any2utf8(utf8_encode("Müller")), "Müller", "it::any2utf8 doubly encoded utf8"); -is(it::any2utf8(utf8_encode(utf8_encode("Müller"))), "Müller", "it::any2utf8 triply encoded utf8"); -is(it::any2utf8(utf8_decode("Müller")), "Müller", "it::any2utf8 incorrectly encoded latin1"); +is(it::any2utf8(it::utf8_encode("Müller")), "Müller", "it::any2utf8 doubly encoded utf8"); +is(it::any2utf8(it::utf8_encode(it::utf8_encode("Müller"))), "Müller", "it::any2utf8 triply encoded utf8"); +is(it::any2utf8(it::utf8_decode("Müller")), "Müller", "it::any2utf8 incorrectly encoded latin1"); is(it::any2utf8("a💚b"), "a💚b", "it::any2utf8 correctly handles 4-byte utf-8 character GREEN HEART"); -is(it::any2utf8(["foo", utf8_decode("bär")]), ["foo", "bär"], "any2utf8 on arrays"); -is(it::any2utf8(["foo", [utf8_decode("bär")]]), ["foo", ["bär"]], "any2utf8 on recursive arrays"); +is(it::any2utf8(["foo", it::utf8_decode("bär")]), ["foo", "bär"], "any2utf8 on arrays"); +is(it::any2utf8(["foo", [it::utf8_decode("bär")]]), ["foo", ["bär"]], "any2utf8 on recursive arrays"); is(it::any2utf8([1, true, false, null]), [1, true, false, null], "any2utf8 should leave types alone"); -is(it::any2utf8([utf8_decode('Müller') => utf8_decode('Müller')]), ['Müller' => 'Müller'], "it::any2utf8 latin1 keys"); +is(it::any2utf8([it::utf8_decode('Müller') => it::utf8_decode('Müller')]), ['Müller' => 'Müller'], "it::any2utf8 latin1 keys"); is(it::any2utf8("\xc2\xad"), "", "it::any2utf8 remove soft hyphens"); @@ -532,10 +550,10 @@ it::file_put($tmpfile, "bb"); is(it::file_get($tmpfile), "bb"); unlink($tmpfile); -requesturi(utf8_decode("lüönd"), "lüönd"); -requesturi(utf8_decode("ü").utf8_encode("ü"), "üü"); -requesturi(utf8_encode("müller"), "müller"); -requesturi(utf8_encode(utf8_encode("müller")), "müller"); +requesturi(it::utf8_decode("lüönd"), "lüönd"); +requesturi(it::utf8_decode("ü").it::utf8_encode("ü"), "üü"); +requesturi(it::utf8_encode("müller"), "müller"); +requesturi(it::utf8_encode(it::utf8_encode("müller")), "müller"); requesturi("I 💚 Nü York", "I 💚 Nü York"); function requesturi($teststring, $expect) diff --git a/test/it_dbi.t b/test/it_dbi.t index 2b5b0c5..2a601b6 100755 --- a/test/it_dbi.t +++ b/test/it_dbi.t @@ -27,6 +27,7 @@ $dbi->query("create temporary table it_dbi_test ( $autoid, x int, y float, + z bigint, foo varchar(42), flag boolean, $dyncols, @@ -44,9 +45,9 @@ try { is(it::match('syntax', $e->getMessage()), 'syntax', "Syntax error triggers error"); } -$record->insert(['x' => 42, 'y' => 0.0001, 'foo' => null, 'flag' => 1]); -$record->insert(['foo' => "bar", 'y' => 1e-10, 'flag' => 0]); -$record->insert(['x' => 64738, 'y' => 1, 'foo' => "q'uux"]); +$record->insert(['x' => 42, 'y' => 0.0001, 'z' => 0, 'foo' => null, 'flag' => 1]); +$record->insert(['foo' => "bar", 'y' => 1e-10, 'z' => 0, 'flag' => 0]); +$record->insert(['x' => 64738, 'y' => 1, 'z' => 0, 'foo' => "q'uux"]); is( $record->ID, @@ -192,6 +193,22 @@ is( "update with function" ); +$value = pow(2, 24) + 1; +$record->update(['x' => $value]); +is($record->x, $value, "update 32bit integer without float representation"); +is($record->select(['x' => $value]), 1, "select 32bit integer without float representation"); + +$value = pow(2, 53) + 1; +$record->update(['z' => $value]); +is($record->z, $value, "update 64bit integer without double representation"); +is($record->select(['z' => $value]), 1, "select 64bit integer without double representation"); + +$record->update(['foo' => "10", 'x' => 10]); +is($record->select(['x <' => 9]), 0, "always use integer comparision for int field"); +is($record->select(['x <' => '9']), 0, "always use integer comparision for int field"); +is($record->select(['foo <' => 9]), 1, "always use string comparision for varchar field"); +is($record->select(['foo <' => '9']), 1, "always use string comparision for varchar field"); + $record->update(['foo' => "00"]); $rand = $record->x; is ( @@ -253,6 +270,14 @@ foreach (new it_dbi_test as $id => $record) is($count, 3, "Iterator without select"); $count = 0; +foreach (new it_dbi($db + ['table' => 'it_dbi_test']) as $id => $record) +{ + $count++; + is($record->_key, $id, "Iterator id $id (it_dbi object)"); +} +is($count, 3, "Iterator without select (it_dbi_object)"); + +$count = 0; foreach (new it_dbi_test(['foo <>' => ""]) as $id => $record) { $count++; @@ -489,14 +514,14 @@ $record->delete(['WHERE TRUE' ]); $record->upsert(['ID' => 1, 'x' => 42, 'y' => 2.5, 'foo' => ['value' => "a"], 'flag' => 0]); $record->read(1); is($record->x, 42, 'value of x after reading with _read_postprocess'); -is($record->_data, ["ID" => 1, "x" => 42, 'y' => 2.5, 'foo' => '{"value":"a"}', 'flag' => 0], 'data after reading with _read_postprocess'); +is($record->_data, ["ID" => 1, "x" => 42, 'y' => 2.5, 'z' => NULL, 'foo' => '{"value":"a"}', 'flag' => 0], 'data after reading with _read_postprocess'); is($record->foo, ['value' => "a"], 'field after reading with _read_postprocess'); $record2 = new it_dbi_test; $record2->read(1); is($record2->x, 43, 'raw value of x after writing with _write_preprocess'); -is($record2->_data, ["ID" => 1, "x" => 43, 'y' => 2.5, 'foo' => '{"value":"a"}', 'flag' => 0], 'raw data after writing with _write_preprocess'); +is($record2->_data, ["ID" => 1, "x" => 43, 'y' => 2.5, 'z' => NULL, 'foo' => '{"value":"a"}', 'flag' => 0], 'raw data after writing with _write_preprocess'); $record->update(['foo' => ['value' => "b"]]); -is($record->_data, ["ID" => 1, "x" => 42, 'y' => 2.5, 'foo' => '{"value":"b"}', 'flag' => 0], 'raw data after updating with _write_preprocess'); +is($record->_data, ["ID" => 1, "x" => 42, 'y' => 2.5, 'z' => NULL, 'foo' => '{"value":"b"}', 'flag' => 0], 'raw data after updating with _write_preprocess'); is($record->foo, ['value' => "b"], 'field data after updating with _write_preprocess'); diff --git a/test/it_url.testserver.php b/test/it_url.testserver.php index 2d1bca4..242ccf6 100644 --- a/test/it_url.testserver.php +++ b/test/it_url.testserver.php @@ -42,6 +42,8 @@ switch ($_SERVER['PHP_SELF']) break; case "/long_sleep": + echo "a\n"; + flush(); sleep(6); echo 'Testserver output after long sleep'; break; diff --git a/test/it_xml.t b/test/it_xml.t index e21f052..88a5cf4 100755 --- a/test/it_xml.t +++ b/test/it_xml.t @@ -63,7 +63,7 @@ _match( _match( '<foo>x ü y</foo>', - utf8_decode('foo Object ( [val] => x ü y ) '), + it::utf8_decode('foo Object ( [val] => x ü y ) '), 'Manual encoding override', "", ['encoding' => "iso-8859-1"] @@ -77,7 +77,7 @@ _match( _match( '<foo>&amp; <a> &amp; <b> &amp; <c> ü</foo>', - utf8_decode('foo Object ( [val] => & <a> & <b> & <c> ü ) '), + it::utf8_decode('foo Object ( [val] => & <a> & <b> & <c> ü ) '), 'Predecode illegal entities while keeping properly encoded ones (iso-8859-1)', "", ['encoding' => "iso-8859-1"] |