1<?php 2/** 3 * Zend Framework 4 * 5 * LICENSE 6 * 7 * This source file is subject to the new BSD license that is bundled 8 * with this package in the file LICENSE.txt. 9 * It is also available through the world-wide-web at this URL: 10 * http://framework.zend.com/license/new-bsd 11 * If you did not receive a copy of the license and are unable to 12 * obtain it through the world-wide-web, please send an email 13 * to license@zend.com so we can send you a copy immediately. 14 * 15 * @category Zend 16 * @package Zend_Db 17 * @subpackage Table 18 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 19 * @license http://framework.zend.com/license/new-bsd New BSD License 20 * @version $Id$ 21 */ 22 23/** 24 * @category Zend 25 * @package Zend_Db 26 * @subpackage Table 27 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 28 * @license http://framework.zend.com/license/new-bsd New BSD License 29 */ 30abstract class Zend_Db_Table_Rowset_Abstract implements SeekableIterator, Countable, ArrayAccess 31{ 32 /** 33 * The original data for each row. 34 * 35 * @var array 36 */ 37 protected $_data = array(); 38 39 /** 40 * Zend_Db_Table_Abstract object. 41 * 42 * @var Zend_Db_Table_Abstract 43 */ 44 protected $_table; 45 46 /** 47 * Connected is true if we have a reference to a live 48 * Zend_Db_Table_Abstract object. 49 * This is false after the Rowset has been deserialized. 50 * 51 * @var boolean 52 */ 53 protected $_connected = true; 54 55 /** 56 * Zend_Db_Table_Abstract class name. 57 * 58 * @var string 59 */ 60 protected $_tableClass; 61 62 /** 63 * Zend_Db_Table_Row_Abstract class name. 64 * 65 * @var string 66 */ 67 protected $_rowClass = 'Zend_Db_Table_Row'; 68 69 /** 70 * Iterator pointer. 71 * 72 * @var integer 73 */ 74 protected $_pointer = 0; 75 76 /** 77 * How many data rows there are. 78 * 79 * @var integer 80 */ 81 protected $_count; 82 83 /** 84 * Collection of instantiated Zend_Db_Table_Row objects. 85 * 86 * @var array 87 */ 88 protected $_rows = array(); 89 90 /** 91 * @var boolean 92 */ 93 protected $_stored = false; 94 95 /** 96 * @var boolean 97 */ 98 protected $_readOnly = false; 99 100 /** 101 * Constructor. 102 * 103 * @param array $config 104 */ 105 public function __construct(array $config) 106 { 107 if (isset($config['table'])) { 108 $this->_table = $config['table']; 109 $this->_tableClass = get_class($this->_table); 110 } 111 if (isset($config['rowClass'])) { 112 $this->_rowClass = $config['rowClass']; 113 } 114 if (!class_exists($this->_rowClass)) { 115 Zend_Loader::loadClass($this->_rowClass); 116 } 117 if (isset($config['data'])) { 118 $this->_data = $config['data']; 119 } 120 if (isset($config['readOnly'])) { 121 $this->_readOnly = $config['readOnly']; 122 } 123 if (isset($config['stored'])) { 124 $this->_stored = $config['stored']; 125 } 126 127 // set the count of rows 128 $this->_count = count($this->_data); 129 130 $this->init(); 131 } 132 133 /** 134 * Store data, class names, and state in serialized object 135 * 136 * @return array 137 */ 138 public function __sleep() 139 { 140 return array('_data', '_tableClass', '_rowClass', '_pointer', '_count', '_rows', '_stored', 141 '_readOnly'); 142 } 143 144 /** 145 * Setup to do on wakeup. 146 * A de-serialized Rowset should not be assumed to have access to a live 147 * database connection, so set _connected = false. 148 * 149 * @return void 150 */ 151 public function __wakeup() 152 { 153 $this->_connected = false; 154 } 155 156 /** 157 * Initialize object 158 * 159 * Called from {@link __construct()} as final step of object instantiation. 160 * 161 * @return void 162 */ 163 public function init() 164 { 165 } 166 167 /** 168 * Return the connected state of the rowset. 169 * 170 * @return boolean 171 */ 172 public function isConnected() 173 { 174 return $this->_connected; 175 } 176 177 /** 178 * Returns the table object, or null if this is disconnected rowset 179 * 180 * @return Zend_Db_Table_Abstract 181 */ 182 public function getTable() 183 { 184 return $this->_table; 185 } 186 187 /** 188 * Set the table object, to re-establish a live connection 189 * to the database for a Rowset that has been de-serialized. 190 * 191 * @param Zend_Db_Table_Abstract $table 192 * @return boolean 193 * @throws Zend_Db_Table_Row_Exception 194 */ 195 public function setTable(Zend_Db_Table_Abstract $table) 196 { 197 $this->_table = $table; 198 $this->_connected = false; 199 // @todo This works only if we have iterated through 200 // the result set once to instantiate the rows. 201 foreach ($this as $row) { 202 $connected = $row->setTable($table); 203 if ($connected == true) { 204 $this->_connected = true; 205 } 206 } 207 $this->rewind(); 208 return $this->_connected; 209 } 210 211 /** 212 * Query the class name of the Table object for which this 213 * Rowset was created. 214 * 215 * @return string 216 */ 217 public function getTableClass() 218 { 219 return $this->_tableClass; 220 } 221 222 /** 223 * Rewind the Iterator to the first element. 224 * Similar to the reset() function for arrays in PHP. 225 * Required by interface Iterator. 226 * 227 * @return Zend_Db_Table_Rowset_Abstract Fluent interface. 228 */ 229 public function rewind() 230 { 231 $this->_pointer = 0; 232 return $this; 233 } 234 235 /** 236 * Return the current element. 237 * Similar to the current() function for arrays in PHP 238 * Required by interface Iterator. 239 * 240 * @return Zend_Db_Table_Row_Abstract current element from the collection 241 */ 242 public function current() 243 { 244 if ($this->valid() === false) { 245 return null; 246 } 247 248 // return the row object 249 return $this->_loadAndReturnRow($this->_pointer); 250 } 251 252 /** 253 * Return the identifying key of the current element. 254 * Similar to the key() function for arrays in PHP. 255 * Required by interface Iterator. 256 * 257 * @return int 258 */ 259 public function key() 260 { 261 return $this->_pointer; 262 } 263 264 /** 265 * Move forward to next element. 266 * Similar to the next() function for arrays in PHP. 267 * Required by interface Iterator. 268 * 269 * @return void 270 */ 271 public function next() 272 { 273 ++$this->_pointer; 274 } 275 276 /** 277 * Check if there is a current element after calls to rewind() or next(). 278 * Used to check if we've iterated to the end of the collection. 279 * Required by interface Iterator. 280 * 281 * @return bool False if there's nothing more to iterate over 282 */ 283 public function valid() 284 { 285 return $this->_pointer >= 0 && $this->_pointer < $this->_count; 286 } 287 288 /** 289 * Returns the number of elements in the collection. 290 * 291 * Implements Countable::count() 292 * 293 * @return int 294 */ 295 public function count() 296 { 297 return $this->_count; 298 } 299 300 /** 301 * Take the Iterator to position $position 302 * Required by interface SeekableIterator. 303 * 304 * @param int $position the position to seek to 305 * @return Zend_Db_Table_Rowset_Abstract 306 * @throws Zend_Db_Table_Rowset_Exception 307 */ 308 public function seek($position) 309 { 310 $position = (int) $position; 311 if ($position < 0 || $position >= $this->_count) { 312 throw new Zend_Db_Table_Rowset_Exception("Illegal index $position"); 313 } 314 $this->_pointer = $position; 315 return $this; 316 } 317 318 /** 319 * Check if an offset exists 320 * Required by the ArrayAccess implementation 321 * 322 * @param string $offset 323 * @return boolean 324 */ 325 public function offsetExists($offset) 326 { 327 return isset($this->_data[(int) $offset]); 328 } 329 330 /** 331 * Get the row for the given offset 332 * Required by the ArrayAccess implementation 333 * 334 * @param string $offset 335 * @return Zend_Db_Table_Row_Abstract 336 */ 337 public function offsetGet($offset) 338 { 339 $offset = (int) $offset; 340 if ($offset < 0 || $offset >= $this->_count) { 341 throw new Zend_Db_Table_Rowset_Exception("Illegal index $offset"); 342 } 343 $this->_pointer = $offset; 344 345 return $this->current(); 346 } 347 348 /** 349 * Does nothing 350 * Required by the ArrayAccess implementation 351 * 352 * @param string $offset 353 * @param mixed $value 354 */ 355 public function offsetSet($offset, $value) 356 { 357 } 358 359 /** 360 * Does nothing 361 * Required by the ArrayAccess implementation 362 * 363 * @param string $offset 364 */ 365 public function offsetUnset($offset) 366 { 367 } 368 369 /** 370 * Returns a Zend_Db_Table_Row from a known position into the Iterator 371 * 372 * @param int $position the position of the row expected 373 * @param bool $seek wether or not seek the iterator to that position after 374 * @return Zend_Db_Table_Row 375 * @throws Zend_Db_Table_Rowset_Exception 376 */ 377 public function getRow($position, $seek = false) 378 { 379 try { 380 $row = $this->_loadAndReturnRow($position); 381 } catch (Zend_Db_Table_Rowset_Exception $e) { 382 throw new Zend_Db_Table_Rowset_Exception('No row could be found at position ' . (int) $position, 0, $e); 383 } 384 385 if ($seek == true) { 386 $this->seek($position); 387 } 388 389 return $row; 390 } 391 392 /** 393 * Returns all data as an array. 394 * 395 * Updates the $_data property with current row object values. 396 * 397 * @return array 398 */ 399 public function toArray() 400 { 401 // @todo This works only if we have iterated through 402 // the result set once to instantiate the rows. 403 foreach ($this->_rows as $i => $row) { 404 $this->_data[$i] = $row->toArray(); 405 } 406 return $this->_data; 407 } 408 409 protected function _loadAndReturnRow($position) 410 { 411 if (!isset($this->_data[$position])) { 412 throw new Zend_Db_Table_Rowset_Exception("Data for provided position does not exist"); 413 } 414 415 // do we already have a row object for this position? 416 if (empty($this->_rows[$position])) { 417 $this->_rows[$position] = new $this->_rowClass( 418 array( 419 'table' => $this->_table, 420 'data' => $this->_data[$position], 421 'stored' => $this->_stored, 422 'readOnly' => $this->_readOnly 423 ) 424 ); 425 426 if ( $this->_table instanceof Zend_Db_Table_Abstract ) { 427 $info = $this->_table->info(); 428 429 if ( $this->_rows[$position] instanceof Zend_Db_Table_Row_Abstract ) { 430 if ($info['cols'] == array_keys($this->_data[$position])) { 431 $this->_rows[$position]->setTable($this->getTable()); 432 } 433 } 434 } else { 435 $this->_rows[$position]->setTable(null); 436 } 437 } 438 439 // return the row object 440 return $this->_rows[$position]; 441 } 442 443} 444