├── .gitignore
├── LICENSE
├── README.md
├── build.sh
├── config.cmake
├── interactive_mode.sh
├── phpunit.xml
├── src
├── MongoClient.cpp
├── MongoClient.php
├── MongoCollection.cpp
├── MongoCollection.php
├── MongoCursor.cpp
├── MongoCursor.php
├── MongoDB.php
├── bson.cpp
├── bson.php
├── bson_decode.cpp
├── bson_decode.h
├── bson_test
├── bson_test.cpp
├── contrib
│ ├── classes.h
│ ├── encode.cpp
│ └── encode.h
├── encode_draft.cpp
├── exceptions
│ ├── MongoConnectionException.php
│ ├── MongoCursorException.php
│ ├── MongoCursorTimeoutException.php
│ ├── MongoDuplicateKeyException.php
│ ├── MongoException.php
│ ├── MongoExecutionTimeoutException.php
│ ├── MongoGridFSException.php
│ ├── MongoProtocolException.php
│ ├── MongoResultException.php
│ └── MongoWriteConcernException.php
├── ext_mongo.cpp
├── ext_mongo.h
├── mongo_common.cpp
├── mongo_common.h
└── types
│ ├── MongoBinData.php
│ ├── MongoCode.php
│ ├── MongoDBRef.php
│ ├── MongoDate.php
│ ├── MongoId.php
│ ├── MongoInt32.php
│ ├── MongoInt64.php
│ ├── MongoMaxKey.php
│ ├── MongoMinKey.php
│ ├── MongoRegex.php
│ └── MongoTimestamp.php
├── test.sh
└── test
├── AutoLoader.php
├── MongoTestCase.php
├── basicTest.php
├── bootstrap.php
├── bson_encode_decode_Test.php
├── mongoClientTest.php
├── mongoCollectionTest.php
├── mongoCursorTest.php
├── mongoDBTest.php
├── mongoDateTest.php
├── mongoclient-getserverversion.php
├── students.json
└── test_collection.json
/.gitignore:
--------------------------------------------------------------------------------
1 | CMakeCache.txt
2 | CMakeFiles
3 | CMakeLists.txt
4 | cmake_install.cmake
5 | Makefile
6 | *.so
7 |
8 | # This file is concatenated from other PHP source files at build time
9 | src/ext_mongo.php
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
204 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **Note**: This repository is unsupported and no longer under active development. Please see [mongodb/mongo-hhvm-driver](https://github.com/mongodb/mongo-hhvm-driver) for our official HHVM driver.
2 |
3 | ----
4 |
5 | DISCLAIMER
6 | ----------
7 | Please note: all tools/ scripts in this repo are released for use "AS IS" without any warranties of any kind, including, but not limited to their installation, use, or performance. We disclaim any and all warranties, either express or implied, including but not limited to any warranty of noninfringement, merchantability, and/ or fitness for a particular purpose. We do not warrant that the technology will meet your requirements, that the operation thereof will be uninterrupted or error-free, or that any errors will be corrected.
8 | Any use of these scripts and tools is at your own risk. There is no guarantee that they have been through thorough testing in a comparable environment and we are not responsible for any damage or data loss incurred with their use.
9 | You are responsible for reviewing and testing any scripts you run thoroughly before use in any non-testing environment.
10 |
11 | # MongoDB driver for HHVM
12 |
13 | This is an implementation of the
14 | [MongoDB PHP driver](https://github.com/mongodb/mongo-php-driver) for
15 | [HHVM](https://github.com/facebook/hhvm). It is not feature-complete and should
16 | be considered experimental.
17 |
18 | This project is not officially supported and GitHub issues have been disabled.
19 |
20 | ## Dependencies
21 |
22 | Compiling this extension requires the following libraries:
23 |
24 | * HHVM (>=3.1.0) must be compiled from source, since the binary distributions
25 | of HHVM do not include necessary development headers. Instructions for
26 | compiling HHVM may be found
27 | [here](https://github.com/facebook/hhvm/wiki#building-hhvm).
28 |
29 | * libmongoc (>=0.94.0) and its corresponding libbson dependency must be
30 | installed as a system library. Instructions for installing libmongoc may be
31 | found
32 | [here](https://github.com/mongodb/mongo-c-driver#fetch-sources-and-build).
33 |
34 | ## Building and installation
35 |
36 | Ensure that the `HPHP_HOME` environment variable is set to the HHVM project
37 | directory. This should be the path to the cloned HHVM git repository where you
38 | compiled the project.
39 |
40 | ```bash
41 | $ export HPHP_HOME=/path/to/hhvm
42 | ```
43 |
44 | Execute this project's `build.sh` script:
45 |
46 | ```bash
47 | $ ./build.sh
48 | ```
49 |
50 | This script checks for the HHVM path, executes `hphpize` to prepare the build
51 | process, and finally executes `cmake` and `make` to compile the extension.
52 |
53 | The build process will produce a `mongo.so` file, which can then be dynamically
54 | loaded by HHVM by adding the following to HHVM's `config.hdf` file:
55 |
56 | ```
57 | DynamicExtensions {
58 | mongo = /path/to/mongo.so
59 | }
60 | ```
61 |
62 | This example is taken from the
63 | [Extension API](https://github.com/facebook/hhvm/wiki/Extension-API)
64 | documentation.
65 |
66 | Note that the `mongo` key in this example is a placeholder; HHVM only cares that
67 | the path to the `mongo.so` file is correct. You may notice that in our test
68 | script, we use `0` as a key when specifying our extension via the command line.
69 |
70 | ## Tests
71 |
72 | The test suite is implemented with [PHPUnit](http://phpunit.de) and may be
73 | executed via the `test.sh` script:
74 |
75 | ```
76 | $ ./test.sh
77 | ```
78 |
79 | The test script depends on the `HPHP_HOME` environment variable and will attempt
80 | to locate PHPUnit via the `which` command, so ensure that the `phpunit` binary
81 | is installed in an executable path.
82 |
83 | ## Interactive Mode
84 |
85 | To try out this work in progress for yourself, you can run the extension in interactive mode on HipHop VM via the `interactive_mode.sh` script:
86 |
87 | ```
88 | $ ./interactive_mode.sh
89 | ```
90 |
91 | Once in interactive mode, you can execute queries and all implemented methods on your existing local databases. For example, if a `test` database exists with a `students` collection, I can access one document in that collection by running the following commands:
92 |
93 | ```
94 | hphpd> $cli = new MongoClient();
95 | hphpd> $db = $cli->selectDB('test');
96 | hphpd> $coll = $db->selectCollection('students');
97 | hphpd> $cur = $coll->find()->limit(1);
98 | hphpd> $cur->rewind();
99 | hphpd> var_dump($cur->current());
100 | ```
101 |
102 | ## Credits
103 |
104 | Máximo Cuadros created the src/contrib/encode.h, src/contrib/encode.cpp and src/contrib/classes.h files.
105 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ "$HPHP_HOME" = "" ]; then
4 | echo "HPHP_HOME environment variable must be set!"
5 | exit 1
6 | fi
7 |
8 | printf " src/ext_mongo.php
9 |
10 | # Base classes must be concatenated/declared first
11 | tail -q -n +2 src/exceptions/MongoException.php >> src/ext_mongo.php
12 | tail -q -n +2 src/exceptions/MongoConnectionException.php >> src/ext_mongo.php
13 | tail -q -n +2 src/exceptions/MongoCursorException.php >> src/ext_mongo.php
14 | tail -q -n +2 src/exceptions/MongoDuplicateKeyException.php >> src/ext_mongo.php
15 | tail -q -n +2 src/exceptions/MongoExecutionTimeoutException.php >> src/ext_mongo.php
16 | tail -q -n +2 src/exceptions/MongoGridFSException.php >> src/ext_mongo.php
17 | tail -q -n +2 src/exceptions/MongoProtocolException.php >> src/ext_mongo.php
18 | tail -q -n +2 src/exceptions/MongoResultException.php >> src/ext_mongo.php
19 | tail -q -n +2 src/exceptions/MongoCursorTimeoutException.php >> src/ext_mongo.php
20 | tail -q -n +2 src/exceptions/MongoWriteConcernException.php >> src/ext_mongo.php
21 |
22 | # Type and base classes have no inheritance hierarchy
23 | tail -q -n +2 src/types/*.php >> src/ext_mongo.php
24 | find src/ -maxdepth 1 -name "*.php" \! -name ext_mongo.php | xargs tail -q -n +2 >> src/ext_mongo.php
25 |
26 | $HPHP_HOME/hphp/tools/hphpize/hphpize
27 | cmake .
28 | make -j5
29 |
--------------------------------------------------------------------------------
/config.cmake:
--------------------------------------------------------------------------------
1 | FIND_PATH(MONGOC_INCLUDE_DIR NAMES mongoc.h
2 | PATHS /usr/include /usr/include/libmongoc-1.0 /usr/local/include /usr/local/include/libmongoc-1.0)
3 |
4 | FIND_LIBRARY(MONGOC_LIBRARY NAMES mongoc-1.0 PATHS /lib /usr/lib /usr/local/lib)
5 |
6 | IF (MONGOC_INCLUDE_DIR AND MONGOC_LIBRARY)
7 | MESSAGE(STATUS "mongoc Include dir: ${MONGOC_INCLUDE_DIR}")
8 | MESSAGE(STATUS "libmongoc library: ${MONGOC_LIBRARY}")
9 | ELSE()
10 | MESSAGE(FATAL_ERROR "Cannot find libmongoc library")
11 | ENDIF()
12 |
13 | FIND_PATH(BSON_INCLUDE_DIR NAMES bson.h
14 | PATHS /usr/include /usr/include/libbson-1.0 /usr/local/include /usr/local/include/libbson-1.0)
15 |
16 | FIND_LIBRARY(BSON_LIBRARY NAMES bson-1.0 PATHS /lib /usr/lib /usr/local/lib)
17 |
18 | IF (BSON_INCLUDE_DIR AND BSON_LIBRARY)
19 | MESSAGE(STATUS "bson Include dir: ${BSON_INCLUDE_DIR}")
20 | MESSAGE(STATUS "libbson library: ${BSON_LIBRARY}")
21 | ELSE()
22 | MESSAGE(FATAL_ERROR "Cannot find libbson library")
23 | ENDIF()
24 |
25 | include_directories(${MONGOC_INCLUDE_DIR})
26 | include_directories(${BSON_INCLUDE_DIR})
27 |
28 | HHVM_EXTENSION(mongo src/ext_mongo.cpp src/mongo_common.cpp src/MongoClient.cpp src/MongoCursor.cpp src/MongoCollection.cpp src/bson.cpp src/bson_decode.cpp src/contrib/encode.cpp)
29 | HHVM_SYSTEMLIB(mongo src/ext_mongo.php)
30 |
31 | target_link_libraries(mongo ${MONGOC_LIBRARY})
32 |
--------------------------------------------------------------------------------
/interactive_mode.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | DIRNAME=`dirname $0`
4 | REALPATH=`which realpath`
5 | if [ ! -z "${REALPATH}" ]; then
6 | DIRNAME=`realpath ${DIRNAME}`
7 | fi
8 |
9 | ${HPHP_HOME}/hphp/hhvm/hhvm -a \
10 | -vDynamicExtensions.0=${DIRNAME}/mongo.so
11 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 | test
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/MongoClient.cpp:
--------------------------------------------------------------------------------
1 | #include "ext_mongo.h"
2 |
3 | #if HHVM_API_VERSION < 20140702L
4 | #define throw_not_implemented(msg) \
5 | throw NotImplementedException(msg);
6 | #endif
7 |
8 | namespace HPHP {
9 |
10 | ////////////////////////////////////////////////////////////////////////////////
11 | // class MongoClient
12 |
13 | static void HHVM_METHOD(MongoClient, __construct, const String& uri, Array options) {
14 | MongocClient *client = MongocClient::GetPersistent(uri);
15 |
16 | if (client == nullptr) {
17 | client = new MongocClient(uri);
18 | }
19 |
20 | if (client->isInvalid()) {
21 | mongoThrow(strcat("Unable to connect: ", uri.c_str()));
22 | }
23 |
24 | MongocClient::SetPersistent(uri, client);
25 | this_->o_set(s_mongoc_client, client, s_mongoclient);
26 | }
27 |
28 | static bool HHVM_METHOD(MongoClient, close, Variant connection) {
29 | throw_not_implemented(__func__);
30 | }
31 |
32 | static bool HHVM_METHOD(MongoClient, connect) {
33 | throw_not_implemented(__func__);
34 | }
35 |
36 | static Array HHVM_METHOD(MongoClient, dropDB, Variant db) {
37 | throw_not_implemented(__func__);
38 | }
39 |
40 | static Object HHVM_METHOD(MongoClient, __get, const String& dbname) {
41 | throw_not_implemented(__func__);
42 | }
43 |
44 | static Array HHVM_STATIC_METHOD(MongoClient, getConnections) {
45 | throw_not_implemented(__func__);
46 | }
47 |
48 | static Array HHVM_METHOD(MongoClient, getHosts) {
49 | throw_not_implemented(__func__);
50 | }
51 |
52 | static Array HHVM_METHOD(MongoClient, getReadPreference) {
53 | throw_not_implemented(__func__);
54 | }
55 |
56 | static bool HHVM_METHOD(MongoClient, killCursor, const String& server_hash, Variant id) {
57 | throw_not_implemented(__func__);
58 | }
59 |
60 | static Array HHVM_METHOD(MongoClient, listDBs) {
61 | throw_not_implemented(__func__);
62 | }
63 |
64 | static Object HHVM_METHOD(MongoClient, selectCollection, const String& db, const String& collection) {
65 | throw_not_implemented(__func__);
66 | }
67 |
68 | static Object HHVM_METHOD(MongoClient, selectDB, const String& name) {
69 | throw_not_implemented(__func__);
70 | }
71 |
72 | static bool HHVM_METHOD(MongoClient, setReadPreference, const String& read_preference, Array tags) {
73 | throw_not_implemented(__func__);
74 | }
75 |
76 | static String HHVM_METHOD(MongoClient, __toString) {
77 | String s = "dummy toString";
78 | return s;
79 | //throw_not_implemented(__func__);
80 | }
81 |
82 | /* Test method that returns the server's version string */
83 | static String HHVM_METHOD(MongoClient, getServerVersion) {
84 | bool result;
85 | bson_t buildInfo, reply;
86 | bson_error_t error;
87 | bson_iter_t iter;
88 | String retval;
89 |
90 | auto client = get_client(this_);
91 |
92 | bson_init(&buildInfo);
93 | bson_append_int32(&buildInfo, "buildInfo", 9, 1);
94 |
95 | result = mongoc_client_command_simple(client->get(), "test", &buildInfo, nullptr, &reply, &error);
96 |
97 | bson_destroy(&buildInfo);
98 |
99 | if ( ! result) {
100 | mongoThrow(strcat("Command error: ", error.message));
101 | }
102 |
103 | if (bson_iter_init_find(&iter, &reply, "version")) {
104 | retval = String(bson_iter_utf8(&iter, nullptr), CopyString);
105 | }
106 |
107 | bson_destroy(&reply);
108 |
109 | return retval;
110 | }
111 |
112 | ////////////////////////////////////////////////////////////////////////////////
113 |
114 | void MongoExtension::_initMongoClientClass() {
115 | HHVM_ME(MongoClient, __construct);
116 | HHVM_ME(MongoClient, close);
117 | HHVM_ME(MongoClient, connect);
118 | HHVM_ME(MongoClient, dropDB);
119 | HHVM_ME(MongoClient, __get);
120 | HHVM_STATIC_ME(MongoClient, getConnections);
121 | HHVM_ME(MongoClient, getHosts);
122 | HHVM_ME(MongoClient, getReadPreference);
123 | HHVM_ME(MongoClient, killCursor);
124 | HHVM_ME(MongoClient, listDBs);
125 | HHVM_ME(MongoClient, selectCollection);
126 | HHVM_ME(MongoClient, selectDB);
127 | HHVM_ME(MongoClient, setReadPreference);
128 | HHVM_ME(MongoClient, __toString);
129 | HHVM_ME(MongoClient, getServerVersion);
130 | }
131 |
132 | } //namespace HPHP
133 |
--------------------------------------------------------------------------------
/src/MongoClient.php:
--------------------------------------------------------------------------------
1 | >
18 | public function __construct (string $server = "mongodb://localhost:27017",
19 | array $options = array('connect' => true)): void;
20 |
21 | /**
22 | * Closes this connection
23 | *
24 | * @param boolean|string $connection - connection If connection is
25 | * not given, or FALSE then connection that would be selected for
26 | * writes would be closed. If
27 | * connection is TRUE then all connections as known by the connection
28 | * manager will be closed. If connection is a string argument, then
29 | * it will only close the connection identified by this hash.
30 | *
31 | * @return bool - Returns if the connection was successfully closed.
32 | */
33 | <<__Native>>
34 | public function close(?mixed $connection): bool;
35 |
36 | /**
37 | * Connects to a database server
38 | *
39 | * @return bool - If the connection was successful.
40 | */
41 | <<__Native>>
42 | public function connect(): bool;
43 |
44 | /**
45 | * Drops a database [deprecated]
46 | *
47 | * @param mixed $db - db The database to drop. Can be a MongoDB
48 | * object or the name of the database.
49 | *
50 | * @return array - Returns the database response.
51 | */
52 | public function dropDB(mixed $db): array {
53 | if(is_object($db)) {
54 | return $db->drop();
55 | }
56 |
57 | if(is_string($db)) {
58 | return $this->selectDB($db)->drop();
59 | }
60 | throw new MongoConnectionException();
61 | }
62 |
63 | /**
64 | * Gets a database
65 | *
66 | * @param string $dbname - dbname The database name.
67 | *
68 | * @return MongoDB - Returns a new db object.
69 | */
70 | public function __get(string $dbname): MongoDB {
71 | return $this->selectDB($dbname);
72 | }
73 |
74 | /**
75 | * Return info about all open connections
76 | *
77 | * @return array - An array of open connections.
78 | */
79 | <<__Native>>
80 | public static function getConnections(): array;
81 |
82 | /**
83 | * Updates status for all associated hosts
84 | *
85 | * @return array - Returns an array of information about the hosts in
86 | * the set.
87 | */
88 | <<__Native>>
89 | public function getHosts(): array;
90 |
91 | /**
92 | * Get the read preference for this connection
93 | *
94 | * @return array -
95 | */
96 | public function getReadPreference(): array {
97 | return $this->read_preference;
98 | }
99 |
100 | /**
101 | * Kills a specific cursor on the server
102 | *
103 | * @param string $server_hash - server_hash The server hash that has
104 | * the cursor. This can be obtained through MongoCursor::info.
105 | * @param int|mongoint64 $id - id The ID of the cursor to kill.
106 | *
107 | * @return bool - Returns TRUE if the method attempted to kill a
108 | * cursor, and FALSE if there was something wrong with the arguments
109 | * (such as a wrong server_hash).
110 | */
111 | <<__Native>>
112 | public function killCursor(string $server_hash,
113 | mixed $id): bool;
114 |
115 | /**
116 | * Lists all of the databases available.
117 | *
118 | * @return array - Returns an associative array containing three
119 | * fields. The first field is databases, which in turn contains an
120 | * array. Each element of the array is an associative array
121 | * corresponding to a database, giving th database's name, size, and if
122 | * it's empty. The other two fields are totalSize (in bytes) and ok,
123 | * which is 1 if this method ran successfully.
124 | */
125 | public function listDBs(): array {
126 | return $this->selectDB('admin')->command(array("listDatabases" => 1));
127 | }
128 |
129 | /**
130 | * Gets a database collection
131 | *
132 | * @param string $db - db The database name.
133 | * @param string $collection - collection The collection name.
134 | *
135 | * @return MongoCollection - Returns a new collection object.
136 | */
137 | public function selectCollection(string $db,
138 | string $collection) {
139 | return $this->selectDB($db)->selectCollection($collection);
140 | }
141 |
142 | /**
143 | * Gets a database
144 | *
145 | * @param string $name - name The database name.
146 | *
147 | * @return MongoDB - Returns a new database object.
148 | */
149 | public function selectDB(string $name): MongoDB {
150 | if (!isset($this->databases[$name])) {
151 | $this->databases[$name] = new MongoDB($this, $name);
152 | }
153 | return $this->databases[$name];
154 | }
155 |
156 | /**
157 | * Set the read preference for this connection
158 | *
159 | * @param string $read_preference -
160 | * @param array $tags -
161 | *
162 | * @return bool -
163 | */
164 | public function setReadPreference(string $read_preference,
165 | array $tags): bool {
166 | $this->read_preference['type'] = $read_preference;
167 | $this->read_preference['tagsets'] = $tags;
168 | return true;
169 | }
170 |
171 | /**
172 | * String representation of this connection
173 | *
174 | * @return string - Returns hostname and port for this connection.
175 | */
176 | <<__Native>>
177 | public function __toString(): string;
178 |
179 | /**
180 | * Test method that returns the server's version string
181 | *
182 | * @return string
183 | */
184 | <<__Native>>
185 | public function getServerVersion(): string;
186 | }
--------------------------------------------------------------------------------
/src/MongoCollection.cpp:
--------------------------------------------------------------------------------
1 | #include "ext_mongo.h"
2 | #include "contrib/encode.h"
3 |
4 | namespace HPHP {
5 |
6 | static mongoc_collection_t *get_collection(Object obj) {
7 | mongoc_collection_t *collection;
8 |
9 | auto db = obj->o_realProp("db", ObjectData::RealPropUnchecked, "MongoCollection")->toObject();
10 | auto client = db->o_realProp("client", ObjectData::RealPropUnchecked, "MongoDB")->toObject();
11 | String db_name = db->o_realProp("db_name", ObjectData::RealPropUnchecked, "MongoDB")->toString();
12 | String collection_name = obj->o_realProp("name", ObjectData::RealPropUnchecked, "MongoCollection")->toString();
13 |
14 | collection = mongoc_client_get_collection (get_client(client)->get(), db_name.c_str(), collection_name.c_str());
15 | return collection;
16 | }
17 |
18 | ////////////////////////////////////////////////////////////////////////////////
19 | // class MongoCollection
20 |
21 | /**
22 | * Inserts a document into the collection
23 | *
24 | * @param array|object $a - a An array or object. If an object is
25 | * used, it may not have protected or private properties. If the
26 | * parameter does not have an _id key or property, a new MongoId
27 | * instance will be created and assigned to it.
28 | * @param array $options - options Options for the insert.
29 | *
30 | * @return bool|array - Returns an array containing the status of the
31 | * insertion if the "w" option is set. Otherwise, returns TRUE if the
32 | * inserted array is not empty (a MongoException will be thrown if the
33 | * inserted array is empty).
34 | */
35 | //public function insert(mixed $a, array $options = array()): mixed;
36 | static Variant HHVM_METHOD(MongoCollection, insert, Variant a, Array options) {
37 | mongoc_collection_t *collection;
38 | bson_t doc;
39 | bson_error_t error;
40 |
41 | collection = get_collection(this_);
42 |
43 | Array doc_array = a.toArray();
44 | doc = encodeToBSON(doc_array);
45 | // bson_init(&doc);
46 | // bson_oid_init_from_string(&oid, doc_array[String("_id")].toString().c_str());
47 | // bson_append_oid(&doc, "_id", 3, &oid);
48 | // //Supporting only "name" key
49 | // bson_append_utf8(&doc, "name", 4, doc_array[String("name")].toString().c_str(), doc_array[String("name")].toString().length());
50 |
51 | bool ret = mongoc_collection_insert(collection, MONGOC_INSERT_NONE, &doc, NULL, &error);
52 |
53 | mongoc_collection_destroy (collection);
54 | bson_destroy(&doc);
55 |
56 | return ret;
57 | /*
58 | bool mongoc_collection_insert (mongoc_collection_t *collection,
59 | mongoc_insert_flags_t flags,
60 | const bson_t *document,
61 | const mongoc_write_concern_t *write_concern,
62 | bson_error_t *error);
63 | */
64 |
65 | }
66 |
67 |
68 | /**
69 | * Remove records from this collection
70 | *
71 | * @param array $criteria - criteria Description of records to
72 | * remove.
73 | * @param array $options - options Options for remove. "justOne"
74 | * Remove at most one record matching this criteria.
75 | *
76 | * @return bool|array - Returns an array containing the status of the
77 | * removal if the "w" option is set. Otherwise, returns TRUE.
78 | */
79 | //public function remove(array $criteria = array(), array $options = array()): mixed;
80 | static Variant HHVM_METHOD(MongoCollection, remove, Array criteria, Array options) {
81 | mongoc_collection_t *collection;
82 | bson_t criteria_b;
83 | bson_error_t error;
84 |
85 | collection = get_collection(this_);
86 |
87 | criteria_b = encodeToBSON(criteria);
88 |
89 | //Supporting only "name" key
90 | //bson_append_utf8(&criteria_b, "name", 4, criteria[String("name")].toString().c_str(), criteria[String("name")].toString().length());
91 |
92 | bool ret = mongoc_collection_delete(collection, MONGOC_DELETE_NONE, &criteria_b, NULL, &error);
93 |
94 | mongoc_collection_destroy (collection);
95 | bson_destroy(&criteria_b);
96 |
97 | return ret;
98 | /*
99 | bool mongoc_collection_delete (mongoc_collection_t *collection,
100 | mongoc_delete_flags_t flags,
101 | const bson_t *selector,
102 | const mongoc_write_concern_t *write_concern,
103 | bson_error_t *error);
104 | */
105 | }
106 |
107 |
108 | /*
109 | public function update(array $criteria,
110 | array $new_object,
111 | array $options = array()): mixed;
112 | */
113 | static Variant HHVM_METHOD(MongoCollection, update, Array criteria, Array new_object, Array options) {
114 | mongoc_collection_t *collection;
115 | bson_t selector; //selector is the criteria (which document to update)
116 | bson_t update; //update is the new_object containing the new data
117 | bson_error_t error;
118 |
119 | collection = get_collection(this_);
120 |
121 | selector = encodeToBSON(criteria);
122 | update = encodeToBSON(new_object);
123 |
124 | //Read oid and name from criteria array
125 | // bson_init(&selector);
126 | // bson_oid_init_from_string(&oid, criteria[String("_id")].toString().c_str());
127 | // bson_append_oid(&selector, "_id", 3, &oid);
128 | // bson_append_utf8(&selector, "name", 4, criteria[String("name")].toString().c_str(), criteria[String("name")].toString().length());
129 |
130 | //Convert new_object to bson
131 | //Hard coded test for now
132 | // bson_init(&update);
133 | // BSON_APPEND_INT32 (&update, "abcd", 1);
134 | // BSON_APPEND_INT32 (&update, "$hi", 1);
135 |
136 | bool ret = mongoc_collection_update(collection, MONGOC_UPDATE_NONE, &selector, &update, NULL, &error);
137 |
138 | mongoc_collection_destroy (collection);
139 |
140 | bson_destroy(&update);
141 | bson_destroy(&selector);
142 |
143 | return ret;
144 |
145 | /*
146 | bool
147 | mongoc_collection_update (mongoc_collection_t *collection,
148 | mongoc_update_flags_t flags,
149 | const bson_t *selector,
150 | const bson_t *update,
151 | const mongoc_write_concern_t *write_concern,
152 | bson_error_t *error)
153 | */
154 | }
155 |
156 |
157 | ////////////////////////////////////////////////////////////////////////////////
158 |
159 | void MongoExtension::_initMongoCollectionClass() {
160 | HHVM_ME(MongoCollection, insert);
161 | HHVM_ME(MongoCollection, remove);
162 | HHVM_ME(MongoCollection, update);
163 | }
164 |
165 | } // namespace HPHP
166 |
--------------------------------------------------------------------------------
/src/MongoCollection.php:
--------------------------------------------------------------------------------
1 | >
39 | public function insert(mixed $a,
40 | array $options = array()): mixed;
41 |
42 | /**
43 | * Remove records from this collection
44 | *
45 | * @param array $criteria - criteria Description of records to
46 | * remove.
47 | * @param array $options - options Options for remove. "justOne"
48 | * Remove at most one record matching this criteria.
49 | *
50 | * @return bool|array - Returns an array containing the status of the
51 | * removal if the "w" option is set. Otherwise, returns TRUE.
52 | */
53 | <<__Native>>
54 | public function remove(array $criteria = array(),
55 | array $options = array()): mixed;
56 |
57 | /**
58 | * Update records based on a given criteria
59 | *
60 | * @param array $criteria - criteria Description of the objects to
61 | * update.
62 | * @param array $new_object - new_object The object with which to
63 | * update the matching records.
64 | * @param array $options - options
65 | *
66 | * @return bool|array - Returns an array containing the status of the
67 | * update if the "w" option is set. Otherwise, returns TRUE.
68 | */
69 | <<__Native>>
70 | public function update(array $criteria,
71 | array $new_object,
72 | array $options = array()): mixed;
73 |
74 |
75 | private function getFullName(): string {
76 | return $this->db . "." . $this->name;
77 | }
78 | /**
79 | * Perform an aggregation using the aggregation framework
80 | *
81 | * @param array $pipeline -
82 | * @param array $op -
83 | * @param array $... -
84 | *
85 | * @return array - The result of the aggregation as an array. The ok
86 | * will be set to 1 on success, 0 on failure.
87 | */
88 | public function aggregate(array $pipeline): array {
89 | $cmd = [
90 | 'aggregate' => $this->name,
91 | 'pipeline' => $pipeline
92 | ];
93 | return $this->db->command($cmd);
94 | }
95 |
96 | /**
97 | * Inserts multiple documents into this collection
98 | *
99 | * @param array $a - a An array of arrays or objects. If any objects
100 | * are used, they may not have protected or private properties. If
101 | * the documents to insert do not have an _id key or property, a new
102 | * MongoId instance will be created and assigned to it.
103 | * @param array $options - options Options for the inserts.
104 | *
105 | * @return mixed - If the w parameter is set to acknowledge the write,
106 | * returns an associative array with the status of the inserts ("ok")
107 | * and any error that may have occurred ("err"). Otherwise, returns
108 | * TRUE if the batch insert was successfully sent, FALSE otherwise.
109 | */
110 | public function batchInsert(array $a,
111 | array $options = array()): mixed {
112 | $results = array();
113 |
114 | foreach ($a as $doc) {
115 | $results[] = $this->insert($doc, $options);
116 | }
117 |
118 | return $results;
119 | }
120 |
121 |
122 | public function __construct(MongoDB $db, string $name) {
123 | $this->db = $db;
124 | $this->name = $name;
125 | }
126 |
127 | /**
128 | * Counts the number of documents in this collection
129 | *
130 | * @param array $query - query Associative array or object with
131 | * fields to match.
132 | * @param int $limit - limit Specifies an upper limit to the number
133 | * returned.
134 | * @param int $skip - skip Specifies a number of results to skip
135 | * before starting the count.
136 | *
137 | * @return int - Returns the number of documents matching the query.
138 | */
139 | public function count(array $query = array(),
140 | int $limit,
141 | int $skip): int {
142 | $cmd = [
143 | 'count' => $this->name,
144 | 'query' => $query,
145 | 'limit' => $limit,
146 | 'skip' => $skip
147 | ];
148 | $cmd_result = $this->db->command($cmd);
149 | if (!$cmd_result["ok"]) {
150 | throw new MongoCursorException();
151 | }
152 | return $cmd_result["n"];
153 | }
154 |
155 | /**
156 | * Creates a database reference
157 | *
158 | * @param mixed $document_or_id - document_or_id If an array or
159 | * object is given, its _id field will be used as the reference ID. If
160 | * a MongoId or scalar is given, it will be used as the reference ID.
161 | *
162 | * @return array - Returns a database reference array. If an array
163 | * without an _id field was provided as the document_or_id parameter,
164 | * NULL will be returned.
165 | */
166 | public function createDBRef(string $collection,
167 | mixed $document_or_id): array {
168 | if (is_array($document_or_id)) {
169 | $id = $document_or_id['_id'];
170 | } else {
171 | $id = $document_or_id;
172 | }
173 | return MongoDBRef::create($this->name, $id, $this->db->__getDBName());
174 | }
175 |
176 | /**
177 | * Deletes an index from this collection
178 | *
179 | * @param string|array $keys - keys Field or fields from which to
180 | * delete the index.
181 | *
182 | * @return array - Returns the database response.
183 | */
184 | public function deleteIndex(mixed $keys): array {
185 | $index = $this->toIndexString($keys);
186 | return $this->db->command(array("deleteIndexes" => $this->getName(), "index" => $index));
187 | }
188 |
189 | /**
190 | * Delete all indices for this collection
191 | *
192 | * @return array - Returns the database response.
193 | */
194 | public function deleteIndexes(): array {
195 | return $this->deleteIndex("*");
196 | }
197 |
198 | /**
199 | * Retrieve a list of distinct values for the given key across a collection.
200 | *
201 | * @param string $key -
202 | * @param array $query -
203 | *
204 | * @return array - Returns an array of distinct values,
205 | */
206 | public function distinct(string $key,
207 | array $query): array {
208 | return $this->db->command(array("distinct" => $this->name, "key" => $key, "query" => $query));
209 | }
210 |
211 | /**
212 | * Drops this collection
213 | *
214 | * @return array - Returns the database response.
215 | */
216 | public function drop(): array {
217 | return $this->db->command(array("drop" => $this->name));
218 | }
219 |
220 | /**
221 | * Creates an index on the given field(s), or does nothing if the index
222 | * already exists
223 | *
224 | *
225 | * @param string|array $key|keys -
226 | * @param array $options - options This parameter is an associative
227 | * array of the form array("optionname" => boolean, ...).
228 | *
229 | * @return bool - Returns an array containing the status of the index
230 | * creation if the "w" option is set. Otherwise, returns TRUE.
231 | */
232 | public function ensureIndex(mixed $key,
233 | array $options = array()): bool {
234 | $indexName = $this->toIndexString($key);
235 | $client = $this->db->__getClient();
236 |
237 | // check client server version, set newer to true if 2.6+
238 | $version = $client->getServerVersion();
239 | $newer = false;
240 | if (intval($version[0]) > 2) {
241 | $newer = true;
242 | } else if (intval($version[0]) == 2) {
243 | if (intval($version[2]) >= 6) {
244 | $newer = true;
245 | }
246 | }
247 |
248 | // indexOptions Object
249 | $indexOptions = array("key" => $key,
250 | "name" => $indexName,
251 | "ns" => $this->name);
252 |
253 | $option_names = ["background", "unique", "dropDups", "sparse",
254 | "expireAfterSeconds", "v", "weights",
255 | "default_language", "language_override"];
256 | foreach ($option_names as $opt) {
257 | if(isset($options[$opt])) {
258 | $indexOptions[$opt] = $options[$opt];
259 | }
260 | }
261 |
262 |
263 | // if server version >= 2.6, can run database command
264 | if ($newer) {
265 | $out = $this->db->command(array("createIndexes" => $this->name,
266 | "indexes" => $indexOptions));
267 | } else {
268 | $out = $this->db->selectCollection("system.indexes")->insert($indexOptions, 0, true);
269 | }
270 | return $out;
271 | }
272 |
273 | /**
274 | * Queries this collection, returning a
275 | * for the result set
276 | *
277 | * @param array $query - query The fields for which to search.
278 | * @param array $fields - fields Fields of the results to return.
279 | * The array is in the format array('fieldname' => true, 'fieldname2'
280 | * => true). The _id field is always returned.
281 | *
282 | * @return MongoCursor - Returns a cursor for the search results.
283 | */
284 | public function find(array $query = array(),
285 | array $fields = array()): MongoCursor {
286 | $ns = $this->getFullName();
287 | return new MongoCursor($this->db->__getClient(), $ns, $query, $fields);
288 | }
289 |
290 | /**
291 | * Update a document and return it
292 | *
293 | * @param array $query -
294 | * @param array $update -
295 | * @param array $fields -
296 | * @param array $options -
297 | *
298 | * @return array - Returns the original document, or the modified
299 | * document when new is set.
300 | */
301 | public function findAndModify(array $query,
302 | array $update,
303 | array $fields,
304 | array $options): array {
305 | $cmd = array( "findAndModify" => $this->name,
306 | "query" => $query,
307 | "update" => $update,
308 | "fields" => $fields);
309 | $opts = ["new", "upsert", "sort"];
310 | foreach ($opts as $option) {
311 | if(isset($options[$option])) {
312 | $cmd[$option] = $options[$option];
313 | }
314 | }
315 | $out = $this->db->command($cmd);
316 | return $out["value"];
317 | }
318 |
319 | /**
320 | * Queries this collection, returning a single element
321 | *
322 | * @param array $query - query The fields for which to search.
323 | * @param array $fields - fields Fields of the results to return.
324 | * The array is in the format array('fieldname' => true, 'fieldname2'
325 | * => true). The _id field is always returned.
326 | *
327 | * @return array - Returns record matching the search or NULL.
328 | */
329 | public function findOne(array $query = array(),
330 | array $fields = array()): array {
331 | $cursor = $this->find($query, $fields);
332 | $cursor = $cursor->limit(-1);
333 | $cursor->rewind(); // TODO: Need to remove later
334 | //var_dump($cursor->current());
335 | return $cursor->current();
336 | }
337 |
338 | /**
339 | * Gets a collection
340 | *
341 | * @param string $name - name The next string in the collection
342 | * name.
343 | *
344 | * @return MongoCollection - Returns the collection.
345 | */
346 | public function __get(string $name): MongoCollection {
347 | return $this->db->selectCollection($name);
348 | }
349 |
350 | /**
351 | * Fetches the document pointed to by a database reference
352 | *
353 | * @param array $ref - ref A database reference.
354 | *
355 | * @return array - Returns the database document pointed to by the
356 | * reference.
357 | */
358 | public function getDBRef(array $ref): array {
359 | return MongoDBRef::get($this->db, $ref);
360 | }
361 |
362 | /**
363 | * Returns information about indexes on this collection
364 | *
365 | * @return array - This function returns an array in which each element
366 | * describes an index. Elements will contain the values name for the
367 | * name of the index, ns for the namespace (a combination of the
368 | * database and collection name), and key for a list of all fields in
369 | * the index and their ordering.
370 | */
371 | public function getIndexInfo(): array {
372 | return $this->db->selectCollection("system.indexes")->findOne(array("ns" => $this->getFullName()));
373 | }
374 |
375 | /**
376 | * Returns this collections name
377 | *
378 | * @return string - Returns the name of this collection.
379 | */
380 | public function getName(): string {
381 | return $this->name;
382 | }
383 |
384 | /**
385 | * Get the read preference for this collection
386 | *
387 | * @return array -
388 | */
389 | public function getReadPreference(): array {
390 | return $this->read_preference;
391 | }
392 |
393 | /**
394 | * Get slaveOkay setting for this collection
395 | *
396 | * @return bool - Returns the value of slaveOkay for this instance.
397 | */
398 | public function getSlaveOkay(): bool {
399 | return $this->slaveOkay;
400 | }
401 |
402 | /** TODO: Make sure MongoCode works?
403 | * Performs an operation similar to SQL's GROUP BY command
404 | *
405 | * @param mixed $keys - keys Fields to group by. If an array or
406 | * non-code object is passed, it will be the key used to group results.
407 | * @param array $initial - initial Initial value of the aggregation
408 | * counter object.
409 | * @param mongocode $reduce - reduce A function that takes two
410 | * arguments (the current document and the aggregation to this point)
411 | * and does the aggregation.
412 | * @param array $options - options Optional parameters to the group
413 | * command.
414 | *
415 | * @return array - Returns an array containing the result.
416 | */
417 | public function group(mixed $keys,
418 | array $initial,
419 | MongoCode $reduce,
420 | array $options = array()): array {
421 | $group = array( '$reduce' => $reduce,
422 | 'initial' => $initial);
423 | if(get_class($keys) == "MongoCode") {
424 | $group['$keyf'] = $keys;
425 | }
426 | else {
427 | $group['key'] = $keys;
428 | }
429 |
430 | $cmd = array('group' => $group);
431 | $ret = $this->db->runCommand($cmd);
432 | if (!$ret["ok"]) {
433 | throw MongoResultException("Group command failed");
434 | }
435 |
436 | return $ret;
437 | }
438 |
439 |
440 |
441 | /**
442 | * Saves a document to this collection
443 | *
444 | * @param array|object $a - a Array or object to save. If an object
445 | * is used, it may not have protected or private properties. If the
446 | * parameter does not have an _id key or property, a new MongoId
447 | * instance will be created and assigned to it.
448 | * @param array $options - options Options for the save.
449 | *
450 | * @return mixed - If w was set, returns an array containing the status
451 | * of the save. Otherwise, returns a boolean representing if the array
452 | * was not empty (an empty array will not be inserted).
453 | */
454 | public function save(mixed $a,
455 | array $options = array()): mixed {
456 | if (is_array($a)) {
457 | if (!isset($a["_id"])) {
458 | $a["_id"] = new MongoId();
459 | return $this->insert($a);
460 | }
461 | return $this->update(array("_id" => $a["_id"]), $a);
462 | }
463 | throw new Exception("Saving objects not implemented");
464 | }
465 |
466 | /**
467 | * Set the read preference for this collection
468 | *
469 | * @param string $read_preference -
470 | * @param array $tags -
471 | *
472 | * @return bool -
473 | */
474 | public function setReadPreference(string $read_preference,
475 | array $tags): bool {
476 | $this->read_preference['type'] = $read_preference;
477 | $this->read_preference['tagsets'] = $tags;
478 | return true;
479 | }
480 |
481 | /**
482 | * Change slaveOkay setting for this collection
483 | *
484 | * @param bool $ok - ok If reads should be sent to secondary members
485 | * of a replica set for all possible queries using this MongoCollection
486 | * instance.
487 | *
488 | * @return bool - Returns the former value of slaveOkay for this
489 | * instance.
490 | */
491 | public function setSlaveOkay(bool $ok = true): bool {
492 | $former = $this->read_preference["type"];
493 | if ($ok) {
494 | $this->read_preference["type"] = MongoClient::RP_PRIMARY;
495 | } else {
496 | $this->read_preference["type"] = MongoClient::RP_SECONDARY_PREFERRED;
497 | }
498 | $this->read_preference["type"] = $ok;
499 | return ($former != MongoClient::RP_PRIMARY);
500 | }
501 |
502 | /**DEPRECATED
503 | * TODO: Enforce only scalar value types
504 | * Converts keys specifying an index to its identifying string
505 | *
506 | * @param mixed $keys - keys Field or fields to convert to the
507 | * identifying string
508 | *
509 | * @return string - Returns a string that describes the index.
510 | */
511 | static protected function toIndexString(mixed $keys): string {
512 | $str = "";
513 |
514 | if (gettype($keys) == "array") {
515 |
516 | foreach ($keys as $key => $val) {
517 | // order must be ascending (1) for boolean field
518 | if (gettype($val) == "boolean")
519 | $val = 1;
520 |
521 | if ($key === key($keys)) {
522 | $str .= $key . "_" . $val;
523 | }
524 | else {
525 | $str .= "_" . $key . "_" . $val;
526 | }
527 | }
528 | } else {
529 | // if $keys is just a string, append _1 as value
530 | $str .= $keys . "_1";
531 | }
532 | return $str;
533 | }
534 |
535 | /**
536 | * String representation of this collection
537 | *
538 | * @return string - Returns the full name of this collection.
539 | */
540 | public function __toString(): string {
541 | return $this->getFullName();
542 | }
543 |
544 | /**
545 | * Validates this collection
546 | *
547 | * @param bool $scan_data - scan_data Only validate indices, not the
548 | * base collection.
549 | *
550 | * @return array - Returns the databases evaluation of this object.
551 | */
552 | public function validate(bool $scan_data = false): array {
553 | $cmd = array("validate" => $this->name);
554 | if ($scan_data) {
555 | $cmd["full"] = true;
556 | }
557 |
558 | return $this->db->command($cmd);
559 | }
560 | }
--------------------------------------------------------------------------------
/src/MongoCursor.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "ext_mongo.h"
3 | #include "bson_decode.h"
4 | #include "contrib/encode.h"
5 |
6 | namespace HPHP {
7 |
8 | ////////////////////////////////////////////////////////////////////////////////
9 | // class MongoCursor
10 |
11 | static void HHVM_METHOD(MongoCursor, rewind);
12 |
13 | static Variant HHVM_METHOD(MongoCursor, current) {
14 | bool started = this_->o_realProp("started_iterating", ObjectData::RealPropUnchecked, "MongoCursor")->toBoolean();
15 | if (!started)
16 | {
17 | return init_null_variant;
18 | }
19 |
20 | mongoc_cursor_t *cursor = get_cursor(this_)->get();
21 | const bson_t *doc;
22 |
23 | doc = mongoc_cursor_current(cursor);
24 | if (doc) {
25 | auto ret = cbson_loads(doc);
26 | return ret;
27 | } else {
28 | return init_null_variant;
29 | }
30 | }
31 |
32 | static bool HHVM_METHOD(MongoCursor, hasNext) {
33 | bson_error_t error;
34 | mongoc_cursor_t *cursor = get_cursor(this_)->get();
35 |
36 | bool ret = mongoc_cursor_more(cursor);
37 | if (mongoc_cursor_error (cursor, &error)) {
38 | mongoThrow((const char *)error.message);
39 | }
40 | return ret;
41 | }
42 |
43 | static void HHVM_METHOD(MongoCursor, next) {
44 | const bson_t *doc;
45 |
46 | bool started = this_->o_realProp("started_iterating", ObjectData::RealPropUnchecked, "MongoCursor")->toBoolean();
47 | if (!started)
48 | {
49 | HHVM_MN(MongoCursor, rewind)(this_);
50 | }
51 |
52 | mongoc_cursor_t *cursor = get_cursor(this_)->get();
53 | // if (!mongoc_cursor_next (cursor, &doc)) {
54 | // if (mongoc_cursor_error (cursor, &error)) {
55 | // throw FatalErrorException(error.message);
56 | // }
57 | // }
58 | mongoc_cursor_next (cursor, &doc); //Note: error would be catched by valid()
59 |
60 | auto at = this_->o_realProp("at", ObjectData::RealPropUnchecked, "MongoCursor")->toInt64();
61 | this_->o_set("at", at + 1, "MongoCursor");
62 | }
63 |
64 | static void HHVM_METHOD(MongoCursor, reset) {
65 | if (get_cursor(this_)) {
66 | get_cursor(this_)->~MongocCursor();
67 | this_->o_set(s_mongoc_cursor, init_null_variant, "MongoCursor");
68 | }
69 |
70 | this_->o_set("at", 0, "MongoCursor");
71 | this_->o_set("started_iterating", false_varNR, "MongoCursor");
72 | }
73 |
74 | static void HHVM_METHOD(MongoCursor, rewind) {
75 | HHVM_MN(MongoCursor, reset)(this_);
76 |
77 | //TODO: need to test with null value
78 | auto connection = this_->o_realProp("connection", ObjectData::RealPropUnchecked, "MongoCursor")->toObject();
79 | auto ns = this_->o_realProp("ns", ObjectData::RealPropUnchecked, "MongoCursor")->toString();
80 | auto query = this_->o_realProp("query", ObjectData::RealPropUnchecked, "MongoCursor")->toArray();
81 | bson_t query_bs;
82 | query_bs = encodeToBSON(query);
83 |
84 | /*
85 | bson_init(&query_bs);
86 | if (!query->empty()) {
87 | //Currently only supports "name" query
88 | bson_append_utf8(&query_bs, "name", 4, query[String("name")].toString().c_str(), query[String("name")].toString().length());
89 |
90 |
91 | }
92 | */
93 |
94 | //Parameters and their types:
95 | //static void HHVM_METHOD(MongoCursor, __construct, const Object& connection, const String& ns, const Array& query, const Array& fields)
96 |
97 | /*
98 | MongocCursor(mongoc_client_t *client,
99 | const char *db_and_collection,
100 | mongoc_query_flags_t flags,
101 | uint32_t skip,
102 | uint32_t limit,
103 | uint32_t batch_size,
104 | bool is_command,
105 | const bson_t *query,
106 | const bson_t *fields,
107 | const mongoc_read_prefs_t *read_prefs);
108 |
109 | */
110 |
111 | //MongocCursor *cursor = new MongocCursor(get_client(connection)->get(), ns.c_str(), MONGOC_QUERY_NONE, 0, 0, 0, false, &query_bs, NULL, NULL);
112 | //std::cout << "Got past cursor construction with" << ns.c_str() << std::endl;
113 |
114 | /* fields needed:
115 |
116 | private $flags = []; //TODO: implement this
117 | private $skip = 0;
118 | private $limit = 0;
119 | private $batchSize = 100;
120 | private $fields = [];
121 | private $read_preference = [];
122 |
123 | */
124 |
125 | bson_t fields_bs;
126 | mongoc_read_prefs_t *read_prefs;
127 | bson_t read_prefs_tags_bs;
128 |
129 | auto flags_array = this_->o_realProp("flags", ObjectData::RealPropUnchecked, "MongoCursor")->toArray();
130 | int flags = MONGOC_QUERY_NONE;
131 |
132 | if (flags_array->exists((int64_t)0)) { flags |= MONGOC_QUERY_NONE;}
133 | if (flags_array->exists((int64_t)1)) { flags = (flags | MONGOC_QUERY_TAILABLE_CURSOR);}
134 | if (flags_array->exists((int64_t)2)) { flags = (flags | MONGOC_QUERY_SLAVE_OK);}
135 | if (flags_array->exists((int64_t)3)) { flags = (flags | MONGOC_QUERY_OPLOG_REPLAY);}
136 | if (flags_array->exists((int64_t)4)) { flags = (flags | MONGOC_QUERY_NO_CURSOR_TIMEOUT);}
137 | if (flags_array->exists((int64_t)5)) { flags = (flags | MONGOC_QUERY_AWAIT_DATA);}
138 | if (flags_array->exists((int64_t)6)) { flags = (flags | MONGOC_QUERY_EXHAUST);}
139 | if (flags_array->exists((int64_t)7)) { flags = (flags | MONGOC_QUERY_PARTIAL);}
140 |
141 | uint32_t skip = this_->o_realProp("skip", ObjectData::RealPropUnchecked, "MongoCursor")->toInt32();
142 | uint32_t limit = this_->o_realProp("limit", ObjectData::RealPropUnchecked, "MongoCursor")->toInt32();
143 | uint32_t batchSize = this_->o_realProp("batchSize", ObjectData::RealPropUnchecked, "MongoCursor")->toInt32();
144 | auto fields = this_->o_realProp("fields", ObjectData::RealPropUnchecked, "MongoCursor")->toArray();
145 | auto read_prefs_array = this_->o_realProp("read_preference", ObjectData::RealPropUnchecked, "MongoCursor")->toArray();
146 | String read_pref_type = read_prefs_array[String("type")].toString();
147 | Array read_pref_tagsets = read_prefs_array[String("tagsets")].toArray();
148 | read_prefs_tags_bs = encodeToBSON(read_pref_tagsets);
149 | /*
150 | MongoClient::RP_PRIMARY,
151 | MongoClient::RP_PRIMARY_PREFERRED,
152 | MongoClient::RP_SECONDARY,
153 | MongoClient::RP_SECONDARY_PREFERRED,
154 | MongoClient::RP_NEAREST
155 | */
156 | read_prefs = mongoc_read_prefs_new(MONGOC_READ_PRIMARY);
157 |
158 | if (read_pref_type.equal(String("RP_PRIMARY"))) {
159 | mongoc_read_prefs_set_mode(read_prefs, MONGOC_READ_PRIMARY);
160 | } else if (read_pref_type.equal(String("RP_PRIMARY_PREFERRED"))) {
161 | mongoc_read_prefs_set_mode(read_prefs, MONGOC_READ_PRIMARY_PREFERRED);
162 | } else if (read_pref_type.equal(String("RP_SECONDARY"))) {
163 | mongoc_read_prefs_set_mode(read_prefs, MONGOC_READ_SECONDARY);
164 | } else if (read_pref_type.equal(String("RP_SECONDARY_PREFERRED"))) {
165 | mongoc_read_prefs_set_mode(read_prefs, MONGOC_READ_SECONDARY_PREFERRED);
166 | } else if (read_pref_type.equal(String("RP_NEAREST"))) {
167 | mongoc_read_prefs_set_mode(read_prefs, MONGOC_READ_NEAREST);
168 | }
169 | mongoc_read_prefs_set_tags(read_prefs, &read_prefs_tags_bs);
170 |
171 | fields_bs = encodeToBSON(fields);
172 |
173 | MongocCursor *cursor= new MongocCursor( get_client(connection)->get(),
174 | ns.c_str(),
175 | (mongoc_query_flags_t)flags,
176 | skip,
177 | limit,
178 | batchSize,
179 | &query_bs,
180 | &fields_bs,
181 | read_prefs);
182 |
183 | this_->o_set(s_mongoc_cursor, cursor, s_mongocursor);
184 | bson_destroy(&query_bs);
185 | bson_destroy(&fields_bs);
186 | bson_destroy(&read_prefs_tags_bs);
187 |
188 | this_->o_set("started_iterating", true_varNR, "MongoCursor");
189 |
190 | HHVM_MN(MongoCursor, next)(this_);
191 | }
192 |
193 | static bool HHVM_METHOD(MongoCursor, valid) {
194 | auto cur = HHVM_MN(MongoCursor, current)(this_);
195 | return ! cur.isNull();
196 | }
197 |
198 | ////////////////////////////////////////////////////////////////////////////////
199 |
200 | void MongoExtension::_initMongoCursorClass() {
201 | HHVM_ME(MongoCursor, current);
202 | HHVM_ME(MongoCursor, hasNext);
203 | HHVM_ME(MongoCursor, next);
204 | HHVM_ME(MongoCursor, reset);
205 | HHVM_ME(MongoCursor, rewind);
206 | HHVM_ME(MongoCursor, valid);
207 | }
208 |
209 | } // namespace HPHP
210 |
--------------------------------------------------------------------------------
/src/MongoCursor.php:
--------------------------------------------------------------------------------
1 | >
37 | public function current(): ?array;
38 |
39 | /**
40 | * Checks if there are any more elements in this cursor.
41 | * May be hard to do in both php and c++
42 | *
43 | * @return bool - Returns if there is another element.
44 | */
45 | <<__Native>>
46 | public function hasNext(): bool;
47 |
48 | /**
49 | * Advances the cursor to the next result
50 | *
51 | * @return void - NULL.
52 | */
53 | <<__Native>>
54 | public function next(): void;
55 |
56 | /**
57 | * Clears the cursor
58 | *
59 | * @return void - NULL.
60 | */
61 | <<__Native>>
62 | public function reset(): void;
63 |
64 | /**
65 | * Returns the cursor to the beginning of the result set
66 | *
67 | * @return void - NULL.
68 | */
69 | <<__Native>>
70 | public function rewind(): void;
71 |
72 | /**
73 | * Checks if the cursor is reading a valid result.
74 | *
75 | * @return bool - If the current result is not null.
76 | */
77 | <<__Native>>
78 | public function valid(): bool;
79 |
80 |
81 |
82 | //NON-NATIVE FUNCTIONS
83 |
84 | /**
85 | * Adds a top-level key/value pair to a query
86 | *
87 | * @param string $key - key Fieldname to add.
88 | * @param mixed $value - value Value to add.
89 | *
90 | * @return MongoCursor - Returns this cursor.
91 | */
92 | public function addOption(string $key,
93 | mixed $value): MongoCursor {
94 | if ($this->started_iterating) {
95 | throw new MongoCursorException("Tried to add an option after started iterating");
96 | }
97 |
98 | // Make the query object special (i.e. wrap in $query) if necessary
99 | if ( ! $this->isSpecial) {
100 | $this->query['$query'] = $this->query;
101 | $this->isSpecial = true;
102 | }
103 |
104 | $this->query[$key] = $value;
105 | return $this;
106 | }
107 |
108 | /**
109 | * Sets whether this cursor will wait for a while for a tailable cursor to
110 | * return more data
111 | *
112 | * @param bool $wait - wait If the cursor should wait for more data
113 | * to become available.
114 | *
115 | * @return MongoCursor - Returns this cursor.
116 | */
117 | public function awaitData(bool $wait = true): MongoCursor {
118 | $this->wait = $wait;
119 | return $this;
120 | }
121 |
122 | /**
123 | * Limits the number of elements returned in one batch.
124 | *
125 | * @param int $batchSize - batchSize The number of results to return
126 | * per batch. If batchSize is 2 or more, it represents the size of each batch of
127 | * objects retrieved. If batchSize is 1 or negative, it will limit
128 | * of number returned documents to the absolute value of batchSize, and
129 | * the cursor will be closed. The batch size can be changed even
130 | * after a cursor is iterated, in which case the setting will apply on
131 | * the next batch retrieval.
132 | *
133 | * @return MongoCursor - Returns this cursor.
134 | */
135 | public function batchSize(int $batchSize): MongoCursor {
136 | //TODO: Handle non-positive batch size
137 | $this->batchSize = $batchSize;
138 | return $this;
139 | }
140 |
141 | /**
142 | * Create a new cursor
143 | *
144 | * @param mongoclient $connection - connection Database connection.
145 | * @param string $ns - ns Full name of database and collection.
146 | * @param array $query - query Database query.
147 | * @param array $fields - fields Fields to return.
148 | *
149 | * @return - Returns the new cursor.
150 | */
151 |
152 | public function __construct(MongoClient $connection,
153 | string $ns,
154 | array $query = array(),
155 | array $fields = array()) {
156 | $this->connection = $connection;
157 | $this->ns = $ns;
158 | $this->query = $query;
159 | $this->fields = $fields;
160 |
161 | }
162 |
163 | /**
164 | * Counts the number of results for this query
165 | *
166 | * @param bool $foundOnly -
167 | *
168 | * @return int - The number of documents returned by this cursor's
169 | * query.
170 | */
171 | public function count(bool $foundOnly = false): int {
172 | $pieces = explode($this->ns);
173 | $db_name = $pieces[0];
174 | $collection_name = $pieces[1];
175 |
176 | $db = $this->connection->selectDB($db_name);
177 | $query = ["count" => $collection_name];
178 | if ($foundOnly) {
179 | if ($this->limit > 0) {
180 | $query["limit"] = $this->limit;
181 | }
182 | if ($this->skip > 0) {
183 | $query["skip"] = $this->skip;
184 | }
185 | }
186 |
187 | $command_result = $db->command($query);
188 | if (!$command_result["ok"]) {
189 | throw new MongoCursorException();
190 | }
191 | return $command_result["n"];
192 | }
193 |
194 | /**
195 | * Checks if there are documents that have not been sent yet from the
196 | * database for this cursor
197 | *
198 | * @return bool - Returns if there are more results that have not been
199 | * sent to the client, yet.
200 | */
201 | public function dead(): bool {
202 | return $this->dead;
203 | }
204 |
205 | /**
206 | * Return an explanation of the query, often useful for optimization and
207 | * debugging
208 | *
209 | * @return array - Returns an explanation of the query.
210 | */
211 | public function explain(): array {
212 | $this->reset();
213 |
214 | $originalLimit = $this->limit;
215 | $this->limit = abs($this->limit) * -1;
216 | $this->addOption('$explain', true);
217 |
218 | /* TODO: rewinding should not be necessary. Since we previously called
219 | * reset(), we should just have to call next() and have it initialize the
220 | * cursor resource automatically. Since we need to recall rewind() here, we
221 | * have to avoid calling next(), lest we advance past the single result.
222 | */
223 | $this->rewind();
224 |
225 | $retval = $this->current();
226 |
227 | $this->limit = $originalLimit;
228 | unset($this->query['$explain']);
229 | $this->reset();
230 |
231 | return $retval;
232 | }
233 |
234 | /**
235 | * Sets the fields for a query
236 | *
237 | * @param array $f - f Fields to return (or not return).
238 | *
239 | * @return MongoCursor - Returns this cursor.
240 | */
241 | public function fields(array $fields) {
242 | if ($this->started_iterating) {
243 | throw new MongoCursorException("Tried to change fields after started iterating");
244 | }
245 | $this->fields = $fields;
246 | return $this;
247 | }
248 |
249 | /**
250 | * Return the next object to which this cursor points, and advance the
251 | * cursor
252 | *
253 | * @return array - Returns the next object.
254 | */
255 | public function getNext(): ?array {
256 | $this->next();
257 | return $this->current();
258 | }
259 |
260 | /**
261 | * Get the read preference for this query
262 | *
263 | * @return array -
264 | */
265 | public function getReadPreference(): array {
266 | return $this->read_preference;
267 | }
268 |
269 | /**
270 | * Gives the database a hint about the query
271 | *
272 | * @param mixed $index - index Index to use for the query. If a
273 | * string is passed, it should correspond to an index name. If an array
274 | * or object is passed, it should correspond to the specification used
275 | * to create the index (i.e. the first argument to
276 | * MongoCollection::ensureIndex()).
277 | *
278 | * @return MongoCursor - Returns this cursor.
279 | */
280 | public function hint(mixed $index): MongoCursor {
281 |
282 | if (is_object($index)) {
283 | $index = get_object_vars($index);
284 | }
285 |
286 | if (is_array($index)) {
287 | $index = MongoCollection::_toIndexString($index);
288 | }
289 |
290 | $this->addOption('$hint', $index);
291 | return $this;
292 | }
293 |
294 | /**
295 | * Sets whether this cursor will timeout
296 | *
297 | * @param bool $liveForever - liveForever If the cursor should be
298 | * immortal.
299 | *
300 | * @return MongoCursor - Returns this cursor.
301 | */
302 | public function immortal(bool $liveForever = true): MongoCursor {
303 | if ($this->started_iterating) {
304 | throw new MongoCursorException("Tried to add an option after started iterating");
305 | }
306 |
307 | $this->immortal = $liveForever;
308 | return $this;
309 | }
310 |
311 | /**
312 | * Gets the query, fields, limit, and skip for this cursor
313 | *
314 | * @return array - Returns the namespace, limit, skip, query, and
315 | * fields for this cursor.
316 | */
317 | public function info(): array {
318 | $info = [
319 | "ns" => $this->ns,
320 | "limit" => $this->limit,
321 | "batchSize" => $this->batchSize,
322 | "skip" => $this->skip,
323 | "flags" => $this->flags,
324 | "query" => $this->query,
325 | "fields" => $this->fields
326 | ];
327 | return $info;
328 | }
329 |
330 | /**
331 | * Returns the current results _id
332 | *
333 | * @return string - The current results _id as a string.
334 | */
335 | public function key(): mixed {
336 | $current = $this->current();
337 |
338 | if ($current === null) {
339 | return null;
340 | }
341 |
342 | if (is_array($current) && array_key_exists('_id', $current)) {
343 | return (string) $current['_id'];
344 | }
345 |
346 | if (is_object($current) && property_exists($current, '_id')) {
347 | return (string) $current->_id;
348 | }
349 |
350 | return $this->at - 1;
351 | }
352 | /**
353 | * Limits the number of results returned
354 | *
355 | * @param int $num - num The number of results to return.
356 | *
357 | * @return MongoCursor - Returns this cursor.
358 | */
359 | public function limit(int $num) {
360 | if ($this->started_iterating) {
361 | throw new MongoCursorException("Tried to add an option after started iterating");
362 | }
363 | $this->limit = $num;
364 | return $this;
365 | }
366 |
367 | /*
368 | * If this query should fetch partial results from if a shard is down
369 | *
370 | * @param bool $okay - okay If receiving partial results is okay.
371 | *
372 | * @return MongoCursor - Returns this cursor.
373 | */
374 | public function partial(bool $okay = true): MongoCursor {
375 | if ($this->started_iterating) {
376 | throw new MongoCursorException("Tried to add an option after started iterating");
377 | }
378 | $this->partialResultsOK = $okay;
379 | return $this;
380 | }
381 |
382 | /**
383 | * Sets arbitrary flags in case there is no method available the specific
384 | * flag
385 | *
386 | * @param int $flag - flag Which flag to set. You can not set flag 6
387 | * (EXHAUST) as the driver does not know how to handle them. You will
388 | * get a warning if you try to use this flag. For available flags,
389 | * please refer to the wire protocol documentation.
390 | * @param bool $set - set Whether the flag should be set (TRUE) or
391 | * unset (FALSE).
392 | *
393 | * @return MongoCursor - Returns this cursor.
394 | */
395 | public function setFlag(int $flag,
396 | bool $set = true): MongoCursor {
397 | if ($this->started_iterating) {
398 | throw new MongoCursorException("Tried to add an option after started iterating");
399 | }
400 | $this->flags[$flag] = $set;
401 | return $this;
402 | }
403 |
404 | /**
405 | * Set the read preference for this query
406 | *
407 | * @param string $read_preference -
408 | * @param array $tags -
409 | *
410 | * @return MongoCursor - Returns this cursor.
411 | */
412 | public function setReadPreference(string $read_preference,
413 | array $tags): MongoCursor {
414 | if ($this->started_iterating) {
415 | throw new MongoCursorException("Tried to add an option after started iterating");
416 | }
417 | $this->read_preference['type'] = $read_preference;
418 | $this->read_preference['tagsets'] = $tags;
419 | return $this;
420 | }
421 |
422 | /**
423 | * Skips a number of results
424 | *
425 | * @param int $num - num The number of results to skip.
426 | *
427 | * @return MongoCursor - Returns this cursor.
428 | */
429 | public function skip(int $num): MongoCursor {
430 | if ($this->started_iterating) {
431 | throw new MongoCursorException("Tried to add an option after started iterating");
432 | }
433 | $this->skip = $num;
434 | return $this;
435 | }
436 |
437 | /** DEPRECATED
438 | * Sets whether this query can be done on a secondary
439 | *
440 | * @param bool $okay - okay If it is okay to query the secondary.
441 | *
442 | * @return MongoCursor - Returns this cursor.
443 | */
444 | public function slaveOkay(bool $okay = true): MongoCursor {
445 | if ($this->started_iterating) {
446 | throw new MongoCursorException("Tried to add an option after started iterating");
447 | }
448 | $this->slaveOkay = $okay;
449 | return $this;
450 | }
451 |
452 | /**
453 | * Use snapshot mode for the query
454 | *
455 | * @return MongoCursor - Returns this cursor.
456 | */
457 | public function snapshot() {
458 | $this->addOption('$snapshot', true);
459 | return $this;
460 | }
461 |
462 | /**
463 | * Sorts the results by given fields
464 | *
465 | * @param array $fields - fields An array of fields by which to
466 | * sort. Each element in the array has as key the field name, and as
467 | * value either 1 for ascending sort, or -1 for descending sort. Each
468 | * result is first sorted on the first field in the array, then (if it
469 | * exists) on the second field in the array, etc.
470 | *
471 | * @return MongoCursor - Returns the same cursor that this method was
472 | * called on.
473 | */
474 | public function sort(array $fields) {
475 | $this->addOption('$orderby', $fields);
476 | return $this;
477 | }
478 |
479 | /** TODO: Make sure C++ code closes cursor appropriately
480 | * Sets whether this cursor will be left open after fetching the last
481 | * results
482 | *
483 | * @param bool $tail - tail If the cursor should be tailable.
484 | *
485 | * @return MongoCursor - Returns this cursor.
486 | */
487 | public function tailable(bool $tail = true): MongoCursor {
488 | if ($this->started_iterating) {
489 | throw new MongoCursorException("Tried to add an option after started iterating");
490 | }
491 | $this->tailable = $tail;
492 | return $this;
493 | }
494 |
495 | /** TODO: How does query-side timeout work?
496 | * Sets a client-side timeout for this query
497 | *
498 | * @param int $ms -
499 | *
500 | * @return MongoCursor - This cursor.
501 | */
502 | public function timeout(int $ms): MongoCursor {
503 | if ($this->started_iterating) {
504 | throw new MongoCursorException("Tried to add an option after started iterating");
505 | }
506 | $this->timeout = $ms;
507 | return $this;
508 | }
509 |
510 | }
511 |
--------------------------------------------------------------------------------
/src/MongoDB.php:
--------------------------------------------------------------------------------
1 | command(array("getnonce" => 1));
33 |
34 | $saltedHash = md5($nonce["nonce"]."${username}${hash}");
35 |
36 | $result = $this->command(array("authenticate" => 1,
37 | "user" => $username,
38 | "nonce" => $nonce["nonce"],
39 | "key" => $saltedHash));
40 |
41 | return $result;
42 | }
43 |
44 | /**
45 | * Execute a database command
46 | *
47 | * @param array $command - command The query to send.
48 | * @param array $options - options
49 | *
50 | * @return array - Returns database response. Every database response
51 | * is always maximum one document, which means that the result of a
52 | * database command can never exceed 16MB.
53 | */
54 | public function command(array $command,
55 | array $options = array()): array {
56 | //echo "Running command ";
57 | //var_dump($command);
58 | //$coll = $this->selectCollection('$cmd');
59 | //echo "Finished selecting collection ";
60 | return $this->selectCollection('$cmd')->findOne($command);
61 | }
62 |
63 | /**
64 | * Creates a new database
65 | *
66 | * @param mongoclient $conn - MongoClient conn Database connection.
67 | * @param string $name - name Database name.
68 | *
69 | * @return - Returns the database.
70 | */
71 |
72 | public function __construct(MongoClient $conn, string $name) {
73 | $this->client = $conn;
74 | $this->db_name = $name;
75 | }
76 |
77 | /**
78 | * Creates a collection
79 | *
80 | * @param string $name - name The name of the collection.
81 | * @param array $options - options An array containing options for
82 | * the collections. Each option is its own element in the options
83 | * array, with the option name listed below being the key of the
84 | * element.
85 | *
86 | * @return MongoCollection - Returns a collection object representing
87 | * the new collection.
88 | */
89 | public function createCollection(string $name,
90 | array $options = array()): MongoCollection {
91 | $cmd = array("create" => $name);
92 | $option_choices = array("capped", "size", "max", "autoIndexId");
93 | foreach ($option_choices as $op) {
94 | if(isset($options[$op])) {
95 | $cmd[$op] = $options[$op];
96 | }
97 | }
98 | $result = $this->command($cmd);
99 |
100 | if (!$result["ok"]) {
101 | throw new MongoException("Unable to create collection");
102 | }
103 | return new MongoCollection($this, $name);
104 | }
105 |
106 |
107 | /** TODO
108 | * Creates a database reference
109 | *
110 | * @param string $collection - collection The collection to which
111 | * the database reference will point.
112 | * @param mixed $document_or_id - document_or_id If an array or
113 | * object is given, its _id field will be used as the reference ID. If
114 | * a MongoId or scalar is given, it will be used as the reference ID.
115 | *
116 | * @return array - Returns a database reference array. If an array
117 | * without an _id field was provided as the document_or_id parameter,
118 | * NULL will be returned.
119 | */
120 | public function createDBRef(string $collection,
121 | mixed $document_or_id): array {
122 | if (is_array($document_or_id)) {
123 | $id = $document_or_id['_id'];
124 | } else {
125 | $id = $document_or_id;
126 | }
127 | return MongoDBRef::create($collection, $id, $this->db_name);
128 | }
129 |
130 | /**
131 | * Drops this database
132 | *
133 | * @return array - Returns the database response.
134 | */
135 | public function drop(): array {
136 | return $this->command(array("dropDatabase" => 1));
137 | }
138 |
139 | /**
140 | * Drops a collection [deprecated].
141 | *
142 | * @param mixed $coll - coll MongoCollection or name of collection
143 | * to drop.
144 | *
145 | * @return array - Returns the database response.
146 | */
147 | public function dropCollection(mixed $coll): array {
148 | if (is_object($coll)) {
149 | $coll = $coll->getName();
150 | }
151 | return $this->command(array('drop' => $coll));
152 | }
153 |
154 | /**
155 | * Runs JavaScript code on the database server.
156 | *
157 | * @param mixed $code - code MongoCode or string to execute.
158 | * @param array $args - args Arguments to be passed to code.
159 | *
160 | * @return array - Returns the result of the evaluation.
161 | */
162 | public function execute(mixed $code, array $args = array()): array {
163 | return $this->command(array('eval' => $code, 'args' => $args));
164 | }
165 |
166 |
167 | /**
168 | * Creates a database error
169 | *
170 | * @return bool - Returns the database response.
171 | */
172 | public function forceError(): bool {
173 | return $this->command(array('forceerror' => 1));
174 | }
175 |
176 | /**
177 | * Gets a collection
178 | *
179 | * @param string $name - name The name of the collection.
180 | *
181 | * @return MongoCollection - Returns the collection.
182 | */
183 | public function __get(string $name): MongoCollection {
184 | return $this->selectCollection($name);
185 | }
186 |
187 | public function __getDBName(): string {
188 | return $this->db_name;
189 | }
190 |
191 | public function __getClient(): MongoClient {
192 | return $this->client;
193 | }
194 |
195 | /** TODO: Handle system collections
196 | * Get all collections from this database
197 | *
198 | * @param bool $includeSystemCollections -
199 | *
200 | * @return array - Returns the names of the all the collections in the
201 | * database as an array.
202 | */
203 | public function getCollectionNames(bool $includeSystemCollections = false): array {
204 | $allCollections = [];
205 | $db_name_length = strlen($this->db_name) + 1;
206 | $c = $this->selectCollection("system.namespaces")->find();
207 | while ($c->hasNext()) {
208 | $c->next();
209 | $curr_coll = $c->current();
210 | $name = $curr_coll["name"];
211 |
212 | if (strpos("$", $name) >= 0 && strpos(".oplog.$") === false) {
213 | continue;
214 | }
215 |
216 | $allCollections[] = substr($name, $db_name_length);
217 | }
218 | sort($allCollections);
219 | return $allCollections;
220 | }
221 |
222 | /**
223 | * Fetches the document pointed to by a database reference
224 | *
225 | * @param array $ref - ref A database reference.
226 | *
227 | * @return array - Returns the document pointed to by the reference.
228 | */
229 | public function getDBRef(array $ref): array {
230 | return MongoDBRef::get($this, $ref);
231 | }
232 |
233 | /**
234 | * TODO: Make MongoGridFS class
235 | * Fetches toolkit for dealing with files stored in this database
236 | *
237 | * @param string $prefix - prefix The prefix for the files and
238 | * chunks collections.
239 | *
240 | * @return MongoGridFS - Returns a new gridfs object for this database.
241 | */
242 | // <<__Native>>
243 | // public function getGridFS(string $prefix = fs): MongoGridFS;
244 |
245 | /**
246 | * Gets this databases profiling level
247 | *
248 | * @return int - Returns the profiling level.
249 | */
250 | public function getProfilingLevel(): int {
251 | return $this->command(array('profile' => -1));
252 | }
253 |
254 | /**
255 | * Get the read preference for this database
256 | *
257 | * @return array -
258 | */
259 | public function getReadPreference(): array {
260 | return $this->read_preference;
261 | }
262 |
263 | /**
264 | * Get slaveOkay setting for this database
265 | *
266 | * @return bool - Returns the value of slaveOkay for this instance.
267 | */
268 | public function getSlaveOkay(): bool{
269 | return $this->slaveOkay;
270 | }
271 |
272 | /**
273 | * Check if there was an error on the most recent db operation performed
274 | *
275 | * @return array - Returns the error, if there was one.
276 | */
277 | public function lastError(): array {
278 | return $this->command(array('getLastError' => 1));
279 | }
280 |
281 | /** TODO: handle system collections
282 | * Gets an array of all MongoCollections for this database
283 | *
284 | * @param bool $includeSystemCollections -
285 | *
286 | * @return array - Returns an array of MongoCollection objects.
287 | */
288 | public function listCollections(bool $includeSystemCollections = false): array {
289 | $collection_names = $this->getCollectionNames();
290 | foreach ($collection_names as $name) {
291 | if (!isset($this->collections[$name])) {
292 | $this->collections[$name] = new MongoCollection($this, $name);
293 | }
294 | }
295 | return $this->collections;
296 | }
297 |
298 | /** TODO: Will be deprecated soon
299 | * Checks for the last error thrown during a database operation
300 | *
301 | * @return array - Returns the error and the number of operations ago
302 | * it occurred.
303 | */
304 | public function prevError(): array {
305 | return $this->command(array('getPrevError' => 1));
306 | }
307 |
308 | /**
309 | * Repairs and compacts this database
310 | *
311 | * @param bool $preserve_cloned_files - preserve_cloned_files If
312 | * cloned files should be kept if the repair fails.
313 | * @param bool $backup_original_files - backup_original_files If
314 | * original files should be backed up.
315 | *
316 | * @return array - Returns db response.
317 | */
318 | public function repair(bool $preserve_cloned_files = false,
319 | bool $backup_original_files = false): array {
320 | return $this->command(array('repairDatabase' => 1));
321 | }
322 |
323 | /**
324 | * Clears any flagged errors on the database
325 | *
326 | * @return array - Returns the database response.
327 | */
328 | public function resetError(): array {
329 | return $this->command(array('reseterror' => 1));
330 | }
331 |
332 | /**
333 | * Gets a collection
334 | *
335 | * @param string $name - name The collection name.
336 | *
337 | * @return MongoCollection - Returns a new collection object.
338 | */
339 | public function selectCollection(string $name): MongoCollection {
340 | if (!isset($this->collections[$name])) {
341 | $this->collections[$name] = new MongoCollection($this, $name);
342 | }
343 | return $this->collections[$name];
344 | }
345 |
346 | /**
347 | * Sets this databases profiling level
348 | *
349 | * @param int $level - level Profiling level.
350 | *
351 | * @return int - Returns the previous profiling level.
352 | */
353 | public function setProfilingLevel(int $level): int {
354 | return $this->command(array('profile' => $level));
355 | }
356 |
357 | /** TODO: When to return false?
358 | * Set the read preference for this database
359 | *
360 | * @param string $read_preference -
361 | * @param array $tags -
362 | *
363 | * @return bool -
364 | */
365 | public function setReadPreference(string $read_preference,
366 | array $tags): bool {
367 | $this->read_preference['type'] = $read_preference;
368 | $this->read_preference['tagsets'] = $tags;
369 | return true;
370 | }
371 |
372 | /**
373 | * Change slaveOkay setting for this database
374 | *
375 | * @param bool $ok - ok If reads should be sent to secondary members
376 | * of a replica set for all possible queries using this MongoDB
377 | * instance.
378 | *
379 | * @return bool - Returns the former value of slaveOkay for this
380 | * instance.
381 | */
382 | public function setSlaveOkay(bool $ok = true): bool {
383 | $former = $this->slaveOkay;
384 | $this->slaveOkay = $ok;
385 | return $former;
386 | }
387 |
388 | public function setWriteConcern(mixed $w, int $timeout = 10000) {
389 | if( !is_integer($w) || !is_string($w)) {
390 | throw Exception("Invalid argument to set write concern");
391 | }
392 | $this->writeConcern["w"] = $w;
393 | if (!isset($this->writeConcern["timeout"])) {
394 | $this->writeConcern["timeout"] = $timeout;
395 | }
396 | }
397 |
398 | /**
399 | * The name of this database
400 | *
401 | * @return string - Returns this databases name.
402 | */
403 | public function __toString(): string {
404 | return $this->db_name;
405 | }
406 |
407 | }
408 |
--------------------------------------------------------------------------------
/src/bson.cpp:
--------------------------------------------------------------------------------
1 | #include "bson_decode.h"
2 | #include "contrib/encode.h"
3 | #include "ext_mongo.h"
4 | #include
5 | namespace HPHP {
6 |
7 | static String encode(const Variant& mixture) {
8 | bson_t bson = encodeToBSON(mixture);
9 |
10 | const char* output = (const char*) bson_get_data(&bson);
11 | String s = String(output, bson.len, CopyString);
12 | return s;
13 | }
14 |
15 | static Array HHVM_FUNCTION(bson_decode, const String& bson) {
16 | return cbson_loads_from_string(bson);
17 | }
18 |
19 | static String HHVM_FUNCTION(bson_encode, const Variant& value) {
20 | return encode(value);
21 | }
22 |
23 | void MongoExtension::_initBSON() {
24 | HHVM_FE(bson_decode);
25 | HHVM_FE(bson_encode);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/bson.php:
--------------------------------------------------------------------------------
1 | >
4 | function bson_decode(string $bson): array;
5 |
6 | <<__Native>>
7 | function bson_encode(mixed $value): string;
8 |
--------------------------------------------------------------------------------
/src/bson_decode.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "./contrib/classes.h"
3 | #include "hphp/runtime/base/base-includes.h"
4 | #include "bson_decode.h"
5 | #include "ext_mongo.h"
6 |
7 | namespace HPHP {
8 |
9 | // Helper Function to Instantiate Defined Mongo Classes
10 | // Adapted from HNI example
11 | static ObjectData *
12 | create_object(const StaticString * className, Array params)
13 | {
14 | TypedValue ret;
15 | Class * cls = Unit::loadClass(className -> get());
16 | ObjectData * obj = ObjectData::newInstance(cls);
17 | obj->incRefCount();
18 |
19 | g_context->invokeFunc(
20 | &ret,
21 | cls->getCtor(),
22 | params,
23 | obj
24 | );
25 | return obj;
26 | }
27 |
28 | static bool
29 | cbson_loads_visit_double (const bson_iter_t *iter,
30 | const char *key,
31 | double v_double,
32 | void *output)
33 | {
34 | ((Array *) output)->add(String(key),v_double);
35 | return false;
36 | }
37 |
38 | static bool
39 | cbson_loads_visit_utf8 (const bson_iter_t *iter,
40 | const char *key,
41 | size_t v_utf8_len,
42 | const char *v_utf8,
43 | void *output)
44 | {
45 | ((Array *) output)->add(String(key),v_utf8);
46 | return false;
47 | }
48 |
49 | // Pre-declaration so compiler doesn't complain about
50 | // undefined methods
51 | static bool
52 | cbson_loads_visit_document (const bson_iter_t *iter,
53 | const char *key,
54 | const bson_t *v_document,
55 | void *data);
56 |
57 |
58 | static bool
59 | cbson_loads_visit_array (const bson_iter_t *iter,
60 | const char *key,
61 | const bson_t *v_array,
62 | void *data);
63 |
64 | static bool
65 | cbson_loads_visit_binary (const bson_iter_t *iter,
66 | const char *key,
67 | bson_subtype_t v_subtype,
68 | size_t v_binary_len,
69 | const uint8_t *v_binary,
70 | void *output)
71 | {
72 | ObjectData * data = create_object(&s_MongoBinData,
73 | make_packed_array(
74 | String((const char*) v_binary, v_binary_len, CopyString),
75 | (int) v_subtype));
76 | ((Array *) output) -> add(String(key), data);
77 | return false;
78 |
79 | }
80 |
81 | static bool
82 | cbson_loads_visit_oid (const bson_iter_t *iter,
83 | const char *key,
84 | const bson_oid_t *oid,
85 | void *output)
86 | {
87 | char id[25];
88 | bson_oid_to_string(oid, id);
89 | ObjectData * data = create_object(&s_MongoId,
90 | make_packed_array(String(id)));
91 | ((Array *) output)->add(String(key), data);
92 | return false;
93 | }
94 |
95 | static bool
96 | cbson_loads_visit_bool (const bson_iter_t *iter,
97 | const char *key,
98 | bool v_bool,
99 | void *output)
100 | {
101 | ((Array *) output)->add(String(key), v_bool);
102 | return false;
103 | }
104 |
105 | static bool
106 | cbson_loads_visit_date_time (const bson_iter_t *iter,
107 | const char *key,
108 | int64_t msec_since_epoch,
109 | void *output)
110 | {
111 | // Renaming for convenience
112 | int64_t msec = msec_since_epoch;
113 |
114 | ObjectData * data = create_object(&s_MongoDate,
115 | make_packed_array(msec / 1000, (msec % 1000) * 1000));
116 |
117 | ((Array *) output)->add(String(key), data);
118 |
119 | return false;
120 | }
121 |
122 | static bool
123 | cbson_loads_visit_null (const bson_iter_t *iter,
124 | const char *key,
125 | void *output)
126 | {
127 | ((Array *) output)->add(String(key), Variant());
128 | return false;
129 | }
130 |
131 | static bool
132 | cbson_loads_visit_regex (const bson_iter_t *iter,
133 | const char *key,
134 | const char *regex,
135 | const char *options,
136 | void *output)
137 | {
138 | String regex_string = "/" + String(regex) + "/" + String(options);
139 | ObjectData * data = create_object(&s_MongoRegex,
140 | make_packed_array(regex_string));
141 |
142 | ((Array *) output)->add(String(key), data);
143 |
144 | return false;
145 | }
146 |
147 | static bool
148 | cbson_loads_visit_dbpointer (const bson_iter_t *iter,
149 | const char *key,
150 | size_t v_collection_len,
151 | const char *v_collection,
152 | const bson_oid_t *v_oid,
153 | void *output)
154 | {
155 | char id[25];
156 | bson_oid_to_string(v_oid, id);
157 | ObjectData * data = create_object(&s_MongoDBRef,
158 | make_packed_array(
159 | String(v_collection, v_collection_len, CopyString),
160 | String(id)));
161 | ((Array *)output)->add(String(key), data);
162 | return false;
163 | //TODO: Finish this
164 | }
165 |
166 | static bool
167 | cbson_loads_visit_code (const bson_iter_t *iter,
168 | const char *key,
169 | size_t v_code_len,
170 | const char *v_code,
171 | void *output)
172 | {
173 | ObjectData * data = create_object(&s_MongoCode,
174 | make_packed_array(String(v_code, v_code_len, CopyString)));
175 | ((Array *)output)->add(String(key), data);
176 | return false;
177 | }
178 |
179 | static bool
180 | cbson_loads_visit_int32 (const bson_iter_t *iter,
181 | const char *key,
182 | int32_t v_int32,
183 | void *output)
184 | {
185 | ((Array *) output)->add(String(key), v_int32);
186 | return false;
187 | }
188 |
189 | // Not implemented in cbson.c
190 | static bool
191 | cbson_loads_visit_timestamp (const bson_iter_t *iter,
192 | const char *key,
193 | uint32_t timestamp,
194 | uint32_t increment,
195 | void *output)
196 | {
197 | ObjectData * data = create_object(&s_MongoTimestamp,
198 | make_packed_array((int64_t)timestamp, (int64_t)increment));
199 |
200 | ((Array *) output)->add(String(key), data);
201 |
202 | return false;
203 | }
204 |
205 | static bool
206 | cbson_loads_visit_int64 (const bson_iter_t *iter,
207 | const char* key,
208 | int64_t v_int64,
209 | void *output)
210 | {
211 | ((Array *) output)->add(String(key),v_int64);
212 | return false;
213 | }
214 |
215 | static bool
216 | cbson_loads_visit_maxkey (const bson_iter_t *iter,
217 | const char* key,
218 | void *output)
219 | {
220 | ObjectData * data = create_object(&s_MongoMaxKey, Array());
221 | ((Array *)output)->add(String(key), data);
222 | return false;
223 | }
224 |
225 | static bool
226 | cbson_loads_visit_minkey (const bson_iter_t *iter,
227 | const char* key,
228 | void *output)
229 | {
230 | ObjectData * data = create_object(&s_MongoMinKey, Array());
231 | ((Array *)output)->add(String(key), data);
232 | return false;
233 | }
234 |
235 | static const
236 | bson_visitor_t gLoadsVisitors = {
237 | NULL, //TODO: visit before
238 | NULL, //TODO: visit after
239 | NULL, //TODO: visit corrupt
240 | cbson_loads_visit_double,
241 | cbson_loads_visit_utf8,
242 | cbson_loads_visit_document,
243 | cbson_loads_visit_array,
244 | cbson_loads_visit_binary, //TODO: visit binary
245 | NULL, //TODO: visit undefined
246 | cbson_loads_visit_oid,
247 | cbson_loads_visit_bool,
248 | cbson_loads_visit_date_time,
249 | cbson_loads_visit_null,
250 | cbson_loads_visit_regex,
251 | cbson_loads_visit_dbpointer, //TODO: visit dbpointer
252 | cbson_loads_visit_code, //TODO: visit code
253 | NULL, //TODO: visit symbol
254 | NULL, //TODO: visit code with scope
255 | cbson_loads_visit_int32,
256 | cbson_loads_visit_timestamp,
257 | cbson_loads_visit_int64,
258 | cbson_loads_visit_maxkey,
259 | cbson_loads_visit_minkey,
260 | };
261 |
262 | static bool
263 | cbson_loads_visit_document (const bson_iter_t *iter,
264 | const char *key,
265 | const bson_t *v_document,
266 | void *data)
267 | {
268 | bson_iter_t child;
269 | Array * ret = (Array *) data;
270 | Array obj;
271 |
272 | bson_return_val_if_fail(iter, true);
273 | bson_return_val_if_fail(key, true);
274 | bson_return_val_if_fail(v_document, true);
275 |
276 | if (bson_iter_init(&child, v_document))
277 | {
278 | obj = Array();
279 | if (!bson_iter_visit_all(&child, &gLoadsVisitors, &obj))
280 | {
281 | ret->add(String(key), obj);
282 | }
283 | }
284 | return false;
285 | }
286 |
287 | static bool
288 | cbson_loads_visit_array (const bson_iter_t *iter,
289 | const char *key,
290 | const bson_t *v_array,
291 | void *data)
292 | {
293 | bson_iter_t child;
294 | Array * ret = (Array *) data;
295 | Array obj;
296 |
297 | bson_return_val_if_fail(iter, true);
298 | bson_return_val_if_fail(key, true);
299 | bson_return_val_if_fail(v_array, true);
300 |
301 | if (bson_iter_init(&child, v_array))
302 | {
303 | obj = Array();
304 | if (!bson_iter_visit_all(&child, &gLoadsVisitors, &obj))
305 | {
306 | ret->add(String(key), obj);
307 | }
308 | }
309 | return false;
310 | }
311 |
312 | Array
313 | cbson_loads (const bson_t * bson)
314 | {
315 | bson_iter_t iter;
316 |
317 | Array ret = Array();
318 |
319 | if (!bson_iter_init(&iter, bson))
320 | {
321 | mongoThrow("Failed to initialize BSON iterator");
322 | }
323 | bson_iter_visit_all(&iter, &gLoadsVisitors, &ret);
324 |
325 | return ret;
326 | }
327 |
328 |
329 |
330 | Array
331 | cbson_loads_from_string (const String& bson)
332 | {
333 | bson_reader_t * reader;
334 | const bson_t * obj;
335 | bool reached_eof;
336 |
337 | Array output = Array();
338 |
339 | reader = bson_reader_new_from_data((uint8_t *)bson.c_str(), bson.size());
340 |
341 | if (!(obj = bson_reader_read(reader, &reached_eof))) {
342 | mongoThrow("Unexpected end of BSON. Input document is likely corrupted!");
343 | }
344 |
345 | output = cbson_loads(obj);
346 | bson_reader_destroy(reader);
347 |
348 | return output;
349 | }
350 | // Namespace
351 | }
--------------------------------------------------------------------------------
/src/bson_decode.h:
--------------------------------------------------------------------------------
1 | #include "hphp/runtime/base/base-includes.h"
2 | #include
3 |
4 | namespace HPHP {
5 |
6 | Array cbson_loads_from_string(const String& bson);
7 | Array cbson_loads (const bson_t * bson);
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/bson_test:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mongodb-labs/mongo-hhvm-driver-unsupported/f274cf2c789856bf122ff34e563f416b832bd815/src/bson_test
--------------------------------------------------------------------------------
/src/bson_test.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | int main()
5 | {
6 | bson_t b[1];
7 | bson_init( b );
8 | bson_append_int32( b, "int32", 5, 1001);
9 | bson_append_int64( b, "int64", 5, 999999);
10 | bson_append_utf8( b, "string", 6, "test string", 11);
11 | bson_append_bool(b, "boolean", 7, true);
12 | printf("number of keys is %d\n", bson_count_keys(b));
13 | // Array arr = HPHP::cbson_loads(b);
14 | bson_destroy( b );
15 | }
16 |
--------------------------------------------------------------------------------
/src/contrib/classes.h:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2014 Di Máximo Cuadros
2 | *
3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 | *
5 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 | *
7 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 | */
9 | #include "hphp/runtime/base/base-includes.h"
10 | #include
11 |
12 | // Adapted from HNI Source
13 | namespace HPHP {
14 | const StaticString s_MongoDate("MongoDate");
15 | const StaticString s_MongoId("MongoId");
16 | const StaticString s_MongoRegex("MongoRegex");
17 | const StaticString s_MongoTimestamp("MongoTimestamp");
18 | const StaticString s_MongoCode("MongoCode");
19 | const StaticString s_MongoBinData("MongoBinData");
20 | const StaticString s_MongoInt32("MongoInt32");
21 | const StaticString s_MongoInt64("MongoInt64");
22 | const StaticString s_MongoDBRef("MongoDBRef");
23 | const StaticString s_MongoMaxKey("MongoMaxKey");
24 | const StaticString s_MongoMinKey("MongoMinKey");
25 | }
26 |
--------------------------------------------------------------------------------
/src/contrib/encode.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2014 Di Máximo Cuadros
2 | *
3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 | *
5 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 | *
7 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 | */
9 | #include "hphp/runtime/base/base-includes.h"
10 | #include
11 | #include "encode.h"
12 | #include "classes.h"
13 |
14 | namespace HPHP {
15 | void fillBSONWithArray(const Array& value, bson_t* bson) {
16 | for (ArrayIter iter(value); iter; ++iter) {
17 | Variant key(iter.first());
18 | const Variant& data(iter.secondRef());
19 |
20 | variantToBSON(data, key.toString().c_str(), bson);
21 | }
22 | }
23 |
24 | void variantToBSON(const Variant& value, const char* key, bson_t* bson) {
25 | switch(value.getType()) {
26 | case KindOfUninit:
27 | case KindOfNull:
28 | nullToBSON(key, bson);
29 | break;
30 | case KindOfBoolean:
31 | boolToBSON(value.toBoolean(), key, bson);
32 | break;
33 | case KindOfInt64:
34 | int64ToBSON(value.toInt64(), key, bson);
35 | break;
36 | case KindOfDouble:
37 | doubleToBSON(value.toDouble(), key, bson);
38 | break;
39 | case KindOfStaticString:
40 | case KindOfString:
41 | stringToBSON(value.toString(), key, bson);
42 | break;
43 | case KindOfArray:
44 | arrayToBSON(value.toArray(), key, bson);
45 | break;
46 | case KindOfObject:
47 | objectToBSON(value.toObject(), key, bson);
48 | break;
49 | default:
50 | break;
51 | }
52 | }
53 |
54 | void arrayToBSON(const Array& value, const char* key, bson_t* bson) {
55 | bson_t child;
56 | bool isDocument = arrayIsDocument(value);
57 | if (isDocument) {
58 | bson_append_document_begin(bson, key, -1, &child);
59 | } else {
60 | bson_append_array_begin(bson, key, -1, &child);
61 | }
62 |
63 | fillBSONWithArray(value, &child);
64 |
65 | if (isDocument) {
66 | bson_append_document_end(bson, &child);
67 | } else {
68 | bson_append_array_end(bson, &child);
69 | }
70 | }
71 |
72 | void doubleToBSON(const double value,const char* key, bson_t* bson) {
73 | bson_append_double(bson, key, -1, value);
74 | }
75 |
76 | void nullToBSON(const char* key, bson_t* bson) {
77 | bson_append_null(bson, key, -1);
78 | }
79 |
80 | void boolToBSON(const bool value, const char* key, bson_t* bson) {
81 | bson_append_bool(bson, key, -1, value);
82 | }
83 |
84 | void int64ToBSON(const int64_t value, const char* key, bson_t* bson) {
85 | bson_append_int64(bson, key, -1, value);
86 | }
87 |
88 | void stringToBSON(const String& value, const char* key, bson_t* bson) {
89 | bson_append_utf8(bson, key, strlen(key), value.c_str(), -1);
90 | }
91 |
92 |
93 | void objectToBSON(const Object& value, const char* key, bson_t* bson) {
94 | const String& className = value->o_getClassName();
95 |
96 | if (className == s_MongoId) {
97 | mongoIdToBSON(value, key, bson);
98 | } else if (className == s_MongoDate) {
99 | mongoDateToBSON(value, key, bson);
100 | } else if (className == s_MongoRegex) {
101 | mongoRegexToBSON(value, key, bson);
102 | } else if (className == s_MongoTimestamp) {
103 | mongoTimestampToBSON(value, key, bson);
104 | } else if (className == s_MongoCode) {
105 | mongoCodeToBSON(value, key, bson);
106 | } else if (className == s_MongoBinData) {
107 | mongoBinDataToBSON(value, key, bson);
108 | } else if (className == s_MongoInt32) {
109 | mongoInt32ToBSON(value, key, bson);
110 | } else if (className == s_MongoInt64) {
111 | mongoInt64ToBSON(value, key, bson);
112 | } else if (className == s_MongoMaxKey) {
113 | mongoMaxKeyToBSON(key, bson);
114 | } else if (className == s_MongoMinKey) {
115 | mongoMinKeyToBSON(key, bson);
116 | } else {
117 | arrayToBSON(value.toArray(), key, bson);
118 | }
119 | }
120 |
121 | //////////////////////////////////////////////////////////////////////////////
122 |
123 | void mongoTimestampToBSON(const Object& value, const char* key, bson_t* bson) {
124 | bson_append_timestamp(bson, key, -1,
125 | value->o_get("sec").toInt64(),
126 | value->o_get("inc").toInt64()
127 | );
128 | }
129 |
130 | void mongoRegexToBSON(const Object& value, const char* key, bson_t* bson) {
131 | bson_append_regex(bson, key, -1,
132 | value->o_get("regex").toString().c_str(),
133 | value->o_get("flags").toString().c_str()
134 | );
135 | }
136 |
137 | void mongoIdToBSON(const Object& value, const char* key, bson_t* bson) {
138 | bson_oid_t oid;
139 | bson_oid_init_from_string(&oid, value->o_get("$id").toString().c_str());
140 | bson_append_oid(bson, key, -1, &oid);
141 | }
142 |
143 | void mongoDateToBSON(const Object& value, const char* key, bson_t* bson) {
144 | int64_t mili =
145 | (value->o_get("sec").toInt64() * 1000) +
146 | (value->o_get("usec").toInt64() / 1000);
147 |
148 | bson_append_date_time(bson, key, -1, mili);
149 | }
150 |
151 | void mongoCodeToBSON(const Object& value, const char* key, bson_t* bson) {
152 | bson_t child;
153 | bson_init(&child);
154 | fillBSONWithArray(
155 | value->o_get("scope", true, s_MongoCode.get()).toArray(),
156 | &child
157 | );
158 |
159 | bson_append_code_with_scope(bson, key, -1,
160 | value->o_get("code", true, s_MongoCode.get()).toString().c_str(),
161 | &child
162 | );
163 | }
164 |
165 | void mongoBinDataToBSON(const Object& value, const char* key, bson_t* bson) {
166 | const String& binary = value->o_get("bin").toString();
167 |
168 | bson_append_binary(bson, key, -1,
169 | (bson_subtype_t) value->o_get("type").toInt32(),
170 | (const uint8_t*) binary.c_str(),
171 | binary.size()
172 | );
173 | }
174 |
175 | void mongoInt32ToBSON(const Object& value, const char* key, bson_t* bson) {
176 | bson_append_int32(bson, key, -1, value->o_get("value").toInt32());
177 | }
178 |
179 | void mongoInt64ToBSON(const Object& value, const char* key, bson_t* bson) {
180 | bson_append_int64(bson, key, -1, value->o_get("value").toInt64());
181 | }
182 |
183 | void mongoMinKeyToBSON(const char* key, bson_t* bson) {
184 | bson_append_minkey(bson, key, -1);
185 | }
186 |
187 | void mongoMaxKeyToBSON(const char* key, bson_t* bson) {
188 | bson_append_maxkey(bson, key, -1);
189 | }
190 |
191 | //////////////////////////////////////////////////////////////////////////////
192 | //* Objects *//
193 | bool arrayIsDocument(const Array& arr) {
194 | int64_t max_index = 0;
195 |
196 | for (ArrayIter it(arr); it; ++it) {
197 | Variant key = it.first();
198 | if (!key.isNumeric()) {
199 | return true;
200 | }
201 | int64_t index = key.toInt64();
202 | if (index < 0) {
203 | return true;
204 | }
205 | if (index > max_index) {
206 | max_index = index;
207 | }
208 | }
209 |
210 | if (max_index >= arr.size() * 2) {
211 | // Might as well store it as a map
212 | return true;
213 | }
214 |
215 | return false;
216 | }
217 |
218 | bson_t encodeToBSON(const Variant& mixture) {
219 | bson_t bson;
220 | bson_init(&bson);
221 | fillBSONWithArray(mixture.toArray(), &bson);
222 | return bson;
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/src/contrib/encode.h:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2014 Di Máximo Cuadros
2 | *
3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 | *
5 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 | *
7 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 | */
9 | #include "hphp/runtime/base/base-includes.h"
10 | #include
11 |
12 | namespace HPHP {
13 | bool arrayIsDocument(const Array& arr);
14 | void fillBSONWithArray(const Array& value, bson_t* bson);
15 | void stringToBSON(const String& value, const char* key, bson_t* bson);
16 | void arrayToBSON(const Array& value, const char* key, bson_t* bson);
17 | void objectToBSON(const Object& value, const char* key, bson_t* bson);
18 | void variantToBSON(const Variant& value, const char* key, bson_t* bson);
19 | void int64ToBSON(const int64_t value, const char* key, bson_t* bson);
20 | void boolToBSON(const bool value, const char* key, bson_t* bson);
21 | void nullToBSON(const char* key, bson_t* bson);
22 | void doubleToBSON(const double value,const char* key, bson_t* bson);
23 | void mongoDateToBSON(const Object& value, const char* key, bson_t* bson);
24 | void mongoIdToBSON(const Object& value, const char* key, bson_t* bson);
25 | void mongoRegexToBSON(const Object& value, const char* key, bson_t* bson);
26 | void mongoTimestampToBSON(const Object& value, const char* key, bson_t* bson);
27 | void mongoCodeToBSON(const Object& value, const char* key, bson_t* bson);
28 | void mongoBinDataToBSON(const Object& value, const char* key, bson_t* bson);
29 | void mongoInt32ToBSON(const Object& value, const char* key, bson_t* bson);
30 | void mongoInt64ToBSON(const Object& value, const char* key, bson_t* bson);
31 | void mongoMinKeyToBSON(const char* key, bson_t* bson);
32 | void mongoMaxKeyToBSON(const char* key, bson_t* bson);
33 | bson_t encodeToBSON(const Variant& mixture);
34 | }
35 |
--------------------------------------------------------------------------------
/src/encode_draft.cpp:
--------------------------------------------------------------------------------
1 | #include "hphp/runtime/base/base-includes.h"
2 | #include
3 |
4 | namespace HPHP {
5 | void fillBSONWithArray(const Array& value, bson_t* bson) {
6 | for (ArrayIter iter(value); iter; ++iter) {
7 | Variant key(iter.first());
8 | const Variant& data(iter.secondRef());
9 |
10 | variantToBSON(data, key.toString().c_str(), bson);
11 | }
12 | }
13 |
14 | void variantToBSON(const Variant& value, const char* key, bson_t* bson) {
15 | switch(value.getType()) {
16 | case KindOfUninit:
17 | case KindOfNull:
18 | nullToBSON(key, bson);
19 | break;
20 | case KindOfBoolean:
21 | boolToBSON(value.toBoolean(), key, bson);
22 | break;
23 | case KindOfInt64:
24 | int64ToBSON(value.toInt64(), key, bson);
25 | break;
26 | case KindOfDouble:
27 | doubleToBSON(value.toDouble(), key, bson);
28 | break;
29 | case KindOfStaticString:
30 | case KindOfString:
31 | stringToBSON(value.toString(), key, bson);
32 | break;
33 | case KindOfArray:
34 | arrayToBSON(value.toArray(), key, bson);
35 | break;
36 | case KindOfObject:
37 | objectToBSON(value.toObject(), key, bson);
38 | break;
39 | default:
40 | break;
41 | }
42 |
43 |
44 | void int32ToBSON(const int32_t value, const char* key, bson_t* bson) {
45 | bson_append_int32(bson, key, -1, value);
46 | }
47 |
48 | void int64TOBSON(const int64_t value, const char* key, bson_t) {
49 | bson_append_int64(bson, key, -1, value);
50 | }
51 |
52 | void boolTOBSON(const bool value, const char* key, bson_t bson) {
53 | bson_append_bool(bson, key, -1, value);
54 | }
55 |
56 | void doubleTOBSON(const double value, const char* key, bson_t bson) {
57 | bson_append_double(bson, key, -1, value);
58 | }
59 |
60 | void stringTOBSON(const String& value, const char* key, bson_t bson) {
61 | bson_append_utf8(bson, key, strlen(key), value.c_str(), -1);
62 | }
63 |
64 | void nullTOBSON(const char* value, const char* key, bson_t bson) {
65 | bson_append_null(bson, key, -1, value);
66 | }
67 |
68 | void documentTOBSON(const int64_t value, const char* key, bson_t bson) {
69 | bson_t child;
70 | bson_append_document_begin(bson, key, -1, &child);
71 | fillBSONWithArray(value, &child);
72 | bson_append_document_end(bson, &child);
73 | }
74 |
75 | void arrayTOBSON(const Array& value, const char* key, bson_t bson) {
76 | bson_t child;
77 | bson_append_array_begin(bson, key, -1, &child);
78 | fillBSONWithArray(value, &child);
79 | bson_append_array_end(bson, &child);
80 | }
81 |
82 | void oidTOBSON(const Object& value, const char* key, bson_t bson) {
83 | bson_oid_t oid;
84 | bson_oid_init_from_string(&oid, value->o_get("$id").toString().c_str());
85 | bson_append_oid(bson, key, -1, &oid);
86 | }
87 |
88 | void timestampToBSON(const Object& value, const char* key, bson_t* bson) {
89 | bson_append_timestamp(bson, key, -1, value->o_get("sec").toInt64(), value->o_get("inc").toInt64());
90 | }
91 |
92 | void regexToBSON(const Object& value, const char* key, bson_t* bson) {
93 | bson_append_regex(bson, key, -1, value->o_get("regex").toString().c_str(), value->o_get("flags").toString().c_str());
94 | }
95 |
96 | void dateToBSON(const Object& value, const char* key, bson_t* bson) {
97 | int64_t mili =
98 | (value->o_get("sec").toInt64() * 1000) +
99 | (value->o_get("usec").toInt64() / 1000);
100 |
101 | bson_append_date_time(bson, key, -1, mili);
102 | }
103 |
104 | void codeToBSON(const Object& value, const char* key, bson_t* bson) {
105 | bson_t child;
106 | bson_init(&child);
107 | fillBSONWithArray(
108 | value->o_get("scope", true, s_MongoCode.get()).toArray(),
109 | &child
110 | );
111 |
112 | bson_append_code_with_scope(bson, key, -1,
113 | value->o_get("code", true, s_MongoCode.get()).toString().c_str(),
114 | &child
115 | );
116 | }
117 |
118 | void binDataToBSON(const Object& value, const char* key, bson_t* bson) {
119 | const String& binary = value->o_get("bin").toString();
120 |
121 | bson_append_binary(bson, key, -1,
122 | (bson_subtype_t) value->o_get("type").toInt32(),
123 | (const uint8_t*) binary.c_str(),
124 | binary.size()
125 | );
126 | }
127 |
128 | } // end of HPHP namespace
129 |
--------------------------------------------------------------------------------
/src/exceptions/MongoConnectionException.php:
--------------------------------------------------------------------------------
1 | invokeFunc(&dummy, \
23 | cls->getCtor(), \
24 | make_packed_array(arg), \
25 | ret.get()); \
26 | return ret; \
27 | } \
28 | \
29 | private: \
30 | static void initClass() { \
31 | cls = Unit::lookupClass(StringData::Make(#CLS)); \
32 | } \
33 | \
34 | static HPHP::Class* cls; \
35 | };
36 |
37 | MONGO_DEFINE_CLASS(MongoException)
38 | MONGO_DEFINE_CLASS(MongoConnectionException)
39 | MONGO_DEFINE_CLASS(MongoCursorException)
40 | MONGO_DEFINE_CLASS(MongoCursorTimeoutException)
41 | MONGO_DEFINE_CLASS(MongoDuplicateKeyException)
42 | MONGO_DEFINE_CLASS(MongoExecutionTimeoutException)
43 | MONGO_DEFINE_CLASS(MongoGridFSException)
44 | MONGO_DEFINE_CLASS(MongoProtocolException)
45 | MONGO_DEFINE_CLASS(MongoResultException)
46 | MONGO_DEFINE_CLASS(MongoWriteConcernException)
47 |
48 | MONGO_DEFINE_CLASS(MongoClient)
49 | MONGO_DEFINE_CLASS(MongoCursor)
50 | MONGO_DEFINE_CLASS(MongoCollection)
51 |
52 | #undef MONGO_DEFINE_CLASS
53 |
54 | template
55 | void mongoThrow(const char* message);
56 |
57 | template
58 | void mongoThrow(const char* message) {
59 | std::string msg(message);
60 | throw T::allocObject(msg);
61 | }
62 |
63 | //////////////////////////////////////////////////////////////////////////////
64 |
65 | class MongoExtension : public Extension {
66 | public:
67 | MongoExtension();
68 | virtual void moduleInit();
69 |
70 | private:
71 | void _initMongoClientClass();
72 | void _initMongoCursorClass();
73 | void _initMongoCollectionClass();
74 | void _initBSON();
75 | };
76 |
77 | } // namespace HPHP
78 |
79 | #endif // incl_HPHP_EXT_MONGO_H_
80 |
--------------------------------------------------------------------------------
/src/mongo_common.cpp:
--------------------------------------------------------------------------------
1 | #include "mongo_common.h"
2 | #include
3 |
4 | namespace HPHP {
5 |
6 | ////////MongocClient
7 |
8 | ////////////////////////////////////////////////////////////////////////////////
9 |
10 | Resource get_client_resource(Object obj) {
11 | auto res = obj->o_realProp(s_mongoc_client, ObjectData::RealPropUnchecked, s_mongoclient);
12 |
13 | if (!res || !res->isResource()) {
14 | return null_resource;
15 | }
16 |
17 | return res->toResource();
18 | }
19 |
20 | MongocClient *get_client(Object obj) {
21 | auto res = get_client_resource(obj);
22 |
23 | return res.getTyped(true, false);
24 | }
25 |
26 | MongocClient *MongocClient::GetPersistent(const String& uri) {
27 | return GetCachedImpl("mongo::persistent_clients", uri);
28 | }
29 |
30 | void MongocClient::SetPersistent(const String& uri, MongocClient *client) {
31 | SetCachedImpl("mongo::persistent_clients", uri, client);
32 | }
33 |
34 | MongocClient *MongocClient::GetCachedImpl(const char *name, const String& uri) {
35 | return dynamic_cast(g_persistentResources->get(name, uri.data()));
36 | }
37 |
38 | void MongocClient::SetCachedImpl(const char *name, const String& uri, MongocClient *client) {
39 | g_persistentResources->set(name, uri.data(), client);
40 | }
41 |
42 | MongocClient::MongocClient(const String &uri) {
43 | m_client = mongoc_client_new(uri.c_str());
44 | }
45 |
46 | MongocClient::~MongocClient() {
47 | if (m_client != nullptr) {
48 | mongoc_client_destroy(m_client);
49 | }
50 | }
51 |
52 | ////////MongocCursor
53 |
54 | ////////////////////////////////////////////////////////////////////////////////
55 |
56 | Resource get_cursor_resource(Object obj) {
57 | auto res = obj->o_realProp(s_mongoc_cursor, ObjectData::RealPropUnchecked, s_mongocursor);
58 |
59 | if (!res || !res->isResource()) {
60 | return null_resource;
61 | }
62 |
63 | return res->toResource();
64 | }
65 |
66 | MongocCursor *get_cursor(Object obj) {
67 | auto res = get_cursor_resource(obj);
68 |
69 | return res.getTyped(true, false);
70 | }
71 |
72 | MongocCursor::MongocCursor(mongoc_client_t *client,
73 | const char *db_and_collection,
74 | mongoc_query_flags_t flags,
75 | uint32_t skip,
76 | uint32_t limit,
77 | uint32_t batch_size,
78 | const bson_t *query,
79 | const bson_t *fields,
80 | const mongoc_read_prefs_t *read_prefs) {
81 | std::string db_name;
82 | std::string collection_name;
83 |
84 | std::string *db_and_collection_str = new std::string(db_and_collection);
85 |
86 | //namespace format: db.collection
87 | size_t dot_pos;
88 | dot_pos = db_and_collection_str->find_first_of( ".", 0 );
89 | db_name = db_and_collection_str->substr( 0, dot_pos );
90 | collection_name = db_and_collection_str->substr( dot_pos+1, std::string::npos );
91 |
92 | mongoc_collection_t *collection;
93 |
94 | collection = mongoc_client_get_collection (client, db_name.c_str(), collection_name.c_str());
95 | m_cursor = mongoc_collection_find (collection,
96 | flags,
97 | skip,
98 | limit,
99 | batch_size,
100 | query,
101 | fields,
102 | read_prefs);
103 | }
104 |
105 | MongocCursor::~MongocCursor() {
106 | if (m_cursor != nullptr) {
107 | mongoc_cursor_destroy (m_cursor);
108 | }
109 | }
110 |
111 | } // namespace HPHP
112 |
--------------------------------------------------------------------------------
/src/mongo_common.h:
--------------------------------------------------------------------------------
1 | #ifndef incl_HPHP_EXT_MONGO_COMMON_H_
2 | #define incl_HPHP_EXT_MONGO_COMMON_H_
3 |
4 | #include "hphp/runtime/base/base-includes.h"
5 | #include "hphp/runtime/base/persistent-resource-store.h"
6 | #include "mongoc.h"
7 | #include "string.h"
8 |
9 | namespace HPHP {
10 |
11 | const StaticString
12 | s_mongoclient("MongoClient"),
13 | s_mongoc_client("__mongoc_client");
14 |
15 | ////////////////////////////////////////////////////////////////////////////////
16 |
17 | class MongocClient : public SweepableResourceData {
18 | public:
19 | static MongocClient *GetPersistent(const String& uri);
20 | static void SetPersistent(const String& uri, MongocClient *client);
21 |
22 | private:
23 | static MongocClient *GetCachedImpl(const char *name, const String& uri);
24 | static void SetCachedImpl(const char *name, const String& uri, MongocClient *client);
25 |
26 | public:
27 | MongocClient(const String& uri);
28 | ~MongocClient();
29 |
30 | CLASSNAME_IS("mongoc client")
31 |
32 | // overriding ResourceData
33 | virtual const String& o_getClassNameHook() const { return classnameof(); }
34 | virtual bool isInvalid() const { return m_client == nullptr; }
35 |
36 | mongoc_client_t *get() { return m_client;}
37 |
38 | private:
39 | mongoc_client_t *m_client;
40 |
41 | };
42 |
43 | MongocClient *get_client(Object obj);
44 |
45 |
46 |
47 |
48 |
49 | const StaticString
50 | s_mongocursor("MongoCursor"),
51 | s_mongoc_cursor("__mongoc_cursor");
52 |
53 | ////////////////////////////////////////////////////////////////////////////////
54 |
55 | class MongocCursor : public SweepableResourceData {
56 | public:
57 | //Reference: https://github.com/mongodb/mongo-c-driver/blob/e6038636bcee5264a264b54afce0b93c39884d97/src/mongoc/mongoc-cursor.c
58 | MongocCursor(mongoc_client_t *client,
59 | const char *db_and_collection,
60 | mongoc_query_flags_t flags,
61 | uint32_t skip,
62 | uint32_t limit,
63 | uint32_t batch_size,
64 | const bson_t *query,
65 | const bson_t *fields,
66 | const mongoc_read_prefs_t *read_prefs);
67 | ~MongocCursor();
68 |
69 | CLASSNAME_IS("mongoc cursor")
70 |
71 | // overriding ResourceData
72 | virtual const String& o_getClassNameHook() const { return classnameof(); }
73 | virtual bool isInvalid() const { return m_cursor == nullptr; }
74 |
75 | mongoc_cursor_t *get() { return m_cursor;}
76 |
77 | void set(mongoc_cursor_t *cursor) {
78 | if (cursor != m_cursor) {
79 | mongoc_cursor_destroy(m_cursor);
80 | m_cursor = cursor;
81 | }
82 | }
83 |
84 | private:
85 | mongoc_cursor_t *m_cursor;
86 |
87 | };
88 |
89 | MongocCursor *get_cursor(Object obj);
90 |
91 | } // namespace HPHP
92 |
93 | #endif // incl_HPHP_EXT_MONGO_COMMON_H_
94 |
--------------------------------------------------------------------------------
/src/types/MongoBinData.php:
--------------------------------------------------------------------------------
1 | bin = $data;
31 | $this->type = $type;
32 | }
33 |
34 | /**
35 | * The string representation of this binary data object.
36 | *
37 | * @return string - Returns the string "Mongo Binary Data". To access
38 | * the contents of a MongoBinData, use the bin field.
39 | */
40 | public function __toString()
41 | {
42 | return "";
43 | }
44 | }
--------------------------------------------------------------------------------
/src/types/MongoCode.php:
--------------------------------------------------------------------------------
1 | code = $code;
22 | $this->scope = $scope;
23 | }
24 |
25 | public function getScope()
26 | {
27 | return $this->scope;
28 | }
29 |
30 | /**
31 | * Returns this code as a string
32 | *
33 | * @return string - This code, the scope is not returned.
34 | */
35 | public function __toString()
36 | {
37 | return $this->code;
38 | }
39 | }
--------------------------------------------------------------------------------
/src/types/MongoDBRef.php:
--------------------------------------------------------------------------------
1 | $collection,
23 | '$id' => $id
24 | ];
25 |
26 | if (isset($database)) {
27 | $ref['$db'] = $database;
28 | }
29 |
30 | return $ref;
31 | }
32 |
33 | /**
34 | * Fetches the object pointed to by a reference
35 | *
36 | * @param mongodb $db - Database to use.
37 | * @param array $ref - Reference to fetch.
38 | *
39 | * @return array - Returns the document to which the reference refers
40 | * or NULL if the document does not exist (the reference is broken).
41 | */
42 | public static function get(MongoDB $db, array $ref) : array
43 | {
44 | if (!isset($ref['$id']) || !isset($ref['$collection'])) {
45 | return;
46 | }
47 | $ns = $ref['$collection'];
48 | $id = $ref['$id'];
49 |
50 | $refdb = null;
51 | if (isset($ref['$db'])) {
52 | $refdb = $ref['$db'];
53 | }
54 |
55 | if (!is_string($ns)) {
56 | throw new MongoException('MongoDBRef::get: $ref field must be a string', 10);
57 | }
58 | if (isset($refdb)) {
59 | if (!is_string($refdb)) {
60 | throw new MongoException('MongoDBRef::get: $db field of $ref must be a string', 11);
61 | }
62 | if ($refdb != (string)$db) {
63 | $db = $db->_getClient()->$refdb;
64 | }
65 | }
66 | $collection = new MongoCollection($db, $ns);
67 | $query = ['_id' => $id];
68 | return $collection->findOne($query);
69 | }
70 |
71 | /**
72 | * Checks if an array is a database reference
73 | *
74 | * @param array $ref - Array to check.
75 | *
76 | * @return bool
77 | */
78 | public static function isRef(array $ref) : bool
79 | {
80 | if (isset($ref['$id']) && isset($ref['collection'])) {
81 | return true;
82 | }
83 |
84 | return false;
85 | }
86 | }
87 |
88 |
--------------------------------------------------------------------------------
/src/types/MongoDate.php:
--------------------------------------------------------------------------------
1 | sec = time();
31 | } else {
32 | $this->sec = $sec;
33 | }
34 |
35 | $this->usec = $usec;
36 | }
37 |
38 | /**
39 | * Returns a string representation of this date
40 | *
41 | * @return string - This date.
42 | */
43 | public function __toString() {
44 | return (string) $this->sec . ' ' . $this->usec;
45 | }
46 | }
--------------------------------------------------------------------------------
/src/types/MongoId.php:
--------------------------------------------------------------------------------
1 | hostname = self::getHostname();
46 | if ($id === null) {
47 | $id = $this->generateId();
48 | } else if (self::isValid($id)) {
49 | $this->disassembleId($id);
50 | } else {
51 | throw new MongoException('Invalid object ID', 19);
52 | }
53 | $this->{'$id'} = $id;
54 | $this->id = $id;
55 | }
56 |
57 | private function generateId()
58 | {
59 | if (null === self::$refInc) {
60 | self::$refInc = (int) mt_rand(0, pow(2, 24));
61 | }
62 |
63 | $this->timestamp = time();
64 | $this->inc = self::$refInc++;
65 | $this->pid = getmypid();
66 |
67 | if ($this->pid > 32768) {
68 | $this->pid = 65536 - $this->pid;
69 | }
70 |
71 | return $this->assembleId();
72 | }
73 |
74 | private function assembleId()
75 | {
76 | $hash = unpack('a3hash', md5($this->hostname, true))['hash'];
77 | $i1 = ($this->inc) & 255;
78 | $i2 = ($this->inc >> 8) & 255;
79 | $i3 = ($this->inc >> 16) & 255;
80 | $binId = pack(
81 | 'Na3vC3',
82 | $this->timestamp,
83 | $hash,
84 | $this->pid,
85 | $i3, $i2, $i1
86 | );
87 |
88 | return bin2hex($binId);
89 | }
90 |
91 | private function disassembleId($id)
92 | {
93 | $vars = unpack('Nts/C3m/vpid/C3i', hex2bin($id));
94 | $this->timestamp = $vars['ts'];
95 | $this->pid = $vars['pid'];
96 | $this->inc = $vars['i3'] | ($vars['i2'] << 8) | ($vars['i1'] << 16);
97 | }
98 |
99 | /**
100 | * Gets the hostname being used for this machine's ids
101 | *
102 | * @return string - Returns the hostname.
103 | */
104 | public static function getHostname()
105 | {
106 | return gethostname();
107 | }
108 |
109 | /**
110 | * Gets the number of seconds since the epoch that this id was created
111 | *
112 | * @return int - Returns the number of seconds since the epoch that
113 | * this id was created. There are only four bytes of timestamp stored,
114 | * so MongoDate is a better choice for storing exact or wide-ranging
115 | * times.
116 | */
117 | public function getTimestamp()
118 | {
119 | return $this->timestamp;
120 | }
121 |
122 | /**
123 | * Gets the process ID
124 | *
125 | * @return int - Returns the PID of the MongoId.
126 | */
127 | public function getPid()
128 | {
129 | return $this->pid;
130 | }
131 |
132 | /**
133 | * Gets the incremented value to create this id
134 | *
135 | * @return int - Returns the incremented value used to create this
136 | * MongoId.
137 | */
138 | public function getInc()
139 | {
140 | return $this->inc;
141 | }
142 |
143 | /**
144 | * Check if a value is a valid ObjectId
145 | *
146 | * @param mixed $value - The value to check for validity.
147 | *
148 | * @return bool - Returns TRUE if value is a MongoId instance or a
149 | * string consisting of exactly 24 hexadecimal characters; otherwise,
150 | * FALSE is returned.
151 | */
152 | public static function isValid($id)
153 | {
154 | return preg_match('/[0-9a-fA-F]{24}/', $id);
155 | }
156 |
157 | /**
158 | * Create a dummy MongoId
159 | *
160 | * @param array $props - Theoretically, an array of properties used to
161 | * create the new id. However, as MongoId instances have no properties,
162 | * this is not used.
163 | *
164 | * @return MongoId - A new id with the value
165 | * "000000000000000000000000".
166 | */
167 | public static function __set_state($props)
168 | {
169 | $id = new self('000000000000000000000000');
170 | foreach($props as $propName => $value) {
171 | $id->{$propName} = $value;
172 | }
173 | $id->id = $id->assembleId();
174 | return $id;
175 | }
176 |
177 | /**
178 | * Returns a hexidecimal representation of this id
179 | *
180 | * @return string - This id.
181 | */
182 | public function __toString()
183 | {
184 | return (string)$this->id;
185 | }
186 | }
--------------------------------------------------------------------------------
/src/types/MongoInt32.php:
--------------------------------------------------------------------------------
1 | value = $value;
21 | }
22 |
23 | /**
24 | * Returns the string representation of this 32-bit integer.
25 | *
26 | * @return string - Returns the string representation of this integer.
27 | */
28 | public function __toString()
29 | {
30 | return $this->value;
31 | }
32 | }
--------------------------------------------------------------------------------
/src/types/MongoInt64.php:
--------------------------------------------------------------------------------
1 | value = $value;
21 | }
22 |
23 | /**
24 | * Returns the string representation of this 64-bit integer.
25 | *
26 | * @return string - Returns the string representation of this integer.
27 | */
28 | public function __toString()
29 | {
30 | return $this->value;
31 | }
32 | }
--------------------------------------------------------------------------------
/src/types/MongoMaxKey.php:
--------------------------------------------------------------------------------
1 | regex = (string)substr($regex, 1, $flagsStart - 1);
32 | $this->flags = (string)substr($regex, $flagsStart + 1);
33 |
34 | if (!$this->regexIsValid($regex)) {
35 | throw new MongoException('invalid regex', MongoException::INVALID_REGEX);
36 | }
37 | }
38 |
39 | public function regexIsValid($regex)
40 | {
41 | return substr_count($regex, '/') >= 2 &&
42 | ((strlen($regex) && @preg_match($regex, null) !== false) || strlen($this->flags));
43 | }
44 |
45 | /**
46 | * A string representation of this regular expression
47 | *
48 | * @return string - This regular expression in the form "/expr/flags".
49 | */
50 | public function __toString()
51 | {
52 | return '/' . $this->regex . '/' . $this->flags;
53 | }
54 | }
--------------------------------------------------------------------------------
/src/types/MongoTimestamp.php:
--------------------------------------------------------------------------------
1 | sec = $sec < 0 ? time() : (int) $sec;
23 | if ($inc < 0) {
24 | $this->inc = self::$globalInc;
25 | self::$globalInc++;
26 | } else {
27 | $this->inc = (int) $inc;
28 | }
29 | }
30 |
31 | /**
32 | * Returns a string representation of this timestamp
33 | *
34 | * @return string - The seconds since epoch represented by this
35 | * timestamp.
36 | */
37 | public function __toString() {
38 | return (string)$this->sec;
39 | }
40 | }
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | DIRNAME=`dirname $0`
4 | REALPATH=`which realpath`
5 | if [ ! -z "${REALPATH}" ]; then
6 | DIRNAME=`realpath ${DIRNAME}`
7 | fi
8 |
9 | #mongoimport --db test --collection cities --file test/cities.json --upsert
10 | mongoimport --db test --collection students --file test/students.json --upsert
11 | #to test the drop method in MongoCollection
12 | mongoimport --db test --collection temp --file test/students.json --upsert
13 |
14 | ${HPHP_HOME}/hphp/hhvm/hhvm \
15 | -vDynamicExtensions.0=${DIRNAME}/mongo.so \
16 | `which phpunit` ${DIRNAME}/test
17 |
18 |
--------------------------------------------------------------------------------
/test/AutoLoader.php:
--------------------------------------------------------------------------------
1 | isDir() && !$file->isLink() && !$file->isDot()) {
15 | // recurse into directories other than a few special ones
16 | self::registerDirectory($file->getPathname());
17 | } elseif (substr($file->getFilename(), -4) === '.php') {
18 | // save the class name / path of a .php file found
19 | $className = substr($file->getFilename(), 0, -4);
20 | AutoLoader::registerClass($className, $file->getPathname());
21 | }
22 | }
23 | }
24 |
25 | public static function registerClass($className, $fileName) {
26 | AutoLoader::$classNames[$className] = $fileName;
27 | }
28 |
29 | public static function loadClass($className) {
30 | if (isset(AutoLoader::$classNames[$className])) {
31 | require_once(AutoLoader::$classNames[$className]);
32 | }
33 | }
34 |
35 | }
36 |
37 | spl_autoload_register(array('AutoLoader', 'loadClass'));
38 |
--------------------------------------------------------------------------------
/test/MongoTestCase.php:
--------------------------------------------------------------------------------
1 | getTestDB()->drop();
14 | }
15 |
16 | protected function tearDown() {
17 | $this->testClient = null;
18 | parent::tearDown();
19 | }
20 |
21 | public function getTestClient() {
22 | if(!$this->testClient) {
23 | $this->testClient = new MongoClient();
24 | }
25 |
26 | return $this->testClient;
27 | }
28 |
29 | public function getTestDB() {
30 | return $this->getTestClient()->selectDB(self::TEST_DB);
31 | }
32 | }
--------------------------------------------------------------------------------
/test/basicTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(true, extension_loaded("mongo"));
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/test/bootstrap.php:
--------------------------------------------------------------------------------
1 | assertEquals(array("x" => "a"), bson_decode($bson));
16 |
17 | //echo "\nTesting oid\n";
18 | $bson = pack('C', 0x07); // byte: oid type
19 | $bson .= pack('a*x', 'id'); // cstring: field name
20 | $bson .= pack('H24', "507f191e810c19729de860ea"); // byte*12: oid value
21 | $bson .= pack('x'); // null byte: document terminator
22 | $bson = pack('V', 4 + strlen($bson)) . $bson; // int32: document length
23 |
24 | $expected = array("id" => new MongoId("507f191e810c19729de860ea"));
25 | $this->assertEquals($expected, bson_decode($bson));
26 |
27 | //echo "\nTesting nested docs\n";
28 | $inner_doc = $bson;
29 | $inner_expected = $expected;
30 | $bson = pack('C', 0x03); // byte: document type
31 | $bson .= pack('a*x', 'doc'); // cstring: field name
32 | $bson .= $inner_doc; // an inner document
33 | $bson .= pack('x'); // null byte: document terminator
34 | $bson = pack('V', 4 + strlen($bson)) . $bson; // int32: document length
35 | //assert(bson_decode($bson) == var_dump(array(["x"] => "")));
36 | $expected = array("doc" => $inner_expected);
37 | $this->assertEquals($expected, bson_decode($bson));
38 |
39 | /*echo "\nTesting datetime\n";
40 | $bson = pack('C', 0x09); // byte: datetime type
41 | $bson .= pack('a*x', 'date'); // cstring: field name
42 | $bson .= pack('H16', "0123456789abcde0");
43 | $bson .= pack('x'); // null byte: document terminator
44 | $bson = pack('V', 4 + strlen($bson)) . $bson; // int32: document length
45 | //assert(bson_decode($bson) == var_dump(array(["x"] => "")));
46 | $date = new MongoDate();
47 | printf("Date: %s", $date);
48 | $expected = array("date" => $date);
49 | $this->assertEquals($expected, bson_decode($bson));*/
50 |
51 |
52 | //echo "\nTesting string type\n";
53 | $bson = pack('C', 0x02); // byte: string type
54 | $bson .= pack('a*x', 'x'); // cstring: field name
55 | $bson .= pack('V', 0); // int32: string length (invalid)
56 | $bson .= pack('a*x', ''); // cstring: string value
57 | $bson .= pack('x'); // null byte: document terminator
58 | $bson = pack('V', 4 + strlen($bson)) . $bson; // int32: document length
59 | //var_dump(bson_decode($bson));
60 |
61 | }
62 |
63 | public function testEncodeDecode() {
64 | $a1 = array("hello" => "world");
65 | //var_dump(bson_decode(bson_encode($a1)));
66 | $this->assertEquals($a1, bson_decode(bson_encode($a1)));
67 |
68 | $id = new MongoId();
69 | $a2 = array("_id" => $id);
70 |
71 | $en_result = bson_encode($a2);
72 | $result = bson_decode($en_result);
73 | $this->assertEquals($a2, $result);
74 |
75 | $int32 = 2;
76 | $a2["a_int32"] = $int32;
77 | $result = bson_decode(bson_encode($a2));
78 | $this->assertEquals($a2, $result);
79 | /*
80 | $int64 = new MongoInt64("64");
81 | $a2["a_int64"] = $int64;
82 | //var_dump(bson_decode(bson_encode($a2["a_int32"])));
83 | //$this->assertTrue(bson_decode(bson_encode($a2["a_int64"])) == $a2["a_int64"]);
84 | */
85 | $bool = true;
86 | $a2["boolean"] = $bool;
87 | $this->assertEquals($a2, bson_decode(bson_encode($a2)));
88 |
89 | $null = null;
90 | $a2["null"] = $null;
91 | $this->assertEquals($a2, bson_decode(bson_encode($a2)));
92 |
93 | $double = 3.2;
94 | $a2["double"] = $double;
95 | $this->assertEquals($a2, bson_decode(bson_encode($a2)));
96 | }
97 |
98 | public function testEncodeDecodeNestedDocument() {
99 | $id1 = new MongoId();
100 |
101 | $bool = true;
102 | $string = "Top level doc";
103 | $int64 = 9001;
104 | $date = new MongoDate();
105 | $timestamp = new MongoTimestamp();
106 | $minkey = new MongoMinKey();
107 | $maxkey = new MongoMaxKey();
108 | $bindata = new MongoBinData("1111");
109 |
110 | $id2 = new MongoId();
111 | $string2 = "Inner doc";
112 | $array = [0, 1, 2 , 3];
113 |
114 | $inner_doc = array("_id" => $id2, "name" => $string2, "list" => $array);
115 |
116 | $this->assertEquals($inner_doc, bson_decode(bson_encode($inner_doc)));
117 |
118 | $out_doc = array("_id" => $id1,
119 | "name" => $string,
120 | "val" => $int64,
121 | "date" => $date,
122 | "timestamp" => $timestamp,
123 | "minkey" => $minkey,
124 | "maxkey" => $maxkey,
125 | "bindata" => $bindata,
126 | "inner_doc" => $inner_doc);
127 |
128 | // var_dump(bson_decode(bson_encode($out_doc)));
129 |
130 | $this->assertEquals($out_doc, bson_decode(bson_encode($out_doc)));
131 | }
132 |
133 | public function testDecodeCorruptException() {
134 | $id1 = new MongoId();
135 |
136 | $bool = true;
137 | $string = "Top level doc";
138 | $int64 = 9001;
139 | $date = new MongoDate();
140 | $timestamp = new MongoTimestamp();
141 | $minkey = new MongoMinKey();
142 | $maxkey = new MongoMaxKey();
143 | $bindata = new MongoBinData("1111");
144 |
145 | $out_doc = array("_id" => $id1,
146 | "name" => $string,
147 | "val" => $int64,
148 | "date" => $date,
149 | "timestamp" => $timestamp,
150 | "minkey" => $minkey,
151 | "maxkey" => $maxkey,
152 | "bindata" => $bindata);
153 |
154 | $encoded_bson = bson_encode($out_doc);
155 |
156 | $corrupted_bson = substr($encoded_bson, 0, strlen($encoded_bson) - 4);
157 |
158 | $message = "Unexpected end of BSON. Input document is likely corrupted!";
159 |
160 | $this->setExpectedException('MongoException', $message);
161 |
162 | bson_decode($corrupted_bson);
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/test/mongoClientTest.php:
--------------------------------------------------------------------------------
1 | getTestClient();
7 | //var_dump((string) $cli);
8 | //var_dump($cli->listDBs());
9 | }
10 | }
--------------------------------------------------------------------------------
/test/mongoCollectionTest.php:
--------------------------------------------------------------------------------
1 | toIndexString($keys);
10 | }
11 | }
12 |
13 | class MongoCollectionTest extends MongoTestCase {
14 |
15 | public function testInsertAndRemove() {
16 | $cli = $this->getTestClient();
17 | $database_name = "test";
18 | $db = new MongoDB($cli, $database_name);
19 | $coll_name = "students";
20 | $coll = new MongoCollection($db, $coll_name);
21 |
22 | // in case function never completes, need to remove Dan or will trigger insert error
23 | $coll->remove(array("name"=>"Dan"));
24 |
25 | //Test case for insert and remove
26 | $new_doc = array("_id"=>"123456781234567812345678", "name" => "Dan"); //24-digit _id required
27 | //$this->assertEquals(true, $coll->insert($new_doc));
28 | $cursor = $coll->find(array("name" => "Dan"));
29 | $cursor->rewind();
30 | while ($cursor->valid())
31 | {
32 | $value = $cursor->current();
33 | //$this->assertEquals("Dan", $value["name"]);
34 | $cursor->next();
35 | }
36 | //$this->assertEquals(true, $coll->remove(array("name"=>"Dan")));
37 | $cursor = $coll->find(array("name" => "Dan"));
38 | $cursor->rewind();
39 | //$this->assertEquals(array(), $cursor->current());
40 | // //Test case for drop
41 | // $temp_coll = new MongoCollection($db, "temp");
42 | // //$this->assertEquals(true, $temp_coll->drop()["return"]);
43 |
44 | // //Test case for save
45 | // $new_doc = array("_id"=>"123456791234567912345679", "name" => "Eva"); //24-digit _id required
46 | // //$this->assertEquals(true, $coll->save($new_doc));
47 | // $cursor = $coll->find(array("name" => "Eva"));
48 | // $cursor->rewind();
49 | // while ($cursor->valid())
50 | // {
51 | // $value = $cursor->current();
52 | // if ((strpos($value["return"], "\"name\" : \"Eva\"")) == FALSE) {
53 | // throw new Exception("MongoCollection save for new document failed.");
54 | // }
55 | // $cursor->next();
56 | // }
57 | // $new_doc_modified = array("_id"=>"123456791234567912345679", "name" => "Frank"); //24-digit _id required
58 | // $this->assertEquals(true, $coll->save($new_doc_modified));
59 | // $cursor = $coll->find(array("name" => "Frank"));
60 | // $cursor->rewind();
61 | // while ($cursor->valid())
62 | // {
63 | // $value = $cursor->current();
64 | // if (strpos($value["return"], "\"name\" : \"Frank\"") == FALSE) {
65 | // throw new Exception("MongoCollection save for existing document failed.");
66 | // }
67 | // $cursor->next();
68 | // }
69 | }
70 |
71 | public function testToIndexString() {
72 | $db = $this->getTestDB();
73 | $coll_name = "students";
74 | $coll = $db->selectCollection($coll_name);
75 |
76 | // Testing toIndexString
77 | $c = new MongoCollectionStub();
78 | $actual = $c->createIndexString("foo");
79 | $expected = "foo_1";
80 | $this->assertEquals($expected, $actual);
81 |
82 | $actual = $c->createIndexString(array('name' => 1, 'age' => -1, 'bool' => false));
83 | $expected = "name_1_age_-1_bool_1";
84 | $this->assertEquals($expected, $actual);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/test/mongoCursorTest.php:
--------------------------------------------------------------------------------
1 | getTestClient();
7 | $database_name = "test.students";
8 | $cursor = new MongoCursor( $cli,
9 | $database_name);
10 |
11 | $cursor->rewind();
12 | while ($cursor->valid())
13 | {
14 | //$key = $cursor->key(); //This should be tested after BSON-PHP decoder is finished
15 | //var_dump($key);
16 | $value = $cursor->current();
17 | //var_dump($value);
18 | $cursor->next();
19 | }
20 | }
21 |
22 | public function testStartedIterating() {
23 | $cli = $this->getTestClient();
24 | $database_name = "test.students";
25 | $cursor = new MongoCursor( $cli,
26 | $database_name,
27 | array("name" => "Bob"),
28 | array() );
29 | $cursor->rewind();
30 | /* TODO: add assertEquals to ensure that the document retrieved is what we want
31 | while ($cursor->valid())
32 | {
33 | //$key = $cursor->key(); //This should be tested after BSON-PHP decoder is finished
34 | //var_dump($key);
35 | $value = $cursor->current();
36 | var_dump($value);
37 | $cursor->next();
38 | }
39 | */
40 |
41 | //Expect to have an exception since the cursor has started iterating.
42 | $this->setExpectedException('MongoCursorException');
43 | $cursor->addOption("", "");
44 | }
45 |
46 | public function testExplain() {
47 | $db = $this->getTestDB();
48 | $coll = $db->selectCollection("students");
49 | $cur = $coll->find();
50 | $cur->rewind();
51 | $res = $cur->current();
52 | $cur->next();
53 | var_dump($cur->explain());
54 | //var_dump($cur);
55 | //$cur->rewind();
56 | //$res2 = $cur->current();
57 | //$this->assertEquals($res, $res2);
58 |
59 | }
60 |
61 | public function testSetFlagOne() {
62 | $cli = $this->getTestClient();
63 | $database_name = "test.students";
64 | //$cursor = new MongoCursor( $cli, $database_name);
65 | $cursor = new MongoCursor( $cli,
66 | $database_name,
67 | array("name" => "Bob"),
68 | array() );
69 |
70 | $cursor->setFlag(1, true);
71 | $cursor->rewind();
72 | $info = $cursor->info();
73 | $this->assertEquals(true, $info["flags"][1]);
74 | //var_dump($info["flags"]);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/test/mongoDBTest.php:
--------------------------------------------------------------------------------
1 | getTestClient();
7 |
8 | //echo "Going to selectDB";
9 | $db = $this->getTestDB();
10 | //var_dump($db->getProfilingLevel());
11 | //$db = new MongoDB($cli, $database_name);
12 | }
13 |
14 | public function testCreateDropCollection() {
15 | $db = $this->getTestDB();
16 | $coll_name = "hello";
17 | $coll = $db->createCollection("hello");
18 | //$this->assertEquals(1, $db_response["ok"]);
19 |
20 | $db_response = $db->dropCollection($coll);
21 | $this->assertEquals(1, $db_response["ok"]);
22 | //$res = $db->dropCollection($coll);
23 | //$res = $db->createCollection("hello");
24 | }
25 |
26 | public function testGetCollectionNames() {
27 | $cli = $this->getTestClient();
28 | $new_colls = array("t1","t2","t3");
29 | $db = $cli->selectDB("testCollectionNames");
30 | foreach ($new_colls as $coll) {
31 | // $db->createCollection($coll);
32 | }
33 |
34 | //$res = $db->getCollectionNames();
35 | //$this->assertEquals($new_colls, $res);
36 | }
37 | }
--------------------------------------------------------------------------------
/test/mongoDateTest.php:
--------------------------------------------------------------------------------
1 | getServerVersion());
5 |
--------------------------------------------------------------------------------
/test/students.json:
--------------------------------------------------------------------------------
1 | {"name": "Alice", "subjects": ["Computer Science", "Statistics", "English", "Physics"], "GPA": 4.0, "_id": "1"}
2 | {"name": "Bob", "subjects": ["History", "Biology", "Mathematics"], "GPA": 3.6, "_id": "2"}
3 | {"name": "Charlie", "subjects": ["Anthropology", "Computer Science", "Physics", "East Asian Studies"], "GPA": 3.2, "_id": "3"}
--------------------------------------------------------------------------------
/test/test_collection.json:
--------------------------------------------------------------------------------
1 | { "_id" : "1", "test_field" : "1", "num" : "1" }
2 | { "_id" : "2", "test_field" : "1", "num" : "2" }
3 | { "_id" : "3", "test_field" : "1", "num" : "3" }
--------------------------------------------------------------------------------