From 06e3c5c0304e300bcecc70b29c75becb39988fd9 Mon Sep 17 00:00:00 2001
From: moodler \$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']} Session Replace: '.$ADODB_SESS_CONN->ErrorMsg().' \$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']} \$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']} Session Replace: '.$ADODB_SESS_CONN->ErrorMsg().' $msg \$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}Session Error: PHP.INI setting session.gc_maxlifetimenot set: $ADODB_SESS_LIFE
"; */
- $ADODB_SESS_LIFE=1440;
-}
-
-function adodb_sess_open($save_path, $session_name)
-{
-GLOBAL $ADODB_SESSION_CONNECT,
- $ADODB_SESSION_DRIVER,
- $ADODB_SESSION_USER,
- $ADODB_SESSION_PWD,
- $ADODB_SESSION_DB,
- $ADODB_SESS_CONN,
- $ADODB_SESS_DEBUG;
-
- $ADODB_SESS_INSERT = false;
-
- if (isset($ADODB_SESS_CONN)) return true;
-
- $ADODB_SESS_CONN = ADONewConnection($ADODB_SESSION_DRIVER);
- if (!empty($ADODB_SESS_DEBUG)) {
- $ADODB_SESS_CONN->debug = true;
- print" conn=$ADODB_SESSION_CONNECT user=$ADODB_SESSION_USER pwd=$ADODB_SESSION_PWD db=$ADODB_SESSION_DB ";
- }
- return $ADODB_SESS_CONN->PConnect($ADODB_SESSION_CONNECT,
- $ADODB_SESSION_USER,$ADODB_SESSION_PWD,$ADODB_SESSION_DB);
-
-}
-
-function adodb_sess_close()
-{
-global $ADODB_SESS_CONN;
-
- if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close();
- return true;
-}
-
-function adodb_sess_read($key)
-{
-$Crypt = new MD5Crypt;
-global $ADODB_SESS_CONN,$ADODB_SESS_INSERT,$ADODB_SESSION_TBL;
- $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time());
- if ($rs) {
- if ($rs->EOF) {
- $ADODB_SESS_INSERT = true;
- $v = '';
- } else {
- /* Decrypt session data */
- $v = rawurldecode($Crypt->Decrypt(reset($rs->fields), ADODB_Session_Key()));
- }
- $rs->Close();
- return $v;
- }
- else $ADODB_SESS_INSERT = true;
-
- return '';
-}
-
-function adodb_sess_write($key, $val)
-{
-$Crypt = new MD5Crypt;
- global $ADODB_SESS_INSERT,$ADODB_SESS_CONN, $ADODB_SESS_LIFE, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY;
-
- $expiry = time() + $ADODB_SESS_LIFE;
-
- /* encrypt session data.. */
- $val = $Crypt->Encrypt(rawurlencode($val), ADODB_Session_Key());
-
- $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val);
- if ($ADODB_SESSION_EXPIRE_NOTIFY) {
- $var = reset($ADODB_SESSION_EXPIRE_NOTIFY);
- global $$var;
- $arr['expireref'] = $$var;
- }
- $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,
- $arr,
- 'sesskey',$autoQuote = true);
-
- if (!$rs) {
- ADOConnection::outp( 'Session Error: PHP.INI setting session.gc_maxlifetimenot set: $ADODB_SESS_LIFE
";
+ $ADODB_SESS_LIFE=1440;
+}
+
+function adodb_sess_open($save_path, $session_name)
+{
+GLOBAL $ADODB_SESSION_CONNECT,
+ $ADODB_SESSION_DRIVER,
+ $ADODB_SESSION_USER,
+ $ADODB_SESSION_PWD,
+ $ADODB_SESSION_DB,
+ $ADODB_SESS_CONN,
+ $ADODB_SESS_DEBUG;
+
+ $ADODB_SESS_INSERT = false;
+
+ if (isset($ADODB_SESS_CONN)) return true;
+
+ $ADODB_SESS_CONN = ADONewConnection($ADODB_SESSION_DRIVER);
+ if (!empty($ADODB_SESS_DEBUG)) {
+ $ADODB_SESS_CONN->debug = true;
+ print" conn=$ADODB_SESSION_CONNECT user=$ADODB_SESSION_USER pwd=$ADODB_SESSION_PWD db=$ADODB_SESSION_DB ";
+ }
+ return $ADODB_SESS_CONN->PConnect($ADODB_SESSION_CONNECT,
+ $ADODB_SESSION_USER,$ADODB_SESSION_PWD,$ADODB_SESSION_DB);
+
+}
+
+function adodb_sess_close()
+{
+global $ADODB_SESS_CONN;
+
+ if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close();
+ return true;
+}
+
+function adodb_sess_read($key)
+{
+$Crypt = new MD5Crypt;
+global $ADODB_SESS_CONN,$ADODB_SESS_INSERT,$ADODB_SESSION_TBL;
+ $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time());
+ if ($rs) {
+ if ($rs->EOF) {
+ $ADODB_SESS_INSERT = true;
+ $v = '';
+ } else {
+ // Decrypt session data
+ $v = rawurldecode($Crypt->Decrypt(reset($rs->fields), ADODB_Session_Key()));
+ }
+ $rs->Close();
+ return $v;
+ }
+ else $ADODB_SESS_INSERT = true;
+
+ return '';
+}
+
+function adodb_sess_write($key, $val)
+{
+$Crypt = new MD5Crypt;
+ global $ADODB_SESS_INSERT,$ADODB_SESS_CONN, $ADODB_SESS_LIFE, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY;
+
+ $expiry = time() + $ADODB_SESS_LIFE;
+
+ // encrypt session data..
+ $val = $Crypt->Encrypt(rawurlencode($val), ADODB_Session_Key());
+
+ $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val);
+ if ($ADODB_SESSION_EXPIRE_NOTIFY) {
+ $var = reset($ADODB_SESSION_EXPIRE_NOTIFY);
+ global $$var;
+ $arr['expireref'] = $$var;
+ }
+ $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,
+ $arr,
+ 'sesskey',$autoQuote = true);
+
+ if (!$rs) {
+ ADOConnection::outp( '
"; -print_r($a); -print ""; -} -/* Lens_ParseTest(); */ - -/** - Parse arguments, treat "text" (text) and 'text' as quotation marks. - To escape, use "" or '' or )) - - @param endstmtchar Character that indicates end of statement - @param tokenchars Include the following characters in tokens apart from A-Z and 0-9 - @returns 2 dimensional array containing parsed tokens. -*/ -function Lens_ParseArgs($args,$endstmtchar=',',$tokenchars='_.-') -{ - $pos = 0; - $intoken = false; - $stmtno = 0; - $endquote = false; - $tokens = array(); - $tokens[$stmtno] = array(); - $max = strlen($args); - $quoted = false; - - while ($pos < $max) { - $ch = substr($args,$pos,1); - switch($ch) { - case ' ': - case "\t": - case "\n": - case "\r": - if (!$quoted) { - if ($intoken) { - $intoken = false; - $tokens[$stmtno][] = implode('',$tokarr); - } - break; - } - - $tokarr[] = $ch; - break; - - case '(': - case ')': - case '"': - case "'": - - if ($intoken) { - if (empty($endquote)) { - $tokens[$stmtno][] = implode('',$tokarr); - if ($ch == '(') $endquote = ')'; - else $endquote = $ch; - $quoted = true; - $intoken = true; - $tokarr = array(); - } else if ($endquote == $ch) { - $ch2 = substr($args,$pos+1,1); - if ($ch2 == $endquote) { - $pos += 1; - $tokarr[] = $ch2; - } else { - $quoted = false; - $intoken = false; - $tokens[$stmtno][] = implode('',$tokarr); - $endquote = ''; - } - } else - $tokarr[] = $ch; - - }else { - - if ($ch == '(') $endquote = ')'; - else $endquote = $ch; - $quoted = true; - $intoken = true; - $tokarr = array(); - } - break; - - default: - - if (!$intoken) { - if ($ch == $endstmtchar) { - $stmtno += 1; - $tokens[$stmtno] = array(); - break; - } - - $intoken = true; - $quoted = false; - $endquote = false; - $tokarr = array(); - - } - - if ($quoted) $tokarr[] = $ch; - else if (ctype_alnum($ch) || strpos($tokenchars,$ch) !== false) $tokarr[] = $ch; - else { - if ($ch == $endstmtchar) { - $tokens[$stmtno][] = implode('',$tokarr); - $stmtno += 1; - $tokens[$stmtno] = array(); - $intoken = false; - $tokarr = array(); - break; - } - $tokens[$stmtno][] = implode('',$tokarr); - $tokens[$stmtno][] = $ch; - $intoken = false; - } - } - $pos += 1; - } - - return $tokens; -} - - -class ADODB_DataDict { - var $connection; - var $debug = false; - var $dropTable = "DROP TABLE %s"; - var $addCol = ' ADD'; - var $alterCol = ' ALTER COLUMN'; - var $dropCol = ' DROP COLUMN'; - var $schema = false; - var $serverInfo = array(); - var $autoIncrement = false; - var $dataProvider; - var $blobSize = 100; /* / any varchar/char field this size or greater is treated as a blob */ - /* / in other words, we use a text area for editting. */ - - function GetCommentSQL($table,$col) - { - return false; - } - - function SetCommentSQL($table,$col,$cmt) - { - return false; - } - - function &MetaTables() - { - return $this->connection->MetaTables(); - } - - function &MetaColumns($tab) - { - return $this->connection->MetaColumns($tab); - } - - function &MetaPrimaryKeys($tab,$owner=false,$intkey=false) - { - return $this->connection->MetaPrimaryKeys($tab.$owner,$intkey); - } - - function MetaType($t,$len=-1,$fieldobj=false) - { - return ADORecordSet::MetaType($t,$len,$fieldobj); - } - - /* Executes the sql array returned by GetTableSQL and GetIndexSQL */ - function ExecuteSQLArray($sql, $continueOnError = true) - { - $rez = 2; - $conn = &$this->connection; - $saved = $conn->debug; - foreach($sql as $line) { - - if ($this->debug) $conn->debug = true; - $ok = $conn->Execute($line); - $conn->debug = $saved; - if (!$ok) { - if ($this->debug) ADOConnection::outp($conn->ErrorMsg()); - if (!$continueOnError) return 0; - $rez = 1; - } - } - return 2; - } - - /* - Returns the actual type given a character code. - - C: varchar - X: CLOB (character large object) or largest varchar size if CLOB is not supported - C2: Multibyte varchar - X2: Multibyte CLOB - - B: BLOB (binary large object) - - D: Date - T: Date-time - L: Integer field suitable for storing booleans (0 or 1) - I: Integer - F: Floating point number - N: Numeric or decimal number - */ - - function ActualType($meta) - { - return $meta; - } - - function CreateDatabase($dbname,$options=false) - { - $options = $this->_Options($options); - $s = 'CREATE DATABASE '.$dbname; - if (isset($options[$this->upperName])) $s .= ' '.$options[$this->upperName]; - $sql[] = $s; - return $sql; - } - - /* - Generates the SQL to create index. Returns an array of sql strings. - */ - function CreateIndexSQL($idxname, $tabname, $flds, $idxoptions = false) - { - if ($this->schema) $tabname = $this->schema.'.'.$tabname; - return $this->_IndexSQL($idxname, $tabname, $flds, $this->_Options($idxoptions)); - } - - function SetSchema($schema) - { - $this->schema = $schema; - } - - function AddColumnSQL($tabname, $flds) - { - if ($this->schema) $tabname = $this->schema.'.'.$tabname; - $sql = array(); - list($lines,$pkey) = $this->_GenFields($flds); - foreach($lines as $v) { - $sql[] = "ALTER TABLE $tabname $this->addCol $v"; - } - return $sql; - } - - function AlterColumnSQL($tabname, $flds) - { - if ($this->schema) $tabname = $this->schema.'.'.$tabname; - $sql = array(); - list($lines,$pkey) = $this->_GenFields($flds); - - foreach($lines as $v) { - $sql[] = "ALTER TABLE $tabname $this->alterCol $v"; - } - return $sql; - } - - function DropColumnSQL($tabname, $flds) - { - if ($this->schema) $tabname = $this->schema.'.'.$tabname; - if (!is_array($flds)) $flds = explode(',',$flds); - $sql = array(); - foreach($flds as $v) { - $sql[] = "ALTER TABLE $tabname $this->dropCol $v"; - } - return $sql; - } - - function DropTableSQL($tabname) - { - if ($this->schema) $tabname = $this->schema.'.'.$tabname; - $sql[] = sprintf($this->dropTable,$tabname); - return $sql; - } - - /* - Generate the SQL to create table. Returns an array of sql strings. - */ - function CreateTableSQL($tabname, $flds, $tableoptions=false) - { - if (!$tableoptions) $tableoptions = array(); - - list($lines,$pkey) = $this->_GenFields($flds); - - $taboptions = $this->_Options($tableoptions); - if ($this->schema) $tabname = $this->schema.'.'.$tabname; - $sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions); - - $tsql = $this->_Triggers($tabname,$taboptions); - foreach($tsql as $s) $sql[] = $s; - - return $sql; - } - - function _GenFields($flds) - { - if (is_string($flds)) { - $padding = ' '; - $txt = $flds.$padding; - $flds = array(); - $flds0 = Lens_ParseArgs($txt,','); - $hasparam = false; - foreach($flds0 as $f0) { - $f1 = array(); - foreach($f0 as $token) { - switch (strtoupper($token)) { - case 'CONSTRAINT': - case 'DEFAULT': - $hasparam = $token; - break; - default: - if ($hasparam) $f1[$hasparam] = $token; - else $f1[] = $token; - $hasparam = false; - break; - } - } - $flds[] = $f1; - - } - } - $this->autoIncrement = false; - $lines = array(); - $pkey = array(); - foreach($flds as $fld) { - $fld = _array_change_key_case($fld); - - $fname = false; - $fdefault = false; - $fautoinc = false; - $ftype = false; - $fsize = false; - $fprec = false; - $fprimary = false; - $fnoquote = false; - $fdefts = false; - $fdefdate = false; - $fconstraint = false; - $fnotnull = false; - $funsigned = false; - - /* ----------------- */ - /* Parse attributes */ - foreach($fld as $attr => $v) { - if ($attr == 2 && is_numeric($v)) $attr = 'SIZE'; - else if (is_numeric($attr) && $attr > 1 && !is_numeric($v)) $attr = strtoupper($v); - - switch($attr) { - case '0': - case 'NAME': $fname = $v; break; - case '1': - case 'TYPE': $ty = $v; $ftype = $this->ActualType(strtoupper($v)); break; - case 'SIZE': $dotat = strpos($v,'.'); - if ($dotat === false) $fsize = $v; - else { - $fsize = substr($v,0,$dotat); - $fprec = substr($v,$dotat+1); - } - break; - case 'UNSIGNED': $funsigned = true; break; - case 'AUTOINCREMENT': - case 'AUTO': $fautoinc = true; $fnotnull = true; break; - case 'KEY': - case 'PRIMARY': $fprimary = $v; $fnotnull = true; break; - case 'DEF': - case 'DEFAULT': $fdefault = $v; break; - case 'NOTNULL': $fnotnull = $v; break; - case 'NOQUOTE': $fnoquote = $v; break; - case 'DEFDATE': $fdefdate = $v; break; - case 'DEFTIMESTAMP': $fdefts = $v; break; - case 'CONSTRAINT': $fconstraint = $v; break; - } /* switch */ - } /* foreach $fld */ - - /* -------------------- */ - /* VALIDATE FIELD INFO */ - if (!strlen($fname)) { - if ($this->debug) ADOConnection::outp("Undefined NAME"); - return false; - } - - if (!strlen($ftype)) { - if ($this->debug) ADOConnection::outp("Undefined TYPE for field '$fname'"); - return false; - } else { - $ftype = strtoupper($ftype); - } - - $ftype = $this->_GetSize($ftype, $ty, $fsize, $fprec); - - if ($ty == 'X' || $ty == 'X2' || $ty == 'B') $fnotnull = false; /* some blob types do not accept nulls */ - - if ($fprimary) $pkey[] = $fname; - - /* some databases do not allow blobs to have defaults */ - if ($ty == 'X') $fdefault = false; - - /* -------------------- */ - /* CONSTRUCT FIELD SQL */ - if ($fdefts) { - if (substr($this->connection->databaseType,0,5) == 'mysql') { - $ftype = 'TIMESTAMP'; - } else { - $fdefault = $this->connection->sysTimeStamp; - } - } else if ($fdefdate) { - if (substr($this->connection->databaseType,0,5) == 'mysql') { - $ftype = 'TIMESTAMP'; - } else { - $fdefault = $this->connection->sysDate; - } - } else if (strlen($fdefault) && !$fnoquote) - if ($ty == 'C' or $ty == 'X' or - ( substr($fdefault,0,1) != "'" && !is_numeric($fdefault))) - if (strlen($fdefault) != 1 && substr($fdefault,0,1) == ' ' && substr($fdefault,strlen($fdefault)-1) == ' ') - $fdefault = trim($fdefault); - else if (strtolower($fdefault) != 'null') - $fdefault = $this->connection->qstr($fdefault); - $suffix = $this->_CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned); - - $fname = str_pad($fname,16); - $lines[] = "$fname $ftype$suffix"; - - if ($fautoinc) $this->autoIncrement = true; - } /* foreach $flds */ - - - return array($lines,$pkey); - } - /* - GENERATE THE SIZE PART OF THE DATATYPE - $ftype is the actual type - $ty is the type defined originally in the DDL - */ - function _GetSize($ftype, $ty, $fsize, $fprec) - { - if (strlen($fsize) && $ty != 'X' && $ty != 'B' && strpos($ftype,'(') === false) { - $ftype .= "(".$fsize; - if ($fprec) $ftype .= ",".$fprec; - $ftype .= ')'; - } - return $ftype; - } - - - /* return string must begin with space */ - function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint) - { - $suffix = ''; - if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; - if ($fnotnull) $suffix .= ' NOT NULL'; - if ($fconstraint) $suffix .= ' '.$fconstraint; - return $suffix; - } - - function _IndexSQL($idxname, $tabname, $flds, $idxoptions) - { - if (isset($idxoptions['REPLACE'])) $sql[] = "DROP INDEX $idxname"; - if (isset($idxoptions['UNIQUE'])) $unique = ' UNIQUE'; - else $unique = ''; - - if (is_array($flds)) $flds = implode(', ',$flds); - $s = "CREATE$unique INDEX $idxname ON $tabname "; - if (isset($idxoptions[$this->upperName])) $s .= $idxoptions[$this->upperName]; - $s .= "($flds)"; - $sql[] = $s; - - return $sql; - } - - function _DropAutoIncrement($tabname) - { - return false; - } - - function _TableSQL($tabname,$lines,$pkey,$tableoptions) - { - $sql = array(); - - if (isset($tableoptions['REPLACE'])) { - $sql[] = sprintf($this->dropTable,$tabname); - if ($this->autoIncrement) { - $sInc = $this->_DropAutoIncrement($tabname); - if ($sInc) $sql[] = $sInc; - } - } - $s = "CREATE TABLE $tabname (\n"; - $s .= implode(",\n", $lines); - if (sizeof($pkey)>0) { - $s .= ",\n PRIMARY KEY ("; - $s .= implode(", ",$pkey).")"; - } - if (isset($tableoptions['CONSTRAINTS'])) - $s .= "\n".$tableoptions['CONSTRAINTS']; - - if (isset($tableoptions[$this->upperName.'_CONSTRAINTS'])) - $s .= "\n".$tableoptions[$this->upperName.'_CONSTRAINTS']; - - $s .= "\n)"; - if (isset($tableoptions[$this->upperName])) $s .= $tableoptions[$this->upperName]; - $sql[] = $s; - - return $sql; - } - - /* - GENERATE TRIGGERS IF NEEDED - used when table has auto-incrementing field that is emulated using triggers - */ - function _Triggers($tabname,$taboptions) - { - return array(); - } - - /* - Sanitize options, so that array elements with no keys are promoted to keys - */ - function _Options($opts) - { - if (!is_array($opts)) return array(); - $newopts = array(); - foreach($opts as $k => $v) { - if (is_numeric($k)) $newopts[strtoupper($v)] = $v; - else $newopts[strtoupper($k)] = $v; - } - return $newopts; - } - - -/* -"Florian Buzin [ easywe ]"
"; +print_r($a); +print ""; +} +//Lens_ParseTest(); + +/** + Parse arguments, treat "text" (text) and 'text' as quotation marks. + To escape, use "" or '' or )) + + @param endstmtchar Character that indicates end of statement + @param tokenchars Include the following characters in tokens apart from A-Z and 0-9 + @returns 2 dimensional array containing parsed tokens. +*/ +function Lens_ParseArgs($args,$endstmtchar=',',$tokenchars='_.-') +{ + $pos = 0; + $intoken = false; + $stmtno = 0; + $endquote = false; + $tokens = array(); + $tokens[$stmtno] = array(); + $max = strlen($args); + $quoted = false; + + while ($pos < $max) { + $ch = substr($args,$pos,1); + switch($ch) { + case ' ': + case "\t": + case "\n": + case "\r": + if (!$quoted) { + if ($intoken) { + $intoken = false; + $tokens[$stmtno][] = implode('',$tokarr); + } + break; + } + + $tokarr[] = $ch; + break; + + case '(': + case ')': + case '"': + case "'": + + if ($intoken) { + if (empty($endquote)) { + $tokens[$stmtno][] = implode('',$tokarr); + if ($ch == '(') $endquote = ')'; + else $endquote = $ch; + $quoted = true; + $intoken = true; + $tokarr = array(); + } else if ($endquote == $ch) { + $ch2 = substr($args,$pos+1,1); + if ($ch2 == $endquote) { + $pos += 1; + $tokarr[] = $ch2; + } else { + $quoted = false; + $intoken = false; + $tokens[$stmtno][] = implode('',$tokarr); + $endquote = ''; + } + } else + $tokarr[] = $ch; + + }else { + + if ($ch == '(') $endquote = ')'; + else $endquote = $ch; + $quoted = true; + $intoken = true; + $tokarr = array(); + } + break; + + default: + + if (!$intoken) { + if ($ch == $endstmtchar) { + $stmtno += 1; + $tokens[$stmtno] = array(); + break; + } + + $intoken = true; + $quoted = false; + $endquote = false; + $tokarr = array(); + + } + + if ($quoted) $tokarr[] = $ch; + else if (ctype_alnum($ch) || strpos($tokenchars,$ch) !== false) $tokarr[] = $ch; + else { + if ($ch == $endstmtchar) { + $tokens[$stmtno][] = implode('',$tokarr); + $stmtno += 1; + $tokens[$stmtno] = array(); + $intoken = false; + $tokarr = array(); + break; + } + $tokens[$stmtno][] = implode('',$tokarr); + $tokens[$stmtno][] = $ch; + $intoken = false; + } + } + $pos += 1; + } + + return $tokens; +} + + +class ADODB_DataDict { + var $connection; + var $debug = false; + var $dropTable = "DROP TABLE %s"; + var $addCol = ' ADD'; + var $alterCol = ' ALTER COLUMN'; + var $dropCol = ' DROP COLUMN'; + var $schema = false; + var $serverInfo = array(); + var $autoIncrement = false; + var $quote = ''; + var $dataProvider; + var $blobSize = 100; /// any varchar/char field this size or greater is treated as a blob + /// in other words, we use a text area for editting. + + function GetCommentSQL($table,$col) + { + return false; + } + + function SetCommentSQL($table,$col,$cmt) + { + return false; + } + + function &MetaTables() + { + return $this->connection->MetaTables(); + } + + function &MetaColumns($tab) + { + return $this->connection->MetaColumns($tab); + } + + function &MetaPrimaryKeys($tab,$owner=false,$intkey=false) + { + return $this->connection->MetaPrimaryKeys($tab.$owner,$intkey); + } + + function MetaType($t,$len=-1,$fieldobj=false) + { + return ADORecordSet::MetaType($t,$len,$fieldobj); + } + + // Executes the sql array returned by GetTableSQL and GetIndexSQL + function ExecuteSQLArray($sql, $continueOnError = true) + { + $rez = 2; + $conn = &$this->connection; + $saved = $conn->debug; + foreach($sql as $line) { + + if ($this->debug) $conn->debug = true; + $ok = $conn->Execute($line); + $conn->debug = $saved; + if (!$ok) { + if ($this->debug) ADOConnection::outp($conn->ErrorMsg()); + if (!$continueOnError) return 0; + $rez = 1; + } + } + return 2; + } + + /* + Returns the actual type given a character code. + + C: varchar + X: CLOB (character large object) or largest varchar size if CLOB is not supported + C2: Multibyte varchar + X2: Multibyte CLOB + + B: BLOB (binary large object) + + D: Date + T: Date-time + L: Integer field suitable for storing booleans (0 or 1) + I: Integer + F: Floating point number + N: Numeric or decimal number + */ + + function ActualType($meta) + { + return $meta; + } + + function CreateDatabase($dbname,$options=false) + { + $options = $this->_Options($options); + if (!preg_match('/^[a-z0-9A-Z_]*$/',$dbname)) $dbname = $this->quote.$dbname.$this->quote; + $s = 'CREATE DATABASE '.$dbname; + if (isset($options[$this->upperName])) $s .= ' '.$options[$this->upperName]; + $sql[] = $s; + return $sql; + } + + /* + Generates the SQL to create index. Returns an array of sql strings. + */ + function CreateIndexSQL($idxname, $tabname, $flds, $idxoptions = false) + { + if ($this->schema) $tabname = $this->schema.'.'.$tabname; + return $this->_IndexSQL($idxname, $tabname, $flds, $this->_Options($idxoptions)); + } + + function SetSchema($schema) + { + $this->schema = $schema; + } + + function AddColumnSQL($tabname, $flds) + { + if ($this->schema) $tabname = $this->schema.'.'.$tabname; + $sql = array(); + list($lines,$pkey) = $this->_GenFields($flds); + foreach($lines as $v) { + $sql[] = "ALTER TABLE $tabname $this->addCol $v"; + } + return $sql; + } + + function AlterColumnSQL($tabname, $flds) + { + if ($this->schema) $tabname = $this->schema.'.'.$tabname; + $sql = array(); + list($lines,$pkey) = $this->_GenFields($flds); + + foreach($lines as $v) { + $sql[] = "ALTER TABLE $tabname $this->alterCol $v"; + } + return $sql; + } + + function DropColumnSQL($tabname, $flds) + { + if ($this->schema) $tabname = $this->schema.'.'.$tabname; + if (!is_array($flds)) $flds = explode(',',$flds); + $sql = array(); + foreach($flds as $v) { + $sql[] = "ALTER TABLE $tabname $this->dropCol $v"; + } + return $sql; + } + + function DropTableSQL($tabname) + { + if ($this->schema) $tabname = $this->schema.'.'.$tabname; + $sql[] = sprintf($this->dropTable,$tabname); + return $sql; + } + + /* + Generate the SQL to create table. Returns an array of sql strings. + */ + function CreateTableSQL($tabname, $flds, $tableoptions=false) + { + if (!$tableoptions) $tableoptions = array(); + + list($lines,$pkey) = $this->_GenFields($flds); + + $taboptions = $this->_Options($tableoptions); + if ($this->schema) $tabname = $this->schema.'.'.$tabname; + $sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions); + + $tsql = $this->_Triggers($tabname,$taboptions); + foreach($tsql as $s) $sql[] = $s; + + return $sql; + } + + function _GenFields($flds) + { + if (is_string($flds)) { + $padding = ' '; + $txt = $flds.$padding; + $flds = array(); + $flds0 = Lens_ParseArgs($txt,','); + $hasparam = false; + foreach($flds0 as $f0) { + $f1 = array(); + foreach($f0 as $token) { + switch (strtoupper($token)) { + case 'CONSTRAINT': + case 'DEFAULT': + $hasparam = $token; + break; + default: + if ($hasparam) $f1[$hasparam] = $token; + else $f1[] = $token; + $hasparam = false; + break; + } + } + $flds[] = $f1; + + } + } + $this->autoIncrement = false; + $lines = array(); + $pkey = array(); + foreach($flds as $fld) { + $fld = _array_change_key_case($fld); + + $fname = false; + $fdefault = false; + $fautoinc = false; + $ftype = false; + $fsize = false; + $fprec = false; + $fprimary = false; + $fnoquote = false; + $fdefts = false; + $fdefdate = false; + $fconstraint = false; + $fnotnull = false; + $funsigned = false; + + //----------------- + // Parse attributes + foreach($fld as $attr => $v) { + if ($attr == 2 && is_numeric($v)) $attr = 'SIZE'; + else if (is_numeric($attr) && $attr > 1 && !is_numeric($v)) $attr = strtoupper($v); + + switch($attr) { + case '0': + case 'NAME': $fname = $v; break; + case '1': + case 'TYPE': $ty = $v; $ftype = $this->ActualType(strtoupper($v)); break; + case 'SIZE': $dotat = strpos($v,'.'); + if ($dotat === false) $fsize = $v; + else { + $fsize = substr($v,0,$dotat); + $fprec = substr($v,$dotat+1); + } + break; + case 'UNSIGNED': $funsigned = true; break; + case 'AUTOINCREMENT': + case 'AUTO': $fautoinc = true; $fnotnull = true; break; + case 'KEY': + case 'PRIMARY': $fprimary = $v; $fnotnull = true; break; + case 'DEF': + case 'DEFAULT': $fdefault = $v; break; + case 'NOTNULL': $fnotnull = $v; break; + case 'NOQUOTE': $fnoquote = $v; break; + case 'DEFDATE': $fdefdate = $v; break; + case 'DEFTIMESTAMP': $fdefts = $v; break; + case 'CONSTRAINT': $fconstraint = $v; break; + } //switch + } // foreach $fld + + //-------------------- + // VALIDATE FIELD INFO + if (!strlen($fname)) { + if ($this->debug) ADOConnection::outp("Undefined NAME"); + return false; + } + + if (!strlen($ftype)) { + if ($this->debug) ADOConnection::outp("Undefined TYPE for field '$fname'"); + return false; + } else { + $ftype = strtoupper($ftype); + } + + $ftype = $this->_GetSize($ftype, $ty, $fsize, $fprec); + + if ($ty == 'X' || $ty == 'X2' || $ty == 'B') $fnotnull = false; // some blob types do not accept nulls + + if ($fprimary) $pkey[] = $fname; + + // some databases do not allow blobs to have defaults + if ($ty == 'X') $fdefault = false; + + //-------------------- + // CONSTRUCT FIELD SQL + if ($fdefts) { + if (substr($this->connection->databaseType,0,5) == 'mysql') { + $ftype = 'TIMESTAMP'; + } else { + $fdefault = $this->connection->sysTimeStamp; + } + } else if ($fdefdate) { + if (substr($this->connection->databaseType,0,5) == 'mysql') { + $ftype = 'TIMESTAMP'; + } else { + $fdefault = $this->connection->sysDate; + } + } else if (strlen($fdefault) && !$fnoquote) + if ($ty == 'C' or $ty == 'X' or + ( substr($fdefault,0,1) != "'" && !is_numeric($fdefault))) + if (strlen($fdefault) != 1 && substr($fdefault,0,1) == ' ' && substr($fdefault,strlen($fdefault)-1) == ' ') + $fdefault = trim($fdefault); + else if (strtolower($fdefault) != 'null') + $fdefault = $this->connection->qstr($fdefault); + $suffix = $this->_CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned); + + $fname = str_pad($fname,16); + $lines[] = "$fname $ftype$suffix"; + + if ($fautoinc) $this->autoIncrement = true; + } // foreach $flds + + + return array($lines,$pkey); + } + /* + GENERATE THE SIZE PART OF THE DATATYPE + $ftype is the actual type + $ty is the type defined originally in the DDL + */ + function _GetSize($ftype, $ty, $fsize, $fprec) + { + if (strlen($fsize) && $ty != 'X' && $ty != 'B' && strpos($ftype,'(') === false) { + $ftype .= "(".$fsize; + if ($fprec) $ftype .= ",".$fprec; + $ftype .= ')'; + } + return $ftype; + } + + + // return string must begin with space + function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint) + { + $suffix = ''; + if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; + if ($fnotnull) $suffix .= ' NOT NULL'; + if ($fconstraint) $suffix .= ' '.$fconstraint; + return $suffix; + } + + function _IndexSQL($idxname, $tabname, $flds, $idxoptions) + { + if (isset($idxoptions['REPLACE'])) $sql[] = "DROP INDEX $idxname"; + if (isset($idxoptions['UNIQUE'])) $unique = ' UNIQUE'; + else $unique = ''; + + if (is_array($flds)) $flds = implode(', ',$flds); + $s = "CREATE$unique INDEX $idxname ON $tabname "; + if (isset($idxoptions[$this->upperName])) $s .= $idxoptions[$this->upperName]; + $s .= "($flds)"; + $sql[] = $s; + + return $sql; + } + + function _DropAutoIncrement($tabname) + { + return false; + } + + function _TableSQL($tabname,$lines,$pkey,$tableoptions) + { + $sql = array(); + + if (isset($tableoptions['REPLACE'])) { + $sql[] = sprintf($this->dropTable,$tabname); + if ($this->autoIncrement) { + $sInc = $this->_DropAutoIncrement($tabname); + if ($sInc) $sql[] = $sInc; + } + } + $s = "CREATE TABLE $tabname (\n"; + $s .= implode(",\n", $lines); + if (sizeof($pkey)>0) { + $s .= ",\n PRIMARY KEY ("; + $s .= implode(", ",$pkey).")"; + } + if (isset($tableoptions['CONSTRAINTS'])) + $s .= "\n".$tableoptions['CONSTRAINTS']; + + if (isset($tableoptions[$this->upperName.'_CONSTRAINTS'])) + $s .= "\n".$tableoptions[$this->upperName.'_CONSTRAINTS']; + + $s .= "\n)"; + if (isset($tableoptions[$this->upperName])) $s .= $tableoptions[$this->upperName]; + $sql[] = $s; + + return $sql; + } + + /* + GENERATE TRIGGERS IF NEEDED + used when table has auto-incrementing field that is emulated using triggers + */ + function _Triggers($tabname,$taboptions) + { + return array(); + } + + /* + Sanitize options, so that array elements with no keys are promoted to keys + */ + function _Options($opts) + { + if (!is_array($opts)) return array(); + $newopts = array(); + foreach($opts as $k => $v) { + if (is_numeric($k)) $newopts[strtoupper($v)] = $v; + else $newopts[strtoupper($k)] = $v; + } + return $newopts; + } + + +/* +"Florian Buzin [ easywe ]"
|<
';
- var $prev = '<<
';
- var $next = '>>
';
- var $last = '>|
';
- var $moreLinks = '...';
- var $startLinks = '...';
- var $gridHeader = false;
- var $htmlSpecialChars = true;
- var $page = 'Page';
- var $linkSelectedColor = 'red';
- var $cache = 0; #secs to cache with CachePageExecute()
-
- /* ---------------------------------------------- */
- /* constructor */
- /* */
- /* $db adodb connection object */
- /* $sql sql statement */
- /* $id optional id to identify which pager, */
- /* if you have multiple on 1 page. */
- /* $id should be only be [a-z0-9]* */
- /* */
- function ADODB_Pager(&$db,$sql,$id = 'adodb', $showPageLinks = false)
- {
- global $HTTP_SERVER_VARS,$PHP_SELF,$HTTP_SESSION_VARS,$HTTP_GET_VARS;
-
- $curr_page = $id.'_curr_page';
- if (empty($PHP_SELF)) $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
-
- $this->sql = $sql;
- $this->id = $id;
- $this->db = $db;
- $this->showPageLinks = $showPageLinks;
-
- $next_page = $id.'_next_page';
-
- if (isset($HTTP_GET_VARS[$next_page])) {
- $HTTP_SESSION_VARS[$curr_page] = $HTTP_GET_VARS[$next_page];
- }
- if (empty($HTTP_SESSION_VARS[$curr_page])) $HTTP_SESSION_VARS[$curr_page] = 1; ## at first page
-
- $this->curr_page = $HTTP_SESSION_VARS[$curr_page];
-
- }
-
- /* --------------------------- */
- /* Display link to first page */
- function Render_First($anchor=true)
- {
- global $PHP_SELF;
- if ($anchor) {
- ?>
- first;?>
- first ";
- }
- }
-
- /* -------------------------- */
- /* Display link to next page */
- function render_next($anchor=true)
- {
- global $PHP_SELF;
-
- if ($anchor) {
- ?>
- next;?>
- next ";
- }
- }
-
- /* ------------------ */
- /* Link to last page */
- /* */
- /* for better performance with large recordsets, you can set */
- /* $this->db->pageExecuteCountRows = false, which disables */
- /* last page counting. */
- function render_last($anchor=true)
- {
- global $PHP_SELF;
-
- if (!$this->db->pageExecuteCountRows) return;
-
- if ($anchor) {
- ?>
- last;?>
- last ";
- }
- }
-
- /* --------------------------------------------------- */
- /* original code by "Pablo Costa" ", - $header, - " |
", - $grid, - " |
", - $footer, - " |
|<
';
+ var $prev = '<<
';
+ var $next = '>>
';
+ var $last = '>|
';
+ var $moreLinks = '...';
+ var $startLinks = '...';
+ var $gridHeader = false;
+ var $htmlSpecialChars = true;
+ var $page = 'Page';
+ var $linkSelectedColor = 'red';
+ var $cache = 0; #secs to cache with CachePageExecute()
+
+ //----------------------------------------------
+ // constructor
+ //
+ // $db adodb connection object
+ // $sql sql statement
+ // $id optional id to identify which pager,
+ // if you have multiple on 1 page.
+ // $id should be only be [a-z0-9]*
+ //
+ function ADODB_Pager(&$db,$sql,$id = 'adodb', $showPageLinks = false)
+ {
+ global $HTTP_SERVER_VARS,$PHP_SELF,$HTTP_SESSION_VARS,$HTTP_GET_VARS;
+
+ $curr_page = $id.'_curr_page';
+ if (empty($PHP_SELF)) $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
+
+ $this->sql = $sql;
+ $this->id = $id;
+ $this->db = $db;
+ $this->showPageLinks = $showPageLinks;
+
+ $next_page = $id.'_next_page';
+
+ if (isset($HTTP_GET_VARS[$next_page])) {
+ $HTTP_SESSION_VARS[$curr_page] = $HTTP_GET_VARS[$next_page];
+ }
+ if (empty($HTTP_SESSION_VARS[$curr_page])) $HTTP_SESSION_VARS[$curr_page] = 1; ## at first page
+
+ $this->curr_page = $HTTP_SESSION_VARS[$curr_page];
+
+ }
+
+ //---------------------------
+ // Display link to first page
+ function Render_First($anchor=true)
+ {
+ global $PHP_SELF;
+ if ($anchor) {
+ ?>
+ first;?>
+ first ";
+ }
+ }
+
+ //--------------------------
+ // Display link to next page
+ function render_next($anchor=true)
+ {
+ global $PHP_SELF;
+
+ if ($anchor) {
+ ?>
+ next;?>
+ next ";
+ }
+ }
+
+ //------------------
+ // Link to last page
+ //
+ // for better performance with large recordsets, you can set
+ // $this->db->pageExecuteCountRows = false, which disables
+ // last page counting.
+ function render_last($anchor=true)
+ {
+ global $PHP_SELF;
+
+ if (!$this->db->pageExecuteCountRows) return;
+
+ if ($anchor) {
+ ?>
+ last;?>
+ last ";
+ }
+ }
+
+ //---------------------------------------------------
+ // original code by "Pablo Costa" ", + $header, + " |
", + $grid, + " |
", + $footer, + " |
Parameter | Value | Description |
Avg Time | Count | SQL | Max | Min |
".round($rs->fields[0],6)." | ".$rs->fields[2]." | ".$prefix.htmlspecialchars($sql).$suffix."". + " | ".$rs->fields[3]." | ".$rs->fields[4]." |
$this->helpurl. ".$this->conn->ErrorMsg()."
"; + $s = "Load | Count | SQL | Max | Min |
".round($rs->fields[0],6)." | ".$rs->fields[2]." | ".$prefix.htmlspecialchars($sql).$suffix."". + " | ".$rs->fields[3]." | ".$rs->fields[4]." |
+ ADOdb Performance Monitor for $app | |
+ Performance Stats View SQL + View Tables Poll Stats", + "$form", + " |
"; + $this->Poll($pollsecs); + break; + case 'viewsql': + if (empty($HTTP_GET_VARS['hidem'])) + echo " Clear SQL Log
"; + echo($this->SuspiciousSQL($nsql)); + echo($this->ExpensiveSQL($nsql)); + echo($this->InvalidSQL($nsql)); + break; + case 'tables': + echo $this->Tables(); break; + } + global $ADODB_vers; + echo "$ADODB_vers Sponsored by phpLens"; + } + + /* + Runs in infinite loop, returning real-time statistics + */ + function Poll($secs=5) + { + $this->conn->fnExecute = false; + //$this->conn->debug=1; + if ($secs <= 1) $secs = 1; + echo "Accumulating statistics, every $secs seconds...\n";flush(); + $arro =& $this->PollParameters(); + $cnt = 0; + set_time_limit(0); + sleep($secs); + while (1) { + $arr =& $this->PollParameters(); + + $hits = sprintf('%2.2f',$arr[0]); + $reads = sprintf('%12.4f',($arr[1]-$arro[1])/$secs); + $writes = sprintf('%12.4f',($arr[2]-$arro[2])/$secs); + $sess = sprintf('%5d',$arr[3]); + + $load = $this->CPULoad(); + if ($load !== false) { + $oslabel = 'WS-CPU%'; + $osval = sprintf(" %2.1f ",(float) $load); + }else { + $oslabel = ''; + $osval = ''; + } + if ($cnt % 10 == 0) echo " Time ".$oslabel." Hit% Sess Reads/s Writes/s\n"; + $cnt += 1; + echo date('H:i:s').' '.$osval."$hits $sess $reads $writes\n"; + flush(); + + sleep($secs); + $arro = $arr; + } + } + + /* + Returns basic health check in a command line interface + */ + function HealthCheckCLI() + { + return $this->HealthCheck(true); + } + + + /* + Returns basic health check as HTML + */ + function HealthCheck($cli=false) + { + $saveE = $this->conn->fnExecute; + $this->conn->fnExecute = false; + if ($cli) $html = ''; + else $html = $this->table.''.$this->titles; + + $oldc = false; + $bgc = ''; + foreach($this->settings as $name => $arr) { + if ($arr === false) break; + + if (!is_string($name)) { + if ($cli) $html .= " -- $arr -- \n"; + else $html .= " '.$this->conn->databaseType.'
color> "; + continue; + } + + if (!is_array($arr)) break; + $category = $arr[0]; + $how = $arr[1]; + if (sizeof($arr)>2) $desc = $arr[2]; + else $desc = ' '; + + + if ($category == 'HIDE') continue; + + $val = $this->_DBParameter($how); + + if ($desc && strncmp($desc,"=",1) === 0) { + $fn = substr($desc,1); + $desc = $this->$fn($val); + } + + if ($val === false) { + $m = $this->conn->ErrorMsg(); + $val = "Error: $m"; + } else { + if (is_numeric($val) && $val >= 256*1024) { + if ($val % (1024*1024) == 0) { + $val /= (1024*1024); + $val .= 'M'; + } else if ($val % 1024 == 0) { + $val /= 1024; + $val .= 'K'; + } + //$val = htmlspecialchars($val); + } + } + if ($category != $oldc) { + $oldc = $category; + //$bgc = ($bgc == ' bgcolor='.$this->color) ? ' bgcolor=white' : ' bgcolor='.$this->color; + } + if (strlen($desc)==0) $desc = ' '; + if (strlen($val)==0) $val = ' '; + if ($cli) { + $html .= str_replace(' ','',sprintf($this->cliFormat,strip_tags($name),strip_tags($val),strip_tags($desc))); + + }else { + $html .= "$arr \n"; + } + } + + if (!$cli) $html .= " ".$name.' '.$val.' '.$desc."
\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
"; - - - Installation - ============ - 1. Create this table in your database (syntax might vary depending on your db): - - create table sessions ( - SESSKEY char(32) not null, - EXPIRY int(11) unsigned not null, - EXPIREREF varchar(64), - DATA CLOB, - primary key (sesskey) - ); - - - 2. Then define the following parameters in this file: - $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; - $ADODB_SESSION_CONNECT='server to connect to'; - $ADODB_SESSION_USER ='user'; - $ADODB_SESSION_PWD ='password'; - $ADODB_SESSION_DB ='database'; - $ADODB_SESSION_TBL = 'sessions' - $ADODB_SESSION_USE_LOBS = false; (or, if you wanna use CLOBS (= 'CLOB') or ( = 'BLOB') - - 3. Recommended is PHP 4.0.6 or later. There are documented - session bugs in earlier versions of PHP. - - 4. If you want to receive notifications when a session expires, then - you can tag a session with an EXPIREREF, and before the session - record is deleted, we can call a function that will pass the EXPIREREF - as the first parameter, and the session key as the second parameter. - - To do this, define a notification function, say NotifyFn: - - function NotifyFn($expireref, $sesskey) - { - } - - Then define a global variable, with the first parameter being the - global variable you would like to store in the EXPIREREF field, and - the second is the function name. - - In this example, we want to be notified when a user's session - has expired, so we store the user id in $USERID, and make this - the value stored in the EXPIREREF field: - - $ADODB_SESSION_EXPIRE_NOTIFY = array('USERID','NotifyFn'); -*/ - -if (!defined('_ADODB_LAYER')) { - include (dirname(__FILE__).'/adodb.inc.php'); -} - -if (!defined('ADODB_SESSION')) { - - define('ADODB_SESSION',1); - - /* if database time and system time is difference is greater than this, then give warning */ - define('ADODB_SESSION_SYNCH_SECS',60); - -/****************************************************************************************\ - Global definitions -\****************************************************************************************/ -GLOBAL $ADODB_SESSION_CONNECT, - $ADODB_SESSION_DRIVER, - $ADODB_SESSION_USER, - $ADODB_SESSION_PWD, - $ADODB_SESSION_DB, - $ADODB_SESS_CONN, - $ADODB_SESS_LIFE, - $ADODB_SESS_DEBUG, - $ADODB_SESSION_EXPIRE_NOTIFY, - $ADODB_SESSION_CRC; - $ADODB_SESSION_USE_LOBS; - - - $ADODB_SESS_LIFE = ini_get('session.gc_maxlifetime'); - if ($ADODB_SESS_LIFE <= 1) { - /* bug in PHP 4.0.3 pl 1 -- how about other versions? */ - /* print "Session: connection failed
",false); -} - -/****************************************************************************************\ - Close the connection -\****************************************************************************************/ -function adodb_sess_close() -{ -global $ADODB_SESS_CONN; - - if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close(); - return true; -} - -/****************************************************************************************\ - Slurp in the session variables and return the serialized string -\****************************************************************************************/ -function adodb_sess_read($key) -{ -global $ADODB_SESS_CONN,$ADODB_SESSION_TBL,$ADODB_SESSION_CRC; - - $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time()); - if ($rs) { - if ($rs->EOF) { - $v = ''; - } else - $v = rawurldecode(reset($rs->fields)); - - $rs->Close(); - - /* new optimization adodb 2.1 */ - $ADODB_SESSION_CRC = strlen($v).crc32($v); - - return $v; - } - - return ''; /* thx to Jorma Tuomainen, webmaster#wizactive.com */ -} - -/****************************************************************************************\ - Write the serialized data to a database. - - If the data has not been modified since adodb_sess_read(), we do not write. -\****************************************************************************************/ -function adodb_sess_write($key, $val) -{ - global - $ADODB_SESS_CONN, - $ADODB_SESS_LIFE, - $ADODB_SESSION_TBL, - $ADODB_SESS_DEBUG, - $ADODB_SESSION_CRC, - $ADODB_SESSION_EXPIRE_NOTIFY, - $ADODB_SESSION_DRIVER, /* added */ - $ADODB_SESSION_USE_LOBS; /* added */ - - $expiry = time() + $ADODB_SESS_LIFE; - - /* crc32 optimization since adodb 2.1 */ - /* now we only update expiry date, thx to sebastian thom in adodb 2.32 */ - if ($ADODB_SESSION_CRC !== false && $ADODB_SESSION_CRC == strlen($val).crc32($val)) { - if ($ADODB_SESS_DEBUG) echo "Session: Only updating date - crc32 not changed
"; - $qry = "UPDATE $ADODB_SESSION_TBL SET expiry=$expiry WHERE sesskey='$key' AND expiry >= " . time(); - $rs = $ADODB_SESS_CONN->Execute($qry); - return true; - } - $val = rawurlencode($val); - - $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val); - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - $var = reset($ADODB_SESSION_EXPIRE_NOTIFY); - global $$var; - $arr['expireref'] = $$var; - } - - - if ($ADODB_SESSION_USE_LOBS === false) { /* no lobs, simply use replace() */ - $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,$arr, 'sesskey',$autoQuote = true); - if (!$rs) { - $err = $ADODB_SESS_CONN->ErrorMsg(); - } - } else { - /* what value shall we insert/update for lob row? */ - switch ($ADODB_SESSION_DRIVER) { - /* empty_clob or empty_lob for oracle dbs */ - case "oracle": - case "oci8": - case "oci8po": - case "oci805": - $lob_value = sprintf("empty_%s()", strtolower($ADODB_SESSION_USE_LOBS)); - break; - - /* null for all other */ - default: - $lob_value = "null"; - break; - } - - /* do we insert or update? => as for sesskey */ - $res = $ADODB_SESS_CONN->Execute("select count(*) as cnt from $ADODB_SESSION_TBL where sesskey = '$key'"); - if ($res && ($res->fields["CNT"] > 0)) { - $qry = sprintf("update %s set expiry = %d, data = %s where sesskey = '%s'", $ADODB_SESSION_TBL, $expiry, $lob_value, $key); - } else { - /* insert */ - $qry = sprintf("insert into %s (sesskey, expiry, data) values ('%s', %d, %s)", $ADODB_SESSION_TBL, $key, $expiry, $lob_value); - } - - $err = ""; - $rs1 = $ADODB_SESS_CONN->Execute($qry); - if (!$rs1) { - $err .= $ADODB_SESS_CONN->ErrorMsg()."\n"; - } - $rs2 = $ADODB_SESS_CONN->UpdateBlob($ADODB_SESSION_TBL, 'data', $val, "sesskey='$key'", strtoupper($ADODB_SESSION_USE_LOBS)); - if (!$rs2) { - $err .= $ADODB_SESS_CONN->ErrorMsg()."\n"; - } - $rs = ($rs1 && $rs2) ? true : false; - } - - if (!$rs) { - ADOConnection::outp( 'Session Replace: '.nl2br($err).'
',false); - } else { - /* bug in access driver (could be odbc?) means that info is not commited */ - /* properly unless select statement executed in Win2000 */ - if ($ADODB_SESS_CONN->databaseType == 'access') - $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); - } - return !empty($rs); -} - -function adodb_sess_destroy($key) -{ - global $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; - - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - reset($ADODB_SESSION_EXPIRE_NOTIFY); - $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); - $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); - $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $ADODB_SESS_CONN->SetFetchMode($savem); - if ($rs) { - $ADODB_SESS_CONN->BeginTrans(); - while (!$rs->EOF) { - $ref = $rs->fields[0]; - $key = $rs->fields[1]; - $fn($ref,$key); - $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $rs->MoveNext(); - } - $ADODB_SESS_CONN->CommitTrans(); - } - } else { - $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE sesskey = '$key'"; - $rs = $ADODB_SESS_CONN->Execute($qry); - } - return $rs ? true : false; -} - -function adodb_sess_gc($maxlifetime) -{ - global $ADODB_SESS_DEBUG, $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; - - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - reset($ADODB_SESSION_EXPIRE_NOTIFY); - $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); - $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); - $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE expiry < " . time()); - $ADODB_SESS_CONN->SetFetchMode($savem); - if ($rs) { - $ADODB_SESS_CONN->BeginTrans(); - while (!$rs->EOF) { - $ref = $rs->fields[0]; - $key = $rs->fields[1]; - $fn($ref,$key); - $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $rs->MoveNext(); - } - $ADODB_SESS_CONN->CommitTrans(); - } - } else { - $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE expiry < " . time(); - $ADODB_SESS_CONN->Execute($qry); - - if ($ADODB_SESS_DEBUG) ADOConnection::outp("Garbage Collection: $qry
"); - } - /* suggested by Cameron, "GaM3R"$msg
"); - } - } - - return true; -} - -session_module_name('user'); -session_set_save_handler( - "adodb_sess_open", - "adodb_sess_close", - "adodb_sess_read", - "adodb_sess_write", - "adodb_sess_destroy", - "adodb_sess_gc"); -} - -/* TEST SCRIPT -- UNCOMMENT */ - -if (0) { -GLOBAL $HTTP_SESSION_VARS; - - session_start(); - session_register('AVAR'); - $HTTP_SESSION_VARS['AVAR'] += 1; - ADOConnection::outp( "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
",false); -} - -?> +\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}"; + +To force non-persistent connections, call adodb_session_open first before session_start(): + + GLOBAL $HTTP_SESSION_VARS; + include('adodb.inc.php'); + include('adodb-session.php'); + adodb_session_open(false,false,false); + session_start(); + session_register('AVAR'); + $HTTP_SESSION_VARS['AVAR'] += 1; + print "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
"; + + + Installation + ============ + 1. Create this table in your database (syntax might vary depending on your db): + + create table sessions ( + SESSKEY char(32) not null, + EXPIRY int(11) unsigned not null, + EXPIREREF varchar(64), + DATA CLOB, + primary key (sesskey) + ); + + + 2. Then define the following parameters in this file: + $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; + $ADODB_SESSION_CONNECT='server to connect to'; + $ADODB_SESSION_USER ='user'; + $ADODB_SESSION_PWD ='password'; + $ADODB_SESSION_DB ='database'; + $ADODB_SESSION_TBL = 'sessions' + $ADODB_SESSION_USE_LOBS = false; (or, if you wanna use CLOBS (= 'CLOB') or ( = 'BLOB') + + 3. Recommended is PHP 4.0.6 or later. There are documented + session bugs in earlier versions of PHP. + + 4. If you want to receive notifications when a session expires, then + you can tag a session with an EXPIREREF, and before the session + record is deleted, we can call a function that will pass the EXPIREREF + as the first parameter, and the session key as the second parameter. + + To do this, define a notification function, say NotifyFn: + + function NotifyFn($expireref, $sesskey) + { + } + + Then you need to define a global variable $ADODB_SESSION_EXPIRE_NOTIFY. + This is an array with 2 elements, the first being the name of the variable + you would like to store in the EXPIREREF field, and the 2nd is the + notification function's name. + + In this example, we want to be notified when a user's session + has expired, so we store the user id in the global variable $USERID, + store this value in the EXPIREREF field: + + $ADODB_SESSION_EXPIRE_NOTIFY = array('USERID','NotifyFn'); + + Then when the NotifyFn is called, we are passed the $USERID as the first + parameter, eg. NotifyFn($userid, $sesskey). +*/ + +if (!defined('_ADODB_LAYER')) { + include (dirname(__FILE__).'/adodb.inc.php'); +} + +if (!defined('ADODB_SESSION')) { + + define('ADODB_SESSION',1); + + /* if database time and system time is difference is greater than this, then give warning */ + define('ADODB_SESSION_SYNCH_SECS',60); + +/****************************************************************************************\ + Global definitions +\****************************************************************************************/ +GLOBAL $ADODB_SESSION_CONNECT, + $ADODB_SESSION_DRIVER, + $ADODB_SESSION_USER, + $ADODB_SESSION_PWD, + $ADODB_SESSION_DB, + $ADODB_SESS_CONN, + $ADODB_SESS_LIFE, + $ADODB_SESS_DEBUG, + $ADODB_SESSION_EXPIRE_NOTIFY, + $ADODB_SESSION_CRC, + $ADODB_SESSION_USE_LOBS; + + if (!isset($ADODB_SESSION_USE_LOBS)) $ADODB_SESSION_USE_LOBS = 'CLOB'; + + $ADODB_SESS_LIFE = ini_get('session.gc_maxlifetime'); + if ($ADODB_SESS_LIFE <= 1) { + // bug in PHP 4.0.3 pl 1 -- how about other versions? + //print "Session: connection failed
",false); +} + +/****************************************************************************************\ + Close the connection +\****************************************************************************************/ +function adodb_sess_close() +{ +global $ADODB_SESS_CONN; + + if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close(); + return true; +} + +/****************************************************************************************\ + Slurp in the session variables and return the serialized string +\****************************************************************************************/ +function adodb_sess_read($key) +{ +global $ADODB_SESS_CONN,$ADODB_SESSION_TBL,$ADODB_SESSION_CRC; + + $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time()); + if ($rs) { + if ($rs->EOF) { + $v = ''; + } else + $v = rawurldecode(reset($rs->fields)); + + $rs->Close(); + + // new optimization adodb 2.1 + $ADODB_SESSION_CRC = strlen($v).crc32($v); + + return $v; + } + + return ''; // thx to Jorma Tuomainen, webmaster#wizactive.com +} + +/****************************************************************************************\ + Write the serialized data to a database. + + If the data has not been modified since adodb_sess_read(), we do not write. +\****************************************************************************************/ +function adodb_sess_write($key, $val) +{ + global + $ADODB_SESS_CONN, + $ADODB_SESS_LIFE, + $ADODB_SESSION_TBL, + $ADODB_SESS_DEBUG, + $ADODB_SESSION_CRC, + $ADODB_SESSION_EXPIRE_NOTIFY, + $ADODB_SESSION_DRIVER, // added + $ADODB_SESSION_USE_LOBS; // added + + $expiry = time() + $ADODB_SESS_LIFE; + + // crc32 optimization since adodb 2.1 + // now we only update expiry date, thx to sebastian thom in adodb 2.32 + if ($ADODB_SESSION_CRC !== false && $ADODB_SESSION_CRC == strlen($val).crc32($val)) { + if ($ADODB_SESS_DEBUG) echo "Session: Only updating date - crc32 not changed
"; + $qry = "UPDATE $ADODB_SESSION_TBL SET expiry=$expiry WHERE sesskey='$key' AND expiry >= " . time(); + $rs = $ADODB_SESS_CONN->Execute($qry); + return true; + } + $val = rawurlencode($val); + + $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val); + if ($ADODB_SESSION_EXPIRE_NOTIFY) { + $var = reset($ADODB_SESSION_EXPIRE_NOTIFY); + global $$var; + $arr['expireref'] = $$var; + } + + + if ($ADODB_SESSION_USE_LOBS === false) { // no lobs, simply use replace() + $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,$arr, 'sesskey',$autoQuote = true); + if (!$rs) { + $err = $ADODB_SESS_CONN->ErrorMsg(); + } + } else { + // what value shall we insert/update for lob row? + switch ($ADODB_SESSION_DRIVER) { + // empty_clob or empty_lob for oracle dbs + case "oracle": + case "oci8": + case "oci8po": + case "oci805": + $lob_value = sprintf("empty_%s()", strtolower($ADODB_SESSION_USE_LOBS)); + break; + + // null for all other + default: + $lob_value = "null"; + break; + } + + // do we insert or update? => as for sesskey + $res = $ADODB_SESS_CONN->Execute("select count(*) as cnt from $ADODB_SESSION_TBL where sesskey = '$key'"); + if ($res && reset($res->fields) > 0) { + $qry = sprintf("update %s set expiry = %d, data = %s where sesskey = '%s'", $ADODB_SESSION_TBL, $expiry, $lob_value, $key); + } else { + // insert + $qry = sprintf("insert into %s (sesskey, expiry, data) values ('%s', %d, %s)", $ADODB_SESSION_TBL, $key, $expiry, $lob_value); + } + + $err = ""; + $rs1 = $ADODB_SESS_CONN->Execute($qry); + if (!$rs1) { + $err .= $ADODB_SESS_CONN->ErrorMsg()."\n"; + } + $rs2 = $ADODB_SESS_CONN->UpdateBlob($ADODB_SESSION_TBL, 'data', $val, "sesskey='$key'", strtoupper($ADODB_SESSION_USE_LOBS)); + if (!$rs2) { + $err .= $ADODB_SESS_CONN->ErrorMsg()."\n"; + } + $rs = ($rs1 && $rs2) ? true : false; + } + + if (!$rs) { + ADOConnection::outp( 'Session Replace: '.nl2br($err).'
',false); + } else { + // bug in access driver (could be odbc?) means that info is not commited + // properly unless select statement executed in Win2000 + if ($ADODB_SESS_CONN->databaseType == 'access') + $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); + } + return !empty($rs); +} + +function adodb_sess_destroy($key) +{ + global $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; + + if ($ADODB_SESSION_EXPIRE_NOTIFY) { + reset($ADODB_SESSION_EXPIRE_NOTIFY); + $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); + $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); + $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); + $ADODB_SESS_CONN->SetFetchMode($savem); + if ($rs) { + $ADODB_SESS_CONN->BeginTrans(); + while (!$rs->EOF) { + $ref = $rs->fields[0]; + $key = $rs->fields[1]; + $fn($ref,$key); + $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); + $rs->MoveNext(); + } + $ADODB_SESS_CONN->CommitTrans(); + } + } else { + $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE sesskey = '$key'"; + $rs = $ADODB_SESS_CONN->Execute($qry); + } + return $rs ? true : false; +} + +function adodb_sess_gc($maxlifetime) +{ + global $ADODB_SESS_DEBUG, $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; + + if ($ADODB_SESSION_EXPIRE_NOTIFY) { + reset($ADODB_SESSION_EXPIRE_NOTIFY); + $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); + $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); + $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE expiry < " . time()); + $ADODB_SESS_CONN->SetFetchMode($savem); + if ($rs) { + $ADODB_SESS_CONN->BeginTrans(); + while (!$rs->EOF) { + $ref = $rs->fields[0]; + $key = $rs->fields[1]; + $fn($ref,$key); + $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); + $rs->MoveNext(); + } + $ADODB_SESS_CONN->CommitTrans(); + } + } else { + $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE expiry < " . time(); + $ADODB_SESS_CONN->Execute($qry); + + if ($ADODB_SESS_DEBUG) ADOConnection::outp("Garbage Collection: $qry
"); + } + // suggested by Cameron, "GaM3R"$msg
"); + } + } + + return true; +} + +session_module_name('user'); +session_set_save_handler( + "adodb_sess_open", + "adodb_sess_close", + "adodb_sess_read", + "adodb_sess_write", + "adodb_sess_destroy", + "adodb_sess_gc"); +} + +/* TEST SCRIPT -- UNCOMMENT */ + +if (0) { +GLOBAL $HTTP_SESSION_VARS; + + session_start(); + session_register('AVAR'); + $HTTP_SESSION_VARS['AVAR'] += 1; + ADOConnection::outp( "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
",false); +} + +?> diff --git a/lib/adodb/adodb-session.php b/lib/adodb/adodb-session.php index d808b06d7c..4a2aefdfb4 100644 --- a/lib/adodb/adodb-session.php +++ b/lib/adodb/adodb-session.php @@ -1,379 +1,398 @@ -\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}"; - -To force non-persistent connections, call adodb_session_open first before session_start(): - - GLOBAL $HTTP_SESSION_VARS; - include('adodb.inc.php'); - include('adodb-session.php'); - adodb_sess_open(false,false,false); - session_start(); - session_register('AVAR'); - $HTTP_SESSION_VARS['AVAR'] += 1; - print "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
"; - - - Installation - ============ - 1. Create this table in your database (syntax might vary depending on your db): - - create table sessions ( - SESSKEY char(32) not null, - EXPIRY int(11) unsigned not null, - EXPIREREF varchar(64), - DATA text not null, - primary key (sesskey) - ); - - - 2. Then define the following parameters in this file: - $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; - $ADODB_SESSION_CONNECT='server to connect to'; - $ADODB_SESSION_USER ='user'; - $ADODB_SESSION_PWD ='password'; - $ADODB_SESSION_DB ='database'; - $ADODB_SESSION_TBL = 'sessions' - - 3. Recommended is PHP 4.0.6 or later. There are documented - session bugs in earlier versions of PHP. - - 4. If you want to receive notifications when a session expires, then - you can tag a session with an EXPIREREF, and before the session - record is deleted, we can call a function that will pass the EXPIREREF - as the first parameter, and the session key as the second parameter. - - To do this, define a notification function, say NotifyFn: - - function NotifyFn($expireref, $sesskey) - { - } - - Then define a global variable, with the first parameter being the - global variable you would like to store in the EXPIREREF field, and - the second is the function name. - - In this example, we want to be notified when a user's session - has expired, so we store the user id in $USERID, and make this - the value stored in the EXPIREREF field: - - $ADODB_SESSION_EXPIRE_NOTIFY = array('USERID','NotifyFn'); -*/ - -if (!defined('_ADODB_LAYER')) { - include (dirname(__FILE__).'/adodb.inc.php'); -} - -if (!defined('ADODB_SESSION')) { - - define('ADODB_SESSION',1); - - /* if database time and system time is difference is greater than this, then give warning */ - define('ADODB_SESSION_SYNCH_SECS',60); - -/****************************************************************************************\ - Global definitions -\****************************************************************************************/ -GLOBAL $ADODB_SESSION_CONNECT, - $ADODB_SESSION_DRIVER, - $ADODB_SESSION_USER, - $ADODB_SESSION_PWD, - $ADODB_SESSION_DB, - $ADODB_SESS_CONN, - $ADODB_SESS_LIFE, - $ADODB_SESS_DEBUG, - $ADODB_SESSION_EXPIRE_NOTIFY, - $ADODB_SESSION_CRC; - - - $ADODB_SESS_LIFE = ini_get('session.gc_maxlifetime'); - if ($ADODB_SESS_LIFE <= 1) { - /* bug in PHP 4.0.3 pl 1 -- how about other versions? */ - /* print "Session: connection failed
",false); -} - -/****************************************************************************************\ - Close the connection -\****************************************************************************************/ -function adodb_sess_close() -{ -global $ADODB_SESS_CONN; - - if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close(); - return true; -} - -/****************************************************************************************\ - Slurp in the session variables and return the serialized string -\****************************************************************************************/ -function adodb_sess_read($key) -{ -global $ADODB_SESS_CONN,$ADODB_SESSION_TBL,$ADODB_SESSION_CRC; - - $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time()); - if ($rs) { - if ($rs->EOF) { - $v = ''; - } else - $v = rawurldecode(reset($rs->fields)); - - $rs->Close(); - - /* new optimization adodb 2.1 */ - $ADODB_SESSION_CRC = strlen($v).crc32($v); - - return $v; - } - - return ''; /* thx to Jorma Tuomainen, webmaster#wizactive.com */ -} - -/****************************************************************************************\ - Write the serialized data to a database. - - If the data has not been modified since adodb_sess_read(), we do not write. -\****************************************************************************************/ -function adodb_sess_write($key, $val) -{ - global - $ADODB_SESS_CONN, - $ADODB_SESS_LIFE, - $ADODB_SESSION_TBL, - $ADODB_SESS_DEBUG, - $ADODB_SESSION_CRC, - $ADODB_SESSION_EXPIRE_NOTIFY; - - $expiry = time() + $ADODB_SESS_LIFE; - - /* crc32 optimization since adodb 2.1 */ - /* now we only update expiry date, thx to sebastian thom in adodb 2.32 */ - if ($ADODB_SESSION_CRC !== false && $ADODB_SESSION_CRC == strlen($val).crc32($val)) { - if ($ADODB_SESS_DEBUG) echo "Session: Only updating date - crc32 not changed
"; - $qry = "UPDATE $ADODB_SESSION_TBL SET expiry=$expiry WHERE sesskey='$key' AND expiry >= " . time(); - $rs = $ADODB_SESS_CONN->Execute($qry); - return true; - } - $val = rawurlencode($val); - - $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val); - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - $var = reset($ADODB_SESSION_EXPIRE_NOTIFY); - global $$var; - $arr['expireref'] = $$var; - } - $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,$arr, - 'sesskey',$autoQuote = true); - - if (!$rs) { - ADOConnection::outp( 'Session Replace: '.$ADODB_SESS_CONN->ErrorMsg().'
',false); - } else { - /* bug in access driver (could be odbc?) means that info is not commited */ - /* properly unless select statement executed in Win2000 */ - if ($ADODB_SESS_CONN->databaseType == 'access') - $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); - } - return !empty($rs); -} - -function adodb_sess_destroy($key) -{ - global $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; - - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - reset($ADODB_SESSION_EXPIRE_NOTIFY); - $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); - $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); - $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $ADODB_SESS_CONN->SetFetchMode($savem); - if ($rs) { - $ADODB_SESS_CONN->BeginTrans(); - while (!$rs->EOF) { - $ref = $rs->fields[0]; - $key = $rs->fields[1]; - $fn($ref,$key); - $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $rs->MoveNext(); - } - $ADODB_SESS_CONN->CommitTrans(); - } - } else { - $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE sesskey = '$key'"; - $rs = $ADODB_SESS_CONN->Execute($qry); - } - return $rs ? true : false; -} - -function adodb_sess_gc($maxlifetime) -{ - global $ADODB_SESS_DEBUG, $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; - - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - reset($ADODB_SESSION_EXPIRE_NOTIFY); - $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); - $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); - $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE expiry < " . time()); - $ADODB_SESS_CONN->SetFetchMode($savem); - if ($rs) { - $ADODB_SESS_CONN->BeginTrans(); - while (!$rs->EOF) { - $ref = $rs->fields[0]; - $key = $rs->fields[1]; - $fn($ref,$key); - $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $rs->MoveNext(); - } - $ADODB_SESS_CONN->CommitTrans(); - } - } else { - $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE expiry < " . time(); - $ADODB_SESS_CONN->Execute($qry); - - if ($ADODB_SESS_DEBUG) ADOConnection::outp("Garbage Collection: $qry
"); - } - /* suggested by Cameron, "GaM3R"$msg
"); - } - } - - return true; -} - -session_module_name('user'); -session_set_save_handler( - "adodb_sess_open", - "adodb_sess_close", - "adodb_sess_read", - "adodb_sess_write", - "adodb_sess_destroy", - "adodb_sess_gc"); -} - -/* TEST SCRIPT -- UNCOMMENT */ - -if (0) { -GLOBAL $HTTP_SESSION_VARS; - - session_start(); - session_register('AVAR'); - $HTTP_SESSION_VARS['AVAR'] += 1; - ADOConnection::outp( "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
",false); -} - +\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}"; + +To force non-persistent connections, call adodb_session_open first before session_start(): + + GLOBAL $HTTP_SESSION_VARS; + include('adodb.inc.php'); + include('adodb-session.php'); + adodb_sess_open(false,false,false); + session_start(); + session_register('AVAR'); + $HTTP_SESSION_VARS['AVAR'] += 1; + print "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
"; + + + Installation + ============ + 1. Create this table in your database (syntax might vary depending on your db): + + create table sessions ( + SESSKEY char(32) not null, + EXPIRY int(11) unsigned not null, + EXPIREREF varchar(64), + DATA text not null, + primary key (sesskey) + ); + + For oracle: + create table sessions ( + SESSKEY char(32) not null, + EXPIRY DECIMAL(16) not null, + EXPIREREF varchar(64), + DATA varchar(4000) not null, + primary key (sesskey) + ); + + + 2. Then define the following parameters. You can either modify + this file, or define them before this file is included: + + $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; + $ADODB_SESSION_CONNECT='server to connect to'; + $ADODB_SESSION_USER ='user'; + $ADODB_SESSION_PWD ='password'; + $ADODB_SESSION_DB ='database'; + $ADODB_SESSION_TBL = 'sessions' + + 3. Recommended is PHP 4.0.6 or later. There are documented + session bugs in earlier versions of PHP. + + 4. If you want to receive notifications when a session expires, then + you can tag a session with an EXPIREREF, and before the session + record is deleted, we can call a function that will pass the EXPIREREF + as the first parameter, and the session key as the second parameter. + + To do this, define a notification function, say NotifyFn: + + function NotifyFn($expireref, $sesskey) + { + } + + Then you need to define a global variable $ADODB_SESSION_EXPIRE_NOTIFY. + This is an array with 2 elements, the first being the name of the variable + you would like to store in the EXPIREREF field, and the 2nd is the + notification function's name. + + In this example, we want to be notified when a user's session + has expired, so we store the user id in the global variable $USERID, + store this value in the EXPIREREF field: + + $ADODB_SESSION_EXPIRE_NOTIFY = array('USERID','NotifyFn'); + + Then when the NotifyFn is called, we are passed the $USERID as the first + parameter, eg. NotifyFn($userid, $sesskey). +*/ + +if (!defined('_ADODB_LAYER')) { + include (dirname(__FILE__).'/adodb.inc.php'); +} + +if (!defined('ADODB_SESSION')) { + + define('ADODB_SESSION',1); + + /* if database time and system time is difference is greater than this, then give warning */ + define('ADODB_SESSION_SYNCH_SECS',60); + +/****************************************************************************************\ + Global definitions +\****************************************************************************************/ +GLOBAL $ADODB_SESSION_CONNECT, + $ADODB_SESSION_DRIVER, + $ADODB_SESSION_USER, + $ADODB_SESSION_PWD, + $ADODB_SESSION_DB, + $ADODB_SESS_CONN, + $ADODB_SESS_LIFE, + $ADODB_SESS_DEBUG, + $ADODB_SESSION_EXPIRE_NOTIFY, + $ADODB_SESSION_CRC; + + + $ADODB_SESS_LIFE = ini_get('session.gc_maxlifetime'); + if ($ADODB_SESS_LIFE <= 1) { + // bug in PHP 4.0.3 pl 1 -- how about other versions? + //print "Session: connection failed
",false); +} + +/****************************************************************************************\ + Close the connection +\****************************************************************************************/ +function adodb_sess_close() +{ +global $ADODB_SESS_CONN; + + if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close(); + return true; +} + +/****************************************************************************************\ + Slurp in the session variables and return the serialized string +\****************************************************************************************/ +function adodb_sess_read($key) +{ +global $ADODB_SESS_CONN,$ADODB_SESSION_TBL,$ADODB_SESSION_CRC; + + $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time()); + if ($rs) { + if ($rs->EOF) { + $v = ''; + } else + $v = rawurldecode(reset($rs->fields)); + + $rs->Close(); + + // new optimization adodb 2.1 + $ADODB_SESSION_CRC = strlen($v).crc32($v); + + return $v; + } + + return ''; // thx to Jorma Tuomainen, webmaster#wizactive.com +} + +/****************************************************************************************\ + Write the serialized data to a database. + + If the data has not been modified since adodb_sess_read(), we do not write. +\****************************************************************************************/ +function adodb_sess_write($key, $val) +{ + global + $ADODB_SESS_CONN, + $ADODB_SESS_LIFE, + $ADODB_SESSION_TBL, + $ADODB_SESS_DEBUG, + $ADODB_SESSION_CRC, + $ADODB_SESSION_EXPIRE_NOTIFY; + + $expiry = time() + $ADODB_SESS_LIFE; + + // crc32 optimization since adodb 2.1 + // now we only update expiry date, thx to sebastian thom in adodb 2.32 + if ($ADODB_SESSION_CRC !== false && $ADODB_SESSION_CRC == strlen($val).crc32($val)) { + if ($ADODB_SESS_DEBUG) echo "Session: Only updating date - crc32 not changed
"; + $qry = "UPDATE $ADODB_SESSION_TBL SET expiry=$expiry WHERE sesskey='$key' AND expiry >= " . time(); + $rs = $ADODB_SESS_CONN->Execute($qry); + return true; + } + $val = rawurlencode($val); + + $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val); + if ($ADODB_SESSION_EXPIRE_NOTIFY) { + $var = reset($ADODB_SESSION_EXPIRE_NOTIFY); + global $$var; + $arr['expireref'] = $$var; + } + $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,$arr, + 'sesskey',$autoQuote = true); + + if (!$rs) { + ADOConnection::outp( 'Session Replace: '.$ADODB_SESS_CONN->ErrorMsg().'
',false); + } else { + // bug in access driver (could be odbc?) means that info is not commited + // properly unless select statement executed in Win2000 + if ($ADODB_SESS_CONN->databaseType == 'access') + $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); + } + return !empty($rs); +} + +function adodb_sess_destroy($key) +{ + global $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; + + if ($ADODB_SESSION_EXPIRE_NOTIFY) { + reset($ADODB_SESSION_EXPIRE_NOTIFY); + $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); + $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); + $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); + $ADODB_SESS_CONN->SetFetchMode($savem); + if ($rs) { + $ADODB_SESS_CONN->BeginTrans(); + while (!$rs->EOF) { + $ref = $rs->fields[0]; + $key = $rs->fields[1]; + $fn($ref,$key); + $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); + $rs->MoveNext(); + } + $ADODB_SESS_CONN->CommitTrans(); + } + } else { + $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE sesskey = '$key'"; + $rs = $ADODB_SESS_CONN->Execute($qry); + } + return $rs ? true : false; +} + +function adodb_sess_gc($maxlifetime) +{ + global $ADODB_SESS_DEBUG, $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; + + if ($ADODB_SESSION_EXPIRE_NOTIFY) { + reset($ADODB_SESSION_EXPIRE_NOTIFY); + $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); + $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); + $rs =& $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE expiry < " . time()); + $ADODB_SESS_CONN->SetFetchMode($savem); + if ($rs) { + $ADODB_SESS_CONN->BeginTrans(); + while (!$rs->EOF) { + $ref = $rs->fields[0]; + $key = $rs->fields[1]; + $fn($ref,$key); + $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); + $rs->MoveNext(); + } + $ADODB_SESS_CONN->CommitTrans(); + } + } else { + $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE expiry < " . time(); + $ADODB_SESS_CONN->Execute($qry); + + if ($ADODB_SESS_DEBUG) ADOConnection::outp("Garbage Collection: $qry
"); + } + // suggested by Cameron, "GaM3R"$msg
"); + } + } + + return true; +} + +session_module_name('user'); +session_set_save_handler( + "adodb_sess_open", + "adodb_sess_close", + "adodb_sess_read", + "adodb_sess_write", + "adodb_sess_destroy", + "adodb_sess_gc"); +} + +/* TEST SCRIPT -- UNCOMMENT */ + +if (0) { +GLOBAL $HTTP_SESSION_VARS; + + session_start(); + session_register('AVAR'); + $HTTP_SESSION_VARS['AVAR'] += 1; + ADOConnection::outp( "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
",false); +} + ?> \ No newline at end of file diff --git a/lib/adodb/adodb-time.inc.php b/lib/adodb/adodb-time.inc.php index 5a0f6cac36..5ea9c9eef9 100644 --- a/lib/adodb/adodb-time.inc.php +++ b/lib/adodb/adodb-time.inc.php @@ -1,868 +1,899 @@ - 4 digit year conversion. The maximum is billions of years in the -future, but this is a theoretical limit as the computation of that year -would take too long with the current implementation of adodb_mktime(). - -This library replaces native functions as follows: - -- getdate() with adodb_getdate() - date() with adodb_date() - gmdate() with adodb_gmdate() - mktime() with adodb_mktime() - gmmktime() with adodb_gmmktime()45 -- -The parameters are identical, except that adodb_date() accepts a subset -of date()'s field formats. Mktime() will convert from local time to GMT, -and date() will convert from GMT to local time, but daylight savings is -not handled currently. - -This library is independant of the rest of ADOdb, and can be used -as standalone code. - -PERFORMANCE - -For high speed, this library uses the native date functions where -possible, and only switches to PHP code when the dates fall outside -the 32-bit signed integer range. - -GREGORIAN CORRECTION - -Pope Gregory shortened October of A.D. 1582 by ten days. Thursday, -October 4, 1582 (Julian) was followed immediately by Friday, October 15, -1582 (Gregorian). - -Since 0.06, we handle this correctly, so: - -adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582) - == 24 * 3600 (1 day) - -============================================================================= - -COPYRIGHT - -(c) 2003 John Lim and released under BSD-style license except for code by jackbbs, -which includes adodb_mktime, adodb_get_gmt_different, adodb_is_leap_year -and originally found at http://www.php.net/manual/en/function.mktime.php - -============================================================================= - -BUG REPORTS - -These should be posted to the ADOdb forums at - - http://phplens.com/lens/lensforum/topics.php?id=4 - -============================================================================= - -FUNCTION DESCRIPTIONS - - -FUNCTION adodb_getdate($date=false) - -Returns an array containing date information, as getdate(), but supports -dates greater than 1901 to 2038. - - -FUNCTION adodb_date($fmt, $timestamp = false) - -Convert a timestamp to a formatted local date. If $timestamp is not defined, the -current timestamp is used. Unlike the function date(), it supports dates -outside the 1901 to 2038 range. - -The format fields that adodb_date supports: - -
-a - "am" or "pm" -A - "AM" or "PM" -d - day of the month, 2 digits with leading zeros; i.e. "01" to "31" -D - day of the week, textual, 3 letters; e.g. "Fri" -F - month, textual, long; e.g. "January" -g - hour, 12-hour format without leading zeros; i.e. "1" to "12" -G - hour, 24-hour format without leading zeros; i.e. "0" to "23" -h - hour, 12-hour format; i.e. "01" to "12" -H - hour, 24-hour format; i.e. "00" to "23" -i - minutes; i.e. "00" to "59" -j - day of the month without leading zeros; i.e. "1" to "31" -l (lowercase 'L') - day of the week, textual, long; e.g. "Friday" -L - boolean for whether it is a leap year; i.e. "0" or "1" -m - month; i.e. "01" to "12" -M - month, textual, 3 letters; e.g. "Jan" -n - month without leading zeros; i.e. "1" to "12" -O - Difference to Greenwich time in hours; e.g. "+0200" -r - RFC 822 formatted date; e.g. "Thu, 21 Dec 2000 16:01:07 +0200" -s - seconds; i.e. "00" to "59" -S - English ordinal suffix for the day of the month, 2 characters; - i.e. "st", "nd", "rd" or "th" -t - number of days in the given month; i.e. "28" to "31" -T - Timezone setting of this machine; e.g. "EST" or "MDT" -U - seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) -w - day of the week, numeric, i.e. "0" (Sunday) to "6" (Saturday) -Y - year, 4 digits; e.g. "1999" -y - year, 2 digits; e.g. "99" -z - day of the year; i.e. "0" to "365" -Z - timezone offset in seconds (i.e. "-43200" to "43200"). - The offset for timezones west of UTC is always negative, - and for those east of UTC is always positive. -- -Unsupported: -
-B - Swatch Internet time -I (capital i) - "1" if Daylight Savings Time, "0" otherwise. -W - ISO-8601 week number of year, weeks starting on Monday - -- - -FUNCTION adodb_gmdate($fmt, $timestamp = false) - -Convert a timestamp to a formatted GMT date. If $timestamp is not defined, the -current timestamp is used. Unlike the function date(), it supports dates -outside the 1901 to 2038 range. - - -FUNCTION adodb_mktime($hr, $min, $sec, $month, $day, $year) - -Converts a local date to a unix timestamp. Unlike the function mktime(), it supports -dates outside the 1901 to 2038 range. Differs from mktime() in that all parameters -are currently compulsory. - -FUNCTION adodb_gmmktime($hr, $min, $sec, $month, $day, $year) - -Converts a gmt date to a unix timestamp. Unlike the function gmmktime(), it supports -dates outside the 1901 to 2038 range. Differs from gmmktime() in that all parameters -are currently compulsory. - -============================================================================= - -NOTES - -Useful url for generating test timestamps: - http://www.4webhelp.net/us/timestamp.php - -Possible future optimizations include - -a. Using an algorithm similar to Plauger's in "The Standard C Library" -(page 428, xttotm.c _Ttotm() function). Plauger's algorithm will not -work outside 32-bit signed range, so i decided not to implement it. - -b. Iterate over a block of years (say 12) when searching for the -correct year. - -c. Implement daylight savings, which looks awfully complicated, see - http://webexhibits.org/daylightsaving/ - - -CHANGELOG -- 3 March 2003 0.08 -Added support for 'S' adodb_date() format char. Added constant ADODB_ALLOW_NEGATIVE_TS -if you want PHP to handle negative timestamps between 1901 to 1969. - -- 27 Feb 2003 0.07 -All negative numbers handled by adodb now because of RH 7.3+ problems. -See http://bugs.php.net/bug.php?id=20048&edit=2 - -- 4 Feb 2003 0.06 -Fixed a typo, 1852 changed to 1582! This means that pre-1852 dates -are now correctly handled. - -- 29 Jan 2003 0.05 - -Leap year checking differs under Julian calendar (pre 1582). Also -leap year code optimized by checking for most common case first. - -We also handle month overflow correctly in mktime (eg month set to 13). - -Day overflow for less than one month's days is supported. - -- 28 Jan 2003 0.04 - -Gregorian correction handled. In PHP5, we might throw an error if -mktime uses invalid dates around 5-14 Oct 1582. Released with ADOdb 3.10. -Added limbo 5-14 Oct 1582 check, when we set to 15 Oct 1582. - -- 27 Jan 2003 0.03 - -Fixed some more month problems due to gmt issues. Added constant ADODB_DATE_VERSION. -Fixed calculation of days since start of year for <1970. - -- 27 Jan 2003 0.02 - -Changed _adodb_getdate() to inline leap year checking for better performance. -Fixed problem with time-zones west of GMT +0000. - -- 24 Jan 2003 0.01 - -First implementation. -*/ - - -/* Initialization */ - -/* - Version Number -*/ -define('ADODB_DATE_VERSION',0.08); - -/* - We check for Windows as only +ve ints are accepted as dates on Windows. - - Apparently this problem happens also with Linux, RH 7.3 and later! - - glibc-2.2.5-34 and greater has been changed to return -1 for dates < - 1970. This used to work. The problem exists with RedHat 7.3 and 8.0 - echo (mktime(0, 0, 0, 1, 1, 1960)); // prints -1 - - References: - http://bugs.php.net/bug.php?id=20048&edit=2 - http://lists.debian.org/debian-glibc/2002/debian-glibc-200205/msg00010.html -*/ - -if (!defined('ADODB_ALLOW_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1); - -function adodb_date_test_date($y1,$m) -{ - /* print " $y1/$m "; */ - $t = adodb_mktime(0,0,0,$m,13,$y1); - if ("$y1-$m-13 00:00:00" != adodb_date('Y-n-d H:i:s',$t)) { - print "$y1 error
Testing gregorian <=> julian conversion
";
- $t = adodb_mktime(0,0,0,10,11,1492);
- /* http://www.holidayorigins.com/html/columbus_day.html - Friday check */
- if (!(adodb_date('D Y-m-d',$t) == 'Fri 1492-10-11')) print 'Error in Columbus landing
';
-
- $t = adodb_mktime(0,0,0,2,29,1500);
- if (!(adodb_date('Y-m-d',$t) == '1500-02-29')) print 'Error in julian leap years
';
-
- $t = adodb_mktime(0,0,0,2,29,1700);
- if (!(adodb_date('Y-m-d',$t) == '1700-03-01')) print 'Error in gregorian leap years
';
-
- print adodb_mktime(0,0,0,10,4,1582).' ';
- print adodb_mktime(0,0,0,10,15,1582);
- $diff = (adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582));
- if ($diff != 3600*24) print " Error in gregorian correction = ".($diff/3600/24)." days
";
-
- print " 15 Oct 1582, Fri=".(adodb_dow(1582,10,15) == 5 ? 'Fri' : 'Error')."
";
- print " 4 Oct 1582, Thu=".(adodb_dow(1582,10,4) == 4 ? 'Thu' : 'Error')."
";
-
- print "
Testing overflow
";
-
- $t = adodb_mktime(0,0,0,3,33,1965);
- if (!(adodb_date('Y-m-d',$t) == '1965-04-02')) print 'Error in day overflow 1
';
- $t = adodb_mktime(0,0,0,4,33,1971);
- if (!(adodb_date('Y-m-d',$t) == '1971-05-03')) print 'Error in day overflow 2
';
- $t = adodb_mktime(0,0,0,1,60,1965);
- if (!(adodb_date('Y-m-d',$t) == '1965-03-01')) print 'Error in day overflow 3 '.adodb_date('Y-m-d',$t).'
';
- $t = adodb_mktime(0,0,0,12,32,1965);
- if (!(adodb_date('Y-m-d',$t) == '1966-01-01')) print 'Error in day overflow 4 '.adodb_date('Y-m-d',$t).'
';
- $t = adodb_mktime(0,0,0,12,63,1965);
- if (!(adodb_date('Y-m-d',$t) == '1966-02-01')) print 'Error in day overflow 5 '.adodb_date('Y-m-d',$t).'
';
- $t = adodb_mktime(0,0,0,13,3,1965);
- if (!(adodb_date('Y-m-d',$t) == '1966-01-03')) print 'Error in mth overflow 1
';
-
- print "Testing 2-digit => 4-digit year conversion
";
- if (adodb_year_digit_check(00) != 2000) print "Err 2-digit 2000
";
- if (adodb_year_digit_check(10) != 2010) print "Err 2-digit 2010
";
- if (adodb_year_digit_check(20) != 2020) print "Err 2-digit 2020
";
- if (adodb_year_digit_check(30) != 2030) print "Err 2-digit 2030
";
- if (adodb_year_digit_check(40) != 1940) print "Err 2-digit 1940
";
- if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950
";
- if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990
";
-
- /* Test string formating */
- print "
Testing date formating
"; - $fmt = '\d\a\t\e T Y-m-d H:i:s a A d D F g G h H i j l L m M n O \R\F\C822 r s t U w y Y z Z 2003'; - $s1 = date($fmt,0); - $s2 = adodb_date($fmt,0); - if ($s1 != $s2) { - print " date() 0 failed"; */
- $pos = strcmp($s1,$s2);
-
- if (($s1) != ($s2)) {
- for ($j=0,$k=strlen($s1); $j < $k; $j++) {
- if ($s1[$j] != $s2[$j]) {
- print substr($s1,$j).' ';
- break;
- }
- }
- print "Error date(): $ts
- \"$s1\" (date len=".strlen($s1).")
- \"$s2\" (adodb_date len=".strlen($s2).")
";
- $fail = true;
- }
-
- $a1 = getdate($ts);
- $a2 = adodb_getdate($ts);
- $rez = array_diff($a1,$a2);
- if (sizeof($rez)>0) {
- print "Error getdate() $ts
";
- print_r($a1);
- print "
";
- print_r($a2);
- print "
"; - $fail = true; - } - } - - /* Test generation of dates outside 1901-2038 */ - print "
Testing random dates between 100 and 4000
"; - adodb_date_test_date(100,1); - for ($i=100; --$i >= 0;) { - $y1 = 100+rand(0,1970-100); - $m = rand(1,12); - adodb_date_test_date($y1,$m); - - $y1 = 3000-rand(0,3000-1970); - adodb_date_test_date($y1,$m); - } - print ''; - $start = 1960+rand(0,10); - $yrs = 12; - $i = 365.25*86400*($start-1970); - $offset = 36000+rand(10000,60000); - $max = 365*$yrs*86400; - $lastyear = 0; - - /* we generate a timestamp, convert it to a date, and convert it back to a timestamp */ - /* and check if the roundtrip broke the original timestamp value. */ - print "Testing $start to ".($start+$yrs).", or $max seconds, offset=$offset: "; - - for ($max += $i; $i < $max; $i += $offset) { - $ret = adodb_date('m,d,Y,H,i,s',$i); - $arr = explode(',',$ret); - if ($lastyear != $arr[2]) { - $lastyear = $arr[2]; - print " $lastyear "; - flush(); - } - $newi = adodb_mktime($arr[3],$arr[4],$arr[5],$arr[0],$arr[1],$arr[2]); - if ($i != $newi) { - print "Error at $i, adodb_mktime returned $newi ($ret)"; - $fail = true; - break; - } - } - - if (!$fail) print "
Passed !
"; - else print "Failed :-(
"; -} - -/** - Returns day of week, 0 = Sunday,... 6=Saturday. - Algorithm from PEAR::Date_Calc -*/ -function adodb_dow($year, $month, $day) -{ -/* -Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and -proclaimed that from that time onwards 3 days would be dropped from the calendar -every 400 years. - -Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian). -*/ - if ($year <= 1582) { - if ($year < 1582 || - ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) $greg_correction = 3; - else - $greg_correction = 0; - } else - $greg_correction = 0; - - if($month > 2) - $month -= 2; - else { - $month += 10; - $year--; - } - - $day = ( floor((13 * $month - 1) / 5) + - $day + ($year % 100) + - floor(($year % 100) / 4) + - floor(($year / 100) / 4) - 2 * - floor($year / 100) + 77); - - return (($day - 7 * floor($day / 7))) + $greg_correction; -} - - -/** - Checks for leap year, returns true if it is. No 2-digit year check. Also - handles julian calendar correctly. -*/ -function _adodb_is_leap_year($year) -{ - if ($year % 4 != 0) return false; - - if ($year % 400 == 0) { - return true; - /* if gregorian calendar (>1582), century not-divisible by 400 is not leap */ - } else if ($year > 1582 && $year % 100 == 0 ) { - return false; - } - - return true; -} - -/** - checks for leap year, returns true if it is. Has 2-digit year check -*/ -function adodb_is_leap_year($year) -{ - return _adodb_is_leap_year(adodb_year_digit_check($year)); -} - -/** - Fix 2-digit years. Works for any century. - Assumes that if 2-digit is more than 30 years in future, then previous century. -*/ -function adodb_year_digit_check($y) -{ - if ($y < 100) { - - $yr = (integer) date("Y"); - $century = (integer) ($yr /100); - - if ($yr%100 > 50) { - $c1 = $century + 1; - $c0 = $century; - } else { - $c1 = $century; - $c0 = $century - 1; - } - $c1 *= 100; - /* if 2-digit year is less than 30 years in future, set it to this century */ - /* otherwise if more than 30 years in future, then we set 2-digit year to the prev century. */ - if (($y + $c1) < $yr+30) $y = $y + $c1; - else $y = $y + $c0*100; - } - return $y; -} - -/** - get local time zone offset from GMT -*/ -function adodb_get_gmt_different() -{ -static $DIFF; - if (isset($DIFF)) return $DIFF; - - $DIFF = mktime(0,0,0,1,2,1970) - gmmktime(0,0,0,1,2,1970); - return $DIFF; -} - -/** - Returns an array with date info. -*/ -function adodb_getdate($d=false,$fast=false) -{ - if ($d === false) return getdate(); - if (!defined('ADODB_TEST_DATES')) { - if ((abs($d) <= 0x7FFFFFFF)) { /* check if number in 32-bit signed range */ - if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) /* if windows, must be +ve integer */ - return @getdate($d); - } - } - return _adodb_getdate($d); -} - -/** - Low-level function that returns the getdate() array. We have a special - $fast flag, which if set to true, will return fewer array values, - and is much faster as it does not calculate dow, etc. -*/ -function _adodb_getdate($origd=false,$fast=false,$is_gmt=false) -{ - $d = $origd - ($is_gmt ? 0 : adodb_get_gmt_different()); - - $_day_power = 86400; - $_hour_power = 3600; - $_min_power = 60; - - if ($d < -12219321600) $d -= 86400*10; /* if 15 Oct 1582 or earlier, gregorian correction */ - - $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); - $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); - - if ($d < 0) { - $origd = $d; - /* The valid range of a 32bit signed timestamp is typically from */ - /* Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT */ - for ($a = 1970 ; --$a >= 0;) { - $lastd = $d; - - if ($leaf = _adodb_is_leap_year($a)) { - $d += $_day_power * 366; - } else - $d += $_day_power * 365; - if ($d >= 0) { - $year = $a; - break; - } - } - - $secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd; - - $d = $lastd; - $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; - for ($a = 13 ; --$a > 0;) { - $lastd = $d; - $d += $mtab[$a] * $_day_power; - if ($d >= 0) { - $month = $a; - $ndays = $mtab[$a]; - break; - } - } - - $d = $lastd; - $day = $ndays + ceil(($d+1) / ($_day_power)); - - $d += ($ndays - $day+1)* $_day_power; - $hour = floor($d/$_hour_power); - - } else { - - for ($a = 1970 ;; $a++) { - $lastd = $d; - - if ($leaf = _adodb_is_leap_year($a)) { - $d -= $_day_power * 366; - } else - $d -= $_day_power * 365; - if ($d <= 0) { - $year = $a; - break; - } - } - $secsInYear = $lastd; - $d = $lastd; - $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; - for ($a = 1 ; $a <= 12; $a++) { - $lastd = $d; - $d -= $mtab[$a] * $_day_power; - if ($d <= 0) { - $month = $a; - $ndays = $mtab[$a]; - break; - } - } - $d = $lastd; - $day = ceil(($d+1) / $_day_power); - $d = $d - ($day-1) * $_day_power; - $hour = floor($d /$_hour_power); - } - - $d -= $hour * $_hour_power; - $min = floor($d/$_min_power); - $secs = $d - $min * $_min_power; - if ($fast) { - return array( - 'seconds' => $secs, - 'minutes' => $min, - 'hours' => $hour, - 'mday' => $day, - 'mon' => $month, - 'year' => $year, - 'yday' => floor($secsInYear/$_day_power), - 'leap' => $leaf, - 'ndays' => $ndays - ); - } - - - $dow = adodb_dow($year,$month,$day); - - return array( - 'seconds' => $secs, - 'minutes' => $min, - 'hours' => $hour, - 'mday' => $day, - 'wday' => $dow, - 'mon' => $month, - 'year' => $year, - 'yday' => floor($secsInYear/$_day_power), - 'weekday' => gmdate('l',$_day_power*(3+$dow)), - 'month' => gmdate('F',mktime(0,0,0,$month,2,1971)), - 0 => $origd - ); -} - -function adodb_gmdate($fmt,$d=false) -{ - return adodb_date($fmt,$d,true); -} - - -/** - Return formatted date based on timestamp $d -*/ -function adodb_date($fmt,$d=false,$is_gmt=false) -{ - if ($d === false) return date($fmt); - if (!defined('ADODB_TEST_DATES')) { - if ((abs($d) <= 0x7FFFFFFF)) { /* check if number in 32-bit signed range */ - if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) /* if windows, must be +ve integer */ - return @date($fmt,$d); - } - } - $_day_power = 86400; - - $arr = _adodb_getdate($d,true,$is_gmt); - $year = $arr['year']; - $month = $arr['mon']; - $day = $arr['mday']; - $hour = $arr['hours']; - $min = $arr['minutes']; - $secs = $arr['seconds']; - - $max = strlen($fmt); - $dates = ''; - - /* - at this point, we have the following integer vars to manipulate: - $year, $month, $day, $hour, $min, $secs - */ - for ($i=0; $i < $max; $i++) { - switch($fmt[$i]) { - case 'T': $dates .= date('T');break; - /* YEAR */ - case 'L': $dates .= $arr['leap'] ? '1' : '0'; break; - case 'r': /* Thu, 21 Dec 2000 16:01:07 +0200 */ - - $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))).', ' - . ($day<10?' '.$day:$day) . ' '.date('M',mktime(0,0,0,$month,2,1971)).' '.$year.' '; - - if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour; - - if ($min < 10) $dates .= ':0'.$min; else $dates .= ':'.$min; - - if ($secs < 10) $dates .= ':0'.$secs; else $dates .= ':'.$secs; - - $gmt = adodb_get_gmt_different(); - $dates .= sprintf(' %s%04d',($gmt<0)?'+':'-',abs($gmt)/36); break; - - case 'Y': $dates .= $year; break; - case 'y': $dates .= substr($year,strlen($year)-2,2); break; - /* MONTH */ - case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break; - case 'n': $dates .= $month; break; - case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break; - case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break; - /* DAY */ - case 't': $dates .= $arr['ndays']; break; - case 'z': $dates .= $arr['yday']; break; - case 'w': $dates .= adodb_dow($year,$month,$day); break; - case 'l': $dates .= gmdate('l',$_day_power*(3+adodb_dow($year,$month,$day))); break; - case 'D': $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))); break; - case 'j': $dates .= $day; break; - case 'd': if ($day<10) $dates .= '0'.$day; else $dates .= $day; break; - case 'S': - $d10 = $day % 10; - if ($d10 == 1) $dates .= 'st'; - else if ($d10 == 2) $dates .= 'nd'; - else if ($d10 == 3) $dates .= 'rd'; - else $dates .= 'th'; - break; - - /* HOUR */ - case 'Z': - $dates .= ($is_gmt) ? 0 : -adodb_get_gmt_different(); break; - case 'O': - $gmt = ($is_gmt) ? 0 : adodb_get_gmt_different(); - $dates .= sprintf('%s%04d',($gmt<0)?'+':'-',abs($gmt)/36); break; - - case 'H': - if ($hour < 10) $dates .= '0'.$hour; - else $dates .= $hour; - break; - case 'h': - if ($hour > 12) $hh = $hour - 12; - else { - if ($hour == 0) $hh = '12'; - else $hh = $hour; - } - - if ($hh < 10) $dates .= '0'.$hh; - else $dates .= $hh; - break; - - case 'G': - $dates .= $hour; - break; - - case 'g': - if ($hour > 12) $hh = $hour - 12; - else { - if ($hour == 0) $hh = '12'; - else $hh = $hour; - } - $dates .= $hh; - break; - /* MINUTES */ - case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break; - /* SECONDS */ - case 'U': $dates .= $d; break; - case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break; - /* AM/PM */ - /* Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM */ - case 'a': - if ($hour>=12) $dates .= 'pm'; - else $dates .= 'am'; - break; - case 'A': - if ($hour>=12) $dates .= 'PM'; - else $dates .= 'AM'; - break; - default: - $dates .= $fmt[$i]; break; - /* ESCAPE */ - case "\\": - $i++; - if ($i < $max) $dates .= $fmt[$i]; - break; - } - } - return $dates; -} - -/** - Returns a timestamp given a GMT/UTC time. - Note that $is_dst is not implemented and is ignored. -*/ -function adodb_gmmktime($hr,$min,$sec,$mon,$day,$year,$is_dst=false) -{ - return adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst,true); -} - -/** - Return a timestamp given a local time. Originally by jackbbs. - Note that $is_dst is not implemented and is ignored. -*/ -function adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst=false,$is_gmt=false) -{ - if (!defined('ADODB_TEST_DATES')) { - /* for windows, we don't check 1970 because with timezone differences, */ - /* 1 Jan 1970 could generate negative timestamp, which is illegal */ - if (!defined('ADODB_NO_NEGATIVE_TS') || ($year >= 1971)) - if (1901 < $year && $year < 2038) - return @mktime($hr,$min,$sec,$mon,$day,$year); - } - - $gmt_different = ($is_gmt) ? 0 : adodb_get_gmt_different(); - - $hr = intval($hr); - $min = intval($min); - $sec = intval($sec); - $mon = intval($mon); - $day = intval($day); - $year = intval($year); - - - $year = adodb_year_digit_check($year); - - if ($mon > 12) { - $y = floor($mon / 12); - $year += $y; - $mon -= $y*12; - } - - $_day_power = 86400; - $_hour_power = 3600; - $_min_power = 60; - - $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); - $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); - - $_total_date = 0; - if ($year >= 1970) { - for ($a = 1970 ; $a <= $year; $a++) { - $leaf = _adodb_is_leap_year($a); - if ($leaf == true) { - $loop_table = $_month_table_leaf; - $_add_date = 366; - } else { - $loop_table = $_month_table_normal; - $_add_date = 365; - } - if ($a < $year) { - $_total_date += $_add_date; - } else { - for($b=1;$b<$mon;$b++) { - $_total_date += $loop_table[$b]; - } - } - } - $_total_date +=$day-1; - $ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different; - - } else { - for ($a = 1969 ; $a >= $year; $a--) { - $leaf = _adodb_is_leap_year($a); - if ($leaf == true) { - $loop_table = $_month_table_leaf; - $_add_date = 366; - } else { - $loop_table = $_month_table_normal; - $_add_date = 365; - } - if ($a > $year) { $_total_date += $_add_date; - } else { - for($b=12;$b>$mon;$b--) { - $_total_date += $loop_table[$b]; - } - } - } - $_total_date += $loop_table[$mon] - $day; - - $_day_time = $hr * $_hour_power + $min * $_min_power + $sec; - $_day_time = $_day_power - $_day_time; - $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different); - if ($ret < -12220185600) $ret += 10*86400; /* if earlier than 5 Oct 1582 - gregorian correction */ - else if ($ret < -12219321600) $ret = -12219321600; /* if in limbo, reset to 15 Oct 1582. */ - } - /* print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret; */ - return $ret; -} - -?> + 4 digit year conversion. The maximum is billions of years in the +future, but this is a theoretical limit as the computation of that year +would take too long with the current implementation of adodb_mktime(). + +This library replaces native functions as follows: + ++ getdate() with adodb_getdate() + date() with adodb_date() + gmdate() with adodb_gmdate() + mktime() with adodb_mktime() + gmmktime() with adodb_gmmktime()45 ++ +The parameters are identical, except that adodb_date() accepts a subset +of date()'s field formats. Mktime() will convert from local time to GMT, +and date() will convert from GMT to local time, but daylight savings is +not handled currently. + +This library is independant of the rest of ADOdb, and can be used +as standalone code. + +PERFORMANCE + +For high speed, this library uses the native date functions where +possible, and only switches to PHP code when the dates fall outside +the 32-bit signed integer range. + +GREGORIAN CORRECTION + +Pope Gregory shortened October of A.D. 1582 by ten days. Thursday, +October 4, 1582 (Julian) was followed immediately by Friday, October 15, +1582 (Gregorian). + +Since 0.06, we handle this correctly, so: + +adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582) + == 24 * 3600 (1 day) + +============================================================================= + +COPYRIGHT + +(c) 2003 John Lim and released under BSD-style license except for code by jackbbs, +which includes adodb_mktime, adodb_get_gmt_different, adodb_is_leap_year +and originally found at http://www.php.net/manual/en/function.mktime.php + +============================================================================= + +BUG REPORTS + +These should be posted to the ADOdb forums at + + http://phplens.com/lens/lensforum/topics.php?id=4 + +============================================================================= + +FUNCTION DESCRIPTIONS + + +FUNCTION adodb_getdate($date=false) + +Returns an array containing date information, as getdate(), but supports +dates greater than 1901 to 2038. + + +FUNCTION adodb_date($fmt, $timestamp = false) + +Convert a timestamp to a formatted local date. If $timestamp is not defined, the +current timestamp is used. Unlike the function date(), it supports dates +outside the 1901 to 2038 range. + +The format fields that adodb_date supports: + +
+a - "am" or "pm" +A - "AM" or "PM" +d - day of the month, 2 digits with leading zeros; i.e. "01" to "31" +D - day of the week, textual, 3 letters; e.g. "Fri" +F - month, textual, long; e.g. "January" +g - hour, 12-hour format without leading zeros; i.e. "1" to "12" +G - hour, 24-hour format without leading zeros; i.e. "0" to "23" +h - hour, 12-hour format; i.e. "01" to "12" +H - hour, 24-hour format; i.e. "00" to "23" +i - minutes; i.e. "00" to "59" +j - day of the month without leading zeros; i.e. "1" to "31" +l (lowercase 'L') - day of the week, textual, long; e.g. "Friday" +L - boolean for whether it is a leap year; i.e. "0" or "1" +m - month; i.e. "01" to "12" +M - month, textual, 3 letters; e.g. "Jan" +n - month without leading zeros; i.e. "1" to "12" +O - Difference to Greenwich time in hours; e.g. "+0200" +Q - Quarter, as in 1, 2, 3, 4 +r - RFC 822 formatted date; e.g. "Thu, 21 Dec 2000 16:01:07 +0200" +s - seconds; i.e. "00" to "59" +S - English ordinal suffix for the day of the month, 2 characters; + i.e. "st", "nd", "rd" or "th" +t - number of days in the given month; i.e. "28" to "31" +T - Timezone setting of this machine; e.g. "EST" or "MDT" +U - seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) +w - day of the week, numeric, i.e. "0" (Sunday) to "6" (Saturday) +Y - year, 4 digits; e.g. "1999" +y - year, 2 digits; e.g. "99" +z - day of the year; i.e. "0" to "365" +Z - timezone offset in seconds (i.e. "-43200" to "43200"). + The offset for timezones west of UTC is always negative, + and for those east of UTC is always positive. ++ +Unsupported: +
+B - Swatch Internet time +I (capital i) - "1" if Daylight Savings Time, "0" otherwise. +W - ISO-8601 week number of year, weeks starting on Monday + ++ +FUNCTION adodb_date2($fmt, $isoDateString = false) +Same as adodb_date, but 2nd parameter accepts iso date, eg. + + adodb_date2('d-M-Y H:i','2003-12-25 13:01:34'); + +FUNCTION adodb_gmdate($fmt, $timestamp = false) + +Convert a timestamp to a formatted GMT date. If $timestamp is not defined, the +current timestamp is used. Unlike the function date(), it supports dates +outside the 1901 to 2038 range. + + +FUNCTION adodb_mktime($hr, $min, $sec, $month, $day, $year) + +Converts a local date to a unix timestamp. Unlike the function mktime(), it supports +dates outside the 1901 to 2038 range. Differs from mktime() in that all parameters +are currently compulsory. + +FUNCTION adodb_gmmktime($hr, $min, $sec, $month, $day, $year) + +Converts a gmt date to a unix timestamp. Unlike the function gmmktime(), it supports +dates outside the 1901 to 2038 range. Differs from gmmktime() in that all parameters +are currently compulsory. + +============================================================================= + +NOTES + +Useful url for generating test timestamps: + http://www.4webhelp.net/us/timestamp.php + +Possible future optimizations include + +a. Using an algorithm similar to Plauger's in "The Standard C Library" +(page 428, xttotm.c _Ttotm() function). Plauger's algorithm will not +work outside 32-bit signed range, so i decided not to implement it. + +b. Iterate over a block of years (say 12) when searching for the +correct year. + +c. Implement daylight savings, which looks awfully complicated, see + http://webexhibits.org/daylightsaving/ + + +CHANGELOG + +- 9 Aug 2003 0.10 +Fixed bug with dates after 2038. +See http://phplens.com/lens/lensforum/msgs.php?id=6980 + +- 1 July 2003 0.09 +Added support for Q (Quarter). +Added adodb_date2(), which accepts ISO date in 2nd param + +- 3 March 2003 0.08 +Added support for 'S' adodb_date() format char. Added constant ADODB_ALLOW_NEGATIVE_TS +if you want PHP to handle negative timestamps between 1901 to 1969. + +- 27 Feb 2003 0.07 +All negative numbers handled by adodb now because of RH 7.3+ problems. +See http://bugs.php.net/bug.php?id=20048&edit=2 + +- 4 Feb 2003 0.06 +Fixed a typo, 1852 changed to 1582! This means that pre-1852 dates +are now correctly handled. + +- 29 Jan 2003 0.05 + +Leap year checking differs under Julian calendar (pre 1582). Also +leap year code optimized by checking for most common case first. + +We also handle month overflow correctly in mktime (eg month set to 13). + +Day overflow for less than one month's days is supported. + +- 28 Jan 2003 0.04 + +Gregorian correction handled. In PHP5, we might throw an error if +mktime uses invalid dates around 5-14 Oct 1582. Released with ADOdb 3.10. +Added limbo 5-14 Oct 1582 check, when we set to 15 Oct 1582. + +- 27 Jan 2003 0.03 + +Fixed some more month problems due to gmt issues. Added constant ADODB_DATE_VERSION. +Fixed calculation of days since start of year for <1970. + +- 27 Jan 2003 0.02 + +Changed _adodb_getdate() to inline leap year checking for better performance. +Fixed problem with time-zones west of GMT +0000. + +- 24 Jan 2003 0.01 + +First implementation. +*/ + + +/* Initialization */ + +/* + Version Number +*/ +define('ADODB_DATE_VERSION',0.10); + +/* + We check for Windows as only +ve ints are accepted as dates on Windows. + + Apparently this problem happens also with Linux, RH 7.3 and later! + + glibc-2.2.5-34 and greater has been changed to return -1 for dates < + 1970. This used to work. The problem exists with RedHat 7.3 and 8.0 + echo (mktime(0, 0, 0, 1, 1, 1960)); // prints -1 + + References: + http://bugs.php.net/bug.php?id=20048&edit=2 + http://lists.debian.org/debian-glibc/2002/debian-glibc-200205/msg00010.html +*/ + +if (!defined('ADODB_ALLOW_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1); + +function adodb_date_test_date($y1,$m) +{ + //print " $y1/$m "; + $t = adodb_mktime(0,0,0,$m,13,$y1); + if ("$y1-$m-13 00:00:00" != adodb_date('Y-n-d H:i:s',$t)) { + print "$y1 error
Testing gregorian <=> julian conversion
";
+ $t = adodb_mktime(0,0,0,10,11,1492);
+ //http://www.holidayorigins.com/html/columbus_day.html - Friday check
+ if (!(adodb_date('D Y-m-d',$t) == 'Fri 1492-10-11')) print 'Error in Columbus landing
';
+
+ $t = adodb_mktime(0,0,0,2,29,1500);
+ if (!(adodb_date('Y-m-d',$t) == '1500-02-29')) print 'Error in julian leap years
';
+
+ $t = adodb_mktime(0,0,0,2,29,1700);
+ if (!(adodb_date('Y-m-d',$t) == '1700-03-01')) print 'Error in gregorian leap years
';
+
+ print adodb_mktime(0,0,0,10,4,1582).' ';
+ print adodb_mktime(0,0,0,10,15,1582);
+ $diff = (adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582));
+ if ($diff != 3600*24) print " Error in gregorian correction = ".($diff/3600/24)." days
";
+
+ print " 15 Oct 1582, Fri=".(adodb_dow(1582,10,15) == 5 ? 'Fri' : 'Error')."
";
+ print " 4 Oct 1582, Thu=".(adodb_dow(1582,10,4) == 4 ? 'Thu' : 'Error')."
";
+
+ print "
Testing overflow
";
+
+ $t = adodb_mktime(0,0,0,3,33,1965);
+ if (!(adodb_date('Y-m-d',$t) == '1965-04-02')) print 'Error in day overflow 1
';
+ $t = adodb_mktime(0,0,0,4,33,1971);
+ if (!(adodb_date('Y-m-d',$t) == '1971-05-03')) print 'Error in day overflow 2
';
+ $t = adodb_mktime(0,0,0,1,60,1965);
+ if (!(adodb_date('Y-m-d',$t) == '1965-03-01')) print 'Error in day overflow 3 '.adodb_date('Y-m-d',$t).'
';
+ $t = adodb_mktime(0,0,0,12,32,1965);
+ if (!(adodb_date('Y-m-d',$t) == '1966-01-01')) print 'Error in day overflow 4 '.adodb_date('Y-m-d',$t).'
';
+ $t = adodb_mktime(0,0,0,12,63,1965);
+ if (!(adodb_date('Y-m-d',$t) == '1966-02-01')) print 'Error in day overflow 5 '.adodb_date('Y-m-d',$t).'
';
+ $t = adodb_mktime(0,0,0,13,3,1965);
+ if (!(adodb_date('Y-m-d',$t) == '1966-01-03')) print 'Error in mth overflow 1
';
+
+ print "Testing 2-digit => 4-digit year conversion
";
+ if (adodb_year_digit_check(00) != 2000) print "Err 2-digit 2000
";
+ if (adodb_year_digit_check(10) != 2010) print "Err 2-digit 2010
";
+ if (adodb_year_digit_check(20) != 2020) print "Err 2-digit 2020
";
+ if (adodb_year_digit_check(30) != 2030) print "Err 2-digit 2030
";
+ if (adodb_year_digit_check(40) != 1940) print "Err 2-digit 1940
";
+ if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950
";
+ if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990
";
+
+ // Test string formating
+ print "
Testing date formating
"; + $fmt = '\d\a\t\e T Y-m-d H:i:s a A d D F g G h H i j l L m M n O \R\F\C822 r s t U w y Y z Z 2003'; + $s1 = date($fmt,0); + $s2 = adodb_date($fmt,0); + if ($s1 != $s2) { + print " date() 0 failed";
+ $pos = strcmp($s1,$s2);
+
+ if (($s1) != ($s2)) {
+ for ($j=0,$k=strlen($s1); $j < $k; $j++) {
+ if ($s1[$j] != $s2[$j]) {
+ print substr($s1,$j).' ';
+ break;
+ }
+ }
+ print "Error date(): $ts
+ \"$s1\" (date len=".strlen($s1).")
+ \"$s2\" (adodb_date len=".strlen($s2).")
";
+ $fail = true;
+ }
+
+ $a1 = getdate($ts);
+ $a2 = adodb_getdate($ts);
+ $rez = array_diff($a1,$a2);
+ if (sizeof($rez)>0) {
+ print "Error getdate() $ts
";
+ print_r($a1);
+ print "
";
+ print_r($a2);
+ print "
"; + $fail = true; + } + } + + // Test generation of dates outside 1901-2038 + print "
Testing random dates between 100 and 4000
"; + adodb_date_test_date(100,1); + for ($i=100; --$i >= 0;) { + $y1 = 100+rand(0,1970-100); + $m = rand(1,12); + adodb_date_test_date($y1,$m); + + $y1 = 3000-rand(0,3000-1970); + adodb_date_test_date($y1,$m); + } + print ''; + $start = 1960+rand(0,10); + $yrs = 12; + $i = 365.25*86400*($start-1970); + $offset = 36000+rand(10000,60000); + $max = 365*$yrs*86400; + $lastyear = 0; + + // we generate a timestamp, convert it to a date, and convert it back to a timestamp + // and check if the roundtrip broke the original timestamp value. + print "Testing $start to ".($start+$yrs).", or $max seconds, offset=$offset: "; + + for ($max += $i; $i < $max; $i += $offset) { + $ret = adodb_date('m,d,Y,H,i,s',$i); + $arr = explode(',',$ret); + if ($lastyear != $arr[2]) { + $lastyear = $arr[2]; + print " $lastyear "; + flush(); + } + $newi = adodb_mktime($arr[3],$arr[4],$arr[5],$arr[0],$arr[1],$arr[2]); + if ($i != $newi) { + print "Error at $i, adodb_mktime returned $newi ($ret)"; + $fail = true; + break; + } + } + + if (!$fail) print "
Passed !
"; + else print "Failed :-(
"; +} + +/** + Returns day of week, 0 = Sunday,... 6=Saturday. + Algorithm from PEAR::Date_Calc +*/ +function adodb_dow($year, $month, $day) +{ +/* +Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and +proclaimed that from that time onwards 3 days would be dropped from the calendar +every 400 years. + +Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian). +*/ + if ($year <= 1582) { + if ($year < 1582 || + ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) $greg_correction = 3; + else + $greg_correction = 0; + } else + $greg_correction = 0; + + if($month > 2) + $month -= 2; + else { + $month += 10; + $year--; + } + + $day = ( floor((13 * $month - 1) / 5) + + $day + ($year % 100) + + floor(($year % 100) / 4) + + floor(($year / 100) / 4) - 2 * + floor($year / 100) + 77); + + return (($day - 7 * floor($day / 7))) + $greg_correction; +} + + +/** + Checks for leap year, returns true if it is. No 2-digit year check. Also + handles julian calendar correctly. +*/ +function _adodb_is_leap_year($year) +{ + if ($year % 4 != 0) return false; + + if ($year % 400 == 0) { + return true; + // if gregorian calendar (>1582), century not-divisible by 400 is not leap + } else if ($year > 1582 && $year % 100 == 0 ) { + return false; + } + + return true; +} + +/** + checks for leap year, returns true if it is. Has 2-digit year check +*/ +function adodb_is_leap_year($year) +{ + return _adodb_is_leap_year(adodb_year_digit_check($year)); +} + +/** + Fix 2-digit years. Works for any century. + Assumes that if 2-digit is more than 30 years in future, then previous century. +*/ +function adodb_year_digit_check($y) +{ + if ($y < 100) { + + $yr = (integer) date("Y"); + $century = (integer) ($yr /100); + + if ($yr%100 > 50) { + $c1 = $century + 1; + $c0 = $century; + } else { + $c1 = $century; + $c0 = $century - 1; + } + $c1 *= 100; + // if 2-digit year is less than 30 years in future, set it to this century + // otherwise if more than 30 years in future, then we set 2-digit year to the prev century. + if (($y + $c1) < $yr+30) $y = $y + $c1; + else $y = $y + $c0*100; + } + return $y; +} + +/** + get local time zone offset from GMT +*/ +function adodb_get_gmt_different() +{ +static $DIFF; + if (isset($DIFF)) return $DIFF; + + $DIFF = mktime(0,0,0,1,2,1970) - gmmktime(0,0,0,1,2,1970); + return $DIFF; +} + +/** + Returns an array with date info. +*/ +function adodb_getdate($d=false,$fast=false) +{ + if ($d === false) return getdate(); + if (!defined('ADODB_TEST_DATES')) { + if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range + if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer + return @getdate($d); + } + } + return _adodb_getdate($d); +} + +/** + Low-level function that returns the getdate() array. We have a special + $fast flag, which if set to true, will return fewer array values, + and is much faster as it does not calculate dow, etc. +*/ +function _adodb_getdate($origd=false,$fast=false,$is_gmt=false) +{ + $d = $origd - ($is_gmt ? 0 : adodb_get_gmt_different()); + + $_day_power = 86400; + $_hour_power = 3600; + $_min_power = 60; + + if ($d < -12219321600) $d -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction + + $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); + $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); + + if ($d < 0) { + $origd = $d; + // The valid range of a 32bit signed timestamp is typically from + // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT + for ($a = 1970 ; --$a >= 0;) { + $lastd = $d; + + if ($leaf = _adodb_is_leap_year($a)) { + $d += $_day_power * 366; + } else + $d += $_day_power * 365; + if ($d >= 0) { + $year = $a; + break; + } + } + + $secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd; + + $d = $lastd; + $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; + for ($a = 13 ; --$a > 0;) { + $lastd = $d; + $d += $mtab[$a] * $_day_power; + if ($d >= 0) { + $month = $a; + $ndays = $mtab[$a]; + break; + } + } + + $d = $lastd; + $day = $ndays + ceil(($d+1) / ($_day_power)); + + $d += ($ndays - $day+1)* $_day_power; + $hour = floor($d/$_hour_power); + + } else { + + for ($a = 1970 ;; $a++) { + $lastd = $d; + + if ($leaf = _adodb_is_leap_year($a)) { + $d -= $_day_power * 366; + } else + $d -= $_day_power * 365; + if ($d < 0) { + $year = $a; + break; + } + } + $secsInYear = $lastd; + $d = $lastd; + $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; + for ($a = 1 ; $a <= 12; $a++) { + $lastd = $d; + $d -= $mtab[$a] * $_day_power; + if ($d <= 0) { + $month = $a; + $ndays = $mtab[$a]; + break; + } + } + $d = $lastd; + $day = ceil(($d+1) / $_day_power); + $d = $d - ($day-1) * $_day_power; + $hour = floor($d /$_hour_power); + } + + $d -= $hour * $_hour_power; + $min = floor($d/$_min_power); + $secs = $d - $min * $_min_power; + if ($fast) { + return array( + 'seconds' => $secs, + 'minutes' => $min, + 'hours' => $hour, + 'mday' => $day, + 'mon' => $month, + 'year' => $year, + 'yday' => floor($secsInYear/$_day_power), + 'leap' => $leaf, + 'ndays' => $ndays + ); + } + + + $dow = adodb_dow($year,$month,$day); + + return array( + 'seconds' => $secs, + 'minutes' => $min, + 'hours' => $hour, + 'mday' => $day, + 'wday' => $dow, + 'mon' => $month, + 'year' => $year, + 'yday' => floor($secsInYear/$_day_power), + 'weekday' => gmdate('l',$_day_power*(3+$dow)), + 'month' => gmdate('F',mktime(0,0,0,$month,2,1971)), + 0 => $origd + ); +} + +function adodb_gmdate($fmt,$d=false) +{ + return adodb_date($fmt,$d,true); +} + +function adodb_date2($fmt, $d=false, $is_gmt=false) +{ + if ($d !== false) { + if (!preg_match( + "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", + ($d), $rr)) return adodb_date($fmt,false,$is_gmt); + + if ($rr[1] <= 100 && $rr[2]<= 1) return adodb_date($fmt,false,$is_gmt); + + // h-m-s-MM-DD-YY + if (!isset($rr[5])) $d = adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]); + else $d = @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]); + } + + return adodb_date($fmt,$d,$is_gmt); +} + +/** + Return formatted date based on timestamp $d +*/ +function adodb_date($fmt,$d=false,$is_gmt=false) +{ + if ($d === false) return date($fmt); + if (!defined('ADODB_TEST_DATES')) { + if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range + if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer + return @date($fmt,$d); + } + } + $_day_power = 86400; + + $arr = _adodb_getdate($d,true,$is_gmt); + $year = $arr['year']; + $month = $arr['mon']; + $day = $arr['mday']; + $hour = $arr['hours']; + $min = $arr['minutes']; + $secs = $arr['seconds']; + + $max = strlen($fmt); + $dates = ''; + + /* + at this point, we have the following integer vars to manipulate: + $year, $month, $day, $hour, $min, $secs + */ + for ($i=0; $i < $max; $i++) { + switch($fmt[$i]) { + case 'T': $dates .= date('T');break; + // YEAR + case 'L': $dates .= $arr['leap'] ? '1' : '0'; break; + case 'r': // Thu, 21 Dec 2000 16:01:07 +0200 + + $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))).', ' + . ($day<10?' '.$day:$day) . ' '.date('M',mktime(0,0,0,$month,2,1971)).' '.$year.' '; + + if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour; + + if ($min < 10) $dates .= ':0'.$min; else $dates .= ':'.$min; + + if ($secs < 10) $dates .= ':0'.$secs; else $dates .= ':'.$secs; + + $gmt = adodb_get_gmt_different(); + $dates .= sprintf(' %s%04d',($gmt<0)?'+':'-',abs($gmt)/36); break; + + case 'Y': $dates .= $year; break; + case 'y': $dates .= substr($year,strlen($year)-2,2); break; + // MONTH + case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break; + case 'Q': $dates .= ($month+3)>>2; break; + case 'n': $dates .= $month; break; + case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break; + case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break; + // DAY + case 't': $dates .= $arr['ndays']; break; + case 'z': $dates .= $arr['yday']; break; + case 'w': $dates .= adodb_dow($year,$month,$day); break; + case 'l': $dates .= gmdate('l',$_day_power*(3+adodb_dow($year,$month,$day))); break; + case 'D': $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))); break; + case 'j': $dates .= $day; break; + case 'd': if ($day<10) $dates .= '0'.$day; else $dates .= $day; break; + case 'S': + $d10 = $day % 10; + if ($d10 == 1) $dates .= 'st'; + else if ($d10 == 2) $dates .= 'nd'; + else if ($d10 == 3) $dates .= 'rd'; + else $dates .= 'th'; + break; + + // HOUR + case 'Z': + $dates .= ($is_gmt) ? 0 : -adodb_get_gmt_different(); break; + case 'O': + $gmt = ($is_gmt) ? 0 : adodb_get_gmt_different(); + $dates .= sprintf('%s%04d',($gmt<0)?'+':'-',abs($gmt)/36); break; + + case 'H': + if ($hour < 10) $dates .= '0'.$hour; + else $dates .= $hour; + break; + case 'h': + if ($hour > 12) $hh = $hour - 12; + else { + if ($hour == 0) $hh = '12'; + else $hh = $hour; + } + + if ($hh < 10) $dates .= '0'.$hh; + else $dates .= $hh; + break; + + case 'G': + $dates .= $hour; + break; + + case 'g': + if ($hour > 12) $hh = $hour - 12; + else { + if ($hour == 0) $hh = '12'; + else $hh = $hour; + } + $dates .= $hh; + break; + // MINUTES + case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break; + // SECONDS + case 'U': $dates .= $d; break; + case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break; + // AM/PM + // Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM + case 'a': + if ($hour>=12) $dates .= 'pm'; + else $dates .= 'am'; + break; + case 'A': + if ($hour>=12) $dates .= 'PM'; + else $dates .= 'AM'; + break; + default: + $dates .= $fmt[$i]; break; + // ESCAPE + case "\\": + $i++; + if ($i < $max) $dates .= $fmt[$i]; + break; + } + } + return $dates; +} + +/** + Returns a timestamp given a GMT/UTC time. + Note that $is_dst is not implemented and is ignored. +*/ +function adodb_gmmktime($hr,$min,$sec,$mon,$day,$year,$is_dst=false) +{ + return adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst,true); +} + +/** + Return a timestamp given a local time. Originally by jackbbs. + Note that $is_dst is not implemented and is ignored. +*/ +function adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst=false,$is_gmt=false) +{ + if (!defined('ADODB_TEST_DATES')) { + // for windows, we don't check 1970 because with timezone differences, + // 1 Jan 1970 could generate negative timestamp, which is illegal + if (!defined('ADODB_NO_NEGATIVE_TS') || ($year >= 1971)) + if (1901 < $year && $year < 2038) + return @mktime($hr,$min,$sec,$mon,$day,$year); + } + + $gmt_different = ($is_gmt) ? 0 : adodb_get_gmt_different(); + + $hr = intval($hr); + $min = intval($min); + $sec = intval($sec); + $mon = intval($mon); + $day = intval($day); + $year = intval($year); + + + $year = adodb_year_digit_check($year); + + if ($mon > 12) { + $y = floor($mon / 12); + $year += $y; + $mon -= $y*12; + } + + $_day_power = 86400; + $_hour_power = 3600; + $_min_power = 60; + + $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); + $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); + + $_total_date = 0; + if ($year >= 1970) { + for ($a = 1970 ; $a <= $year; $a++) { + $leaf = _adodb_is_leap_year($a); + if ($leaf == true) { + $loop_table = $_month_table_leaf; + $_add_date = 366; + } else { + $loop_table = $_month_table_normal; + $_add_date = 365; + } + if ($a < $year) { + $_total_date += $_add_date; + } else { + for($b=1;$b<$mon;$b++) { + $_total_date += $loop_table[$b]; + } + } + } + $_total_date +=$day-1; + $ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different; + + } else { + for ($a = 1969 ; $a >= $year; $a--) { + $leaf = _adodb_is_leap_year($a); + if ($leaf == true) { + $loop_table = $_month_table_leaf; + $_add_date = 366; + } else { + $loop_table = $_month_table_normal; + $_add_date = 365; + } + if ($a > $year) { $_total_date += $_add_date; + } else { + for($b=12;$b>$mon;$b--) { + $_total_date += $loop_table[$b]; + } + } + } + $_total_date += $loop_table[$mon] - $day; + + $_day_time = $hr * $_hour_power + $min * $_min_power + $sec; + $_day_time = $_day_power - $_day_time; + $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different); + if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction + else if ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582. + } + //print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret; + return $ret; +} + +?> \ No newline at end of file diff --git a/lib/adodb/adodb-time.zip b/lib/adodb/adodb-time.zip new file mode 100644 index 0000000000000000000000000000000000000000..ac2b4a857de50ef0307f6403e74c29c327a46f5a GIT binary patch literal 8826 zcmZ{qLv$s8f<$AZW53w8ZQFLowrzE6zc}gG?%1|%Tl4>!<(%2ws#BZVUnN;^2y_q- z5EziwZWzYHDwFm{Vh|8IYY-6B|Fp(t4rV6wu2y#D3|9804357YCv`tmH>B==a4J6q zA60sqLEnhijxloRF-J{lVYP`mQ#n+=E2Fzf-oKkZGl^W1}=g$ahzO38W;5voMS6m!o zm;hr7{<;j=PNgJjITFUy-L-V%^ zTIUp(1Du?4 F$djnl;|z?k9meycWr z`h_bl9tzcWt_o?k<|t{Rfpo*mYhwZoMgAhk4mh;UUsD;?D&*_ELiB$%VYk;p<#%;e zYMac^W0l>Y;NJv+Va6IfnSq9rmV+U{tcCLzzGB$(8abs@GH#C3Ochu*4o`c1`>sxb zR3Hw$c63z6_ %Op3{cRUVb9fJ ~q#)KWn>aM%gAvI}OXQ(lN4Txy9P~1T~G_ zT_2zB#X2gcmQ;UaMeKG{g9hpEZu{;sEJ@=*E64pU5wbg|!{=4P$M?7^$_tjd*WN#e zj`bfutV8XsrutM%mWg%*#wY>Cn4bo3%^h?H=X9Uv*X9Tf6*=oa!32?8;@Qi89Nuwg z@t9!G@@<7AF}AoH5z)?=xfMmX^q$2MGt7%F0p<2;DaEMvbr_$9Mi6k-H|C;it8W*v z<&W{-q#B=IZ}$X(vA0u0PwmyV+eao1k-w9~=Rf4dNm!PsjtneHr+n2ktn_T2(@ogW zl4!WFyvh!Nwah!`%WxHGWTh^Fp@wb&nwtOS$>07KLP8p6}D`C!7SWUSgSWSOb6!duP&ZZYh#kBDQvu*srd+^#xj8b05kIZ;u z>GOH>*C=VXmwMt;=s9_)LSwMOi4v3Bu LE7_^xMu@<->9C4O zB$!bREr?&bHmo?Vuzlh5CEtn`uqYk!@_RcxrwMgjr6^-6&i}Y>*keiH`hy}%z;EGe zwCpKzA&j!5CbJWt&Rl$O<*%Rn-`o~dExVkhLvj>iT>^?6;WN(GamnlaOM%l;?K{=O zRZbQgOvC@kLlsqH)01RtR*F~XpgMQ^J0S5i vJ_Efap#L9Umpvx8UcEZv8Av>Pe1c(~{^zqP &1#Q)_5Qm@X znN75Icrz$N0|JCll=37^gZTu%oWO@NHnwEq7{^h;(R2F@UFHT*c0nbTL9`j%=aypG zyO;@3TGfc&NpM7)KHzMjwBNj}v}LAffvN)jz-9q3mw=L(o3f%fficcw6#EWD$ ?9MBq{Z ^L-L-as!$-|8&lkH98FnDTPq9c;|~l{gbx zs^AOjvJljT-Ve_uCAuh#m*gxOYe~(16$MvesQCrBc57|T|K-LbM3uSZyu`o|b`3Ii zdHtc+4VjS9;!Y&tNJ5j4=%D*YoIvhgBd_UI+N19dKm%QzkKts-276ud7E;?C>%0k{ z8+T==vy{(?MSBomwKxL2U)sP_^4_B8b<6dR71=XfMNmPrK(s6Lb>w}@XbChfHYljp zbJ0;xAZR@S`LnWSRMdJ~sGgu}KjUuFavEXGF8{mBfxISgVyn1oZxQ}7SaW+8o-iQ~ zEW){JBzJD%K>1k>wZ1S_GOhJ<7x{B#Cfc??;d_=-gX1*I?%f&%Q>6Zp7?bU(N87`5 zC|l04Jdo2Wk9Fi|7{R>pTXmRL^|4`YdR_BKKKs!xY)``{t`{N?>DL&2zgM;mqmvd} zX|uy3r9XBi%Xd@}A`PvbDe4=3mkZ%>V&g=dD`Snb_Auaj{1cxzI3dMY*fGlXsv#@0 zV!@2QsWU75Y-x5N;QsZ2Kq4MLXgx11dpKR}=~fv5kkCO62N{LsFO%8JuRM4n#J zLWP=MBisIVBM-P5HCwiVWsZbnbqu~rd+M+ly {s za>cHFEA8U`d+8YWgl3x3wQ?nhP_C_e5^jAq*7LVu-T?v6b-_k+c!SI^0SR`)(v!~i z9uF8Brya;7p<@4cG4-k?oOjK&4ra={zG7(b&-0smr*PbSTaTXxgL;^Y#%0ahO8nB* zGloDLg@E49-MdA)E(CIuMOkW-qm-^4d+%T}Z^q`f3eHUB2#tJkRsnZ)nj=#9&shUC z;e@`_Gx8~AMHC`WQ8;mgGUwk!aR;Rr+Kf8On5T>nRHu9t@Z*ih6`iKB*0?i&(G_on zx)Y*JBtV5tpsm}7(yLnsrkaaidUY0mlcynl&y9flonPzH(0jL_{^rNSkiKQz#};qE z=(q;vjdF1}01e@6=IlNv@5L|lDyVYg*YZzQ=#^ht?Pa~CFNASOuLO3=tDy0!4tsP} zs=auGtSGC-ar5GmWDn$;jt~6&Dq(U7j7cCKtPd%;KAJ)v0UjZZP92mEoipI?@HZk~ zZU2El=!hRWU}pCYFmS*Rr#Q2F#vfYtLdEA7Ho|{dH!=vG(9g=`35C3e!v{B0*zfZC zD{l~0QQkZxXZYw`OUAH78ff#?GO0{I>NH|zketEZdq@1b8$cxh)1i@>I6g5}h6Z;U zn>Uw|w-9TfBt=MFq=~*@BXb%opy%)TyqA*D$~s#b;bcGF;{v7FkeC6 7m!zpt8a^Y7pX_8bQrP`0w zh}zFT2X_y|Hscq;bHbkr?P-UrzsGz#ee^8iLz3C;cQY_U-;`f_$Ztj8X+t^A!7|R0 zp<53cM|(_59076?Z=bM!MgZT{BA}YaY%D%d#Mj5h2XfJL_D`J$r6->-g~@$vtK5jK zRq2Gde6?BYj7U@KS~bj s_kkn}2p!^C|=;wCW;LBL{MCH2Mr^`&X0As&_iK zxlhbm;~!z`-RxK=+ODHVH=%8Uf;Y~W=^vX?QRXrOV3;QVPUB8YdCdchEv8j<*($)V zmA2|YUI JlKH{dd=>w$-SG02_q{09*G$0`qC3)a zUv(}pOSjf=<%K&Xe|}Z(Sy b(9(+<4vi6D}P;^0O<^3Zij7+Lt=EPy8u7(9~ft zB8-RPc }Yh9FhFI{9VkM(XI9Lz*t*&N5~svorVQ*oHFmhws73XCE{5c^xdDo0 zCHO9t*5EWwe1xR(`l?IW>+U2tJr1Q8TCAdEW>h>z3w!Jxi?sD8eh~vkTR)ZLnX}TO zSXkAl4KE?4eNMvAcs5(%;vz;fYW64iWWYhVu?H6CAyXzTf8b67JePYlAd4e3s?%ja z?1oa?fH|7{C=<8#aI{WO-)EyMJ^lozE4BV^Pqd@dQHbQJu@CW$DtY6KeRP)Q%v%c> zL0J;=?Ozc&3gkv=p9ykx Na5%7&JX@a8;F^a$_NmSrj?en4Tk*Kf73Q@h##3rq?tH}7eNyldlfil=A8~h zeu8+Y76W^F^iwHh1>v1j;+&&O+k*gRXTI^=n05S*Rd#S0&-A=DU-az9CT~~KKkFW} z%YKc)UX|!d*AJfM@r0>x8hKS?Ki{n#mB4%?47RUVQr$Mbc^->nut{HwL h4DCEv$fx);lpCp&jV`WuUOg@ zCt0`6s?3Yz;&hEy|0R}>?HtQCS}a`6#Sh6!!Sk}M0`=pBNcN~Z!xI7yes#bVFxSL- zl>Z *P-9M;RH`-1!OH>@_b|MY%YMRNUT3A zQ}xem&SZiQ#6N||IB}MDudtUUA+T^0S2Cc__8xBgT7)bai1D97u|ZsT-#+I{b(sKD z64YE&arj5I71tHDl}?^iguf6Zv?Pfts66E%<-gi1 ~bX98J&2PP|pos+La% z$beOu#f ll7R;E#88M o^tYUR#fHSngBw+RY ztTKZ>OI0tCWx4{#=AD<+$6+NAqr-Zkjj2?YEdw#lZRuB7fVRi5_6#lQe#3)^i&7xY zN8qku(vYI5FBl&~zk#Ic7YYfBx>EAjsGs*IZodUhG|CMsp)3tU7sg~;E_`qC17dS} z{J$Xmf4}Xf9aflK)kPXEpqm(r5_$Ck$aUTS uYmS z4|#{ns`GdTc&?%h;|3#g&@L$1{!0%E{5kW`@}xl8CR;&Z52<*Y ;pNm2$f=n=_ WN_3H64iu2w_GF=c1Bx2EHv?!|I*Y!B4$lRd`V2bS(S zycrkuJ!BF8_fz1Jh@O`uWlXI}An8dC7oHOcRMwQkEdb1xOd^R|s?QEouvEN8TkZFH zbt@;wMmFBNeW2oKDWbm3ns@Pejk_thPpj1FIhSpIkzn0Lw1~DJW6IEUZ{sTlDWr@@ zaY5J`XT5=$NUSbbS7^wMM3 <}XQK8QI+V&@Nl(m1MJt>s);v;U*;ae~zKob3EKG=9c>P3ShVA6D;J! zSjc@3*;LB;XSH{Kt!p#pEGO#cS(?bculg+P)_29VIJMK HxBljFCsRwPN$dELm@;;l?4DL}ag60dC~^I<8aHjNg2I zN}cVirF3y5>2#*wB m%YYD6_7$)}k6=)tX6wpT7#f z2q5o;?3oTZhQqg5B(*gIJ`CTg^K4&w?D&Q)rLSb0TPeYfI>I8
C)Bi>j`ROft(8om_NUzvcmNJa-O^M8 wJcOA2MOFMmHAc zXyj3h67M!M(c-voRpTT>$;!5xphD6k0NWUY;8CQQ!RCgBBtbxuP;2`p(*XCatFM zb|KRu`88rCUJk&~^Z6Lu$rFlh4~*wV_@ _uExUr$?xp$*oK}GHiEQ>Zt-b#5^w0ush6cR%>qoA9@PYpw$AVFKRfQ@ z3ESvE+_MY7@-9a?SS!FSMZEvP-Wc#!5AOgi?77eT0asIq VRFrUFt)`r0q+8K| z(YjE6BTiGfe`6P{svZXZ8C^Ui^H)aoPQ68K6kouCA3 2aElVrulqAI0aY2;%eo_}no^smIAJwv z(&$|8F07hC_&?<$4M>W $@yxwZcb2W zKrViOx219%C`K}%_eo**VJ_tJC3)|1{`-htezvxC-@Y{yI`1``UdRibt5or?HDJsb zWDHHhFx(OPqYQJ4jtFX1?B;0a%Lc@AT7(z+W4E87>Bz-Ja(mN6)w6zg^nf2`->+6^ z{NQV?3_kG47%EFp+xMs~nZn$QHS&zq=#S_|!6f?mEc@7~%}S?m#a#GHmMg`;w1oy_ z?ODG(l(-{3^7V3v3m{N&02zOdX!7Oyu(3Gw8?TvlUsAHt6jq&bD413<15Fe=%}ZNv zCOs%X2x9%HYhglLrY02TOkmvpnJ1xJwpA^qc5J#RDKwZHlRXpR8eQS|A*FmS7D5Zt zg!yh8Fogs; zJW(*pv@4AKFw!cRgHOnnA!r$@_^ns+zGduFG@FyCeDm}n*^DfC|8a+&!`d^2p! x+mDLB3d zAy^&2nPqOC!qLxK;`MPC@rCR#rK8d7tG$GesL@55)9iIqJFz=SN1P0C1e@zfvwoNG zHr*)z^s)Bj#1$w!7k;-$!4lu&lCtc$9NxqU^n1(chS+iSmTgrDbzZw>S3I1VC08t` zkyh3abGz4#9+aHv-;YS|N)-P6(DGVPsc@Ov^}N4xE{l5jb~yTvBF8zCya=h!@stI3 z Z1DE9K%)mj4 zBWyOCoF|9@$uD@2+D2)9{K?WY$6qsCm>`|=BA%mLzzI^_jY(4d2+2Q(H&dkg6QlvL zyjgvi`o_qz)mSFT0hnDSlx}crxs*f!N?-fetZT)|`8Q0uMVJ41ntYE&XN^LE^K1$r z9w23??X+ITbKq#dhJ&tfHsq-zb8{Rj)zH&AS2HH>U#ImALxf*_;d;_ =o&98s)mQndu-V=6=S{Hz^`)a1TuBxb q3?1bEw?zIUihzKE5P`G={g?i)GomC53H84lP!OU202#@D)BgZh=p-Tl literal 0 HcmV?d00001 diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/Changelog b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/Changelog new file mode 100644 index 0000000000..aeabe1d8fa --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/Changelog @@ -0,0 +1,43 @@ +2003-05-03 14:20 richtl + + * INSTALL, xmlschema.html: Renamed adodb-xmlschema.php to + adodb-xmlschema.inc.php in the docs. + +2003-05-03 14:17 richtl + + * adodb-xmlschema.inc.php, adodb-xmlschema.php: Renamed + adodb-xmlschema.php to adodb-xmlschema.inc.php to fit in with the + ADODB standard. + +2003-05-03 14:16 richtl + + * xmlschema.html: Fixed a doc bug in the example. + +2003-05-03 14:15 richtl + + * example.php, example.xml: Initial add to CVS + +2003-05-03 13:46 richtl + + * adodb-xmlschema.php, xmlschema.html, INSTALL, LICENSE, README, + xmlschema.dtd, docs/blank.html, docs/classtrees_xmlschema.html, + docs/elementindex.html, docs/elementindex_xmlschema.html, + docs/errors.html, docs/index.html, docs/li_xmlschema.html, + docs/packages.html, docs/media/bg_left.png, + docs/media/stylesheet.css, + docs/xmlschema/_adodb-xmlschema_php.html, + docs/xmlschema/adoSchema.html, + docs/xmlschema/package_xmlschema.html: Initial import + +2003-05-03 13:46 richtl + + * adodb-xmlschema.php, xmlschema.html, INSTALL, LICENSE, README, + xmlschema.dtd, docs/blank.html, docs/classtrees_xmlschema.html, + docs/elementindex.html, docs/elementindex_xmlschema.html, + docs/errors.html, docs/index.html, docs/li_xmlschema.html, + docs/packages.html, docs/media/bg_left.png, + docs/media/stylesheet.css, + docs/xmlschema/_adodb-xmlschema_php.html, + docs/xmlschema/adoSchema.html, + docs/xmlschema/package_xmlschema.html: Initial revision + diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/INSTALL b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/INSTALL new file mode 100644 index 0000000000..27355847ff --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/INSTALL @@ -0,0 +1,8 @@ +INSTALL + +To install adodb-xmlschema, simply copy the adodb-xmlschema.inc.php file into your ADODB directory. + +------------------------------------------------------------------------------------------------------------------------------------ +If you have any questions or comments, please email them to me at richtl@arscognita.com. + +$Id$ \ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/LICENSE b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/LICENSE new file mode 100644 index 0000000000..d15af7a1e5 --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/LICENSE @@ -0,0 +1,544 @@ +adodb-xmlschema is dual licensed using BSD-Style and LGPL. Where there is any di +screpancy, the BSD-Style license will take precedence. In plain English, you do +not need to distribute your application in source code form, nor do you need to + distribute adodb-xmlschema source code, provided you follow the rest of terms o +f the BSD-style license. + +Commercial use of adodb-xmlschema is encouraged. Make money and multiply! + +BSD Style-License +================= + +Copyright (c) 2003 ars Cognita, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, + +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this l +ist of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. +Neither the name of the John Lim nor the names of its contributors may be used t +o endorse or promote products derived from this software without specific prior +written permission. + +DISCLAIMER: +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WA +RRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIREC +T, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR P +ROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWI +SE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE P +OSSIBILITY OF SUCH DAMAGE. + +========================================================== + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/README b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/README new file mode 100644 index 0000000000..2a290cc5b9 --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/README @@ -0,0 +1,36 @@ +adodb-xmlschema +------------------------------------------------------------------------------------------------------------------------------------ +Written by Richard Tango-Lowy. +For more information, contact richtl@arscognita.com or visit our site at www.arscognita.com. + +To report bugs or comments, see the ADOdb main README file. + +Introduction: + +adodb-xmlschema is a class that allows the user to quickly and easily build a database on any +ADOdb-supported platform using a simple XML format. + +This library is dual-licensed under a BSD-style license and under the GNU LESSER PUBLIC LICENSE. +See the LICENSE file for more information. + +Features: + + * Darned easy to install + * Quickly to create schemas that build on any platform supported by ADODB. + +Notes: + +See the INSTALL file for installation notes. +See docs/index.html for documentation, including installation, use, and tutorials. + +Thanks: + +Thanks to John Lim for giving us ADODB, and for the hard work that keeps it on top of things. +And particulary for the datadict code that made xmlschema possible. +And to the kind folks at PHP Documentor. Cool tool. +And to Linus. I thought the end of Amiga was the end of computing. Guess I was wrong :-) + +------------------------------------------------------------------------------------------------------------------------------------ +If you have any questions or comments, please email them to me at richtl@arscognita.com. + +$Id$ \ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/adodb-xmlschema.inc.php b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/adodb-xmlschema.inc.php new file mode 100644 index 0000000000..64309aa16f --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/adodb-xmlschema.inc.php @@ -0,0 +1,722 @@ +tableName = $name; + } + + /** + * Adds a field to a table object + * + * $name is the name of the table to which the field should be added. + * $type is an ADODB datadict field type. The following field types + * are supported as of ADODB 3.40: + * - C: varchar + * - X: CLOB (character large object) or largest varchar size + * if CLOB is not supported + * - C2: Multibyte varchar + * - X2: Multibyte CLOB + * - B: BLOB (binary large object) + * - D: Date (some databases do not support this, and we return a datetime type) + * - T: Datetime or Timestamp + * - L: Integer field suitable for storing booleans (0 or 1) + * - I: Integer (mapped to I4) + * - I1: 1-byte integer + * - I2: 2-byte integer + * - I4: 4-byte integer + * - I8: 8-byte integer + * - F: Floating point number + * - N: Numeric or decimal number + * + * @param string $name Name of the table to which the field will be added. + * @param string $type ADODB datadict field type. + * @param string $size Field size + * @param array $opts Field options array + * @return array Field specifier array + */ + function addField( $name, $type, $size = NULL, $opts = NULL ) { + + // Set the field index so we know where we are + $this->currentField = $name; + + // Set the field type (required) + $this->fieldSpec[$name]['TYPE'] = $type; + + // Set the field size (optional) + if( isset( $size ) ) { + $this->fieldSpec[$name]['SIZE'] = $size; + } + + // Set the field options + if( isset( $opts ) ) $this->fieldSpec[$name]['OPTS'] = $opts; + + // Return array containing field specifier + return $this->fieldSpec; + } + + /** + * Adds a field option to the current field specifier + * + * This method adds a field option allowed by the ADOdb datadict + * and appends it to the given field. + * + * @param string $field Field name + * @param string $opt ADOdb field option + * @param mixed $value Field option value + * @return array Field specifier array + */ + function addFieldOpt( $field, $opt, $value = NULL ) { + + // Add the option to the field specifier + if( $value === NULL ) { // No value, so add only the option + $this->fieldSpec[$field]['OPTS'][] = $opt; + } else { // Add the option and value + $this->fieldSpec[$field]['OPTS'][] = array( "$opt" => "$value" ); + } + + // Return array containing field specifier + return $this->fieldSpec; + } + + /** + * Adds an option to the table + * + *This method takes a comma-separated list of table-level options + * and appends them to the table object. + * + * @param string $opt Table option + * @return string Option list + */ + function addTableOpt( $opt ) { + + $optlist = &$this->tableOpts; + $optlist ? $optlist .= ", $opt" : $optlist = $opt; + + // Return the options list + return $optlist; + } + + /** + * Generates the SQL that will create the table in the database + * + * Returns SQL that will create the table represented by the object. + * + * @param object $dict ADOdb data dictionary + * @return array Array containing table creation SQL + */ + function create( $dict ) { + + // Loop through the field specifier array, building the associative array for the field options + $fldarray = array(); + $i = 0; + + foreach( $this->fieldSpec as $field => $finfo ) { + $i++; + + // Set an empty size if it isn't supplied + if( !isset( $finfo['SIZE'] ) ) $finfo['SIZE'] = ''; + + // Initialize the field array with the type and size + $fldarray[$i] = array( $field, $finfo['TYPE'], $finfo['SIZE'] ); + + // Loop through the options array and add the field options. + $opts = $finfo['OPTS']; + if( $opts ) { + foreach( $finfo['OPTS'] as $opt ) { + + if( is_array( $opt ) ) { // Option has an argument. + $key = key( $opt ); + $value = $opt[key( $opt ) ]; + $fldarray[$i][$key] = $value; + } else { // Option doesn't have arguments + array_push( $fldarray[$i], $opt ); + } + } + } + } + + // Build table array + $sqlArray = $dict->CreateTableSQL( $this->tableName, $fldarray, $this->tableOpts ); + + // Return the array containing the SQL to create the table + return $sqlArray; + } + + /** + * Destructor + */ + function destroy() { + unset( $this ); + } +} + +/** +* Creates an index object in ADOdb's datadict format +* +* This class stores information about a database index. As charactaristics +* of the index are loaded from the external source, methods and properties +* of this class are used to build up the index description in ADOdb's +* datadict format. +* +* @package xmlschema +* @access private +*/ +class dbIndex { + + /** + * @var string Index name + */ + var $indexName; + + /** + * @var string Name of the table this index is attached to + */ + var $tableName; + + /** + * @var array Indexed fields: Table columns included in this index + */ + var $fields; + + /** + * Constructor. Initialize the index and table names. + * + * @param string $name Index name + * @param string $table Name of indexed table + */ + function dbIndex( $name, $table ) { + $this->indexName = $name; + $this->tableName = $table; + } + + /** + * Adds a field to the index + * + * This method adds the specified column to an index. + * + * @param string $name Field name + * @return string Field list + */ + function addField( $name ) { + $fieldlist = &$this->fields; + $fieldlist ? $fieldlist .=" , $name" : $fieldlist = $name; + + // Return the field list + return $fieldlist; + } + + /** + * Generates the SQL that will create the index in the database + * + * Returns SQL that will create the index represented by the object. + * + * @param object $dict ADOdb data dictionary object + * @return array Array containing index creation SQL + */ + function create( $dict ) { + + // Build table array + $sqlArray = $dict->CreateIndexSQL( $this->indexName, $this->tableName, $this->fields ); + + // Return the array containing the SQL to create the table + return $sqlArray; + } + + /** + * Destructor + */ + function destroy() { + unset( $this ); + } +} + +/** +* Creates the SQL to execute a list of provided SQL queries +* +* This class compiles a list of SQL queries specified in the external file. +* +* @package xmlschema +* @access private +*/ +class dbQuerySet { + + /** + * @var array List of SQL queries + */ + var $querySet; + + /** + * @var string String used to build of a query line by line + */ + var $query; + + /** + * Constructor. Initializes the queries array + */ + function dbQuerySet() { + $this->querySet = array(); + $this->query = ''; + } + + /** + * Appends a line to a query that is being built line by line + * + * $param string $data Line of SQL data or NULL to initialize a new query + */ + function buildQuery( $data = NULL ) { + isset( $data ) ? $this->query .= " " . trim( $data ) : $this->query = ''; + } + + /** + * Adds a completed query to the query list + * + * @return string SQL of added query + */ + function addQuery() { + + // Push the query onto the query set array + $finishedQuery = $this->query; + array_push( $this->querySet, $finishedQuery ); + + // Return the query set array + return $finishedQuery; + } + + /** + * Creates and returns the current query set + * + * @return array Query set + */ + function create() { + return $this->querySet; + } + + /** + * Destructor + */ + function destroy() { + unset( $this ); + } +} + + +/** +* Loads and parses an XML file, creating an array of "ready-to-run" SQL statements +* +* This class is used to load and parse the XML file, to create an array of SQL statements +* that can be used to build a database, and to build the database using the SQL array. +* +* @package xmlschema +*/ +class adoSchema { + + /** + * @var array Array containing SQL queries to generate all objects + */ + var $sqlArray; + + /** + * @var object XML Parser object + */ + var $xmlParser; + + /** + * @var object ADOdb connection object + */ + var $dbconn; + + /** + * @var string Database type (platform) + */ + var $dbType; + + /** + * @var object ADOdb Data Dictionary + */ + var $dict; + + /** + * @var object Temporary dbTable object + * @access private + */ + var $table; + + /** + * @var object Temporary dbIndex object + * @access private + */ + var $index; + + /** + * @var object Temporary dbQuerySet object + * @access private + */ + var $querySet; + + /** + * @var string Current XML element + * @access private + */ + var $currentElement; + + /** + * @var long Original Magic Quotes Runtime value + * @access private + */ + var $mgq; + + /** + * Constructor. Initializes the xmlschema object + * + * @param object $dbconn ADOdb connection object + */ + function adoSchema( $dbconn ) { + + // Initialize the environment + $this->mgq = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + + $this->dbconn = &$dbconn; + $this->dbType = $dbconn->databaseType; + $this->sqlArray = array(); + + // Create an ADOdb dictionary object + $this->dict = NewDataDictionary( $dbconn ); + } + + /** + * Loads and parses an XML file + * + * This method accepts a path to an xmlschema-compliant XML file, + * loads it, parses it, and uses it to create the SQL to generate the objects + * described by the XML file. + * + * @param string $file XML file + * @return array Array of SQL queries, ready to execute + */ + function ParseSchema( $file ) { + + // Create the parser + $this->xmlParser = &$xmlParser; + $xmlParser = xml_parser_create(); + xml_set_object( $xmlParser, &$this ); + + // Initialize the XML callback functions + xml_set_element_handler( $xmlParser, "_xmlcb_startElement", "_xmlcb_endElement" ); + xml_set_character_data_handler( $xmlParser, "_xmlcb_cData" ); + + // Open the file + if( !( $fp = fopen( $file, "r" ) ) ) { + die( "Unable to open file" ); + } + + // Process the file + while( $data = fread( $fp, 4096 ) ) { + if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) { + die( sprint( "XML error: %s at line %d", + xml_error_string( xml_get_error_code( $xmlParser ) ), + xml_get_current_line_number( $xmlParser ) ) ); + } + } + + // Return the array of queries + return $this->sqlArray; + } + + /** + * Loads a schema into the database + * + * Accepts an array of SQL queries generated by the parser + * and executes them. + * + * @param array $sqlArray Array of SQL statements + * @param boolean $continueOnErr Don't fail out if an error is encountered + * @return integer 0 if failed, 1 if errors, 2 if successful + */ + function ExecuteSchema( $sqlArray, $continueOnErr = TRUE ) { + $err = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr ); + + // Return the success code + return $err; + } + + /** + * XML Callback to process start elements + * + * @access private + */ + function _xmlcb_startElement( $parser, $name, $attrs ) { + + $dbType = $this->dbType; + if( isset( $this->table ) ) $table = &$this->table; + if( isset( $this->index ) ) $index = &$this->index; + if( isset( $this->querySet ) ) $querySet = &$this->querySet; + $this->currentElement = $name; + + // Process the element. Ignore unimportant elements. + if( in_array( trim( $name ), array( "SCHEMA", "DESCR", "COL", "CONSTRAINT" ) ) ) { + return FALSE; + } + + switch( $name ) { + + case "TABLE": // Table element + if( $this->supportedPlatform( $attrs['PLATFORM'] ) ) { + $this->table = new dbTable( $attrs['NAME'] ); + } else { + unset( $this->table ); + } + break; + + case "FIELD": // Table field + if( isset( $this->table ) ) { + $fieldName = $attrs['NAME']; + $fieldType = $attrs['TYPE']; + isset( $attrs['SIZE'] ) ? $fieldSize = $attrs['SIZE'] : $fieldSize = NULL; + isset( $attrs['OPTS'] ) ? $fieldOpts = $attrs['OPTS'] : $fieldOpts = NULL; + $this->table->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts ); + } + break; + + case "KEY": // Table field option + if( isset( $this->table ) ) { + $this->table->addFieldOpt( $this->table->currentField, 'KEY' ); + } + break; + + case "NOTNULL": // Table field option + if( isset( $this->table ) ) { + $this->table->addFieldOpt( $this->table->currentField, 'NOTNULL' ); + } + break; + + case "AUTOINCREMENT": // Table field option + if( isset( $this->table ) ) { + $this->table->addFieldOpt( $this->table->currentField, 'AUTOINCREMENT' ); + } + break; + + case "DEFAULT": // Table field option + if( isset( $this->table ) ) { + $this->table->addFieldOpt( $this->table->currentField, 'DEFAULT', $attrs['VALUE'] ); + } + break; + + case "INDEX": // Table index + if( $this->supportedPlatform( $attrs['PLATFORM'] ) ) { + $this->index = new dbIndex( $attrs['NAME'], $attrs['TABLE'] ); + } else { + if( isset( $this->index ) ) unset( $this->index ); + } + break; + + case "SQL": // Freeform SQL queryset + if( $this->supportedPlatform( $attrs['PLATFORM'] ) ) { + $this->querySet = new dbQuerySet( $attrs ); + } else { + if( isset( $this->querySet ) ) unset( $this->querySet ); + } + break; + + case "QUERY": // Queryset SQL query + if( isset( $this->querySet ) ) { + // Ignore this query set if a platform is specified and it's different than the + // current connection platform. + if( $this->supportedPlatform( $attrs['PLATFORM'] ) ) { + $this->querySet->buildQuery(); + } else { + if( isset( $this->querySet->query ) ) unset( $this->querySet->query ); + } + } + break; + + default: + print "OPENING ELEMENT '$name'
\n"; + } + } + + /** + * XML Callback to process cDATA elements + * + * @access private + */ + function _xmlcb_cData( $parser, $data ) { + + $element = &$this->currentElement; + + if( trim( $data ) == "" ) return; + + // Process the data depending on the element + switch( $element ) { + + case "COL": // Index column + if( isset( $this->index ) ) $this->index->addField( $data ); + break; + + case "DESCR": // Description element + // Display the description information + if( isset( $this->table ) ) { + $name = "({$this->table->tableName}): "; + } elseif( isset( $this->index ) ) { + $name = "({$this->index->indexName}): "; + } else { + $name = ""; + } + print "$name $data\n"; + break; + + case "QUERY": // Query SQL data + if( isset( $this->querySet ) and isset( $this->querySet->query ) ) $this->querySet->buildQuery( $data ); + break; + + case "CONSTRAINT": // Table constraint + if( isset( $this->table ) ) $this->table->addTableOpt( $data ); + break; + + default: + print " \n"; + } + } + + /** + * XML Callback to process end elements + * + * @access private + */ + function _xmlcb_endElement( $parser, $name ) { + + // Process the element. Ignore unimportant elements. + if( in_array( trim( $name ), + array( "SCHEMA", "DESCR", "KEY", "AUTOINCREMENT", "FIELD", + "DEFAULT", "NOTNULL", "CONSTRAINT", "COL" ) ) ) { + return FALSE; + } + + switch( trim( $name ) ) { + + case "TABLE": // Table element + if( isset( $this->table ) ) { + $tableSQL = $this->table->create( $this->dict ); + array_push( $this->sqlArray, $tableSQL[0] ); + $this->table->destroy(); + } + break; + + case "INDEX": // Index element + if( isset( $this->index ) ) { + $indexSQL = $this->index->create( $this->dict ); + array_push( $this->sqlArray, $indexSQL[0] ); + $this->index->destroy(); + } + break; + + case "QUERY": // Queryset element + if( isset( $this->querySet ) and isset( $this->querySet->query ) ) $this->querySet->addQuery(); + break; + + case "SQL": // Query SQL element + if( isset( $this->querySet ) ) { + $querySQL = $this->querySet->create(); + $this->sqlArray = array_merge( $this->sqlArray, $querySQL );; + $this->querySet->destroy(); + } + break; + + default: + print "
- CDATA ($element) $data
CLOSING $name\n"; + } + } + + /** + * Checks if element references a specific platform + * + * Returns TRUE is no platform is specified or if we are currently + * using the specified platform. + * + * @param string $platform Requested platform + * @return boolean TRUE if platform check succeeds + * + * @access private + */ + function supportedPlatform( $platform = NULL ) { + + $dbType = $this->dbType; + $regex = "/^(\w*\|)*" . $dbType . "(\|\w*)*$/"; + + if( !isset( $platform ) or + preg_match( $regex, $platform ) ) { + return TRUE; + } else { + return FALSE; + } + } + + /** + * Destructor + */ + function Destroy() { + xml_parser_free( $this->xmlParser ); + set_magic_quotes_runtime( $this->mgq ); + unset( $this ); + } +} +?> diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/blank.html b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/blank.html new file mode 100644 index 0000000000..3b434bfbf7 --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/blank.html @@ -0,0 +1,114 @@ + adodb-xmlschema
+Written by Richard Tango-Lowy.
+For more information, contact richtl@arscognita.com +or visit our site at www.arscognita.com.
+At the moment, you should report bugs by mailing them to me. (If I can't convince John to +make this part of ADODB :-), I'll create a project for it on SourceForge.) + +
Introduction
+adodb-xmlschema is a class that allows the user to quickly and easily +build a database on any ADOdb-supported platform using a simple XML format.
+This library is dual-licensed under a BSD-style license and under the GNU Lesser Public License. +See the LICENSE file for more information.
+ +Features
+
+ +- Darned easy to install +
- Quickly to create schemas that build on any platform supported by ADODB.
Installation
+To install adodb-xmlschema, simply copy the adodb-xmlschema.php file into your +ADODB directory.
+ +Quick Start
+First, create an XML database schema. Let's call it "schema.xml:"
+<?xml version="1.0"?> +<schema> + <table name="mytable"> + <field name="row1" type="I"> + <descr>An integer row that's a primary key and autoincrements</descr> + <KEY/> + <AUTOINCREMENT/> + </field> + <field name="row2" type="C" size="16"> + <descr>A 16 character varchar row that can't be null</descr> + <NOTNULL/> + </field> + </table> + <index name="myindex" table="mytable"> + <col>row1</col> + <col>row2</col> + </index> + <sql> + <descr>SQL to be executed only on specific platforms</descr> + <query platform="postgres|postgres7"> + insert into mytable ( row1, row2 ) values ( 12, 'stuff' ) + </query> + <query platform="mysql"> + insert into mytable ( row1, row2 ) values ( 12, 'different stuff' ) + </query> + </sql> +</schema> +Create a new database using the appropriate tool for your platform. +Executing the following PHP code will create the a mytable and myindex +in the database and insert two rows into mytable.
+// To build the schema, start by creating a normal ADOdb connection: +$db->NewADOConnection( 'mysql' ); +$db->Connect( ... ); + +// Create the schema object and build the query array. +$schema = new adoSchema( $db ); + +// Build the SQL array +$sql = $schema->ParseSchema( "schema.xml" ); + +// Execute the SQL on the database +$result = $schema->ExecuteSchema( $sql ); + +// Finally, clean up after the XML parser +// (PHP won't do this for you!) +$schema->Destroy(); ++ +XML Schema Format:
+(See ADOdb_schema.dtd for the full specification)
++<?xml version="1.0"?> +<schema> + <table name="tablename" platform="platform1|platform2|..."> + <descr>Optional description</descr> + <field name="fieldname" type="datadict_type" size="size"> + <KEY/> + <NOTNULL/> + <AUTOINCREMENT/> + <DEFAULT value="value"/> + </field> + ... more fields + </table> + ... more tables + + <index name="indexname" platform="platform1|platform2|..."> + <descr>Optional description</descr> + <col>fieldname</col> + ... more columns + </index> + ... more indices + + <sql platform="platform1|platform2|..."> + <descr>Optional description</descr> + <query platform="platform1|platform2|...">SQL query</query> + ... more queries + </sql> + ... more SQL + </schema> ++
+Thanks
+Thanks to John Lim for giving us ADODB, and for the hard work that keeps it on top of things. +And particulary for the datadict code that made xmlschema possible.
+And to the kind folks at PHP Documentor. Cool tool.
+And to Linus. I thought the end of Amiga was the end of computing. Guess I was wrong :-)
+
+If you have any questions or comments, please email them to me at +richtl@arscognita.com. + +$Id$
\ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/classtrees_xmlschema.html b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/classtrees_xmlschema.html new file mode 100644 index 0000000000..b2f326bc51 --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/classtrees_xmlschema.html @@ -0,0 +1,28 @@ + + + + + + +Class Trees for Package xmlschema + + + + + ++ Class Trees for Package xmlschema +
+Root class adoSchema ++
+++ + \ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/elementindex.html b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/elementindex.html new file mode 100644 index 0000000000..74c75d31d3 --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/elementindex.html @@ -0,0 +1,99 @@ + + + + + + +
+ Documention generated on Sat, 3 May 2003 13:13:37 -0400 by phpDocumentor 1.2.0rc1 +Element Index + + + + +Index of All Elements
+Indexes by package:
+xmlschema
+
+ a + d + e + + p + s ++
+ + \ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/elementindex_xmlschema.html b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/elementindex_xmlschema.html new file mode 100644 index 0000000000..8b20de0825 --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/elementindex_xmlschema.html @@ -0,0 +1,109 @@ + + + + + + ++ +top
++
++ ++ a + ++ adodb-xmlschema.php procedural page adodb-xmlschema.php + adoSchema in file adodb-xmlschema.php, method adoSchema::adoSchema()
Constructor.+ adoSchema in file adodb-xmlschema.php, class adoSchema
Loads and parses an XML file, creating an array of "ready-to-run" SQL statements+ +top
++
++ ++ d + ++ $dbconn in file adodb-xmlschema.php, variable adoSchema::$dbconn + $dbType in file adodb-xmlschema.php, variable adoSchema::$dbType + $dict in file adodb-xmlschema.php, variable adoSchema::$dict + Destroy in file adodb-xmlschema.php, method adoSchema::Destroy()
Destructor+ +top
++
++ ++ e + ++ ExecuteSchema in file adodb-xmlschema.php, method adoSchema::ExecuteSchema()
Loads a schema into the database+ +top
++
++ ++ p + ++ ParseSchema in file adodb-xmlschema.php, method adoSchema::ParseSchema()
Loads and parses an XML file+ +top
++
++ ++ s + ++ $sqlArray in file adodb-xmlschema.php, variable adoSchema::$sqlArray + +top
++
++ ++ x + ++ $xmlParser in file adodb-xmlschema.php, variable adoSchema::$xmlParser Package xmlschema Element Index + + + + +Element index for package xmlschema
+
+Index of all elements
+ a + d + e + i + p + s + x ++
+ + \ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/errors.html b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/errors.html new file mode 100644 index 0000000000..9d0991c7d7 --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/errors.html @@ -0,0 +1,20 @@ + + + + + + ++ +top
++
++ ++ a + ++ adodb-xmlschema.php procedural page adodb-xmlschema.php + adoSchema in file adodb-xmlschema.php, method adoSchema::adoSchema()
Constructor.+ adoSchema in file adodb-xmlschema.php, class adoSchema
Loads and parses an XML file, creating an array of "ready-to-run" SQL statements+ +top
++
++ ++ d + ++ $dbconn in file adodb-xmlschema.php, variable adoSchema::$dbconn + $dbType in file adodb-xmlschema.php, variable adoSchema::$dbType + $dict in file adodb-xmlschema.php, variable adoSchema::$dict + Destroy in file adodb-xmlschema.php, method adoSchema::Destroy()
Destructor+ +top
++
++ ++ e + ++ ExecuteSchema in file adodb-xmlschema.php, method adoSchema::ExecuteSchema()
Loads a schema into the database+ +top
++
++ ++ i + ++ +top
++
++ ++ p + ++ ParseSchema in file adodb-xmlschema.php, method adoSchema::ParseSchema()
Loads and parses an XML file+ +top
++
++ ++ s + ++ $sqlArray in file adodb-xmlschema.php, variable adoSchema::$sqlArray + +top
++
++ ++ x + ++ $xmlParser in file adodb-xmlschema.php, variable adoSchema::$xmlParser phpDocumentor Parser Errors and Warnings + + + +Post-parsing
+++ + \ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/index.html b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/index.html new file mode 100644 index 0000000000..6ba6690c56 --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/index.html @@ -0,0 +1,17 @@ + + + + + +
+ Documention generated on Sat, 3 May 2003 13:13:39 -0400 by phpDocumentor 1.2.0rc1 +ADODB xmlschema Documentation + + + \ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/li_xmlschema.html b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/li_xmlschema.html new file mode 100644 index 0000000000..965d6bb661 --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/li_xmlschema.html @@ -0,0 +1,38 @@ + + + + + + ++ + + + +Package xmlschema
+Class Trees
+Alphabetical Element Index
+xmlschema + +Files
+ +Classes
++
+- adoSchema
+Functions
++
+phpDocumentor v 1.2.0rc1 + + \ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/packages.html b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/packages.html new file mode 100644 index 0000000000..b8f89bd71a --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/docs/packages.html @@ -0,0 +1,25 @@ + + + + + + ++ + + + + Packages
++
+ + \ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/example.php b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/example.php new file mode 100644 index 0000000000..125b020a3b --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/example.php @@ -0,0 +1,24 @@ +Connect( 'localhost', 'someuser', '', 'schematest' ); + +// Create the schema object and build the query array. +$schema = new adoSchema( $db ); + +// Build the SQL array +$sql = $schema->ParseSchema( "example.xml" ); + +print "Here's the SQL to do the build:\n"; +print_r( $sql ); +print "\n"; + +// Execute the SQL on the database +$result = $schema->ExecuteSchema( $sql ); + +// Finally, clean up after the XML parser +// (PHP won't do this for you!) +$schema->Destroy(); +?> \ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/example.xml b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/example.xml new file mode 100644 index 0000000000..4e1d42273f --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/example.xml @@ -0,0 +1,27 @@ + +- xmlschema
++ diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/xmlschema.dtd b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/xmlschema.dtd new file mode 100644 index 0000000000..b412067292 --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/xmlschema.dtd @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +] > diff --git a/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/xmlschema.html b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/xmlschema.html new file mode 100644 index 0000000000..d528e1d76f --- /dev/null +++ b/lib/adodb/adodb-xmlschema-0.0.1-snap20030511/xmlschema.html @@ -0,0 +1,116 @@ ++
++ +An integer row that's a primary key and autoincrements ++ + + +A 16 character varchar row that can't be null ++ + +row1 + row2 + + +SQL to be executed only on specific platforms ++ insert into mytable ( row1, row2 ) values ( 12, 'stuff' ) + ++ insert into mytable ( row1, row2 ) values ( 12, 'different stuff' ) + +adodb-xmlschema
+Written by Richard Tango-Lowy.
+For more information, contact richtl@arscognita.com +or visit our site at www.arscognita.com.
+At the moment, you should report bugs by mailing them to me. (If I can't convince John to +make this part of ADODB :-), I'll create a project for it on SourceForge.) + +
Introduction
+adodb-xmlschema is a class that allows the user to quickly and easily +build a database using the excellent +ADODB database library and a simple +XML formatted file.
+This library is dual-licensed under a BSD-style license and under the GNU Lesser Public License. +See the LICENSE file for more information.
+ +Features
+
+ +- Darned easy to install +
- Quickly to create schemas that build on any platform supported by ADODB.
Installation
+To install adodb-xmlschema, simply copy the adodb-xmlschema.inc.php file into your +ADODB directory.
+ +Quick Start
+First, create an XML database schema. Let's call it "schema.xml:"
+<?xml version="1.0"?> +<schema> + <table name="mytable"> + <field name="row1" type="I"> + <descr>An integer row that's a primary key and autoincrements</descr> + <KEY/> + <AUTOINCREMENT/> + </field> + <field name="row2" type="C" size="16"> + <descr>A 16 character varchar row that can't be null</descr> + <NOTNULL/> + </field> + </table> + <index name="myindex" table="mytable"> + <col>row1</col> + <col>row2</col> + </index> + <sql> + <descr>SQL to be executed only on specific platforms</descr> + <query platform="postgres|postgres7"> + insert into mytable ( row1, row2 ) values ( 12, 'stuff' ) + </query> + <query platform="mysql"> + insert into mytable ( row1, row2 ) values ( 12, 'different stuff' ) + </query> + </sql> +</schema> +Create a new database using the appropriate tool for your platform. +Executing the following PHP code will create the a mytable and myindex +in the database and insert one row into mytable if the platform is postgres or mysql.
+// To build the schema, start by creating a normal ADOdb connection: +$db->NewADOConnection( 'mysql' ); +$db->Connect( ... ); + +// Create the schema object and build the query array. +$schema = new adoSchema( $db ); + +// Build the SQL array +$sql = $schema->ParseSchema( "schema.xml" ); + +// Execute the SQL on the database +$result = $schema->ExecuteSchema( $sql ); + +// Finally, clean up after the XML parser +// (PHP won't do this for you!) +$schema->Destroy(); ++ +XML Schema Format:
+(See ADOdb_schema.dtd for the full specification)
++<?xml version="1.0"?> +<schema> + <table name="tablename" platform="platform1|platform2|..."> + <descr>Optional description</descr> + <field name="fieldname" type="datadict_type" size="size"> + <KEY/> + <NOTNULL/> + <AUTOINCREMENT/> + <DEFAULT value="value"/> + </field> + ... more fields + </table> + ... more tables + + <index name="indexname" platform="platform1|platform2|..."> + <descr>Optional description</descr> + <col>fieldname</col> + ... more columns + </index> + ... more indices + + <sql platform="platform1|platform2|..."> + <descr>Optional description</descr> + <query platform="platform1|platform2|...">SQL query</query> + ... more queries + </sql> + ... more SQL + </schema> ++
+Thanks
+Thanks to John Lim for giving us ADODB, and for the hard work that keeps it on top of things. +And particulary for the datadict code that made xmlschema possible.
+And to the kind folks at PHP Documentor. Cool tool.
+And to Linus. I thought the end of Amiga was the end of computing. Guess I was wrong :-)
+
+If you have any questions or comments, please email them to me at +richtl@arscognita.com. + +$Id$
\ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema.inc.php b/lib/adodb/adodb-xmlschema.inc.php index 9debc392e9..e86b62b23f 100644 --- a/lib/adodb/adodb-xmlschema.inc.php +++ b/lib/adodb/adodb-xmlschema.inc.php @@ -1,722 +1,1201 @@ -tableName = $name; - } - - /** - * Adds a field to a table object - * - * $name is the name of the table to which the field should be added. - * $type is an ADODB datadict field type. The following field types - * are supported as of ADODB 3.40: - * - C: varchar - * - X: CLOB (character large object) or largest varchar size - * if CLOB is not supported - * - C2: Multibyte varchar - * - X2: Multibyte CLOB - * - B: BLOB (binary large object) - * - D: Date (some databases do not support this, and we return a datetime type) - * - T: Datetime or Timestamp - * - L: Integer field suitable for storing booleans (0 or 1) - * - I: Integer (mapped to I4) - * - I1: 1-byte integer - * - I2: 2-byte integer - * - I4: 4-byte integer - * - I8: 8-byte integer - * - F: Floating point number - * - N: Numeric or decimal number - * - * @param string $name Name of the table to which the field will be added. - * @param string $type ADODB datadict field type. - * @param string $size Field size - * @param array $opts Field options array - * @return array Field specifier array - */ - function addField( $name, $type, $size = NULL, $opts = NULL ) { - - /* Set the field index so we know where we are */ - $this->currentField = $name; - - /* Set the field type (required) */ - $this->fieldSpec[$name]['TYPE'] = $type; - - /* Set the field size (optional) */ - if( isset( $size ) ) { - $this->fieldSpec[$name]['SIZE'] = $size; - } - - /* Set the field options */ - if( isset( $opts ) ) $this->fieldSpec[$name]['OPTS'] = $opts; - - /* Return array containing field specifier */ - return $this->fieldSpec; - } - - /** - * Adds a field option to the current field specifier - * - * This method adds a field option allowed by the ADOdb datadict - * and appends it to the given field. - * - * @param string $field Field name - * @param string $opt ADOdb field option - * @param mixed $value Field option value - * @return array Field specifier array - */ - function addFieldOpt( $field, $opt, $value = NULL ) { - - /* Add the option to the field specifier */ - if( $value === NULL ) { /* No value, so add only the option */ - $this->fieldSpec[$field]['OPTS'][] = $opt; - } else { /* Add the option and value */ - $this->fieldSpec[$field]['OPTS'][] = array( "$opt" => "$value" ); - } - - /* Return array containing field specifier */ - return $this->fieldSpec; - } - - /** - * Adds an option to the table - * - *This method takes a comma-separated list of table-level options - * and appends them to the table object. - * - * @param string $opt Table option - * @return string Option list - */ - function addTableOpt( $opt ) { - - $optlist = &$this->tableOpts; - $optlist ? $optlist .= ", $opt" : $optlist = $opt; - - /* Return the options list */ - return $optlist; - } - - /** - * Generates the SQL that will create the table in the database - * - * Returns SQL that will create the table represented by the object. - * - * @param object $dict ADOdb data dictionary - * @return array Array containing table creation SQL - */ - function create( $dict ) { - - /* Loop through the field specifier array, building the associative array for the field options */ - $fldarray = array(); - $i = 0; - - foreach( $this->fieldSpec as $field => $finfo ) { - $i++; - - /* Set an empty size if it isn't supplied */ - if( !isset( $finfo['SIZE'] ) ) $finfo['SIZE'] = ''; - - /* Initialize the field array with the type and size */ - $fldarray[$i] = array( $field, $finfo['TYPE'], $finfo['SIZE'] ); - - /* Loop through the options array and add the field options. */ - $opts = $finfo['OPTS']; - if( $opts ) { - foreach( $finfo['OPTS'] as $opt ) { - - if( is_array( $opt ) ) { /* Option has an argument. */ - $key = key( $opt ); - $value = $opt[key( $opt ) ]; - $fldarray[$i][$key] = $value; - } else { /* Option doesn't have arguments */ - array_push( $fldarray[$i], $opt ); - } - } - } - } - - /* Build table array */ - $sqlArray = $dict->CreateTableSQL( $this->tableName, $fldarray, $this->tableOpts ); - - /* Return the array containing the SQL to create the table */ - return $sqlArray; - } - - /** - * Destructor - */ - function destroy() { - unset( $this ); - } -} - -/** -* Creates an index object in ADOdb's datadict format -* -* This class stores information about a database index. As charactaristics -* of the index are loaded from the external source, methods and properties -* of this class are used to build up the index description in ADOdb's -* datadict format. -* -* @package xmlschema -* @access private -*/ -class dbIndex { - - /** - * @var string Index name - */ - var $indexName; - - /** - * @var string Name of the table this index is attached to - */ - var $tableName; - - /** - * @var array Indexed fields: Table columns included in this index - */ - var $fields; - - /** - * Constructor. Initialize the index and table names. - * - * @param string $name Index name - * @param string $table Name of indexed table - */ - function dbIndex( $name, $table ) { - $this->indexName = $name; - $this->tableName = $table; - } - - /** - * Adds a field to the index - * - * This method adds the specified column to an index. - * - * @param string $name Field name - * @return string Field list - */ - function addField( $name ) { - $fieldlist = &$this->fields; - $fieldlist ? $fieldlist .=" , $name" : $fieldlist = $name; - - /* Return the field list */ - return $fieldlist; - } - - /** - * Generates the SQL that will create the index in the database - * - * Returns SQL that will create the index represented by the object. - * - * @param object $dict ADOdb data dictionary object - * @return array Array containing index creation SQL - */ - function create( $dict ) { - - /* Build table array */ - $sqlArray = $dict->CreateIndexSQL( $this->indexName, $this->tableName, $this->fields ); - - /* Return the array containing the SQL to create the table */ - return $sqlArray; - } - - /** - * Destructor - */ - function destroy() { - unset( $this ); - } -} - -/** -* Creates the SQL to execute a list of provided SQL queries -* -* This class compiles a list of SQL queries specified in the external file. -* -* @package xmlschema -* @access private -*/ -class dbQuerySet { - - /** - * @var array List of SQL queries - */ - var $querySet; - - /** - * @var string String used to build of a query line by line - */ - var $query; - - /** - * Constructor. Initializes the queries array - */ - function dbQuerySet() { - $this->querySet = array(); - $this->query = ''; - } - - /** - * Appends a line to a query that is being built line by line - * - * $param string $data Line of SQL data or NULL to initialize a new query - */ - function buildQuery( $data = NULL ) { - isset( $data ) ? $this->query .= " " . trim( $data ) : $this->query = ''; - } - - /** - * Adds a completed query to the query list - * - * @return string SQL of added query - */ - function addQuery() { - - /* Push the query onto the query set array */ - $finishedQuery = $this->query; - array_push( $this->querySet, $finishedQuery ); - - /* Return the query set array */ - return $finishedQuery; - } - - /** - * Creates and returns the current query set - * - * @return array Query set - */ - function create() { - return $this->querySet; - } - - /** - * Destructor - */ - function destroy() { - unset( $this ); - } -} - - -/** -* Loads and parses an XML file, creating an array of "ready-to-run" SQL statements -* -* This class is used to load and parse the XML file, to create an array of SQL statements -* that can be used to build a database, and to build the database using the SQL array. -* -* @package xmlschema -*/ -class adoSchema { - - /** - * @var array Array containing SQL queries to generate all objects - */ - var $sqlArray; - - /** - * @var object XML Parser object - */ - var $xmlParser; - - /** - * @var object ADOdb connection object - */ - var $dbconn; - - /** - * @var string Database type (platform) - */ - var $dbType; - - /** - * @var object ADOdb Data Dictionary - */ - var $dict; - - /** - * @var object Temporary dbTable object - * @access private - */ - var $table; - - /** - * @var object Temporary dbIndex object - * @access private - */ - var $index; - - /** - * @var object Temporary dbQuerySet object - * @access private - */ - var $querySet; - - /** - * @var string Current XML element - * @access private - */ - var $currentElement; - - /** - * @var long Original Magic Quotes Runtime value - * @access private - */ - var $mgq; - - /** - * Constructor. Initializes the xmlschema object - * - * @param object $dbconn ADOdb connection object - */ - function adoSchema( $dbconn ) { - - /* Initialize the environment */ - $this->mgq = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - - $this->dbconn = &$dbconn; - $this->dbType = $dbconn->databaseType; - $this->sqlArray = array(); - - /* Create an ADOdb dictionary object */ - $this->dict = NewDataDictionary( $dbconn ); - } - - /** - * Loads and parses an XML file - * - * This method accepts a path to an xmlschema-compliant XML file, - * loads it, parses it, and uses it to create the SQL to generate the objects - * described by the XML file. - * - * @param string $file XML file - * @return array Array of SQL queries, ready to execute - */ - function ParseSchema( $file ) { - - /* Create the parser */ - $this->xmlParser = &$xmlParser; - $xmlParser = xml_parser_create(); - xml_set_object( $xmlParser, &$this ); - - /* Initialize the XML callback functions */ - xml_set_element_handler( $xmlParser, "_xmlcb_startElement", "_xmlcb_endElement" ); - xml_set_character_data_handler( $xmlParser, "_xmlcb_cData" ); - - /* Open the file */ - if( !( $fp = fopen( $file, "r" ) ) ) { - die( "Unable to open file" ); - } - - /* Process the file */ - while( $data = fread( $fp, 4096 ) ) { - if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) { - die( sprint( "XML error: %s at line %d", - xml_error_string( xml_get_error_code( $xmlParser ) ), - xml_get_current_line_number( $xmlParser ) ) ); - } - } - - /* Return the array of queries */ - return $this->sqlArray; - } - - /** - * Loads a schema into the database - * - * Accepts an array of SQL queries generated by the parser - * and executes them. - * - * @param array $sqlArray Array of SQL statements - * @param boolean $continueOnErr Don't fail out if an error is encountered - * @return integer 0 if failed, 1 if errors, 2 if successful - */ - function ExecuteSchema( $sqlArray, $continueOnErr = TRUE ) { - $err = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr ); - - /* Return the success code */ - return $err; - } - - /** - * XML Callback to process start elements - * - * @access private - */ - function _xmlcb_startElement( $parser, $name, $attrs ) { - - $dbType = $this->dbType; - if( isset( $this->table ) ) $table = &$this->table; - if( isset( $this->index ) ) $index = &$this->index; - if( isset( $this->querySet ) ) $querySet = &$this->querySet; - $this->currentElement = $name; - - /* Process the element. Ignore unimportant elements. */ - if( in_array( trim( $name ), array( "SCHEMA", "DESCR", "COL", "CONSTRAINT" ) ) ) { - return FALSE; - } - - switch( $name ) { - - case "TABLE": /* Table element */ - if( $this->supportedPlatform( $attrs['PLATFORM'] ) ) { - $this->table = new dbTable( $attrs['NAME'] ); - } else { - unset( $this->table ); - } - break; - - case "FIELD": /* Table field */ - if( isset( $this->table ) ) { - $fieldName = $attrs['NAME']; - $fieldType = $attrs['TYPE']; - isset( $attrs['SIZE'] ) ? $fieldSize = $attrs['SIZE'] : $fieldSize = NULL; - isset( $attrs['OPTS'] ) ? $fieldOpts = $attrs['OPTS'] : $fieldOpts = NULL; - $this->table->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts ); - } - break; - - case "KEY": /* Table field option */ - if( isset( $this->table ) ) { - $this->table->addFieldOpt( $this->table->currentField, 'KEY' ); - } - break; - - case "NOTNULL": /* Table field option */ - if( isset( $this->table ) ) { - $this->table->addFieldOpt( $this->table->currentField, 'NOTNULL' ); - } - break; - - case "AUTOINCREMENT": /* Table field option */ - if( isset( $this->table ) ) { - $this->table->addFieldOpt( $this->table->currentField, 'AUTOINCREMENT' ); - } - break; - - case "DEFAULT": /* Table field option */ - if( isset( $this->table ) ) { - $this->table->addFieldOpt( $this->table->currentField, 'DEFAULT', $attrs['VALUE'] ); - } - break; - - case "INDEX": /* Table index */ - if( $this->supportedPlatform( $attrs['PLATFORM'] ) ) { - $this->index = new dbIndex( $attrs['NAME'], $attrs['TABLE'] ); - } else { - if( isset( $this->index ) ) unset( $this->index ); - } - break; - - case "SQL": /* Freeform SQL queryset */ - if( $this->supportedPlatform( $attrs['PLATFORM'] ) ) { - $this->querySet = new dbQuerySet( $attrs ); - } else { - if( isset( $this->querySet ) ) unset( $this->querySet ); - } - break; - - case "QUERY": /* Queryset SQL query */ - if( isset( $this->querySet ) ) { - /* Ignore this query set if a platform is specified and it's different than the */ - /* current connection platform. */ - if( $this->supportedPlatform( $attrs['PLATFORM'] ) ) { - $this->querySet->buildQuery(); - } else { - if( isset( $this->querySet->query ) ) unset( $this->querySet->query ); - } - } - break; - - default: - print "OPENING ELEMENT '$name'
\n"; - } - } - - /** - * XML Callback to process cDATA elements - * - * @access private - */ - function _xmlcb_cData( $parser, $data ) { - - $element = &$this->currentElement; - - if( trim( $data ) == "" ) return; - - /* Process the data depending on the element */ - switch( $element ) { - - case "COL": /* Index column */ - if( isset( $this->index ) ) $this->index->addField( $data ); - break; - - case "DESCR": /* Description element */ - /* Display the description information */ - if( isset( $this->table ) ) { - $name = "({$this->table->tableName}): "; - } elseif( isset( $this->index ) ) { - $name = "({$this->index->indexName}): "; - } else { - $name = ""; - } - print "$name $data\n"; - break; - - case "QUERY": /* Query SQL data */ - if( isset( $this->querySet ) and isset( $this->querySet->query ) ) $this->querySet->buildQuery( $data ); - break; - - case "CONSTRAINT": /* Table constraint */ - if( isset( $this->table ) ) $this->table->addTableOpt( $data ); - break; - - default: - print " \n"; - } - } - - /** - * XML Callback to process end elements - * - * @access private - */ - function _xmlcb_endElement( $parser, $name ) { - - /* Process the element. Ignore unimportant elements. */ - if( in_array( trim( $name ), - array( "SCHEMA", "DESCR", "KEY", "AUTOINCREMENT", "FIELD", - "DEFAULT", "NOTNULL", "CONSTRAINT", "COL" ) ) ) { - return FALSE; - } - - switch( trim( $name ) ) { - - case "TABLE": /* Table element */ - if( isset( $this->table ) ) { - $tableSQL = $this->table->create( $this->dict ); - array_push( $this->sqlArray, $tableSQL[0] ); - $this->table->destroy(); - } - break; - - case "INDEX": /* Index element */ - if( isset( $this->index ) ) { - $indexSQL = $this->index->create( $this->dict ); - array_push( $this->sqlArray, $indexSQL[0] ); - $this->index->destroy(); - } - break; - - case "QUERY": /* Queryset element */ - if( isset( $this->querySet ) and isset( $this->querySet->query ) ) $this->querySet->addQuery(); - break; - - case "SQL": /* Query SQL element */ - if( isset( $this->querySet ) ) { - $querySQL = $this->querySet->create(); - $this->sqlArray = array_merge( $this->sqlArray, $querySQL );; - $this->querySet->destroy(); - } - break; - - default: - print "
- CDATA ($element) $data
CLOSING $name\n"; - } - } - - /** - * Checks if element references a specific platform - * - * Returns TRUE is no platform is specified or if we are currently - * using the specified platform. - * - * @param string $platform Requested platform - * @return boolean TRUE if platform check succeeds - * - * @access private - */ - function supportedPlatform( $platform = NULL ) { - - $dbType = $this->dbType; - $regex = "/^(\w*\|)*" . $dbType . "(\|\w*)*$/"; - - if( !isset( $platform ) or - preg_match( $regex, $platform ) ) { - return TRUE; - } else { - return FALSE; - } - } - - /** - * Destructor - */ - function Destroy() { - xml_parser_free( $this->xmlParser ); - set_magic_quotes_runtime( $this->mgq ); - unset( $this ); - } -} -?> +tableName = $name; + + // If upgrading, set and handle the upgrade method + if( isset( $upgrade ) ) { + $upgrade = strtoupper( $upgrade ); + + switch( $upgrade ) { + + case "ALTER": + $this->upgrade = strtoupper( $upgrade ); + logMsg( "Upgrading table '$name' using {$this->upgrade}" ); + break; + + case "REPLACE": + $this->upgrade = strtoupper( $upgrade ); + logMsg( "Upgrading table '$name' using {$this->upgrade}" ); + + // Need to fetch column names, run them through the case handler (for MySQL), + // create the new column, migrate the data, then drop the old column. + $this->legacyFieldMetadata = $dbconn->MetaColumns( $name ); + logMsg( $this->legacyFieldMetadata, "Legacy Metadata" ); + break; + + default: + unset( $this->upgrade ); + break; + } + + } else { + logMsg( "Creating table '$name'" ); + } + logMsg( " ---dbTable" ); + } + + /** + * Adds a field to a table object + * + * $name is the name of the table to which the field should be added. + * $type is an ADODB datadict field type. The following field types + * are supported as of ADODB 3.40: + * - C: varchar + * - X: CLOB (character large object) or largest varchar size + * if CLOB is not supported + * - C2: Multibyte varchar + * - X2: Multibyte CLOB + * - B: BLOB (binary large object) + * - D: Date (some databases do not support this, and we return a datetime type) + * - T: Datetime or Timestamp + * - L: Integer field suitable for storing booleans (0 or 1) + * - I: Integer (mapped to I4) + * - I1: 1-byte integer + * - I2: 2-byte integer + * - I4: 4-byte integer + * - I8: 8-byte integer + * - F: Floating point number + * - N: Numeric or decimal number + * + * @param string $name Name of the table to which the field will be added. + * @param string $type ADODB datadict field type. + * @param string $size Field size + * @param array $opts Field options array + * @return array Field specifier array + */ + function addField( $name, $type, $size = NULL, $opts = NULL ) { + logMsg( " +++addField( $name, $type, $size, $opts )" ); + // Set the field index so we know where we are + $this->currentField = $name; + + // Set the field type (required) + $this->fieldSpec[$name]['TYPE'] = $type; + + // Set the field size (optional) + if( isset( $size ) ) { + $this->fieldSpec[$name]['SIZE'] = $size; + } + + // Set the field options + if( isset( $opts ) ) $this->fieldSpec[$name]['OPTS'] = $opts; + + // Return array containing field specifier + logMsg( " ---addField" ); + return $this->fieldSpec; + } + + /** + * Adds a field option to the current field specifier + * + * This method adds a field option allowed by the ADOdb datadict + * and appends it to the given field. + * + * @param string $field Field name + * @param string $opt ADOdb field option + * @param mixed $value Field option value + * @return array Field specifier array + */ + function addFieldOpt( $field, $opt, $value = NULL ) { + logMsg( " +++addFieldOpt( $field, $opt, $value )" ); + + // Add the option to the field specifier + if( $value === NULL ) { // No value, so add only the option + $this->fieldSpec[$field]['OPTS'][] = $opt; + } else { // Add the option and value + $this->fieldSpec[$field]['OPTS'][] = array( "$opt" => "$value" ); + } + + // Return array containing field specifier + logMsg( " ---addFieldOpt( $field )" ); + return $this->fieldSpec; + } + + /** + * Adds an option to the table + * + *This method takes a comma-separated list of table-level options + * and appends them to the table object. + * + * @param string $opt Table option + * @return string Option list + */ + function addTableOpt( $opt ) { + logMsg( " +++addTableOpt( $opt )" ); + + $optlist = &$this->tableOpts; + $optlist ? ( $optlist .= ", $opt" ) : ($optlist = $opt ); + + // Return the options list + logMsg( " ---addTableOpt( $opt )" ); + return $optlist; + } + + /** + * Generates the SQL that will create the table in the database + * + * Returns SQL that will create the table represented by the object. + * + * @param object $dict ADOdb data dictionary + * @return array Array containing table creation SQL + */ + function create( $dict ) { + logMsg( " +++create( $dict )" ); + + // Drop the table + if( $this->dropTable ) { + $sqlArray[] = "DROP TABLE {$this->tableName}"; + return $sqlArray; + } + + // Loop through the field specifier array, building the associative array for the field options + $fldarray = array(); + $i = 0; + + foreach( $this->fieldSpec as $field => $finfo ) { + $i++; + + // Set an empty size if it isn't supplied + if( !isset( $finfo['SIZE'] ) ) $finfo['SIZE'] = ''; + + // Initialize the field array with the type and size + $fldarray[$i] = array( $field, $finfo['TYPE'], $finfo['SIZE'] ); + + // Loop through the options array and add the field options. + if( isset( $finfo['OPTS'] ) ) { + foreach( $finfo['OPTS'] as $opt ) { + + if( is_array( $opt ) ) { // Option has an argument. + $key = key( $opt ); + $value = $opt[key( $opt ) ]; + $fldarray[$i][$key] = $value; + + } else { // Option doesn't have arguments + array_push( $fldarray[$i], $opt ); + } + } + } + } + + // Check for existing table + $legacyTables = $dict->MetaTables(); + if( is_array( $legacyTables ) and count( $legacyTables > 0 ) ) { + foreach( $dict->MetaTables() as $table ) { + $this->legacyTables[ strtoupper( $table ) ] = $table; + } + if( isset( $this->legacyTables ) and is_array( $this->legacyTables ) and count( $this->legacyTables > 0 ) ) { + if( array_key_exists( strtoupper( $this->tableName ), $this->legacyTables ) ) { + $existingTableName = $this->legacyTables[strtoupper( $this->tableName )]; + logMsg( "Upgrading $existingTableName using '{$this->upgrade}'" ); + } + } + } + + // Build table array + if( !isset( $this->upgrade ) or !isset( $existingTableName ) ) { + + // Create the new table + $sqlArray = $dict->CreateTableSQL( $this->tableName, $fldarray, $this->tableOpts ); + logMsg( $sqlArray, "Generated CreateTableSQL" ); + + } else { + + // Upgrade an existing table + switch( $this->upgrade ) { + + case 'ALTER': + // Use ChangeTableSQL + $sqlArray = $dict->ChangeTableSQL( $this->tableName, $fldarray, $this->tableOpts ); + logMsg( $sqlArray, "Generated ChangeTableSQL (ALTERing table)" ); + break; + + case 'REPLACE': + logMsg( "Doing upgrade REPLACE (testing)" ); + $sqlArray = $dict->ChangeTableSQL( $this->tableName, $fldarray, $this->tableOpts ); + $this->replace( $dict ); + break; + + default: + } + } + + // Return the array containing the SQL to create the table + logMsg( " ---create" ); + return $sqlArray; + } + + /** + * Generates the SQL that will drop the table or field from the database + * + * @param object $dict ADOdb data dictionary + * @return array Array containing table creation SQL + */ + function drop( $dict ) { + if( isset( $this->currentField ) ) { + // Drop the current field + logMsg( "Dropping field '{$this->currentField}' from table '{$this->tableName}'" ); + $this->dropField = TRUE; + $sqlArray = $dict->DropColumnSQL( $this->tableName, $this->currentField ); + } else { + // Drop the current table + logMsg( "Dropping table '{$this->tableName}'" ); + $this->dropTable = TRUE; + $sqlArray = false; + } + return $sqlArray; + } + + /** + * Generates the SQL that will replace an existing table in the database + * + * Returns SQL that will replace the table represented by the object. + * + * @return array Array containing table replacement SQL + */ + function replace( $dict ) { + logMsg( " +++replace( $dict )" ); + + // Identify new columns + + logMsg( " ---replace)" ); + } + + /** + * Destructor + */ + function destroy() { + logMsg( "===destroy" ); + unset( $this ); + } +} + +/** +* Creates an index object in ADOdb's datadict format +* +* This class stores information about a database index. As charactaristics +* of the index are loaded from the external source, methods and properties +* of this class are used to build up the index description in ADOdb's +* datadict format. +* +* @access private +*/ +class dbIndex { + + /** + * @var string Index name + */ + var $indexName; + + /** + * @var array Index options: Index-level options + */ + var $indexOpts; + + /** + * @var string Name of the table this index is attached to + */ + var $tableName; + + /** + * @var array Indexed fields: Table columns included in this index + */ + var $fields; + + /** + * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database + * @access private + */ + var $upgrade; + + /** + * @var boolean Mark index for destruction + * @access private + */ + var $dropIndex; + + /** + * Constructor. Initialize the index and table names. + * + * If the upgrade argument is set to ALTER or REPLACE, axmls will attempt to drop the index + * before and replace it with the new one. + * + * @param string $name Index name + * @param string $table Name of indexed table + * @param string $upgrade Upgrade method (NULL, ALTER, or REPLACE) + */ + function dbIndex( $name, $table, $upgrade = NULL ) { + logMsg( "===dbIndex( $name, $table )" ); + $this->indexName = $name; + $this->tableName = $table; + $this->dropIndex = FALSE; + + // If upgrading, set the upgrade method + if( isset( $upgrade ) ) { + $upgrade = strtoupper( $upgrade ); + if( $upgrade == 'ALTER' or $upgrade == 'REPLACE' ) { + $this->upgrade = strtoupper( $upgrade ); + // Drop the old index + logMsg( "Dropping old index '$name' using {$this->upgrade}" ); + } else { + unset( $this->upgrade ); + } + + } else { + logMsg( "Creating index '$name'" ); + } + } + + /** + * Adds a field to the index + * + * This method adds the specified column to an index. + * + * @param string $name Field name + * @return string Field list + */ + function addField( $name ) { + logMsg( " +++addField( $name )" ); + + $fieldlist = &$this->fields; + $fieldlist ? ( $fieldlist .=" , $name" ) : ( $fieldlist = $name ); + + // Return the field list + logMsg( " ---addField" ); + return $fieldlist; + } + + /** + * Adds an option to the index + * + *This method takes a comma-separated list of index-level options + * and appends them to the index object. + * + * @param string $opt Index option + * @return string Option list + */ + function addIndexOpt( $opt ) { + logMsg( " +++addIndexOpt( $opt )" ); + $optlist = &$this->indexOpts; + $optlist ? ( $optlist .= ", $opt" ) : ( $optlist = $opt ); + + // Return the options list + logMsg( " ---addIndexOpt" ); + return $optlist; + } + + /** + * Generates the SQL that will create the index in the database + * + * Returns SQL that will create the index represented by the object. + * + * @param object $dict ADOdb data dictionary object + * @return array Array containing index creation SQL + */ + function create( $dict ) { + logMsg( " +++create( $dict )" ); + + // Drop the index + if( $this->dropIndex == TRUE ) { + $sqlArray = array( "DROP INDEX {$this->indexName}" ); + return $sqlArray; + } + + if (isset($this->indexOpts) ) { + // CreateIndexSQL requires an array of options. + $indexOpts_arr = explode(",",$this->indexOpts); + } else { + $indexOpts_arr = NULL; + } + + // Build index SQL array + $sqlArray = $dict->CreateIndexSQL( $this->indexName, $this->tableName, $this->fields, $indexOpts_arr ); + + // If upgrading, prepend SQL to drop the old index + if( isset( $this->upgrade ) ) { + $dropSql = "DROP INDEX {$this->indexName} ON {$this->tableName}"; + array_unshift( $sqlArray, $dropSql ); + } + + // Return the array containing the SQL to create the table + logMsg( " ---create" ); + return $sqlArray; + } + + /** + * Marks an index for destruction + */ + function drop() { + logMsg( "Marking index '{$this->indexName}' from '{$this->tableName}' for drop" ); + $this->dropIndex = TRUE; + } + + /** + * Destructor + */ + function destroy() { + logMsg( "===destroy" ); + unset( $this ); + } +} + +/** +* Creates the SQL to execute a list of provided SQL queries +* +* This class compiles a list of SQL queries specified in the external file. +* +* @access private +*/ +class dbQuerySet { + + /** + * @var array List of SQL queries + */ + var $querySet; + + /** + * @var string String used to build of a query line by line + */ + var $query; + + /** + * Constructor. Initializes the queries array + */ + function dbQuerySet() { + logMsg( "===dbQuerySet" ); + $this->querySet = array(); + $this->query = ''; + } + + /** + * Appends a line to a query that is being built line by line + * + * $param string $data Line of SQL data or NULL to initialize a new query + */ + function buildQuery( $data = NULL ) { + logMsg( " +++buildQuery( $data )" ); + isset( $data ) ? ( $this->query .= " " . trim( $data ) ) : ( $this->query = '' ); + logMsg( " ---buildQuery" ); + } + + /** + * Adds a completed query to the query list + * + * @return string SQL of added query + */ + function addQuery() { + logMsg( " +++addQuery" ); + + // Push the query onto the query set array + $finishedQuery = $this->query; + array_push( $this->querySet, $finishedQuery ); + + // Return the query set array + logMsg( " ---addQuery" ); + return $finishedQuery; + } + + /** + * Creates and returns the current query set + * + * @return array Query set + */ + function create() { + logMsg( " ===create" ); + return $this->querySet; + } + + /** + * Destructor + */ + function destroy() { + logMsg( "===destroy" ); + unset( $this ); + } +} + + +/** +* Loads and parses an XML file, creating an array of "ready-to-run" SQL statements +* +* This class is used to load and parse the XML file, to create an array of SQL statements +* that can be used to build a database, and to build the database using the SQL array. +* +* @author Richard Tango-Lowy +* @version $Revision$ +* @copyright (c) 2003 ars Cognita, Inc., all rights reserved +*/ +class adoSchema { + + /** + * @var array Array containing SQL queries to generate all objects + */ + var $sqlArray; + + /** + * @var object XML Parser object + * @access private + */ + var $xmlParser; + + /** + * @var object ADOdb connection object + * @access private + */ + var $dbconn; + + /** + * @var string Database type (platform) + * @access private + */ + var $dbType; + + /** + * @var object ADOdb Data Dictionary + * @access private + */ + var $dict; + + /** + * @var object Temporary dbTable object + * @access private + */ + var $table; + + /** + * @var object Temporary dbIndex object + * @access private + */ + var $index; + + /** + * @var object Temporary dbQuerySet object + * @access private + */ + var $querySet; + + /** + * @var string Current XML element + * @access private + */ + var $currentElement; + + /** + * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database + * @access private + */ + var $upgradeMethod; + + /** + * @var mixed Existing tables before upgrade + * @access private + */ + var $legacyTables; + + /** + * @var string Optional object prefix + * @access private + */ + var $objectPrefix; + + /** + * @var long Original Magic Quotes Runtime value + * @access private + */ + var $mgq; + + /** + * @var long System debug + * @access private + */ + var $debug; + + /** + * Constructor. Initializes the xmlschema object. + * + * adoSchema provides methods to parse and process the XML schema file. The dbconn argument + * is a database connection object created by ADONewConnection. To upgrade an existing database to + * the provided schema, set the upgradeSchema flag to TRUE. By default, adoSchema will attempt to + * upgrade tables by ALTERing them on the fly. If your RDBMS doesn't support direct alteration + * (e.g., PostgreSQL), setting the forceReplace flag to TRUE will replace existing tables rather than + * altering them, copying data from each column in the old table to the like-named column in the + * new table. + * + * @param object $dbconn ADOdb connection object + * @param object $upgradeSchema Upgrade the database (deprecated) + * @param object $forceReplace If upgrading, REPLACE tables (deprecated) + */ + function adoSchema( $dbconn, $upgradeSchema = FALSE, $forceReplace = FALSE ) { + logMsg( "+++adoSchema( $dbconn, $upgradeSchema, $forceReplace )" ); + + // Initialize the environment + $this->mgq = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + + $this->dbconn = &$dbconn; + $this->dbType = $dbconn->databaseType; + $this->sqlArray = array(); + $this->debug = $this->dbconn->debug; + + // Create an ADOdb dictionary object + $this->dict = NewDataDictionary( $dbconn ); + + $GLOBALS['AXMLS_DBCONN'] = $this->dbconn; + $GLOBALS['AXMLS_DBDICT'] = $this->dict; + + // If upgradeSchema is set, we will be upgrading an existing database to match + // the provided schema. If forceReplace is set, objects are marked for replacement + // rather than alteration. Both these options are deprecated in favor of the + // upgradeSchema method. + if( $upgradeSchema == TRUE ) { + + if( $forceReplace == TRUE ) { + logMsg( "upgradeSchema option deprecated. Use adoSchema->upgradeSchema('REPLACE') method instead" ); + $method = 'REPLACE'; + } else { + logMsg( "upgradeSchema option deprecated. Use adoSchema->upgradeSchema() method instead" ); + $method = 'BEST'; + } + $method = $this->upgradeSchema( $method ); + logMsg( "Upgrading database using '$method'" ); + + } else { + logMsg( "Creating new database schema" ); + unset( $this->upgradeMethod ); + } + logMsg( "---adoSchema" ); + } + + /** + * Upgrades an existing schema rather than creating a new one + * + * Upgrades an exsiting database to match the provided schema. The method + * option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to + * alter each database object directly, REPLACE attempts to rebuild each object + * from scratch, BEST attempts to determine the best upgrade method for each + * object, and NONE disables upgrading. + * + * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE) + * @returns string Upgrade method used + */ + function upgradeSchema( $method = 'BEST' ) { + + // Get the metadata from existing tables, then map the names back to case-insensitive + // names (for RDBMS' like MySQL,. that are case specific.) + $legacyTables = $this->dict->MetaTables(); + + if( is_array( $legacyTables ) and count( $legacyTables > 0 ) ) { + foreach( $this->dict->MetaTables() as $table ) { + $this->legacyTables[ strtoupper( $table ) ] = $table; + } + logMsg( $this->legacyTables, "Legacy Tables Map" ); + } + + // Handle the upgrade methods + switch( strtoupper( $method ) ) { + + case 'ALTER': + $this->upgradeMethod = 'ALTER'; + break; + + case 'REPLACE': + $this->upgradeMethod = 'REPLACE'; + break; + + case 'BEST': + $this->upgradeMethod = 'ALTER'; + break; + + case 'NONE': + $this->upgradeMethod = ''; + break; + + default: + // Fail out if no legitimate method is passed. + return FALSE; + } + return $method; + } + + /** + * Loads and parses an XML file + * + * This method accepts a path to an xmlschema-compliant XML file, + * loads it, parses it, and uses it to create the SQL to generate the objects + * described by the XML file. + * + * @param string $file XML file + * @return array Array of SQL queries, ready to execute + */ + function ParseSchema( $file ) { + logMsg( "+++ParseSchema( $file )" ); + + // Create the parser + $this->xmlParser = &$xmlParser; + $xmlParser = xml_parser_create(); + xml_set_object( $xmlParser, $this ); + + // Initialize the XML callback functions + xml_set_element_handler( $xmlParser, "_xmlcb_startElement", "_xmlcb_endElement" ); + xml_set_character_data_handler( $xmlParser, "_xmlcb_cData" ); + + // Open the file + if( !( $fp = fopen( $file, "r" ) ) ) { + die( "Unable to open file" ); + } + + // Process the file + while( $data = fread( $fp, 4096 ) ) { + if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) { + die( sprintf( "XML error: %s at line %d", + xml_error_string( xml_get_error_code( $xmlParser ) ), + xml_get_current_line_number( $xmlParser ) ) ); + } + } + + // Return the array of queries + logMsg( "---ParseSchema" ); + return $this->sqlArray; + } + + /** + * Loads a schema into the database + * + * Accepts an array of SQL queries generated by the parser + * and executes them. + * + * @param array $sqlArray Array of SQL statements + * @param boolean $continueOnErr Don't fail out if an error is encountered + * @returns integer 0 if failed, 1 if errors, 2 if successful + */ + function ExecuteSchema( $sqlArray, $continueOnErr = TRUE ) { + logMsg( "+++ExecuteSchema( $sqlArray, $continueOnErr )" ); + + $err = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr ); + + // Return the success code + logMsg( "---ExecuteSchema" ); + return $err; + } + + /** + * XML Callback to process start elements + * + * @access private + */ + function _xmlcb_startElement( $parser, $name, $attrs ) { + + isset( $this->upgradeMethod ) ? ( $upgradeMethod = $this->upgradeMethod ) : ( $upgradeMethod = '' ); + + $dbType = $this->dbType; + if( isset( $this->table ) ) $table = &$this->table; + if( isset( $this->index ) ) $index = &$this->index; + if( isset( $this->querySet ) ) $querySet = &$this->querySet; + $this->currentElement = $name; + + // Process the element. Ignore unimportant elements. + if( in_array( trim( $name ), array( "SCHEMA", "DESCR", "COL", "CONSTRAINT" ) ) ) { + return FALSE; + } + + switch( $name ) { + + case "CLUSTERED": // IndexOpt + case "BITMAP": // IndexOpt + case "UNIQUE": // IndexOpt + case "FULLTEXT": // IndexOpt + case "HASH": // IndexOpt + if( isset( $this->index ) ) $this->index->addIndexOpt( $name ); + break; + + case "TABLE": // Table element + if( !isset( $attrs['PLATFORM'] ) or $this->supportedPlatform( $attrs['PLATFORM'] ) ) { + isset( $this->objectPrefix ) ? ( $tableName = $this->objectPrefix . $attrs['NAME'] ) : ( $tableName = $attrs['NAME'] ); + $this->table = new dbTable( $tableName, $upgradeMethod ); + } else { + unset( $this->table ); + } + break; + + case "FIELD": // Table field + if( isset( $this->table ) ) { + $fieldName = $attrs['NAME']; + $fieldType = $attrs['TYPE']; + isset( $attrs['SIZE'] ) ? ( $fieldSize = $attrs['SIZE'] ) : ( $fieldSize = NULL ); + isset( $attrs['OPTS'] ) ? ( $fieldOpts = $attrs['OPTS'] ) : ( $fieldOpts = NULL ); + $this->table->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts ); + } + break; + + case "KEY": // Table field option + if( isset( $this->table ) ) { + $this->table->addFieldOpt( $this->table->currentField, 'KEY' ); + } + break; + + case "NOTNULL": // Table field option + if( isset( $this->table ) ) { + $this->table->addFieldOpt( $this->table->currentField, 'NOTNULL' ); + } + break; + + case "AUTOINCREMENT": // Table field option + if( isset( $this->table ) ) { + $this->table->addFieldOpt( $this->table->currentField, 'AUTOINCREMENT' ); + } + break; + + case "DEFAULT": // Table field option + if( isset( $this->table ) ) { + $this->table->addFieldOpt( $this->table->currentField, 'DEFAULT', $attrs['VALUE'] ); + } + break; + + case "INDEX": // Table index + + if( !isset( $attrs['PLATFORM'] ) or $this->supportedPlatform( $attrs['PLATFORM'] ) ) { + if (isset($attrs['TABLE'])) + isset( $this->objectPrefix) ? ( $tableName = $this->objectPrefix . $attrs['TABLE'] ) : ( $tableName = $attrs['TABLE'] ); + else + $tableName = ''; + $this->index = new dbIndex( $attrs['NAME'], $tableName, $upgradeMethod ); + } else { + if( isset( $this->index ) ) unset( $this->index ); + } + break; + + case "SQL": // Freeform SQL queryset + if( !isset( $attrs['PLATFORM'] ) or $this->supportedPlatform( $attrs['PLATFORM'] ) ) { + $this->querySet = new dbQuerySet( $attrs ); + } else { + if( isset( $this->querySet ) ) unset( $this->querySet ); + } + break; + + case "QUERY": // Queryset SQL query + if( isset( $this->querySet ) ) { + // Ignore this query set if a platform is specified and it's different than the + // current connection platform. + if( !isset( $attrs['PLATFORM'] ) or $this->supportedPlatform( $attrs['PLATFORM'] ) ) { + $this->querySet->buildQuery(); + } else { + if( isset( $this->querySet->query ) ) unset( $this->querySet->query ); + } + } + break; + + default: + if( $this->debug ) print "OPENING ELEMENT '$name'
\n"; + } + } + + /** + * XML Callback to process cDATA elements + * + * @access private + */ + function _xmlcb_cData( $parser, $data ) { + + $element = &$this->currentElement; + + if( trim( $data ) == "" ) return; + + // Process the data depending on the element + switch( $element ) { + + case "COL": // Index column + if( isset( $this->index ) ) $this->index->addField( $data ); + break; + + case "DESCR": // Description element + // Display the description information + if( isset( $this->table ) ) { + $name = "({$this->table->tableName}): "; + } elseif( isset( $this->index ) ) { + $name = "({$this->index->indexName}): "; + } else { + $name = ""; + } + if( $this->debug ) print "$name $data\n"; + break; + + case "QUERY": // Query SQL data + if( isset( $this->querySet ) and isset( $this->querySet->query ) ) $this->querySet->buildQuery( $data ); + break; + + case "CONSTRAINT": // Table constraint + if( isset( $this->table ) ) $this->table->addTableOpt( $data ); + break; + + default: + if( $this->debug ) print " \n"; + } + } + + /** + * XML Callback to process end elements + * + * @access private + */ + function _xmlcb_endElement( $parser, $name ) { + + // Process the element. Ignore unimportant elements. + if( in_array( trim( $name ), + array( "SCHEMA", "DESCR", "KEY", "AUTOINCREMENT", "FIELD", + "DEFAULT", "NOTNULL", "CONSTRAINT", "COL" ) ) ) { + return FALSE; + } + + switch( trim( $name ) ) { + + case "TABLE": // Table element + if( isset( $this->table ) ) { + $tableSQL = $this->table->create( $this->dict ); + + // Handle case changes in MySQL + // Get the metadata from the database, convert old and new table names to the + // same case and compare. If they're the same, pop a RENAME onto the query stack. + $tableName = $this->table->tableName; + if( $this->dict->upperName == 'MYSQL' + and is_array( $this->legacyTables ) + and array_key_exists( strtoupper( $tableName ), $this->legacyTables ) + and $oldTableName = $this->legacyTables[ strtoupper( $tableName ) ] ) { + if( $oldTableName != $tableName ) { + logMsg( "RENAMING table $oldTableName to $tableName" ); + array_push( $this->sqlArray, "RENAME TABLE $oldTableName TO $tableName" ); + } + } + foreach( $tableSQL as $query ) { + array_push( $this->sqlArray, $query ); + } + $this->table->destroy(); + } + break; + + case "DROP": // Drop an item + logMsg( "DROPPING" ); + if( isset( $this->table ) ) { + // Drop a table or field + $dropSQL = $this->table->drop( $this->dict ); + } + if( isset( $this->index ) ) { + // Drop an index + $dropSQL = $this->index->drop(); + } + break; + + case "INDEX": // Index element + if( isset( $this->index ) ) { + $indexSQL = $this->index->create( $this->dict ); + foreach( $indexSQL as $query ) { + array_push( $this->sqlArray, $query ); + } + $this->index->destroy(); + } + break; + + case "QUERY": // Queryset element + if( isset( $this->querySet ) and isset( $this->querySet->query ) ) $this->querySet->addQuery(); + break; + + case "SQL": // Query SQL element + if( isset( $this->querySet ) ) { + $querySQL = $this->querySet->create(); + $this->sqlArray = array_merge( $this->sqlArray, $querySQL );; + $this->querySet->destroy(); + } + break; + + default: + if( $this->debug ) print "
- CDATA ($element) $data
CLOSING $name\n"; + } + } + + /** + * Set object prefix + * + * Sets a standard prefix that will be prepended to all database tables during + * database creation. Calling setPrefix with no arguments clears the prefix. + * + * @param string $prefix Prefix + * @return boolean TRUE if successful, else FALSE + */ + function setPrefix( $prefix = '' ) { + + if( !preg_match( '/[^\w]/', $prefix ) and strlen( $prefix < XMLS_PREFIX_MAXLEN ) ) { + $this->objectPrefix = $prefix; + logMsg( "Prepended prefix: $prefix" ); + return TRUE; + } else { + logMsg( "No prefix" ); + return FALSE; + } + } + + + /** + * Checks if element references a specific platform + * + * Returns TRUE is no platform is specified or if we are currently + * using the specified platform. + * + * @param string $platform Requested platform + * @returns boolean TRUE if platform check succeeds + * + * @access private + */ + function supportedPlatform( $platform = NULL ) { + + $dbType = $this->dbType; + $regex = "/^(\w*\|)*" . $dbType . "(\|\w*)*$/"; + + if( !isset( $platform ) or preg_match( $regex, $platform ) ) { + logMsg( "Platform $platform is supported" ); + return TRUE; + } else { + logMsg( "Platform $platform is NOT supported" ); + return FALSE; + } + } + + /** + * Destructor: Destroys an adoSchema object. + * + * You should call this to clean up when you're finished with adoSchema. + */ + function Destroy() { + xml_parser_free( $this->xmlParser ); + set_magic_quotes_runtime( $this->mgq ); + unset( $this ); + } +} + +/** +* Message loggging function +* +* @access private +*/ +function logMsg( $msg, $title = NULL ) { + if( XMLS_DEBUG ) { + print " "; + if( isset( $title ) ) { + print ""; + } +} +?> \ No newline at end of file diff --git a/lib/adodb/adodb-xmlschema.zip b/lib/adodb/adodb-xmlschema.zip index c2614fa735b42f98ba4084fd84e51037c75e8713..14e6a8f3287bbf2c1874a87746c518a317ff27b9 100644 GIT binary patch literal 64071 zcmZs?W0WRAvo-p(rtR)&PGj1(ZQHhO+qP}nwr$(Cb>}_jJNMjm*Y_hUGP9zpR#xpD z6_G1KRssYR1poj*0H_<~Xt1zyB7E=x0Hpu`0PWwbo}rDQK9#GLrGtT~k(C~`nY976 zt*Px4mzfI|OMFh+%OfrV_f)Js>H65=RYnq(jN@$BwycQbCGmypV>+SeQ~ITGdqy%- z*w4qZU;iAq(23^EU^6Cfo~%+SG}!A94|R9j>iglDf>R$title
"; + } + if( isset( $this ) ) { + print "[" . get_class( $this ) . "] "; + } + if( is_array( $msg ) or is_object( $msg ) ) { + print_r( $msg ); + print "
"; + } else { + print "$msg
"; + } + print "=#o`@28W_7(al}qjH5&@rZkV0^QW(X*~D!iUJ{d{9c%Q zPwL45M^`9>8&kH38{DWc*!|zG8OS?Sq&(53m ?Cu`?hdqYFPtAom0j>q{@->rGBbM{9qpZ!Ti z7%@LpK5L&5E^c_|tzw^Kxr2n3bqM$lJT-?o`1i}gGD(#rh!;bu1CohY 3* z-_=$0dByaCX}q-X7y9$qz4aPCA-AQM{v2!6_DIAM|CAFwh#2uT&{Rq5{@l;Go!?)k zs2u^F%Wow{oB-Q_kykB6Q(*?p$DyO$r7@S567U^ZaFg2CnVh*oXKCF&xnez9C^%d1 zY9nIee#6BvF=ApmPk~{Pprfm&KciR@8rp@mYkCMSe9^)Mj!LgcLc1U5fMH)!!Gpi! z`< ZY-kV$$)Qlk-K>;i?|J zl=cu-LF&MRIY*vNkHi_KtLJy|vCb93z$q%%5l&%`gq56IhK^FUayYv`f+xSj-4lVb z=vSGku6=?1rD*GeqBa6&Fg(4q85qawPxGG0mwkP@7Y|%9FDzxU&i}4g&1;Dqa4-!` z_rW|KK?$?W^tKiW#(2ui#dJ8##yY&I4!wD*9XcUQ*<+lcJ3fAJJ;_w1 hkENGQ`U#9zRF 8lu^<8Zl zPcI*N5sWcHF-8$a(ZmTNk^GEojf6c^Q~RO{6~P}lDDA6F5YdrhS0VCVmZYqooBD(h z#y(=E$7@Z`N`7ho`BlIOwvp;9J+_U>H}a+%{#PR^E+%H0(N9X~(g%gRp$1gPDWOpo zz864`2(6o%eQC*P@y8&B_a4GA{fSR+#12v9;dQ654ap1FlqUag6g%YNIJV=~p`RM; zaRwE4IXL5ma!zN&KHC#`8zN8`Msd5eg;DU{fn;cS-7rBcAd&qXPxMigaO^Jwc0O%i zbp0AsS>QvnnPE?rtuWr@Djy9f%dO0DoEa_0{=qSA|B8) _eJ@$ zNsiYtt6_ s65pN+7%GEkbWBIlL=cR;n4&7b=7WD(e0 zk2rsxVul_Pe38|r`;S)i9WY&V2$q&%(@3@Mb&pT6U{WRkoy>h>7^UP_Lh`?r`(r8PeJ3u?JT6_A|9q}M4 zZ3@GZkdy1X?bDTQ$%$_C*halanx8)nG6>Ewhq>dtR4o}V+I$PjBE`#9h+|pZFVbkr zhYp&%4aBI4Ddw{HatwKyrVz`v!wGjd{~L^| `IW^KJgpyqLVgeV2d;{ zZu6iNf0(FdFMP_)tu3bZKF2;Bjp;;eN!cl^3NRQrtsN@4V9A=)1Cn=--NJreXYE_$ zf$^Jp)K5C#16rUH)B)~51x`vST{B?l6BT->*==waOex0q&sO;r{_o#vWN5)cZ2SGa z-^)MuYdBA4SLFp *~Zlc(J%h*8~fY5^DFlLhE~4t 64Jv zK#Irt#CmYgOS(NTg&WCY6rZ1^*rP$9!h3B)7wnuEwU%UIztpA*XaRMk{TFZ! zXfp4gtPsD~Cv>cX)JNBSVivGxcvo@@AV|=QCf~GH`tOZJA!&q{e9s=Kd5>H+_Uf0z zj(vy%{jb_l2XWN~<=lAX^YlnCwqxKC;jkLW!HkuxiyN9>Su7C9B1{8>{f=qxbD6sD z(XdP&+ENRSDkMJ!#RG`aV=L2V`SZHe#blo~cF7MB7Zlk U*<>^wAsaZhvS#!?x4q1l zxbGFHS1W%vI|Cvuipp94z66b9`4j1bBq4ZeT=fL@AcF3YeM~tgh B8Gt?n)_+NiJR?@Vxwn=!9&ZaRUk! z&C1lt(8RXrG4`W=f$leY<%ix<@p%H-a*&H_9^gTVDZzdJD4+TO_ATRDdfL4uWO`o` z)1|g3kMkwA4Eiz7$L#j0Le7%2$1?F3m7FVsa3j^m8~zMK#_|$-j`5|felr+g=o&nr zp0o?gw%TnKtF ?Qeut}f>w~Sc{Q{! zC$X_KyIOmp+Aq6CU)Ub$xJsGFaPb!7z@LI5^znNC$*+Zr4F}GA*<6+YZv_VkN};dU zzYihH+tt5{zt<&yV(p=UY$2Ulu!DJYD~WpMBk5jtSk0a$*%VU5f?jim!Od1%3%W~H zyHRe06E+m)^P>*zAzD~OB>cW)&?fmZ=0u;wrA{6K;gCWw^4p4fC>-#c;w*-U5c?`) zWddD!7dhMt%z0KSL6&xk7cE}YV552BrcO;ggvx)XuWk~FS=p#7xvm0YY^c{+t-$-8 zFRPbr=`I}bSWG7hWI*Z-*Tv2dW4Dz_ab`z+>F;@$m^Y<_UfNPmdu>ozGuPmPm PGsc`4mc~_Eq9T z1+xkoRHnJxUl&o4hRHv6M*>$W;)MFo{mn{pc7wS)vZVuu6{w~rb(xF~I{v?ko5a(y zH?+EP?=ttLwJW9SpgyzBBNXW6_E4;JEPk2E^65+~lY`4F_7#6kDAEJ>^(cg|V+V_8 z|K=N5Dd-?gQ*#syHz!4Y{0K^POJwdNEWd!8zg>t%E*VY$^_c8XNw4+F{ELSlo7wB! zCnFgDUhC7dl=0gk(B!L{DFQ0~0@drvZ83>;g1yIV$ZBYgC?=yLG0Sp??gN6x1IabI zRd20#qdYpi<12(?EL49?sCwNOui+Ra;sy&`ES<{drKN6WUZ_)<<6V}=5k?N;Kzd2R znS9Zq6C=;d$;*v_HcBaO= 0iBEaOS8LI1vbp9sBQR&SMp;d0 z(K~BfV!^{X#`j2&-!~#>%nl*krl2vbA$vs)@<#n}DeU!lmUvou0wixt3H-L4A@vkd zl`bA+AW~UOQ=+lrZJ~cGN`D!W|HsEiu~_VV4D=PHw_9eFqJ*TdXMj8trs!DK6!a2= zt5;rnm4X4Nw^G*nu#q{ZibI-ullYaGfm?23X%@m{4nOldlg1|BeOohoh%H;ha#HEt zs3O$p=?D{;A?Tc83Z`utn+tzEoz|^p6Dgh`-Z)rp)vz0=njtPZ7nw^!7l1)l>ol;* zUlL=o59EUQbs=KbABl>1nT8CA04ZrVC8F*VCSVOS?DbxePOfaUJ|fwlFqc}1sz{US z5Ec4KF_37w(#~%sm8s8rxvf@Hjv(spuM~AT8?|OO$dhLJ=`bN>mVI!%4qN9SQ&Xkr zu(0 mm5S%AQRI3;@EMD~%f|CNK0ldcZA zku3L#&%rv0xY&C2$G0&w_r!2TAq+Dm-F;WCBAiKE**WkUT*fjk7~IM`RC-i|F)--o zLz>9Aet(rvPaM&c3}#&npI@Dnoc=4JLZo zK?Br@ra?Dm?ea~^gJXWjm3D_pTKJW}jRg6@++6Lk0EW5!elkXg>)N4}fx1$%u9>yi z@8d~-n#y#K@<5cls&g&4SlvaH_^ q~0k)=1a2Qm_px4zPy zp@qMrp6ajDtUo-XmwLOqK^shc0!#_Y+tCmM4*caP3~n{;jEFN3vJ&6rH;&@%r!rHb zuAaW8x}Ctng3H ;CuxNlQl&Kf^m%U4vaPue&jJObcQ7aQcLXV9O@erh0V2!^y> zeoQ%*qf)VDjS15YbtEdaJn|$U*Q)2ptuSnk5%6KQaMw6R)oFs!N#Ke%X{`CeQUrfq z%MfkkbU9t|{2($z6+H#nUU1|=>c?XZSw@{8^g%FnyA(g>p-4;4imfUjRG~4QHMx7U zfN7-9ni^d&=#`mQDI;>sTq3Tc;+h%ebXG8zNzDQe{m@MHI@en6L*=N9ut34ELExib z{(M+9J#%A^cgRX!b>laj*t{|yq;pJo7MLZy?Vr*N*e>)IE_|-BxKJc38m !74~D5soYxL{{sEZa@}{LVrrtfjNMD5Vyk9Ojt1D zcBGCqfqk0EUFD?=P8rBea cUJuY)G z??q}eiq*VQ8#hc<2;z0G1u@M?P6|2LVIyM-?f8+)^nGcJCPN#_{}$x8K&rVxbHJ(n zMINH=Iw#RUX# 7<)?ftkDFUMG2` z2jAx2gO6{?>;TeBqMdvo*L%2S4WWu1C#CmcUY+f8K1!;G!ZF2Cg9~PX^5WZ31N1$T zX_6^lT@DA^O+pJHZRH^LnK2hLy^qFQrFPNQHY)G!eSmnn`QgvL!k1H$dI{WxN^rSg z`a7l%uh>+EMg?+9>fxHRGR>6kqYm+VNdV3q=W@f8$pZ!9iQG^9!n1?LWLylt#6fB| zVFOtf0fSlTm^5(~vB9WUj#a<~f2zyMgI93&EC|cOle~Rqm2+IxubOS)MWk7x=j^^4 zY-A)PFb;Mdy=wd{NNwK{XUbL$J?MF9FMwW&oht^W$bcf9e-_~kRtHLTmw_dbJe%jy zZh)(hg0^5Jq(r6pU4fcMdLL%x&lGf{e7x$_`8UkuflxYH0}OPMvng26s=2$|?tEtK zTLi=erlVO(AB@YRy!_z8Y^6UVE30lpP%ie;0t4Z(VlQZQ8Pz?*_8q5pg$tblH>Afr zOtW+i+l8q9yt)nS06q}>RiEbWvLi|AH@3+2SU5tUOs{gPW4u4TKm&cDe5PJuAqDhV z(rki5lQwIKXSJZM=Df|GxRMXmCH D#t z^-Ea_!;LKYG8M5}9KpxqFcRx3Flb=kEMz4+Kj&R_PMmUfYip|>s!B!>V VB~uEvoDR!hSt({8evv;<~yDyHikhH`TzkY!!*@~k4-^!9s)NX0elPs zUP~}c6oZS-myOJCqIU#yZCO2O*%i)ecPntWt@G2Qp)d!rF9)%n?kj$mo>x3TW&6T# zxt7Hec#St-vRT!_%PMmsh;y%hTH(|yjYaP_Vve4iS`y|SgL&!&5YAP9_Y&mzEQb}q zfy6yLcyk2KP2-bNs%B;kSFuu&&Sn~Y*h|y-DLJNrF{y)FBj@wD#Vg(TgX{WpwE{wk z^%@d8;Ks~QrhuC?CTNF|>sk=C$)1gXR~*H_)YshFbkUqzW6#jB(Z{G4M6n%g4wRgc zMyglT*$1T1)`sg>38LWZI6~~V5{f`zH~B4`QT)DvM=jXz)21-4fLxY-5zr}E?gSR+ zCv!fjxBg w-i=YSeOO yIo9q~K5{#3RPPjD_;yajVf`X76ZT3R$PMK-)CF||1UerjC-%9x{ zuP&rpO!<~Ua1A`=|DL_cOL(Ps9DI?{>q0iB7tdO@0@>Cxmr==SCa4yOq=>4 Iz@b!54@f=c!7f7o-RMop<-2zvg-=BK` z!|?S7#LlaDV+}x9s4?-ub`twNUfuS-8cx-E3hj<%sa5dH(M)LinyQ@xvM}Was9Xrk z5fl1^kz=b962dnjg}wUM<2!^EoXh b*u_g%hqwRLg~ z^I>Dhy~j0CSxEsWF $CHSnc`IW %LVSO(0^o8TzrnGu}&QmH#y9r6Up^*}f<-VQpoesnA>OTssRUc!V2# z(`&y8Q^5E*1DL-^w{9kYIzL4c&3jC!Ju%gCC&La6zxow&P&}r~9=@8v0${mgXXiWf z#x(SwnHYpx8H9NEwpYEgIR)!IF8GmbcxMLUFNa&NAffL8%M82n-v-+ydKAr`Z~G>o^jX72 zZ5K021gvIK`dMJf4>MT_V&CpiM^*nU%MQksn8|=IKqsFL)S*RYb %53#ipnNoa zKQ1f!-CzzblpTY!-13USabi?1Zn`(Y16SX~ow^+8r1^EwbOGCUc!*1C2r!o+*cfMD z>G{>17^Tub-?+=6_D1Jd#+v8oz)VX|cPj_rmwC4_eC*%epH1M#Gy8q|;_-m4+^6ra z-`({+_0dFIkcq840jrBgyZK!AJ_KIN z>S?bk61ma&MdgE{!gE0uOowQD8i;$QJ5Q&R3V*U`_dfiRj-VR8jWy8tW95%F zkdQ(wPpBWU3%>Xqs-+zFoG*@i#rZTEbH-H+x_p*m><3{uTMW+mV=M_4EtkpoYbkmH zwIq_aR-sw@FUt}ajdd_h+@nY#V|#spGx~Q#$*iT)>~b+C1P#x({MSJ=W_wX(de4MU zN+kT;IAhcKJU6=U)A${~Ouorje@mspwnLq4Diu8$;gg+M0YHmm8o71kfiy-0=oPYM z-dpVTa> jX=#*oBK(}_x7~g?e!=x;f_sBI{JoQ%m2lJjkl!LUauTrc74!OcLssv{$d16 zp?K?panye{nl<9z;r-4vn@fPBXc2(M^;PXjSMgRR9bvpcki}hVg?H}cq!*J!PwV+K z{L*ha2h=L_GH}gv*ONZJWksD68lw&Wzz7NiV~58&al^Z)DffWmz$kf?k|R*Ma)Me4 zGlx#{QKAC>cLV5xXOMBVsb)y9oreYpK4?@M6#B|AA;iwjTMrwiQ!(155V~vE`WgfM z)DbAriDJ|6L?g9G1R2btom{&BZBiiEPuWzhQL1U&y`;jQf1k%jfuZS6@b&mbIPjq! z<`RKBAH(7k|6MU;SbgeLa4h;~N=R( X#)QCXL10E-mUcGF?L!sP-@o;o=ieX90rMLn2+z35EO=_&`S(2ZRu z|8_FQN8(Q-T-i{Q#mT;daw6?DMGxs5ke6|bBI|}yR!Pmz)4*3oN$PK{aXTa~Cmllv zax*#|oznB3R7&|u@t#Oo(6J#ezsJt?9rZxy37+c?l7LLJCm#%?bQbE*-)Ki~I$39I z5=*~)hlZXqxNl>Gy<0jzbYBTQrJoRpS3@9a$(WW2TZ-KmkF%Qzw`0LWQeR$cMwtpb zYIzOJztt#SXn>GmT&7l?UJ^8!j4Z3H&Su*ebD`{ic4&5>#Cakkn+4ga+zs4+ ~dqA#vl-@^3{4$dOkkWu^E z_2KKEk(P4f_Hm(o)SeZ3Y(`c$bLfh-su#Z43Y=FT7vuzn&2KOc)gx3UC%1F?czL}! z16d(mgLXX*&ac1x;MN-BSqe>Z&x{>P7nfJ8Ao~6=htzH#P!-bV*X1(Hd!~@&fzv^{ zEr%{ib55S1_|7Lfl2R!!1ZdapSI2aVI6(1ZOx;SYuUQ6T3CC)@a7SwSQUz!qGQeo< z>ubAfA{B+(;K9P#bpIAZ%z!9=Ad=ACMMFR*QJ(+E=;=CaMw8jAnYLBV3_o98X$30n zgtK|;u&zbhE`=h*TJ5WM1@C3i*cihNfV5_K`{wLLZ|J=$H_R}`-gCJldsvJQ#(0iN z`2m;xe{(h{h}dXUSRH3aK>z@FH~;|hU(SYrsh+iok)@5vm9mu00W$)ZM327p7E1H} zjH^ls@fr$5EafaK7itin1VILq5ES8`jh}9UvE`Qd#*;3wfi#wwfiw|m;es>UM=RE} zKG*9R8vrAGvvO uri6-r_HP#$c;ko0Y7^fs>|NS+HYvgh})Y z#3}N@(5s-S-e1)TR5?~F6u--DxhUzYTVa&Pn9rP>O)t+cRayC_LT;0ZT=-21Z%O?` z>QI!6k^*vj(Tk4g2Mwb5ob;AZOPl74&CLuTwo%tApK`=TDTB}UxuwP^Rpu>}JwL0I z?@~fGZf4*?PPZs<)G{F?eK a#DWNK(UBQq*Im)`8fDKMFSkB^##3+vKZ8~9N ze#O@6ikSHLNevqHts?)C3qNCDYgzl7ozPh7B`uj}%i+Q+Wzuof5L)(Ua!A#>5R|mV zwvO?%L%Q#VpufmKVrimCoJr!Kt@=u#vNhCsl|%6-L;6IE+rs$`{9Q;x4s-bf_6ZGv zlfVlPe*t#Y%pu=HFDFTum9z^)X>^}Ss!bQD9WEu#n34UX_nL{!Q+mI*QSvG7FNB@Z zZ1v(hE)1n-8-a1ekSVi{rV@ITZs#pY&Js8=UpYuBC %GlJaSgU1<@RNdzR*~}69 zN&VdBky-`QME||Jv5NR~G~CV6$JWDrXPWGo4y7)N9PArbxI$baWJEj6V{U+AF=KT> zd}Gh((F8@^W?CcOjAw{#h`b$pMymJnZH6GlgEEYwXA=Ib& 9QwlTEFXJ z;?uq{PVYg?(Z$um0w5db2G^VN?ET|3jt@y~oAM~(KsCdVrcp1&zvy0P>Ejryo+F>? z_R2+5=hTCSxs`LZ-2kgRYweWyxRN-*$wr;!1i%x{A~>ahtp3$Rj|Cp(lWX_$D_c;r497YR4PR z2$aW*uMIA__-y$O%zTBCkf&Yp7?p*c{5{YEICR-!e`!cq2u%}9{I+La(RV*I(kxbn zZHhc${vuf+R&LN#zaAlf$j3@OX4iA!%qjbdQ>Ps874b%o4BHKwr&Lg coKpc&E%n5vDd2 zER+Y?o7z&BDF!zXj(e(+Jfeo4?S1y=>2^RvmYVH-WUpff?gOu#G_l!Iei{Z6=Kh;? z>qVRJo^8e;E^HHNkE_t~NBfk&qDRmYgh3^uOfu*$!mVz2ECQ)%RAC4iX>A%zq78Mp zUL+6JZF&t`Wf`M;dIi)1AI jxHx`nWyXa0k3_sN?8oUfRt|+Zq#aT z>>-sqER`{`f%RzEcM<`QQ);O{_Ol4lu+^q05_g{Hz^xyYg 4WOG+L zZRRx5HC!m&|4z+j3!p3GgHK$RjuBbD`+E6)IT&%OB0-*nJjFx=S$BxgsTK#d{4tY) z=)k5g)ITd?B|FU*cTRYon8>*$V?=2WqA|e uV~X>0Ue?1?3RB6ZZ@%Uq#KFkyzs-ca2n5;>tD*LZ{jdXlpWUBZu-*zY2t z1cEw#3<8o|*g#mMUl{=^dNBFOe1lSu%yYjFf*NiwkKM6(iSO2G(SEB7c2!lilzp8> zA8{g!WQ!wXy4#pM6Hw3RnIp_1!TE|gt=5^tE!aNjsrU2Iz=V3B`_5q&;CwQ>{>01C zo#NeQX#v&lf#Wd`{wW;iKLbBt@Ev#0I++eFbw)ndC1NDS>n)HJ1fJ*E0R9HSKqGf? zspy6VN!3fMbpUIlYeKL-Y-azt1rWNsG^FFnx7x0)LyfSUynaX>jSwUY*fNPYuR7E7 zEm$>d#k77AmOEe9fX|6UYHuPb5>jLY{;g;U2J%#J+#?jUrL-1|yKmK=Ylx3+hpUF& zPoO&fl&cLxV5=1aZ6!a30Jb$B$Hi}7OpGoduSuV*sRaDwwAoD4g;QxjNmTmg1Ha}> z^%X$e|6MZ>#;2zDbrp>uN>(O?kd$};pvcXDLy)cND6u3$Qf$&F=^}FL_;!d>!RR@- z@&LQ_&lx!mJ5fXEr=VlR68~m&vG7qwhk2kDtDvmBU)ZY~Cv)iQ{EZ)&R?my?OKkWj zkJ2>evYedn@l8Cb)#A0j$-M4F*A=puJb79PfNzoEnKlv{45tmTVhBfHkmrsdMhL2> z{kJbj`OK)qI;$+zUr(v<7o&xtw4$Zv{<}j)k#^y|;;}zb#Ymp#A{|&BhZ57#RrYtIiQk{PYgVb^KyWTtjp~nF#wlQ)I?WrK!-A=*2b*@$)d&o|(5$?A z4N*-!v-AV`Z+MzPCq+uTLRXligouoc?2bRIKQ^_@PM$t4UT#kA4&O(g=hUi*EO>4@ z3%arvwgU+~=TAv72L?;$W9<^avvlQ;`(Mt47sdn-x3 D@)!Ra{iJ>LEkV$VyWpq3b#~Ydq$A?VmZgIz^A?|I1YF|PfUwrV z&X7sfzm%!`gZ5BVVo|Ue*bxdl;&~dNZ1=gj8yLJxFYgB{)e4$KE@awe)6TQ7UtFJT zx*fY2je*qqAy42QmK&?~>_PqW=^5zV
WI9uc%&AOodTHYwBkTC zS*rkk=2OAHQqg2*@GEQ+l*o&^qsWD-5_UL9z?Vphp)#zoqj+Mp$kasviBRsLq|io+ zWLUrI$;()U_WTsOpsq0EY$c2+z-~j=_?R^c2(^wZ%YQZ6H#O8*5j=cU0yR**u0R7{ zT4^qsY4DRGhUThTr6+?<25wn~Kar&Eb}^$l(qTMr^B(mM|5ldHFf-)|C2+!REKuLj z>%KW4r021sQgEWH9>LGjsM931t9qNiY5Iag$xliKhn|N6F=l$Z02b<#hEw!62FS@v z9wE0OMHf$aon#CWaW}fBv|UUMA&*Qz$=g-^$qK-8*3scQQKz*|lNZr)8HFGfGa;DT zFFXOMD+U8u ({jnh<-6>)Ww&*@x&sFH@Tt}x?77~!E z3Rb(3Dw-faJ6{a BCmti7P}7(waRoUrl`bb4ioF-TxBMoF-<;-?CtM-;|X%+^;o+ z=aARUxGU~8V(momwO7CM-UB_dr1InO#q |;HT-zR+7TaF zEp2GovtLU+^XkyWYhwzPjk>1d0oLN`>}}`C)9yg(eK<#RGeqAN)asiWoMMvB3>tzp z?&4A+v1xu1JTJY&Gxe=BWh41hDR&wU_h6J Gh?MzRj>k#w@eS zSCxP>);Sm-@#4^$)uGYs%S8KVM{JTw)p{m9Oiod{b-#8qi-yS>&hcB_Pa@<4)mVY| zme9}LIDdjH8U5wbH^m%+GoVhI3vsq!V-81 O->YtAX0CY6S(fn8E{~W;nNkbb02b%v?IU~5CR_m0h z+4&a>{-+`QkBYvDj-`>YBekuyNtmp(2rLxlf9?q@CMqcZPj38cpb-D`0kll*q5m0} z@JaCj0CmxxA94x*#&`yz@=^eRI}rfj8vp>j{rr=s0Du!M0C27g0I;Y0D;2OwDN9=f z06+k;l8Qpc#>VOC=?x7HQ&Uq%M@Kg|H@CO9KR-VZ5D<`%kWf%i(9qB@Ffg#N{~QZA zczAdO1O!AxL?k36WMpI%6ckic)Zf2 >L~%oSd9oTwL7T+&nxyyu7@8e0==; z`~m_3f`WoVLPEmA!XhFfqN1W=Vq)Ur;t~=Pl9G~AQc}{=(lRnKva+&ra&q$W@(KzH zii(O#N=nMg$|@= KYmvnwpwgT3XuL+B!Nqy1Kf0dV2c$`UVCDhK7bl zMn?bMn2Cvrsi~=%nVGq{xrK#=rKP2nm6f%%wT+F9t*xz{ot?eCy@P{;qobpflasTv zvx|$1tE;PCcvmzTG UEST?Jv}|Wy}f;Xef|CY0|NttgM&juL&L+vBO@cDqoZSEW8>rF6B84Ylav3@ zH9b8&Gcz+gJ3BWwH$OkWu&}VWxVW^mw7k5$va+(ey1KTuw!Xf;v9Yna`7bGXYkPZp zXJ==3cXw}ZZ-0OP;Nal!@bDi*kB^T}PEJlwPtVTI&d<*;E-o%FFR!kyuCK5E8@+dT zclY=A4-XHIkB?7JPtVWKFE1~zudi=!Z}0E#|8luLKR>^|zP`V||Ka=~T!H5wZ?we( z`4n9<^>uNxef{FXFz^6t3b7yn2> WTl7#RO+%IFG(nSTae5fPu6LRN`nnj|fI}8v0Os`FF9cn{)en-op4O$(zb662 zgro(l`E~vOH&XnMF^FaOF3J0M_8(yZ@c-I>*$aJ3J!^~q3Ko@QCg6baVK^=Jc@{gy z;bXbdUO|YnJ7}o|bFl=Yd}t! 8vVMquKscRik9zsQf zB2o&MTr|PXOJ)mJT_y&=`b0rd^VX)cNZl%ySWoF#mXT)ohwakIm) 3jD1xiGa|gl*;36#C*$#P9U)&tCB4$2tF3sA}+3#n?`v4cQsxA;$EvR z%=t$k0Kgv<0KopA)fian{o{nak&%PW|2Ga!Qrw8nphfVS7XJz^_kqm^5K(h&=C*02F`}ZD9I?mqaHICvt#Hj$r+&w3B4LTKjA1+k ztkrT%{cP^LKmNT`I}%f-H^xBpGA#ZFo`EM9diZ#JL+#Xo3e3#byf#qeK3p+vQz# zMoiWr2_{uPh$SP=I3S5~BHNZM;*HPZhcjB@7Jcn9w3|tvZdUeBW+Xelc4$mZLKYei z@G2!HNBAV>fd#;v{x>E-vig2S(-M9(CivHvTRjDT&btzKt1}3K 5P96+5VsRGq+%V5l+{;q)3wQH!5Z@C=giLdH~F|CmHV&lkZf`HoQ zn5Vvf!}z F!Of&ceo!}-6U<$p`s{ +5WF7SO-Pn6WOJBso^ES{d#?d zR-^xgA)w8QM)!FrZHMDUr>`-kIf}O>t5ryoFb4Hys1 -=&fNu|H=`J5VzJu8bf60I>f5s{iUF75=+I z@;`7IQn#|%5JvjG1b)FiMlY}fs6Aq-^Cm&E)(8Bu)n8Fwr||DrjiZP|3!k7&Np>2I z=}Gg#Pl>-A3|@E_ki4Flyu4(XnViM={&>`9{C>EH@&41p5i$W^GqV$ow+cJJ11F;A z%FfM%Eb`Umg)X72m^LohcTdadfh+j)u9sTwR_)`Eyn9oXH?BBcpn#}yD=dnBR9Wsz zw9hX6mI*7sAVcv``3f3;GQwAs%vK}+FR(XPyFcK)<3Jr&Lgh`y24~X<$2^fAD;eSI z!A {tpn3zjGth(f)5#$Ljh(!kXlRqvzAQWl%8 z$Ox2C%c|gJBMptj8fIkxa4^8Af%;TU?6T!N@EZyB-zXP$7S)MB)?&%rV_(`XSz-md z7EaHJ|Mt;m@FtsN%0RB3jy?m1_b;vM7hmhV&2w7Lmnfdc^T$0=i-&lEZggG)f7$3j z?+gSqw%m=MP|gHMf!Db>n(h30^ye9%zyC@yp|F1b Ndm#V+0z zC11$59j%FdGAhU_#f>*)zJ=UG>Ofm4#-2AIMShYnV@DG*?`rGN9vZeW-%(VPR?TpT zGBnu8A}4F&O>wAypOMQF@s*#37lJ(bD5)SkLWA0dp_>+O@QJ(g3GQzK-u?8c$b-9S z;#`|9q`X}m?--#rDtEt6>?UL-S18fQ02%|Pk$gQ-DB0(0+i>V$nqgbTV09%l1Mo^u zRsltteWHB2!5<-#Ch?%nGHuw|a{?~)6C6Bc@7T723QFlTN!`itv{f#&h$94p`ZWfa zC_wx-M+&Oe?22K+>J^1|#@SNs4o~1!w5(2j5*N~uym5mf7hx9Y&0x*0uFVa?mSUgi zWVmD %cQQYzMf-L>q& z32#-vlZ#fzQYGgBnV%nlDMq-HBi2dR0d&UpDlp_S+W}PEzqp41e#X@GVI2~F=hi04 zF?^5|jJ=neQ)+zePmIM-mQ>bCiCAYL!1=y2r9`ZvwSABXqqy$)&Mf{M$&A5_@}iR) z;!n(kfgC9zs)E)z O(c&Ds8OTqRCbEYbPN)$lC&F< z3q^zp(@f(eHr8DMtv&oCU+ku4alWEWh42S;nj2HCF$~go@FaRLI_mUW;rqg3Xrox# zA>vAYyTal$JSl6am)SYf&_D|*FM}pAvB3)i<_hd04dE5jY{6z~?BE0l=Z*69`+Btn zL$HqXutS@owNE3Xt(3e={RHu=b&rHX$ik{-2)f8Ks6U0e_~5tux3{NHGCX=!2TxEm ziKGFhl;R6W=qXICnnFclj25w*2#nS_DZHD_^!MgL#yHV>s!0$a9)TLkE1Ar5x{HMd zG{9x&%-D5JMiEr@Ln}AfQ%yKb>0vUe!b%yzvnQAb=mrpb%m-j8m; *1T{I8Ep8==rM)^_(BLThvIx3NsrWO`UV;O5%1BXyD5& zfsg{u>Cc6sZc0ppCPs)qPkNHrMKOV~sY`<{xo>cgjYQ1$B7W&{fJ)e8CgfDgy~D?S zE}h79B2p#aC%8HCo(XcP%P3YN$`X4M5B;fgpkpU1^OdfJ%&Ak=g9>;PFV`+d?9~Tr zlQ{g(H2!7kB!#_6S@zqerjvsPiishIUXZwYvu?p28NIM8RL!hzZx`6GU8OsDKBbhR zSg3f(`8Ks`(&bQ0v2VDWnOGSbp(5jW1I_Mv64~fW4%u6Hy~-r#V!OB|WG3BoVaUnS zIrXf$RPwm <*-BrCnzHIij0X zSIksH!ueM3kt6aITcL*Uctqz$>4@Og5!C1#t~a%NIr+ils7y+$iN8>n{1vzK@HKMb z_2TaR_dkjTc?TKQi_pO%B#WvBd%)gjTzDv^)`vCJ&vtP Rv*kC1EM!^%G@-_!CM2n|BY$M}Q&Y;-D@L`34#xjvc{;-7X_827=SPl2EwLX> z;mR55*eOlq*DIl^kC 1GicE(QEQ?A=p*r2Don{MhN(wr$($ z*tV^XZQDl2w(X>2qhqr>PELBxwdb01t$EJg`~95Hdvo?h?kZIm_0*{UcRXYK3i!*{ zlhoPMhACCas>}Z9CcD!svFFhOshIl)rma3jBm#0f6=Y`{Pm#5+xmFV1K=M>KFh$-s z$Sj5(p(0VAk~UR~4
w%vzB Rfwd|-2Z3eCVkGhv3+lO7?!K_EAzB7y6PQy0j=s0E z+bu@>Yqkrl**@pzjkdJ63WmX $BqoKOvgOf?0! zGfji^A0SnxWaw<@fY)GY=CPHaS;GRN{X~^XeKWdtM^O?1c|4EGN~%lVwI)`$HrLnG zp RVqIUAbAIo(- ziv{%OfO~+~w7WJmi(zzp%b`^M)Mr<#Hk0Fzgm52}35vkWbqx{n`sVF3&CoZN^EjjW z^F2NApnK*8@0V ieHo-~Y7w{@-i$Wl)l_F@6*S){j5be=Q2u z<~Gibw5H}Jj`vRWHPU+~+a_ju0Dd2x%2Qvn*ihgEGpX+7V;0wagYaY#vY!;h>-VtV z^bCw^zySW*RB2bGh-ChFcJ_}ylz)8{JFEZmi?~U3r{4KBBQWSZcih-|b&Z&kD8{?f zCHnPZ{>#g>NQQHHfAnrvKW=jTo3tIB+^viq&5VtmXbl}58xyr{{ppc|t68r3V$W0% z^(2FJ^7QaASHPK8jFbE)eG&C+Y*u`9`QQm^%o%AQ32f!$a&B=~!wfqhbUSTKVOgsM z!nX|M*0 23IK!f4j*$J@DR7%$v+LS zOgUo5h3VhOFF0m05B9NCIT-9e4FqT$vZwZ84kOrDLP*uD>8A6WBaWc>_AUS#eVX~H zm{#_6>TWTa(watDL$gdtgR|oc$Dq2=Di^hE)= ()BoL7$T=Kim)^3E(Nt?(CT(TQCuj~Cf zbka*rOIr1tx=tTnIbXHD`}@U{#iu>yXg=8k`wlglmmzm%%znwaI#kip_%Zgb7yj9$ z>iBi!Sso7K2X2W-`uGy)q=gz~Ov<)h{xpM(xkJH4^(kn~Fvf_o4oQP*s~8D+Uf%-; zQH%PNjwuc{kcqDEOa>42VfAgK_{)*vZK|*r`{}l(#Nh^Xp%s0u{LaH!3LczcLii7D z@>aGY(%hW=E+Lk-1xZPF*O-&;<7DrGf*#w_vu&*Dw_he7l38bklWF3o&cS9!ACdcH z5$BNKo)H72bgpJY lQneSda!%q|$;?$8zCwS*e$9b;r+oN01u11nyNo)TM5? z1u`uw+cm2it$J!}xkKKv)2I% o5Tja)&OBoG<>$~)Go+l4O*vop1Z39?VJRreyI z4l~yK(>f!-Gn2M)jNOtsNiQtN+Y2+VPNbqIDiDLBbTiBWs^H|2M&eSbfa%i`WkC~O zv2PETOvH~$1={G{+$y6p=30S=_ifA4W$C1$rhL2-Q}bc%NND6v-v%4& f88Tne4y04<9QVD$w^Jl`ZG 3=|*gg9aSKo zQ5>)aO#!KkR{QS% MjO z*0u$r@NZ}0k8qB{D0mw6#7$KSAmZqD(h<6W_Oi!l(EY1328-aQoR9a-?Y@S?Q`)kU z8a`JSjP UUkDNSM>IJ@Gv~76JN3|{nkZ~q=p4YKAlO zJBTulKo(_a2D-Fo)t! nl^s6UK;1KGOfPmy{`++plZ9qR%7 zW*m-35k966?00zw$AWyVr1ou}!iV}s$eB>X){5^cDL4YApEk-oUuvoBDFOsqVW4G( zL>Vz})}PKQieO?^xk?H{M*;G!QZCVIIZIiXv5!{!$9(q6rIcv-!uKT&Q*H>4%Fg{o zxJWDw2noNGwnB39vA()O(4l?qp>ewm`5-g}Mrv9;5IscLkNeAJFA0m5#q z&TXk=HIGXD0J>!r&WWvb-he^o6Tt`HJ~SOhhEs8EhM8IbP+Y$=bB7lln1qESdJtR7 zK 9i-F!F8@RL9maB_acU+Gnn~lMG4QJ!np@dV8toS46 z;%irn3%y=?q?W^i4)5JXMJ=1jeKqyZ u(r%rYI8p5@Ocu-lo =xD0UXVIN}>2BIloJyjjr-N`9inOZzv5ZetG0PCahO&jt(`hPJ}^6~ zrj?-J1zRR-2et!kiv(CmOke8AW>N{i=uEd2OrM}g3t
u# zo{V{$ms){={azSr-IF-xLS_r4c)H$*u8Xa6rM>pZu=Z^yQ$JLramokumiOhB{&Mi0 zZQ|7$_c}-3*0)vu6?bC#U=!`&X@8#hhXyBjLv2=%Q|Fx1)HUf4vozBqehd>GkS6&8 zB+iQa>1VD$DJ-Y#5bQN3N2E8fvYds>XEr&mH;430pc@1}7%=aIcdo=-+=LQPHx3-R zZ|`bAXNQ9sIQOPR5%;H>*mM~xoX#DWxl7muPt{{=|134XPAT!Q;B5~;000T#004yl zc1m$Fc68GHJ?rw1a+9TOZMjDOaXJxr4=9E+3|Np{?dfrb(VVRnYzk&6h-C0L&^3@> zxiM;$d3(Jx8J3x?o5G_$zPMX|ruf3siaYHyMf)w~afu@R^?|t)lgLwY }}a?~_lP^O8r%a-?=yJH*0%PeX<*Y%Ii1>j?=_6o w?Z30ZgSqVB(4Vc5!Kai;^BHn6~I>cuQ?# zD00^eb*VL*n1+#w^f~cbg_P3kn!{P1HS*BOdPT^w>YSX^B#hCpS2^yA(;A6zM{bh1 zW3aHQkoJ+JHfiro24%-JCg{CDFsD>5K?&|+!cwvMz(ArEYk(EP!?Q94<%-!y8BKzz zqC?mfkr+{-_=R{}OZgbBtO-d0*$fs`ybNFD$qxG1BM|I}Q~s!8i%%>--|dZHK4b2! zW=m}R+-Al^GKqlTW9(|ETZz~&))M>F3%EL`M}QvUY3QD4j+$`NQfOW3%MIFJKR3mQ zy0SbUU(u9^e-fs#m9e$4jgz^Jk+IuvC;3J-{h05p@UI<7Pk36o&K5-hJr^Hi#8!n| z{7VT(I16=cD#{ 4pK>3pK7l%pmyY4Ol6( ?K{pl?AFwIaL`}?~ zQnb)rwk@s_$}+Islb1YYQ=!nI#oM02$Kc&0E&`Q_e~u 8L z%{|1vP$!jM@)romW5L$|0RB$hH4JofuLD3%A5J6eLEx|~n{#f`iR9m+uRVFqqa|T2 zLIK@fM *!sTPr-x$J94T#(aLN)+C=Lr|8{ZZx2zby z8%bQ13V$~vh|Qwt%2vbna6Oj`BsJgxaEX8FVb1$*pvkZEKIc%Og{=`uY-L<2~tZRoP>~BteR!a9WzK2W@i2#C$|On9|RrLx{?DnxP>XzV@SN zSwAF?=>1Vnha{F|h@^d+`o0} UY5E6=Y_OJ zNtFQ P{fvdc-2pT5BS(AKjV_HXt+BT55nZJ8SQ|gAE{L={sx*!30d#=4#1dp|?am8OwEy z)U3KYFyi^_V6yCQ&JKHghb>-G8JE4f@s!VgX;hlm!Q{<21im(I{&3>BU9P$NT=1sa zQIAti(*`F!Xhrae8~^^Ql5u`!W7OlQx3pK9!K}lZV7C0BF~97aNuO %0O-3*c% z>!IFCgUO0sNfuY>+jN|%>D!e2p~O6A!m#|vl%L=tW)QVDr6+;sHyed @Bh+Hm>E%7%Kj)guOA~Af0*FU1?MjlUyW-2XdwW; z!eKKZW6h|!AnN&+LztagQN>o`#^+)M_vQ}P7bT&x#7myNKH-o^Cchv``R>9s3FBVw zw;o0pzS<7wh{5^D+Kke{b#KOv?hd!h5ed?9Uyd(~+z!F*8hmeC!ixp}qw<{FGP~VI z;iVOg`1sks@Z8E+fq)>PdK*LIV;@i>1+PP?2^x%)1on^JLlIEXe&3^#$G0hEwFKk@ zqEAe9hGLuV8FR~9OdNO#hH6U2m=b|H*w>u?nDSSwm?Rl1>N?aK9!}e{ wG@zgLkTpY_l=f;G zb cEZol+{fKv(QH&s>9eW6od(C6~-CN7++IF zW1BYHb5dt*2A^0-I*phnB{FNm)EADow;F@G$x#4E#oQcsFjDYyR*iT@2UAFTP0Dau z;k<~UvaPcr&(EAL>l?l$U6gVoWg+w XMEZ8xEcllg7f)*}|0F%!b? z_3r1G2Fmj~2)HEtsJY~c18$*Ya1YZU$PMi1V&goVMlDX*Elx@} W=kun^c*ScD2=sU2CG)Uo1 z#K;OCeEKFN@C4EqYF>?{cL#jeczYSOK8A8AO (xalQqoIpA#uhEL~j9( x^+01sKJ<< z`E-4k7+R8rkh4tbt=wUWifOhqq-ogMeBRKno`q<-0C?C>d#u=2$W`*j^;-R)m*KQD zz{h}6pSE0IVq4TzHzV#jdC}FW6_A!Y>-Iy^v*L~|vrII`@ftVMvW%Dp$6A2jam*@X zYGt$Sz=^l=yqovj%-ZCR9&?4_+X&~!3>Wm^!zpW5a1_`ICetwFy;|s@44g{sl$@H) zk~~Z^cF^;uDQj6>Vn0*Dgyc$l=P|7c&O@N*hgT=h=j-dksSr(%G84DvbC*{3Eww_} z)8S~1dMm$D#16sn#}{L(ay}^JkIIJiMQQzfm~q0vNqz|ku?HKdIlLRdM{LB`b7;|F z O0z)?M|p7o(;vVN|3 gE7`{T z)zk)U#yq^nmn=~UVyHZ0DNQixrRT2>2EeUB?4}vOtmi&C=Y_~eXrGjoZDmLhyScmR zay&QdH&)vZj0@hs?)Iy{bNf!^kO{N{CZDt8QNa$I|IB!d=vTndz>fKvJ8S;}J&q}n zri^3CnU!3egt#kIvgO?7-H$tJ@1W)a7UU8pZs2aaHNUy?6Ew@^Dt5IA*RuWKVkzUK zs{my?u{hCC0y5C?D6)<;!nf2OrNS}!qW6v9g;ioZSF5V$P5#HW(2ssPy%e}{W9Z10 zERF6BIM3>Ug6>4B)F>o`s3Y`%oEZigk_JUVQx-B+#kYW;gO@WufbD1q4k1y9B#S^X zk(c6~NVCKtpr_irE!7K*3Wvfie>5x`9U~vl%hG1#^pv)fEA_%~WE2iWtM$2|xO{Dm z*6&d;hr42zJy7o99+DtHy56EZovn8Aj7;R2Sz2Rz%<(HDV@T-%n-Q;ppBX?rgrBD` zgz`XpP54Yf@s8$xIrrIU__Je;zzaWcqyI4OI^YDcV~v$uoz;fYQQ9#lOeQhEV@AG) zqhK}ajpxtMIlqW-*V;uo^LTvQ^MvgTiXdf5X6QQZ8j=JJq9slHnuxfNMwAv5guOTx z1QA?qq^}KiEOGqVYDkl@Y~vzq@^`0XesHjS5S%|u>b>kwG*HGPJ+i*^N21X2HD$aq zEv?d}_$bi@e5o!$?`6-!-VZ$Q8#Ui$Y&oMOt6cmhU*r$PQy$YFm@O~;-D!Ddz7{{n z!fh` u%ALW9f5Y1dzFvh)iUqEtkJVLK~DL;YNvWsf=}YS?dl+((cfHp zZ$yL>F7c4ARW$)biF&9KTHIZ82DCcC*sqWv{;BnKZyK<;aqY8exiJsSf!8*QsN ^|&y*_aR+EvPTW=i9N3HsFu1$^3qatjLFVp_=~e!>Th zNEizYQv?#EXe4FylMGsZZ8g`brFCs8*QGISc_%oqfX-pXBb4 yla}85I^)K!=77-kK|>D5K*+dU}y| z44?+=q1{$67$iV;l5k3`XW>~T(_sKL^hzijN=NNX}7})Yjqj#^^jBTY!n w0e5R6vxs8bZ4pb<*r3x{u-M(soNADj;~@-8f#sZE66~XW7gNI(|h}U zB>=Ew-cHgQOt5nYP!=@fD%55Y ?uC}#x2N3 %nst${lTD`L&K_QhA;x q*8A4z>=CzuyQ_l!}?7hv!8Ld#6Va zTN~2co_8h^& ?Q*u>&BSu!N2S(5!?&a9e9+ZFfngG%K&>O^Ocu!HEJ zNHvwsnTc(|C14c`8npQn4Rr;}BH$9xMazOhHBLdNPo$CppT7tT$y76xcGMB%LsKus z^~0I@J~kjlvGDbxRX}Ca1HHI1HJ z$K<{kpWk($2E|2YbLd5L4|ta{Zv=OoCn~I;_y8m^bOCpWt|>q%y#iTd&mmLjTIuus z5%LcS3UQ0(7=30Zhgtdla ez;HF1K- zZa*RIV^g=71B4c`nQQFemL17Bsi?wl$P($o1fCV6_eK=%qE`s@k*wM;N1yps-*ff{ zoF%Madu)Lt-pU}$@ldn&h803O>&Q@4QG@Ov3ofh*b^`mz^`bCo?#b~XYAoP(t2%8- z6E9rasWY?al})U~-M;wmT4ibvcc7UWJ1x7c=G;R6^PT_IcH1gVhEIeB0I2&o%Kx#8 z^3T3Or@F_liRpJPpbIXmF#KA;j0S8rbCh%*KCG>;O_)?iC?Ns0gsQXI60{$I1B-ch zZzd)waNp`YLV#d_BG!jH4%zC*uQF~k>W`}vpnsY_&85n8Wky#|r8eV%ba!w;``;e7 zX0hME1zVN5>qrtQPtD=CwEWEPhUqIBRd_<-_S;g;iz7vmM_<;(mN537ZV->@oUKrl zrwyXVWjYna_7LSx$ETHw8Qoi!del&+Z=N^M>3(E?_L-^mFc;}$fys2bVpkl+3_*P4 zr?wY08_&fZf9ywhQ*Ww%8x_1L9ipI7ONxbw!rgTU%^<(%qf+~JhRow~BoYM? zJ@zw*uF?@vrbYb=f=9wAvHSs%<-P;l58GipJ&%~UtllsTIYrwH!YLM{d)7X*2Q*Z9 z!wRLJFzdBe=*SK4%2d8ZGgkyy9yank>?!7xD{t)se)8;dS+$}7!*ngXVUi}#G3pva zQgPQ@X~V9Ab#?}wUlua%^*2M@>&T%r6MQ6BLTA@G%Cy6?t}7E)A&$+&L~Va?)mL|y z;6V6TVmQy&64BLqAwp%r_aHYa+)*Qe VH);*15w7Zk1h5JcMrKQz>VWJP7FxgwUa!WGn(rc+@(MG>kv1Rof39F5bU} zxvuO_i~`Pm#wR#VEn^PQ?R5*;oJ}=A1}R?{yPWgnY$CW);>lA0ics@?fZjRm0}&N+ zfxvmGnJJ^Z=&9R+D%Fxc@c*e<~e%XGFbGH zBH^w3ks{#}u?(`WP)WW)@9mL^pR_ GdoOOoAZr%AVL`d+EDD)4&AVn{3LmK`RY?;2@zW+`rwzY-)?a%}+y!je*p zEaVN8K* 3 =rK=MkFPF4*840`8i!-chFkA(F9tjiSDga%5 zKSk`@PH%7f4&a?cGY1{MciwEz5q`xighmQEGih0PP_RpX-|iGGvjnL@t46JGc|I|h z8G@zXHyB$zVQQhikNAkh-{K>*>EmC)#oxzrN8{A;#uTRbu^_OBVOYVd`?x}7K@iwT zmW8n*(Wd%tr`Zw!7{lm918QanfzG@V(qmjSr zNVClsTbsw2W%EP}qZer0M~$*Kw9elWw|4T<4=aPy z5U~Ig*B1;u-#vN={o-~0;x^#`QDrjz$s=Q8#>sZ&LQKmWvo-1Y75^#pn-t1IoD(ag zhzcDnuv_s_pFrrgnkTBr*zOlmI!eJ4&X%T7xhAz6@Lm(ay&h78?XpxNh@L7AAKxPd z$JOYv4l~9rb-*`1`>l9GWUMCCU^PGy(5r4We=&qTk-A~Ypq3GU?*uf9Y8rX?^fcoh zR+#+L9uO32mt|OEfhZSPXljAeDce<&5%Dz~FN|RBeb>rPQ@`RPmVU)YfH5~a@^Qfg z+h-KgLTh_#aI0__)21%r2iDY4eul_}R2L(MUM1pEB?;x-Dn* p Zk%K~jZ zG?M%f=CG`TQ;-^fP7fGVMzh*8Gz}AV`hA~@n-0duC%MT_5K%y=K =ZXd!+THv)$46r&5lQ~HVXjl(QOY)*KeT_&8dCV z)w6f%C{?z_OS(h5Xv9T+#a%6aAE6P8O-;*`_mRIsBe>eaAlmL}ZbLfVzgSBp;*fWL zECDOEzq&_0(D&m?51<_I7!6_S=TQq&AI`+jFV{@gvfni#wu!*A-<8c$1C)7} I|I3aLZxwQ;$CKsnq=hZYEkOtF*$5smL@(r-W-L}pw z$9GNSfLYwGrm!+t|4NN;^yP0fkpiB8E )swdEvGTr zUOV#KUi?sn8cR~kvg;?fbvJO3S;2m (Z;&NS%Fl3xp|xQsv&3+rP=?PqTN)aLyC&hWnl zE~>S*H`E#mEtW(S }OB9fqjYA@_xD5bdNTGnOm47vojW=cp6lr0%OtHSxnjKKY8W<(WN+}l4gBQDGz zG+&(_unGrFI?X?Kelip#ir>uceYIgJHXUC{Hga+%G;|E9c6J}?eg*0*qHt3%(mg{t z+j|_`A5FQQ&wr>D 5)8w1z_JEV?%NoGH@{}>Nz|6eTW 4I`_E<(m;_pJcG6olxpHk?cd5u+!Vn`BHwVlFmg(3~KO3d UYR{2CS?xtuAM3Wp;Mij_&u}iHK zIXkGf9niz3)dOtPj twG&Wn_*{{WJ!l=rxHf{MD;jW=YjmB$!56<8?8 z$f0*7JY}O-%^0*%llK|nsSmN~dw7z{_UyN=ek&-5Y~s(;V@DRzBttq}+j*Ek)dOlJ z*$_wHj?Limv24F{mb}8SY_M0oQ8fPwjkttY0XvHX6_Q_-T2AR!;C?0mjMQBVv?089 z+5e%;XmAecdFcoHN)(J{(tCx^j$XIpTHuN&y5+5xOnnCr>LIg4Z+3E_%5qeIXuhu6 zTlA`-2o98*K_g7d+-!>R6MD0!hM*mAG7bw1mmnuVsL999$*$&FzWCO!0L$!ZeF!5I z76qem{l=Ic<^- Z<;i#M|}AB1Y=o_5=Pxgli`~rP9h@A%@gSdRQsE zb@d*3>NYaZo;7v=Xrj7E8jq+zKQi@0IfUp}B>ak&h0^ToLnokD)uF%4dS9H-wLkUF zwUobOr++hulNu>1j^Kz@@Y04(YvR_)&C+)i_)fFZQx ^Hy}L)c5#T}1M7S8}5L;Qj1u3aenVO=->u zDNZ5})-?9ZGl=<}Jz|$x3E|`I*01Md18MfMnWI)lyS-wNR8+@<6uWl L{%>Xem)j {=2(m|LU&Y zzjN30AMQ%}Z@Ww6-`&;oCwJ}tau?k1?$S&fZvM?(Zr}dwF6m$H;`~>4HU91{!{6LB zUF(JX-*y-1-?)qX@7x9Q4|jRc{k^-me{)y
29-)C_ZSi9+*@)o*9to$9mswCQ=ck{@`j#(s? zgp(21sY4|@z_`26IZ0&Kvks^6_8&eb3()NW(VaxqWVldeEL&Rvk bOt|Gzw~M5g0@O+7 zKv+rFuQ2HBD<{wzq__tNWqKd1X4n9}4@gcZG?Fuv`R~@B)+3P !cNU3Q67FystIXE`S?WeU;80eXf z8j{KDnke^hGPc8+z^!*Nild420{{<3c(*-`4dn|L$dqA7*Vbk=)%YMj24E3b@XnlN z>TYBvz +UPwH c0|n!k0Ds;oMvd{7=%HvXr(?0 zbRV&Ovya!I`|w(m$v8hb)AEjWe#Q2^5Ia!aKMUL;6r@m=5gl70hF9odf?Y{}3krbm zt$iR2PaOUcsiPD)?`&s{kYiQ5cke$LIN~Qm*rUuK2Jfrw=neO+=%4{d*?rNds~+gZ zXRnK7n4Hy$2BsD$9BSLA79@snB*G{r3CcbWa6m|-qP9U$Ku;stb(z^W+ZA4+X7l&h zK3Ccrh#a~e?IctZea8o9n47@e@uSF7E%<>d21a}t7&DtgZx?i!T~-Ng)V7xfw+e?b zE&6H!2z_JaTzGz@y6DfS4Pq`IzaQe&B~MZetGif^>VX=(q*rEOtqlr)BMfZ56J#fI zfX{?!0T3rF2!B9=sAZO@<9f1~7EXcADq_K3fBdeb1-6k8I=16Us#e#EGbV }0YgobrXmQqwpbf@@obY*^b z7soGm$@2W}u8p}5cX9phF30JAxNGTmcS)dBRh6jf4)2=&tGfdK-d)ze-1Yc>bXVnH z-L>iZe{z>j${+5U{;Ruqr~l$E=l`9%G86uPbeG_7?vl;;cXu`YXYOkIi@SjT;;xim z?gIX^yPE%N?jrjScQyZS-8K7XcZvTe?z;TtF4KQ^SMJ}ttLeY%uAaZTE8 @!Hf8#E)e{ol| z{(s^w?_^j1tWYh${wx|*8))1T@5|Y55!pFnv1=qN>^ji;r%HB6bjOs=MdFVN#{M*k z{-i{HOqROTN>I~%%NxEt&FehCw(Lnejp=9vBTK`4MaR4eSq8U(I7&HpY6mFETHHDP z$Lra3pc8(1F-)!7R)gagdKGp4t28;UAYS^An)ZW|RCdQ^Tl%b^AaaPMrp8Vj!-$5} zI9H!x{ZtJq6y-qfj+`36zeTaxXU%yBr`uqwz9+8fCQ5F*LQny_4Eg7gpOc*3>QZFC z!~zUeTMM$qxwYTiFk;khs#bM+u- <7 z)a7XV1~zS7 rwAdh#9gpU3#$b|dUYKBfx+07k+7G JE#kL+edhgf#FB=yBK9~*B++Id< Pq7wkTFCkFrP`?+s+0Lf9;GN!)v@SsEAoVD1?kNg*;^{{m` zR49d4`rTT!ILD(El^n5Uu86Uhzp7nTMVOYGks*YAg#E< ~3C*)ofIqYUJu` z5@U)M%)FB@_O96aM^W5H2B&Q4Ax8JqTU<}5XEBmee;)Km){_GocDC+)B<5a=w7V-4 zy{@oqtSIlLTHU*&Yyri&$fJn5JXF4pC3Dfb%0KBPm|KFJ2-jL>Y8!cQgfmN$@6sr* zB`)WYWyU4+B40-C}z6mrveWDyA3Qsq;bjB92UQ19?%-B2bdqJ4qip#WU}+;R<0O zd%7y5bw2M)oQ|$V7}84k1eVdH8P0-*?=^?wRNAZFnFQ)o&BIKu6G~Mqmb!n{u Z}#^G{?97?|E$vg&no@@TdVZHO2W#Z$tK9hf)FwU0Kmsj zEd054{B`$yL0R)-F9_LN);mZKhP5WDQwN{cYb0I}+L&fTW4XX!w6>HQ GII?-Z3Zrf4B zG9gb$6KRmhQwX4 &h0@R@FP@YAo8gm51UQM~PStD$;a j z22@zEy?(Y$Gz?PT!0gC@n^oIt&;*O^eHemxHZ0f=mIZgEoUBd7ch%d#Xd7b-SWv_Qy@?yN$jX2#5{d%1gFZKR6)xF>oy{sk6(y{`qCn` z !s`7I+7tI)dfHXK*d-uA-wYZOU(x#7nC@ G z $ekY%{fVlBBBT1E}J}gO(Cprt`2y9Gg+}>Hb>qoPQ7Gjw$qh(Eu1{HL+8vC z6*-+tJ!zoHt0XpzJ3tyyGSWu(Pn+X;7PV-8?OcFFcVJOC`i447W9SPBdpV&@aaWB$ zSu@W);nB8ZkFE%O{ (Kt^q>MjuCI9awWEfP|sW*LG21g%J3x8Z_zg4GSp%|4P zzxz}`56`3c^_>9mqzKU*0@5O0F$1?vp=d*>eHK@n8?YRVXxH=H;{ADbS&w{%c-8@X zy5pW(_u%?@uKHWfDV1qI r|uJ@6tB`jC&A<@q^=ISNBgR zc_5+a#-1D}o)=$uGGs{0!wunxWdkF9Op&pC_nFoJZF|JKyaYi?OI?w@1~RRg&eDX~ zAWUNP*&&W0*dgGA2G|h;B_1)x`5ifyRBdPx AUoYClJLTyMf_;1+z&Fs6QS7er~!dzP2#}8A9OrE?_?rS zqcld1MC4}F5UVfKUxzuL3Tk*Q?x*@>bHgGmzz7o(KS20!zhIwV-9V;BOqq4xhRjoG zlC-?8nSl!@SEJQC-6&{SQU7^`2&xn$mMm ZZEoItIMU14Vz_2F76r8@aMy7rQh%tzcE@%n~JGHN&$W%>1r@9P( zU>3m>JoZlh8Ijn^Pmsj& 75{5<`nwGB1A(D=(os*GHUhy3|T|^6u kL(vOwrf0_ zx-%IVO46~-*BHSF>k1kQ(5AH4XGv&a!fjG2z`p%WN7>6A|HaHODb#$mdjAT5g=x_7 zRMHk$CDj%hG=MPVRjHYz>lpZ0gV65RK&GD(f@*j!Tn^R+B>JXhLZQUNtm%Z2Y3w1} zC6_fUre4i-hNDGLJ0vyZfnCWF`}yAlj0NVqA;UgWzven8q`3fAyTaWeLwB%n^E@=F+jmRj0d@3iRtPH%s~axwQ3UKk*oIvAE7-3$ z_U~!-$9?2KdKrJp-2GKXHxds)Dn9<#oB;p;=RdB3e?IuHsk`Tz){d* @kME1u+l zJ8kRIqAed!QAV9~vMh|5kTsq{dn-@pRjx0mb!}WsP}nVzH0Dnc@(`s$=@9GsUcJJ) zeTg88Z)?U+l%bd+tzyIu2YUdIc5y1)uGh=pejWaj{IFN3;vu2)(&&BD_V#33sNkVo zZZ2HIW1+@VZrkKbPpXX?7i~MtP^-=Aa^R%T=EU*I|GBf5Z)D^wp&0cxNp6>;NQ)>` zRB`t%{Ztm)a?jJq<(8@J``7K+`Jo3jD>>C?vrKKw%h1$KYB{UJerf}WZ58Z;`cm0! zg=;PnMbxXYc^$X6GTF0{daobUKi>k+Ro-RMN{qL~uI>wo0g1in9^l8bSgjb%bW *NKH>tZz7v2;|KLO29KZ}3dU8soLTl1Y$E+Bstp-CXypPy?N z)y)6weKNtb6=inCD~k%vrD0t|w!Yh93& 25#c;1~kaMpoSQuI)AV^8MmW=w=Mv8e1XqR@w&a zw}$D$)|{PA#sY?BBH3l7tTKH^O^k&{NX|Ie5`u|m;rI}QpoqR$PY_jW<2X%~{EFKb z