TeraWurflDatabase
[ class tree: TeraWurflDatabase ] [ index: TeraWurflDatabase ] [ all elements ]

Source for file TeraWurflDatabase_MySQL5.php

Documentation is available at TeraWurflDatabase_MySQL5.php

  1. <?php
  2. /**
  3.  * Tera_WURFL - PHP MySQL driven WURFL
  4.  *
  5.  * Tera-WURFL was written by Steve Kamerman, and is based on the
  6.  * Java WURFL Evolution package by Luca Passani and WURFL PHP Tools by Andrea Trassati.
  7.  * This version uses a MySQL database to store the entire WURFL file, multiple patch
  8.  * files, and a persistent caching mechanism to provide extreme performance increases.
  9.  *
  10.  * @package TeraWurflDatabase
  11.  * @author Steve Kamerman <stevekamerman AT gmail.com>
  12.  * @version Stable 2.1.3 $Date: 2010/09/18 15:43:21
  13.  * @license http://www.mozilla.org/MPL/ MPL Vesion 1.1
  14.  */
  15. /**
  16.  * Provides connectivity from Tera-WURFL to MySQL 5
  17.  * @package TeraWurflDatabase
  18.  * @see TeraWurflDatabase
  19.  * @see TeraWurflDatabase_MySQL5_NestedSet
  20.  * @see TeraWurflDatabase_MySQL5_Profiling
  21.  */
  22.  
  23.     // Properties
  24.     public $errors;
  25.     public $db_implements_ris = true;
  26.     public $db_implements_ld = false;
  27.     public $db_implements_fallback = true;
  28.     public $use_nested_set = false;
  29.     public $numQueries = 0;
  30.     public $connected = false;
  31.  
  32.     protected $dbcon;
  33.     protected $hostPrefix = '';
  34.  
  35.     public $maxquerysize = 0;
  36.     /**
  37.      * The maximum number of new rows that the database can handle in one INSERT statement
  38.      * @var int 
  39.      */
  40.     protected static $DB_MAX_INSERTS 500;
  41.     protected static $WURFL_ID_COLUMN_TYPE "VARCHAR";
  42.     protected static $WURFL_ID_MAX_LENGTH 64;
  43.     protected static $STORAGE_ENGINE "MyISAM";
  44.     // To use InnoDB for this setting, you need to remove DELAYED from the cache query
  45.     protected static $CACHE_STORAGE_ENGINE "MyISAM";
  46.     protected static $PERSISTENT_CONNECTION true;
  47.  
  48.     public function __construct(){
  49.         if(version_compare(PHP_VERSION,'5.3.0','>='&& self::$PERSISTENT_CONNECTION){
  50.             $this->hostPrefix = 'p:';
  51.         }
  52.         parent::__construct();
  53.     }
  54.     /**
  55.      * Destructor, disconnect from database
  56.      */
  57.     public function __destruct(){
  58.         @$this->dbcon->close();
  59.     }
  60.  
  61.     // Device Table Functions (device,hybrid,patch)
  62.     public function getDeviceFromID($wurflID){
  63.         $this->numQueries++;
  64.         $res $this->dbcon->query("SELECT * FROM `".TeraWurflConfig::$TABLE_PREFIX.'Merge'."` WHERE `deviceID`=".$this->SQLPrep($wurflID)) or die($this->dbcon->error);
  65.         if($res->num_rows == 0){
  66.             $res->close();
  67.             throw new Exception("Tried to lookup an invalid WURFL Device ID: $wurflID");
  68.         }
  69.         $data $res->fetch_assoc();
  70.         $res->close();
  71.         return unserialize($data['capabilities']);
  72.     }
  73.     public function getActualDeviceAncestor($wurflID){
  74.         if($wurflID == "" || $wurflID == WurflConstants::$GENERIC)
  75.         return WurflConstants::$GENERIC;
  76.         $device $this->getDeviceFromID($wurflID);
  77.         if($device['actual_device_root']){
  78.             return $device['id'];
  79.         }else{
  80.             return $this->getActualDeviceAncestor($device['fall_back']);
  81.         }
  82.     }
  83.     public function getFullDeviceList($tablename){
  84.         $this->numQueries++;
  85.         $res $this->dbcon->query("SELECT `deviceID`, `user_agent` FROM `$tablename` WHERE `match`=1");
  86.         if($res->num_rows == 0){
  87.             $res->close();
  88.             return array();
  89.         }
  90.         $data array();
  91.         while($row $res->fetch_assoc()){
  92.             $data[$row['deviceID']]=$row['user_agent'];
  93.         }
  94.         return $data;
  95.     }
  96.     // Exact Match
  97.     public function getDeviceFromUA($userAgent){
  98.         $this->numQueries++;
  99.         $query "SELECT `deviceID` FROM `".TeraWurflConfig::$TABLE_PREFIX.'Merge'."` WHERE `user_agent`=".$this->SQLPrep($userAgent);
  100.         $res $this->dbcon->query($query);
  101.         if($res->num_rows == 0){
  102.             $res->close();
  103.             return false;
  104.         }
  105.         $data $res->fetch_assoc();
  106.         $res->close();
  107.         return $data['deviceID'];
  108.     }
  109.     // RIS == Reduction in String (reduce string one char at a time)
  110.     public function getDeviceFromUA_RIS($userAgent,$tolerance,UserAgentMatcher &$matcher){
  111.         $this->numQueries++;
  112.         $query sprintf("CALL ".TeraWurflConfig::$TABLE_PREFIX."_RIS(%s,%s,%s)",$this->SQLPrep($userAgent),$tolerance,$this->SQLPrep($matcher->tableSuffix()));
  113.         $res $this->dbcon->query($query);
  114.         if(!$res){
  115.             throw new Exception(sprintf("Error in DB RIS Query: %s. \nQuery: %s\n",$this->dbcon->error,$query));
  116.             exit();
  117.         }
  118.         $data $res->fetch_assoc();
  119.         $this->cleanConnection();
  120.         $wurflid $data['DeviceID'];
  121.         return ($wurflid == 'NULL' || is_null($wurflid))WurflConstants::$GENERIC$wurflid;
  122.     }
  123.     // TODO: Implement with Stored Proc
  124.     // LD == Levesthein Distance
  125.     public function getDeviceFromUA_LD($userAgent,$tolerance,UserAgentMatcher &$matcher){
  126.         throw new Exception("Error: this function (LD) is not yet implemented in MySQL");
  127.         $safe_ua $this->SQLPrep($userAgent);
  128.         $this->numQueries++;
  129.         //$res = $this->dbcon->query("call ".TeraWurflConfig::$TABLE_PREFIX."_LD($safe_ua,$tolerance)");
  130.         // TODO: check for false
  131.         $data array();
  132.         while($row $res->fetch_assoc()){
  133.             $data[]=$row;
  134.         }
  135.         $this->cleanConnection();
  136.         return $data;
  137.     }
  138.     public function getDeviceFallBackTree($wurflID){
  139.         if($this->use_nested_set){
  140.             return $this->getDeviceFallBackTree_NS($wurflID);
  141.         }
  142.         $data array();
  143.         $this->numQueries++;
  144.         $query sprintf("CALL ".TeraWurflConfig::$TABLE_PREFIX."_FallBackDevices(%s)",$this->SQLPrep($wurflID));
  145.         $this->dbcon->multi_query($query);
  146.         $i 0;
  147.         do{
  148.             if($res $this->dbcon->store_result()){
  149.                 $row $res->fetch_row();
  150.                 $data[$i++]=unserialize($row[0]);
  151.                 $res->free();
  152.             }
  153.         }while($this->dbcon->more_results(&& $this->dbcon->next_result());
  154.         if($data[$i-1]['id'!= WurflConstants::$GENERIC){
  155.             $tw new TeraWurfl();
  156.             $tw->toLog("WURFL Error: device {$data[$i-1]['id']} falls back on an inexistent device: {$data[$i-1]['fall_back']}",LOG_ERR,__CLASS__.'::'.__FUNCTION__);
  157.         }
  158.         return $data;
  159.     }
  160.     /**
  161.      * Returns an Array containing the complete capabilities array for each
  162.      * device in the fallback tree.  These arrays would need to be flattened
  163.      * in order to be used for any real puropse
  164.      * @param $wurflID 
  165.      * @return array array of the capabilities arrays for all the devices in the fallback tree
  166.      */
  167.     public function getDeviceFallBackTree_NS($wurflID){
  168.         $data array();
  169.         $this->numQueries++;
  170.         $query sprintf("SELECT `data`.capabilities FROM %s AS node, %s AS parent
  171. INNER JOIN %s `data` ON parent.deviceID = `data`.deviceID
  172. WHERE node.`lt` BETWEEN parent.`lt` AND parent.`rt`
  173. AND node.deviceID = %s
  174. ORDER BY parent.`rt`",
  175.         TeraWurflConfig::$TABLE_PREFIX.'Index',
  176.         TeraWurflConfig::$TABLE_PREFIX.'Index',
  177.         TeraWurflConfig::$TABLE_PREFIX.'Merge',
  178.         $this->SQLPrep($wurflID)
  179.         );
  180.         $res $this->dbcon->query($query);
  181.         while($row $res->fetch_assoc()){
  182.             $data[]=unserialize($row['capabilities']);
  183.         }
  184.         return $data;
  185.     }
  186.     protected function cleanConnection(){
  187.         while($this->dbcon->more_results()){
  188.             $this->dbcon->next_result();
  189.             $res $this->dbcon->use_result();
  190.             if ($res instanceof mysqli_result){$res->free();}
  191.         }
  192.     }
  193.     public function loadDevices(&$tables){
  194.         $insert_errors array();
  195.         $insertcache array();
  196.         $insertedrows 0;
  197.         $this->createIndexTable();
  198.         $this->createSettingsTable();
  199.         $this->clearMatcherTables();
  200.         $this->createProcedures();
  201.         foreach($tables as $table => $devices){
  202.             // insert records into a new temp table until we know everything is OK
  203.             $temptable $table (self::$DB_TEMP_EXT);
  204.             $parts explode('_',$table);
  205.             $matcher array_pop($parts);
  206.             $this->createGenericDeviceTable($temptable);
  207.             foreach($devices as $device){
  208.                 $this->dbcon->query("INSERT INTO `".TeraWurflConfig::$TABLE_PREFIX.'Index'."` (`deviceID`,`matcher`) VALUE (".$this->SQLPrep($device['id']).",".$this->SQLPrep($matcher).")");
  209.                 // convert device root to tinyint format (0|1) for db
  210.                 if(strlen($device['user_agent']255){
  211.                     $insert_errors["Warning: user agent too long: \"".($device['id']).'"';
  212.                 }
  213.                 $insertcache[sprintf("(%s,%s,%s,%s,%s,%s)",
  214.                     $this->SQLPrep($device['id']),
  215.                     $this->SQLPrep($device['user_agent']),
  216.                     $this->SQLPrep($device['fall_back']),
  217.                     $this->SQLPrep((isset($device['actual_device_root']))?$device['actual_device_root']:''),
  218.                     preg_match('/^DO_NOT_MATCH/',$device['user_agent'])01,
  219.                     $this->SQLPrep(serialize($device))
  220.                 );
  221.                 // This batch of records is ready to be inserted
  222.                 if(count($insertcache>= self::$DB_MAX_INSERTS){
  223.                     $query "INSERT INTO `$temptable` (`deviceID`, `user_agent`, `fall_back`, `actual_device_root`, `match`, `capabilities`) VALUES ".implode(",",$insertcache);
  224.                     $this->dbcon->query($queryor $insert_errors["DB server reported error on id \"".$device['id']."\": ".$this->dbcon->error;
  225.                     $insertedrows += $this->dbcon->affected_rows;
  226.                     $insertcache array();
  227.                     $this->numQueries++;
  228.                     $this->maxquerysize = (strlen($query)>$this->maxquerysize)strlen($query)$this->maxquerysize;
  229.                 }
  230.             }
  231.             // some records are probably left in the insertcache
  232.             if(count($insertcache0){
  233.                 $query "INSERT INTO `$temptable` (`deviceID`, `user_agent`, `fall_back`, `actual_device_root`, `match`, `capabilities`) VALUES ".implode(",",$insertcache);
  234.                 $this->dbcon->query($queryor $insert_errors["DB server reported error on id \"".$device['id']."\": ".$this->dbcon->error;
  235.                 $insertedrows += $this->dbcon->affected_rows;
  236.                 $insertcache array();
  237.                 $this->numQueries++;
  238.                 $this->maxquerysize = (strlen($query)>$this->maxquerysize)strlen($query)$this->maxquerysize;
  239.             }
  240.             if(count($insert_errors0){
  241.                 // Roll back changes
  242.                 // leave the temp table in the DB for manual inspection
  243.                 $this->errors = array_merge($this->errors,$insert_errors);
  244.                 return false;
  245.             }
  246.             $this->numQueries++;
  247.             $this->dbcon->query("DROP TABLE IF EXISTS `$table`");
  248.             $this->numQueries++;
  249.             $this->dbcon->query("RENAME TABLE `$temptable` TO `$table`");
  250.         }
  251.         // Create Merge Table
  252.         $this->createMergeTable(array_keys($tables));
  253.         if($this->use_nested_set){
  254.             require_once realpath(dirname(__FILE__).'/TeraWurflMySQLNestedSet.php');
  255.             $nest new TeraWurflMySQLNestedSet($this->dbcon,'TeraWurflMerge','TeraWurflIndex','deviceID','fall_back','lt','rt');
  256.             $nest->generateNestedSet('generic');
  257.             $this->numQueries += $nest->numQueries;
  258.             unset($nest);
  259.         }
  260.         return true;
  261.     }
  262.     /**
  263.      * Drops and creates the given device table
  264.      *
  265.      * @param string Table name (ex: TeraWurflConfig::$HYBRID)
  266.      * @return boolean success
  267.      */
  268.     public function createGenericDeviceTable($tablename){
  269.         $droptable "DROP TABLE IF EXISTS ".$tablename;
  270.         $createtable "CREATE TABLE `".$tablename."` (
  271.             `deviceID` ".self::$WURFL_ID_COLUMN_TYPE."(".self::$WURFL_ID_MAX_LENGTH.") binary NOT NULL default '',
  272.             `user_agent` varchar(255) binary default NULL,
  273.             `fall_back` ".self::$WURFL_ID_COLUMN_TYPE."(".self::$WURFL_ID_MAX_LENGTH.") default NULL,
  274.             `actual_device_root` tinyint(1) default '0',
  275.             `match` tinyint(1) default '1',
  276.             `capabilities` mediumtext,
  277.             PRIMARY KEY  (`deviceID`),
  278.             KEY `fallback` (`fall_back`),
  279.             KEY `useragent` (`user_agent`),
  280.             KEY `dev_root` (`actual_device_root`),
  281.             KEY `idxmatch` (`match`)
  282.             ) ENGINE=".self::$STORAGE_ENGINE;
  283.         $this->numQueries++;
  284.         $this->dbcon->query($droptable);
  285.         $this->numQueries++;
  286.         $this->dbcon->query($createtable);
  287.         return true;
  288.     }
  289.     /**
  290.      * Drops then creates all the UserAgentMatcher device tables
  291.      * @return boolean success
  292.      */
  293.     protected function clearMatcherTables(){
  294.         foreach(UserAgentFactory::$matchers as $matcher){
  295.             $table TeraWurflConfig::$TABLE_PREFIX."_".$matcher;
  296.             $this->createGenericDeviceTable($table);
  297.         }
  298.         return true;
  299.     }
  300.     /**
  301.      * Drops and creates the MERGE table
  302.      *
  303.      * @param array Table names
  304.      * @return boolean success
  305.      */
  306.     public function createMergeTable($tables){
  307.         $tablename TeraWurflConfig::$TABLE_PREFIX.'Merge';
  308.         foreach($tables as &$table){$table="SELECT * FROM `$table`";}
  309.         $droptable "DROP TABLE IF EXISTS ".$tablename;
  310.         $this->createGenericDeviceTable($tablename);
  311.         $createtable "INSERT INTO `$tablename".implode(" UNION ALL ",$tables);
  312.         $this->numQueries++;
  313.         $this->dbcon->query($createtableor die("ERROR: ".$this->dbcon->error);
  314.         return true;
  315.     }
  316.     /**
  317.      * Drops and creates the index table
  318.      *
  319.      * @return boolean success
  320.      */
  321.     public function createIndexTable(){
  322.         $tablename TeraWurflConfig::$TABLE_PREFIX.'Index';
  323.         $droptable "DROP TABLE IF EXISTS ".$tablename;
  324.         $createtable "CREATE TABLE `".$tablename."` (
  325.   `deviceID` ".self::$WURFL_ID_COLUMN_TYPE."(".self::$WURFL_ID_MAX_LENGTH.") binary NOT NULL default '',
  326.   `matcher` varchar(64) NOT NULL,
  327.   PRIMARY KEY  (`deviceID`)
  328. ) ENGINE=".self::$STORAGE_ENGINE;
  329.         $this->numQueries++;
  330.         $this->dbcon->query($droptable);
  331.         $this->numQueries++;
  332.         $this->dbcon->query($createtable);
  333.         return true;
  334.     }
  335.     /**
  336.      * Creates the settings table if it does not already exist
  337.      * @return boolean success
  338.      */
  339.     public function createSettingsTable(){
  340.         $tablename TeraWurflConfig::$TABLE_PREFIX.'Settings';
  341.         $checktable "SHOW TABLES LIKE '$tablename'";
  342.         $this->numQueries++;
  343.         $res $this->dbcon->query($checktable);
  344.         if($res->num_rows 0return true;
  345.         $createtable "CREATE TABLE `".$tablename."` (
  346.   `id` varchar(64) NOT NULL,
  347.   `value` varchar(255) DEFAULT NULL,
  348.   PRIMARY KEY (`id`)
  349. ) ENGINE=".self::$STORAGE_ENGINE;
  350.         $this->numQueries++;
  351.         $this->dbcon->query($createtable);
  352.         return true;
  353.     }
  354.     // Cache Table Functions
  355.  
  356.     // should return (bool)false or the device array
  357.     public function getDeviceFromCache($userAgent){
  358.         $tablename TeraWurflConfig::$TABLE_PREFIX.'Cache';
  359.         $this->numQueries++;
  360.         $res $this->dbcon->query("SELECT * FROM `$tablename` WHERE `user_agent`=".$this->SQLPrep($userAgent)) or die("Error: ".$this->dbcon->error);
  361.         if($res->num_rows == 0){
  362.             $res->close();
  363.             //echo "[[UA NOT FOUND IN CACHE: $userAgent]]";
  364.             return false;
  365.         }
  366.         $data $res->fetch_assoc();
  367.         $res->close();
  368.         return unserialize($data['cache_data']);
  369.  
  370.     }
  371.     public function saveDeviceInCache($userAgent,$device){
  372.         if(strlen($userAgent)==0return true;
  373.         $tablename TeraWurflConfig::$TABLE_PREFIX.'Cache';
  374.         $ua $this->SQLPrep($userAgent);
  375.         $packed_device $this->SQLPrep(serialize($device));
  376.         $this->numQueries++;
  377.         $this->dbcon->query("INSERT DELAYED INTO `$tablename` (`user_agent`,`cache_data`) VALUES ($ua,$packed_device)")or die("Error: ".$this->dbcon->error);
  378.         if($this->dbcon->affected_rows 0){
  379.             return true;
  380.         }
  381.         return false;
  382.     }
  383.     public function createCacheTable(){
  384.         $tablename TeraWurflConfig::$TABLE_PREFIX.'Cache';
  385.         $droptable "DROP TABLE IF EXISTS `$tablename`";
  386.         $createtable "CREATE TABLE `$tablename` (
  387.             `user_agent` varchar(255) binary NOT NULL default '',
  388.             `cache_data` mediumtext NOT NULL,
  389.             PRIMARY KEY  (`user_agent`)
  390.         ) ENGINE=".self::$CACHE_STORAGE_ENGINE;
  391.         $this->numQueries++;
  392.         $this->dbcon->query($droptable);
  393.         $this->numQueries++;
  394.         $this->dbcon->query($createtable);
  395.         return true;
  396.     }
  397.     public function createTempCacheTable(){
  398.         $tablename TeraWurflConfig::$TABLE_PREFIX.'Cache'.self::$DB_TEMP_EXT;
  399.         $droptable "DROP TABLE IF EXISTS `$tablename`";
  400.         $createtable "CREATE TABLE `$tablename` (
  401.             `user_agent` varchar(255) binary NOT NULL default '',
  402.             `cache_data` mediumtext NOT NULL,
  403.             PRIMARY KEY  (`user_agent`)
  404.         ) ENGINE=".self::$CACHE_STORAGE_ENGINE;
  405.         $this->numQueries++;
  406.         $this->dbcon->query($droptable);
  407.         $this->numQueries++;
  408.         $this->dbcon->query($createtable);
  409.         return true;
  410.     }
  411.     public function rebuildCacheTable(){
  412.         // We'll use this instance to rebuild the cache and to facilitate logging
  413.         $rebuilder new TeraWurfl();
  414.         $cachetable TeraWurflConfig::$TABLE_PREFIX.'Cache';
  415.         $temptable TeraWurflConfig::$TABLE_PREFIX.'Cache'.self::$DB_TEMP_EXT;
  416.         $checkcachequery "SHOW TABLES LIKE '$cachetable'";
  417.         $checkres $this->dbcon->query($checkcachequery);
  418.         $this->numQueries++;
  419.         if($checkres->num_rows === 0){
  420.             // This can only happen if the table doesn't exist
  421.             $this->createCacheTable();
  422.             $this->numQueries++;
  423.             // This table must be empty, so we're finished
  424.             //            $rebuilder->toLog($query,LOG_ERR,"rebuildCacheTable");
  425.             $rebuilder->toLog("Created empty cache table",LOG_NOTICE,"rebuildCacheTable");
  426.             return true;
  427.         }
  428.         $droptemptable "DROP TABLE IF EXISTS `$temptable`";
  429.         $this->numQueries++;
  430.         $this->dbcon->query($droptemptable);
  431.         $query "RENAME TABLE `$cachetable` TO `$temptable`";
  432.         $this->numQueries++;
  433.         $this->dbcon->query($query);
  434.         $this->createCacheTable();
  435.         $query "SELECT `user_agent` FROM `$temptable`";
  436.         $this->numQueries++;
  437.         $res $this->dbcon->query($query);
  438.         if($res->num_rows == 0){
  439.             // No records in cache table == nothing to rebuild
  440.             $rebuilder->toLog("Rebuilt cache table, existing table was empty - this is very unusual.",LOG_WARNING,"rebuildCacheTable");
  441.             return true;
  442.         }
  443.         while($dev $res->fetch_assoc()){
  444.             // Just looking the device up will force it to be cached
  445.             $rebuilder->GetDeviceCapabilitiesFromAgent($dev['user_agent']);
  446.             // Reset the number of queries since we're not going to re-instantiate the object
  447.             $this->numQueries += $rebuilder->db->numQueries;
  448.             $rebuilder->db->numQueries 0;
  449.         }
  450.         $droptable "DROP TABLE IF EXISTS `$temptable`";
  451.         $this->numQueries++;
  452.         $this->dbcon->query($droptable);
  453.         $rebuilder->toLog("Rebuilt cache table.",LOG_NOTICE,"rebuildCacheTable");
  454.         return true;
  455.     }
  456.     // Supporting DB Functions
  457.  
  458.     // truncate or drop+create given table
  459.     public function clearTable($tablename){
  460.         if($tablename == TeraWurflConfig::$TABLE_PREFIX.'Cache'){
  461.             $this->createCacheTable();
  462.         }else{
  463.             $this->createGenericDeviceTable($tablename);
  464.         }
  465.     }
  466.     public function createProcedures(){
  467.         $TeraWurfl_RIS "CREATE PROCEDURE `".TeraWurflConfig::$TABLE_PREFIX."_RIS`(IN ua VARCHAR(255), IN tolerance INT, IN matcher VARCHAR(64))
  468. BEGIN
  469. DECLARE curlen INT;
  470. DECLARE wurflid ".self::$WURFL_ID_COLUMN_TYPE."(".self::$WURFL_ID_MAX_LENGTH.") DEFAULT NULL;
  471. DECLARE curua VARCHAR(255);
  472.  
  473. SELECT CHAR_LENGTH(ua)  INTO curlen;
  474. findua: WHILE ( curlen >= tolerance ) DO
  475.     SELECT CONCAT(LEFT(ua, curlen ),'%') INTO curua;
  476.     SELECT idx.DeviceID INTO wurflid
  477.         FROM ".TeraWurflConfig::$TABLE_PREFIX.'Index'." idx INNER JOIN ".TeraWurflConfig::$TABLE_PREFIX.'Merge'." mrg ON idx.DeviceID = mrg.DeviceID
  478.         WHERE mrg.match = 1 AND idx.matcher = matcher
  479.         AND mrg.user_agent LIKE curua
  480.         LIMIT 1;
  481.     IF wurflid IS NOT NULL THEN
  482.         LEAVE findua;
  483.     END IF;
  484.     SELECT curlen - 1 INTO curlen;
  485. END WHILE;
  486.  
  487. SELECT wurflid as DeviceID;
  488. END";
  489.         $this->dbcon->query("DROP PROCEDURE IF EXISTS `".TeraWurflConfig::$TABLE_PREFIX."_RIS`");
  490.         $this->dbcon->query($TeraWurfl_RIS);
  491.         $TeraWurfl_FallBackDevices "CREATE PROCEDURE `".TeraWurflConfig::$TABLE_PREFIX."_FallBackDevices`(current_fall_back ".self::$WURFL_ID_COLUMN_TYPE."(".self::$WURFL_ID_MAX_LENGTH."))
  492. BEGIN
  493. WHILE current_fall_back != 'root' DO
  494.     SELECT capabilities FROM ".TeraWurflConfig::$TABLE_PREFIX.'Merge'." WHERE deviceID = current_fall_back;
  495.     SELECT fall_back FROM ".TeraWurflConfig::$TABLE_PREFIX.'Merge'." WHERE deviceID = current_fall_back INTO current_fall_back;
  496. END WHILE;
  497. END";
  498.         $this->dbcon->query("DROP PROCEDURE IF EXISTS `".TeraWurflConfig::$TABLE_PREFIX."_FallBackDevices`");
  499.         $this->dbcon->query($TeraWurfl_FallBackDevices);
  500.         return true;
  501.     }
  502.     /**
  503.      * Establishes connection to database (does not check for DB sanity)
  504.      */
  505.     public function connect(){
  506.         $this->numQueries++;
  507.         if(strpos(TeraWurflConfig::$DB_HOST,':')){
  508.             list($host,$portexplode(':',TeraWurflConfig::$DB_HOST,2);
  509.             if(is_numeric($port)){
  510.                 $this->dbcon = @new mysqli($this->hostPrefix.$host,TeraWurflConfig::$DB_USER,TeraWurflConfig::$DB_PASS,TeraWurflConfig::$DB_SCHEMA,$port);
  511.             }else{
  512.                 // $port contains the socket / named pipe
  513.                 $this->dbcon = @new mysqli($this->hostPrefix.$host,TeraWurflConfig::$DB_USER,TeraWurflConfig::$DB_PASS,TeraWurflConfig::$DB_SCHEMA,null,$port);
  514.             }
  515.         }else{
  516.             $this->dbcon = @new mysqli($this->hostPrefix.TeraWurflConfig::$DB_HOST,TeraWurflConfig::$DB_USER,TeraWurflConfig::$DB_PASS,TeraWurflConfig::$DB_SCHEMA);
  517.         }
  518.         if(mysqli_connect_errno()){
  519.             $this->errors[]=mysqli_connect_error();
  520.             $this->connected = mysqli_connect_errno();
  521.             return false;
  522.         }
  523.         $this->connected = true;
  524.         return true;
  525.     }
  526.     public function updateSetting($key,$value){
  527.         $tablename TeraWurflConfig::$TABLE_PREFIX.'Settings';
  528.         $query sprintf("REPLACE INTO `%s` (`%s`, `%s`) VALUES (%s, %s)"$tablename'id''value'$this->SQLPrep($key)$this->SQLPrep($value));
  529.         $this->numQueries++;
  530.         $this->dbcon->query($query);
  531.     }
  532.     public function getSetting($key){
  533.         $query "SELECT `value` FROM `".TeraWurflConfig::$TABLE_PREFIX.'Settings'."` WHERE `id` = ".$this->SQLPrep($key);
  534.         $this->numQueries++;
  535.         $res $this->dbcon->query($query);
  536.         if($res->num_rows == 0return null;
  537.         $row $res->fetch_assoc();
  538.         return $row['value'];
  539.     }
  540.     // prep raw text for use in queries (adding quotes if necessary)
  541.     public function SQLPrep($value){
  542.         if($value == ''$value 'NULL';
  543.         else if (!is_numeric($value|| $value[0== '0'$value "'" $this->dbcon->real_escape_string($value"'"//Quote if not integer
  544.         return $value;
  545.     }
  546.     public function getTableList(){
  547.         $tablesres $this->dbcon->query("SHOW TABLES");
  548.         $tables array();
  549.         while($table $tablesres->fetch_row())$tables[]=$table[0];
  550.         $tablesres->close();
  551.         return $tables;
  552.     }
  553.     public function getMatcherTableList(){
  554.         $tablesres $this->dbcon->query("SHOW TABLES LIKE '".TeraWurflConfig::$TABLE_PREFIX."\\_%'");
  555.         $tables array();
  556.         while($table $tablesres->fetch_row())$tables[]=$table[0];
  557.         $tablesres->close();
  558.         return $tables;
  559.     }
  560.     public function getTableStats($table){
  561.         $stats array();
  562.         $fields array();
  563.         $fieldnames array();
  564.         $fieldsres $this->dbcon->query("SHOW COLUMNS FROM ".$table);
  565.         while($row $fieldsres->fetch_assoc()){
  566.             $fields['CHAR_LENGTH(`'.$row['Field'].'`)';
  567.             $fieldnames[]=$row['Field'];
  568.         }
  569.         $fieldsres->close();
  570.         $bytesizequery "SUM(".implode('+',$fields).") AS `bytesize`";
  571.         $query "SELECT COUNT(*) AS `rowcount`, $bytesizequery FROM `$table`";
  572.         $res $this->dbcon->query($query);
  573.         $rows $res->fetch_assoc();
  574.         $stats['rows'$rows['rowcount'];
  575.         $stats['bytesize'$rows['bytesize'];
  576.         $res->close();
  577.         if(in_array("actual_device_root",$fieldnames)){
  578.             $res $this->dbcon->query("SELECT COUNT(*) AS `devcount` FROM `$table` WHERE actual_device_root=1");
  579.             $row $res->fetch_assoc();
  580.             $stats['actual_devices'$row['devcount'];
  581.             $res->close();
  582.         }
  583.         return $stats;
  584.     }
  585.     public function getCachedUserAgents(){
  586.         $uas array();
  587.         $cacheres $this->dbcon->query("SELECT user_agent FROM ".TeraWurflConfig::$TABLE_PREFIX.'Cache'." ORDER BY user_agent");
  588.         while($ua $cacheres->fetch_row())$uas[]=$ua[0];
  589.         $cacheres->close();
  590.         return $uas;
  591.     }
  592.     public function verifyConfig(){
  593.         $errors array();
  594.         $createProc "CREATE PROCEDURE `".TeraWurflConfig::$TABLE_PREFIX."_TestProc`()
  595. BEGIN
  596.     SELECT 1;
  597. END";
  598.         $testProc "CALL ".TeraWurflConfig::$TABLE_PREFIX."_TestProc";
  599.         $this->dbcon->query($createProc);
  600.         $res $this->dbcon->query($testProc);
  601.         if(!$res || $res->num_rows 1){
  602.             $errors["Could not create MySQL Procedure. Please make sure you have these privileges: CREATE_ROUTINE, DROP, EXECUTE";
  603.         }
  604.         $this->cleanConnection();
  605.         $this->dbcon->query("DROP PROCEDURE IF EXISTS `".TeraWurflConfig::$TABLE_PREFIX."_TestProc`");
  606.         return $errors;
  607.     }
  608.     public function getServerVersion(){
  609.         $res $this->dbcon->query("SELECT version() AS `version`");
  610.         if(!$res || $res->num_rows == 0return false;
  611.         $row $res->fetch_assoc();
  612.         return($row['version']);
  613.     }
  614. }

Documentation generated on Sun, 19 Sep 2010 00:16:01 +0000 by phpDocumentor 1.4.3