1<?php 2///////////////////////////////////////////////////////////////// 3/// getID3() by James Heinrich <info@getid3.org> // 4// available at https://github.com/JamesHeinrich/getID3 // 5// or https://www.getid3.org // 6// or http://getid3.sourceforge.net // 7// // 8// extension.cache.mysqli.php - part of getID3() // 9// Please see readme.txt for more information // 10// // 11///////////////////////////////////////////////////////////////// 12// // 13// extension.cache.sqlite3.php - part of getID3() // 14// Please see readme.txt for more information // 15// // 16///////////////////////////////////////////////////////////////// 17/// // 18// MySQL extension written by Allan Hansen <ahØartemis*dk> // 19// Table name mod by Carlo Capocasa <calroØcarlocapocasa*com> // 20// MySQL extension was reworked for SQLite3 by // 21// Karl G. Holz <newaeonØmac*com> // 22// /// 23///////////////////////////////////////////////////////////////// 24 25/** 26* This is a caching extension for getID3(). It works the exact same 27* way as the getID3 class, but return cached information much faster 28* 29* Normal getID3 usage (example): 30* 31* require_once 'getid3/getid3.php'; 32* $getID3 = new getID3; 33* $getID3->encoding = 'UTF-8'; 34* $info1 = $getID3->analyze('file1.flac'); 35* $info2 = $getID3->analyze('file2.wv'); 36* 37* getID3_cached usage: 38* 39* require_once 'getid3/getid3.php'; 40* require_once 'getid3/extension.cache.sqlite3.php'; 41* // all parameters are optional, defaults are: 42* $getID3 = new getID3_cached_sqlite3($table='getid3_cache', $hide=FALSE); 43* $getID3->encoding = 'UTF-8'; 44* $info1 = $getID3->analyze('file1.flac'); 45* $info2 = $getID3->analyze('file2.wv'); 46* 47* 48* Supported Cache Types (this extension) 49* 50* SQL Databases: 51* 52* cache_type cache_options 53* ------------------------------------------------------------------- 54* mysql host, database, username, password 55* 56* sqlite3 table='getid3_cache', hide=false (PHP5) 57* 58* 59* *** database file will be stored in the same directory as this script, 60* *** webserver must have write access to that directory! 61* *** set $hide to TRUE to prefix db file with .ht to pervent access from web client 62* *** this is a default setting in the Apache configuration: 63* 64* The following lines prevent .htaccess and .htpasswd files from being viewed by Web clients. 65* 66* <Files ~ "^\.ht"> 67* Order allow,deny 68* Deny from all 69* Satisfy all 70* </Files> 71* 72******************************************************************************** 73* 74* ------------------------------------------------------------------- 75* DBM-Style Databases: (use extension.cache.dbm) 76* 77* cache_type cache_options 78* ------------------------------------------------------------------- 79* gdbm dbm_filename, lock_filename 80* ndbm dbm_filename, lock_filename 81* db2 dbm_filename, lock_filename 82* db3 dbm_filename, lock_filename 83* db4 dbm_filename, lock_filename (PHP5 required) 84* 85* PHP must have write access to both dbm_filename and lock_filename. 86* 87* Recommended Cache Types 88* 89* Infrequent updates, many reads any DBM 90* Frequent updates mysql 91******************************************************************************** 92* 93* IMHO this is still a bit slow, I'm using this with MP4/MOV/ M4v files 94* there is a plan to add directory scanning and analyzing to make things work much faster 95* 96* 97*/ 98class getID3_cached_sqlite3 extends getID3 99{ 100 /** 101 * hold the sqlite db 102 * 103 * @var SQLite3 Resource 104 */ 105 private $db; 106 107 /** 108 * table to use for caching 109 * 110 * @var string $table 111 */ 112 private $table; 113 114 /** 115 * @param string $table holds name of sqlite table 116 * @param boolean $hide 117 * 118 * @throws getid3_exception 119 * @throws Exception 120 */ 121 public function __construct($table='getid3_cache', $hide=false) { 122 // Check for SQLite3 support 123 if (!function_exists('sqlite_open')) { 124 throw new Exception('PHP not compiled with SQLite3 support.'); 125 } 126 127 $this->table = $table; // Set table 128 $file = dirname(__FILE__).'/'.basename(__FILE__, 'php').'sqlite'; 129 if ($hide) { 130 $file = dirname(__FILE__).'/.ht.'.basename(__FILE__, 'php').'sqlite'; 131 } 132 $this->db = new SQLite3($file); 133 $db = $this->db; 134 $this->create_table(); // Create cache table if not exists 135 $version = ''; 136 $sql = $this->getQuery('version_check'); 137 $stmt = $db->prepare($sql); 138 $stmt->bindValue(':filename', getID3::VERSION, SQLITE3_TEXT); 139 $result = $stmt->execute(); 140 list($version) = $result->fetchArray(); 141 if ($version != getID3::VERSION) { // Check version number and clear cache if changed 142 $this->clear_cache(); 143 } 144 parent::__construct(); 145 } 146 147 /** 148 * close the database connection 149 */ 150 public function __destruct() { 151 $db=$this->db; 152 $db->close(); 153 } 154 155 /** 156 * clear the cache 157 * 158 * @return SQLite3Result 159 */ 160 private function clear_cache() { 161 $db = $this->db; 162 $sql = $this->getQuery('delete_cache'); 163 $db->exec($sql); 164 $sql = $this->getQuery('set_version'); 165 $stmt = $db->prepare($sql); 166 $stmt->bindValue(':filename', getID3::VERSION, SQLITE3_TEXT); 167 $stmt->bindValue(':dirname', getID3::VERSION, SQLITE3_TEXT); 168 $stmt->bindValue(':val', getID3::VERSION, SQLITE3_TEXT); 169 return $stmt->execute(); 170 } 171 172 /** 173 * analyze file and cache them, if cached pull from the db 174 * 175 * @param string $filename 176 * @param integer $filesize 177 * @param string $original_filename 178 * @param resource $fp 179 * 180 * @return mixed|false 181 */ 182 public function analyze($filename, $filesize=null, $original_filename='', $fp=null) { 183 if (!file_exists($filename)) { 184 return false; 185 } 186 // items to track for caching 187 $filetime = filemtime($filename); 188 $filesize_real = filesize($filename); 189 // this will be saved for a quick directory lookup of analized files 190 // ... why do 50 seperate sql quries when you can do 1 for the same result 191 $dirname = dirname($filename); 192 // Lookup file 193 $db = $this->db; 194 $sql = $this->getQuery('get_id3_data'); 195 $stmt = $db->prepare($sql); 196 $stmt->bindValue(':filename', $filename, SQLITE3_TEXT); 197 $stmt->bindValue(':filesize', $filesize_real, SQLITE3_INTEGER); 198 $stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER); 199 $res = $stmt->execute(); 200 list($result) = $res->fetchArray(); 201 if (count($result) > 0 ) { 202 return unserialize(base64_decode($result)); 203 } 204 // if it hasn't been analyzed before, then do it now 205 $analysis = parent::analyze($filename, $filesize, $original_filename, $fp); 206 // Save result 207 $sql = $this->getQuery('cache_file'); 208 $stmt = $db->prepare($sql); 209 $stmt->bindValue(':filename', $filename, SQLITE3_TEXT); 210 $stmt->bindValue(':dirname', $dirname, SQLITE3_TEXT); 211 $stmt->bindValue(':filesize', $filesize_real, SQLITE3_INTEGER); 212 $stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER); 213 $stmt->bindValue(':atime', time(), SQLITE3_INTEGER); 214 $stmt->bindValue(':val', base64_encode(serialize($analysis)), SQLITE3_TEXT); 215 $res = $stmt->execute(); 216 return $analysis; 217 } 218 219 /** 220 * create data base table 221 * this is almost the same as MySQL, with the exception of the dirname being added 222 * 223 * @return bool 224 */ 225 private function create_table() { 226 $db = $this->db; 227 $sql = $this->getQuery('make_table'); 228 return $db->exec($sql); 229 } 230 231 /** 232 * get cached directory 233 * 234 * This function is not in the MySQL extention, it's ment to speed up requesting multiple files 235 * which is ideal for podcasting, playlists, etc. 236 * 237 * @param string $dir directory to search the cache database for 238 * 239 * @return array return an array of matching id3 data 240 */ 241 public function get_cached_dir($dir) { 242 $db = $this->db; 243 $rows = array(); 244 $sql = $this->getQuery('get_cached_dir'); 245 $stmt = $db->prepare($sql); 246 $stmt->bindValue(':dirname', $dir, SQLITE3_TEXT); 247 $res = $stmt->execute(); 248 while ($row=$res->fetchArray()) { 249 $rows[] = unserialize(base64_decode($row)); 250 } 251 return $rows; 252 } 253 254 /** 255 * returns NULL if query is not found 256 * 257 * @param string $name 258 * 259 * @return null|string 260 */ 261 public function getQuery($name) 262 { 263 switch ($name) { 264 case 'version_check': 265 return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = '-1' AND filetime = '-1' AND analyzetime = '-1'"; 266 case 'delete_cache': 267 return "DELETE FROM $this->table"; 268 case 'set_version': 269 return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, -1, -1, -1, :val)"; 270 case 'get_id3_data': 271 return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = :filesize AND filetime = :filetime"; 272 case 'cache_file': 273 return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, :filesize, :filetime, :atime, :val)"; 274 case 'make_table': 275 return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) DEFAULT '', dirname VARCHAR(255) DEFAULT '', filesize INT(11) DEFAULT '0', filetime INT(11) DEFAULT '0', analyzetime INT(11) DEFAULT '0', val text, PRIMARY KEY (filename, filesize, filetime))"; 276 case 'get_cached_dir': 277 return "SELECT val FROM $this->table WHERE dirname = :dirname"; 278 default: 279 return null; 280 } 281 } 282 283 /** 284 * use the magical __get() for sql queries 285 * 286 * access as easy as $this->{case name}, returns NULL if query is not found 287 * 288 * @param string $name 289 * 290 * @return string 291 * @deprecated use getQuery() instead 292 */ 293 public function __get($name) { 294 return $this->getQuery($name); 295 } 296 297} 298