- Added Validators
[activemongo.git] / lib / ActiveMongo.php
blob697753cdc673a0d734a07a18a5a7c87ca0b7068a
1 <?php
2 /*
3 +---------------------------------------------------------------------------------+
4 | Copyright (c) 2010 ActiveMongo |
5 +---------------------------------------------------------------------------------+
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met: |
8 | 1. Redistributions of source code must retain the above copyright |
9 | notice, this list of conditions and the following disclaimer. |
10 | |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | 3. All advertising materials mentioning features or use of this software |
16 | must display the following acknowledgement: |
17 | This product includes software developed by César D. Rodas. |
18 | |
19 | 4. Neither the name of the César D. Rodas nor the |
20 | names of its contributors may be used to endorse or promote products |
21 | derived from this software without specific prior written permission. |
22 | |
23 | THIS SOFTWARE IS PROVIDED BY CÉSAR D. RODAS ''AS IS'' AND ANY |
24 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
25 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
26 | DISCLAIMED. IN NO EVENT SHALL CÉSAR D. RODAS BE LIABLE FOR ANY |
27 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
28 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
29 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
30 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
32 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE |
33 +---------------------------------------------------------------------------------+
34 | Authors: César Rodas <crodas@php.net> |
35 +---------------------------------------------------------------------------------+
38 require dirname(__FILE__)."/Events.php";
40 // Class FilterException {{{
41 /**
42 * FilterException
44 * This is Exception is thrown if any validation
45 * fails when save() is called.
48 final class FilterException extends Exception
51 // }}}
53 // array get_object_vars_ex(stdobj $obj) {{{
54 /**
55 * Simple hack to avoid get private and protected variables
57 * @param obj
59 * @return array
61 function get_object_vars_ex($obj)
63 return get_object_vars($obj);
65 // }}}
67 /**
68 * ActiveMongo
70 * Simple ActiveRecord pattern built on top of MongoDB. This class
71 * aims to provide easy iteration, data validation before update,
72 * and efficient update.
74 * @author César D. Rodas <crodas@php.net>
75 * @license PHP License
76 * @package ActiveMongo
77 * @version 1.0
80 abstract class ActiveMongo extends ActiveMongo_Events implements Iterator
83 // properties {{{
84 /**
85 * Current databases objects
87 * @type array
89 private static $_dbs;
90 /**
91 * Current collections objects
93 * @type array
95 private static $_collections;
96 /**
97 * Current connection to MongoDB
99 * @type MongoConnection
101 private static $_conn;
103 * Database name
105 * @type string
107 private static $_db;
109 * Host name
111 * @type string
113 private static $_host;
115 * Current document
117 * @type array
119 private $_current = array();
121 * Result cursor
123 * @type MongoCursor
125 private $_cursor = null;
127 * Current document ID
129 * @type MongoID
131 private $_id;
134 * Tell if the current object
135 * is cloned or not.
137 * @type bool
139 private $_cloned = false;
140 // }}}
142 // GET CONNECTION CONFIG {{{
144 // string getCollectionName() {{{
146 * Get Collection Name, by default the class name,
147 * but you it can be override at the class itself to give
148 * a custom name.
150 * @return string Collection Name
152 protected function getCollectionName()
154 return strtolower(get_class($this));
156 // }}}
158 // string getDatabaseName() {{{
160 * Get Database Name, by default it is used
161 * the db name set by ActiveMong::connect()
163 * @return string DB Name
165 protected function getDatabaseName()
167 if (is_null(self::$_db)) {
168 throw new MongoException("There is no information about the default DB name");
170 return self::$_db;
172 // }}}
174 // void install() {{{
176 * Install.
178 * This static method iterate over the classes lists,
179 * and execute the setup() method on every ActiveMongo
180 * subclass. You should do this just once.
183 final public static function install()
185 $classes = array_reverse(get_declared_classes());
186 foreach ($classes as $class)
188 if ($class == __CLASS__) {
189 break;
191 if (is_subclass_of($class, __CLASS__)) {
192 $obj = new $class;
193 $obj->setup();
197 // }}}
199 // void connection($db, $host) {{{
201 * Connect
203 * This method setup parameters to connect to a MongoDB
204 * database. The connection is done when it is needed.
206 * @param string $db Database name
207 * @param string $host Host to connect
209 * @return void
211 final public static function connect($db, $host='localhost')
213 self::$_host = $host;
214 self::$_db = $db;
216 // }}}
218 // MongoConnection _getConnection() {{{
220 * Get Connection
222 * Get a valid database connection
224 * @return MongoConnection
226 final protected function _getConnection()
228 if (is_null(self::$_conn)) {
229 if (is_null(self::$_host)) {
230 self::$_host = 'localhost';
232 self::$_conn = new Mongo(self::$_host);
234 $dbname = $this->getDatabaseName();
235 if (!isSet(self::$_dbs[$dbname])) {
236 self::$_dbs[$dbname] = self::$_conn->selectDB($dbname);
238 return self::$_dbs[$dbname];
240 // }}}
242 // MongoCollection _getCollection() {{{
244 * Get Collection
246 * Get a collection connection.
248 * @return MongoCollection
250 final protected function _getCollection()
252 $colName = $this->getCollectionName();
253 if (!isset(self::$_collections[$colName])) {
254 self::$_collections[$colName] = self::_getConnection()->selectCollection($colName);
256 return self::$_collections[$colName];
258 // }}}
260 // }}}
262 // GET DOCUMENT TO SAVE OR UPDATE {{{
264 // bool getCurrentSubDocument(array &$document, string $parent_key, array $values, array $past_values) {{{
266 * Generate Sub-document
268 * This method build the difference between the current sub-document,
269 * and the origin one. If there is no difference, it would do nothing,
270 * otherwise it would build a document containing the differences.
272 * @param array &$document Document target
273 * @param string $parent_key Parent key name
274 * @param array $values Current values
275 * @param array $past_values Original values
277 * @return false
279 final function getCurrentSubDocument(&$document, $parent_key, Array $values, Array $past_values)
282 * The current property is a embedded-document,
283 * now we're looking for differences with the
284 * previous value (because we're on an update).
286 * It behaves exactly as getCurrentDocument,
287 * but this is simples (it doesn't support
288 * yet filters)
290 foreach ($values as $key => $value) {
291 $super_key = "{$parent_key}.{$key}";
292 if (is_array($value)) {
294 * Inner document detected
296 if (!isset($past_values[$key]) || !is_array($past_values[$key])) {
298 * We're lucky, it is a new sub-document,
299 * we simple add it
301 $document['$set'][$super_key] = $value;
302 } else {
304 * This is a document like this, we need
305 * to find out the differences to avoid
306 * network overhead.
308 if (!$this->getCurrentSubDocument($document, $super_key, $value, $past_values[$key])) {
309 return false;
312 continue;
313 } else if (!isset($past_values[$key]) || $past_values[$key] != $value) {
314 $document['$set'][$super_key] = $value;
318 foreach (array_diff(array_keys($past_values), array_keys($values)) as $key) {
319 $super_key = "{$parent_key}.{$key}";
320 $document['$unset'][$super_key] = 1;
323 return true;
325 // }}}
327 // array getCurrentDocument(bool $update) {{{
329 * Get Current Document
331 * Based on this object properties a new document (Array)
332 * is returned. If we're modifying an document, just the modified
333 * properties are included in this document, which uses $set,
334 * $unset, $pushAll and $pullAll.
337 * @param bool $update
339 * @return array
341 final protected function getCurrentDocument($update=false, $current=false)
343 $document = array();
344 $object = get_object_vars_ex($this);
346 if (!$current) {
347 $current = (array)$this->_current;
351 $this->findReferences($object);
353 $this->triggerEvent('before_validate_'.($update?'update':'creation'), array(&$object));
354 $this->triggerEvent('before_validate', array(&$object));
356 foreach ($object as $key => $value) {
357 if (!$value) {
358 continue;
360 if ($update) {
361 if (is_array($value) && isset($current[$key])) {
363 * If the Field to update is an array, it has a different
364 * behaviour other than $set and $unset. Fist, we need
365 * need to check if it is an array or document, because
366 * they can't be mixed.
369 if (!is_array($current[$key])) {
371 * We're lucky, the field wasn't
372 * an array previously.
374 $this->runFilter($key, $value, $current[$key]);
375 $document['$set'][$key] = $value;
376 continue;
379 if (!$this->getCurrentSubDocument($document, $key, $value, $current[$key])) {
380 throw new Exception("{$key}: Array and documents are not compatible");
382 } else if(!isset($current[$key]) || $value !== $current[$key]) {
384 * It is 'linear' field that has changed, or
385 * has been modified.
387 $past_value = isset($current[$key]) ? $current[$key] : null;
388 $this->runFilter($key, $value, $past_value);
389 $document['$set'][$key] = $value;
391 } else {
393 * It is a document insertation, so we
394 * create the document.
396 $this->runFilter($key, $value, null);
397 $document[$key] = $value;
401 /* Updated behaves in a diff. way */
402 if ($update) {
403 foreach (array_diff(array_keys($this->_current), array_keys($object)) as $property) {
404 if ($property == '_id') {
405 continue;
407 $document['$unset'][$property] = 1;
411 if (count($document) == 0) {
412 return array();
415 $this->triggerEvent('after_validate_'.($update?'update':'creation'), array(&$object));
416 $this->triggerEvent('after_validate', array(&$document));
418 return $document;
420 // }}}
422 // }}}
424 // void setCursor(MongoCursor $obj) {{{
426 * Set Cursor
428 * This method receive a MongoCursor and make
429 * it iterable.
431 * @param MongoCursor $obj
433 * @return void
435 final protected function setCursor(MongoCursor $obj)
437 $this->_cursor = $obj;
438 $this->setResult($obj->getNext());
440 // }}}
442 // void setResult(Array $obj) {{{
444 * Set Result
446 * This method takes an document and copy it
447 * as properties in this object.
449 * @param Array $obj
451 * @return void
453 final protected function setResult($obj)
455 /* Unsetting previous results, if any */
456 foreach (array_keys((array)$this->_current) as $key) {
457 unset($this->$key);
460 /* Add our current resultset as our object's property */
461 foreach ((array)$obj as $key => $value) {
462 if ($key[0] == '$') {
463 continue;
465 $this->$key = $value;
468 /* Save our record */
469 $this->_current = $obj;
471 // }}}
473 // this find([$_id]) {{{
475 * Simple find.
477 * Really simple find, which uses this object properties
478 * for fast filtering
480 * @return object this
482 final function find($_id = null)
484 $vars = get_object_vars_ex($this);
485 foreach ($vars as $key => $value) {
486 if (!$value) {
487 unset($vars[$key]);
489 $parent_class = __CLASS__;
490 if ($value InstanceOf $parent_class) {
491 $this->getColumnDeference($vars, $key, $value);
492 unset($vars[$key]); /* delete old value */
495 if ($_id != null) {
496 if (is_array($_id)) {
497 $vars['_id'] = array('$in' => $_id);
498 } else {
499 $vars['_id'] = $_id;
502 $res = $this->_getCollection()->find($vars);
503 $this->setCursor($res);
504 return $this;
506 // }}}
508 // void save(bool $async) {{{
510 * Save
512 * This method save the current document in MongoDB. If
513 * we're modifying a document, a update is performed, otherwise
514 * the document is inserted.
516 * On updates, special operations such as $set, $pushAll, $pullAll
517 * and $unset in order to perform efficient updates
519 * @param bool $async
521 * @return void
523 final function save($async=true)
525 $update = isset($this->_id) && $this->_id InstanceOf MongoID;
526 $conn = $this->_getCollection();
527 $obj = $this->getCurrentDocument($update);
528 if (count($obj) == 0) {
529 return; /*nothing to do */
532 /* PRE-save hook */
533 $this->triggerEvent('before_'.($update ? 'update' : 'create'), array(&$obj));
535 if ($update) {
536 $conn->update(array('_id' => $this->_id), $obj);
537 foreach ($obj as $key => $value) {
538 if ($key[0] == '$') {
539 continue;
541 $this->_current[$key] = $value;
543 } else {
544 $conn->insert($obj, $async);
545 $this->_id = $obj['_id'];
546 $this->_current = $obj;
549 $this->triggerEvent('after_'.($update ? 'update' : 'create'), array($obj));
551 // }}}
553 // bool delete() {{{
555 * Delete the current document
557 * @return bool
559 final function delete()
561 if ($this->valid()) {
562 $document = array('_id' => $this->_id);
563 $this->triggerEvent('before_delete', array($document));
564 $result = $this->_getCollection()->remove($document);
565 $this->triggerEvent('after_delete', array($document));
566 return $result;
568 return false;
570 // }}}
572 // void drop() {{{
574 * Delete the current colleciton and all its documents
576 * @return void
578 final static function drop()
580 $class = get_called_class();
581 if ($class == __CLASS__) {
582 return false;
584 $obj = new $class;
585 return $obj->_getCollection()->drop();
587 // }}}
589 // int count() {{{
591 * Return the number of documents in the actual request. If
592 * we're not in a request, it will return 0.
594 * @return int
596 final function count()
598 if ($this->valid()) {
599 return $this->_cursor->count();
601 return 0;
603 // }}}
605 // void setup() {{{
607 * This method should contain all the indexes, and shard keys
608 * needed by the current collection. This try to make
609 * installation on development environments easier.
611 function setup()
614 // }}}
616 // bool addIndex(array $columns, array $options) {{{
618 * addIndex
620 * Create an Index in the current collection.
622 * @param array $columns L ist of columns
623 * @param array $options Options
625 * @return bool
627 final function addIndex($columns, $options=array())
629 $default_options = array(
630 'background' => 1,
633 foreach ($default_options as $option => $value) {
634 if (!isset($options[$option])) {
635 $options[$option] = $value;
639 $collection = $this->_getCollection();
641 return $collection->ensureIndex($columns, $options);
643 // }}}
645 // string __toString() {{{
647 * To String
649 * If this object is treated as a string,
650 * it would return its ID.
652 * @return string
654 final function __toString()
656 return (string)$this->getID();
658 // }}}
660 // array sendCmd(array $cmd) {{{
662 * This method sends a command to the current
663 * database.
665 * @param array $cmd Current command
667 * @return array
669 final protected function sendCmd($cmd)
671 return $this->_getConnection()->command($cmd);
673 // }}}
675 // ITERATOR {{{
677 // void reset() {{{
679 * Reset our Object, delete the current cursor if any, and reset
680 * unsets the values.
682 * @return void
684 final function reset()
686 $this->_cursor = null;
687 $this->setResult(array());
689 // }}}
691 // bool valid() {{{
693 * Valid
695 * Return if we're on an iteration and if it is still valid
697 * @return true
699 final function valid()
701 return $this->_cursor InstanceOf MongoCursor && $this->_cursor->valid();
703 // }}}
705 // bool next() {{{
707 * Move to the next document
709 * @return bool
711 final function next()
713 if ($this->_cloned) {
714 throw new MongoException("Cloned objects can't iterate");
716 return $this->_cursor->next();
718 // }}}
720 // this current() {{{
722 * Return the current object, and load the current document
723 * as this object property
725 * @return object
727 final function current()
729 $this->setResult($this->_cursor->current());
730 return $this;
732 // }}}
734 // bool rewind() {{{
736 * Go to the first document
738 final function rewind()
740 return $this->_cursor->rewind();
742 // }}}
744 // }}}
746 // REFERENCES {{{
748 // array getReference() {{{
750 * ActiveMongo extended the Mongo references, adding
751 * the concept of 'dynamic' requests, saving in the database
752 * the current query with its options (sort, limit, etc).
754 * This is useful to associate a document with a given
755 * request. To undestand this better please see the 'reference'
756 * example.
758 * @return array
760 final function getReference($dynamic=false)
762 if (!$this->getID() && !$dynamic) {
763 return null;
766 $document = array(
767 '$ref' => $this->getCollectionName(),
768 '$id' => $this->getID(),
769 '$db' => $this->getDatabaseName(),
770 'class' => get_class($this),
773 if ($dynamic) {
774 $cursor = $this->_cursor;
775 if (!is_callable(array($cursor, "getQuery"))) {
776 throw new Exception("Please upgrade your PECL/Mongo module to use this feature");
778 $document['dynamic'] = array();
779 $query = $cursor->getQuery();
780 foreach ($query as $type => $value) {
781 $document['dynamic'][$type] = $value;
784 return $document;
786 // }}}
788 // void getDocumentReferences($document, &$refs) {{{
790 * Get Current References
792 * Inspect the current document trying to get any references,
793 * if any.
795 * @param array $document Current document
796 * @param array &$refs References found in the document.
797 * @param array $parent_key Parent key
799 * @return void
801 final protected function getDocumentReferences($document, &$refs, $parent_key=null)
803 foreach ($document as $key => $value) {
804 if (is_array($value)) {
805 if (MongoDBRef::isRef($value)) {
806 $pkey = $parent_key;
807 $pkey[] = $key;
808 $refs[] = array('ref' => $value, 'key' => $pkey);
809 } else {
810 $parent_key[] = $key;
811 $this->getDocumentReferences($value, $refs, $parent_key);
816 // }}}
818 // object _deferencingCreateObject(string $class) {{{
820 * Called at deferencig time
822 * Check if the given string is a class, and it is a sub class
823 * of ActiveMongo, if it is instance and return the object.
825 * @param string $class
827 * @return object
829 private function _deferencingCreateObject($class)
831 if (!is_subclass_of($class, __CLASS__)) {
832 throw new MongoException("Fatal Error, imposible to create ActiveMongo object of {$class}");
834 return new $class;
836 // }}}
838 // void _deferencingRestoreProperty(array &$document, array $keys, mixed $req) {{{
840 * Called at deferencig time
842 * This method iterates $document until it could match $keys path, and
843 * replace its value by $req.
845 * @param array &$document Document to replace
846 * @param array $keys Path of property to change
847 * @param mixed $req Value to replace.
849 * @return void
851 private function _deferencingRestoreProperty(&$document, $keys, $req)
853 $obj = & $document;
855 /* find the $req proper spot */
856 foreach ($keys as $key) {
857 $obj = & $obj[$key];
860 $obj = $req;
862 /* Delete reference variable */
863 unset($obj);
865 // }}}
867 // object _deferencingQuery($request) {{{
869 * Called at deferencig time
871 * This method takes a dynamic reference and request
872 * it to MongoDB.
874 * @param array $request Dynamic reference
876 * @return this
878 private function _deferencingQuery($request)
880 $collection = $this->_getCollection();
881 $cursor = $collection->find($request['query'], $request['fields']);
882 if ($request['limit'] > 0) {
883 $cursor->limit($request['limit']);
885 if ($request['skip'] > 0) {
886 $cursor->limit($request['limit']);
889 $this->setCursor($cursor);
891 return $this;
893 // }}}
895 // void doDeferencing() {{{
897 * Perform a deferencing in the current document, if there is
898 * any reference.
900 * ActiveMongo will do its best to group references queries as much
901 * as possible, in order to perform as less request as possible.
903 * ActiveMongo doesn't rely on MongoDB references, but it can support
904 * it, but it is prefered to use our referencing.
906 * @experimental
908 final function doDeferencing($refs=array())
910 /* Get current document */
911 $document = get_object_vars_ex($this);
913 if (count($refs)==0) {
914 /* Inspect the whole document */
915 $this->getDocumentReferences($document, $refs);
918 $db = $this->_getConnection();
920 /* Gather information about ActiveMongo Objects
921 * that we need to create
923 $classes = array();
924 foreach ($refs as $ref) {
925 if (!isset($ref['ref']['class'])) {
927 /* Support MongoDBRef, we do our best to be compatible {{{ */
928 /* MongoDB 'normal' reference */
930 $obj = MongoDBRef::get($db, $ref['ref']);
932 /* Offset the current document to the right spot */
933 /* Very inefficient, never use it, instead use ActiveMongo References */
935 $this->_deferencingRestoreProperty($document, $ref['key'], clone $req);
937 /* Dirty hack, override our current document
938 * property with the value itself, in order to
939 * avoid replace a MongoDB reference by its content
941 $this->_deferencingRestoreProperty($this->_current, $ref['key'], clone $req);
943 /* }}} */
945 } else {
947 if (isset($ref['ref']['dynamic'])) {
948 /* ActiveMongo Dynamic Reference */
950 /* Create ActiveMongo object */
951 $req = $this->_deferencingCreateObject($ref['ref']['class']);
953 /* Restore saved query */
954 $req->_deferencingQuery($ref['ref']['dynamic']);
956 $results = array();
958 /* Add the result set */
959 foreach ($req as $result) {
960 $results[] = clone $result;
963 /* add information about the current reference */
964 foreach ($ref['ref'] as $key => $value) {
965 $results[$key] = $value;
968 $this->_deferencingRestoreProperty($document, $ref['key'], $results);
970 } else {
971 /* ActiveMongo Reference FTW! */
972 $classes[$ref['ref']['class']][] = $ref;
977 /* {{{ Create needed objects to query MongoDB and replace
978 * our references by its objects documents.
980 foreach ($classes as $class => $refs) {
981 $req = $this->_deferencingCreateObject($class);
983 /* Load list of IDs */
984 $ids = array();
985 foreach ($refs as $ref) {
986 $ids[] = $ref['ref']['$id'];
989 /* Search to MongoDB once for all IDs found */
990 $req->find($ids);
992 if ($req->count() != count($refs)) {
993 $total = $req->count();
994 $expected = count($refs);
995 throw new MongoException("Dereferencing error, MongoDB replied {$total} objects, we expected {$expected}");
998 /* Replace our references by its objects */
999 foreach ($refs as $ref) {
1000 $id = $ref['ref']['$id'];
1001 $place = $ref['key'];
1002 $req->rewind();
1003 while ($req->getID() != $id && $req->next());
1005 assert($req->getID() == $id);
1007 $this->_deferencingRestoreProperty($document, $place, clone $req);
1009 unset($obj);
1012 /* Release request, remember we
1013 * safely cloned it,
1015 unset($req);
1017 // }}}
1019 /* Replace the current document by the new deferenced objects */
1020 foreach ($document as $key => $value) {
1021 $this->$key = $value;
1024 // }}}
1026 // void getColumnDeference(&$document, $propety, ActiveMongo Obj) {{{
1028 * Prepare a "selector" document to search treaing the property
1029 * as a reference to the given ActiveMongo object.
1032 final function getColumnDeference(&$document, $property, ActiveMongo $obj)
1034 $document["{$property}.\$id"] = $obj->getID();
1036 // }}}
1038 // void findReferences(&$document) {{{
1040 * Check if in the current document to insert or update
1041 * exists any references to other ActiveMongo Objects.
1043 * @return void
1045 final function findReferences(&$document)
1047 if (!is_array($document)) {
1048 return;
1050 foreach($document as &$value) {
1051 $parent_class = __CLASS__;
1052 if (is_array($value)) {
1053 if (MongoDBRef::isRef($value)) {
1054 /* If the property we're inspecting is a reference,
1055 * we need to remove the values, restoring the valid
1056 * Reference.
1058 $arr = array(
1059 '$ref'=>1, '$id'=>1, '$db'=>1, 'class'=>1, 'dynamic'=>1
1061 foreach (array_keys($value) as $key) {
1062 if (!isset($arr[$key])) {
1063 unset($value[$key]);
1066 } else {
1067 $this->findReferences($value);
1069 } else if ($value InstanceOf $parent_class) {
1070 $value = $value->getReference();
1073 /* trick: delete last var. reference */
1074 unset($value);
1076 // }}}
1078 // void __clone() {{{
1079 /**
1080 * Cloned objects are rarely used, but ActiveMongo
1081 * uses it to create different objects per everyrecord,
1082 * which is used at deferencing. Therefore cloned object
1083 * do not contains the recordset, just the actual document,
1084 * so iterations are not allowed.
1087 final function __clone()
1089 unset($this->_cursor);
1090 $this->_cloned = true;
1092 // }}}
1094 // }}}
1096 // GET DOCUMENT ID {{{
1098 // getID() {{{
1100 * Return the current document ID. If there is
1101 * no document it would return false.
1103 * @return object|false
1105 final public function getID()
1107 if ($this->_id instanceof MongoID) {
1108 return $this->_id;
1110 return false;
1112 // }}}
1114 // string key() {{{
1116 * Return the current key
1118 * @return string
1120 final function key()
1122 return $this->getID();
1124 // }}}
1126 // }}}
1130 * Local variables:
1131 * tab-width: 4
1132 * c-basic-offset: 4
1133 * End:
1134 * vim600: sw=4 ts=4 fdm=marker
1135 * vim<600: sw=4 ts=4