时间:2021-07-01 10:21:17 帮助过:13人阅读
Common.php
* * Permission is hereby granted, free of charge, to any person obtaining a copy* of this software and associated documentation files (the "Software"), to deal* in the Software without restriction, including without limitation the rights* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell* copies of the Software, and to permit persons to whom the Software is* furnished to do so, subject to the following conditions:* * The above copyright notice and this permission notice shall be included in* all copies or substantial portions of the Software.* * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN* THE SOFTWARE.* * @package Tyrant* @author Bertrand Mansion* @license http://www.opensource.org/licenses/mit-license.php MIT License* @link http://mamasam.indefero.net/p/tyrant/*/require_once dirname(__FILE__).'/../Tyrant.php';require_once dirname(__FILE__).'/Exception.php';/*** Abstract base class for all types of database connections** This base class is mostly here to avoid duplication of code since * databases share common functions. It** @package Tyrant* @author Bertrand Mansion */abstract class Tyrant_Common implements ArrayAccess, Countable, Iterator{ protected $socket; abstract function put($key, $value); abstract function get($key); public function __construct(&$socket) { $this->socket =& $socket; } /** * Close the connection to TokyoTyrant */ public function disconnect() { if (is_resource($this->socket)) { socket_close($this->socket); $this->socket = null; } } /** * Returns the connection socket * @return resource Connection socket */ public function &socket() { return $this->socket; } /** * Removes a record * * @param string Specifies the primary key * @return bool True when successful, false otherwise */ public function out($key) { $cmd = pack('CCN', 0xC8, 0x20, strlen($key)) . $key; $code = $this->_send($cmd); if ($code !== 0) { return false; } return true; } /** * Gets the size of the value of a record * * @param string Specifies the key * @return int|false Number with size or false otherwise */ public function vsiz($key) { $cmd = pack("CCN", 0xC8, 0x38, strlen($key)) . $key; $code = $this->_send($cmd); if ($code !== 0) { return false; } return $this->_recvInt32(); } /** * Initializes the iterator * * The iterator is used in order to access the key of every record * stored in a database. * * @return bool True when successful, false otherwise */ public function iterinit() { $cmd = pack("CC", 0xC8, 0x50); $code = $this->_send($cmd); if ($code !== 0) { return false; } return true; } /** * Gets the next key of the iterator * * It is possible to access every record by iteration of * calling this method. It is allowed to update or remove * records whose keys are fetched while the iteration. * However, it is not assured if updating the database is * occurred while the iteration. Besides, the order of this * traversal access method is arbitrary, so it is not assured * that the order of storing matches the one of the traversal * access. * * @return mixed Either the next key when successful, false if no more records are available */ public function iternext() { $cmd = pack("CC", 0xC8, 0x51); $code = $this->_send($cmd); if ($code !== 0) { return false; } $ksiz = $this->_recvInt32(); if ($ksiz === false) { return false; } $kref = $this->_recv($ksiz); return $kref; } /** * Gets forward matching keys * * The return value is an array of the keys of the * corresponding records. This method does never fail and return * an empty array even if no record corresponds. Note that this * method may be very slow because every key in the database is * scanned. * * @param string Prefix of the corresponding keys * @param int Maximum number of keys to be fetched. If it * is not defined or negative, no limit is specified. * @return array An array of found primary keys */ public function fwmkeys($prefix, $max = null) { $keys = array(); if (empty($max) || $max < 0) { $max = (1<<31); } $cmd = pack("CCNN", 0xC8, 0x58, strlen($prefix), $max) . $prefix; $code = $this->_send($cmd); if ($code !== 0) { return $keys; } $knum = $this->_recvInt32(); if ($knum === false) { return $keys; } for ($i = 0; $i < $knum; $i++) { $ksiz = $this->_recvInt32(); if ($ksiz === false) { return $keys; } $kref = $this->_recv($ksiz); $keys[] = $kref; } return $keys; } /** * Synchronizes updated contents with the file and the device * @return bool True when successful, false otherwise */ public function sync() { $cmd = pack('CC', 0xC8, 0x70); $code = $this->_send($cmd); if ($code !== 0) { return false; } return true; } /** * Optimize the database file * @return bool True when successful, false otherwise */ public function optimize($params = "") { $cmd = pack('CCN', 0xC8, 0x71, strlen($params)) . $params; $code = $this->_send($cmd); if ($code !== 0) { return false; } return true; } /** * Remove all records * @return bool True when successful, false otherwise */ public function vanish() { $cmd = pack('CC', 0xC8, 0x72); $code = $this->_send($cmd); if ($code !== 0) { return false; } return true; } /** * Copy the database file * * The database file is assured to be kept synchronized and not modified * while the copying or executing operation is in progress. * So, this method is useful to create a backup file of the database file. * * @param string Specifies the path of the destination file. * If it begins with `@', the trailing substring * is executed as a command line. * @return True if successful, false otherwise. */ public function copy($path) { $cmd = pack('CCN', 0xC8, 0x73, strlen($path)) . $path; $code = $this->_send($cmd); if ($code !== 0) { return false; } return true; } /** * Restore the database with update log * * @param Specifies the path of the update log directory * @param Specifies the beginning time stamp in microseconds * @param Specifies options by bitwise-or: * - Tyrant::ROCHKCON for consistency checking * @return True if successful, false otherwise. */ public function restore($path, $msec, $opts = 0) { $cmd = pack('CCN', 0xC8, 0x74, strlen($path)) . $this->_pack64($msec) . $opts . $path; $code = $this->_send($cmd); if ($code !== 0) { return false; } return true; } /** * Get the number of records * @return int|false Number of records or false if something goes wrong */ public function rnum() { $cmd = pack('CC', 0xC8, 0x80); $code = $this->_send($cmd); if ($code !== 0) { return false; } return $this->_recvInt64(); } /** * Get the size of the database * @return mixed Database size or false if something goes wrong */ public function size() { $cmd = pack('CC', 0xC8, 0x81); $code = $this->_send($cmd); if ($code !== 0) { return false; } return $this->_recvInt64(); } /** * Get some statistics about the database * @return array Array of statistics about the database */ public function stat() { $cmd = pack('CC', 0xC8, 0x88); $code = $this->_send($cmd); if ($code !== 0) { return false; } $value = $this->_recv(); $value = explode("\n", trim($value)); $stats = array(); foreach ($value as $v) { $v = explode("\t", $v); $stats[$v[0]] = $v[1]; } return $stats; } /** * Call a versatile function for miscellaneous operations * * All databases support "putlist", "outlist", and "getlist". * - putlist is to store records. It receives keys and values one * after the other, and returns an empty list. * - outlist is to remove records. It receives keys, and returns * an empty list. * - getlist is to retrieve records. It receives keys, and returns * values. * * Table database supports "setindex", "search", "genuid". * * @param string Specifies the name of the function * @param array Specifies an array containing arguments * @param int Specifies options by bitwise-or * bitflag that can be Tyrant::MONOULOG to prevent * writing to the update log * @return array|false Values or false if something goes wrong */ public function misc($name, Array $args = array(), $opts = 0) { $cmd = pack('CCNNN', 0xC8, 0x90, strlen($name), $opts, count($args)) . $name; foreach ($args as $arg) { $cmd .= pack('N', strlen($arg)) . $arg; } $code = $this->_send($cmd); if ($code !== 0) { return false; } $rnum = $this->_recvInt32(); $res = array(); for ($i = 0; $i < $rnum; $i++) { $esiz = $this->_recvInt32(); if ($esiz === false) { return false; } $eref = $this->_recv($esiz); if ($eref === false) { return false; } $res[] = $eref; } return $res; } /** * Call a function of the script language extension * * @param string Specifies the function name * @param string Specifies the key. Defaults to an empty string. * @param string Specifies the value. Defaults to an empty string. * @param int Specifies options by bitwise-or: * - Tyrant::XOLCKREC for record locking * - Tyrant::XOLCKGLB for global locking * Defaults to no option. * @return mixed Value of the response or false on failure */ public function ext($name, $key = '', $value = '', $opts = 0) { $cmd = pack('CCNNNN', 0xC8, 0x68, strlen($name), $opts, strlen($key), strlen($value)) . $name . $key . $value; $code = $this->_send($cmd); if ($code !== 0) { return false; } $vsiz = $this->_recvInt32(); if ($vsiz < 0) { return false; } $vbuf = $this->_recv($vsiz); return $vbuf; } protected function _socketWrite($cmd) { $len = strlen($cmd); $offset = 0; while ($offset < $len) { $sent = socket_write($this->socket, substr($cmd, $offset), $len-$offset); if ($sent === false) { return false; } $offset += $sent; } return ($offset < $len) ? false : true; } protected function _send($cmd) { $status = $this->_socketWrite($cmd); if ($status === false) { return false; } $code = $this->_recvCode(); if ($code === false) { return false; } return $code; } protected function _recv($len = null) { if (is_null($len)) { $len = $this->_recvInt32(); if ($len === false) { return false; } } if ($len < 1) { return ""; } $str = ""; if (($rec = socket_recv($this->socket, $str, $len, 0)) <= 0) { return false; } if (strlen($str) == $len) { return $str; } $len -= strlen($str); while ($len > 0) { $tstr = ""; if (($rec = socket_recv($this->socket, $tstr, $len, 0)) <= 0) { return false; } $len -= strlen($tstr); $str .= $tstr; } return $str; } protected function _recvCode() { if (($rbuf = $this->_recv(1)) !== false) { $c = unpack("C", $rbuf); if (!isset($c[1])) { return false; } return $c[1]; } return false; } protected function _recvInt32() { if (($rbuf = $this->_recv(4)) !== false) { $num = unpack("N", $rbuf); if (!isset($num[1])) { return false; } $size = unpack("l", pack("l", $num[1])); return $size[1]; } return false; } protected function _recvInt64() { if (($rbuf = $this->_recv(8)) !== false) { return $this->_unpack64($rbuf); } return false; } /** * Portability function to pack a x64 value with PHP limitations * @return mixed Packed number */ protected function _pack64($v) { // x64 if (PHP_INT_SIZE >= 8) { $v = (int)$v; return pack ("NN", $v>>32, $v&0xFFFFFFFF); } // x32, int if (is_int($v)) { return pack("NN", $v < 0 ? -1 : 0, $v); } // x32, bcmath if (function_exists("bcmul")) { if (bccomp($v, 0) == -1) { $v = bcadd("18446744073709551616", $v); } $h = bcdiv($v, "4294967296", 0); $l = bcmod($v, "4294967296"); return pack ("NN", (float)$h, (float)$l); // conversion to float is intentional; int would lose 31st bit } // x32, no-bcmath $p = max(0, strlen($v) - 13); $lo = abs((float)substr($v, $p)); $hi = abs((float)substr($v, 0, $p)); $m = $lo + $hi*1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912 $q = floor($m/4294967296.0); $l = $m - ($q*4294967296.0); $h = $hi*2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328 if ($v < 0) { if ($l == 0) { $h = 4294967296.0 - $h; } else { $h = 4294967295.0 - $h; $l = 4294967296.0 - $l; } } return pack("NN", $h, $l); } /** * Portability function to unpack a x64 value with PHP limitations * @return mixed Might return a string of numbers or the actual value */ protected function _unpack64($v) { list($hi, $lo) = array_values (unpack("N*N*", $v)); // x64 if (PHP_INT_SIZE >= 8) { if ($hi < 0) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again if ($lo < 0) $lo += (1<<32); return ($hi<<32) + $lo; } // x32, int if ($hi == 0) { if ($lo > 0) { return $lo; } return sprintf("%u", $lo); } elseif ($hi == -1) { // x32, int if ($lo < 0) { return $lo; } return sprintf("%.0f", $lo - 4294967296.0); } $neg = ""; $c = 0; if ($hi < 0) { $hi = ~$hi; $lo = ~$lo; $c = 1; $neg = "-"; } $hi = sprintf ("%u", $hi); $lo = sprintf ("%u", $lo); // x32, bcmath if (function_exists("bcmul")) { return $neg . bcadd(bcadd($lo, bcmul($hi, "4294967296")), $c); } // x32, no-bcmath $hi = (float)$hi; $lo = (float)$lo; $q = floor($hi/10000000.0); $r = $hi - $q*10000000.0; $m = $lo + $r*4967296.0; $mq = floor($m/10000000.0); $l = $m - $mq*10000000.0 + $c; $h = $q*4294967296.0 + $r*429.0 + $mq; $h = sprintf("%.0f", $h); $l = sprintf("%07.0f", $l); if ($h == "0") { return $neg . sprintf("%.0f", (float)$l); } return $neg . $h . $l; } /** * Store the current iterator key or false if no key is available * @var string */ protected $_current; /** * Rewind the Iterator to the first element. * Similar to the reset() function for arrays in PHP * @return void */ public function rewind() { $this->iterinit(); $this->_current = $this->iternext(); } /** * Return the current element. * Similar to the current() function for arrays in PHP * @return mixed current element from the collection */ public function current() { return $this->get($this->_current); } /** * Return the identifying key of the current element. * Similar to the key() function for arrays in PHP * @return mixed either an integer or a string */ public function key() { return $this->_current; } /** * Move forward to next element. * Similar to the next() function for arrays in PHP * @return void */ public function next() { $this->_current = $this->iternext(); } /** * Check if there is a current element after calls to rewind() or next(). * Used to check if we've iterated to the end of the collection * @return boolean FALSE if there's nothing more to iterate over */ public function valid() { return $this->_current !== false; } /** * Returns whether the key exists * @return boolean */ public function offsetExists($offset) { return $this->vsiz($offset) !== false; } /** * Returns the value associated with the key * @return mixed */ public function offsetGet($offset) { return $this->get($offset); } /** * Sets a value for the key * @return boolean True if value was set successfully */ public function offsetSet($offset, $value) { return $this->put($offset, $value); } /** * Removes the value for the key * @return void */ public function offsetUnset($offset) { $this->out($offset); } /** * Returns the number of records in the database * @return int Number of records */ public function count() { return $this->rnum(); }}
Exception.php
* * Permission is hereby granted, free of charge, to any person obtaining a copy* of this software and associated documentation files (the "Software"), to deal* in the Software without restriction, including without limitation the rights* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell* copies of the Software, and to permit persons to whom the Software is* furnished to do so, subject to the following conditions:* * The above copyright notice and this permission notice shall be included in* all copies or substantial portions of the Software.* * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN* THE SOFTWARE.* * @package Tyrant* @author Bertrand Mansion* @license http://www.opensource.org/licenses/mit-license.php MIT License* @link http://mamasam.indefero.net/p/tyrant/*//*** Base class for Exceptions in Tyrant package** @package Tyrant*/class Tyrant_Exception extends Exception { }
Query.php
** Permission is hereby granted, free of charge, to any person obtaining a copy* of this software and associated documentation files (the "Software"), to deal* in the Software without restriction, including without limitation the rights* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell* copies of the Software, and to permit persons to whom the Software is* furnished to do so, subject to the following conditions:** The above copyright notice and this permission notice shall be included in* all copies or substantial portions of the Software.** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN* THE SOFTWARE.** @package Tyrant* @author Bertrand Mansion* @license http://www.opensource.org/licenses/mit-license.php MIT License* @link http://mamasam.indefero.net/p/tyrant/*//*** Query class for the RDBTable database queries** @package Tyrant* @author Bertrand Mansion */class Tyrant_Query{ /** * Query arguments * @var array */ protected $args = array(); /** * Query condition: string is equal to */ const QCSTREQ = 0; /** * Query condition: string is included in */ const QCSTRINC = 1; /** * Query condition: string begins with */ const QCSTRBW = 2; /** * Query condition: string ends with */ const QCSTREW = 3; /** * Query condition: string includes all tokens in */ const QCSTRAND = 4; /** * Query condition: string includes at least one token in */ const QCSTROR = 5; /** * Query condition: string is equal to at least one token in */ const QCSTROREQ = 6; /** * Query condition: string matches regular expressions of */ const QCSTRRX = 7; /** * Query condition: number is equal to */ const QCNUMEQ = 8; /** * Query condition: number is greater than */ const QCNUMGT = 9; /** * Query condition: number is greater than or equal to */ const QCNUMGE = 10; /** * Query condition: number is less than */ const QCNUMLT = 11; /** * Query condition: number is less than or equal to */ const QCNUMLE = 12; /** * Query condition: number is between two tokens of */ const QCNUMBT = 13; /** * Query condition: number is equal to at least one token in */ const QCNUMOREQ = 14; /** * Query condition: full-text search with the phrase of the expression */ const QCFTSPH = 15; /** * Query condition: full-text search with all tokens in the expression */ const QCFTSAND = 16; /** * Query condition: full-text search with at least one token in the expression */ const QCFTSOR = 17; /** * Query condition: full-text search with the compound expression */ const QCFTSEX = 18; /** * Query condition: negation flag */ const QCNEGATE = 16777216; /** * Query condition: no index flag */ const QCNOIDX = 33554432; /** * Order type: string ascending */ const QOSTRASC = 0; /** * Order type: string descending */ const QOSTRDESC = 1; /** * Order type: number ascending */ const QONUMASC = 2; /** * Order type: number descending */ const QONUMDESC = 3; /** * Add a query argument for "string is equal to column" * @param string Column to query upon * @param string Value to search */ public function is($name, $expr) { $this->addCond($name, self::QCSTREQ, $expr); } /** * Add a query argument for "string is included in column" * @param string Column to query upon * @param string Value to search */ public function like($name, $expr) { $this->addCond($name, self::QCSTRINC, $expr); } /** * Add a query argument for "string includes at least one token from column" * @param string Column to query upon * @param string Value to search */ public function has($name, $expr) { $this->addCond($name, self::QCSTROR, $expr); } /** * Add a query argument for "string includes all tokens from column" * @param string Column to query upon * @param string Value to search */ public function hasAll($name, $expr) { $this->addCond($name, self::QCSTRAND, $expr); } /** * Add a query argument for "string is equal to at least one token from column" * @param string Column to query upon * @param string Value to search */ public function isOne($name, $expr) { $this->addCond($name, self::QCSTROREQ, $expr); } /** * Add a query argument for "string begins with" * @param string Column to query upon * @param string Value to search */ public function starts($name, $expr) { $this->addCond($name, self::QCSTRBW, $expr); } /** * Add a query argument for "string ends with" * @param string Column to query upon * @param string Value to search */ public function ends($name, $expr) { $this->addCond($name, self::QCSTREW, $expr); } /** * Add a query argument for "string matches regular expressions of" * @param string Column to query upon * @param string Value to search */ public function matches($name, $expr) { $this->addCond($name, self::QCSTRRX, $expr); } /** * Add a query argument for "number is equal to" * @param string Column to query upon * @param string Value to search */ public function eq($name, $expr) { $this->addCond($name, self::QCNUMEQ, $expr); } /** * Add a query argument for "number is greater than" * @param string Column to query upon * @param string Value to search */ public function gt($name, $expr) { $this->addCond($name, self::QCNUMGT, $expr); } /** * Add a query argument for "number is greater than or equal to" * @param string Column to query upon * @param string Value to search */ public function gte($name, $expr) { $this->addCond($name, self::QCNUMGE, $expr); } /** * Add a query argument for "number is less than" * @param string Column to query upon * @param string Value to search */ public function lt($name, $expr) { $this->addCond($name, self::QCNUMLT, $expr); } /** * Add a query argument for "number is less than or equal to" * @param string Column to query upon * @param string Value to search */ public function lte($name, $expr) { $this->addCond($name, self::QCNUMLE, $expr); } /** * Add a query argument for "number is between two tokens of" * @param string Column to query upon * @param string Value to search */ public function between($name, $expr) { $this->addCond($name, self::QCNUMBT, $expr); } /** * Add a query argument for "number is equal to at least one token in" * @param string Column to query upon * @param string Value to search */ public function eqOne($name, $expr) { $this->addCond($name, self::QCNUMOREQ, $expr); } /** * Add a sort parameter for the query * @param string Column to sort * @param string 'numeric' or 'literal' * @param string 'asc' or 'desc' */ public function sortBy($name, $type = 'numeric', $direction = 'asc') { if ($type != 'numeric') { $type = $direction != 'asc' ? self::QOSTRDESC : self::QOSTRASC; } else { $type = $direction != 'asc' ? self::QONUMDESC : self::QONUMASC; } $this->setOrder($name, $type); } /** * Add a narrowing condition for the query. * @param string Name of a column. An empty string means the primary key. * @param int Operation type, see class constants. * @param mixed Operand exression. */ public function addCond($name, $op, $expr) { $this->args[] = "addcond\0" . $name . "\0" . $op . "\0" . $expr; } /** * Add a sort parameter for the query * @param string Name of a column. * @param int Sort type, see class constants. */ public function setOrder($name, $type) { $this->args['order'] = "setorder\0" . $name . "\0" . $type; } /** * Limit the number of records returned by the query * @param int Maximum number of records returned * @param int Number of records to skip */ public function setLimit($max = -1, $skip = -1) { $this->args['limit'] = "setlimit\0" . $max . "\0" . $skip; } /** * Return the query arguments * @return array Query arguments */ public function args() { return $this->args; }}
RDB.php
* * Permission is hereby granted, free of charge, to any person obtaining a copy* of this software and associated documentation files (the "Software"), to deal* in the Software without restriction, including without limitation the rights* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell* copies of the Software, and to permit persons to whom the Software is* furnished to do so, subject to the following conditions:* * The above copyright notice and this permission notice shall be included in* all copies or substantial portions of the Software.* * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN* THE SOFTWARE.* * @package Tyrant* @author Bertrand Mansion* @license http://www.opensource.org/licenses/mit-license.php MIT License* @link http://mamasam.indefero.net/p/tyrant/*/require_once dirname(__FILE__).'/Common.php';/*** Hash and other types of database connection, all except the Table type** @package Tyrant* @author Bertrand Mansion */class Tyrant_RDB extends Tyrant_Common{ /** * Unconditionally set key to value * If a record with the same key exists in the database, * it is overwritten. * * @param string|int Specifies the key. * @param mixed A scalar value (or an object with __toString) * @return bool True if successful, false otherwise * @throws Tyrant_Exception */ public function put($key, $value) { $cmd = pack('CCNN', 0xC8, 0x10, strlen($key), strlen($value)) . $key . $value; $code = $this->_send($cmd); if ($code !== 0) { throw new Tyrant_Exception("Put error"); } return true; } /** * Store a new record * If a record with the same key exists in the database, * this method has no effect. * * @param string|int Specifies the key. * @param mixed A scalar value (or an object with __toString) * @return bool True if successful, false otherwise * @throws Tyrant_Exception */ public function putkeep($key, $value) { $cmd = pack('CCNN', 0xC8, 0x11, strlen($key), strlen($value)) . $key . $value; $code = $this->_send($cmd); if ($code !== 0) { throw new Tyrant_Exception("Put error"); } return true; } /** * Append value to the existing value for key, or set key to * value if it does not already exist. * * @param string|int Specifies the key. * @param mixed A scalar value (or an object with __toString) * @return bool True if successful, false otherwise * @throws Tyrant_Exception */ public function putcat($key, $value) { $cmd = pack('CCNN', 0xC8, 0x12, strlen($key), strlen($value)) . $key . $value; $code = $this->_send($cmd); if ($code !== 0) { throw new Tyrant_Exception("Put error"); } return true; } /** * Concatenate a value at the end of the existing record and * shift it to the left * * @param string|int Specifies the key. * @param mixed A scalar value (or an object with __toString) * @return bool True if successful, false otherwise * @throws Tyrant_Exception */ public function putshl($key, $value, $width) { $width = $width < 0 ? 0 : (int)$width; $cmd = pack('CCNNN', 0xC8, 0x13, strlen($key), strlen($value), $width) . $key . $value; $code = $this->_send($cmd); if ($code !== 0) { throw new Tyrant_Exception("Put error"); } return true; } /** * Set key to value without waiting for a server response * * @param string|int Specifies the key. * @param mixed A scalar value (or an object with __toString) * @return bool True if successful, false otherwise * @throws Tyrant_Exception */ public function putnr($key, $value) { $cmd = pack('CCNN', 0xC8, 0x18, strlen($key), strlen($value)) . $key . $value; $status = $this->_socketWrite($cmd); if ($status === false) { throw new Tyrant_Exception("Put error"); } return true; } /** * Get the value of a key from the server * * @param string|int Specifies the key. * @return string|null The value */ public function get($key) { $cmd = pack('CCN', 0xC8, 0x30, strlen($key)) . $key; $code = $this->_send($cmd); if ($code !== 0) { return null; } $value = $this->_recv(); return $value; } /** * Retrieve records * As a result of this method, keys existing in the database have * the corresponding values and keys not existing in the database * are removed. * * @param array Associative array containing the retrieval keys * The array is given by reference so it will be * filled with the values found. * @return int|false Number of retrieved records or false on failure. */ public function mget(&$recs) { $rnum = 0; $cmd = ""; foreach ($recs as $key => $value) { $cmd .= pack("N", strlen($key)) . $key; $rnum++; } $cmd = pack("CCN", 0xC8, 0x31, $rnum) . $cmd; $code = $this->_send($cmd); if ($code !== 0) { return false; } $rnum = $this->_recvInt32(); if ($rnum === false) { return false; } $recs = array(); for ($i = 0; $i < $rnum; $i++) { $ksiz = $this->_recvInt32(); $vsiz = $this->_recvInt32(); if ($ksiz === false || $vsiz === false) { return false; } $kref = $this->_recv($ksiz); $vref = $this->_recv($vsiz); $recs[$kref] = $vref; } return $rnum; } /** * Add an integer to a record * If the corresponding record exists, the value is treated as * an integer and is added to the existing value. If no record exists, * a new record is created with the value. * * @param string The key * @param int The additional value * @return int|false The summation value or false * @throws Tyrant_Exception */ public function addInt($key, $num = 0) { $cmd = pack("CCNN", 0xC8, 0x60, strlen($key), (int)$num) . $key; $code = $this->_send($cmd); if ($code !== 0) { throw new Tyrant_Exception("Could not addInt to ".$key); } return $this->_recvInt32(); } /** * Add a real number to a record * If the corresponding record exists, the value is treated as * a real number and is added to the existing value. If no record exists, * a new record is created with the value. * * @param string The key * @param int The additional value * @return int|false The summation value or false * @throws Tyrant_Exception */ public function addDouble($key, $num = 0) { $integ = substr($num, 0, strpos($num, '.')); $fract = (abs($num) - floor(abs($num)))*1000000000000; $cmd = pack('CCN', 0xC8, 0x61, strlen($key)) . $this->_pack64($integ) . $this->_pack64($fract) . $key; $code = $this->_send($cmd); if ($code !== 0) { throw new Tyrant_Exception("AddDouble error"); } $integ = $this->_recvint64(); $fract = $this->_recvint64(); return $integ + ($fract / 1000000000000); }}
RDBTable.php
** Permission is hereby granted, free of charge, to any person obtaining a copy* of this software and associated documentation files (the "Software"), to deal* in the Software without restriction, including without limitation the rights* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell* copies of the Software, and to permit persons to whom the Software is* furnished to do so, subject to the following conditions:** The above copyright notice and this permission notice shall be included in* all copies or substantial portions of the Software.** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN* THE SOFTWARE.** @package Tyrant* @author Bertrand Mansion* @license http://www.opensource.org/licenses/mit-license.php MIT License* @link http://mamasam.indefero.net/p/tyrant/*/require_once dirname(__FILE__).'/Common.php';require_once dirname(__FILE__).'/Query.php';/*** Table type database connection** @package Tyrant* @author Bertrand Mansion */class Tyrant_RDBTable extends Tyrant_Common{ /** * index type: lexical string */ const ITLEXICAL = 0; /** * index type: decimal string */ const ITDECIMAL = 1; /** * index type: token inverted index */ const ITTOKEN = 2; /** * index type: q-gram inverted index */ const ITQGRAM = 3; /** * index type: optimize */ const ITOPT = 9998; /** * index type: void */ const ITVOID = 9999; /** * index type: keep existing index */ const ITKEEP = 16777216; // 1 << 24 /** * Store a record * If a record with the same key exists in the database, * it is overwritten. * * @param string|int Specifies the primary key. * @param array Associative array containing key/values. * @return bool True if successful, false otherwise * @throws Tyrant_Exception */ public function put($key, $value) { $args = array($key); foreach ($value as $ckey => $cvalue) { $args[] = $ckey; $args[] = $cvalue; } $rv = $this->misc('put', $args, 0); if ($rv === false) { throw new Tyrant_Exception("Put error"); } return true; } /** * Store a new record * If a record with the same key exists in the database, * this method has no effect. * * @param string|int Specifies the primary key. * @param array Associative array containing key/values. * @return bool True if successful, false otherwise * @throws Tyrant_Exception */ public function putkeep($key, Array $values) { $args = array($key); foreach ($values as $ckey => $cvalue) { $args[] = $ckey; $args[] = $cvalue; } $rv = $this->misc('putkeep', $args, 0); if ($rv === false) { throw new Tyrant_Exception("Put error"); } return true; } /** * Concatenate columns of the existing record * If there is no corresponding record, a new record is created. * * @param string|int Specifies the primary key. * @param array Associative array containing key/values. * @return bool True if successful, false otherwise * @throws Tyrant_Exception */ public function putcat($key, Array $values) { $args = array($key); foreach ($values as $ckey => $cvalue) { $args[] = $ckey; $args[] = $cvalue; } $rv = $this->misc('putcat', $args, 0); if ($rv === false) { throw new Tyrant_Exception("Put error"); } return true; } /** * Retrieve a record * @param int|string Specifies the primary key. * @return array|false If successful, the return value is an * associative array, false if no record were found. */ public function get($key) { $args = array($key); $rv = $this->misc('get', $args, Tyrant::MONOULOG); if ($rv === false) { $rnum = $this->_recvInt32(); return null; } $cols = array(); $cnum = count($rv) - 1; $i = 0; while ($i < $cnum) { $cols[$rv[$i]] = $rv[$i+1]; $i += 2; } return $cols; } /** * Retrieve records * Due to the protocol restriction, this method can not handle records * with binary columns including the "\0" chracter. * * @param array Associative array containing the primary keys. * As a result of this method, keys existing in the * database have the corresponding columns and keys * not existing in the database are removed. * @return int|false If successful, the return value is the number of * records found. False if none found. */ public function mget(Array &$recs) { $rnum = 0; $cmd = ""; foreach ($recs as $key => $value) { $cmd .= pack("N", strlen($key)) . $key; $rnum++; } $cmd = pack("CCN", 0xC8, 0x31, $rnum) . $cmd; $code = $this->_send($cmd); if ($code !== 0) { return false; } $rnum = $this->_recvInt32(); if ($rnum === false) { return false; } $recs = array(); for ($i = 0; $i < $rnum; $i++) { $ksiz = $this->_recvInt32(); $vsiz = $this->_recvInt32(); if ($ksiz === false || $vsiz === false) { return false; } $cols = array(); $kref = $this->_recv($ksiz); $vref = $this->_recv($vsiz); $cary = explode("\0", $vref); $cnum = count($cary) - 1; $j = 0; while ($j < $cnum) { $cols[$cary[$j]] = $cary[$j+1]; $j += 2; } $recs[$kref] = $cols; } return $rnum; } /** * Set a column index * @param string Name of a column. * If the name of an existing index is specified, * the index is rebuilt. An empty string means the * primary key. * @param int Specifies the index type: * - Tyrant_RDBTable::ITLEXICAL for lexical string * - Tyrant_RDBTable::ITDECIMAL for decimal string * - Tyrant::ITOPT for optimizing the index * - tyrant_RDBTable::ITVOID for removing the index * If Tyrant_RDBTable::ITKEEP is added by bitwise-or and * the index exists, this method merely returns failure. * * @return bool True if successful * @throws Tyrant_Exception */ public function setindex($name, $type) { $args = array($name, $type); $rv = $this->misc('setindex', $args, 0); if ($rv === false) { throw new Tyrant_Exception("Could not set index on ".$name); } return true; } /** * Generate a unique ID number * @return int The new unique ID number * @throws Tyrant_Exception */ public function genuid() { $rv = $this->misc('genuid', array()); if ($rv === false) { throw new Tyrant_Exception("Could not generate a new unique ID"); } return $rv[0]; } /** * Execute a search * This method does never fail and return an empty array even * if no record corresponds. * * @param object A Tyrant_Query object * @return array Array of the primary keys of records found. */ public function search(Tyrant_Query $query) { $rv = $this->misc("search", $query->args(), Tyrant::MONOULOG); return empty($rv) ? array() : $rv; } /** * Remove each corresponding records * * @param object A Tyrant_Query object * @return bool True if successful, false otherwise */ public function searchOut(Tyrant_Query $query) { $args = $query->args(); $args[] = "out"; $rv = $this->misc("search", $args, 0); return empty($rv) ? array() : $rv; } /** * Get records corresponding to the search of a query object * The return value is an array of associative arrays with column of * the corresponding records. This method does never fail and return * an empty array even if no record corresponds. * Due to the protocol restriction, this method can not handle records * with binary columns including the "\0" chracter. * * @param object A Tyrant_Query object * @param string|array Array of column names to be fetched. * An empty string returns the primary key. * If it is left null, every column is fetched. * @return array Array of records found */ public function searchGet(Tyrant_Query $query, $names = null) { $args = $query->args(); if (is_array($names)) { $args[] = "get\0" . implode("\0", $names); } else { $args[] = "get"; } $rv = $this->misc("search", $args, Tyrant::MONOULOG); if (empty($rv)) { return array(); } foreach ($rv as $i => $v) { $cols = array(); $cary = explode("\0", $v); $cnum = count($cary) - 1; $j = 0; while ($j < $cnum) { $cols[$cary[$j]] = $cary[$j+1]; $j += 2; } $rv[$i] = $cols; } return $rv; } /** * Get the count of corresponding records * * @param object A Tyrant_Query object * @return int Count of corresponding records or 0 on failure */ public function searchCount(Tyrant_Query $query) { $args = $query->args(); $args[] = "count"; $rv = $this->misc("search", $args, Tyrant::MONOULOG); return empty($rv) ? 0 : $rv[0]; }}
与Tyrant同级目录的文件
Tyrant.php
* * Permission is hereby granted, free of charge, to any person obtaining a copy* of this software and associated documentation files (the "Software"), to deal* in the Software without restriction, including without limitation the rights* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell* copies of the Software, and to permit persons to whom the Software is* furnished to do so, subject to the following conditions:* * The above copyright notice and this permission notice shall be included in* all copies or substantial portions of the Software.* * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN* THE SOFTWARE.* * @package Tyrant* @author Bertrand Mansion* @license http://www.opensource.org/licenses/mit-license.php MIT License* @link http://mamasam.indefero.net/p/tyrant/*/require_once dirname(__FILE__).'/Tyrant/Exception.php';/*** Factory for Tokyo Tyrant connections** @package Tyrant* @author Bertrand Mansion */class Tyrant{ /** * Scripting extension option for record locking */ const XOLCKREC = 1; /** * scripting extension option for global locking */ const XOLCKGLB = 2; /** * Misc function option for omission of the update log */ const MONOULOG = 1; /** * Restore function option for consistency checking */ const ROCHKCON = 1; /** * Keeps track of the connection objects * Makes it possible to easily reuse a connection. * @var array */ protected static $connections = array(); /** * Current connection object * @var object */ protected static $connection; /** * Opens a connection to a Tokyo Tyrant server * * try { * $tt = Tyrant::connect('localhost', 1978); * } catch (Tyrant_Exception $e) { * echo $e->getMessage(); * } *
* * @param string Server hostname or IP address * @param string Server port * @param string Optional existing connection id * @return object Database connection */ public static function connect($host = 'localhost', $port = '1978', $id = 0) { $id = implode(':', array($host, $port, $id)); // Check if connection already exists if (isset(self::$connections[$id])) { $connection =& self::$connections[$id]; return $connection; } // Start a new connection $ip = gethostbyname($host); $addr = ip2long($ip); if (empty($addr)) { throw new Tyrant_Exception("Host not found"); } $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if (!is_resource($socket)){ throw new Tyrant_Exception("Connection refused"); } if (!socket_connect($socket, $addr, $port)) { throw new Tyrant_Exception("Connection refused"); } if (defined('TCP_NODELAY')) { socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1); } else { // See http://bugs.php.com/bug.php?id=46360 socket_set_option($socket, SOL_TCP, 1, 1); } // Determine the database type if (socket_write($socket, pack('CC', 0xC8, 0x88)) === false) { throw new Tyrant_Exception("Unable to get database type"); } $str = ''; if (socket_recv($socket, $str, 1, 0) === false) { throw new Tyrant_Exception("Unable to get database type"); } $c = unpack("C", $str); if (!isset($c[1]) || $c[1] !== 0) { throw new Tyrant_Exception("Unable to get database type"); } $str = ''; if (socket_recv($socket, $str, 4, 0) === false) { throw new Tyrant_Exception("Unable to get database type"); } $num = unpack("N", $str); if (!isset($num[1])) { throw new Tyrant_Exception("Unable to get database type"); } $size = unpack("l", pack("l", $num[1])); $len = $size[1]; $str = ''; if (socket_recv($socket, $str, $len, 0) === false) { throw new Tyrant_Exception("Unable to get database type"); } $value = explode("\n", trim($str)); $stats = array(); foreach ($value as $v) { $v = explode("\t", $v); $stats[$v[0]] = $v[1]; } if (!isset($stats['type'])) { throw new Tyrant_Exception("Unable to get database type"); } // Get the right interface for the database type if ($stats['type'] == 'table') { include_once dirname(__FILE__).'/Tyrant/RDBTable.php'; $conn = new Tyrant_RDBTable($socket); } else { include_once dirname(__FILE__).'/Tyrant/RDB.php'; $conn = new Tyrant_RDB($socket); } self::$connections[$id] =& $conn; self::$connection = $conn; return $conn; } /** * Return the current connection * The current connection is set using Tyrant::setConnection() and * defaults to the last connection made * * @return object|null First connection in the stack */ public function getConnection() { return self::$connection; } /** * Changes the current connection * @param string Server hostname or IP address * @param string Server port * @param string Optional existing connection id * @return object|null First connection in the stack */ public function setConnection($host = 'localhost', $port = '1978', $id = 0) { $id = implode(':', array($host, $port, $id)); self::$connection =& self::$connections[$id]; } /** * Disconnects and removes a connection * @param string Server hostname or IP address * @param string Server port * @param string Optional existing connection id */ public function disconnect($host = 'localhost', $port = '1978', $id = 0) { $id = implode(':', array($host, $port, $id)); if (isset(self::$connections[$id])) { $connection =& self::$connections[$id]; $connection->disconnect(); unset(self::$connections[$id]); return true; } return false; }}
cache的使用类
cache_ttserver.php
_conn = Tyrant::connect($host , $port); $this->_query = new Tyrant_Query; } /** * 写入一行记录 */ public function set($key , $value = null) { $args_num = func_num_args(); if(is_array($key) && $args_num == 1) { $values = $key; foreach($values AS $_key => $_val) { $this->_conn->put($_key , $_val); } } else { return $this->_conn->put($key , $value); } return $this; } /** * 读取一行记录 */ public function get($key) { if(is_array($key)) { $keys = $key; $value = array(); foreach($keys AS $_key) { $serialized_value = $this->_conn->get($_key); $value[$_key] = $serialized_value; } } else { $value = $this->_conn->get($key); } return $value; } /** * 删除一行记录 */ public function remove($key) { return $this->_conn->out($key); } /** * 删除所有记录 */ public function removeAll() { return $this->_conn->vanish(); } /** * 使用条件:TC HASH数据库 * 写入整形记录。若key存在,则更新记录,否则插入一条记录。 */ public function add($key , $increment) { return $this->_conn->addInt($key , $increment); } /** * 使用条件:TC TABLE数据库 * 获取一个连接对象遍历数据库中的所有键/值。 */ public function getIterator() { return $this->_conn; } /** * 获取记录数 */ public function rnum() { return $this->_conn->rnum(); } /** * 使用条件:TC TABLE数据库 * 检索记录集,返回key */ public function search() { return $this->_conn->search($this->_query); } /** * 使用条件:TC TABLE数据库 * 删除检索匹配的记录集 */ public function searchOut() { return $this->_conn->searchOut($this->_query); } /** * 使用条件:TC TABLE数据库 * 检索匹配的记录集,返回记录数组 */ public function searchGet($names = null) { return $this->_conn->searchGet($this->_query, $names); } /** * 使用条件:TC TABLE数据库 * 统计检索匹配的记录集个数 */ public function searchCount() { return $this->_conn->searchCount($this->_query); }}