[READ-ONLY] Mirror of https://github.com/andrioid/travian.scrolls.org. Retired project. I got a bit overly engaged in an online game called Travian.
0

Configure Feed

Select the types of activity you want to include in your feed.

travian.scrolls.org / php / systemList.class.php
9.3 kB 332 lines
1<?php 2 3function fromCache($key) { 4 if (function_exists('apc_fetch') and ($result = @apc_fetch($key))) { 5 return $result; 6 } 7 return false; 8} 9 10function toCache($key, $data, $ttl=-1) { 11 global $Config; 12 if (!function_exists('apc_store')) { return false; } 13 if ($ttl == -1) { $ttl = 60*60*12; } 14 if (!@apc_store($key, $data, $ttl)) { return false; } 15 return true; 16} 17 18function arraytolist ($array) { 19 $list = ''; 20 if (!is_array($array)) { return false; } 21 $count = count($array); 22 foreach ($array as $i=>$item) { 23 $list .= $item; 24 if ($i != $count-1) { $list .= ","; } 25 } 26 return $list; 27} 28 29function listtoarray ($list) { 30 $strip = array(' '); 31 $list = str_replace($strip, '', $list); 32 $return = explode(',', $list); 33 return $return; 34} 35 36 37 38// A list parent class that should help with paging and maybe rss/json feeds 39class SystemList { 40 public $list = array(); // our object list 41 public $q = ''; // This is the base query (can be overwritten) 42 private $cache = true; 43 private $conditions = array(); 44 private $page = 0; // defaults 45 private $perPage = 0; // defaults 46 private $order = null; // asc/desc (no defaults, skipped if null) 47 private $orderBy = null; // field name (no defaults, skipped if null) 48 private $groupBy = null; 49 private $fields = array(); 50 private $fieldTypes = ''; 51 private $identifier = null; // if null, we'll use the md5 of the sql query (used for caching) 52 public $total = 0; // Total result (SQL_COUNT_ROWS) 53 public $count = 0; // Current list count 54 public $cacheTime = 0; // Cache time in seconds 55 56 public function __construct() { 57 global $Config; 58 $this->cacheTime = $Config['cacheTime']; 59 } 60 61 // We will handle WHERE and LIMIT for the query here 62 public function populate () { 63 $where = ''; 64 $order = ''; 65 $paging = ''; 66 $paraType = ''; 67 $query = ''; 68 $this->list = array(); // We must clear it 69 $para = array(); 70 if (empty($this->q)) { throw new Exception("Cannot populate a list without a query"); } 71 72 73 // Conditions into "where" 74 $where = $this->processConditions(); 75 76 // Paging 77 if ($this->page > 0) { 78 $from = (int)($this->page-1) * (int)$this->perPage; 79 } else { 80 $from = 0; 81 } 82 if ($this->perPage > 0) { // Skip limit if not defined 83 $paging = sprintf("LIMIT %d,%d", $from, $this->perPage); 84 } 85 86 // Order 87 if ($this->orderBy != null) { 88 $order = sprintf("ORDER BY %s", $this->orderBy); 89 if ($this->order != null) { 90 $order .= " ".$this->order; 91 } 92 } 93 94 // Group By 95 if ($this->groupBy != null) { 96 $groupby = sprintf("GROUP BY %s", $this->groupBy); 97 } else { $groupby = ''; } 98 99 // Create the new query 100 $query = sprintf("%s %s %s %s %s", $this->q, $where, $groupby, $order, $paging); 101 //printf("Query: %s<br>", htmlspecialchars($query)); 102 103 // Caching 104 if ($this->identifier != null) { 105 $cacheName = $this->identifier; 106 } else { 107 $cacheName = md5($query.$this->fieldTypes.arraytolist($this->fields)); 108 } 109 110 if ($this->cache == true) { 111 list($this->list, $this->count, $this->total) = fromCache($cacheName); 112 if (is_array($this->list) and count($this->list) > 0) { 113 return true; 114 } 115 } 116 //printf("Using database for: %s <br>", $this->q); 117 $db = Database::getInstance(); 118 119 120 // Prepare the query 121 if (!$stmt = $db->mysql->prepare($query)) { 122 throw new Exception("Could not prepare statement: ".$db->mysql->error); 123 } 124 125 // Do some binding to the statement 126 // - This is some voodoo shit, we have to dynamically handle all parameters and output variables 127 // - Read comments on: http://www.php.net/manual/en/mysqli-stmt.bind-result.php 128 if (strlen($this->fieldTypes) > 0) { 129 call_user_func_array('mysqli_stmt_bind_param', array_merge(array($stmt, &$this->fieldTypes), $this->fields)); 130 } 131 //echo "<pre>"; print_r($stmt); echo "</pre>"; 132 if (!$stmt->execute()) { throw new Exception("Failed to execute: ".$stmt->error); } 133 134 $result = mysqli_stmt_result_metadata($stmt); 135 $data = array(); // Store the results in 136 $args = array(); 137 138 while ($field = mysqli_fetch_field($result)) { 139 $name = $field->name; 140 $args[$name] =& $data[$field->name]; 141 } 142 if (!@call_user_func_array(array(&$stmt, "bind_result"), $args)) { 143 printf("SystemList: Name conflict in query, please fix."); 144 exit(0); 145 } 146 147 148 while ($stmt->fetch()) { 149 $this->processRow($args); 150 } 151 // Update count 152 $this->count = count($this->list); 153 // Update total count 154 if (!$result = $db->mysql->query("SELECT FOUND_ROWS()")) { throw new SystemException("List failed to update total"); } 155 list ($this->total) = $result->fetch_row(); 156 157 if (!toCache($cacheName,array($this->list, $this->count, $this->total),$this->cacheTime)) { 158 throw new Exception("Failed to store cache."); 159 } 160 161 if ($this->list == null) { // In case the processing method does something weird 162 $this->list = array(); 163 } 164 165 166 $stmt->close(); 167 168 } 169 protected function processRow() { 170 // This should be replaced with your own object method 171 // - this prototype simply converts to stdclass and adds to list 172 /* Important note: 173 * - Each row is stored in $data as a reference. When the next row is called, those values change. 174 * - We need to copy the values before exiting this function 175 */ 176 $args = func_get_args(); 177 $out = array(); 178 $numarg = count($args); 179 // de-referencing the array (damn this is ugly) 180 foreach ($args[0] as $key=>$value) { 181 $out[$key] = $value; 182 } 183 184 $this->list[] = (object)$out; 185 //echo "<pre>"; print_r($this->list); echo "</pre>"; 186 return true; 187 } 188 189 190 public function setCache($cache) { 191 if (is_bool($cache)) { 192 $this->cache = $cache; 193 return true; 194 } 195 if (is_numeric($cache)) { 196 if ($cache == 0) { 197 $this->cache = false; 198 return true; 199 } else if ($cache == 1) { 200 $this->cache = true; 201 return true; 202 } 203 } 204 return false; 205 } 206 207 // Offer lists to name their caching value (so they can clear it) 208 public function setIdentifier($identifier) { 209 $this->identifier = $identifier; 210 } 211 212 public function addCondition ($field, $value, $sign='=', $op='AND', $opjoin=false, $internal=false) { 213 // Example: ('username', 'moocow', '=', 'AND', false) 214 // - opjoin dictates if the value should be put into previous parenthesis or left seperate. 215 // - the $op is ignored for the first value (as it doesn't matter) 216 if (is_array($value)) { 217 foreach ($value as $v) { 218 $c = new stdClass(); 219 $c->field = $field; 220 $c->value = $v; 221 $c->sign = $sign; 222 $c->op = $op; 223 $c->opjoin = $opjoin; 224 $c->internal = $internal; 225 $this->conditions[] = $c; 226 //printf("Array value %s: %s<br>\n", $field, $v); 227 } 228 } else { 229 $c = new stdClass(); 230 $c->field = $field; 231 $c->value = $value; 232 $c->sign = $sign; 233 $c->op = $op; 234 $c->opjoin = $opjoin; 235 $c->internal = $internal; 236 $this->conditions[] = $c; 237 } 238 } 239 240 public function processConditions() { 241 $p =& $this->conditions; 242 $i = 0; 243 $num = count($p); 244 $where = ''; 245 $this->fieldTypes = ''; // We must reinitialize 246 $this->fields = array(); // We must reinitialize 247 if ($num > 0) { $where = 'WHERE '; } 248 foreach ($p as $key=>$v) { 249 if ($i != 0 && $v->opjoin == false) { $where .= ') '; } // Close any lingering parenthesis 250 if ($i != 0) { $where .= sprintf(" %s ", strtoupper($v->op)); } 251 if ($i == 0 || $v->opjoin == false) { $where .= '('; } // Initial condition, open with parenthesis 252 if ($v->internal == false) { 253 $where .= sprintf("%s %s ?", $v->field, $v->sign); 254 // Fix binding 255 if (is_double($v->value)) { 256 $this->fieldTypes .= 'd'; 257 } elseif (is_int($v->value)) { 258 $this->fieldTypes .= 'i'; 259 } else { 260 $this->fieldTypes .= 's'; 261 } 262 $this->fields[] = &$v->value; 263 } else { 264 $where .= sprintf("%s %s %s", $v->field, $v->sign, $v->value); 265 } 266 if ($i+1 >= $num) { $where .= ")"; } // Last line, close anything remaining 267 268 // Increment 269 $i++; 270 } 271 272 // Debug 273 //printf("<h3>Debug: %s</h3>\n", $this->fieldTypes); 274 //echo "<pre>"; print_r($this->fields); echo "</pre>"; 275 276 return $where; 277 } 278 279 public function setPaging ($page, $perPage=20) { 280 $this->page = (int)$page; 281 $this->perPage = (int)$perPage; 282 return true; 283 } 284 285 public function setOrder ($orderBy, $first='asc') { 286 if (strtolower($first) == 'asc') { 287 $this->order = 'ASC'; 288 } else { 289 $this->order = 'DESC'; 290 } 291 $this->orderBy = $orderBy; 292 return true; 293 } 294 295 public function setQuery ($query) { 296 $query = preg_replace('/^SELECT/i', 'SELECT SQL_CALC_FOUND_ROWS', $query); 297 $this->q = $query; 298 } 299 300 public function setNavigation ($nav) { // Allows us to set paging and order with the nav variable 301 $this->page = (int)$nav['page']; 302 $this->perPage = (int)$nav['number']; 303 $this->orderBy = $nav['sort']; 304 $this->order = $nav['order']; 305 } 306 307 public function random($num=null) { 308 shuffle($this->list); 309 $count = count($this->list); 310 if ($num != null) { 311 if ($count<$num) { $num=$count; } 312 $return = array(); 313 for ($i=0; $i<$num; $i++) { 314 $return[] = $this->list[$i]; 315 } 316 $this->list = $return; 317 } 318 } 319 320 public function showConditions() { 321 echo "<pre>"; print_r($this->conditions); echo "</pre>"; 322 } 323 324 public function setGroupBy ($gb) { 325 $this->groupBy = $gb; 326 } 327 328 public function setCacheTime ($time) { 329 $this->cacheTime =$time; 330 } 331} 332?>