├── .editorconfig ├── .gitignore ├── .scrutinizer.yml ├── _config.yml ├── composer.json ├── docs ├── code_conventions.md ├── code_of_conduct.md ├── contributing.md ├── examples │ └── operations.md ├── getting-started.md ├── issue_template.md ├── pull-request-template.md ├── query-tree.md ├── roadmap.md └── tests.md ├── license.md ├── phpcs.xml ├── phpunit.xml ├── readme.md ├── src ├── Connection │ ├── Connection.php │ ├── ConnectionCursorInterface.php │ ├── ConnectionException.php │ ├── ConnectionInterface.php │ ├── ConnectionQueryInterface.php │ ├── Options.php │ ├── OptionsInterface.php │ ├── Registry.php │ ├── RegistryInterface.php │ └── Socket │ │ ├── Exception.php │ │ ├── Handshake.php │ │ ├── HandshakeInterface.php │ │ └── Socket.php ├── Message │ ├── ExprMessage.php │ ├── Message.php │ └── MessageInterface.php ├── Query │ ├── AbstractQuery.php │ ├── Aggregation │ │ ├── AggregationTrait.php │ │ ├── Avg.php │ │ ├── Count.php │ │ ├── Group.php │ │ ├── Max.php │ │ ├── Min.php │ │ ├── Sum.php │ │ └── Ungroup.php │ ├── Builder.php │ ├── Database.php │ ├── Exception │ │ └── QueryException.php │ ├── Logic │ │ ├── AndLogic.php │ │ ├── EqualLogic.php │ │ ├── FuncLogic.php │ │ ├── GreaterThanLogic.php │ │ ├── GreaterThanOrEqualToLogic.php │ │ ├── LowerThanLogic.php │ │ ├── LowerThanOrEqualToLogic.php │ │ ├── NotEqualLogic.php │ │ ├── NotLogic.php │ │ └── OrLogic.php │ ├── Manipulation │ │ ├── GetField.php │ │ ├── HasFields.php │ │ ├── Keys.php │ │ ├── LogicTrait.php │ │ ├── ManipulationTrait.php │ │ ├── Pluck.php │ │ ├── RowHasFields.php │ │ ├── Values.php │ │ └── Without.php │ ├── Operation │ │ ├── Between.php │ │ ├── Changes.php │ │ ├── DbCreate.php │ │ ├── DbDrop.php │ │ ├── DbList.php │ │ ├── Delete.php │ │ ├── Filter.php │ │ ├── FilterByRow.php │ │ ├── Get.php │ │ ├── GetAll.php │ │ ├── IndexCreate.php │ │ ├── IndexDrop.php │ │ ├── IndexList.php │ │ ├── IndexRename.php │ │ ├── Insert.php │ │ ├── OperationTrait.php │ │ ├── Sync.php │ │ ├── TableCreate.php │ │ ├── TableDrop.php │ │ ├── TableList.php │ │ └── Update.php │ ├── Options.php │ ├── OptionsInterface.php │ ├── Ordening.php │ ├── QueryInterface.php │ ├── Row.php │ ├── Table.php │ └── Transformation │ │ ├── IsEmpty.php │ │ ├── Limit.php │ │ ├── OrderBy.php │ │ ├── Skip.php │ │ └── TransformationTrait.php ├── Response │ ├── Cursor.php │ ├── Response.php │ └── ResponseInterface.php ├── Rethink.php ├── RethinkInterface.php ├── Serializer │ └── QueryNormalizer.php └── Types │ ├── Frame │ └── FrameType.php │ ├── Query │ └── QueryType.php │ ├── Response │ ├── ErrorType.php │ ├── ResponseNote.php │ └── ResponseType.php │ ├── Term │ └── TermType.php │ └── VersionDummy │ ├── Protocol.php │ └── Version.php └── test ├── Bootstrap.php ├── config.php.dist ├── config_scrutinizer.php ├── integration ├── AbstractTestCase.php ├── Aggregation │ ├── AvgTest.php │ ├── CountTest.php │ ├── GroupTest.php │ ├── MaxTest.php │ ├── MinTest.php │ └── SumTest.php ├── Connection │ └── ConnectionTest.php ├── Manipulation │ ├── HasFieldsTest.php │ ├── KeysTest.php │ ├── PluckTest.php │ ├── ValuesTest.php │ └── WithoutTest.php ├── Operation │ ├── AbstractTableTest.php │ ├── BetweenTest.php │ ├── ChangesTest.php │ ├── CursorTest.php │ ├── DatabaseTest.php │ ├── DeleteTest.php │ ├── EmptyTableTest.php │ ├── FilterTest.php │ ├── GetAllTest.php │ ├── GetTest.php │ ├── IndexTest.php │ ├── InsertTest.php │ ├── RowTest.php │ ├── SyncTest.php │ └── UpdateTest.php └── Transformation │ ├── IsEmptyTest.php │ ├── LimitTest.php │ ├── OrderByTest.php │ └── SkipTest.php └── unit ├── BaseUnitTestCase.php ├── Connection ├── ConnectionCursorTest.php ├── ConnectionQueryTest.php ├── ConnectionTest.php ├── ConnectionTestCase.php ├── OptionsTest.php ├── RegistryTest.php └── Socket │ └── HandshakeTest.php ├── Query └── MessageTest.php ├── Response └── ResponseTest.php └── Serializer └── QueryNormalizerTest.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | charset = utf-8 9 | 10 | # 4 space indentation 11 | [*.php] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | # Matches the exact files 16 | [{composer.json,*.js}] 17 | indent_style = space 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | vendor/ 3 | rethinkdb_data 4 | test/config.php 5 | composer.lock 6 | 7 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | before_commands: 2 | - "composer install" 3 | 4 | checks: 5 | php: true 6 | 7 | build: 8 | environment: 9 | timezone: 'Europe/Amsterdam' 10 | variables: 11 | integration_test_file: 'config_scrutinizer.php' 12 | php: 13 | version: '7.1' 14 | ini: 15 | 'date.timezone': 'Europe/Amsterdam' 16 | dependencies: 17 | # Runs before inferred commands 18 | before: 19 | - 'source /etc/lsb-release && echo "deb http://download.rethinkdb.com/apt $DISTRIB_CODENAME main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list' 20 | - 'wget -qO- https://download.rethinkdb.com/apt/pubkey.gpg | sudo apt-key add -' 21 | - 'sudo apt-get update -y' 22 | - 'sudo apt-get install rethinkdb -y' 23 | - 'sudo cp /etc/rethinkdb/default.conf.sample /etc/rethinkdb/instances.d/instance1.conf' 24 | - 'sudo /etc/init.d/rethinkdb start' 25 | # Overwrites inferred commands 26 | override: [] 27 | # Runs after inferred commands 28 | after: [] 29 | # Run after dependencies 30 | project_setup: 31 | before: [] 32 | override: [] 33 | after: [] 34 | nodes: 35 | tests: true 36 | analysis: 37 | tests: 38 | override: 39 | - 40 | command: phpcs-run --standard=phpcs.xml 41 | use_website_config: false 42 | - php-scrutinizer-run 43 | 44 | build_failure_conditions: 45 | # No critical issue is introduced (existing ones are tolerated) 46 | - 'issues.label("coding-style").exists' 47 | 48 | # No new critical issue is introduced (existing ones are tolerated) 49 | - 'issues.label("coding-style").new.exists' 50 | 51 | # No critical issue is present 52 | - 'issues.severity(= MINOR).exists' 53 | 54 | # No new critical issue is introduced (existing ones are tolerated) 55 | - 'issues.severity(= MINOR).new.exists' 56 | 57 | # Class has no tests 58 | - 'classes.metric("php_code_coverage.coverage", = 0).exists' 59 | 60 | # Rating is C or worse for existing classes 61 | - 'elements.rating(<= D).exists' 62 | 63 | # Rating is C or worse is introduced 64 | - 'elements.rating(<= D).new.exists' 65 | 66 | - 'project.metric("scrutinizer.quality", < 9)' # Code Quality Rating drops below 9 67 | - 'project.metric("scrutinizer.test_coverage", < 0.85)' # Code Coverage drops below 90% 68 | 69 | # Code Coverage decreased from previous inspection by more than 5% 70 | - 'project.metric_change("scrutinizer.test_coverage", < -0.05)' 71 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tbolier/php-rethink-ql", 3 | "description": "A clean and solid RethinkDB driver for PHP.", 4 | "type": "library", 5 | "license": "Apache-2.0", 6 | "homepage": "https://github.com/tbolier/php-rethink-ql/", 7 | "authors": [ 8 | { 9 | "name": "Timon Bolier", 10 | "email": "timonb@gmail.com" 11 | } 12 | ], 13 | "keywords": [ 14 | "adapter", 15 | "database", 16 | "driver", 17 | "rethinkdb", 18 | "nosql", 19 | "nosql-database", 20 | "php", 21 | "php-rethinkdb", 22 | "php-rethinkdb-driver", 23 | "rethinkdb-adapter", 24 | "reql", 25 | "rethinkdb-ql" 26 | ], 27 | "minimum-stability": "stable", 28 | "require": { 29 | "php": "^7.1", 30 | "psr/http-message": "^1.0", 31 | "symfony/serializer": "^4.1", 32 | "symfony/property-access": "^4.1" 33 | }, 34 | "require-dev": { 35 | "mockery/mockery": "^1.0", 36 | "phpunit/phpunit": "^7.0 || ^8.0" 37 | }, 38 | "autoload": { 39 | "psr-4": { 40 | "TBolier\\RethinkQL\\": "src/", 41 | "TBolier\\RethinkQL\\UnitTest\\": "test/unit/", 42 | "TBolier\\RethinkQL\\IntegrationTest\\": "test/integration/" 43 | } 44 | }, 45 | "support": { 46 | "docs": "https://github.com/tbolier/php-rethink-ql/tree/master/docs/", 47 | "issues": "https://github.com/tbolier/php-rethink-ql/issues/", 48 | "source": "https://github.com/tbolier/php-rethink-ql/" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /docs/code_conventions.md: -------------------------------------------------------------------------------- 1 | # Code conventions 2 | 3 | We value quality. Because everyone can contribute we have created some guidelines. 4 | 5 | # Naming conventions 6 | 7 | 1. Interfaces MUST be suffixed by Interface: e.g. `TBolier\Foo\QueryInterface`. 8 | 2. Abstract classes MUST be prefixed by Abstract: e.g. `TBolier\Foo\AbstractQuery`. 9 | 3. Traits MUST be suffixed by Trait: e.g. `TBolier\Foo\QueryTrait`. 10 | 4. PSR-1, 2 and 4 MUST be followed. 11 | 5. The vendor namespace MUST be `TBolier`. 12 | 13 | # Code standards and principles 14 | 15 | 1. The [PHP-FIG PHP Standards Recommendations](https://www.php-fig.org/psr/) (PSR-1, 2 and 4) MUST be followed. 16 | 2. Create understandable, flexible and maintainable code by using the [Solid Principles](https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)) as a set of guidelines. 17 | 3. Use [Composite over inheritance](https://en.wikipedia.org/wiki/Composition_over_inheritance) as a guideline. 18 | 4. Create integration and unit tests for your fixes and code contributions. 19 | 20 | Thank you! 21 | -------------------------------------------------------------------------------- /docs/code_of_conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | Our focus is to create an open-source RethinkDB driver for PHP where everyone is welcome to contribute and discuss. We hope by community input and improvements, we deliver a community-driven driver that is well supported for both RethinkDB versions and API, and PHP. Please feel free to participate in this project and keep in mind the guidelines below. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Please read the following guidelines if you would like to contribute to this project. 4 | 5 | 1. Agree with our [code of conduct](code_of_conduct.md) 6 | 2. Follow the [code conventions](code_conventions.md) guidelines 7 | 3. Check the [roadmap](roadmap.md) to see what's planned for the next version 8 | 4. Go to [open issues](https://github.com/tbolier/php-rethink-ql/issues) and pick a _good first issue_ or _help wanted_ issue 9 | 5. Run the tests. See [here for the configuration](tests.md) 10 | 11 | ## Discussions and chat 12 | 13 | You can find us at Gitter.im in the `rethinkdb-php` room at https://gitter.im/rethinkdb-php/Lobby 14 | -------------------------------------------------------------------------------- /docs/examples/operations.md: -------------------------------------------------------------------------------- 1 | # ReQL operation examples 2 | 3 | ## between 4 | 5 | Insert three documents, select `between` 1 and 2, assert the result. 6 | 7 | ```php 8 | // We can do our operations meanwhile. 9 | $res = $r 10 | ->table('tabletest') 11 | ->insert([ 12 | [ 13 | 'id' => 1, 14 | 'title' => 'Test document 1', 15 | 'description' => 'A document description.', 16 | ], 17 | [ 18 | 'id' => 2, 19 | 'title' => 'Test document 2', 20 | 'description' => 'A document description.', 21 | ], 22 | [ 23 | 'id' => 3, 24 | 'title' => 'Test document 3', 25 | 'description' => 'A document description.', 26 | ], 27 | ]) 28 | ->run(); 29 | 30 | /** @var Cursor $cursor */ 31 | $cursor = $r 32 | ->table('tabletest') 33 | ->between(1, 2) 34 | ->run(); 35 | 36 | echo $cursor->count() === 2 ? 'equals' : 'different'; 37 | foreach ($cursor as $document) { 38 | print_r($document); 39 | } 40 | ``` 41 | 42 | ## changes 43 | Insert five documents and consume the `changes` afterwards. 44 | ```php 45 | // In this example we set the time limit to 5 seconds before the process is terminated. 46 | set_time_limit(5); 47 | 48 | // The feed is an iterable cursor. 49 | $feed = $r 50 | ->table('tabletest') 51 | ->changes() 52 | ->run(); 53 | 54 | // We can do our operations meanwhile. 55 | $res = $r 56 | ->table('tabletest') 57 | ->insert([ 58 | [ 59 | 'id' => 2, 60 | 'title' => 'Test document 2', 61 | 'description' => 'A document description.', 62 | ], 63 | [ 64 | 'id' => 1, 65 | 'title' => 'Test document 1', 66 | 'description' => 'A document description.', 67 | ], 68 | ]) 69 | ->run(); 70 | 71 | // When we iterate over the feed, it will print out the changes. 72 | foreach ($feed as $change) { 73 | extract($change); 74 | print_r($old_val, $new_val); 75 | } 76 | ``` 77 | 78 | #### changes with options 79 | Insert one document, update it, and consume the change feed with a squashed cursor. 80 | For all possible options, please visit the Java documentation [here](https://rethinkdb.com/api/java/changes/). 81 | ```php 82 | $feed = $r 83 | ->table('tabletest') 84 | ->changes(['squash' => true]) 85 | ->run(); 86 | 87 | $res = $r 88 | ->table('tabletest') 89 | ->insert([ 90 | [ 91 | 'id' => 1, 92 | 'title' => 'Test document 1', 93 | 'description' => 'A document description.', 94 | ] 95 | ]) 96 | ->run(); 97 | 98 | $r 99 | ->table('tabletest') 100 | ->filter(['id' => 1]) 101 | ->update(['description' => 'cool!']) 102 | ->run(); 103 | 104 | $change = $feed->current(); 105 | print_r($change); 106 | ``` 107 | 108 | ## tableCreate 109 | ```php 110 | $r->db() 111 | ->tableCreate('Table') 112 | ->run(); 113 | ``` 114 | 115 | ## insert 116 | Insert one or more documents. 117 | ```php 118 | $r->table('tableName') 119 | ->insert([ 120 | 'documentId' => 1, 121 | 'title' => 'Test document 1', 122 | 'description' => 'A document description.' 123 | ]) 124 | ->run(); 125 | 126 | $r->table('tableName') 127 | ->insert([ 128 | [ 129 | 'documentId' => 2, 130 | 'title' => 'Test document 2', 131 | 'description' => 'A document description.' 132 | ], 133 | [ 134 | 'documentId' => 3, 135 | 'title' => 'Test document 3', 136 | 'description' => 'A document description.' 137 | ], 138 | ]) 139 | ->run(); 140 | ``` 141 | 142 | ## update 143 | Update one or more documents. 144 | ```php 145 | $r->table('tableName') 146 | ->filter([ 147 | [ 148 | 'title' => 'A document description.', 149 | ], 150 | ]) 151 | ->update([ 152 | [ 153 | 'title' => 'Updated document description.', 154 | ], 155 | ]) 156 | ->run(); 157 | ``` 158 | 159 | ## filter 160 | Filter and count the results. 161 | ```php 162 | $r->table('tableName') 163 | ->filter([ 164 | [ 165 | 'title' => 'Test document', 166 | ], 167 | ]) 168 | ->count() 169 | ->run(); 170 | ``` 171 | 172 | ## sync 173 | Save writes to permanent storage. 174 | 175 | ```php 176 | $r->table('tableName') 177 | ->sync() 178 | ->run(); 179 | ``` -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | 3 | *Work in progress document.* 4 | 5 | Multiple connections can be injected into the connection `Registry`. 6 | Create the Rethink driver object by injecting a `Connection` object into it. 7 | 8 | ```php 9 | [ 15 | 'host' => 'localhost', 16 | 'port' => 28015, 17 | 'default_db' => 'demoDB', 18 | 'user' => 'demo', 19 | 'password' => 'demo', 20 | 'timeout' => 5, 21 | 'timeout_stream' => 10, 22 | ], 23 | ]; 24 | 25 | $registry = new Registry($connections); 26 | 27 | $r = new Rethink($registry->getConnection('default_connection')); 28 | 29 | // Now you can connect to RethinkDB. 30 | $r->connection()->connect(); 31 | ``` 32 | 33 | The driver class `Rethink` has an API Interface that supports the ReQL domain-specific language (DSL). 34 | 35 | ## Examples 36 | 37 | ### Operations 38 | 39 | Examples on ReQL operations can be [found here](examples/operations.md). -------------------------------------------------------------------------------- /docs/issue_template.md: -------------------------------------------------------------------------------- 1 | ## Issue summary 2 | 3 | Please describe your issue. 4 | 5 | ## Issue checklist 6 | A checklist overview of items to do before the issue can be marked as done. 7 | - [ ] Item one 8 | - [ ] Item two 9 | 10 | The following sections can be removed only, whenever they are not applicable or you have a simple question. 11 | 12 | ## Actual Behavior 13 | 14 | What is it doing now? 15 | 16 | ## Expected Behavior 17 | 18 | What do you expect it to do? 19 | 20 | ## Steps to Reproduce the Problem 21 | 22 | 1. 23 | 1. 24 | 1. 25 | 26 | ## Specifications 27 | 28 | - PHP Version: 29 | - RethinkDB Version: 30 | - Platform: Mac OS / Linux (Distro?) / Windows 31 | -------------------------------------------------------------------------------- /docs/pull-request-template.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | _Describe the problem or feature in addition to a link to the issues._ 3 | 4 | ## Approach 5 | _How does this change address the problem?_ 6 | 7 | #### Open Questions and Pre-Merge TODOs 8 | - [ ] Use github checklists. When solved, check the box and explain the answer. 9 | 10 | ## Research 11 | _Describe the research stage_ 12 | 13 | _Links to blog posts, patterns, libraries or addons used to solve this problem_ 14 | 15 | ## Solution 16 | 17 | _How did you solve the issue?_ 18 | 19 | -------------------------------------------------------------------------------- /docs/query-tree.md: -------------------------------------------------------------------------------- 1 | # Query tree guide 2 | 3 | A simple RethinkDB query like: 4 | 5 | ```php 6 | $r->db('test')->table('tabletest')->filter('{"title":"Test document"}')->count(); 7 | ``` 8 | 9 | ## Under the hood 10 | 11 | First we nest all query operation objects. This is a too long dump for the example. So afterwards the `toArray` method is called to normalize the query. 12 | This is how the message object looks like with the normalized query before we serialize it. 13 | 14 | ```php 15 | TBolier\RethinkQL\Message\Message Object 16 | ( 17 | [queryType:TBolier\RethinkQL\Message\Message:private] => 1 18 | [query:TBolier\RethinkQL\Message\Message:private] => Array 19 | ( 20 | [0] => 43 21 | [1] => Array 22 | ( 23 | [0] => Array 24 | ( 25 | [0] => 39 26 | [1] => Array 27 | ( 28 | [0] => Array 29 | ( 30 | [0] => 15 31 | [1] => Array 32 | ( 33 | [0] => tabletest 34 | ) 35 | 36 | ) 37 | 38 | [1] => Array 39 | ( 40 | [0] => 98 41 | [1] => Array 42 | ( 43 | [0] => {"title":"Test document"} 44 | ) 45 | 46 | ) 47 | 48 | ) 49 | 50 | ) 51 | 52 | ) 53 | 54 | ) 55 | 56 | [options:TBolier\RethinkQL\Message\Message:private] => TBolier\RethinkQL\Query\Options Object 57 | ( 58 | [db:TBolier\RethinkQL\Query\Options:private] => Array 59 | ( 60 | [0] => 14 61 | [1] => Array 62 | ( 63 | [0] => test 64 | ) 65 | 66 | ) 67 | 68 | ) 69 | 70 | ) 71 | ``` 72 | 73 | Now we JSON serialize the Message object and we end up with a raw query that we send to the RethinkDB (server): 74 | 75 | ```php 76 | [ 77 | 1, 78 | [ 79 | 43, 80 | [ 81 | [ 82 | 39, 83 | [ 84 | [ 85 | 15, 86 | [ 87 | "tabletest" 88 | ] 89 | ], 90 | [ 91 | 98, 92 | [ 93 | "{\"title\":\"Test document\"}" 94 | ] 95 | ] 96 | ] 97 | ] 98 | ] 99 | ], 100 | { 101 | "db": [ 102 | 14, 103 | [ 104 | "test" 105 | ] 106 | ] 107 | } 108 | ] 109 | ``` 110 | -------------------------------------------------------------------------------- /docs/tests.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | We value quality. Because everyone can contribute we have created some guidelines. 4 | 5 | ## Configuration 6 | 7 | First you need to install RethinkDB. 8 | 9 | Then it's necessary to duplicate the standard configuration, and replace default values in `test/config.php`. 10 | 11 | ```sh 12 | cp test/config.php.dist test/config.php 13 | ``` 14 | 15 | ## Run 16 | 17 | ```sh 18 | composer update 19 | 20 | vendor/bin/phpunit 21 | ``` 22 | 23 | Thank you! 24 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The coding standard for PHP_CodeSniffer itself. 4 | 5 | 6 | 7 | 8 | 9 | 10 | src 11 | test 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | test/integration/* 8 | 9 | 10 | test/unit/* 11 | 12 | 13 | 14 | 15 | src/* 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | PHP-RETHINK-QL 2 | A PHP RethinkDB driver for the RethinkDB query language (ReQL). 3 | License: Apache License 2.0 4 | 5 | PHP-RETHINK-QL [![by](https://img.shields.io/badge/by-%40tbolier-blue.svg)](https://github.com/tbolier) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/tbolier/php-rethink-ql/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/tbolier/php-rethink-ql/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/tbolier/php-rethink-ql/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/tbolier/php-rethink-ql/?branch=master) [![Build Status](https://scrutinizer-ci.com/g/tbolier/php-rethink-ql/badges/build.png?b=master)](https://scrutinizer-ci.com/g/tbolier/php-rethink-ql/build-status/master) 6 | ======================== 7 | 8 | # License 9 | PHP-RETHINK-QL is licensed under the terms of the [Apache License 2.0](LICENSE.md) 10 | 11 | # Description 12 | 13 | A new clean and solid RethinkDB driver for PHP, focused on clean code with SOLID principles in mind. 14 | 15 | Unfortunately the original PHP-RQL driver is no longer is no longer actively maintained and patched. That's why we have started this new PHP RethinkDB driver with the goal to create an easy to understand driver that can be improved and maintained by the community. 16 | 17 | # Requirements 18 | 19 | ## RethinkDB version 20 | 21 | This library supports the RethinkDB release `>=2.3.0` and protocol version `V1_0`. 22 | Earliers version of RethinkDB are not supported at this moment. 23 | 24 | ## PHP 25 | 26 | PHP version `>=7.1` 27 | 28 | ## Supported ReQL Command API overview. 29 | 30 | In the release [roadmap](docs/roadmap.md) you will find a table with the currently and future supported ReQL command methods. 31 | 32 | # Getting started 33 | 34 | Multiple connections can be injected into the connection `Registry`. 35 | Create the Rethink driver object by injecting a `Connection` object into it. 36 | 37 | ```php 38 | [ 44 | 'host' => 'localhost', 45 | 'port' => 28015, 46 | 'default_db' => 'demoDB', 47 | 'user' => 'demo', 48 | 'password' => 'demo', 49 | 'timeout' => 5, 50 | 'timeout_stream' => 10, 51 | ], 52 | ]; 53 | 54 | $registry = new Registry($connections); 55 | 56 | $r = new Rethink($registry->getConnection('default_connection')); 57 | 58 | // Now you can connect to RethinkDB. 59 | $r->connection()->connect(); 60 | ``` 61 | 62 | The driver class `Rethink` has a default database defined in the connection options. However you can always switch database if needed. 63 | ```php 64 | $r->use('demoDB-2'); 65 | ``` 66 | 67 | The driver class `Rethink` has an API Interface that supports the ReQL domain-specific language (DSL). 68 | 69 | A create table example: 70 | ```php 71 | $r->db() 72 | ->tableCreate('Table') 73 | ->run(); 74 | ``` 75 | 76 | For more examples about executing queries go to our docs section: [Getting started](docs/getting-started.md) 77 | 78 | ## Contributing 79 | 80 | Please read the [contributing guidelines](docs/contributing.md) if you would like to contribute to this project. 81 | 82 | ## Discussions and chat 83 | 84 | You can find us at Gitter.im in the `rethinkdb-php` room at https://gitter.im/rethinkdb-php/Lobby 85 | 86 | ## Author and collaborators 87 | 88 | * **Timon Bolier** - *Author and collaborator* - [tbolier](https://github.com/tbolier) 89 | * **Michel Maas** - *Collaborator* - [AxaliaN](https://github.com/AxaliaN) 90 | * **Jérémy** - *Collaborator* - [Th3Mouk](https://github.com/Th3Mouk) 91 | 92 | See also the list of [contributors](https://github.com/tbolier/php-rethink-ql/contributors) who participated in this project. 93 | -------------------------------------------------------------------------------- /src/Connection/ConnectionCursorInterface.php: -------------------------------------------------------------------------------- 1 | hostname = $options['hostname'] ?? 'localhost'; 51 | $this->port = $options['port'] ?? 28015; 52 | $this->dbname = $options['dbname'] ?? ''; 53 | $this->user = $options['user'] ?? ''; 54 | $this->password = $options['password'] ?? ''; 55 | $this->timeout = $options['timeout'] ?? 1.0; 56 | $this->timeoutStream = $options['timeout_stream'] ?? 3; 57 | $this->ssl = $options['ssl'] ?? false; 58 | } 59 | 60 | public function getHostname(): string 61 | { 62 | return $this->hostname; 63 | } 64 | 65 | public function getPort(): int 66 | { 67 | return $this->port; 68 | } 69 | 70 | public function getDbName(): string 71 | { 72 | return $this->dbname; 73 | } 74 | 75 | public function getUser(): string 76 | { 77 | return $this->user; 78 | } 79 | 80 | public function getPassword(): string 81 | { 82 | return $this->password; 83 | } 84 | 85 | public function getTimeout(): float 86 | { 87 | return $this->timeout; 88 | } 89 | 90 | public function getTimeoutStream(): int 91 | { 92 | return $this->timeoutStream; 93 | } 94 | 95 | public function isSsl(): bool 96 | { 97 | return $this->ssl; 98 | } 99 | 100 | public function hasDefaultDatabase(): bool 101 | { 102 | return !empty($this->dbname); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Connection/OptionsInterface.php: -------------------------------------------------------------------------------- 1 | $options) { 28 | if (!$options instanceof OptionsInterface) { 29 | continue; 30 | } 31 | 32 | $this->addConnection($name, $options); 33 | } 34 | } 35 | } 36 | 37 | /** 38 | * @throws ConnectionException 39 | */ 40 | public function addConnection(string $name, OptionsInterface $options): bool 41 | { 42 | if ($this->hasConnection($name)) { 43 | throw new ConnectionException("The connection {$name} has already been added.", 400); 44 | } 45 | 46 | // TODO: Create factory for instantiation Connection. 47 | $this->connections[$name] = new Connection( 48 | function () use ($options) { 49 | return new Socket( 50 | $options 51 | ); 52 | }, 53 | new Handshake($options->getUser(), $options->getPassword(), Version::V1_0), 54 | $options->getDbName(), 55 | new Serializer( 56 | [new QueryNormalizer()], 57 | [new JsonEncoder()] 58 | ), 59 | new Serializer( 60 | [new ObjectNormalizer()], 61 | [new JsonEncoder()] 62 | ) 63 | ); 64 | 65 | return true; 66 | } 67 | 68 | public function hasConnection(string $name): bool 69 | { 70 | return isset($this->connections[$name]); 71 | } 72 | 73 | /** 74 | * @throws ConnectionException 75 | */ 76 | public function getConnection(string $name): ConnectionInterface 77 | { 78 | if (!$this->connections[$name]) { 79 | throw new ConnectionException("The connection {$name} does not exist.", 400); 80 | } 81 | 82 | return $this->connections[$name]; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Connection/RegistryInterface.php: -------------------------------------------------------------------------------- 1 | queryType = $queryType ?? QueryType::START; 29 | $this->query = $query ?? ''; 30 | $this->options = $options ?? new Options(); 31 | } 32 | 33 | public function getQueryType(): int 34 | { 35 | return $this->queryType; 36 | } 37 | 38 | public function setCommand(int $queryType): MessageInterface 39 | { 40 | $this->queryType = $queryType; 41 | 42 | return $this; 43 | } 44 | 45 | public function setQuery($query): MessageInterface 46 | { 47 | $this->query = (string) $query; 48 | 49 | return $this; 50 | } 51 | 52 | public function getOptions(): OptionsInterface 53 | { 54 | return $this->options; 55 | } 56 | 57 | public function setOptions(OptionsInterface $options): MessageInterface 58 | { 59 | $this->options = $options; 60 | 61 | return $this; 62 | } 63 | 64 | public function toArray(): array 65 | { 66 | return [ 67 | $this->queryType, 68 | $this->query, 69 | (object) $this->getOptions(), 70 | ]; 71 | } 72 | 73 | public function jsonSerialize(): array 74 | { 75 | return $this->toArray(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Message/Message.php: -------------------------------------------------------------------------------- 1 | queryType = $queryType ?? QueryType::START; 30 | $this->query = $query ?? []; 31 | $this->options = $options ?? new Options(); 32 | } 33 | 34 | public function getQueryType(): int 35 | { 36 | return $this->queryType; 37 | } 38 | 39 | public function setCommand(int $queryType): MessageInterface 40 | { 41 | $this->queryType = $queryType; 42 | 43 | return $this; 44 | } 45 | 46 | public function setQuery($query): MessageInterface 47 | { 48 | $this->query = (array) $query; 49 | 50 | return $this; 51 | } 52 | 53 | public function getOptions(): OptionsInterface 54 | { 55 | return $this->options; 56 | } 57 | 58 | public function setOptions(OptionsInterface $options): MessageInterface 59 | { 60 | $this->options = $options; 61 | 62 | return $this; 63 | } 64 | 65 | public function toArray(): array 66 | { 67 | return [ 68 | $this->queryType, 69 | $this->query, 70 | (object) $this->getOptions() 71 | ]; 72 | } 73 | 74 | public function jsonSerialize(): array 75 | { 76 | return $this->toArray(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Message/MessageInterface.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 25 | } 26 | 27 | public function run() 28 | { 29 | $message = new Message(); 30 | $message->setQuery($this->toArray()); 31 | 32 | return $this->rethink->connection()->run($message); 33 | } 34 | 35 | abstract public function toArray(): array; 36 | } 37 | -------------------------------------------------------------------------------- /src/Query/Aggregation/AggregationTrait.php: -------------------------------------------------------------------------------- 1 | rethink, /** @scrutinizer ignore-type */ $this); 13 | } 14 | 15 | public function group(string $key) 16 | { 17 | return new Group($this->rethink, /** @scrutinizer ignore-type */ $this, $key); 18 | } 19 | 20 | public function ungroup() 21 | { 22 | return new Ungroup($this->rethink, /** @scrutinizer ignore-type */ $this); 23 | } 24 | 25 | public function sum(string $key): QueryInterface 26 | { 27 | return new Sum($this->rethink, /** @scrutinizer ignore-type */ $this, $key); 28 | } 29 | 30 | public function avg(string $key): QueryInterface 31 | { 32 | return new Avg($this->rethink, /** @scrutinizer ignore-type */ $this, $key); 33 | } 34 | 35 | public function min(string $key) 36 | { 37 | return new Min($this->rethink, /** @scrutinizer ignore-type */ $this, $key); 38 | } 39 | 40 | public function max(string $key) 41 | { 42 | return new Max($this->rethink, /** @scrutinizer ignore-type */ $this, $key); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Query/Aggregation/Avg.php: -------------------------------------------------------------------------------- 1 | query = $query; 33 | $this->key = $key; 34 | $this->rethink = $rethink; 35 | } 36 | 37 | public function toArray(): array 38 | { 39 | return [ 40 | TermType::AVG, 41 | [ 42 | $this->query->toArray(), 43 | $this->key, 44 | ], 45 | ]; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Query/Aggregation/Count.php: -------------------------------------------------------------------------------- 1 | query = $query; 23 | $this->rethink = $rethink; 24 | } 25 | 26 | public function toArray(): array 27 | { 28 | return [ 29 | TermType::COUNT, 30 | [ 31 | $this->query->toArray(), 32 | ], 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Query/Aggregation/Group.php: -------------------------------------------------------------------------------- 1 | query = $query; 37 | $this->key = $key; 38 | $this->rethink = $rethink; 39 | } 40 | 41 | public function toArray(): array 42 | { 43 | return [ 44 | TermType::GROUP, 45 | [ 46 | $this->query->toArray(), 47 | $this->key, 48 | ], 49 | ]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Query/Aggregation/Max.php: -------------------------------------------------------------------------------- 1 | query = $query; 38 | $this->key = $key; 39 | $this->rethink = $rethink; 40 | } 41 | 42 | public function toArray(): array 43 | { 44 | return [ 45 | TermType::MAX, 46 | [ 47 | $this->query->toArray(), 48 | $this->key, 49 | ], 50 | ]; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Query/Aggregation/Min.php: -------------------------------------------------------------------------------- 1 | query = $query; 33 | $this->key = $key; 34 | $this->rethink = $rethink; 35 | } 36 | 37 | public function toArray(): array 38 | { 39 | return [ 40 | TermType::MIN, 41 | [ 42 | $this->query->toArray(), 43 | $this->key, 44 | ], 45 | ]; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Query/Aggregation/Sum.php: -------------------------------------------------------------------------------- 1 | query = $query; 33 | $this->key = $key; 34 | $this->rethink = $rethink; 35 | } 36 | 37 | public function toArray(): array 38 | { 39 | return [ 40 | TermType::SUM, 41 | [ 42 | $this->query->toArray(), 43 | $this->key, 44 | ], 45 | ]; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Query/Aggregation/Ungroup.php: -------------------------------------------------------------------------------- 1 | query = $query; 30 | $this->rethink = $rethink; 31 | } 32 | 33 | public function toArray(): array 34 | { 35 | return [ 36 | TermType::UNGROUP, 37 | [ 38 | $this->query->toArray(), 39 | ], 40 | ]; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Query/Builder.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 38 | } 39 | 40 | public function table(string $name): Table 41 | { 42 | if ($this->table) { 43 | unset($this->table); 44 | } 45 | 46 | $this->table = new Table($name, $this->rethink); 47 | 48 | return $this->table; 49 | } 50 | 51 | public function database(): Database 52 | { 53 | if ($this->database) { 54 | unset($this->database); 55 | } 56 | 57 | $this->database = new Database($this->rethink); 58 | 59 | return $this->database; 60 | } 61 | 62 | public function ordening(string $key): Ordening 63 | { 64 | if ($this->ordering) { 65 | unset($this->ordering); 66 | } 67 | 68 | $this->ordering = new Ordening($key, $this->rethink); 69 | 70 | return $this->ordering; 71 | } 72 | 73 | public function row(string $value = null): Row 74 | { 75 | $this->row = new Row($this->rethink, $value); 76 | 77 | return $this->row; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Query/Database.php: -------------------------------------------------------------------------------- 1 | dbList(); 26 | } 27 | 28 | public function dbCreate(string $name): Database 29 | { 30 | $this->query = new DbCreate($this->rethink, $name); 31 | 32 | return $this; 33 | } 34 | 35 | public function dbDrop(string $name): Database 36 | { 37 | $this->query = new DbDrop($this->rethink, $name); 38 | 39 | return $this; 40 | } 41 | 42 | public function dbList(): Database 43 | { 44 | $this->query = new DbList($this->rethink); 45 | 46 | return $this; 47 | } 48 | 49 | public function tableList(): Database 50 | { 51 | $this->query = new TableList($this->rethink); 52 | 53 | return $this; 54 | } 55 | 56 | public function tableCreate(string $name): Database 57 | { 58 | $this->query = new TableCreate($this->rethink, $name); 59 | 60 | return $this; 61 | } 62 | 63 | public function tableDrop(string $name): Database 64 | { 65 | $this->query = new TableDrop($this->rethink, $name); 66 | 67 | return $this; 68 | } 69 | 70 | public function toArray(): array 71 | { 72 | return $this->query->toArray(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Query/Exception/QueryException.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 33 | 34 | $this->functionOne = $functionOne; 35 | $this->functionTwo = $functionTwo; 36 | } 37 | 38 | public function toArray(): array 39 | { 40 | return 41 | [ 42 | TermType::AND, 43 | [ 44 | $this->functionOne->toArray(), 45 | $this->functionTwo->toArray(), 46 | ], 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Query/Logic/EqualLogic.php: -------------------------------------------------------------------------------- 1 | value = $value; 33 | $this->rethink = $rethink; 34 | 35 | $this->query = $query; 36 | } 37 | 38 | public function toArray(): array 39 | { 40 | return 41 | [ 42 | TermType::EQ, 43 | [ 44 | $this->query->toArray(), 45 | $this->value, 46 | ], 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Query/Logic/FuncLogic.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 27 | 28 | $this->query = $query; 29 | } 30 | 31 | public function toArray(): array 32 | { 33 | return 34 | [ 35 | TermType::FUNC, 36 | [ 37 | [ 38 | TermType::MAKE_ARRAY, 39 | [ 40 | TermType::DATUM, 41 | ], 42 | ], 43 | $this->query->toArray(), 44 | ], 45 | ]; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Query/Logic/GreaterThanLogic.php: -------------------------------------------------------------------------------- 1 | value = $value; 33 | $this->rethink = $rethink; 34 | 35 | $this->query = $query; 36 | } 37 | 38 | public function toArray(): array 39 | { 40 | return 41 | [ 42 | TermType::GT, 43 | [ 44 | $this->query->toArray(), 45 | $this->value, 46 | ], 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Query/Logic/GreaterThanOrEqualToLogic.php: -------------------------------------------------------------------------------- 1 | value = $value; 33 | $this->rethink = $rethink; 34 | 35 | $this->query = $query; 36 | } 37 | 38 | public function toArray(): array 39 | { 40 | return 41 | [ 42 | TermType::GE, 43 | [ 44 | $this->query->toArray(), 45 | $this->value, 46 | ], 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Query/Logic/LowerThanLogic.php: -------------------------------------------------------------------------------- 1 | value = $value; 33 | $this->rethink = $rethink; 34 | 35 | $this->query = $query; 36 | } 37 | 38 | public function toArray(): array 39 | { 40 | return 41 | [ 42 | TermType::LT, 43 | [ 44 | $this->query->toArray(), 45 | $this->value, 46 | ], 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Query/Logic/LowerThanOrEqualToLogic.php: -------------------------------------------------------------------------------- 1 | value = $value; 33 | $this->rethink = $rethink; 34 | 35 | $this->query = $query; 36 | } 37 | 38 | public function toArray(): array 39 | { 40 | return 41 | [ 42 | TermType::LE, 43 | [ 44 | $this->query->toArray(), 45 | $this->value, 46 | ], 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Query/Logic/NotEqualLogic.php: -------------------------------------------------------------------------------- 1 | value = $value; 33 | $this->rethink = $rethink; 34 | 35 | $this->query = $query; 36 | } 37 | 38 | public function toArray(): array 39 | { 40 | return 41 | [ 42 | TermType::NE, 43 | [ 44 | $this->query->toArray(), 45 | $this->value, 46 | ], 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Query/Logic/NotLogic.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 28 | $this->query = $query; 29 | } 30 | 31 | public function toArray(): array 32 | { 33 | return 34 | [ 35 | TermType::NOT, 36 | [ 37 | $this->query->toArray(), 38 | ], 39 | ]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Query/Logic/OrLogic.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 34 | 35 | $this->functionOne = $functionOne; 36 | $this->functionTwo = $functionTwo; 37 | } 38 | 39 | public function toArray(): array 40 | { 41 | return 42 | [ 43 | TermType::OR, 44 | [ 45 | $this->functionOne->toArray(), 46 | $this->functionTwo->toArray(), 47 | ], 48 | ]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Query/Manipulation/GetField.php: -------------------------------------------------------------------------------- 1 | field = $field; 37 | $this->rethink = $rethink; 38 | 39 | $this->query = $query; 40 | } 41 | 42 | public function toArray(): array 43 | { 44 | if ($this->query !== null) { 45 | return [ 46 | TermType::GET_FIELD, 47 | [ 48 | $this->query->toArray(), 49 | $this->field, 50 | ], 51 | ]; 52 | } 53 | 54 | return [ 55 | TermType::GET_FIELD, 56 | [ 57 | [ 58 | TermType::IMPLICIT_VAR, 59 | [], 60 | ], 61 | $this->field, 62 | ], 63 | ]; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Query/Manipulation/HasFields.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 38 | $this->query = $query; 39 | $this->keys = $keys; 40 | } 41 | 42 | public function toArray(): array 43 | { 44 | if (\count($this->keys) === 1) { 45 | $keysQuery = implode($this->keys); 46 | } else { 47 | $keysQuery = [ 48 | TermType::MAKE_ARRAY, 49 | array_values($this->keys) 50 | ]; 51 | } 52 | 53 | return [ 54 | TermType::HAS_FIELDS, 55 | [ 56 | $this->query->toArray(), 57 | $keysQuery 58 | ] 59 | ]; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Query/Manipulation/Keys.php: -------------------------------------------------------------------------------- 1 | query = $query; 31 | $this->rethink = $rethink; 32 | } 33 | 34 | public function toArray(): array 35 | { 36 | return [ 37 | TermType::KEYS, 38 | [ 39 | $this->query->toArray() 40 | ] 41 | ]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Query/Manipulation/LogicTrait.php: -------------------------------------------------------------------------------- 1 | rethink, $field, $this->query); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Query/Manipulation/ManipulationTrait.php: -------------------------------------------------------------------------------- 1 | rethink, /** @scrutinizer ignore-type */ $this, $keys); 13 | } 14 | 15 | public function without(...$keys): Without 16 | { 17 | return new Without($this->rethink, /** @scrutinizer ignore-type */ $this, $keys); 18 | } 19 | 20 | public function keys(): Keys 21 | { 22 | return new Keys($this->rethink, /** @scrutinizer ignore-type */ $this); 23 | } 24 | 25 | public function values(): Values 26 | { 27 | return new Values($this->rethink, /** @scrutinizer ignore-type */ $this); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Query/Manipulation/Pluck.php: -------------------------------------------------------------------------------- 1 | query = $query; 38 | $this->rethink = $rethink; 39 | $this->keys = $keys; 40 | } 41 | 42 | public function toArray(): array 43 | { 44 | if (\count($this->keys) === 1) { 45 | $keysQuery = implode($this->keys); 46 | } else { 47 | $keysQuery = [ 48 | TermType::MAKE_ARRAY, 49 | array_values($this->keys) 50 | ]; 51 | } 52 | 53 | return [ 54 | TermType::PLUCK, 55 | [ 56 | $this->query->toArray(), 57 | $keysQuery 58 | ] 59 | ]; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Query/Manipulation/RowHasFields.php: -------------------------------------------------------------------------------- 1 | keys = $keys; 28 | $this->rethink = $rethink; 29 | } 30 | 31 | public function toArray(): array 32 | { 33 | if (\count($this->keys) === 1) { 34 | $keysQuery = implode($this->keys); 35 | } else { 36 | $keysQuery = [ 37 | TermType::MAKE_ARRAY, 38 | array_values($this->keys) 39 | ]; 40 | } 41 | 42 | return [ 43 | TermType::HAS_FIELDS, 44 | [ 45 | [ 46 | TermType::IMPLICIT_VAR 47 | ], 48 | $keysQuery 49 | ] 50 | ]; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Query/Manipulation/Values.php: -------------------------------------------------------------------------------- 1 | query = $query; 37 | $this->rethink = $rethink; 38 | } 39 | 40 | public function toArray(): array 41 | { 42 | return [ 43 | TermType::VALUES, 44 | [ 45 | $this->query->toArray() 46 | ], 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Query/Manipulation/Without.php: -------------------------------------------------------------------------------- 1 | query = $query; 38 | $this->rethink = $rethink; 39 | $this->keys = $keys; 40 | } 41 | 42 | public function toArray(): array 43 | { 44 | if (\count($this->keys) === 1) { 45 | $keysQuery = implode($this->keys); 46 | } else { 47 | $keysQuery = [ 48 | TermType::MAKE_ARRAY, 49 | array_values($this->keys) 50 | ]; 51 | } 52 | 53 | return [ 54 | TermType::WITHOUT, 55 | [ 56 | $this->query->toArray(), 57 | $keysQuery 58 | ] 59 | ]; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Query/Operation/Between.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 49 | $this->query = $query; 50 | $this->min = $min; 51 | $this->max = $max; 52 | $this->options = $options; 53 | } 54 | 55 | public function toArray(): array 56 | { 57 | $args = [ 58 | $this->query->toArray(), 59 | $this->min, 60 | $this->max, 61 | ]; 62 | 63 | if ($this->options !== null) { 64 | $args = array_merge($args, [$this->options]); 65 | } 66 | 67 | return [ 68 | TermType::BETWEEN, 69 | $args, 70 | ]; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Query/Operation/Changes.php: -------------------------------------------------------------------------------- 1 | query = $query; 31 | $this->rethink = $rethink; 32 | $this->options = $options; 33 | } 34 | 35 | public function toArray(): array 36 | { 37 | $query = [ 38 | TermType::CHANGES, 39 | [ 40 | $this->query->toArray(), 41 | ], 42 | ]; 43 | 44 | if (!empty($this->options)) { 45 | $query[] = $this->options; 46 | } 47 | 48 | return $query; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Query/Operation/DbCreate.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 22 | 23 | $this->name = $name; 24 | } 25 | 26 | public function toArray(): array 27 | { 28 | return [ 29 | TermType::DB_CREATE, 30 | [ 31 | [ 32 | TermType::DATUM, 33 | $this->name, 34 | ], 35 | ], 36 | ]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Query/Operation/DbDrop.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 22 | 23 | $this->name = $name; 24 | } 25 | 26 | public function toArray(): array 27 | { 28 | return [ 29 | TermType::DB_DROP, 30 | [ 31 | [ 32 | TermType::DATUM, 33 | $this->name, 34 | ], 35 | ], 36 | ]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Query/Operation/DbList.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 17 | } 18 | 19 | public function toArray(): array 20 | { 21 | return [ 22 | TermType::DB_LIST, 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Query/Operation/Delete.php: -------------------------------------------------------------------------------- 1 | query = $query; 23 | $this->rethink = $rethink; 24 | } 25 | 26 | public function toArray(): array 27 | { 28 | return [ 29 | TermType::DELETE, 30 | [ 31 | $this->query->toArray(), 32 | ], 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Query/Operation/Filter.php: -------------------------------------------------------------------------------- 1 | query = $query; 39 | $this->predicate = [$predicate]; 40 | $this->rethink = $rethink; 41 | } 42 | 43 | public function toArray(): array 44 | { 45 | $jsonDocuments = []; 46 | foreach ($this->predicate as $key => $document) { 47 | $jsonDocuments[] = json_encode($document); 48 | } 49 | 50 | return [ 51 | TermType::FILTER, 52 | [ 53 | $this->query->toArray(), 54 | [ 55 | TermType::JSON, 56 | $jsonDocuments, 57 | ], 58 | ], 59 | ]; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Query/Operation/FilterByRow.php: -------------------------------------------------------------------------------- 1 | query = $query; 37 | $this->functionQuery = $manipulation; 38 | $this->rethink = $rethink; 39 | } 40 | 41 | public function toArray(): array 42 | { 43 | return [ 44 | TermType::FILTER, 45 | [ 46 | $this->query->toArray(), 47 | $this->functionQuery->toArray(), 48 | ], 49 | ]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Query/Operation/Get.php: -------------------------------------------------------------------------------- 1 | query = $query; 34 | $this->key = $key; 35 | $this->rethink = $rethink; 36 | } 37 | 38 | public function toArray(): array 39 | { 40 | return [ 41 | TermType::GET, 42 | [ 43 | $this->query->toArray(), 44 | [ 45 | TermType::DATUM, 46 | $this->key, 47 | ], 48 | ], 49 | ]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Query/Operation/GetAll.php: -------------------------------------------------------------------------------- 1 | query = $query; 39 | $this->keys = $keys; 40 | $this->rethink = $rethink; 41 | } 42 | 43 | public function toArray(): array 44 | { 45 | return [ 46 | TermType::GET_ALL, 47 | array_merge( 48 | [$this->query->toArray()], 49 | array_values($this->keys) 50 | ), 51 | ]; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Query/Operation/IndexCreate.php: -------------------------------------------------------------------------------- 1 | query = $query; 31 | $this->rethink = $rethink; 32 | 33 | $this->name = $name; 34 | } 35 | 36 | public function toArray(): array 37 | { 38 | return [ 39 | TermType::INDEX_CREATE, 40 | [ 41 | $this->query->toArray(), 42 | [ 43 | TermType::DATUM, 44 | $this->name, 45 | ], 46 | ], 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Query/Operation/IndexDrop.php: -------------------------------------------------------------------------------- 1 | query = $query; 31 | $this->rethink = $rethink; 32 | 33 | $this->name = $name; 34 | } 35 | 36 | public function toArray(): array 37 | { 38 | return [ 39 | TermType::INDEX_DROP, 40 | [ 41 | $this->query->toArray(), 42 | [ 43 | TermType::DATUM, 44 | $this->name, 45 | ], 46 | ], 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Query/Operation/IndexList.php: -------------------------------------------------------------------------------- 1 | query = $query; 23 | $this->rethink = $rethink; 24 | } 25 | 26 | public function toArray(): array 27 | { 28 | return [ 29 | TermType::INDEX_LIST, 30 | [ 31 | $this->query->toArray(), 32 | ], 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Query/Operation/IndexRename.php: -------------------------------------------------------------------------------- 1 | query = $query; 37 | $this->rethink = $rethink; 38 | 39 | $this->oldValue = $oldValue; 40 | $this->newValue = $newValue; 41 | } 42 | 43 | public function toArray(): array 44 | { 45 | return [ 46 | TermType::INDEX_RENAME, 47 | [ 48 | $this->query->toArray(), 49 | [ 50 | TermType::DATUM, 51 | $this->oldValue, 52 | ], 53 | [ 54 | TermType::DATUM, 55 | $this->newValue, 56 | ], 57 | ], 58 | ]; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Query/Operation/Insert.php: -------------------------------------------------------------------------------- 1 | query = $query; 30 | $this->documents = [$documents]; 31 | $this->rethink = $rethink; 32 | } 33 | 34 | public function toArray(): array 35 | { 36 | $jsonDocuments = []; 37 | foreach ($this->documents as $key => $document) { 38 | $jsonDocuments[] = json_encode($document); 39 | } 40 | 41 | return [ 42 | TermType::INSERT, 43 | [ 44 | $this->query->toArray(), 45 | [ 46 | TermType::JSON, 47 | $jsonDocuments, 48 | ], 49 | ], 50 | ]; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Query/Operation/OperationTrait.php: -------------------------------------------------------------------------------- 1 | rethink, /** @scrutinizer ignore-type */ $this, $options); 14 | } 15 | 16 | public function delete(): QueryInterface 17 | { 18 | return new Delete($this->rethink, /** @scrutinizer ignore-type */ $this); 19 | } 20 | 21 | public function filter($value) 22 | { 23 | if (\is_object($value)) { 24 | return new FilterByRow($this->rethink, /** @scrutinizer ignore-type */ $this, $value); 25 | } 26 | 27 | return new Filter($this->rethink, /** @scrutinizer ignore-type */ $this, $value); 28 | } 29 | 30 | public function getAll(...$keys): GetAll 31 | { 32 | return new GetAll($this->rethink, /** @scrutinizer ignore-type */ $this, $keys); 33 | } 34 | 35 | public function update(array $elements): QueryInterface 36 | { 37 | return new Update($this->rethink, /** @scrutinizer ignore-type */ $this, $elements); 38 | } 39 | 40 | public function insert(array $document): QueryInterface 41 | { 42 | return new Insert($this->rethink, /** @scrutinizer ignore-type */ $this, $document); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Query/Operation/Sync.php: -------------------------------------------------------------------------------- 1 | query = $query; 25 | $this->rethink = $rethink; 26 | } 27 | 28 | public function toArray(): array 29 | { 30 | return [ 31 | TermType::SYNC, 32 | [ 33 | $this->query->toArray(), 34 | ], 35 | ]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Query/Operation/TableCreate.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 22 | 23 | $this->name = $name; 24 | } 25 | 26 | public function toArray(): array 27 | { 28 | return [ 29 | TermType::TABLE_CREATE, 30 | [ 31 | [ 32 | TermType::DATUM, 33 | $this->name, 34 | ], 35 | ], 36 | ]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Query/Operation/TableDrop.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 22 | 23 | $this->name = $name; 24 | } 25 | 26 | public function toArray(): array 27 | { 28 | return [ 29 | TermType::TABLE_DROP, 30 | [ 31 | [ 32 | TermType::DATUM, 33 | $this->name, 34 | ], 35 | ], 36 | ]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Query/Operation/TableList.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 17 | } 18 | 19 | public function toArray(): array 20 | { 21 | return [ 22 | TermType::TABLE_LIST, 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Query/Operation/Update.php: -------------------------------------------------------------------------------- 1 | query = $query; 31 | $this->elements = $elements; 32 | $this->rethink = $rethink; 33 | } 34 | 35 | public function toArray(): array 36 | { 37 | $jsonElements = json_encode($this->elements); 38 | 39 | return [ 40 | TermType::UPDATE, 41 | [ 42 | $this->query->toArray(), 43 | [ 44 | TermType::JSON, 45 | [ 46 | $jsonElements 47 | ], 48 | ], 49 | ], 50 | ]; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Query/Options.php: -------------------------------------------------------------------------------- 1 | db = [ 18 | TermType::DB, 19 | [$name], 20 | ]; 21 | 22 | return $this; 23 | } 24 | 25 | public function jsonSerialize() 26 | { 27 | if (empty($this->db)) { 28 | return []; 29 | } 30 | 31 | return [ 32 | 'db' => $this->db 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Query/OptionsInterface.php: -------------------------------------------------------------------------------- 1 | asc($key); 21 | } 22 | 23 | public function asc(string $key): Ordening 24 | { 25 | $this->query = [ 26 | TermType::ASC, 27 | [ 28 | $key, 29 | ], 30 | ]; 31 | 32 | return $this; 33 | } 34 | 35 | public function desc(string $key): Ordening 36 | { 37 | $this->query = [ 38 | TermType::DESC, 39 | [ 40 | $key, 41 | ], 42 | ]; 43 | 44 | return $this; 45 | } 46 | 47 | public function toArray(): array 48 | { 49 | return $this->query; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Query/QueryInterface.php: -------------------------------------------------------------------------------- 1 | rethink = $rethink; 38 | 39 | 40 | $this->query = [ 41 | TermType::TABLE, 42 | [ 43 | $name, 44 | ], 45 | ]; 46 | } 47 | 48 | public function changes(array $options = null): Changes 49 | { 50 | return new Changes($this->rethink, $this, $options); 51 | } 52 | 53 | public function get($key): Get 54 | { 55 | return new Get($this->rethink, $this, $key); 56 | } 57 | 58 | public function indexCreate(string $name): AbstractQuery 59 | { 60 | return new IndexCreate($this->rethink, $this, $name); 61 | } 62 | 63 | public function indexDrop(string $name): AbstractQuery 64 | { 65 | return new IndexDrop($this->rethink, $this, $name); 66 | } 67 | 68 | public function indexList(): AbstractQuery 69 | { 70 | return new IndexList($this->rethink, $this); 71 | } 72 | 73 | public function indexRename(string $oldValue, string $newValue): AbstractQuery 74 | { 75 | return new IndexRename($this->rethink, $this, $oldValue, $newValue); 76 | } 77 | 78 | public function between($min, $max, array $options = null): Between 79 | { 80 | return new Between($this->rethink, $this, $min, $max, $options); 81 | } 82 | 83 | public function hasFields(...$keys) 84 | { 85 | return new HasFields($this->rethink, $this, $keys); 86 | } 87 | 88 | public function sync() 89 | { 90 | return new Sync($this->rethink, $this); 91 | } 92 | 93 | public function toArray(): array 94 | { 95 | return $this->query; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Query/Transformation/IsEmpty.php: -------------------------------------------------------------------------------- 1 | query = $query; 23 | $this->rethink = $rethink; 24 | } 25 | 26 | public function toArray(): array 27 | { 28 | return [ 29 | TermType::IS_EMPTY, 30 | [ 31 | $this->query->toArray(), 32 | ], 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Query/Transformation/Limit.php: -------------------------------------------------------------------------------- 1 | query = $query; 37 | $this->n = $n; 38 | $this->rethink = $rethink; 39 | } 40 | 41 | public function toArray(): array 42 | { 43 | return [ 44 | TermType::LIMIT, 45 | [ 46 | $this->query->toArray(), 47 | [ 48 | TermType::DATUM, 49 | $this->n, 50 | ], 51 | ], 52 | ]; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Query/Transformation/OrderBy.php: -------------------------------------------------------------------------------- 1 | query = $query; 35 | $this->key = $key; 36 | $this->rethink = $rethink; 37 | } 38 | 39 | public function toArray(): array 40 | { 41 | $ordering = $this->key instanceof QueryInterface ? $this->key->toArray() : $this->key; 42 | 43 | return [ 44 | TermType::ORDER_BY, 45 | [ 46 | $this->query->toArray(), 47 | $ordering, 48 | ], 49 | ]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Query/Transformation/Skip.php: -------------------------------------------------------------------------------- 1 | query = $query; 35 | $this->n = $n; 36 | $this->rethink = $rethink; 37 | } 38 | 39 | public function toArray(): array 40 | { 41 | return [ 42 | TermType::SKIP, 43 | [ 44 | $this->query->toArray(), 45 | [ 46 | TermType::DATUM, 47 | $this->n, 48 | ], 49 | ], 50 | ]; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Query/Transformation/TransformationTrait.php: -------------------------------------------------------------------------------- 1 | rethink, /** @scrutinizer ignore-type */ $this); 13 | } 14 | 15 | public function limit($value): Limit 16 | { 17 | return new Limit($this->rethink, /** @scrutinizer ignore-type */ $this, $value); 18 | } 19 | 20 | public function skip($value): Skip 21 | { 22 | return new Skip($this->rethink, /** @scrutinizer ignore-type */ $this, $value); 23 | } 24 | 25 | public function orderBy($key): OrderBy 26 | { 27 | return new OrderBy($this->rethink, /** @scrutinizer ignore-type */ $this, $key); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Response/Cursor.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 49 | $this->token = $token; 50 | $this->addResponse($response); 51 | $this->message = $message; 52 | } 53 | 54 | /** 55 | * @throws \Exception 56 | */ 57 | public function current() 58 | { 59 | $this->seek(); 60 | 61 | if (!$this->valid()) { 62 | return; 63 | } 64 | 65 | if ($this->response->isAtomic()) { 66 | return $this->response->getData(); 67 | } 68 | 69 | return $this->response->getData()[$this->index]; 70 | } 71 | 72 | /** 73 | * @throws \Exception 74 | */ 75 | public function next(): void 76 | { 77 | $this->index++; 78 | 79 | $this->seek(); 80 | } 81 | 82 | public function key(): int 83 | { 84 | return $this->index; 85 | } 86 | 87 | public function valid(): bool 88 | { 89 | return (!$this->isComplete || ($this->index < $this->size)); 90 | } 91 | 92 | /** 93 | * @throws ConnectionException 94 | */ 95 | public function rewind(): void 96 | { 97 | if ($this->index === 0) { 98 | return; 99 | } 100 | 101 | $this->close(); 102 | $this->addResponse($this->connection->rewindFromCursor($this->message)); 103 | } 104 | 105 | public function count() 106 | { 107 | return $this->size; 108 | } 109 | 110 | private function addResponse(ResponseInterface $response) 111 | { 112 | $this->index = 0; 113 | $this->isComplete = $response->getType() === ResponseType::SUCCESS_SEQUENCE; 114 | 115 | if (\is_null($response->getData())) { 116 | $this->size = 0; 117 | } else { 118 | $this->size = $response->isAtomic() ? 1 : \count($response->getData()); 119 | } 120 | 121 | $this->response = $response; 122 | } 123 | 124 | /** 125 | * @throws \Exception 126 | */ 127 | private function seek(): void 128 | { 129 | if ($this->index >= $this->size && !$this->isComplete) { 130 | $this->request(); 131 | } 132 | } 133 | 134 | /** 135 | * @throws \Exception 136 | */ 137 | private function request(): void 138 | { 139 | try { 140 | $response = $this->connection->continueQuery($this->token); 141 | $this->addResponse($response); 142 | } catch (\Exception $e) { 143 | $this->isComplete = true; 144 | $this->close(); 145 | 146 | throw $e; 147 | } 148 | } 149 | 150 | private function close(): void 151 | { 152 | if (!$this->isComplete) { 153 | $this->connection->stopQuery($this->token); 154 | $this->isComplete = true; 155 | } 156 | 157 | $this->index = 0; 158 | $this->size = 0; 159 | $this->response = null; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/Response/Response.php: -------------------------------------------------------------------------------- 1 | type = $t; 35 | !$r ?: $this->data = $r; 36 | !$b ?: $this->backtrace = $b; 37 | !$p ?: $this->profile = $p; 38 | !$n ?: $this->note = $n; 39 | } 40 | 41 | public function getType(): ?int 42 | { 43 | return $this->type; 44 | } 45 | 46 | public function getData() 47 | { 48 | if (!\is_array($this->data)) { 49 | return null; 50 | } 51 | 52 | if (isset($this->data[0]['$reql_type$']) && $this->data[0]['$reql_type$'] === 'GROUPED_DATA') { 53 | $groups = []; 54 | foreach ($this->data[0]['data'] as $group) { 55 | $groups[] = [ 56 | 'group' => $group[0], 57 | 'reduction' => $group[1], 58 | ]; 59 | } 60 | 61 | return $groups; 62 | } 63 | 64 | return \count($this->data) === 1 && array_key_exists(0, $this->data) ? $this->data[0] : $this->data; 65 | } 66 | 67 | public function isAtomic(): bool 68 | { 69 | return \is_string($this->data) || (!\is_null($this->data) && \count($this->data) === 1); 70 | } 71 | 72 | public function getBacktrace(): ?array 73 | { 74 | return $this->backtrace; 75 | } 76 | 77 | public function getProfile(): ?array 78 | { 79 | return $this->profile; 80 | } 81 | 82 | public function getNote(): ?array 83 | { 84 | return $this->note; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Response/ResponseInterface.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 27 | $this->builder = new Builder($this); 28 | } 29 | 30 | public function db(): Database 31 | { 32 | return $this->builder->database(); 33 | } 34 | 35 | public function dbCreate(string $name): Database 36 | { 37 | return $this->builder->database()->dbCreate($name); 38 | } 39 | 40 | public function dbDrop(string $name): Database 41 | { 42 | return $this->builder->database()->dbDrop($name); 43 | } 44 | 45 | public function dbList(): Database 46 | { 47 | return $this->builder->database()->dbList(); 48 | } 49 | 50 | public function connection(): ConnectionInterface 51 | { 52 | return $this->connection; 53 | } 54 | 55 | public function table(string $name): Table 56 | { 57 | return $this->builder->table($name); 58 | } 59 | 60 | public function desc($key): Ordening 61 | { 62 | return $this->builder->ordening($key)->desc($key); 63 | } 64 | 65 | public function asc($key): Ordening 66 | { 67 | return $this->builder->ordening($key)->asc($key); 68 | } 69 | 70 | public function row(?string $value = null): Row 71 | { 72 | return $this->builder->row($value); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/RethinkInterface.php: -------------------------------------------------------------------------------- 1 | isCircularReference($object, $context)) { 17 | return $this->handleCircularReference($object); 18 | } 19 | 20 | if (!$object instanceof \JsonSerializable && !$object instanceof \stdClass) { 21 | throw new InvalidArgumentException( 22 | sprintf('The '.get_class($object).' must implement "%s".', \JsonSerializable::class) 23 | ); 24 | } 25 | 26 | if (!$this->serializer instanceof NormalizerInterface) { 27 | throw new LogicException('Cannot normalize object because injected serializer is not a normalizer'); 28 | } 29 | 30 | if ($object instanceof \stdClass) { 31 | return (array) $object; 32 | } 33 | 34 | if ($object instanceof OptionsInterface) { 35 | return (object) $object->jsonSerialize(); 36 | } 37 | 38 | return $this->serializer->normalize($object->jsonSerialize(), $format, $context); 39 | } 40 | 41 | public function supportsNormalization($data, $format = null) 42 | { 43 | return \is_object($data) && $format === 'json'; 44 | } 45 | 46 | public function supportsDenormalization($data, $type, $format = null) 47 | { 48 | return false; 49 | } 50 | 51 | public function denormalize($data, $class, $format = null, array $context = []) 52 | { 53 | throw new LogicException(sprintf('Cannot denormalize with "%s".', __CLASS__)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Types/Frame/FrameType.php: -------------------------------------------------------------------------------- 1 | [ 10 | 'hostname' => 'localhost', 11 | 'port' => 28015, 12 | 'dbname' => 'test', 13 | 'user' => 'admin', 14 | 'password' => '', 15 | 'timeout' => 5, 16 | 'timeout_stream' => 10, 17 | ], 18 | ] 19 | ); 20 | -------------------------------------------------------------------------------- /test/config_scrutinizer.php: -------------------------------------------------------------------------------- 1 | [ 7 | 'hostname' => 'localhost', 8 | 'port' => 28015, 9 | 'dbname' => 'test', 10 | 'user' => 'admin', 11 | 'password' => '', 12 | 'timeout' => 5, 13 | 'timeout_stream' => 10, 14 | ], 15 | ] 16 | ); 17 | -------------------------------------------------------------------------------- /test/integration/AbstractTestCase.php: -------------------------------------------------------------------------------- 1 | allowMockingNonExistentMethods(false); 39 | 40 | // Make sure we destroy a failed previous unit test database schema 41 | $this->tearDown(); 42 | 43 | parent::setUp(); 44 | } 45 | 46 | protected function r() 47 | { 48 | if ($this->r !== null) { 49 | return $this->r; 50 | } 51 | 52 | $name = 'phpunit_default'; 53 | $options = new Options(PHPUNIT_CONNECTIONS[$name]); 54 | 55 | /** @var ConnectionInterface $connection */ 56 | $connection = $this->createConnection($name)->connect(); 57 | $connection->connect()->use($options->getDbName()); 58 | 59 | $this->r = new Rethink($connection); 60 | 61 | /** @var ResponseInterface $res */ 62 | $res = $this->r->dbList()->run(); 63 | if (\is_array($res->getData()) && !\in_array($options->getDbName(), $res->getData(), true)) { 64 | $this->r->dbCreate($options->getDbName())->run(); 65 | } 66 | 67 | return $this->r; 68 | } 69 | 70 | protected function tearDown() 71 | { 72 | if ($this->r === null) { 73 | return; 74 | } 75 | 76 | $name = 'phpunit_default'; 77 | $options = new Options(PHPUNIT_CONNECTIONS[$name]); 78 | 79 | /** @var ResponseInterface $res */ 80 | $res = $this->r->dbList()->run(); 81 | if (\is_array($res->getData()) && \in_array($options->getDbName(), $res->getData(), true)) { 82 | $this->r->dbDrop($options->getDbName())->run(); 83 | } 84 | } 85 | 86 | /** 87 | * @param string $name 88 | * @return ConnectionInterface 89 | * @throws Exception 90 | */ 91 | protected function createConnection(string $name): ConnectionInterface 92 | { 93 | $options = new Options(PHPUNIT_CONNECTIONS[$name]); 94 | 95 | $connection = new Connection( 96 | function () use ($options) { 97 | return new Socket( 98 | $options 99 | ); 100 | }, 101 | new Handshake($options->getUser(), $options->getPassword(), Version::V1_0), 102 | $options->getDbName(), 103 | new Serializer( 104 | [new QueryNormalizer()], 105 | [new JsonEncoder()] 106 | ), 107 | new Serializer( 108 | [new ObjectNormalizer()], 109 | [new JsonEncoder()] 110 | ) 111 | ); 112 | 113 | $this->connections[] = $connection; 114 | 115 | return $connection; 116 | } 117 | 118 | public function __destruct() 119 | { 120 | foreach ($this->connections as $connection) { 121 | $connection->close(); 122 | } 123 | 124 | Mockery::close(); 125 | } 126 | 127 | /** 128 | * @param $status 129 | * @param $data 130 | * @throws \Exception 131 | */ 132 | protected function assertObStatus($status, $data) 133 | { 134 | $res = []; 135 | $statuses = [ 136 | 'tables_created', 137 | 'tables_dropped', 138 | 'created', 139 | 'dropped', 140 | 'renamed', 141 | 'unchanged', 142 | 'skipped', 143 | 'replaced', 144 | 'inserted', 145 | 'errors', 146 | 'deleted', 147 | ]; 148 | $data = new ArrayObject($data); 149 | 150 | foreach ($statuses as $s) { 151 | $status[$s] = $status[$s] ?? 0; 152 | } 153 | 154 | $data->setFlags($data::ARRAY_AS_PROPS); 155 | 156 | foreach ($statuses as $s) { 157 | $res[$s] = $data[$s] ?? 0; 158 | } 159 | 160 | $this->assertEquals($status, $res); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /test/integration/Aggregation/AvgTest.php: -------------------------------------------------------------------------------- 1 | insertDocumentWithNumber(5, 5); 18 | $this->insertDocumentWithNumber(4, 10); 19 | $this->insertDocumentWithNumber(3, 15); 20 | $this->insertDocumentWithNumber(2, 20); 21 | $this->insertDocumentWithNumber(1, 25); 22 | 23 | /** @var ResponseInterface $res */ 24 | $res = $this->r() 25 | ->table('tabletest') 26 | ->avg('number') 27 | ->run(); 28 | 29 | $this->assertEquals(15, $res->getData()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/integration/Aggregation/CountTest.php: -------------------------------------------------------------------------------- 1 | insertDocumentWithNumber(5, 5); 17 | $this->insertDocumentWithNumber(4, 10); 18 | $this->insertDocumentWithNumber(3, 15); 19 | $this->insertDocumentWithNumber(2, 20); 20 | $this->insertDocumentWithNumber(1, 25); 21 | 22 | $res = $this->r() 23 | ->table('tabletest') 24 | ->count() 25 | ->run(); 26 | 27 | $this->assertEquals(5, $res->getData()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/integration/Aggregation/GroupTest.php: -------------------------------------------------------------------------------- 1 | insertDocumentWithNumber(5, 10); 18 | $this->insertDocumentWithNumber(4, 10); 19 | $this->insertDocumentWithNumber(3, 20); 20 | $this->insertDocumentWithNumber(2, 20); 21 | $this->insertDocumentWithNumber(1, 30); 22 | 23 | /** @var ResponseInterface $res */ 24 | $res = $this->r() 25 | ->table('tabletest') 26 | ->group('description') 27 | ->run(); 28 | 29 | $this->assertCount(1, $res->getData()); 30 | 31 | $this->assertEquals('A document description.', $res->getData()[0]['group']); 32 | $this->assertCount(1, $res->getData()); 33 | } 34 | 35 | /** 36 | * @return void 37 | * @throws \Exception 38 | */ 39 | public function testGroupByNumber(): void 40 | { 41 | $this->insertDocumentWithNumber(5, 10); 42 | $this->insertDocumentWithNumber(4, 10); 43 | $this->insertDocumentWithNumber(3, 20); 44 | $this->insertDocumentWithNumber(2, 20); 45 | $this->insertDocumentWithNumber(1, 30); 46 | 47 | /** @var ResponseInterface $res */ 48 | $res = $this->r() 49 | ->table('tabletest') 50 | ->group('number') 51 | ->run(); 52 | 53 | $this->assertCount(3, $res->getData()); 54 | 55 | $this->assertEquals(10, $res->getData()[0]['group']); 56 | $this->assertCount(2, $res->getData()[0]['reduction']); 57 | 58 | $this->assertEquals(20, $res->getData()[1]['group']); 59 | $this->assertCount(2, $res->getData()[1]['reduction']); 60 | 61 | $this->assertEquals(30, $res->getData()[2]['group']); 62 | $this->assertCount(1, $res->getData()[2]['reduction']); 63 | } 64 | 65 | /** 66 | * @return void 67 | * @throws \Exception 68 | */ 69 | public function testGroupAndMaxByNumberReductionByNumberAndUngroupOrderByNumber(): void 70 | { 71 | $this->insertDocumentWithNumber('Bob', 10); 72 | $this->insertDocumentWithNumber('Alice', 20); 73 | $this->insertDocumentWithNumber('Bob', 30); 74 | $this->insertDocumentWithNumber('Alice', 40); 75 | $this->insertDocumentWithNumber('Harry', 50); 76 | $this->insertDocumentWithNumber('Harry', 45); 77 | 78 | /** @var ResponseInterface $res */ 79 | $res = $this->r() 80 | ->table('tabletest') 81 | ->group('id')->max('number')->getField('number') 82 | ->ungroup() 83 | ->orderBy($this->r()->desc('number')) 84 | ->run(); 85 | 86 | $this->assertCount(3, $res->getData()); 87 | 88 | $this->assertEquals('Alice', $res->getData()[0]['group']); 89 | $this->assertEquals(20, $res->getData()[0]['reduction'][0]); 90 | 91 | $this->assertEquals('Bob', $res->getData()[1]['group']); 92 | $this->assertEquals(10, $res->getData()[1]['reduction'][0]); 93 | 94 | $this->assertEquals('Harry', $res->getData()[2]['group']); 95 | $this->assertEquals(50, $res->getData()[2]['reduction'][0]); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /test/integration/Aggregation/MaxTest.php: -------------------------------------------------------------------------------- 1 | insertDocumentWithNumber(5, 99); 18 | $this->insertDocumentWithNumber(4, 77); 19 | $this->insertDocumentWithNumber(3, 1045); 20 | $this->insertDocumentWithNumber(2, 4); 21 | $this->insertDocumentWithNumber(1, 43534); 22 | 23 | /** @var ResponseInterface $res */ 24 | $res = $this->r() 25 | ->table('tabletest') 26 | ->max('number') 27 | ->run(); 28 | 29 | /** @var array $array */ 30 | $array = $res->getData(); 31 | 32 | $this->assertArraySubset(['number' => 43534], $array); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/integration/Aggregation/MinTest.php: -------------------------------------------------------------------------------- 1 | insertDocumentWithNumber(5, 99); 18 | $this->insertDocumentWithNumber(4, 77); 19 | $this->insertDocumentWithNumber(3, 1045); 20 | $this->insertDocumentWithNumber(2, 4); 21 | $this->insertDocumentWithNumber(1, 43534); 22 | 23 | /** @var ResponseInterface $res */ 24 | $res = $this->r() 25 | ->table('tabletest') 26 | ->min('number') 27 | ->run(); 28 | 29 | /** @var array $array */ 30 | $array = $res->getData(); 31 | 32 | $this->assertArraySubset(['number' => 4], $array); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/integration/Aggregation/SumTest.php: -------------------------------------------------------------------------------- 1 | insertDocumentWithNumber(5, 5); 18 | $this->insertDocumentWithNumber(4, 10); 19 | $this->insertDocumentWithNumber(3, 15); 20 | $this->insertDocumentWithNumber(2, 20); 21 | $this->insertDocumentWithNumber(1, 25); 22 | 23 | /** @var ResponseInterface $res */ 24 | $res = $this->r() 25 | ->table('tabletest') 26 | ->sum('number') 27 | ->run(); 28 | 29 | $this->assertEquals(75, $res->getData()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/integration/Connection/ConnectionTest.php: -------------------------------------------------------------------------------- 1 | optionsMock = Mockery::mock(OptionsInterface::class); 26 | } 27 | 28 | /** 29 | * @throws \Exception 30 | */ 31 | public function testConnect() 32 | { 33 | /** @var ConnectionInterface $connection */ 34 | $connection = $this->createConnection('phpunit_default')->connect(); 35 | $result = $connection->expr('foo'); 36 | 37 | $this->assertInstanceOf(ResponseInterface::class, $result); 38 | $this->assertEquals('foo', $result->getData()); 39 | } 40 | 41 | /** 42 | * @throws \Exception 43 | */ 44 | public function testServer() 45 | { 46 | /** @var ConnectionInterface $connection */ 47 | $res = $this->createConnection('phpunit_default')->connect()->server(); 48 | 49 | $this->assertEquals(QueryType::SERVER_INFO, $res->getType()); 50 | $this->assertInternalType('string', $res->getData()['name']); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/integration/Manipulation/HasFieldsTest.php: -------------------------------------------------------------------------------- 1 | insertDocument(1); 17 | $this->insertDocument(2); 18 | $this->insertDocumentWithNumber(3, 1); 19 | 20 | /** @var Cursor $cursor */ 21 | $cursor = $this->r() 22 | ->table('tabletest') 23 | ->hasFields('number') 24 | ->run(); 25 | 26 | $this->assertInstanceOf(\Iterator::class, $cursor); 27 | $this->assertEquals(1, $cursor->count()); 28 | } 29 | 30 | /** 31 | * @throws \Exception 32 | */ 33 | public function testHasField() 34 | { 35 | $this->insertDocument(1); 36 | $this->insertDocument(2); 37 | $this->insertDocumentWithNumber(3, 1); 38 | 39 | /** @var Cursor $cursor */ 40 | $cursor = $this->r() 41 | ->table('tabletest') 42 | ->filter($this->r()->row()->hasFields('number')) 43 | ->run(); 44 | 45 | $this->assertInstanceOf(\Iterator::class, $cursor); 46 | $this->assertEquals(1, $cursor->count()); 47 | } 48 | 49 | /** 50 | * @throws \Exception 51 | */ 52 | public function testHasFields() 53 | { 54 | $this->insertDocument(1); 55 | $this->insertDocument(2); 56 | $this->insertDocumentWithNumber(3, 1); 57 | 58 | /** @var Cursor $cursor */ 59 | $cursor = $this->r() 60 | ->table('tabletest') 61 | ->filter($this->r()->row()->hasFields('id', 'number')) 62 | ->run(); 63 | 64 | $this->assertInstanceOf(\Iterator::class, $cursor); 65 | $this->assertEquals(1, $cursor->count()); 66 | } 67 | 68 | /** 69 | * @throws \Exception 70 | */ 71 | public function testHasFieldsNot() 72 | { 73 | $this->insertDocument(1); 74 | $this->insertDocument(2); 75 | $this->insertDocumentWithNumber(3, 1); 76 | 77 | /** @var Cursor $cursor */ 78 | $cursor = $this->r() 79 | ->table('tabletest') 80 | ->filter($this->r()->row()->hasFields('id', 'number')->not()) 81 | ->run(); 82 | 83 | $this->assertInstanceOf(\Iterator::class, $cursor); 84 | $this->assertEquals(2, $cursor->count()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /test/integration/Manipulation/KeysTest.php: -------------------------------------------------------------------------------- 1 | insertDocumentWithNumber(1, 1); 17 | 18 | /** @var ResponseInterface $response */ 19 | $response = $this->r() 20 | ->table('tabletest') 21 | ->get(1) 22 | ->keys() 23 | ->run(); 24 | 25 | $this->assertInstanceOf(ResponseInterface::class, $response); 26 | $this->assertCount(4, $response->getData()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/integration/Manipulation/PluckTest.php: -------------------------------------------------------------------------------- 1 | insertDocumentWithNumber(1, 1); 17 | 18 | /** @var ResponseInterface $response */ 19 | $response = $this->r() 20 | ->table('tabletest') 21 | ->get(1) 22 | ->pluck('id', 'description') 23 | ->run(); 24 | 25 | $this->assertInstanceOf(ResponseInterface::class, $response); 26 | $this->assertCount(2, $response->getData()); 27 | $this->assertArrayHasKey('id', $response->getData()); 28 | $this->assertArrayHasKey('description', $response->getData()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/integration/Manipulation/ValuesTest.php: -------------------------------------------------------------------------------- 1 | insertDocumentWithNumber(1, 777); 17 | 18 | /** @var ResponseInterface $cursor */ 19 | $response = $this->r() 20 | ->table('tabletest') 21 | ->get(1) 22 | ->values() 23 | ->run(); 24 | 25 | $this->assertInstanceOf(ResponseInterface::class, $response); 26 | $this->assertCount(4, $response->getData()); 27 | $this->assertArraySubset([2 => 777], $response->getData()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/integration/Manipulation/WithoutTest.php: -------------------------------------------------------------------------------- 1 | insertDocumentWithNumber(1, 1); 17 | 18 | /** @var ResponseInterface $cursor */ 19 | $response = $this->r() 20 | ->table('tabletest') 21 | ->get(1) 22 | ->without('title', 'number') 23 | ->run(); 24 | 25 | $this->assertInstanceOf(ResponseInterface::class, $response); 26 | $this->assertCount(2, $response->getData()); 27 | $this->assertArrayNotHasKey('title', $response->getData()); 28 | $this->assertArrayNotHasKey('number', $response->getData()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/integration/Operation/AbstractTableTest.php: -------------------------------------------------------------------------------- 1 | r()->db()->tableList()->run(); 16 | if (\is_array($res->getData()) && !\in_array('tabletest', $res->getData(), true)) { 17 | $this->r()->db()->tableCreate('tabletest')->run(); 18 | } 19 | } 20 | 21 | public function tearDown() 22 | { 23 | $res = $this->r()->db()->tableList()->run(); 24 | if (\is_array($res->getData()) && \in_array('tabletest', $res->getData(), true)) { 25 | $this->r()->db()->tableDrop('tabletest')->run(); 26 | } 27 | } 28 | 29 | /** 30 | * @param int|string $id 31 | * @return ResponseInterface 32 | */ 33 | protected function insertDocument($id): ResponseInterface 34 | { 35 | $res = $this->r() 36 | ->table('tabletest') 37 | ->insert([ 38 | 'id' => $id, 39 | 'title' => 'Test document '.$id, 40 | 'description' => 'A document description.', 41 | ]) 42 | ->run(); 43 | 44 | return $res; 45 | } 46 | 47 | /** 48 | * @param int|string $id 49 | * @param int $number 50 | * @return ResponseInterface 51 | */ 52 | protected function insertDocumentWithNumber($id, int $number): ResponseInterface 53 | { 54 | $res = $this->r() 55 | ->table('tabletest') 56 | ->insert([ 57 | 'id' => $id, 58 | 'title' => 'Test document '.$id, 59 | 'number' => $number, 60 | 'description' => 'A document description.', 61 | ]) 62 | ->run(); 63 | 64 | return $res; 65 | } 66 | 67 | /** 68 | * @param int|string $id 69 | * @param \DateTimeInterface $dateTime 70 | * @return ResponseInterface 71 | */ 72 | protected function insertDocumentWithDate($id, \DateTimeInterface $dateTime): ResponseInterface 73 | { 74 | $res = $this->r() 75 | ->table('tabletest') 76 | ->insert([ 77 | 'id' => $id, 78 | 'title' => 'Test document '.$id, 79 | 'date' => $dateTime->format(\Datetime::ATOM), 80 | 'description' => 'A document description.', 81 | ]) 82 | ->run(); 83 | 84 | return $res; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /test/integration/Operation/BetweenTest.php: -------------------------------------------------------------------------------- 1 | insertDocument(1); 18 | $this->insertDocument(3); 19 | 20 | /** @var Cursor $cursor */ 21 | $cursor = $this->r() 22 | ->table('tabletest') 23 | ->between(1, 2) 24 | ->run(); 25 | 26 | /** @var array $array */ 27 | $array = $cursor->current(); 28 | 29 | $this->assertInstanceOf(\Iterator::class, $cursor); 30 | $this->assertEquals(1, $cursor->count()); 31 | $this->assertArraySubset(['title' => 'Test document 1'], $array); 32 | } 33 | 34 | /** 35 | * @throws \Exception 36 | */ 37 | public function testBetweenMax() 38 | { 39 | $this->insertDocument(1); 40 | $this->insertDocument(3); 41 | 42 | /** @var Cursor $cursor */ 43 | $cursor = $this->r() 44 | ->table('tabletest') 45 | ->between(2, 3) 46 | ->run(); 47 | 48 | $this->assertInstanceOf(\Iterator::class, $cursor); 49 | $this->assertCount(0, $cursor); 50 | } 51 | 52 | /** 53 | * @throws \Exception 54 | */ 55 | public function testBetweenMultiple() 56 | { 57 | $this->insertDocument(1); 58 | $this->insertDocument(2); 59 | $this->insertDocument(3); 60 | $this->insertDocument(4); 61 | $this->insertDocument(5); 62 | 63 | /** @var Cursor $cursor */ 64 | $cursor = $this->r() 65 | ->table('tabletest') 66 | ->between(2, 4) 67 | ->run(); 68 | 69 | $this->assertInstanceOf(\Iterator::class, $cursor); 70 | $this->assertCount(2, $cursor); 71 | } 72 | 73 | /** 74 | * @return void 75 | * @throws \Exception 76 | */ 77 | public function testBetweenNoResult(): void 78 | { 79 | $this->insertDocument('stringId'); 80 | 81 | /** @var Cursor $cursor */ 82 | $cursor = $this->r() 83 | ->table('tabletest') 84 | ->between(2, 4) 85 | ->run(); 86 | 87 | $this->assertInstanceOf(\Iterator::class, $cursor); 88 | $this->assertCount(0, $cursor); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /test/integration/Operation/ChangesTest.php: -------------------------------------------------------------------------------- 1 | r() 18 | ->table('tabletest') 19 | ->changes() 20 | ->run(); 21 | 22 | $this->insertDocument(1); 23 | $this->insertDocument(2); 24 | $this->insertDocument(3); 25 | $this->insertDocument(4); 26 | $this->insertDocument(5); 27 | 28 | /** @var ResponseInterface $res */ 29 | $i = 1; 30 | foreach ($feed as $change) { 31 | $old_val = $change['old_val']; 32 | $new_val = $change['new_val']; 33 | 34 | $this->assertEmpty($old_val); 35 | $this->assertEquals($i, $new_val['id']); 36 | 37 | if ($i === 5) { 38 | break; 39 | } 40 | 41 | $i++; 42 | } 43 | } 44 | 45 | /** 46 | * @throws \Exception 47 | */ 48 | public function testChangesUpdates(): void 49 | { 50 | /** @var Cursor $feed */ 51 | $feed = $this->r() 52 | ->table('tabletest') 53 | ->changes() 54 | ->run(); 55 | 56 | $this->insertDocument(777); 57 | 58 | $this->r()->table('tabletest')->filter(['id' => 777])->update(['description' => 'cool!'])->run(); 59 | 60 | $i = 777; 61 | foreach ($feed as $change) { 62 | $old_val = $change['old_val']; 63 | $new_val = $change['new_val']; 64 | 65 | if ($old_val !== null) { 66 | $this->assertEquals('A document description.', $old_val['description']); 67 | $this->assertEquals('cool!', $new_val['description']); 68 | 69 | break; 70 | } 71 | 72 | $this->assertEmpty($old_val); 73 | $this->assertEquals($i, $new_val['id']); 74 | } 75 | } 76 | 77 | /** 78 | * @throws \Exception 79 | */ 80 | public function testChangesWithOptions(): void 81 | { 82 | /** @var Cursor $feed */ 83 | $feed = $this->r() 84 | ->table('tabletest') 85 | ->changes(['squash' => true]) 86 | ->run(); 87 | 88 | $this->insertDocument(1); 89 | $this->r()->table('tabletest')->filter(['id' => 1])->update(['description' => 'cool!'])->run(); 90 | 91 | $change = $feed->current(); 92 | $old_val = $change['old_val']; 93 | $new_val = $change['new_val']; 94 | 95 | $this->assertEmpty($old_val); 96 | $this->assertEquals(1, $new_val['id']); 97 | $this->assertEquals('cool!', $new_val['description']); 98 | } 99 | 100 | /** 101 | * @throws \Exception 102 | */ 103 | public function testChangesCreateWithFilter(): void 104 | { 105 | /** @var Cursor $feed */ 106 | $feed = $this->r() 107 | ->table('tabletest') 108 | ->filter(['id' => 4]) 109 | ->changes() 110 | ->run(); 111 | 112 | $this->insertDocument(1); 113 | $this->insertDocument(2); 114 | $this->insertDocument(3); 115 | $this->insertDocument(4); 116 | $this->insertDocument(5); 117 | 118 | $change = $feed->current(); 119 | $old_val = $change['old_val']; 120 | $new_val = $change['new_val']; 121 | 122 | $this->assertEmpty($old_val); 123 | $this->assertEquals(4, $new_val['id']); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /test/integration/Operation/CursorTest.php: -------------------------------------------------------------------------------- 1 | r()->db()->tableList()->run(); 16 | if (\is_array($res->getData()) && !\in_array('tabletest_big', $res->getData(), true)) { 17 | $this->r()->db()->tableCreate('tabletest_big')->run(); 18 | } 19 | } 20 | 21 | public function tearDown() 22 | { 23 | parent::tearDown(); 24 | 25 | $res = $this->r()->db()->tableList()->run(); 26 | if (\is_array($res->getData()) && \in_array('tabletest_big', $res->getData(), true)) { 27 | $this->r()->db()->tableDrop('tabletest_big')->run(); 28 | } 29 | } 30 | 31 | /** 32 | * @throws \Exception 33 | */ 34 | public function testCursor() 35 | { 36 | $this->insertDocuments(); 37 | 38 | $response = $this->r() 39 | ->table('tabletest') 40 | ->count() 41 | ->run(); 42 | 43 | $this->assertEquals(1000, $response->getData()); 44 | } 45 | 46 | /** 47 | * @throws \Exception 48 | */ 49 | public function testCursorBigDocuments() 50 | { 51 | $data = [ 52 | 'Professor X' => 'Charles Francis Xavier', 53 | 'Cyclops' => 'Scott Summers', 54 | 'Iceman' => 'Robert Louis "Bobby" Drake', 55 | 'Angel' => 'Warren Kenneth Worthington III', 56 | 'Beast' => 'Henry Philip "Hank" McCoy', 57 | 'Marvel Girl/Phoenix' => 'Jean Elaine Grey/Jean Elaine Grey-Summers', 58 | 'Magnetrix/Polaris' => 'Lorna Sally Dane', 59 | 'Nightcrawler' => 'Kurt Wagner', 60 | 'Wolverine' => 'James "Logan" Howlett', 61 | 'Storm' => 'Ororo Monroe', 62 | 'Colossus' => 'Piotr Nikolaievitch "Peter" Rasputin', 63 | 'Sprite/Ariel/Shadowcat' => 'Katherine Anne "Kitty" Pryde', 64 | 'Rogue' => 'Anna Marie', 65 | 'Phoenix/Marvel Girl/Prestige' => 'Rachel Anne Grey-Summers', 66 | 'Psylocke' => 'Elizabeth "Betsy" Braddock', 67 | 'Gambit' => 'Rémy Etienne LeBeau', 68 | 'Jubilee' => 'Jubilation Lee', 69 | 'Bishop' => 'Lucas Bishop', 70 | ]; 71 | 72 | $this->insertBigDocuments($data); 73 | 74 | $cursor = $this->r()->table('tabletest_big')->run(); 75 | 76 | $i = 0; 77 | foreach ($cursor as $document) { 78 | // Assert the document every 1000s documents. 79 | if ($i % 1000 === 0) { 80 | $this->assertArraySubset($data, $document); 81 | } 82 | $i++; 83 | } 84 | 85 | $this->assertEquals($i, $this->r()->table('tabletest_big')->count()->run()->getData()); 86 | 87 | $this->assertInstanceOf(Cursor::class, $cursor); 88 | 89 | $this->r()->table('tabletest_big')->delete()->run(); 90 | } 91 | 92 | /** 93 | * @return ResponseInterface 94 | * @throws \Exception 95 | */ 96 | private function insertDocuments(): ResponseInterface 97 | { 98 | $documents = []; 99 | for ($i = 1; $i <= 1000; $i++) { 100 | $documents[] = [ 101 | 'id' => $i, 102 | 'title' => 'Test document', 103 | 'description' => 'My first document.', 104 | ]; 105 | } 106 | 107 | /** @var ResponseInterface $res */ 108 | $res = $this->r() 109 | ->table('tabletest') 110 | ->insert($documents) 111 | ->run(); 112 | 113 | $this->assertEquals(1000, $res->getData()['inserted']); 114 | 115 | return $res; 116 | } 117 | 118 | /** 119 | * @param array $data 120 | * @return ResponseInterface 121 | * @throws \Exception 122 | */ 123 | private function insertBigDocuments(array $data): ResponseInterface 124 | { 125 | $documents = []; 126 | for ($i = 1; $i <= 100; $i++) { 127 | $documents = []; 128 | 129 | for ($x = 1; $x <= 100; $x++) { 130 | $documents[] = $data; 131 | } 132 | } 133 | 134 | /** @var ResponseInterface $res */ 135 | $res = $this->r() 136 | ->table('tabletest_big') 137 | ->insert($documents) 138 | ->run(); 139 | 140 | $this->assertEquals(100, $res->getData()['inserted']); 141 | 142 | return $res; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /test/integration/Operation/DatabaseTest.php: -------------------------------------------------------------------------------- 1 | r() 14 | ->db() 15 | ->tableList() 16 | ->run(); 17 | 18 | $this->assertInternalType('array', $res->getData()); 19 | } 20 | 21 | /** 22 | * @throws \Exception 23 | */ 24 | public function testTableCreate() 25 | { 26 | $res = $this->r() 27 | ->db() 28 | ->tableCreate('createtable') 29 | ->run(); 30 | 31 | $this->assertObStatus(['tables_created' => 1], $res->getData()); 32 | 33 | $this->r() 34 | ->db() 35 | ->tableDrop('createtable') 36 | ->run(); 37 | } 38 | 39 | /** 40 | * @throws \Exception 41 | */ 42 | public function testTableDrop() 43 | { 44 | $this->r() 45 | ->db() 46 | ->tableCreate('createtable') 47 | ->run(); 48 | 49 | $res = $this->r() 50 | ->db() 51 | ->tableDrop('createtable') 52 | ->run(); 53 | 54 | $this->assertObStatus(['tables_dropped' => 1], $res->getData()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/integration/Operation/DeleteTest.php: -------------------------------------------------------------------------------- 1 | r() 16 | ->table('tabletest') 17 | ->insert([ 18 | 'title' => 'Delete document', 19 | ]) 20 | ->run(); 21 | 22 | /** @var ResponseInterface $count */ 23 | $count = $this->r() 24 | ->table('tabletest') 25 | ->filter(['title' => 'Delete document']) 26 | ->count() 27 | ->run(); 28 | 29 | $this->assertInternalType('int', $count->getData()); 30 | 31 | /** @var ResponseInterface $res */ 32 | $res = $this->r() 33 | ->table('tabletest') 34 | ->filter(['title' => 'Delete document']) 35 | ->delete() 36 | ->run(); 37 | 38 | $this->assertObStatus(['deleted' => $count->getData()], $res->getData()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/integration/Operation/EmptyTableTest.php: -------------------------------------------------------------------------------- 1 | r() 17 | ->table('tabletest') 18 | ->filter(['title' => 'Test document']) 19 | ->count() 20 | ->run(); 21 | 22 | $this->assertInternalType('int', $count->getData()); 23 | 24 | $res = $this->r() 25 | ->table('tabletest') 26 | ->delete() 27 | ->run(); 28 | 29 | $this->assertObStatus(['deleted' => $count->getData()], $res->getData()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/integration/Operation/GetAllTest.php: -------------------------------------------------------------------------------- 1 | insertDocument(1); 18 | $this->insertDocument('stringId'); 19 | 20 | /** @var Cursor $cursor */ 21 | $cursor = $this->r() 22 | ->table('tabletest') 23 | ->getAll(1, 'stringId') 24 | ->run(); 25 | 26 | /** @var array $array */ 27 | $array = $cursor->current(); 28 | 29 | $this->assertArraySubset(['description' => 'A document description.'], $array); 30 | } 31 | 32 | /** 33 | * @return void 34 | * @throws \Exception 35 | */ 36 | public function testGetAllAndIsEmpty(): void 37 | { 38 | $this->insertDocument(1); 39 | $this->insertDocument('stringId'); 40 | 41 | /** @var ResponseInterface $res */ 42 | $res = $this->r() 43 | ->table('tabletest') 44 | ->getAll(1, 'stringId') 45 | ->isEmpty() 46 | ->run(); 47 | 48 | $this->assertFalse($res->getData()); 49 | } 50 | 51 | /** 52 | * @return void 53 | * @throws \Exception 54 | */ 55 | public function testGetAllAndCount(): void 56 | { 57 | $this->insertDocument(1); 58 | $this->insertDocument('stringId'); 59 | 60 | /** @var ResponseInterface $res */ 61 | $res = $this->r() 62 | ->table('tabletest') 63 | ->getAll(1, 'stringId') 64 | ->count() 65 | ->run(); 66 | 67 | $this->assertEquals(2, $res->getData()); 68 | } 69 | 70 | /** 71 | * @return void 72 | * @throws \Exception 73 | */ 74 | public function testGetAllAndAvg(): void 75 | { 76 | $this->insertDocumentWithNumber(1, 50); 77 | $this->insertDocumentWithNumber(2, 100); 78 | 79 | /** @var ResponseInterface $res */ 80 | $res = $this->r() 81 | ->table('tabletest') 82 | ->getAll(1, 2) 83 | ->avg('number') 84 | ->run(); 85 | 86 | $this->assertEquals(75, $res->getData()); 87 | } 88 | 89 | /** 90 | * @return void 91 | * @throws \Exception 92 | */ 93 | public function testGetAllAndSum(): void 94 | { 95 | $this->insertDocumentWithNumber(1, 50); 96 | $this->insertDocumentWithNumber(2, 100); 97 | 98 | /** @var ResponseInterface $res */ 99 | $res = $this->r() 100 | ->table('tabletest') 101 | ->getAll(1, 2) 102 | ->sum('number') 103 | ->run(); 104 | 105 | $this->assertEquals(150, $res->getData()); 106 | } 107 | 108 | /** 109 | * @return void 110 | * @throws \Exception 111 | */ 112 | public function testGetAllAndLimit(): void 113 | { 114 | $this->insertDocument(1); 115 | $this->insertDocument('stringId'); 116 | 117 | /** @var ResponseInterface $res */ 118 | $cursor = $this->r() 119 | ->table('tabletest') 120 | ->getAll(1, 'stringId') 121 | ->limit(1) 122 | ->run(); 123 | 124 | $this->assertCount(1, $cursor); 125 | } 126 | 127 | /** 128 | * @return void 129 | * @throws \Exception 130 | */ 131 | public function testGetAllAndSkip(): void 132 | { 133 | $this->insertDocument(1); 134 | $this->insertDocument('stringId'); 135 | 136 | /** @var ResponseInterface $res */ 137 | $cursor = $this->r() 138 | ->table('tabletest') 139 | ->getAll(1, 'stringId') 140 | ->skip(1) 141 | ->run(); 142 | 143 | $this->assertCount(1, $cursor); 144 | } 145 | 146 | /** 147 | * @return void 148 | * @throws \Exception 149 | */ 150 | public function testGetAllAndOrderBy(): void 151 | { 152 | $this->insertDocument(1); 153 | $this->insertDocument('stringId'); 154 | 155 | /** @var ResponseInterface $res */ 156 | $res = $this->r() 157 | ->table('tabletest') 158 | ->getAll(1, 'stringId') 159 | ->orderBy($this->r()->desc('id')) 160 | ->run(); 161 | 162 | $this->assertArraySubset(['id' => 'stringId'], $res->getData()[0]); 163 | } 164 | 165 | /** 166 | * @return void 167 | * @throws \Exception 168 | */ 169 | public function testGetAllAndMin(): void 170 | { 171 | $this->insertDocumentWithNumber(1, 77); 172 | $this->insertDocumentWithNumber(2, 99); 173 | 174 | /** @var ResponseInterface $res */ 175 | $res = $this->r() 176 | ->table('tabletest') 177 | ->getAll(1, 2) 178 | ->min('number') 179 | ->run(); 180 | 181 | $this->assertArraySubset(['number' => 77], $res->getData()); 182 | } 183 | 184 | /** 185 | * @return void 186 | * @throws \Exception 187 | */ 188 | public function testGetAllAndMax(): void 189 | { 190 | $this->insertDocumentWithNumber(1, 77); 191 | $this->insertDocumentWithNumber(2, 99); 192 | 193 | /** @var ResponseInterface $res */ 194 | $res = $this->r() 195 | ->table('tabletest') 196 | ->getAll(1, 2) 197 | ->max('number') 198 | ->run(); 199 | 200 | $this->assertArraySubset(['number' => 99], $res->getData()); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /test/integration/Operation/GetTest.php: -------------------------------------------------------------------------------- 1 | insertDocument(1); 17 | 18 | /** @var ResponseInterface $res */ 19 | $res = $this->r() 20 | ->table('tabletest') 21 | ->get(1) 22 | ->run(); 23 | 24 | /** @var array $array */ 25 | $array = $res->getData(); 26 | 27 | $this->assertArraySubset(['id' => 1], $array); 28 | } 29 | 30 | /** 31 | * @return void 32 | * @throws \Exception 33 | */ 34 | public function testGetNonExistingDocument(): void 35 | { 36 | /** @var ResponseInterface $res */ 37 | $res = $this->r() 38 | ->table('tabletest') 39 | ->get('bar') 40 | ->run(); 41 | 42 | $this->assertEquals(null, $res->getData()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/integration/Operation/IndexTest.php: -------------------------------------------------------------------------------- 1 | insertDocument(1); 16 | 17 | $res = $this->r() 18 | ->table('tabletest') 19 | ->indexCreate('title') 20 | ->run(); 21 | 22 | $this->assertObStatus(['created' => 1], $res->getData()); 23 | } 24 | 25 | /** 26 | * @throws \Exception 27 | */ 28 | public function testIndexDrop() 29 | { 30 | $this->insertDocument(1); 31 | 32 | $this->r() 33 | ->table('tabletest') 34 | ->indexCreate('title') 35 | ->run(); 36 | 37 | $res = $this->r() 38 | ->table('tabletest') 39 | ->indexDrop('title') 40 | ->run(); 41 | 42 | $this->assertObStatus(['dropped' => 1], $res->getData()); 43 | } 44 | 45 | /** 46 | * @throws \Exception 47 | */ 48 | public function testIndexList() 49 | { 50 | $this->insertDocument(1); 51 | 52 | $this->r() 53 | ->table('tabletest') 54 | ->indexCreate('title') 55 | ->run(); 56 | 57 | /** @var ResponseInterface $res */ 58 | $res = $this->r() 59 | ->table('tabletest') 60 | ->indexList() 61 | ->run(); 62 | 63 | $this->assertEquals('title', $res->getData()[0]); 64 | } 65 | 66 | /** 67 | * @throws \Exception 68 | */ 69 | public function testIndexRename() 70 | { 71 | $this->insertDocument(1); 72 | 73 | $this->r() 74 | ->table('tabletest') 75 | ->indexCreate('title') 76 | ->run(); 77 | 78 | $res = $this->r() 79 | ->table('tabletest') 80 | ->indexRename('title', 'description') 81 | ->run(); 82 | 83 | $this->assertObStatus(['renamed' => 1], $res->getData()); 84 | 85 | $res = $this->r() 86 | ->table('tabletest') 87 | ->indexList() 88 | ->run(); 89 | 90 | $this->assertEquals('description', $res->getData()[0]); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /test/integration/Operation/InsertTest.php: -------------------------------------------------------------------------------- 1 | insertDocument(1); 19 | 20 | $this->assertObStatus(['inserted' => 1], $res->getData()); 21 | } 22 | 23 | /** 24 | * @throws \Exception 25 | */ 26 | public function testMultipleInserts() 27 | { 28 | $res = $this->r() 29 | ->table('tabletest') 30 | ->insert([ 31 | [ 32 | 'id' => 1, 33 | 'title' => 'Test document', 34 | 'description' => 'My first document.', 35 | ], 36 | [ 37 | 'id' => 2, 38 | 'title' => 'Test document', 39 | 'description' => 'My first document.', 40 | ], 41 | [ 42 | 'id' => 3, 43 | 'title' => 'Test document', 44 | 'description' => 'My first document.', 45 | ] 46 | ]) 47 | ->run(); 48 | 49 | $this->assertObStatus(['inserted' => 3], $res->getData()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/integration/Operation/SyncTest.php: -------------------------------------------------------------------------------- 1 | insertDocument(1); 16 | $this->insertDocument(2); 17 | $this->insertDocument(3); 18 | $this->insertDocument(4); 19 | $this->insertDocument(5); 20 | 21 | /** @var ResponseInterface $result */ 22 | $result = $this->r() 23 | ->table('tabletest') 24 | ->sync() 25 | ->run(); 26 | 27 | $this->assertEquals(['synced' => true], $result->getData()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/integration/Operation/UpdateTest.php: -------------------------------------------------------------------------------- 1 | insertDocument(1); 16 | 17 | $this->r() 18 | ->table('tabletest') 19 | ->insert(['title' => 'Update document']) 20 | ->run(); 21 | 22 | /** @var ResponseInterface $count */ 23 | $count = $this->r() 24 | ->table('tabletest') 25 | ->filter(['title' => 'Update document']) 26 | ->count() 27 | ->run(); 28 | 29 | /** @var ResponseInterface $res */ 30 | $res = $this->r() 31 | ->table('tabletest') 32 | ->filter(['title' => 'Update document']) 33 | ->update(['title' => 'Updated document']) 34 | ->run(); 35 | 36 | $this->assertObStatus(['replaced' => $count->getData()], $res->getData()); 37 | } 38 | 39 | /** 40 | * @throws \Exception 41 | */ 42 | public function testFilterUpdate() 43 | { 44 | $this->insertDocument(1); 45 | $this->insertDocument(2); 46 | $this->insertDocument(3); 47 | $this->insertDocument(4); 48 | $this->insertDocument(5); 49 | 50 | /** @var ResponseInterface $res */ 51 | $res = $this->r() 52 | ->table('tabletest') 53 | ->filter(['id' => 5]) 54 | ->update(['title' => 'Updated document']) 55 | ->run(); 56 | 57 | $this->assertObStatus(['replaced' => 1], $res->getData()); 58 | } 59 | 60 | /** 61 | * @throws \Exception 62 | */ 63 | public function testFilterUpdateWithMultipleDocuments() 64 | { 65 | $this->insertDocument(1); 66 | $this->insertDocument(2); 67 | $this->insertDocument(3); 68 | $this->insertDocument(4); 69 | $this->insertDocument(5); 70 | 71 | /** @var ResponseInterface $res */ 72 | $res = $this->r() 73 | ->table('tabletest') 74 | ->filter(['description' => 'A document description.']) 75 | ->update(['title' => 'Updated document']) 76 | ->run(); 77 | 78 | $this->assertObStatus(['replaced' => 5], $res->getData()); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /test/integration/Transformation/IsEmptyTest.php: -------------------------------------------------------------------------------- 1 | r() 16 | ->table('tabletest') 17 | ->isEmpty() 18 | ->run(); 19 | 20 | $this->assertTrue($res->getData()); 21 | } 22 | 23 | /** 24 | * @throws \Exception 25 | */ 26 | public function testIsNotEmpty() 27 | { 28 | $this->insertDocument(1); 29 | 30 | $res = $this->r() 31 | ->table('tabletest') 32 | ->isEmpty() 33 | ->run(); 34 | 35 | $this->assertFalse($res->getData()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/integration/Transformation/LimitTest.php: -------------------------------------------------------------------------------- 1 | insertDocument(1); 19 | $this->insertDocument(2); 20 | $this->insertDocument(3); 21 | $this->insertDocument(4); 22 | $this->insertDocument(5); 23 | 24 | /** @var Cursor $cursor */ 25 | $cursor = $this->r() 26 | ->table('tabletest') 27 | ->limit(2) 28 | ->run(); 29 | 30 | $this->assertCount(2, $cursor); 31 | } 32 | 33 | public function testLimitWithMultipleTransformations(): void 34 | { 35 | $this->insertDocument(1); 36 | $this->insertDocument(2); 37 | $this->insertDocument(3); 38 | $this->insertDocument(4); 39 | $this->insertDocument(5); 40 | 41 | /** @var ResponseInterface $cursor */ 42 | $response = $this->r() 43 | ->table('tabletest') 44 | ->filter(['description' => 'A document description.']) 45 | ->orderBy($this->r()->desc('id')) 46 | ->skip(1) 47 | ->limit(2) 48 | ->run(); 49 | 50 | $this->assertCount(2, $response->getData()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/integration/Transformation/OrderByTest.php: -------------------------------------------------------------------------------- 1 | insertDocument(5); 18 | $this->insertDocument(4); 19 | $this->insertDocument(3); 20 | $this->insertDocument(2); 21 | $this->insertDocument(1); 22 | 23 | /** @var ResponseInterface $res */ 24 | $res = $this->r() 25 | ->table('tabletest') 26 | ->orderBy($this->r()->desc('id')) 27 | ->run(); 28 | 29 | $this->assertArraySubset(['id' => 5], $res->getData()[0]); 30 | } 31 | 32 | /** 33 | * @return void 34 | * @throws \Exception 35 | */ 36 | public function testOrderByAsc(): void 37 | { 38 | $this->insertDocument(5); 39 | $this->insertDocument(4); 40 | $this->insertDocument(3); 41 | $this->insertDocument(2); 42 | $this->insertDocument(1); 43 | 44 | /** @var ResponseInterface $res */ 45 | $res = $this->r() 46 | ->table('tabletest') 47 | ->orderBy($this->r()->asc('id')) 48 | ->run(); 49 | 50 | $this->assertArraySubset(['id' => 1], $res->getData()[0]); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/integration/Transformation/SkipTest.php: -------------------------------------------------------------------------------- 1 | insertDocument(1); 19 | $this->insertDocument(2); 20 | $this->insertDocument(3); 21 | $this->insertDocument(4); 22 | $this->insertDocument(5); 23 | 24 | /** @var Cursor $cursor */ 25 | $cursor = $this->r() 26 | ->table('tabletest') 27 | ->skip(2) 28 | ->run(); 29 | 30 | $this->assertCount(3, $cursor); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/unit/BaseUnitTestCase.php: -------------------------------------------------------------------------------- 1 | allowMockingNonExistentMethods(false); 19 | 20 | parent::setUp(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/unit/Connection/ConnectionCursorTest.php: -------------------------------------------------------------------------------- 1 | connect(); 15 | 16 | $message = \Mockery::mock(MessageInterface::class); 17 | $message->shouldReceive('setOptions')->once(); 18 | 19 | $response = \Mockery::mock(ResponseInterface::class); 20 | $response->shouldReceive('getType')->atLeast()->andReturn(ResponseType::SUCCESS_ATOM); 21 | 22 | $this->setExpectations($response); 23 | 24 | try { 25 | $this->connection->rewindFromCursor($message); 26 | } catch (\Exception $e) { 27 | $this->fail($e->getMessage()); 28 | } 29 | 30 | $this->assertTrue(true); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/unit/Connection/ConnectionQueryTest.php: -------------------------------------------------------------------------------- 1 | connect(); 16 | 17 | $token = 1; 18 | $message = \Mockery::mock(MessageInterface::class); 19 | $message->shouldReceive('setOptions')->once(); 20 | 21 | $this->stream->shouldReceive('write')->once()->andReturn(20); 22 | 23 | $this->querySerializer->shouldReceive('serialize')->atLeast()->andReturn("['serialized': true]"); 24 | 25 | try { 26 | $this->connection->writeQuery($token, $message); 27 | } catch (\Exception $e) { 28 | $this->fail($e->getMessage()); 29 | } 30 | 31 | $this->assertTrue(true); 32 | } 33 | 34 | public function testStopQuery() 35 | { 36 | $this->connect(); 37 | 38 | $token = 1; 39 | 40 | $response = \Mockery::mock(ResponseInterface::class); 41 | $response->shouldReceive('getType')->atLeast()->andReturn(ResponseType::SUCCESS_ATOM); 42 | 43 | $buffer = new \stdClass(); 44 | $this->catchStreamWrite($buffer); 45 | $this->catchStreamRead(4 + 8, $buffer); 46 | $this->catchStreamRead(20, $buffer); 47 | 48 | $this->querySerializer->shouldReceive('serialize')->once()->andReturn("['serialized': true]"); 49 | 50 | $this->responseSerializer->shouldReceive('deserialize')->once()->andReturn($response); 51 | 52 | try { 53 | $this->connection->stopQuery($token); 54 | } catch (\Exception $e) { 55 | $this->fail($e->getMessage()); 56 | } 57 | 58 | $this->assertTrue(true); 59 | } 60 | 61 | public function testContinueQuery() 62 | { 63 | $this->connect(); 64 | 65 | $token = 1; 66 | 67 | $response = \Mockery::mock(ResponseInterface::class); 68 | $response->shouldReceive('getType')->atLeast()->andReturn(ResponseType::SUCCESS_ATOM); 69 | 70 | $buffer = new \stdClass(); 71 | $this->catchStreamWrite($buffer); 72 | $this->catchStreamRead(4 + 8, $buffer); 73 | $this->catchStreamRead(20, $buffer); 74 | 75 | $this->querySerializer->shouldReceive('serialize')->once()->andReturn("['serialized': true]"); 76 | 77 | $this->responseSerializer->shouldReceive('deserialize')->once()->andReturn($response); 78 | 79 | try { 80 | $this->connection->continueQuery($token); 81 | } catch (\Exception $e) { 82 | $this->fail($e->getMessage()); 83 | } 84 | 85 | $this->assertTrue(true); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /test/unit/Connection/ConnectionTestCase.php: -------------------------------------------------------------------------------- 1 | stream = Mockery::mock(StreamInterface::class); 52 | $this->streamWrapper = function () { 53 | return $this->stream; 54 | }; 55 | 56 | $this->handshake = Mockery::mock(HandshakeInterface::class); 57 | $this->querySerializer = Mockery::mock(SerializerInterface::class); 58 | $this->responseSerializer = Mockery::mock(SerializerInterface::class); 59 | 60 | $this->connection = new Connection( 61 | $this->streamWrapper, 62 | $this->handshake, 63 | 'test', 64 | $this->querySerializer, 65 | $this->responseSerializer 66 | ); 67 | } 68 | 69 | /** 70 | * @return void 71 | */ 72 | protected function connect(): void 73 | { 74 | try { 75 | $this->handshake->shouldReceive('hello')->once(); 76 | /** @var ConnectionInterface $connection */ 77 | $this->assertInstanceOf(ConnectionInterface::class, $this->connection->connect()); 78 | } catch (\Exception $e) { 79 | $this->fail($e->getMessage()); 80 | } 81 | } 82 | 83 | /** 84 | * @param MockInterface $response 85 | * @return void 86 | */ 87 | protected function setExpectations($response = null): void 88 | { 89 | $this->querySerializer->shouldReceive('serialize')->atLeast()->andReturn("['serialized': true]"); 90 | 91 | $buffer = new \stdClass(); 92 | $this->catchStreamWrite($buffer); 93 | 94 | if ($response) { 95 | $this->catchStreamRead(4 + 8, $buffer); 96 | $this->catchStreamRead(20, $buffer); 97 | 98 | $this->responseSerializer->shouldReceive('deserialize')->atLeast()->andReturn($response); 99 | } 100 | } 101 | 102 | /** 103 | * @param \stdClass $buffer 104 | * @return \stdClass 105 | */ 106 | protected function catchStreamWrite(\stdClass $buffer) 107 | { 108 | $this->stream->shouldReceive('write')->andReturnUsing(function ($string) use ($buffer) { 109 | $buffer->data = $string; 110 | 111 | return 20; 112 | }); 113 | 114 | return $buffer; 115 | } 116 | 117 | protected function catchStreamRead(int $bytes, \stdClass $buffer): void 118 | { 119 | $this->stream->shouldReceive('read')->once()->with($bytes)->andReturnUsing(function ($bytes) use (&$buffer) { 120 | $data = ''; 121 | if (isset($buffer->data)) { 122 | $data = substr($buffer->data, 0, $bytes); 123 | } 124 | 125 | return $data; 126 | }); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /test/unit/Connection/OptionsTest.php: -------------------------------------------------------------------------------- 1 | 'example.test', 19 | 'port' => 42, 20 | 'dbname' => 'test', 21 | 'user' => 'admin', 22 | 'password' => 'secret', 23 | 'timeout' => 120, 24 | 'timeout_stream' => 300, 25 | 'ssl' => true, 26 | ]; 27 | 28 | $options = new Options($config); 29 | 30 | $this->assertEquals($config['hostname'], $options->getHostname()); 31 | $this->assertEquals($config['port'], $options->getPort()); 32 | $this->assertEquals($config['dbname'], $options->getDbName()); 33 | $this->assertEquals($config['user'], $options->getUser()); 34 | $this->assertEquals($config['password'], $options->getPassword()); 35 | $this->assertEquals($config['timeout'], $options->getTimeout()); 36 | $this->assertEquals($config['timeout_stream'], $options->getTimeoutStream()); 37 | $this->assertEquals($config['ssl'], $options->isSsl()); 38 | } 39 | 40 | /** 41 | * @return void 42 | * @throws \Exception 43 | */ 44 | public function testIfHasDefaultDatabaseReturnsTrue(): void 45 | { 46 | $config = [ 47 | 'dbname' => 'test', 48 | ]; 49 | 50 | $options = new Options($config); 51 | 52 | $this->assertTrue($options->hasDefaultDatabase()); 53 | } 54 | 55 | /** 56 | * @return void 57 | * @throws \Exception 58 | */ 59 | public function testIfHasDefaultDatabaseReturnsFalse(): void 60 | { 61 | $config = []; 62 | 63 | $options = new Options($config); 64 | 65 | $this->assertFalse($options->hasDefaultDatabase()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/unit/Connection/RegistryTest.php: -------------------------------------------------------------------------------- 1 | 'foo', 22 | ]; 23 | 24 | $options = new Options($optionsConfig); 25 | 26 | $options2Config = [ 27 | 'dbname' => 'bar', 28 | ]; 29 | 30 | $options2 = new Options($options2Config); 31 | 32 | $registry = new Registry( 33 | [ 34 | 'fooConnection' => $options, 35 | 'barConnection' => $options2, 36 | 'bazConnection' => [], 37 | ] 38 | ); 39 | 40 | $this->assertTrue($registry->hasConnection('fooConnection')); 41 | $this->assertTrue($registry->hasConnection('barConnection')); 42 | $this->assertFalse($registry->hasConnection('bazConnection')); 43 | 44 | $this->assertInstanceOf(Connection::class, $registry->getConnection('fooConnection')); 45 | $this->assertInstanceOf(Connection::class, $registry->getConnection('barConnection')); 46 | } 47 | 48 | /** 49 | * @expectedException \TBolier\RethinkQL\Connection\ConnectionException 50 | * @expectedExceptionMessage The connection fooConnection has already been added 51 | * @expectedExceptionCode 400 52 | * @return void 53 | * @throws \TBolier\RethinkQL\Connection\ConnectionException 54 | */ 55 | public function testIfExceptionThrownOnDuplicateConnection(): void 56 | { 57 | $optionsConfig = [ 58 | 'dbname' => 'foo', 59 | ]; 60 | 61 | $options = new Options($optionsConfig); 62 | 63 | $registry = new Registry( 64 | [ 65 | 'fooConnection' => $options, 66 | ] 67 | ); 68 | 69 | $registry->addConnection('fooConnection', $options); 70 | } 71 | 72 | /** 73 | * @expectedException \TBolier\RethinkQL\Connection\ConnectionException 74 | * @expectedExceptionMessage The connection fooConnection does not exist 75 | * @expectedExceptionCode 400 76 | * @return void 77 | * @throws \TBolier\RethinkQL\Connection\ConnectionException 78 | */ 79 | public function testIfExceptionThrownOnMissingConnection(): void 80 | { 81 | $registry = new Registry([]); 82 | 83 | $registry->getConnection('fooConnection'); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /test/unit/Connection/Socket/HandshakeTest.php: -------------------------------------------------------------------------------- 1 | handshake = new Handshake('foo', 'bar', 42); 24 | } 25 | 26 | /** 27 | * @expectedException \TBolier\RethinkQL\Connection\Socket\Exception 28 | * @expectedExceptionMessage Not connected 29 | * @return void 30 | */ 31 | public function testExceptionThrownOnStreamNotWritable(): void 32 | { 33 | $stream = \Mockery::mock(StreamInterface::class); 34 | $stream->shouldReceive('isWritable')->atLeast()->andReturn(false); 35 | $stream->shouldReceive('close'); 36 | 37 | $this->handshake->hello($stream); 38 | } 39 | 40 | /** 41 | * @expectedException \TBolier\RethinkQL\Connection\Socket\Exception 42 | * @expectedExceptionMessage Foobar 43 | * @return void 44 | */ 45 | public function testExceptionThrownOnError(): void 46 | { 47 | $stream = \Mockery::mock(StreamInterface::class); 48 | $stream->shouldReceive('isWritable')->atLeast()->andReturn(true); 49 | $stream->shouldReceive('close'); 50 | $stream->shouldReceive('write'); 51 | $stream->shouldReceive('getContents')->atLeast()->andReturn('ERROR: Foobar'); 52 | 53 | $this->handshake->hello($stream); 54 | } 55 | 56 | /** 57 | * @expectedException \TBolier\RethinkQL\Connection\Socket\Exception 58 | * @expectedExceptionMessage Foobar 59 | * @return void 60 | */ 61 | public function testExceptionThrownOnVerifyProtocolWithError(): void 62 | { 63 | $stream = \Mockery::mock(StreamInterface::class); 64 | $stream->shouldReceive('isWritable')->atLeast()->andReturn(true); 65 | $stream->shouldReceive('close'); 66 | $stream->shouldReceive('write'); 67 | $stream->shouldReceive('getContents')->atLeast()->andReturn('{"success":false, "error": "Foobar"}'); 68 | 69 | $this->handshake->hello($stream); 70 | } 71 | 72 | /** 73 | * @expectedException \TBolier\RethinkQL\Connection\Socket\Exception 74 | * @expectedExceptionMessage Unsupported protocol version. 75 | * @return void 76 | */ 77 | public function testExceptionThrownOnInvalidProtocolVersion(): void 78 | { 79 | $stream = \Mockery::mock(StreamInterface::class); 80 | $stream->shouldReceive('isWritable')->atLeast()->andReturn(true); 81 | $stream->shouldReceive('close'); 82 | $stream->shouldReceive('write'); 83 | $stream->shouldReceive('getContents')->atLeast() 84 | ->andReturn('{"success":true, "max_protocol_version": 1, "min_protocol_version": 1}'); 85 | 86 | $this->handshake->hello($stream); 87 | } 88 | 89 | 90 | /** 91 | * @expectedException \TBolier\RethinkQL\Connection\Socket\Exception 92 | * @expectedExceptionMessage Woops! 93 | * @return void 94 | */ 95 | public function testExceptionThrownOnProtocolError(): void 96 | { 97 | /** @var MockInterface $stream */ 98 | $stream = \Mockery::mock(StreamInterface::class); 99 | $stream->shouldReceive('isWritable')->atLeast()->andReturn(true); 100 | $stream->shouldReceive('close'); 101 | $stream->shouldReceive('write'); 102 | $stream->shouldReceive('getContents')->atLeast()->andReturn('ERROR: Woops!'); 103 | 104 | $this->handshake->hello($stream); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /test/unit/Query/MessageTest.php: -------------------------------------------------------------------------------- 1 | setCommand($queryType); 24 | $message->setQuery($query); 25 | $message->setOptions($options); 26 | 27 | $this->assertEquals(1, $message->getQueryType()); 28 | $this->assertEquals([ 29 | 1, 30 | ['yo'], 31 | new Options(), 32 | ], $message->toArray()); 33 | $this->assertEquals($options, $message->getOptions()); 34 | } 35 | 36 | /** 37 | * @return void 38 | * @throws \Exception 39 | */ 40 | public function testToArray(): void 41 | { 42 | $queryType = 1; 43 | $query = []; 44 | $options = new Options(); 45 | 46 | $message = new Message($queryType, $query, $options); 47 | 48 | $expectedResults = [ 49 | 1, 50 | $query, 51 | (object) $options, 52 | ]; 53 | 54 | $this->assertEquals($expectedResults, $message->toArray()); 55 | } 56 | 57 | /** 58 | * @return void 59 | * @throws \Exception 60 | */ 61 | public function testJsonSerialize(): void 62 | { 63 | $queryType = 1; 64 | $query = []; 65 | $options = new Options(); 66 | 67 | $message = new Message($queryType, $query, $options); 68 | 69 | $expectedResults = [ 70 | 1, 71 | $query, 72 | (object) $options, 73 | ]; 74 | 75 | $this->assertEquals($expectedResults, $message->jsonSerialize()); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/unit/Response/ResponseTest.php: -------------------------------------------------------------------------------- 1 | 'bar']; 19 | $backtrace = [0 => [1 => []]]; 20 | $profile = [2 => [3 => []]]; 21 | $note = [4 => [5 => []]]; 22 | 23 | $response = new Response( 24 | $type, 25 | $data, 26 | $backtrace, 27 | $profile, 28 | $note 29 | ); 30 | 31 | $this->assertEquals($type, $response->getType()); 32 | $this->assertEquals($data, $response->getData()); 33 | $this->assertEquals($backtrace, $response->getBacktrace()); 34 | $this->assertEquals($profile, $response->getProfile()); 35 | $this->assertEquals($note, $response->getNote()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/unit/Serializer/QueryNormalizerTest.php: -------------------------------------------------------------------------------- 1 | normalizer = new QueryNormalizer(); 25 | 26 | $serializer = new Serializer([$this->normalizer]); 27 | 28 | $this->normalizer->setSerializer($serializer); 29 | } 30 | 31 | /** 32 | * @return void 33 | * @throws \Exception 34 | */ 35 | public function testNormalizeWithStdClass(): void 36 | { 37 | $object = new \stdClass(); 38 | $object->foo = 'bar'; 39 | 40 | $data = $this->normalizer->normalize($object); 41 | 42 | $this->assertEquals(['foo' => 'bar'], $data); 43 | } 44 | 45 | /** 46 | * @return void 47 | * @throws \Exception 48 | */ 49 | public function testNormalizeWithOptions(): void 50 | { 51 | $object = new Options(); 52 | $object->setDb('foobar'); 53 | 54 | $expectedObject = new \stdClass(); 55 | $expectedObject->db = [0 => 14, 1 => ['foobar']]; 56 | 57 | $data = $this->normalizer->normalize($object); 58 | 59 | $this->assertEquals($expectedObject, $data); 60 | } 61 | 62 | /** 63 | * @expectedException \Symfony\Component\Serializer\Exception\CircularReferenceException 64 | * @expectedExceptionMessage A circular reference has been detected when serializing the object of class "stdClass" 65 | * (configured limit: 1) 66 | * @return void 67 | */ 68 | public function testNormalizeWithCircularReference(): void 69 | { 70 | $object = new \stdClass(); 71 | $object->foo = 'bar'; 72 | 73 | $context = [ 74 | 'circular_reference_limit' => [ 75 | spl_object_hash($object) => 1, 76 | ], 77 | ]; 78 | 79 | $this->normalizer->normalize($object, null, $context); 80 | } 81 | 82 | /** 83 | * @return void 84 | * @throws \Exception 85 | */ 86 | public function testNormalizeWithJsonSerializable(): void 87 | { 88 | $expectedReturn = ['foo' => 'bar']; 89 | 90 | $object = Mockery::mock('\JsonSerializable'); 91 | $object->shouldReceive('jsonSerialize')->once()->andReturn($expectedReturn); 92 | 93 | 94 | $data = $this->normalizer->normalize($object); 95 | 96 | $this->assertEquals($expectedReturn, $data); 97 | } 98 | 99 | /** 100 | * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException 101 | * @expectedExceptionMessage The ArrayObject must implement "JsonSerializable" 102 | * @return void 103 | */ 104 | public function testInvalidArgumentExceptionThrownOnInvalidClass(): void 105 | { 106 | $object = new \ArrayObject(); 107 | 108 | $this->normalizer->normalize($object); 109 | } 110 | 111 | /** 112 | * @expectedException \Symfony\Component\Serializer\Exception\LogicException 113 | * @expectedExceptionMessage Cannot normalize object because injected serializer is not a normalizer 114 | * @return void 115 | */ 116 | public function testLogicExceptionThrownOnInvalidNormalizer(): void 117 | { 118 | $object = new \stdClass(); 119 | $object->foo = 'bar'; 120 | 121 | $serializerMock = Mockery::mock('\Symfony\Component\Serializer\SerializerInterface'); 122 | $this->normalizer->setSerializer($serializerMock); 123 | 124 | $this->normalizer->normalize($object); 125 | } 126 | 127 | /** 128 | * @return void 129 | * @throws \Exception 130 | */ 131 | public function testSupportsDenormalizationReturnsFalse(): void 132 | { 133 | $this->assertFalse($this->normalizer->supportsDenormalization('foo', 'foo', 'foo')); 134 | } 135 | 136 | /** 137 | * @expectedException \Symfony\Component\Serializer\Exception\LogicException 138 | * @expectedExceptionMessage Cannot denormalize with "TBolier\RethinkQL\Serializer\QueryNormalizer". 139 | * @return void 140 | */ 141 | public function testIfDenormalizeThrowsLogicException(): void 142 | { 143 | $this->normalizer->denormalize('foo', 'bar'); 144 | } 145 | } 146 | --------------------------------------------------------------------------------