Location: PHPKode > projects > ArangoDB-PHP > ArangoDB-PHP-1.1.0/lib/triagens/ArangoDb/Cursor.php
<?php

/**
 * ArangoDB PHP client: result set cursor
 * 
 * @package ArangoDbPhpClient
 * @author Jan Steemann
 * @copyright Copyright 2012, triagens GmbH, Cologne, Germany
 */

namespace triagens\ArangoDb;

/**
 * Provides access to the results of a read-only statement
 * 
 * The cursor might not contain all results in the beginning.
 * 
 * If the result set is too big to be transferred in one go, the
 * cursor might issue additional HTTP requests to fetch the
 * remaining results from the server.
 *
 * @package ArangoDbPhpClient
 */
class Cursor implements \Iterator {
  /**
   * The connection object
   * @var Connection
   */
  private $_connection;

  /**
   * Cursor options
   * @var array
   */
  private $_options;
  
  /**
   * The result set
   * @var array
   */
  private $_result;
  
  /**
   * "has more" indicator - if true, the server has more results
   * @var bool
   */
  private $_hasMore;
  
  /**
   * cursor id - might be NULL if cursor does not have an id
   * @var mixed
   */
  private $_id;

  /**
   * current position in result set iteration (zero-based)
   * @var int
   */
  private $_position;
  
  /**
   * total length of result set (in number of documents)
   * @var int
   */
  private $_length;


  /**
   * result entry for cursor id
   */
  const ENTRY_ID        = 'id';
  
  /**
   * result entry for "hasMore" flag
   */
  const ENTRY_HASMORE   = 'hasMore';
  
  /**
   * result entry for result documents
   */
  const ENTRY_RESULT    = 'result';
  
  /**
   * sanitize option entry
   */
  const ENTRY_SANITIZE  = 'sanitize';

  /**
   * Initialise the cursor with the first results and some metadata
   *
   * @param Connection $connection - connection to be used
   * @param array $data - initial result data as returned by the server
   * @param array $options - cursor options
   * @return void 
   */
  public function __construct(Connection $connection, array $data, array $options) {
    $this->_connection = $connection;

    $this->_id = NULL;
    if (isset($data[self::ENTRY_ID])) {
      $this->_id = $data[self::ENTRY_ID];
    }

    // attribute must be there
    assert(isset($data[self::ENTRY_HASMORE]));
    $this->_hasMore = (bool) $data[self::ENTRY_HASMORE];

    $this->_options = $options;
    $this->_result = array();
    $this->addDocumentsFromArray((array) $data[self::ENTRY_RESULT], $options);
    $this->updateLength();

    $this->rewind();
  }
  
  /**
   * Explicitly delete the cursor
   * 
   * This might issue an HTTP DELETE request to inform the server about
   * the deletion.
   *
   * @throws Exception
   * @return bool - true if the server acknowledged the deletion request, false otherwise
   */
  public function delete() {
    if ($this->_id) {
      try {
        $this->_connection->delete(Urls::URL_CURSOR . '/' . $this->_id);
        return true;
      } 
      catch (Exception $e) {
      }
    }

    return false;
  }
  
  /**
   * Get the total number of results in the cursor
   * 
   * This might issue additional HTTP requests to fetch any outstanding
   * results from the server
   *
   * @throws Exception
   * @return int - total number of results
   */
  public function getCount() {
    while ($this->_hasMore) {
      $this->fetchOutstanding();
    }

    return $this->_length;
  }
  
  /**
   * Get all results as an array 
   * 
   * This might issue additional HTTP requests to fetch any outstanding
   * results from the server
   *
   * @throws Exception
   * @return array - an array of all results
   */
  public function getAll() {
    while ($this->_hasMore) {
      $this->fetchOutstanding();
    }

    return $this->_result;
  }
  
  /**
   * Rewind the cursor, necessary for Iterator
   *
   * @return void
   */
  public function rewind() {
    $this->_position = 0;
  }

  /**
   * Return the current result row, necessary for Iterator
   *
   * @return array - the current result row as an assoc array
   */
  public function current() {
    return $this->_result[$this->_position];
  }
  
  /**
   * Return the index of the current result row, necessary for Iterator
   *
   * @return int - the current result row index
   */
  public function key() {
    return $this->_position;
  }

  /**
   * Advance the cursor, necessary for Iterator
   *
   * @return void
   */
  public function next() {
    ++$this->_position;
  }
  
  /**
   * Check if cursor can be advanced further, necessary for Iterator
   * 
   * This might issue additional HTTP requests to fetch any outstanding
   * results from the server
   *
   * @throws Exception
   * @return bool - true if the cursor can be advanced further, false if cursor is at end
   */
  public function valid() {
    if ($this->_position <= $this->_length -1) {
      // we have more results than the current position is
      return true;
    }

    if (!$this->_hasMore || !$this->_id) {
      // we don't have more results, but the cursor is exhausted
      return false;
    }
  
    // need to fetch additional results from the server
    $this->fetchOutstanding();

    return ($this->_position <= $this->_length - 1);
  }
  
  /**
   * Create an array of documents from the input array
   * 
   * @param array $data - array of incoming "document" arrays
   * @param array $options - array of document options
   * @return void 
   */
  private function addDocumentsFromArray(array $data, $options=array())
  {
    foreach ($this->sanitize($data) as $row) {
      $this->_result[] = Document::createFromArray($row, $options);
    }
  }

  /**
   * Sanitize the result set rows
   * 
   * This will remove the _id and _rev attributes from the results if the
   * "sanitize" option is set
   *
   * @param array $rows - array of rows to be sanitized
   * @return array - sanitized rows
   */
  private function sanitize(array $rows) {
    if (isset($this->_options[self::ENTRY_SANITIZE]) and $this->_options[self::ENTRY_SANITIZE]) {
      foreach ($rows as $key=>$value) {
        unset($rows[$key][Document::ENTRY_ID]);
        unset($rows[$key][Document::ENTRY_REV]);
      }
    }
    return $rows;
  }
      
  /**
   * Fetch outstanding results from the server
   *
   * @throws Exception
   * @return void
   */
  private function fetchOutstanding() {
    // continuation
    $response = $this->_connection->put(Urls::URL_CURSOR . "/" . $this->_id, '');
    $data = $response->getJson();

    $this->_hasMore = (bool) $data[self::ENTRY_HASMORE];
    $this->addDocumentsFromArray($data[self::ENTRY_RESULT], $this->_options);

    if (!$this->_hasMore) {
      // we have fetch the complete result set and can unset the id now 
      $this->_id = NULL;
    }

    $this->updateLength();
  }

  /**
   * Set the length of the (fetched) result set
   *
   * @return void
   */
  private function updateLength() {
    $this->_length = count($this->_result); 
  }
}
Return current item: ArangoDB-PHP