├── .gitattributes ├── .gitignore ├── .noninteractive ├── .scrutinizer.yml ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── composer.json ├── example ├── basic_example.php ├── conn_example.php ├── core_example.php ├── hash_set_example.php ├── hyperlog_example.php ├── key_val_example.php ├── list_example.php ├── set_example.php └── transaction_example.php ├── phpunit.xml ├── src └── Redis │ ├── Command │ ├── Api │ │ ├── ApiChannelInterface.php │ │ ├── ApiClusterInterface.php │ │ ├── ApiConnInterface.php │ │ ├── ApiCoreInterface.php │ │ ├── ApiGeospatialInterface.php │ │ ├── ApiHyperLogInterface.php │ │ ├── ApiKeyValInterface.php │ │ ├── ApiListInterface.php │ │ ├── ApiSetHashInterface.php │ │ ├── ApiSetInterface.php │ │ ├── ApiSetSortedInterface.php │ │ └── ApiTransactionInterface.php │ ├── Builder.php │ ├── CommandInterface.php │ ├── Compose │ │ ├── ApiChannelTrait.php │ │ ├── ApiClusterTrait.php │ │ ├── ApiConnTrait.php │ │ ├── ApiCoreTrait.php │ │ ├── ApiGeospatialTrait.php │ │ ├── ApiHyperLogTrait.php │ │ ├── ApiKeyValTrait.php │ │ ├── ApiListTrait.php │ │ ├── ApiSetHashTrait.php │ │ ├── ApiSetSortedTrait.php │ │ ├── ApiSetTrait.php │ │ └── ApiTransactionTrait.php │ └── Enum.php │ ├── Driver │ ├── Driver.php │ ├── DriverInterface.php │ ├── Request.php │ └── RequestInterface.php │ ├── Redis.php │ └── RedisInterface.php └── test ├── Callback.php ├── TModule.php ├── TModule ├── Command │ ├── RedisApiChannelTest.php │ ├── RedisApiConnTest.php │ ├── RedisApiCoreTest.php │ ├── RedisApiHyperLogTest.php │ ├── RedisApiKeyValTest.php │ ├── RedisApiListTest.php │ ├── RedisApiSetHashTest.php │ ├── RedisApiSetSortedTest.php │ ├── RedisApiSetTest.php │ └── RedisApiTransactionTest.php └── _Support │ └── RedisTrait.php ├── TUnit.php ├── _Simulation ├── Event.php ├── EventCollection.php ├── Simulation.php └── SimulationInterface.php └── bootstrap.php /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | .idea/ 3 | .DS_Store/ 4 | composer.lock 5 | composer.phar 6 | -------------------------------------------------------------------------------- /.noninteractive: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dazzle-php/redis/0203b71564b27eac641ec3d3858425452b80c235/.noninteractive -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | excluded_paths: 3 | - 'build/*' 4 | - 'build-ci/*' 5 | - 'example/*' 6 | - 'example-bench/*' 7 | - 'test/*' 8 | - 'vendor/*' 9 | 10 | checks: 11 | php: 12 | return_doc_comments: true 13 | remove_extra_empty_lines: true 14 | 15 | coding_style: 16 | php: 17 | indentation: 18 | general: 19 | use_tabs: false 20 | size: 4 21 | switch: 22 | indent_case: true 23 | spaces: 24 | general: 25 | linefeed_character: newline 26 | before_parentheses: 27 | function_declaration: false 28 | closure_definition: false 29 | function_call: false 30 | if: true 31 | for: true 32 | while: true 33 | switch: true 34 | catch: true 35 | array_initializer: false 36 | around_operators: 37 | assignment: true 38 | logical: true 39 | equality: true 40 | relational: true 41 | bitwise: true 42 | additive: true 43 | multiplicative: true 44 | shift: true 45 | unary_additive: false 46 | concatenation: true 47 | negation: false 48 | before_left_brace: 49 | class: true 50 | function: true 51 | if: true 52 | else: true 53 | for: true 54 | while: true 55 | do: true 56 | switch: true 57 | try: true 58 | catch: true 59 | finally: true 60 | before_keywords: 61 | else: true 62 | while: true 63 | catch: true 64 | finally: true 65 | within: 66 | brackets: false 67 | array_initializer: false 68 | grouping: false 69 | function_call: false 70 | function_declaration: false 71 | if: false 72 | for: false 73 | while: false 74 | switch: false 75 | catch: false 76 | type_cast: false 77 | ternary_operator: 78 | before_condition: true 79 | after_condition: true 80 | before_alternative: true 81 | after_alternative: true 82 | in_short_version: false 83 | other: 84 | before_comma: false 85 | after_comma: true 86 | before_semicolon: false 87 | after_semicolon: true 88 | after_type_cast: true 89 | braces: 90 | classes_functions: 91 | class: new-line 92 | function: new-line 93 | closure: end-of-line 94 | if: 95 | opening: new-line 96 | always: false 97 | else_on_new_line: true 98 | for: 99 | opening: new-line 100 | always: true 101 | while: 102 | opening: new-line 103 | always: true 104 | do_while: 105 | opening: new-line 106 | always: true 107 | while_on_new_line: true 108 | switch: 109 | opening: new-line 110 | try: 111 | opening: new-line 112 | catch_on_new_line: true 113 | finally_on_new_line: true 114 | upper_lower_casing: 115 | keywords: 116 | general: lower 117 | constants: 118 | true_false_null: lower 119 | 120 | tools: 121 | external_code_coverage: 122 | timeout: 1800 123 | runs: 1 124 | php_code_coverage: false -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | dist: trusty 4 | sudo: required 5 | 6 | php: 7 | - 5.6 8 | - 7.0 9 | - 7.1 10 | 11 | services: 12 | - redis-server 13 | 14 | env: 15 | global: 16 | - TEST_DB_REDIS_ENDPOINT="tcp://127.0.0.1:6379" 17 | 18 | before_install: 19 | - export PHP_MAJOR="$(echo $TRAVIS_PHP_VERSION | cut -d '.' -f 1,2)" 20 | 21 | install: 22 | - travis_retry composer self-update 23 | - travis_retry composer install --prefer-source --no-interaction 24 | - php -m 25 | 26 | script: 27 | - vendor/bin/phpunit -d memory_limit=1024M --coverage-text --coverage-clover=coverage.clover 28 | 29 | after_script: 30 | - if [ "$TRAVIS_PHP_VERSION" = "7.1" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi 31 | - if [ "$TRAVIS_PHP_VERSION" = "7.1" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi 32 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | This changelog references the relevant changes, bug and security fixes done. 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and accepted via **Pull Requests** on [GitHub](https://github.com/dazzle-php/redis). 4 | 5 | ## Pull Requests 6 | 7 | - **Naming convention** - all pull requests fixing a problem should match "Fix #issue Message" pattern, the new features and non-fix changes should match "Resolve #issue Message", the rest should contain only "Message". 8 | - **Follow our template of code** - all contributions have to follow [PSR-2 coding standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) with an exception of control structures, which have to have opening parenthesis always placed in the next line instead of the same. 9 | - **Add tests** - the contribution won't be accepted if it doesn't have tests. 10 | - **Document any change in behaviour** - make sure the `README.md` is kept up to date. 11 | - **Create feature branches** - don't create pull requests from your master branch. 12 | - **One pull request per feature** - for multiple things that you want to do, send also multiple pull requests. 13 | - **Keep coherent history** - make sure each individual commit in your pull request is meaningful. If you had to make multiple commits during development cycle, please squash them before submitting. 14 | 15 | ## Running Tests 16 | 17 | ``` 18 | $> vendor/bin/phpunit 19 | ``` 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2017 Kamil Jamróz 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dazzle Async Redis Driver 2 | 3 | [![Build Status](https://travis-ci.org/dazzle-php/redis.svg)](https://travis-ci.org/dazzle-php/redis) 4 | [![Code Coverage](https://scrutinizer-ci.com/g/dazzle-php/redis/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/dazzle-php/redis/?branch=master) 5 | [![Code Quality](https://scrutinizer-ci.com/g/dazzle-php/redis/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/dazzle-php/redis/?branch=master) 6 | [![Latest Stable Version](https://poser.pugx.org/dazzle-php/redis/v/stable)](https://packagist.org/packages/dazzle-php/redis) 7 | [![Latest Unstable Version](https://poser.pugx.org/dazzle-php/redis/v/unstable)](https://packagist.org/packages/dazzle-php/redis) 8 | [![License](https://poser.pugx.org/dazzle-php/redis/license)](https://packagist.org/packages/dazzle-php/redis/license) 9 | 10 | > **Note:** This repository is part of [Dazzle Project](https://github.com/dazzle-php/dazzle) - the next-gen library for PHP. The project's purpose is to provide PHP developers with a set of complete tools to build functional async applications. Please, make sure you read the attached README carefully and it is guaranteed you will be surprised how easy to use and powerful it is. In the meantime, you might want to check out the rest of our async libraries in [Dazzle repository](https://github.com/dazzle-php) for the full extent of Dazzle experience. 11 | 12 |
13 |

14 | 15 |

16 | 17 | ## Description 18 | 19 | TODO 20 | 21 | ## Feature Highlights 22 | 23 | Dazzle Redis features: 24 | 25 | TODO 26 | 27 | ## Provided Example(s) 28 | 29 | ### Quickstart 30 | 31 | TODO 32 | 33 | ### Additional 34 | 35 | TODO 36 | 37 | ## Comparison 38 | 39 | This section contains Dazzle vs React comparison many users requested. If you are wondering why this section has been created, see the author note at the end of it. 40 | 41 | #### Performance 42 | 43 | TODO 44 | 45 | #### Details 46 | 47 | TODO 48 | 49 | #### Note from the author 50 | 51 | > Few years ago, whenever I needed async tools in PHP, I was actively using other, hugely popular php library called React. Back then it was mind-blowing experience for me and I was astonished how easy it was to simulate async behaviour in PHP. I started to trust this aproach more and more and began to use it in more complicated projects. However, the bigger the project I was working on was, the more defects I was able to find. Its code, in my experience, suffered from uneven performance, leaking memory, the occasional bugs and what had upset me most - lacking interfaces which focused only on async side of things, ignoring functionality of its components as a whole. I started to write my own extensions for the library, including missing boilerplate and fixes needed. I wanted to share that with the community, created PRs with some of them, but they were never approved or rejected. React project was dead at that time, but in fact, I still needed those tools. That prompted me to create Dazzle Project. It was designed as modern, more reliable and more complete replacement for React library. Although I hold React library dear to my heart up to this day, I believe I was able to achieve that goal perfectly. Since the first day Dazzle was published I received many requests to include comparisons and benchmarks that proves the previous statement. That's why this section has been attached to the README. I hope the readers will be able to find all the necessary pieces of information they are looking for in it. 52 | 53 | ## Requirements 54 | 55 | Dazzle Redis requires: 56 | 57 | * PHP-5.6 or PHP-7.0+, 58 | * UNIX or Windows OS. 59 | 60 | ## Installation 61 | 62 | To install this library make sure you have [composer](https://getcomposer.org/) installed, then run following command: 63 | 64 | ``` 65 | $> composer require dazzle-php/redis 66 | ``` 67 | 68 | ## Tests 69 | 70 | Tests can be run via: 71 | 72 | ``` 73 | $> vendor/bin/phpunit -d memory_limit=1024M 74 | ``` 75 | 76 | ## Versioning 77 | 78 | Versioning of Dazzle libraries is being shared between all packages included in [Dazzle Project](https://github.com/dazzle-php/dazzle). That means the releases are being made concurrently for all of them. On one hand this might lead to "empty" releases for some packages at times, but don't worry. In the end it is far much easier for contributors to maintain and -- what's the most important -- much more straight-forward for users to understand the compatibility and inter-operability of the packages. 79 | 80 | ## Contributing 81 | 82 | Thank you for considering contributing to this repository! 83 | 84 | - The contribution guide can be found in the [contribution tips](https://github.com/dazzle-php/redis/blob/master/CONTRIBUTING.md). 85 | - Open tickets can be found in [issues section](https://github.com/dazzle-php/redis/issues). 86 | - Current contributors are listed in [graphs section](https://github.com/dazzle-php/redis/graphs/contributors) 87 | - To contact the author(s) see the information attached in [composer.json](https://github.com/dazzle-php/redis/blob/master/composer.json) file. 88 | 89 | ## License 90 | 91 | Dazzle Redis is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT). 92 | 93 |
94 |

95 | "Everything is possible. The impossible just takes longer." ― Dan Brown 96 |

97 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dazzle-php/redis", 3 | "description": "Dazzle Asynchronous Redis Driver.", 4 | "keywords": [ 5 | "dazzle", "dazzle-php", "async", "async-redis", "redis", "redis-client", "cache", "queue", "client" 6 | ], 7 | "license": "MIT", 8 | "support": { 9 | "issues": "https://github.com/dazzle-php/redis/issues", 10 | "source": "https://github.com/dazzle-php/redis" 11 | }, 12 | "authors": [ 13 | { 14 | "name": "Kamil Jamroz", 15 | "homepage": "https://github.com/khelle" 16 | }, 17 | { 18 | "name": "Hidehalo", 19 | "homepage": "https://github.com/hidehalo" 20 | }, 21 | { 22 | "name": "The contributors", 23 | "homepage": "http://github.com/dazzle-php/channel/contributors" 24 | } 25 | ], 26 | "require": { 27 | "php": ">=5.6.7", 28 | "dazzle-php/event": "0.5.*", 29 | "dazzle-php/loop": "0.5.*", 30 | "dazzle-php/promise": "0.5.*", 31 | "dazzle-php/socket": "0.5.*", 32 | "dazzle-php/throwable": "0.5.*", 33 | "dazzle-php/util": "0.5.*", 34 | "clue/redis-protocol": "0.3.*" 35 | }, 36 | "require-dev": { 37 | "phpunit/phpunit": ">=4.8.0 <5.4.0" 38 | }, 39 | "autoload": { 40 | "psr-4": { 41 | "Dazzle\\Redis\\": "src/Redis", 42 | "Dazzle\\Redis\\Test\\": "test" 43 | } 44 | }, 45 | "extra": { 46 | "branch-alias": { 47 | "dev-master": "0.5-dev" 48 | } 49 | }, 50 | "minimum-stability": "dev", 51 | "prefer-stable": true 52 | } 53 | -------------------------------------------------------------------------------- /example/basic_example.php: -------------------------------------------------------------------------------- 1 | on('start', function(Redis $redis) { 15 | 16 | $redis->flushDb(); 17 | 18 | $redis->set('test','Hello Dazzle Redis!')->then(function ($value) { 19 | printf('result is %s'.PHP_EOL, $value); 20 | }); 21 | 22 | $redis->get('test')->then(function ($value) { 23 | printf('result is %s'.PHP_EOL, $value); 24 | }); 25 | 26 | $redis->end(); 27 | }); 28 | //redis client stop handler 29 | $redis->on('stop', function() use ($loop) { 30 | echo 'stop right now'.PHP_EOL; 31 | $loop->stop(); 32 | }); 33 | //redis client error handler 34 | $redis->on('error', function(Exception $ex) { 35 | printf('error message is %s'.PHP_EOL, $ex->getMessage()); 36 | }); 37 | //set redis client run when global loop start 38 | $loop->onStart(function () use ($redis) { 39 | $redis->start(); 40 | }); 41 | //then the global loop run... 42 | $loop->start(); 43 | 44 | 45 | -------------------------------------------------------------------------------- /example/conn_example.php: -------------------------------------------------------------------------------- 1 | on('start', function () use ($redis) { 19 | $redis->auth('no password')->then(function ($value) { 20 | printf("Auth result is %s\n", $value); 21 | }, function (Exception $e) { 22 | printf("Auth is failed cauz %s\n", $e->getMessage()); 23 | }); 24 | 25 | $redis->select(0)->then(function ($value) { 26 | printf("Select result is %s\n", $value); 27 | }); 28 | 29 | $redis->ping()->then(function ($value) { 30 | printf("Hello %s message\n", $value); 31 | }); 32 | 33 | $redis->quit()->then(function ($value) { 34 | printf("Quit result is %s\n", $value); 35 | }); 36 | }); 37 | 38 | $redis->on('stop', function () use ($loop) { 39 | $loop->stop(); 40 | }); 41 | 42 | $loop->onStart(function () use ($redis) { 43 | $redis->start(); 44 | }); 45 | 46 | $loop->start(); 47 | 48 | -------------------------------------------------------------------------------- /example/core_example.php: -------------------------------------------------------------------------------- 1 | on('start', function (Redis $redis) { 17 | $redis->info()->then(function ($value) { 18 | var_export($value); 19 | }); 20 | 21 | $redis->end(); 22 | }); 23 | 24 | $redis->on('stop', function () use ($loop) { 25 | $loop->stop(); 26 | }); 27 | 28 | $loop->onStart(function () use ($redis) { 29 | $redis->start(); 30 | }); 31 | 32 | $loop->start(); -------------------------------------------------------------------------------- /example/hash_set_example.php: -------------------------------------------------------------------------------- 1 | on('start', function (Redis $redis) { 17 | $redis->flushDb(); 18 | 19 | $redis->hSet('t_key','f', 1)->then(function ($value) { 20 | printf("Set hash set filed \"f\"=1: %s\n", $value>0?'OK':'Failed'); 21 | }); 22 | $redis->hGet('t_key', 'f')->then(function ($value) { 23 | printf("Get hash set filed \"f\": %s\n", $value); 24 | }); 25 | $redis->hGetAll('t_key')->then(function ($value) { 26 | printf("Get all fileds %s and values: %s\n", 27 | implode(',', array_keys($value)), 28 | implode(',', array_values($value)) 29 | ); 30 | }); 31 | $redis->hMSet('t_key', [ 32 | 'f_1' => 1, 33 | 'f_2' => 2, 34 | ])->then(function ($value) { 35 | printf("Multi set: %s\n", $value); 36 | }); 37 | $redis->hMGet('t_key', 'f_1', 'f_2')->then(function ($value) { 38 | printf("Multi get: %s\n", implode(',', $value)); 39 | }); 40 | $redis->hExists('t_key', 'f_1')->then(function ($value) { 41 | printf("[t_key] filed \"f\" exist: %s\n", $value > 0 ? 'Y':'N'); 42 | }); 43 | $redis->hIncrBy('t_key', 'f', 5)->then(function ($value) { 44 | printf("1 increment by 5: %s\n", $value); 45 | }); 46 | $redis->hVals('t_key')->then(function ($value) { 47 | printf("All values of hashes sets: %s\n", implode(',', $value)); 48 | }); 49 | $redis->hDel('t_key', 'f')->then(function ($value) { 50 | printf("Delete: %s\n", $value > 0 ? 'OK' : "Failed"); 51 | }); 52 | 53 | $redis->end(); 54 | }); 55 | 56 | $redis->on('stop', function () use ($loop) { 57 | $loop->stop(); 58 | }); 59 | 60 | $loop->onStart(function () use ($redis) { 61 | $redis->start(); 62 | }); 63 | 64 | $loop->start(); -------------------------------------------------------------------------------- /example/hyperlog_example.php: -------------------------------------------------------------------------------- 1 | on('start', function (Redis $redis) { 17 | $redis->flushDb(); 18 | 19 | $redis->pFAdd('t_hll',1, 2, 3, 4, 5, 6)->then(function ($value) { 20 | printf("Add [t_hll]: %s\n", $value > 0? 'OK' : 'Failed'); 21 | }); 22 | $redis->pFAdd('t_hll_1', 7, 8, 9)->then(function ($value) { 23 | printf("Add another [t_hll_1]: %s\n", $value > 0? 'OK' : 'Failed'); 24 | }); 25 | $redis->pFCount('t_hll')->then(function ($value) { 26 | printf("Count log(s) for [t_hll]: %s\n", $value); 27 | }); 28 | $redis->pFMerge('t_hll', 't_hll_1')->then(function ($value) { 29 | printf("Merge [t_hll_1] into [t_hll_1] log(s): %s\n", $value); 30 | }); 31 | $redis->pFCount('t_hll')->then(function ($value) { 32 | printf("Count log(s) for [t_hll] again: %s\n", $value); 33 | }); 34 | 35 | $redis->end(); 36 | }); 37 | 38 | $redis->on('stop', function () use ($loop) { 39 | $loop->stop(); 40 | }); 41 | 42 | $loop->onStart(function () use ($redis) { 43 | $redis->start(); 44 | }); 45 | 46 | $loop->start(); -------------------------------------------------------------------------------- /example/key_val_example.php: -------------------------------------------------------------------------------- 1 | on('start', function (Redis $redis) { 18 | $redis->flushDb(); 19 | //normal string opts 20 | $redis->set('t_key', 'Hello Dazzle Redis!', ['ex', 60])->then(function ($value) { 21 | printf("Set [t_key] equals \"Hello Dazzle Redis!\": %s\n", $value); 22 | }); 23 | $redis->append('t_key', 'XD')->then(function ($value) { 24 | printf("Append \"XD\" to [t_key]: %s\n", $value>0?'OK':'Failed'); 25 | }); 26 | $redis->get('t_key')->then(function ($value) { 27 | printf("Get [t_key]: %s\n", $value); 28 | }); 29 | $redis->strLen('t_key')->then(function ($value) { 30 | printf("String length: %s\n", $value); 31 | }); 32 | //multi opts 33 | $redis->mSet([ 34 | 't_key_1' => 1, 35 | 't_key_2' => 2 36 | ])->then(function ($value) { 37 | printf("Multi set [t_key_1, t_key_2] keys %s\n", $value); 38 | }); 39 | $redis->mGet('t_key_1', 't_key_2')->then(function ($value) { 40 | printf("Multi get [t_key_1, t_key_2]: %s\n", implode(',', $value)); 41 | }); 42 | //increment and decrement 43 | $redis->incrBy('t_key_1', 5)->then(function ($value) { 44 | printf("The result of 1 increment by 5: %s\n", $value); 45 | }); 46 | 47 | $redis->decr('t_key_1')->then(function ($value) { 48 | printf("The result of 6 increment by 1: %s\n", $value); 49 | }); 50 | 51 | //bit opts 52 | $redis->setBit('t_b_1', 0, 1); 53 | $redis->setBit('t_b_2', 0, 1); 54 | $redis->bitOp('AND', 'answer', 't_b_1', 't_b_2'); 55 | $redis->getBit('answer', 0)->then(function ($value) { 56 | printf("The answer of \"1&1\" is: %d\n", $value); 57 | }); 58 | 59 | //generic key opts 60 | $redis->exists('t_b_1', 't_b_2', 't_key_1')->then(function ($value) { 61 | printf("%s given key(s) is exist\n", $value); 62 | }); 63 | $redis->expire('t_key_1', 60)->then(function ($value) { 64 | printf("Expire result: %s\n", $value); 65 | }); 66 | $redis->expireAt('t_key_2', time() + 60)->then(function ($value) { 67 | printf("Expire at unix timestamp + 60 result: %s\n", $value); 68 | }); 69 | $redis->persist('t_key')->then(function ($value) { 70 | printf("Persist result: %s\n", $value > 0 ? 'OK' : 'Failed'); 71 | }); 72 | $redis->type('t_key')->then(function ($value) { 73 | printf("Type: %s\n", $value); 74 | }); 75 | $redis->ttl('t_key_1')->then(function ($value) { 76 | printf("TTL: %s(secs)\n", $value); 77 | }); 78 | $redis->del('t_key', 't_key_1', 't_key_2', 'answer')->then(function ($value) { 79 | printf("Delete %d key(s)\n", $value); 80 | }); 81 | 82 | $redis->end(); 83 | }); 84 | 85 | $redis->on('stop', function () use ($loop) { 86 | $loop->stop(); 87 | }); 88 | 89 | $loop->onStart(function () use ($redis) { 90 | $redis->start(); 91 | }); 92 | 93 | $loop->start(); -------------------------------------------------------------------------------- /example/list_example.php: -------------------------------------------------------------------------------- 1 | on('start', function (Redis $redis) { 18 | //this one is not a command of redis list commands part 19 | $redis->flushDb(); 20 | 21 | $redis->lPush('t_list', 1, 2, 3, 4, 5)->then(function ($value) { 22 | printf("Push %d elements to a new list\n", $value); 23 | }); 24 | $redis->lPop('t_list')->then(function ($value) { 25 | printf("Pop \"%s\" elements from list\n", $value); 26 | }); 27 | $redis->lIndex('t_list', 0)->then(function ($value) { 28 | printf("Index 0 element: \"%s\" elements from list\n", $value); 29 | 30 | }); 31 | $redis->lInsert('t_list', 'after', 4, 10)->then(function ($_) { 32 | printf("New element: \"%s\" insert to list\n", '10'); 33 | }); 34 | $redis->lLen('t_list')->then(function ($value) { 35 | printf("Now list length: %d\n", $value); 36 | }); 37 | $redis->lRange('t_list')->then(function ($value) { 38 | printf("All elements: %s\n", implode(',', $value)); 39 | }); 40 | $redis->lRem('t_list', 0, 4)->then(function ($value) { 41 | printf("Remove %d element(s) of that value equals 4\n", $value); 42 | }); 43 | $redis->lSet('t_list', 0, 3)->then(function ($_) { 44 | printf("Set index 0 elements value to 3."); 45 | }); 46 | $redis->lRange('t_list')->then(function ($value) { 47 | printf("In the end,the results should be: %s", implode(',', $value)); 48 | 49 | }); 50 | 51 | $redis->end(); 52 | }); 53 | 54 | $redis->on('stop', function () use ($loop) { 55 | $loop->stop(); 56 | }); 57 | 58 | $loop->onStart(function () use ($redis) { 59 | $redis->start(); 60 | }); 61 | 62 | $loop->start(); -------------------------------------------------------------------------------- /example/set_example.php: -------------------------------------------------------------------------------- 1 | on('start', function (Redis $redis) { 18 | $redis->flushDb(); 19 | 20 | $redis->sAdd('t_set', 1, 2, 3, 4, 5)->then(function ($value) { 21 | printf("Add %d elements to set\n", $value); 22 | }); 23 | 24 | $redis->sMembers('t_set')->then(function ($value) { 25 | printf("All elements are: %s\n", implode(',', $value)); 26 | }); 27 | 28 | $redis->sAdd('t_a_set', 4, 5, 6, 7, 8, 9)->then(function ($value) { 29 | printf("Then add %d elements to another set\n", $value); 30 | }); 31 | 32 | $redis->sMembers('t_a_set')->then(function ($value) { 33 | printf("All elements are: %s\n", implode(',', $value)); 34 | }); 35 | 36 | $redis->sDiff('t_set', 't_a_set')->then(function ($value) { 37 | printf("Difference set [t_set - t_a_set] is: %s\n", implode(',', $value)); 38 | }); 39 | 40 | $redis->sUnion('t_set', 't_a_set')->then(function ($value) { 41 | printf("Intersection [t_set %s t_a_set] is: %s\n", "\u{2229}" ,implode(',', $value)); 42 | }); 43 | 44 | $redis->sMove('t_set', 't_a_set', 1)->then(function ($value) use ($redis) { 45 | printf("Move %d elements from t_set to t_a_set\n", $value); 46 | }); 47 | 48 | $redis->sCard('t_set')->then(function ($value) use ($redis) { 49 | printf("Now t_set has %d elements,", $value); 50 | }); 51 | 52 | $redis->sCard('t_a_set')->then(function ($value) use ($redis) { 53 | printf("t_set has %d elements\n", $value); 54 | }); 55 | 56 | $redis->sRem('t_set', 2)->then(function ($value) { 57 | printf("Remove %d element from t_set and it is %s\n", $value, '"2"'); 58 | }); 59 | 60 | $redis->sIsMember('test', 2)->then(function ($value) { 61 | printf("In the end %s %s exist in t_set too\n", '"2"', $value ? 'is' : 'is not'); 62 | }); 63 | 64 | //And more than those commands that you could use... 65 | $redis->end(); 66 | }); 67 | 68 | //stop global loop cauz that loop run for only one component(redis) 69 | //if use more than one dazzle component,do not use like that 70 | $redis->on('stop', function () use ($loop) { 71 | $loop->stop(); 72 | }); 73 | 74 | $redis->on('error', function (Exception $e) use ($redis) { 75 | echo $e->getMessage().PHP_EOL; 76 | $redis->stop(); 77 | }); 78 | 79 | $loop->onStart(function () use ($redis) { 80 | $redis->start(); 81 | }); 82 | 83 | $loop->start(); -------------------------------------------------------------------------------- /example/transaction_example.php: -------------------------------------------------------------------------------- 1 | on('start', function (Redis $redis) { 14 | $redis->multi()->then(function ($value) { 15 | printf("Transactions start %s\n", $value); 16 | }); 17 | //declare transactions 18 | $redis->set('1', 1); 19 | $redis->get('1'); 20 | //execute transactions 21 | $redis->exec()->then(function ($value) { 22 | printf("Transactions executed results: %s\n", implode(',', $value)); 23 | }); 24 | 25 | $redis->end(); 26 | }); 27 | 28 | $redis->on('stop', function () use ($loop) { 29 | $loop->stop(); 30 | }); 31 | 32 | $loop->onStart(function () use ($redis) { 33 | $redis->start(); 34 | }); 35 | 36 | $loop->start(); -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | test/TModule 21 | 22 | 23 | 24 | test/TUnit 25 | 26 | 27 | 28 | 29 | 30 | src 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Redis/Command/Api/ApiChannelInterface.php: -------------------------------------------------------------------------------- 1 | dispatch(Builder::build($command, $args)); 28 | } 29 | 30 | /** 31 | * @override 32 | * @inheritDoc 33 | */ 34 | public function pubSub($command, array $args = []) 35 | { 36 | // TODO: Implement pubSub() method. 37 | $command = Enum::PUBSUB; 38 | 39 | return $this->dispatch(Builder::build($command, $args)); 40 | } 41 | 42 | /** 43 | * @override 44 | * @inheritDoc 45 | */ 46 | public function publish($channel, $message) 47 | { 48 | // TODO: Implement publish() method. 49 | $command = Enum::PUBLISH; 50 | $args = [$channel, $message]; 51 | 52 | return $this->dispatch(Builder::build($command, $args)); 53 | } 54 | 55 | /** 56 | * @override 57 | * @inheritDoc 58 | */ 59 | public function pUnsubscribe(...$patterns) 60 | { 61 | // TODO: Implement pUnsubscribe() method. 62 | $command = Enum::PUNSUBSCRIBE; 63 | $args = $patterns; 64 | 65 | return $this->dispatch(Builder::build($command, $args)); 66 | } 67 | 68 | /** 69 | * @override 70 | * @inheritDoc 71 | */ 72 | public function unSubscribe(...$channels) 73 | { 74 | // TODO: Implement unSubscribe() method. 75 | $command = Enum::UNSUBSCRIBE; 76 | $args = $channels; 77 | 78 | return $this->dispatch(Builder::build($command, $args)); 79 | } 80 | 81 | /** 82 | * @override 83 | * @inheritDoc 84 | */ 85 | public function subscribe(...$channels) 86 | { 87 | // TODO: Implement subscribe() method. 88 | $command = Enum::SUBSCRIBE; 89 | $args = $channels; 90 | 91 | return $this->dispatch(Builder::build($command, $args)); 92 | } 93 | } -------------------------------------------------------------------------------- /src/Redis/Command/Compose/ApiClusterTrait.php: -------------------------------------------------------------------------------- 1 | dispatch(Builder::build($command, $args)); 28 | } 29 | 30 | /** 31 | * @override 32 | * @inheritDoc 33 | */ 34 | public function clusterCountFailureReports($nodeId) 35 | { 36 | // TODO: Implement clusterCountFailureReports() method. 37 | $command = Enum::CLUSTER_COUNT_FAILURE_REPORTS; 38 | $args = [$nodeId]; 39 | 40 | return $this->dispatch(Builder::build($command, $args)); 41 | } 42 | 43 | /** 44 | * @override 45 | * @inheritDoc 46 | */ 47 | public function clusterCountKeysInSlot($slot) 48 | { 49 | // TODO: Implement clusterCountKeysInSlot() method. 50 | $command = Enum::CLUSTER_COUNTKEYSINSLOT; 51 | $args = $slot; 52 | 53 | return $this->dispatch(Builder::build($command, $args)); 54 | } 55 | 56 | /** 57 | * @override 58 | * @inheritDoc 59 | */ 60 | public function clusterDelSlots(...$slots) 61 | { 62 | // TODO: Implement clusterDelSlots() method. 63 | $command = Enum::CLUSTER_DELSLOTS; 64 | $args = $slots; 65 | 66 | return $this->dispatch(Builder::build($command, $args)); 67 | } 68 | 69 | /** 70 | * @override 71 | * @inheritDoc 72 | */ 73 | public function clusterFailOver($operation) 74 | { 75 | // TODO: Implement clusterFailOver() method. 76 | } 77 | 78 | /** 79 | * @override 80 | * @inheritDoc 81 | */ 82 | public function clusterForget($nodeId) 83 | { 84 | // TODO: Implement clusterForget() method. 85 | } 86 | 87 | /** 88 | * @override 89 | * @inheritDoc 90 | */ 91 | public function clusterGetKeyInSlot($slot, $count) 92 | { 93 | // TODO: Implement clusterGetKeyInSlot() method. 94 | } 95 | 96 | /** 97 | * @override 98 | * @inheritDoc 99 | */ 100 | public function clusterInfo() 101 | { 102 | // TODO: Implement clusterInfo() method. 103 | $command = Enum::CLUSTER_INFO; 104 | 105 | return $this->dispatch(Builder::build($command)); 106 | } 107 | 108 | /** 109 | * @override 110 | * @inheritDoc 111 | */ 112 | public function clusterKeySlot($key) 113 | { 114 | // TODO: Implement clusterKeySlot() method. 115 | $command = Enum::CLUSTER_KEYSLOT; 116 | $args = [$key]; 117 | 118 | return $this->dispatch(Builder::build($command, $args)); 119 | } 120 | 121 | /** 122 | * @override 123 | * @inheritDoc 124 | */ 125 | public function clusterMeet($ip, $port) 126 | { 127 | // TODO: Implement clusterMeet() method. 128 | $command = Enum::CLUSTER_MEET; 129 | $args = [$ip, $port]; 130 | 131 | return $this->dispatch(Builder::build($command, $args)); 132 | } 133 | 134 | /** 135 | * @override 136 | * @inheritDoc 137 | */ 138 | public function clusterNodes() 139 | { 140 | // TODO: Implement clusterNodes() method. 141 | } 142 | 143 | /** 144 | * @override 145 | * @inheritDoc 146 | */ 147 | public function clusterReplicate($nodeId) 148 | { 149 | // TODO: Implement clusterReplicate() method. 150 | } 151 | 152 | /** 153 | * @override 154 | * @inheritDoc 155 | */ 156 | public function clusterReset($mode) 157 | { 158 | // TODO: Implement clusterReset() method. 159 | } 160 | 161 | /** 162 | * @override 163 | * @inheritDoc 164 | */ 165 | public function clusterSaveConfig() 166 | { 167 | // TODO: Implement clusterSaveConfig() method. 168 | } 169 | 170 | /** 171 | * @override 172 | * @inheritDoc 173 | */ 174 | public function clusterSetConfigEpoch($configEpoch) 175 | { 176 | // TODO: Implement clusterSetConfigEpoch() method. 177 | } 178 | 179 | /** 180 | * @inheritDoc 181 | */ 182 | public function clusterSetSlot($command, $nodeId) 183 | { 184 | // TODO: Implement clusterSetSlot() method. 185 | $command = Enum::CLUSTER_SETSLOT; 186 | $args = [$command, $nodeId]; 187 | 188 | return $this->dispatch(Builder::build($command, $args)); 189 | } 190 | 191 | /** 192 | * @override 193 | * @inheritDoc 194 | */ 195 | public function clusterSlaves($nodeId) 196 | { 197 | // TODO: Implement clusterSlaves() method. 198 | $command = Enum::CLUSTER_SLAVES; 199 | $args = [$nodeId]; 200 | 201 | return $this->dispatch(Builder::build($command, $args)); 202 | } 203 | 204 | /** 205 | * @override 206 | * @inheritDoc 207 | */ 208 | public function clusterSlots() 209 | { 210 | // TODO: Implement clusterSlots() method. 211 | $command = Enum::CLUSTER_SLOTS; 212 | 213 | return $this->dispatch(Builder::build($command)); 214 | } 215 | 216 | /** 217 | * @override 218 | * @inheritDoc 219 | */ 220 | public function readOnly() 221 | { 222 | // TODO: Implement readOnly() method. 223 | $command = Enum::READONLY; 224 | 225 | return $this->dispatch(Builder::build($command)); 226 | } 227 | 228 | /** 229 | * @override 230 | * @inheritDoc 231 | */ 232 | public function readWrite() 233 | { 234 | // TODO: Implement readWrite() method. 235 | $command = Enum::READWRITE; 236 | 237 | return $this->dispatch(Builder::build($command)); 238 | } 239 | } -------------------------------------------------------------------------------- /src/Redis/Command/Compose/ApiConnTrait.php: -------------------------------------------------------------------------------- 1 | dispatch(Builder::build($command, $args)); 27 | } 28 | 29 | /** 30 | * @override 31 | * @inheritDoc 32 | */ 33 | public function ping($message = 'PING') 34 | { 35 | $command = Enum::PING; 36 | $args = [$message]; 37 | 38 | return $this->dispatch(Builder::build($command, $args)); 39 | } 40 | 41 | /** 42 | * @override 43 | * @inheritDoc 44 | */ 45 | public function quit() 46 | { 47 | $command = Enum::QUIT; 48 | 49 | return $this->dispatch(Builder::build($command)); 50 | } 51 | 52 | /** 53 | * @override 54 | * @inheritDoc 55 | */ 56 | public function select($index) 57 | { 58 | $command = Enum::SELECT; 59 | $args = [$index]; 60 | 61 | return $this->dispatch(Builder::build($command, $args)); 62 | } 63 | 64 | /** 65 | * @override 66 | * @inheritDoc 67 | */ 68 | public function swapBb($opt, $dst) 69 | { 70 | $command = Enum::SWAPDB; 71 | $args = [$opt, $dst]; 72 | 73 | return $this->dispatch(Builder::build($command, $args)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Redis/Command/Compose/ApiCoreTrait.php: -------------------------------------------------------------------------------- 1 | dispatch(Builder::build($command)); 26 | } 27 | 28 | /** 29 | * @override 30 | * @inheritDoc 31 | */ 32 | public function bgSave() 33 | { 34 | $command = Enum::BGSAVE; 35 | 36 | return $this->dispatch(Builder::build($command)); 37 | } 38 | 39 | /** 40 | * @override 41 | * @inheritDoc 42 | */ 43 | public function sync() 44 | { 45 | $command = Enum::SYNC; 46 | 47 | return $this->dispatch(Builder::build($command)); 48 | } 49 | 50 | /** 51 | * @override 52 | * @inheritDoc 53 | */ 54 | public function time() 55 | { 56 | $command = Enum::TIME; 57 | 58 | return $this->dispatch(Builder::build($command)); 59 | } 60 | 61 | /** 62 | * @override 63 | * @inheritDoc 64 | */ 65 | public function monitor() 66 | { 67 | $command = Enum::MONITOR; 68 | 69 | return $this->dispatch(Builder::build($command)); 70 | } 71 | 72 | /** 73 | * @override 74 | * @inheritDoc 75 | */ 76 | public function flushAll() 77 | { 78 | $command = Enum::FLUSHALL; 79 | 80 | return $this->dispatch(Builder::build($command)); 81 | } 82 | 83 | /** 84 | * @override 85 | * @inheritDoc 86 | */ 87 | public function flushDb() 88 | { 89 | $command = Enum::FLUSHDB; 90 | 91 | return $this->dispatch(Builder::build($command)); 92 | } 93 | 94 | /** 95 | * @override 96 | * @inheritDoc 97 | */ 98 | public function info($section = []) 99 | { 100 | $command = Enum::INFO; 101 | 102 | return $this->dispatch(Builder::build($command, $section))->then(function ($value) { 103 | if ($value) { 104 | $ret = explode("\r\n", $value); 105 | $handled = []; 106 | $lastKey = ''; 107 | 108 | foreach ($ret as $_ => $v) 109 | { 110 | if (($pos = strpos($v, '#')) !== false) 111 | { 112 | $lastKey = strtolower(substr($v,$pos+2)); 113 | $handled[$lastKey] = []; 114 | continue; 115 | } 116 | if ($v === '') { 117 | continue; 118 | } 119 | if (($statMap = explode(':', $v)) && $statMap[0] && $statMap[1]) 120 | { 121 | list($name, $stat) = explode(':', $v); 122 | $handled[$lastKey][$name] = $stat; 123 | } 124 | } 125 | 126 | return $handled; 127 | } 128 | 129 | return $value; 130 | }); 131 | } 132 | 133 | /** 134 | * @override 135 | * @inheritDoc 136 | */ 137 | public function slaveOf($host, $port) 138 | { 139 | $command = Enum::SLAVEOF; 140 | $args = [$host, $port]; 141 | 142 | return $this->dispatch(Builder::build($command, $args)); 143 | } 144 | 145 | /** 146 | * @override 147 | * @inheritDoc 148 | */ 149 | public function slowLog($subCommand, array $args = []) 150 | { 151 | $command = Enum::SLOWLOG; 152 | $args = array_merge([$subCommand],$args); 153 | 154 | return $this->dispatch(Builder::build($command, $args)); 155 | } 156 | 157 | /** 158 | * @override 159 | * @inheritDoc 160 | */ 161 | public function save() 162 | { 163 | $command = Enum::SAVE; 164 | 165 | return $this->dispatch(Builder::build($command)); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/Redis/Command/Compose/ApiGeospatialTrait.php: -------------------------------------------------------------------------------- 1 | dispatch(Builder::build($command, $args)); 29 | } 30 | 31 | /** 32 | * @override 33 | * @inheritDoc 34 | */ 35 | public function geoHash($key, ...$members) 36 | { 37 | // TODO: Implement geoHash() method. 38 | $command = Enum::GEOHASH; 39 | } 40 | 41 | /** 42 | * @override 43 | * @inheritDoc 44 | */ 45 | public function geoPos($key, ...$members) 46 | { 47 | // TODO: Implement geoPos() method. 48 | $command = Enum::GEOPOS; 49 | $args = [$key]; 50 | $args = array_merge($args, $members); 51 | 52 | return $this->dispatch(Builder::build($command, $args)); 53 | } 54 | 55 | /** 56 | * @override 57 | * @inheritDoc 58 | */ 59 | public function geoDist($key, $memberA, $memberB, $unit) 60 | { 61 | // TODO: Implement geoDist() method. 62 | $command = Enum::GEODIST; 63 | $args = [$key, $memberA, $memberB ,$unit]; 64 | 65 | return $this->dispatch(Builder::build($command, $args)); 66 | } 67 | 68 | /** 69 | * @override 70 | * @inheritDoc 71 | */ 72 | public function geoRadius($key, $longitude, $latitude, $unit, $command, $count, $sort) 73 | { 74 | // TODO: Implement geoRadius() method. 75 | $command = Enum::GEORADIUS; 76 | $args = [$key, $longitude, $latitude, $unit, $command, $count, $sort]; 77 | 78 | return $this->dispatch(Builder::build($command, $args)); 79 | } 80 | 81 | /** 82 | * @override 83 | * @inheritDoc 84 | */ 85 | public function geoRadiusByMember($key, $member, $unit, $command, $count, $sort, $store, $storeDist) 86 | { 87 | // TODO: Implement geoRadiusByMember() method. 88 | $command = Enum::GEORADIUSBYMEMBER; 89 | $args = [$key, $member, $unit, $command, $count, $sort, $store, $storeDist]; 90 | 91 | return $this->dispatch(Builder::build($command, $args)); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Redis/Command/Compose/ApiHyperLogTrait.php: -------------------------------------------------------------------------------- 1 | dispatch(Builder::build($command, $args)); 28 | } 29 | 30 | /** 31 | * @override 32 | * @inheritDoc 33 | */ 34 | public function pFCount(...$keys) 35 | { 36 | $command = Enum::PFCOUNT; 37 | $args = $keys; 38 | 39 | return $this->dispatch(Builder::build($command, $args)); 40 | } 41 | 42 | /** 43 | * @override 44 | * @inheritDoc 45 | */ 46 | public function pFMerge($dstKey, ...$srcKeys) 47 | { 48 | $command = Enum::PFMERGE; 49 | $args = array_merge([$dstKey], $srcKeys); 50 | 51 | return $this->dispatch(Builder::build($command, $args)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Redis/Command/Compose/ApiKeyValTrait.php: -------------------------------------------------------------------------------- 1 | dispatch(Builder::build($command, $args)); 27 | } 28 | 29 | /** 30 | * @override 31 | * @inheritDoc 32 | */ 33 | public function bitCount($key, $start = 0, $end = -1) 34 | { 35 | $command = Enum::BITCOUNT; 36 | $args = [$key, $start, $end]; 37 | 38 | return $this->dispatch(Builder::build($command, $args)); 39 | } 40 | 41 | /** 42 | * @override 43 | * @inheritDoc 44 | */ 45 | public function bitField($key, $subCommand, ...$param) 46 | { 47 | $command = Enum::BITFIELD; 48 | $subCommand = strtoupper($subCommand); 49 | //TODO: control flow improvement 50 | switch ($subCommand) { 51 | case 'GET' : { 52 | @list ($type, $offset) = $param; 53 | $args = [$key, $subCommand, $type, $offset]; 54 | break; 55 | } 56 | case 'SET' : { 57 | @list ($type, $offset, $value) = $param; 58 | $args = [$key, $subCommand, $type, $offset, $value]; 59 | break; 60 | } 61 | case 'INCRBY' : { 62 | @list ($type, $offset, $increment) = $param; 63 | $args = [$key, $type, $offset, $increment]; 64 | break; 65 | } 66 | case 'OVERFLOW' : { 67 | @list ($behavior) = $param; 68 | $args = [$key, $subCommand, $behavior]; 69 | break; 70 | } 71 | default : { 72 | $args = []; 73 | break; 74 | } 75 | } 76 | 77 | return $this->dispatch(Builder::build($command, $args)); 78 | } 79 | 80 | /** 81 | * @override 82 | * @inheritDoc 83 | */ 84 | public function bitOp($operation, $dstKey, $srcKey, ...$keys) 85 | { 86 | $command = Enum::BITOP; 87 | $args = [$operation, $dstKey, $srcKey]; 88 | $args = array_merge($args, $keys); 89 | 90 | return $this->dispatch(Builder::build($command, $args)); 91 | } 92 | 93 | /** 94 | * @override 95 | * @inheritDoc 96 | */ 97 | public function bitPos($key, $bit, $start = 0, $end = -1) 98 | { 99 | $command = Enum::BITPOS; 100 | $args = [$key, $bit, $start, $end]; 101 | 102 | return $this->dispatch(Builder::build($command, $args)); 103 | } 104 | 105 | /** 106 | * @override 107 | * @inheritDoc 108 | */ 109 | public function decr($key) 110 | { 111 | $command = Enum::DECR; 112 | $args = [$key]; 113 | 114 | return $this->dispatch(Builder::build($command, $args)); 115 | } 116 | 117 | /** 118 | * @override 119 | * @inheritDoc 120 | */ 121 | public function decrBy($key, $decrement) 122 | { 123 | $command = Enum::DECRBY; 124 | $args = [$key, $decrement]; 125 | 126 | return $this->dispatch(Builder::build($command, $args)); 127 | } 128 | 129 | /** 130 | * @override 131 | * @inheritDoc 132 | */ 133 | public function get($key) 134 | { 135 | $command = Enum::GET; 136 | $args = [$key]; 137 | 138 | return $this->dispatch(Builder::build($command, $args)); 139 | } 140 | 141 | /** 142 | * @override 143 | * @inheritDoc 144 | */ 145 | public function getBit($key, $offset) 146 | { 147 | $command = Enum::GETBIT; 148 | $args = [$key, $offset]; 149 | 150 | return $this->dispatch(Builder::build($command, $args)); 151 | } 152 | 153 | /** 154 | * @override 155 | * @inheritDoc 156 | */ 157 | public function getRange($key, $start, $end) 158 | { 159 | $command = Enum::GETRANGE; 160 | $args = [$key, $start, $end]; 161 | 162 | return $this->dispatch(Builder::build($command, $args)); 163 | } 164 | 165 | /** 166 | * @override 167 | * @inheritDoc 168 | */ 169 | public function getSet($key, $value) 170 | { 171 | $command = Enum::GETSET; 172 | $args = [$key, $value]; 173 | 174 | return $this->dispatch(Builder::build($command, $args)); 175 | } 176 | 177 | /** 178 | * @override 179 | * @inheritDoc 180 | */ 181 | public function incr($key) 182 | { 183 | $command = Enum::INCR; 184 | $args = [$key]; 185 | 186 | return $this->dispatch(Builder::build($command, $args)); 187 | } 188 | 189 | /** 190 | * @override 191 | * @inheritDoc 192 | */ 193 | public function incrBy($key, $increment) 194 | { 195 | $command = Enum::INCRBY; 196 | $args = [$key, $increment]; 197 | 198 | return $this->dispatch(Builder::build($command, $args)); 199 | } 200 | 201 | /** 202 | * @override 203 | * @inheritDoc 204 | */ 205 | public function incrByFloat($key, $increment) 206 | { 207 | $command = Enum::INCRBYFLOAT; 208 | $args = [$key, $increment]; 209 | 210 | return $this->dispatch(Builder::build($command, $args)); 211 | } 212 | 213 | /** 214 | * @override 215 | * @inheritDoc 216 | */ 217 | public function set($key, $value, array $options = []) 218 | { 219 | $command = Enum::SET; 220 | array_unshift($options, $key, $value); 221 | $args = $options; 222 | 223 | return $this->dispatch(Builder::build($command, $args)); 224 | } 225 | 226 | /** 227 | * @override 228 | * @inheritDoc 229 | */ 230 | public function setBit($key, $offset, $value) 231 | { 232 | $command = Enum::SETBIT; 233 | $args = [$key, $offset, $value]; 234 | 235 | return $this->dispatch(Builder::build($command, $args)); 236 | } 237 | 238 | /** 239 | * @override 240 | * @inheritDoc 241 | */ 242 | public function setEx($key, $seconds, $value) 243 | { 244 | $command = Enum::SETEX; 245 | $args = [$key, $seconds, $value]; 246 | 247 | return $this->dispatch(Builder::build($command, $args)); 248 | } 249 | 250 | /** 251 | * @override 252 | * @inheritDoc 253 | */ 254 | public function setNx($key, $value) 255 | { 256 | $command = Enum::SETNX; 257 | $args = [$key, $value]; 258 | 259 | return $this->dispatch(Builder::build($command, $args)); 260 | } 261 | 262 | /** 263 | * @override 264 | * @inheritDoc 265 | */ 266 | public function setRange($key, $offset, $value) 267 | { 268 | $command = Enum::SETRANGE; 269 | $args = [$key, $offset, $value]; 270 | 271 | return $this->dispatch(Builder::build($command, $args)); 272 | } 273 | 274 | /** 275 | * @override 276 | * @inheritDoc 277 | */ 278 | public function pSetEx($key, $milliseconds, $value) 279 | { 280 | $command = Enum::PSETEX; 281 | $args = [$key, $milliseconds, $value]; 282 | 283 | return $this->dispatch(Builder::build($command, $args)); 284 | } 285 | 286 | /** 287 | * @override 288 | * @inheritDoc 289 | */ 290 | public function mGet($key, ...$keys) 291 | { 292 | $command = Enum::MGET; 293 | $args = [$key]; 294 | $args = array_merge($args, $keys); 295 | 296 | return $this->dispatch(Builder::build($command, $args)); 297 | } 298 | 299 | /** 300 | * @override 301 | * @inheritDoc 302 | */ 303 | public function mSet(array $kvMap) 304 | { 305 | //TODO: change the param $kvMap to ...$kv,cauz map not allow duplicate key 306 | $command = Enum::MSET; 307 | $args = []; 308 | if (!empty($kvMap)) { 309 | foreach ($kvMap as $key => $val) { 310 | $args[] = $key; 311 | $args[] = $val; 312 | } 313 | } 314 | 315 | return $this->dispatch(Builder::build($command, $args)); 316 | } 317 | 318 | /** 319 | * @override 320 | * @inheritDoc 321 | */ 322 | public function mSetNx($kvMap) 323 | { 324 | $command = Enum::MSETNX; 325 | $args = []; 326 | if (!empty($kvMap)) { 327 | foreach ($kvMap as $key => $val) { 328 | $args[] = $key; 329 | $args[] = $val; 330 | } 331 | } 332 | 333 | return $this->dispatch(Builder::build($command, $args)); 334 | } 335 | 336 | /** 337 | * @override 338 | * @inheritDoc 339 | */ 340 | public function strLen($key) 341 | { 342 | $command = Enum::STRLEN; 343 | $args = [$key]; 344 | 345 | return $this->dispatch(Builder::build($command, $args)); 346 | } 347 | 348 | /** 349 | * @override 350 | * @inheritDoc 351 | */ 352 | public function del($key,...$keys) 353 | { 354 | $command = Enum::DEL; 355 | $keys[] = $key; 356 | $args = $keys; 357 | 358 | return $this->dispatch(Builder::build($command, $args)); 359 | } 360 | 361 | /** 362 | * @override 363 | * @inheritDoc 364 | */ 365 | public function dump($key) 366 | { 367 | $command = Enum::DUMP; 368 | $args = [$key]; 369 | 370 | return $this->dispatch(Builder::build($command, $args)); 371 | } 372 | 373 | /** 374 | * @override 375 | * @inheritDoc 376 | */ 377 | public function exists($key, ...$keys) 378 | { 379 | $command = Enum::EXISTS; 380 | $args = [$key]; 381 | $args = array_merge($args, $keys); 382 | 383 | return $this->dispatch(Builder::build($command, $args)); 384 | } 385 | 386 | /** 387 | * @override 388 | * @inheritDoc 389 | */ 390 | public function expire($key, $seconds) 391 | { 392 | $command = Enum::EXPIRE; 393 | $args = [$key, $seconds]; 394 | 395 | return $this->dispatch(Builder::build($command, $args)); 396 | } 397 | 398 | /** 399 | * @override 400 | * @inheritDoc 401 | */ 402 | public function expireAt($key, $timestamp) 403 | { 404 | $command = Enum::EXPIREAT; 405 | $args = [$key, $timestamp]; 406 | 407 | return $this->dispatch(Builder::build($command, $args)); 408 | } 409 | 410 | /** 411 | * @override 412 | * @inheritDoc 413 | */ 414 | public function persist($key) 415 | { 416 | $command = Enum::PERSIST; 417 | $args = [$key]; 418 | 419 | return $this->dispatch(Builder::build($command, $args)); 420 | } 421 | 422 | /** 423 | * @override 424 | * @inheritDoc 425 | */ 426 | public function pExpire($key, $milliseconds) 427 | { 428 | $command = Enum::PEXPIRE; 429 | $args = [$key, $milliseconds]; 430 | 431 | return $this->dispatch(Builder::build($command, $args)); 432 | } 433 | 434 | /** 435 | * @override 436 | * @inheritDoc 437 | */ 438 | public function pExpireAt($key, $milTimestamp) 439 | { 440 | $command = Enum::PEXPIREAT; 441 | $args = [$key, $milTimestamp]; 442 | 443 | return $this->dispatch(Builder::build($command, $args)); 444 | } 445 | 446 | /** 447 | * @override 448 | * @inheritDoc 449 | */ 450 | public function touch($key, ...$keys) 451 | { 452 | $command = Enum::TOUCH; 453 | $args = [$key]; 454 | $args = array_merge($args, $keys); 455 | 456 | return $this->dispatch(Builder::build($command, $args)); 457 | } 458 | 459 | /** 460 | * @override 461 | * @inheritDoc 462 | */ 463 | public function ttl($key) 464 | { 465 | $command = Enum::TTL; 466 | $args = [$key]; 467 | 468 | return $this->dispatch(Builder::build($command, $args)); 469 | } 470 | 471 | /** 472 | * @override 473 | * @inheritDoc 474 | */ 475 | public function type($key) 476 | { 477 | $command = Enum::TYPE; 478 | $args = [$key]; 479 | 480 | return $this->dispatch(Builder::build($command, $args)); 481 | } 482 | 483 | /** 484 | * @override 485 | * @inheritDoc 486 | */ 487 | public function unLink($key, ...$keys) 488 | { 489 | $command = Enum::UNLINK; 490 | $args = [$key]; 491 | $args = array_merge($args, $keys); 492 | 493 | return $this->dispatch(Builder::build($command, $args)); 494 | } 495 | 496 | /** 497 | * @override 498 | * @inheritDoc 499 | */ 500 | public function wait($numSlaves, $timeout) 501 | { 502 | $command = Enum::WAIT; 503 | $args = [$numSlaves, $timeout]; 504 | 505 | return $this->dispatch(Builder::build($command, $args)); 506 | } 507 | 508 | /** 509 | * @override 510 | * @inheritDoc 511 | */ 512 | public function randomKey() 513 | { 514 | $command = Enum::RANDOMKEY; 515 | 516 | return $this->dispatch(Builder::build($command)); 517 | } 518 | 519 | /** 520 | * @override 521 | * @inheritDoc 522 | */ 523 | public function rename($key, $newKey) 524 | { 525 | $command = Enum::RENAME; 526 | $args = [$key, $newKey]; 527 | 528 | return $this->dispatch(Builder::build($command, $args)); 529 | } 530 | 531 | /** 532 | * @override 533 | * @inheritDoc 534 | */ 535 | public function renameNx($key, $newKey) 536 | { 537 | $command = Enum::RENAMENX; 538 | $args = [$key, $newKey]; 539 | 540 | return $this->dispatch(Builder::build($command, $args)); 541 | } 542 | 543 | /** 544 | * @override 545 | * @inheritDoc 546 | */ 547 | public function restore($key, $ttl, $value) 548 | { 549 | $command = Enum::RESTORE; 550 | $args = [$key, $ttl, $value]; 551 | 552 | return $this->dispatch(Builder::build($command, $args)); 553 | } 554 | 555 | /** 556 | * @override 557 | * @inheritDoc 558 | */ 559 | public function pTtl($key) 560 | { 561 | $command = Enum::PTTL; 562 | $args = [$key]; 563 | 564 | return $this->dispatch(Builder::build($command, $args)); 565 | } 566 | 567 | /** 568 | * @override 569 | * @inheritDoc 570 | */ 571 | public function move($key, $db) 572 | { 573 | $command = Enum::MOVE; 574 | $args = [$key, $db]; 575 | 576 | return $this->dispatch(Builder::build($command, $args)); 577 | } 578 | 579 | /** 580 | * @override 581 | * @inheritDoc 582 | */ 583 | public function scan($cursor, array $options = []) 584 | { 585 | $command = Enum::SCAN; 586 | $args = [$cursor]; 587 | $args = array_merge($args, $options); 588 | 589 | return $this->dispatch(Builder::build($command, $args)); 590 | } 591 | 592 | /** 593 | * @override 594 | * @inheritDoc 595 | */ 596 | public function sort($key, array $options = []) 597 | { 598 | $command = Enum::SORT; 599 | $args = [$key]; 600 | $args = array_merge($args, $options); 601 | 602 | return $this->dispatch(Builder::build($command, $args)); 603 | } 604 | 605 | /** 606 | * @override 607 | * @inheritDoc 608 | */ 609 | public function keys($key = '*') 610 | { 611 | $command = Enum::KEYS; 612 | $args = [$key]; 613 | 614 | return $this->dispatch(Builder::build($command, $args)); 615 | } 616 | } 617 | -------------------------------------------------------------------------------- /src/Redis/Command/Compose/ApiListTrait.php: -------------------------------------------------------------------------------- 1 | dispatch(Builder::build($command, $args)); 27 | $promise = $promise->then(function ($value) { 28 | if (is_array($value)) { 29 | list($k,$v) = $value; 30 | 31 | return [ 32 | 'key'=>$k, 33 | 'value'=>$v 34 | ]; 35 | } 36 | 37 | return $value; 38 | }); 39 | 40 | return $promise; 41 | } 42 | 43 | /** 44 | * @override 45 | * @inheritDoc 46 | */ 47 | public function brPop(array $keys, $timeout) 48 | { 49 | $command = Enum::BRPOP; 50 | $keys[] = $timeout; 51 | $args = $keys; 52 | $promise = $this->dispatch(Builder::build($command, $args)); 53 | $promise = $promise->then(function ($value) { 54 | if (is_array($value)) { 55 | list($k,$v) = $value; 56 | 57 | return [ 58 | 'key'=>$k, 59 | 'value'=>$v 60 | ]; 61 | } 62 | 63 | return $value; 64 | }); 65 | 66 | return $promise; 67 | } 68 | 69 | /** 70 | * @override 71 | * @inheritDoc 72 | */ 73 | public function brPopLPush($src, $dst, $timeout) 74 | { 75 | $command = Enum::BRPOPLPUSH; 76 | $args = [$src, $dst, $timeout]; 77 | 78 | return $this->dispatch(Builder::build($command, $args)); 79 | } 80 | 81 | /** 82 | * @override 83 | * @inheritDoc 84 | */ 85 | public function lIndex($key, $index) 86 | { 87 | $command = Enum::LINDEX; 88 | $args = [$key, $index]; 89 | 90 | return $this->dispatch(Builder::build($command, $args)); 91 | } 92 | 93 | /** 94 | * @override 95 | * @inheritDoc 96 | */ 97 | public function lInsert($key, $action, $pivot, $value) 98 | { 99 | $command = Enum::LINSERT; 100 | $args = [$key, $action, $pivot, $value]; 101 | 102 | return $this->dispatch(Builder::build($command, $args)); 103 | } 104 | 105 | /** 106 | * @override 107 | * @inheritDoc 108 | */ 109 | public function lLen($key) 110 | { 111 | $command = Enum::LLEN; 112 | $args = [$key]; 113 | 114 | return $this->dispatch(Builder::build($command, $args)); 115 | } 116 | 117 | /** 118 | * @override 119 | * @inheritDoc 120 | */ 121 | public function lPop($key) 122 | { 123 | $command = Enum::LPOP; 124 | $args = [$key]; 125 | 126 | return $this->dispatch(Builder::build($command, $args)); 127 | } 128 | 129 | /** 130 | * @override 131 | * @inheritDoc 132 | */ 133 | public function lPush($key,...$values) 134 | { 135 | $command = Enum::LPUSH; 136 | array_unshift($values, $key); 137 | 138 | return $this->dispatch(Builder::build($command, $values)); 139 | } 140 | 141 | public function lPushX($key, $value) 142 | { 143 | $command = Enum::LPUSHX; 144 | $args = [$key, $value]; 145 | 146 | return $this->dispatch(Builder::build($command, $args)); 147 | } 148 | 149 | /** 150 | * @override 151 | * @inheritDoc 152 | */ 153 | public function lRange($key, $start = 0, $stop = -1) 154 | { 155 | $command = Enum::LRANGE; 156 | $args = [$key, $start, $stop]; 157 | 158 | return $this->dispatch(Builder::build($command, $args)); 159 | } 160 | 161 | /** 162 | * @override 163 | * @inheritDoc 164 | */ 165 | public function lRem($key, $count, $value) 166 | { 167 | $command = Enum::LREM; 168 | $args = [$key, $count, $value]; 169 | 170 | return $this->dispatch(Builder::build($command, $args)); 171 | } 172 | 173 | /** 174 | * @override 175 | * @inheritDoc 176 | */ 177 | public function lSet($key, $index, $value) 178 | { 179 | $command = Enum::LSET; 180 | $args = [$key, $index, $value]; 181 | 182 | return $this->dispatch(Builder::build($command, $args)); 183 | } 184 | 185 | /** 186 | * @override 187 | * @inheritDoc 188 | */ 189 | public function lTrim($key, $start, $stop) 190 | { 191 | $command = Enum::LTRIM; 192 | $args = [$key, $start, $stop]; 193 | 194 | return $this->dispatch(Builder::build($command, $args)); 195 | } 196 | 197 | /** 198 | * @override 199 | * @inheritDoc 200 | */ 201 | public function rPop($key) 202 | { 203 | $command = Enum::RPOP; 204 | $args = [$key]; 205 | 206 | return $this->dispatch(Builder::build($command, $args)); 207 | } 208 | 209 | /** 210 | * @override 211 | * @inheritDoc 212 | */ 213 | public function rPopLPush($src, $dst) 214 | { 215 | $command = Enum::RPOPLPUSH; 216 | $args = [$src, $dst]; 217 | 218 | return $this->dispatch(Builder::build($command, $args)); 219 | } 220 | 221 | /** 222 | * @override 223 | * @inheritDoc 224 | */ 225 | public function rPush($key, ...$values) 226 | { 227 | $command = Enum::RPUSH; 228 | $args = [$key]; 229 | $args = array_merge($args, $values); 230 | 231 | return $this->dispatch(Builder::build($command, $args)); 232 | } 233 | 234 | /** 235 | * @override 236 | * @inheritDoc 237 | */ 238 | public function rPushX($key, $value) 239 | { 240 | $command = Enum::RPUSHX; 241 | $args = [$key, $value]; 242 | 243 | return $this->dispatch(Builder::build($command, $args)); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/Redis/Command/Compose/ApiSetHashTrait.php: -------------------------------------------------------------------------------- 1 | dispatch(Builder::build($command, $args)); 28 | } 29 | 30 | /** 31 | * @override 32 | * @inheritDoc 33 | */ 34 | public function hGet($key, $field) 35 | { 36 | $command = Enum::HGET; 37 | $args = [$key, $field]; 38 | 39 | return $this->dispatch(Builder::build($command, $args)); 40 | } 41 | 42 | /** 43 | * @override 44 | * @inheritDoc 45 | */ 46 | public function hGetAll($key) 47 | { 48 | $command = Enum::HGETALL; 49 | $args = [$key]; 50 | 51 | return $this->dispatch(Builder::build($command, $args))->then(function ($value) { 52 | if (!empty($value)) { 53 | $tmp = []; 54 | $size = count($value); 55 | for ($i=0; $i<$size; $i+=2) { 56 | $field = $value[$i]; 57 | $val = $value[$i+1]; 58 | $tmp[$field] = $val; 59 | } 60 | $value = $tmp; 61 | } 62 | 63 | return $value; 64 | }); 65 | } 66 | 67 | /** 68 | * @override 69 | * @inheritDoc 70 | */ 71 | public function hIncrBy($key, $field, $increment) 72 | { 73 | $command = Enum::HINCRBY; 74 | $args = [$key, $field, $increment]; 75 | 76 | return $this->dispatch(Builder::build($command, $args)); 77 | } 78 | 79 | /** 80 | * @override 81 | * @inheritDoc 82 | */ 83 | public function hIncrByFloat($key, $field, $increment) 84 | { 85 | $command = Enum::HINCRBYFLOAT; 86 | $args = [$key, $field, $increment]; 87 | 88 | return $this->dispatch(Builder::build($command, $args)); 89 | } 90 | 91 | /** 92 | * @override 93 | * @inheritDoc 94 | */ 95 | public function hKeys($key) 96 | { 97 | $command = Enum::HKEYS; 98 | $args = [$key]; 99 | 100 | return $this->dispatch(Builder::build($command, $args)); 101 | } 102 | 103 | /** 104 | * @override 105 | * @inheritDoc 106 | */ 107 | public function hLen($key) 108 | { 109 | $command = Enum::HLEN; 110 | $args = [$key]; 111 | 112 | return $this->dispatch(Builder::build($command, $args)); 113 | } 114 | 115 | /** 116 | * @override 117 | * @inheritDoc 118 | */ 119 | public function hMGet($key, ...$fields) 120 | { 121 | $command = Enum::HMGET; 122 | $args = [$key]; 123 | $args = array_merge($args, $fields); 124 | 125 | return $this->dispatch(Builder::build($command, $args)); 126 | } 127 | 128 | /** 129 | * @override 130 | * @inheritDoc 131 | */ 132 | public function hMSet($key, array $fvMap) 133 | { 134 | //TODO: replace param $fvMap to ...$fvs,cauz hash map not allow duplicate key 135 | $command = Enum::HMSET; 136 | $args = [$key]; 137 | if (!empty($fvMap)) { 138 | foreach ($fvMap as $field => $value) { 139 | $tmp[] = $field; 140 | $tmp[] = $value; 141 | } 142 | $fvMap = $tmp; 143 | } 144 | $args = array_merge($args, $fvMap); 145 | 146 | return $this->dispatch(Builder::build($command, $args)); 147 | } 148 | 149 | /** 150 | * @override 151 | * @inheritDoc 152 | */ 153 | public function hSet($key, $field, $value) 154 | { 155 | $command = Enum::HSET; 156 | $args = [$key, $field, $value]; 157 | 158 | return $this->dispatch(Builder::build($command, $args)); 159 | } 160 | 161 | /** 162 | * @override 163 | * @inheritDoc 164 | */ 165 | public function hSetNx($key, $filed, $value) 166 | { 167 | $command = Enum::HSETNX; 168 | $args = [$key, $filed, $value]; 169 | 170 | return $this->dispatch(Builder::build($command, $args)); 171 | } 172 | 173 | /** 174 | * @override 175 | * @inheritDoc 176 | */ 177 | public function hStrLen($key, $field) 178 | { 179 | $command = Enum::HSTRLEN; 180 | $args = [$key, $field]; 181 | 182 | return $this->dispatch(Builder::build($command, $args)); 183 | } 184 | 185 | /** 186 | * @override 187 | * @inheritDoc 188 | */ 189 | public function hVals($key) 190 | { 191 | $command = Enum::HVALS; 192 | $args = [$key]; 193 | 194 | return $this->dispatch(Builder::build($command, $args)); 195 | } 196 | 197 | /** 198 | * @override 199 | * @inheritDoc 200 | */ 201 | public function hScan($key, $cursor, array $options = []) 202 | { 203 | // TODO: Implement hScan() method. 204 | $command = Enum::HSCAN; 205 | $args = [$key, $cursor]; 206 | $args = array_merge($args, $options); 207 | 208 | return $this->dispatch(Builder::build($command, $args)); 209 | } 210 | 211 | /** 212 | * @inheritDoc 213 | */ 214 | public function hExists($key, $field) 215 | { 216 | $command = Enum::HEXISTS; 217 | $args = [$key, $field]; 218 | 219 | return $this->dispatch(Builder::build($command, $args)); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/Redis/Command/Compose/ApiSetSortedTrait.php: -------------------------------------------------------------------------------- 1 | dispatch(Builder::build($command, $args)); 27 | } 28 | 29 | /** 30 | * @override 31 | * @inheritDoc 32 | */ 33 | public function zCard($key) 34 | { 35 | $command = Enum::ZCARD; 36 | $args = [$key]; 37 | 38 | return $this->dispatch(Builder::build($command, $args)); 39 | } 40 | 41 | /** 42 | * @override 43 | * @inheritDoc 44 | */ 45 | public function zCount($key, $min, $max) 46 | { 47 | $command = Enum::ZCOUNT; 48 | $args = [$key, $min, $max]; 49 | 50 | return $this->dispatch(Builder::build($command, $args)); 51 | } 52 | 53 | /** 54 | * @override 55 | * @inheritDoc 56 | */ 57 | public function zIncrBy($key, $increment, $member) 58 | { 59 | $command = Enum::ZINCRBY; 60 | $args = [$key, $increment, $member]; 61 | 62 | return $this->dispatch(Builder::build($command, $args)); 63 | } 64 | 65 | /** 66 | * @override 67 | * @inheritDoc 68 | */ 69 | public function zInterStore($dst, $numKeys) 70 | { 71 | $command = Enum::ZINTERSTORE; 72 | $args = [$dst, $numKeys]; 73 | 74 | return $this->dispatch(Builder::build($command, $args)); 75 | } 76 | 77 | /** 78 | * @override 79 | * @inheritDoc 80 | */ 81 | public function zLexCount($key, $min, $max) 82 | { 83 | $command = Enum::ZLEXCOUNT; 84 | $args = [$key, $min, $max]; 85 | 86 | return $this->dispatch(Builder::build($command, $args)); 87 | } 88 | 89 | /** 90 | * @override 91 | * @inheritDoc 92 | */ 93 | public function zRange($key, $star = 0, $stop = -1, $withScores = false) 94 | { 95 | $command = Enum::ZRANGE; 96 | $args = [$key, $star, $stop]; 97 | if ($withScores) { 98 | $args[] = 'WITHSCORES'; 99 | return $this->dispatch(Builder::build($command, $args))->then(function ($value) { 100 | $len = count($value); 101 | $ret = []; 102 | for ($i=0; $i<$len; $i+=2) { 103 | $ret[$value[$i]] = $value[$i+1]; 104 | } 105 | return $ret; 106 | }); 107 | } 108 | 109 | return $this->dispatch(Builder::build($command, $args)); 110 | } 111 | 112 | /** 113 | * @override 114 | * @inheritDoc 115 | */ 116 | public function zRangeByLex($key, $min, $max, array $options = []) 117 | { 118 | $command = Enum::ZRANGEBYLEX; 119 | $args = [$key, $min, $max]; 120 | $args = array_merge($args, $options); 121 | 122 | return $this->dispatch(Builder::build($command, $args)); 123 | } 124 | 125 | /** 126 | * @override 127 | * @inheritDoc 128 | */ 129 | public function zRevRangeByLex($key, $max, $min, array $options = []) 130 | { 131 | $command = Enum::ZREVRANGEBYLEX; 132 | $args = [$key, $max,$min]; 133 | $args = array_merge($args,$options); 134 | 135 | return $this->dispatch(Builder::build($command, $args)); 136 | } 137 | 138 | /** 139 | * @override 140 | * @inheritDoc 141 | */ 142 | public function zRangeByScore($key, $min, $max, $withScores = false, $offset = 0, $count = 0) 143 | { 144 | $command = Enum::ZRANGEBYSCORE; 145 | $args = [$key, $min, $max]; 146 | if ($withScores === true) { 147 | $args[] = 'WITHSCORES'; 148 | } 149 | if ($offset != 0 || $count != 0) { 150 | $args[] = 'LIMIT'; 151 | $args[] = $offset; 152 | $args[] = $count; 153 | } 154 | $promise = $this->dispatch(Builder::build($command, $args)); 155 | 156 | return $withScores ? $promise->then(function ($value) { 157 | $len = is_array($value) ? count($value) : 0; 158 | if ($len > 0) { 159 | $ret = []; 160 | for ($i=0; $i<$len; $i+=2) { 161 | $ret[$value[$i]] = $value[$i+1]; 162 | } 163 | 164 | return $ret; 165 | } 166 | 167 | return $value; 168 | } ) : $promise; 169 | } 170 | 171 | /** 172 | * @override 173 | * @inheritDoc 174 | */ 175 | public function zRank($key, $member) 176 | { 177 | $command = Enum::ZRANK; 178 | $args = [$key,$member]; 179 | 180 | return $this->dispatch(Builder::build($command, $args)); 181 | } 182 | 183 | /** 184 | * @override 185 | * @inheritDoc 186 | */ 187 | public function zRem($key, ...$members) 188 | { 189 | $command = Enum::ZREM; 190 | $args = [$key]; 191 | $args = array_merge($args, $members); 192 | 193 | return $this->dispatch(Builder::build($command, $args)); 194 | } 195 | 196 | /** 197 | * @override 198 | * @inheritDoc 199 | */ 200 | public function zRemRangeByLex($key, $min, $max, array $options = []) 201 | { 202 | $command = Enum::ZREMRANGEBYLEX; 203 | $args = [$key, $min, $max]; 204 | $args = array_merge($args, $options); 205 | 206 | return $this->dispatch(Builder::build($command, $args)); 207 | } 208 | 209 | /** 210 | * @override 211 | * @inheritDoc 212 | */ 213 | public function zRemRangeByRank($key, $start, $stop) 214 | { 215 | $command = Enum::ZREMRANGEBYRANK; 216 | $args = [$key, $start,$stop]; 217 | 218 | return $this->dispatch(Builder::build($command, $args)); 219 | } 220 | 221 | /** 222 | * @override 223 | * @inheritDoc 224 | */ 225 | public function zRemRangeByScore($key, $min, $max, array $options = []) 226 | { 227 | $command = Enum::ZREMRANGEBYSCORE; 228 | $args = [$key, $min, $max]; 229 | $args = array_merge($args, $options); 230 | 231 | return $this->dispatch(Builder::build($command, $args)); 232 | } 233 | 234 | /** 235 | * @override 236 | * @inheritDoc 237 | */ 238 | public function zRevRange($key, $start, $stop, $withScores = false) 239 | { 240 | $command = Enum::ZREVRANGE; 241 | $args = [$key, $start, $stop]; 242 | 243 | if ($withScores === true) { 244 | $args[] = 'WITHSCORES'; 245 | 246 | return $this->dispatch(Builder::build($command, $args)) 247 | ->then(function ($value) { 248 | $len = is_array($value) ? count($value) : 0; 249 | if ($len > 0) { 250 | $ret = []; 251 | for ($i=0; $i<$len; $i+=2) { 252 | $member = $value[$i]; 253 | $score = $value[$i+1]; 254 | $ret[$member] = $score; 255 | } 256 | 257 | return $ret; 258 | } 259 | 260 | return $value; 261 | }); 262 | } 263 | 264 | return $promise = $this->dispatch(Builder::build($command, $args)); 265 | } 266 | 267 | /** 268 | * @override 269 | * @inheritDoc 270 | */ 271 | public function zRevRangeByScore($key, $max, $min, $withScores = false, $offset = 0, $count = 0) 272 | { 273 | $command = Enum::ZREVRANGEBYSCORE; 274 | $args = [$key, $max, $min]; 275 | if ($withScores === true) { 276 | $args[] = 'WITHSCORES'; 277 | } 278 | if ($offset != 0 || $count != 0) { 279 | $args[] = 'LIMIT'; 280 | $args[] = $offset; 281 | $args[] = $count; 282 | } 283 | $promise = $this->dispatch(Builder::build($command, $args)); 284 | 285 | return $withScores ? $promise->then(function ($value) { 286 | $len = is_array($value) ? count($value) : 0; 287 | if ($len > 0) { 288 | $ret = []; 289 | for ($i=0; $i<$len; $i+=2) { 290 | $ret[$value[$i]] = $value[$i+1]; 291 | } 292 | 293 | return $ret; 294 | } 295 | 296 | return $value; 297 | } ) : $promise; 298 | } 299 | 300 | /** 301 | * @override 302 | * @inheritDoc 303 | */ 304 | public function zRevRank($key, $member) 305 | { 306 | $command = Enum::ZREVRANK; 307 | $args = [$key,$member]; 308 | 309 | return $this->dispatch(Builder::build($command, $args)); 310 | } 311 | 312 | /** 313 | * @override 314 | * @inheritDoc 315 | */ 316 | public function zScore($key, $member) 317 | { 318 | $command = Enum::ZSCORE; 319 | $args = [$key,$member]; 320 | 321 | return $this->dispatch(Builder::build($command, $args)); 322 | } 323 | 324 | /** 325 | * @override 326 | * @inheritDoc 327 | */ 328 | public function zScan($key, $cursor, array $options = []) 329 | { 330 | $command = Enum::ZSCAN; 331 | $args = [$key , $cursor]; 332 | $args = array_merge($args, $options); 333 | 334 | return $this->dispatch(Builder::build($command, $args)); 335 | } 336 | 337 | /** 338 | * @inheritDoc 339 | */ 340 | public function zUnionScore($dst, $numKeys) 341 | { 342 | $command = Enum::ZUNIIONSCORE; 343 | $args = [$dst, $numKeys]; 344 | 345 | return $this->dispatch(Builder::build($command, $args)); 346 | } 347 | } -------------------------------------------------------------------------------- /src/Redis/Command/Compose/ApiSetTrait.php: -------------------------------------------------------------------------------- 1 | dispatch(Builder::build($command, $args)); 28 | } 29 | 30 | /** 31 | * @override 32 | * @inheritDoc 33 | */ 34 | public function sInter(...$keys) 35 | { 36 | $command = Enum::SINTER; 37 | $args = $keys; 38 | 39 | return $this->dispatch(Builder::build($command, $args)); 40 | } 41 | 42 | /** 43 | * @override 44 | * @inheritDoc 45 | */ 46 | public function sInterStore($dst, ...$keys) 47 | { 48 | $command = Enum::SINTERSTORE; 49 | $args = [$dst]; 50 | $args = array_merge($args, $keys); 51 | 52 | return $this->dispatch(Builder::build($command, $args)); 53 | } 54 | 55 | /** 56 | * @override 57 | * @inheritDoc 58 | */ 59 | public function sIsMember($key, $member) 60 | { 61 | $command = Enum::SISMEMBER; 62 | $args = [$key ,$member]; 63 | 64 | return $this->dispatch(Builder::build($command, $args)); 65 | } 66 | 67 | /** 68 | * @override 69 | * @inheritDoc 70 | */ 71 | public function sMembers($key) 72 | { 73 | $command = Enum::SMEMBERS; 74 | $args = [$key]; 75 | 76 | return $this->dispatch(Builder::build($command, $args)); 77 | } 78 | 79 | /** 80 | * @override 81 | * @inheritDoc 82 | */ 83 | public function sMove($src, $dst, $member) 84 | { 85 | $command = Enum::SMOVE; 86 | $args = [$src, $dst, $member]; 87 | 88 | return $this->dispatch(Builder::build($command, $args)); 89 | } 90 | 91 | /** 92 | * @override 93 | * @inheritDoc 94 | */ 95 | public function sPop($key, $count) 96 | { 97 | $command = Enum::SPOP; 98 | $args = [$key, $count]; 99 | 100 | return $this->dispatch(Builder::build($command, $args)); 101 | } 102 | 103 | /** 104 | * @override 105 | * @inheritDoc 106 | */ 107 | public function sRandMember($key, $count) 108 | { 109 | $command = Enum::SRANDMEMBER; 110 | $args = [$key, $count]; 111 | 112 | return $this->dispatch(Builder::build($command, $args)); 113 | } 114 | 115 | /** 116 | * @override 117 | * @inheritDoc 118 | */ 119 | public function sRem($key, ...$members) 120 | { 121 | $command = Enum::SREM; 122 | $args = [$key]; 123 | $args = array_merge($args, $members); 124 | 125 | return $this->dispatch(Builder::build($command, $args)); 126 | } 127 | 128 | /** 129 | * @override 130 | * @inheritDoc 131 | */ 132 | public function sUnion(...$keys) 133 | { 134 | $command = Enum::SUNION; 135 | $args = $keys; 136 | 137 | return $this->dispatch(Builder::build($command, $args)); 138 | } 139 | 140 | /** 141 | * @override 142 | * @inheritDoc 143 | */ 144 | public function sUnionStore($dst, ...$keys) 145 | { 146 | $command = Enum::SUNIONSTORE; 147 | $args = [$dst]; 148 | $args = array_merge($args, $keys); 149 | 150 | return $this->dispatch(Builder::build($command, $args)); 151 | } 152 | 153 | 154 | /** 155 | * @override 156 | * @inheritDoc 157 | */ 158 | public function sAdd($key, ...$members) 159 | { 160 | $command = Enum::SADD; 161 | $args = [$key]; 162 | $args = array_merge($args, $members); 163 | 164 | return $this->dispatch(Builder::build($command, $args)); 165 | } 166 | 167 | /** 168 | * @override 169 | * @inheritDoc 170 | */ 171 | public function sCard($key) 172 | { 173 | $command = Enum::SCARD; 174 | $args = [$key]; 175 | 176 | return $this->dispatch(Builder::build($command, $args)); 177 | } 178 | 179 | /** 180 | * @override 181 | * @inheritDoc 182 | */ 183 | public function sDiff(...$keys) 184 | { 185 | $command = Enum::SDIFF; 186 | $args = $keys; 187 | 188 | return $this->dispatch(Builder::build($command, $args)); 189 | } 190 | 191 | /** 192 | * @override 193 | * @inheritDoc 194 | */ 195 | public function sDiffStore($dst, ...$keys) 196 | { 197 | $command = Enum::SDIFFSTORE; 198 | $args = [$dst]; 199 | $args = array_merge($args, $keys); 200 | 201 | return $this->dispatch(Builder::build($command, $args)); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/Redis/Command/Compose/ApiTransactionTrait.php: -------------------------------------------------------------------------------- 1 | dispatch(Builder::build($command)); 26 | } 27 | 28 | /** 29 | * @override 30 | * @inheritDoc 31 | */ 32 | public function exec() 33 | { 34 | $command = Enum::EXEC; 35 | 36 | return $this->dispatch(Builder::build($command)); 37 | } 38 | 39 | /** 40 | * @override 41 | * @inheritDoc 42 | */ 43 | public function multi() 44 | { 45 | $command = Enum::MULTI; 46 | 47 | return $this->dispatch(Builder::build($command)); 48 | } 49 | 50 | /** 51 | * @override 52 | * @inheritDoc 53 | */ 54 | public function unWatch() 55 | { 56 | $command = Enum::UNWATCH; 57 | 58 | return $this->dispatch(Builder::build($command)); 59 | } 60 | 61 | /** 62 | * @override 63 | * @inheritDoc 64 | */ 65 | public function watch($key, ...$keys) 66 | { 67 | $command = Enum::WATCH; 68 | $args = [$key]; 69 | $args = array_merge($args, $keys); 70 | 71 | return $this->dispatch(Builder::build($command, $args)); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Redis/Command/Enum.php: -------------------------------------------------------------------------------- 1 | requestParser = new RequestParser(); 33 | $this->responseParser = new ResponseParser(); 34 | $this->serializer = new RecursiveSerializer(); 35 | } 36 | 37 | /** 38 | * @inheritDoc 39 | */ 40 | public function commands(Request $request) 41 | { 42 | return $this->serializer->getRequestMessage( 43 | $request->getCommand(), 44 | $request->getArgs() 45 | ); 46 | } 47 | 48 | /** 49 | * @return RecursiveSerializer 50 | */ 51 | public function getSerializer() 52 | { 53 | return $this->serializer; 54 | } 55 | 56 | /** 57 | * @return RequestParser 58 | */ 59 | public function getRequestParser() 60 | { 61 | return $this->requestParser; 62 | } 63 | 64 | /** 65 | * @return ResponseParser 66 | */ 67 | public function getResponseParser() 68 | { 69 | return $this->responseParser; 70 | } 71 | 72 | /** 73 | * @param $data 74 | * @return ModelInterface[] 75 | */ 76 | public function parseResponse($data) 77 | { 78 | return $this->responseParser->pushIncoming($data); 79 | } 80 | 81 | /** 82 | * @param $data 83 | * @return string 84 | */ 85 | public function buildResponse($data) 86 | { 87 | return $this->serializer->getReplyMessage($data); 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /src/Redis/Driver/DriverInterface.php: -------------------------------------------------------------------------------- 1 | endpoint = $endpoint; 83 | $this->loop = $loop; 84 | $this->stream = null; 85 | $this->driver = new Driver(); 86 | 87 | $this->isConnected = false; 88 | $this->isBeingDisconnected = false; 89 | $this->endPromise = null; 90 | 91 | $this->reqs = []; 92 | } 93 | 94 | /** 95 | * 96 | */ 97 | public function __destruct() 98 | { 99 | $this->stop(); 100 | parent::__destruct(); 101 | } 102 | 103 | /** 104 | * @override 105 | * @inheritDoc 106 | */ 107 | public function isPaused() 108 | { 109 | return $this->stream === null ? false : $this->stream->isPaused(); 110 | } 111 | 112 | /** 113 | * @override 114 | * @inheritDoc 115 | */ 116 | public function pause() 117 | { 118 | if ($this->stream !== null) 119 | { 120 | $this->stream->pause(); 121 | } 122 | } 123 | 124 | /** 125 | * @override 126 | * @inheritDoc 127 | */ 128 | public function resume() 129 | { 130 | if ($this->stream !== null) 131 | { 132 | $this->stream->resume(); 133 | } 134 | } 135 | 136 | /** 137 | * @override 138 | * @inheritDoc 139 | */ 140 | public function isStarted() 141 | { 142 | return $this->isConnected; 143 | } 144 | 145 | /** 146 | * @override 147 | * @inheritDoc 148 | */ 149 | public function isBusy() 150 | { 151 | return !empty($this->reqs); 152 | } 153 | 154 | /** 155 | * @override 156 | * @inheritDoc 157 | */ 158 | public function start() 159 | { 160 | if ($this->isStarted()) 161 | { 162 | return Promise::doResolve($this); 163 | } 164 | 165 | $ex = null; 166 | $stream = null; 167 | 168 | try 169 | { 170 | $stream = $this->createClient($this->endpoint); 171 | } 172 | catch (Error $ex) 173 | {} 174 | catch (Exception $ex) 175 | {} 176 | 177 | if ($ex !== null) 178 | { 179 | return Promise::doReject($ex); 180 | } 181 | 182 | $this->isConnected = true; 183 | $this->isBeingDisconnected = false; 184 | $this->stream = $stream; 185 | $this->handleStart(); 186 | $this->emit('start', [ $this ]); 187 | 188 | return Promise::doResolve($this); 189 | } 190 | 191 | /** 192 | * @override 193 | * @inheritDoc 194 | */ 195 | public function stop() 196 | { 197 | if (!$this->isStarted()) 198 | { 199 | return Promise::doResolve($this); 200 | } 201 | 202 | $this->isBeingDisconnected = true; 203 | $this->isConnected = false; 204 | 205 | $this->stream->close(); 206 | $this->stream = null; 207 | 208 | foreach ($this->reqs as $req) 209 | { 210 | $req->reject(new ExecutionException('Connection has been closed!')); 211 | } 212 | 213 | $this->reqs = []; 214 | $this->handleStop(); 215 | $this->emit('stop', [ $this ]); 216 | 217 | if ($this->endPromise !== null) 218 | { 219 | $promise = $this->endPromise; 220 | $this->endPromise = null; 221 | $promise->resolve($this); 222 | } 223 | 224 | return Promise::doResolve($this); 225 | } 226 | 227 | /** 228 | * @override 229 | * @inheritDoc 230 | */ 231 | public function end() 232 | { 233 | if (!$this->isStarted()) 234 | { 235 | return Promise::doResolve($this); 236 | } 237 | if ($this->isBeingDisconnected) 238 | { 239 | return Promise::doReject(new WriteException('Tried to double end same connection.')); 240 | } 241 | if (!$this->isBusy()) 242 | { 243 | return $this->stop(); 244 | } 245 | 246 | $promise = new Promise(); 247 | $this->isBeingDisconnected = true; 248 | $this->endPromise = $promise; 249 | 250 | return $promise; 251 | } 252 | 253 | /** 254 | * Dispatch Redis request. 255 | * 256 | * @param Request $command 257 | * @return PromiseInterface 258 | */ 259 | protected function dispatch(Request $command) 260 | { 261 | $request = new Deferred(); 262 | $promise = $request->getPromise(); 263 | 264 | if ($this->isBeingDisconnected) 265 | { 266 | $request->reject(new ExecutionException('Redis client connection is being stopped now.')); 267 | } 268 | else 269 | { 270 | $this->stream->write($this->driver->commands($command)); 271 | $this->reqs[] = $request; 272 | } 273 | 274 | return $promise; 275 | } 276 | 277 | /** 278 | * @internal 279 | */ 280 | protected function handleStart() 281 | { 282 | if ($this->stream !== null) 283 | { 284 | $this->stream->on('data', [ $this, 'handleData' ]); 285 | $this->stream->on('close', [ $this, 'stop' ]); 286 | } 287 | } 288 | 289 | /** 290 | * @internal 291 | */ 292 | protected function handleStop() 293 | { 294 | if ($this->stream !== null) 295 | { 296 | $this->stream->removeListener('data', [ $this, 'handleData' ]); 297 | $this->stream->removeListener('close', [ $this, 'stop' ]); 298 | } 299 | } 300 | 301 | /** 302 | * @internal 303 | * @param SocketInterface $stream 304 | * @param string $chunk 305 | */ 306 | public function handleData($stream, $chunk) 307 | { 308 | try 309 | { 310 | $models = $this->driver->parseResponse($chunk); 311 | } 312 | catch (ParserException $error) 313 | { 314 | $this->emit('error', [ $this, $error ]); 315 | $this->stop(); 316 | return; 317 | } 318 | 319 | foreach ($models as $data) 320 | { 321 | try 322 | { 323 | $this->handleMessage($data); 324 | } 325 | catch (UnderflowException $error) 326 | { 327 | $this->emit('error', [ $this, $error ]); 328 | $this->stop(); 329 | return; 330 | } 331 | } 332 | } 333 | 334 | /** 335 | * @internal 336 | * @param ModelInterface $message 337 | */ 338 | protected function handleMessage(ModelInterface $message) 339 | { 340 | if (!$this->reqs) 341 | { 342 | throw new UnderflowException('Unexpected reply received, no matching request found'); 343 | } 344 | 345 | $request = array_shift($this->reqs); 346 | 347 | if ($message instanceof ErrorReply) 348 | { 349 | $request->reject($message); 350 | } 351 | else 352 | { 353 | $request->resolve($message->getValueNative()); 354 | } 355 | 356 | if ($this->isBeingDisconnected && !$this->isBusy()) 357 | { 358 | $this->stop(); 359 | } 360 | } 361 | 362 | /** 363 | * Create socket client with connection to Redis database. 364 | * 365 | * @param string $endpoint 366 | * @return SocketInterface 367 | * @throws ExecutionException 368 | */ 369 | protected function createClient($endpoint) 370 | { 371 | $ex = null; 372 | 373 | try 374 | { 375 | return new Socket($endpoint, $this->loop); 376 | } 377 | catch (Error $ex) 378 | {} 379 | catch (Exception $ex) 380 | {} 381 | 382 | throw new ExecutionException('Redis connection socket could not be created!', 0, $ex); 383 | } 384 | }; 385 | -------------------------------------------------------------------------------- /src/Redis/RedisInterface.php: -------------------------------------------------------------------------------- 1 | loop = null; 46 | $this->sim = null; 47 | } 48 | 49 | /** 50 | * 51 | */ 52 | public function tearDown() 53 | { 54 | unset($this->sim); 55 | unset($this->loop); 56 | } 57 | 58 | /** 59 | * @return LoopInterface|null 60 | */ 61 | public function getLoop() 62 | { 63 | return $this->loop; 64 | } 65 | 66 | /** 67 | * Run test scenario as simulation. 68 | * 69 | * @param callable(Simulation) $scenario 70 | * @return TModule 71 | */ 72 | public function simulate(callable $scenario) 73 | { 74 | try 75 | { 76 | $this->loop = new Loop(new SelectLoop); 77 | $this->loop->erase(true); 78 | 79 | $this->sim = new Simulation($this->loop); 80 | $this->sim->setScenario($scenario); 81 | $this->sim->begin(); 82 | } 83 | catch (Exception $ex) 84 | { 85 | $this->fail($ex->getMessage()); 86 | } 87 | 88 | if ($this->sim->getState() === Simulation::STATE_FAILED) 89 | { 90 | $this->fail($this->sim->getStateMessage()); 91 | } 92 | 93 | if ($this->sim->getState() === Simulation::STATE_SKIPPED) 94 | { 95 | $this->markTestSkipped($this->sim->getStateMessage()); 96 | } 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * @param $events 103 | * @param int $flags 104 | * @return TModule 105 | */ 106 | public function expect($events, $flags = Simulation::EVENTS_COMPARE_IN_ORDER) 107 | { 108 | $expectedEvents = []; 109 | 110 | foreach ($events as $event) 111 | { 112 | $data = isset($event[1]) ? $event[1] : []; 113 | $expectedEvents[] = new Event($event[0], $data); 114 | } 115 | 116 | $this->assertEvents( 117 | $this->sim->getExpectations(), 118 | $expectedEvents, 119 | $flags 120 | ); 121 | 122 | return $this; 123 | } 124 | 125 | /** 126 | * @param Event[] $actualEvents 127 | * @param Event[] $expectedEvents 128 | * @param int $flags 129 | */ 130 | public function assertEvents($actualEvents = [], $expectedEvents = [], $flags = Simulation::EVENTS_COMPARE_IN_ORDER) 131 | { 132 | $count = max(count($actualEvents), count($expectedEvents)); 133 | 134 | if ($flags === Simulation::EVENTS_COMPARE_RANDOMLY) 135 | { 136 | sort($actualEvents); 137 | sort($expectedEvents); 138 | } 139 | 140 | for ($i=0; $i<$count; $i++) 141 | { 142 | if (!isset($actualEvents[$i])) 143 | { 144 | $this->fail( 145 | sprintf(self::MSG_EVENT_GET_ASSERTION_FAILED, $i, $expectedEvents[$i]->name(), 'null') 146 | ); 147 | } 148 | else if (!isset($expectedEvents[$i])) 149 | { 150 | $this->fail( 151 | sprintf(self::MSG_EVENT_GET_ASSERTION_FAILED, $i, 'null', $actualEvents[$i]->name()) 152 | ); 153 | } 154 | 155 | $actualEvent = $actualEvents[$i]; 156 | $expectedEvent = $expectedEvents[$i]; 157 | 158 | $this->assertSame( 159 | $expectedEvent->name(), 160 | $actualEvent->name(), 161 | sprintf(self::MSG_EVENT_NAME_ASSERTION_FAILED, $i) 162 | ); 163 | $this->assertSame( 164 | $expectedEvent->data(), 165 | $actualEvent->data(), 166 | sprintf(self::MSG_EVENT_DATA_ASSERTION_FAILED, $i) 167 | ); 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /test/TModule/Command/RedisApiChannelTest.php: -------------------------------------------------------------------------------- 1 | checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 23 | $params = []; 24 | 25 | return Promise::doResolve()->then(function () use ($redis, $params) {}); 26 | }); 27 | } 28 | 29 | /** 30 | * @group testing 31 | * @dataProvider redisProvider 32 | * @param RedisInterface $redis 33 | */ 34 | public function testRedis_pSubscribe(RedisInterface $redis) 35 | { 36 | //TODO: Implementation 37 | $this->checkRedisVersionedCommand($redis, '2.0.0', function(RedisInterface $redis) { 38 | $params = []; 39 | 40 | return Promise::doResolve()->then(function () use ($redis, $params) {}); 41 | }); 42 | } 43 | 44 | /** 45 | * @group testing 46 | * @dataProvider redisProvider 47 | * @param RedisInterface $redis 48 | */ 49 | public function testRedis_pubSub(RedisInterface $redis) 50 | { 51 | //TODO: Implementation 52 | $this->checkRedisVersionedCommand($redis, '2.8.0', function(RedisInterface $redis) { 53 | $params = []; 54 | 55 | return Promise::doResolve()->then(function () use ($redis, $params) {}); 56 | }); 57 | } 58 | 59 | /** 60 | * @group testing 61 | * @dataProvider redisProvider 62 | * @param RedisInterface $redis 63 | */ 64 | public function testRedis_publish(RedisInterface $redis) 65 | { 66 | //TODO: Implementation 67 | $this->checkRedisVersionedCommand($redis, '2.0.0', function(RedisInterface $redis) { 68 | $params = []; 69 | 70 | return Promise::doResolve()->then(function () use ($redis, $params) {}); 71 | }); 72 | } 73 | 74 | /** 75 | * @group testing 76 | * @dataProvider redisProvider 77 | * @param RedisInterface $redis 78 | */ 79 | public function testRedis_pUnsubscribe(RedisInterface $redis) 80 | { 81 | //TODO: Implementation 82 | $this->checkRedisVersionedCommand($redis, '2.0.0', function(RedisInterface $redis) { 83 | $params = []; 84 | 85 | return Promise::doResolve()->then(function () use ($redis, $params) {}); 86 | }); 87 | } 88 | 89 | /** 90 | * @group testing 91 | * @dataProvider redisProvider 92 | * @param RedisInterface $redis 93 | */ 94 | public function testRedis_unSubscribe(RedisInterface $redis) 95 | { 96 | //TODO: Implementation 97 | $this->checkRedisVersionedCommand($redis, '2.0.0', function(RedisInterface $redis) { 98 | $params = []; 99 | 100 | return Promise::doResolve()->then(function () use ($redis, $params) {}); 101 | }); 102 | } 103 | 104 | /** 105 | * @group testing 106 | * @dataProvider redisProvider 107 | * @param RedisInterface $redis 108 | */ 109 | public function testRedis_subscribe(RedisInterface $redis) 110 | { 111 | //TODO: Implementation 112 | $this->checkRedisVersionedCommand($redis, '2.0.0', function(RedisInterface $redis) { 113 | $params = []; 114 | 115 | return Promise::doResolve()->then(function () use ($redis, $params) {}); 116 | }); 117 | } 118 | } -------------------------------------------------------------------------------- /test/TModule/Command/RedisApiConnTest.php: -------------------------------------------------------------------------------- 1 | checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 23 | $params = []; 24 | 25 | return Promise::doResolve()->then(function () use ($redis, $params) {}); 26 | }); 27 | } 28 | 29 | /** 30 | * @group passed 31 | * @dataProvider redisProvider 32 | * @param RedisInterface $redis 33 | */ 34 | public function testRedis_ping(RedisInterface $redis) 35 | { 36 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 37 | $params = [ 38 | 'MSG' => 'MESSAGE' 39 | ]; 40 | 41 | return Promise::doResolve()->then(function () use ($redis, $params) { 42 | return $redis->ping($params['MSG']); 43 | }) 44 | ->then(function ($value) use ($params) { 45 | $this->assertSame($value, $params['MSG']); 46 | }); 47 | }); 48 | } 49 | 50 | /** 51 | * @group passed 52 | * @dataProvider redisProvider 53 | * @param RedisInterface $redis 54 | */ 55 | public function testRedis_quit(RedisInterface $redis) 56 | { 57 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 58 | $params = []; 59 | 60 | return Promise::doResolve()->then(function () use ($redis, $params) { 61 | return $redis->quit(); 62 | }) 63 | ->then(function ($value) { 64 | $this->assertSame($value, 'OK'); 65 | }); 66 | }); 67 | } 68 | 69 | /** 70 | * @group passed 71 | * @dataProvider redisProvider 72 | * @param RedisInterface $redis 73 | */ 74 | public function testRedis_select(RedisInterface $redis) 75 | { 76 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 77 | $params = [ 78 | 'INDEX' => 0, 79 | ]; 80 | 81 | return Promise::doResolve()->then(function () use ($redis, $params) { 82 | return $redis->select($params['INDEX']); 83 | }) 84 | ->then(function ($value) { 85 | $this->assertSame($value, 'OK'); 86 | }); 87 | }); 88 | } 89 | 90 | /** 91 | * @group ignored 92 | * @dataProvider redisProvider 93 | * @param RedisInterface $redis 94 | */ 95 | public function testRedis_swapBb(RedisInterface $redis) 96 | { 97 | $this->checkRedisVersionedCommand($redis, '4.0.0', function(RedisInterface $redis) { 98 | return Promise::doResolve()->then(function () use ($redis) { 99 | return $redis->swapBb(0, 1); 100 | }) 101 | ->then(function ($value) { 102 | $this->assertSame('OK', $value); 103 | }); 104 | }); 105 | } 106 | } -------------------------------------------------------------------------------- /test/TModule/Command/RedisApiCoreTest.php: -------------------------------------------------------------------------------- 1 | checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 23 | $params = []; 24 | 25 | return Promise::doResolve()->then(function () use ($redis, $params) { 26 | 27 | }); 28 | }); 29 | } 30 | 31 | /** 32 | * @group passed 33 | * @dataProvider redisProvider 34 | * @param RedisInterface $redis 35 | */ 36 | public function testRedis_bgRewriteAoF(RedisInterface $redis) 37 | { 38 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 39 | $params = []; 40 | 41 | return Promise::doResolve()->then(function () use ($redis, $params) { 42 | return $redis->bgRewriteAoF(); 43 | }) 44 | ->then(function ($value) { 45 | $this->assertSame('Background append only file rewriting started', $value); 46 | }); 47 | }); 48 | } 49 | 50 | /** 51 | * @group passed 52 | * @dataProvider redisProvider 53 | * @param RedisInterface $redis 54 | */ 55 | public function testRedis_bgSave(RedisInterface $redis) 56 | { 57 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 58 | $params = []; 59 | 60 | return Promise::doResolve()->then(function () use ($redis, $params) { 61 | return $redis->info(['persistence']); 62 | }) 63 | ->then(function ($value) use ($redis) { 64 | if ($value['persistence']['aof_rewrite_in_progress'] <= 0) { 65 | return $redis->bgSave(); 66 | } 67 | 68 | return 'An AOF log rewriting in progress'; 69 | }) 70 | ->then(function ($value) { 71 | $this->assertSame('An AOF log rewriting in progress', $value); 72 | }); 73 | }); 74 | } 75 | 76 | /** 77 | * @group ignored 78 | * @dataProvider redisProvider 79 | * @param RedisInterface $redis 80 | */ 81 | public function testRedis_sync(RedisInterface $redis) 82 | { 83 | //TODO: Implementation 84 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 85 | $params = []; 86 | 87 | return Promise::doResolve()->then(function () use ($redis, $params) { 88 | // return $redis->sync(); 89 | }); 90 | }); 91 | } 92 | 93 | /** 94 | * @group passed 95 | * @dataProvider redisProvider 96 | * @param RedisInterface $redis 97 | */ 98 | public function testRedis_time(RedisInterface $redis) 99 | { 100 | $this->checkRedisVersionedCommand($redis, '2.6.0', function(RedisInterface $redis) { 101 | $params = []; 102 | 103 | return Promise::doResolve()->then(function () use ($redis, $params) { 104 | return $redis->time(); 105 | }) 106 | ->then(function ($value) { 107 | $this->assertNotEmpty($value); 108 | }); 109 | }); 110 | } 111 | 112 | /** 113 | * @group passed 114 | * @dataProvider redisProvider 115 | * @param RedisInterface $redis 116 | */ 117 | public function testRedis_monitor(RedisInterface $redis) 118 | { 119 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 120 | $params = []; 121 | 122 | return Promise::doResolve()->then(function () use ($redis, $params) { 123 | return $redis->monitor(); 124 | }) 125 | ->then(function ($value) { 126 | $this->assertSame('OK', $value); 127 | }); 128 | }); 129 | } 130 | 131 | 132 | /** 133 | * @group passed 134 | * @dataProvider redisProvider 135 | * @param RedisInterface $redis 136 | */ 137 | public function testRedis_flushAll(RedisInterface $redis) 138 | { 139 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 140 | $params = []; 141 | 142 | return Promise::doResolve()->then(function () use ($redis, $params) { 143 | return $redis->flushAll(); 144 | }) 145 | ->then(function ($value) { 146 | $this->assertSame('OK', $value); 147 | }); 148 | }); 149 | } 150 | 151 | /** 152 | * @group passed 153 | * @dataProvider redisProvider 154 | * @param RedisInterface $redis 155 | */ 156 | public function testRedis_flushDb(RedisInterface $redis) 157 | { 158 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 159 | $params = []; 160 | 161 | return Promise::doResolve()->then(function () use ($redis, $params) { 162 | return $redis->flushDb(); 163 | }) 164 | ->then(function ($value) { 165 | $this->assertSame('OK', $value); 166 | }); 167 | }); 168 | } 169 | 170 | /** 171 | * @group passed 172 | * @dataProvider redisProvider 173 | * @param RedisInterface $redis 174 | */ 175 | public function testRedis_info(RedisInterface $redis) 176 | { 177 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 178 | $params = []; 179 | 180 | return Promise::doResolve()->then(function () use ($redis, $params) { 181 | return $redis->info(); 182 | }) 183 | ->then(function ($value) { 184 | $this->assertArrayHasKey('cpu', $value); 185 | $this->assertArrayHasKey('persistence', $value); 186 | $this->assertArrayHasKey('memory', $value); 187 | $this->assertArrayHasKey('clients', $value); 188 | $this->assertArrayHasKey('server', $value); 189 | }); 190 | }); 191 | } 192 | 193 | /** 194 | * @group passed 195 | * @dataProvider redisProvider 196 | * @param RedisInterface $redis 197 | */ 198 | public function testRedis_slaveOf(RedisInterface $redis) 199 | { 200 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 201 | $params = []; 202 | 203 | return Promise::doResolve()->then(function () use ($redis, $params) { 204 | return $redis->slaveOf('127.0.0.1',6379); 205 | }) 206 | ->then(function ($value) use ($redis) { 207 | $redis->slaveOf('no', 'one'); 208 | $this->assertSame('OK', $value); 209 | }); 210 | }); 211 | } 212 | 213 | /** 214 | * @group ignored 215 | * @dataProvider redisProvider 216 | * @param RedisInterface $redis 217 | */ 218 | public function testRedis_slowLog(RedisInterface $redis) 219 | { 220 | //TODO: Implementation 221 | $this->checkRedisVersionedCommand($redis, '2.2.12', function(RedisInterface $redis) { 222 | $params = []; 223 | 224 | return Promise::doResolve()->then(function () use ($redis, $params) { 225 | // $redis->slowLog(); 226 | }); 227 | }); 228 | } 229 | 230 | /** 231 | * @group passed 232 | * @dataProvider redisProvider 233 | * @param RedisInterface $redis 234 | */ 235 | public function testRedis_save(RedisInterface $redis) 236 | { 237 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 238 | $params = []; 239 | 240 | return Promise::doResolve()->then(function () use ($redis, $params) { 241 | return $redis->save(); 242 | }) 243 | ->then(function ($value) { 244 | $this->assertSame('OK', $value); 245 | }); 246 | }); 247 | } 248 | } -------------------------------------------------------------------------------- /test/TModule/Command/RedisApiHyperLogTest.php: -------------------------------------------------------------------------------- 1 | checkRedisVersionedCommand($redis, '2.8.9', function (RedisInterface $redis) { 22 | $params = [ 23 | 'KEY' => 'T_KEY', 24 | 'E_1' => 'T_ELEMENT_1', 25 | 'E_2' => 'T_ELEMENT_2', 26 | 'E_3' => 'T_ELEMENT_3', 27 | 'E_4' => 'T_ELEMENT_4', 28 | ]; 29 | 30 | return Promise::doResolve()->then(function () use ($redis, $params) { 31 | return $redis->pFAdd($params['KEY'], $params['E_1'], $params['E_2'], $params['E_3'], $params['E_4']); 32 | }) 33 | ->then(function ($value) { 34 | $this->assertSame(1, $value); 35 | }); 36 | }); 37 | } 38 | 39 | /** 40 | * @group passed 41 | * @dataProvider redisProvider 42 | * @param RedisInterface $redis 43 | */ 44 | public function testRedis_pFCount(RedisInterface $redis) 45 | { 46 | $this->checkRedisVersionedCommand($redis, '2.8.9', function (RedisInterface $redis) { 47 | $params = [ 48 | 'KEY' => 'T_KEY', 49 | 'E_1' => 'T_ELEMENT_1', 50 | 'E_2' => 'T_ELEMENT_2', 51 | 'E_3' => 'T_ELEMENT_3', 52 | 'E_4' => 'T_ELEMENT_4', 53 | ]; 54 | 55 | return Promise::doResolve()->then(function () use ($redis, $params) { 56 | $redis->pFAdd($params['KEY'], $params['E_1'], $params['E_2'], $params['E_3'], $params['E_4']); 57 | 58 | return $redis->pFCount($params['KEY']); 59 | }) 60 | ->then(function ($value) { 61 | $this->assertSame(4, $value); 62 | }); 63 | }); 64 | } 65 | 66 | /** 67 | * @group passed 68 | * @dataProvider redisProvider 69 | * @param RedisInterface $redis 70 | */ 71 | public function testRedis_pFMerge(RedisInterface $redis) 72 | { 73 | $this->checkRedisVersionedCommand($redis, '2.8.9', function (RedisInterface $redis) { 74 | $params = [ 75 | 'KEY_1' => 'T_KEY_1', 76 | 'E_1' => 'T_ELEMENT_1', 77 | 'E_2' => 'T_ELEMENT_2', 78 | 'E_3' => 'T_ELEMENT_3', 79 | 'E_4' => 'T_ELEMENT_4', 80 | 'KEY_2' => 'T_KEY_2', 81 | 'E_5' => 'T_ELEMENT_5', 82 | 'E_6' => 'T_ELEMENT_6', 83 | 'E_7' => 'T_ELEMENT_7', 84 | 'E_8' => 'T_ELEMENT_8', 85 | ]; 86 | 87 | return Promise::doResolve()->then(function () use ($redis, $params) { 88 | $redis->pFAdd($params['KEY_1'], $params['E_1'], $params['E_2'], $params['E_3'], $params['E_4']); 89 | $redis->pFAdd($params['KEY_2'], $params['E_5'], $params['E_6'], $params['E_7'], $params['E_8']); 90 | $redis->pFMerge($params['KEY_1'], $params['KEY_2']); 91 | 92 | return $redis->pFCount($params['KEY_1']); 93 | }) 94 | ->then(function ($value) { 95 | $this->assertSame(8, $value); 96 | }); 97 | }); 98 | } 99 | } -------------------------------------------------------------------------------- /test/TModule/Command/RedisApiSetTest.php: -------------------------------------------------------------------------------- 1 | checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 22 | $params = [ 23 | 'SET' => 'T_SET', 24 | 'E_1' => 'Hello', 25 | 'E_2' => 'World', 26 | ]; 27 | 28 | return Promise::doResolve()->then(function () use ($redis, $params) { 29 | return $redis->sAdd($params['SET'], $params['E_1'], $params['E_2']) 30 | ->then(function ($value) { 31 | $this->assertSame(2, $value); 32 | }); 33 | }); 34 | }); 35 | } 36 | 37 | /** 38 | * @group passed 39 | * @dataProvider redisProvider 40 | * @param RedisInterface $redis 41 | */ 42 | public function testRedis_sInterStore(RedisInterface $redis) 43 | { 44 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 45 | $params = [ 46 | 'SET_1' => 'T_SET_1', 47 | 'S1_E1' => 'T_S1_E1', 48 | 'SET_2' => 'T_SET_2', 49 | 'S2_E1' => 'T_S2_E1', 50 | 'SET_3' => 'T_SET_3', 51 | 'INTER' => 'E_INTER', 52 | ]; 53 | 54 | return Promise::doResolve()->then(function () use ($redis, $params) { 55 | $redis->sAdd($params['SET_1'], $params['S1_E1'], $params['INTER']); 56 | $redis->sAdd($params['SET_2'], $params['S2_E1'], $params['INTER']); 57 | }) 58 | ->then(function () use ($redis, $params) { 59 | return $redis->sInterStore($params['SET_3'], $params['SET_1'], $params['SET_2']); 60 | }) 61 | ->then(function ($value) { 62 | $this->assertSame(1, $value); 63 | }); 64 | }); 65 | } 66 | 67 | /** 68 | * @group passed 69 | * @dataProvider redisProvider 70 | * @param RedisInterface $redis 71 | */ 72 | public function testRedis_sIsMember(RedisInterface $redis) 73 | { 74 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 75 | $params = [ 76 | 'SET' => 'T_SET', 77 | 'E_1' => 'T_E_1', 78 | ]; 79 | 80 | return Promise::doResolve()->then(function () use ($redis, $params) { 81 | return $redis->sIsMember($params['SET'], $params['E_1']); 82 | }) 83 | ->then(function ($value) use ($redis, $params) { 84 | $this->assertSame(0, $value); 85 | $redis->sAdd($params['SET'], $params['E_1']); 86 | 87 | return $redis->sIsMember($params['SET'], $params['E_1']); 88 | }) 89 | ->then(function ($value) { 90 | $this->assertSame(1, $value); 91 | }); 92 | }); 93 | } 94 | 95 | /** 96 | * @group passed 97 | * @dataProvider redisProvider 98 | * @param RedisInterface $redis 99 | */ 100 | public function testRedis_sMembers(RedisInterface $redis) 101 | { 102 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 103 | $params = [ 104 | 'SET' => 'T_SET', 105 | 'E_1' => 'T_E_1', 106 | ]; 107 | 108 | return Promise::doResolve()->then(function () use ($redis, $params) { 109 | return $redis->sAdd($params['SET'], $params['E_1']); 110 | }) 111 | ->then(function () use ($redis, $params) { 112 | return $redis->sMembers($params['SET']); 113 | }) 114 | ->then(function ($value) use ($params) { 115 | $this->assertSame([$params['E_1']], $value); 116 | }); 117 | }); 118 | } 119 | 120 | /** 121 | * @group passed 122 | * @dataProvider redisProvider 123 | * @param RedisInterface $redis 124 | */ 125 | public function testRedis_sMove(RedisInterface $redis) 126 | { 127 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 128 | $params = [ 129 | 'SET_1' => 'T_SET_1', 130 | 'S1_E1' => 'T_S1_E1', 131 | 'SET_2' => 'T_SET_2', 132 | 'S2_E1' => 'T_S2_E1', 133 | ]; 134 | 135 | return Promise::doResolve()->then(function () use ($redis, $params) { 136 | $redis->sAdd($params['SET_1'], $params['S1_E1']); 137 | $redis->sAdd($params['SET_2'], $params['S2_E1']); 138 | }) 139 | ->then(function () use ($redis, $params) { 140 | return $redis->sMove($params['SET_1'], $params['SET_2'], $params['S1_E1']); 141 | }) 142 | ->then(function ($value) { 143 | $this->assertSame(1, $value); 144 | }); 145 | }); 146 | } 147 | 148 | /** 149 | * @group passed 150 | * @dataProvider redisProvider 151 | * @param RedisInterface $redis 152 | */ 153 | public function testRedis_sPop(RedisInterface $redis) 154 | { 155 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 156 | $params = [ 157 | 'SET' => 'T_SET', 158 | 'E_1' => 'T_E_1', 159 | ]; 160 | 161 | return Promise::doResolve()->then(function () use ($redis, $params) { 162 | return $redis->sAdd($params['SET'], $params['E_1']); 163 | }) 164 | ->then(function () use ($redis, $params) { 165 | return $redis->sPop($params['SET'], 1); 166 | }) 167 | ->then(function ($value) use ($params) { 168 | $this->assertSame([$params['E_1']], $value); 169 | }); 170 | }); 171 | } 172 | 173 | /** 174 | * @group passed 175 | * @dataProvider redisProvider 176 | * @param RedisInterface $redis 177 | */ 178 | public function testRedis_sRandMember(RedisInterface $redis) 179 | { 180 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 181 | $params = [ 182 | 'SET' => 'T_SET', 183 | 'E_1' => 'T_E_1', 184 | ]; 185 | 186 | return Promise::doResolve()->then(function () use ($redis, $params) { 187 | return $redis->sAdd($params['SET'], $params['E_1']); 188 | }) 189 | ->then(function () use ($redis, $params) { 190 | return $redis->sRandMember($params['SET'], 1); 191 | }) 192 | ->then(function ($value) use ($params) { 193 | $this->assertSame([$params['E_1']], $value); 194 | }); 195 | }); 196 | } 197 | 198 | /** 199 | * @group passed 200 | * @dataProvider redisProvider 201 | * @param RedisInterface $redis 202 | */ 203 | public function testRedis_sRem(RedisInterface $redis) 204 | { 205 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 206 | $params = [ 207 | 'SET' => 'T_SET', 208 | 'E_1' => 'T_E_1', 209 | ]; 210 | 211 | return Promise::doResolve()->then(function () use ($redis, $params) { 212 | return $redis->sAdd($params['SET'], $params['E_1']); 213 | }) 214 | ->then(function () use ($redis, $params) { 215 | return $redis->sRem($params['SET'], $params['E_1']); 216 | }) 217 | ->then(function ($value) use ($params) { 218 | $this->assertSame(1, $value); 219 | }); 220 | }); 221 | } 222 | 223 | /** 224 | * @group ignored 225 | * @dataProvider redisProvider 226 | * @param RedisInterface $redis 227 | */ 228 | public function testRedis_sScan(RedisInterface $redis) 229 | { 230 | $this->checkRedisVersionedCommand($redis, '2.8.0', function(RedisInterface $redis) { 231 | return Promise::doResolve()->then(function () use ($redis) { 232 | //TODO: implementation 233 | }); 234 | }); 235 | } 236 | 237 | /** 238 | * @group passed 239 | * @dataProvider redisProvider 240 | * @param RedisInterface $redis 241 | */ 242 | public function testRedis_sUnion(RedisInterface $redis) 243 | { 244 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 245 | $params = [ 246 | 'SET_1' => 'T_SET_1', 247 | 'S1_E1' => '1', 248 | 'SET_2' => 'T_SET_2', 249 | 'S2_E1' => '2', 250 | ]; 251 | return Promise::doResolve()->then(function () use ($redis, $params) { 252 | $redis->sAdd($params['SET_1'], $params['S1_E1']); 253 | $redis->sAdd($params['SET_2'], $params['S2_E1']); 254 | }) 255 | ->then(function () use ($redis, $params) { 256 | return $redis->sUnion($params['SET_1'], $params['SET_2']); 257 | }) 258 | ->then(function ($value) use ($params) { 259 | $this->assertSame([ 260 | $params['S1_E1'], 261 | $params['S2_E1'], 262 | ], $value); 263 | }); 264 | }); 265 | } 266 | 267 | /** 268 | * @group passed 269 | * @dataProvider redisProvider 270 | * @param RedisInterface $redis 271 | */ 272 | public function testRedis_sUnionStore(RedisInterface $redis) 273 | { 274 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 275 | $params = [ 276 | 'SET_1' => 'T_SET_1', 277 | 'S1_E1' => 'T_S1_E1', 278 | 'SET_2' => 'T_SET_2', 279 | 'S2_E1' => 'T_S2_E1', 280 | 'SET_3' => 'T_SET_3', 281 | ]; 282 | return Promise::doResolve()->then(function () use ($redis, $params) { 283 | $redis->sAdd($params['SET_1'], $params['S1_E1']); 284 | $redis->sAdd($params['SET_2'], $params['S2_E1']); 285 | }) 286 | ->then(function () use ($redis, $params) { 287 | return $redis->sUnionStore($params['SET_3'], $params['SET_1'], $params['SET_2']); 288 | }) 289 | ->then(function ($value) { 290 | $this->assertSame(2, $value); 291 | }); 292 | }); 293 | } 294 | 295 | /** 296 | * @group passed 297 | * @dataProvider redisProvider 298 | * @param RedisInterface $redis 299 | */ 300 | public function testRedis_sAdd(RedisInterface $redis) 301 | { 302 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 303 | $params = [ 304 | 'SET' => 'T_SET', 305 | 'E_1' => 'T_E_1', 306 | ]; 307 | return Promise::doResolve()->then(function () use ($redis, $params) { 308 | return $redis->sAdd($params['SET'], $params['E_1']); 309 | }) 310 | ->then(function ($value) use ($params) { 311 | $this->assertSame(1, $value); 312 | }); 313 | }); 314 | } 315 | 316 | /** 317 | * @group passed 318 | * @dataProvider redisProvider 319 | * @param RedisInterface $redis 320 | */ 321 | public function testRedis_sCard(RedisInterface $redis) 322 | { 323 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 324 | $params = [ 325 | 'SET' => 'T_SET', 326 | 'E_1' => 'T_E_1', 327 | ]; 328 | return Promise::doResolve()->then(function () use ($redis, $params) { 329 | $redis->sAdd($params['SET'], $params['E_1']); 330 | 331 | return $redis->sCard($params['SET']); 332 | }) 333 | ->then(function ($value) use ($params) { 334 | $this->assertSame(1, $value); 335 | }); 336 | }); 337 | } 338 | 339 | /** 340 | * @group passed 341 | * @dataProvider redisProvider 342 | * @param RedisInterface $redis 343 | */ 344 | public function testRedis_sDiff(RedisInterface $redis) 345 | { 346 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 347 | $params = [ 348 | 'SET_1' => 'T_SET_1', 349 | 'S1_E1' => 'T_S1_E1', 350 | 'SET_2' => 'T_SET_2', 351 | 'S2_E1' => 'T_S2_E1', 352 | ]; 353 | return Promise::doResolve()->then(function () use ($redis, $params) { 354 | $redis->sAdd($params['SET_1'], $params['S1_E1']); 355 | $redis->sAdd($params['SET_2'], $params['S2_E1']); 356 | }) 357 | ->then(function () use ($redis, $params) { 358 | return $redis->sDiff($params['SET_1'], $params['SET_2']); 359 | }) 360 | ->then(function ($value) use ($params) { 361 | $this->assertSame([ 362 | $params['S1_E1'], 363 | ], $value); 364 | }); 365 | }); 366 | } 367 | 368 | /** 369 | * @group passed 370 | * @dataProvider redisProvider 371 | * @param RedisInterface $redis 372 | */ 373 | public function testRedis_sDiffStore(RedisInterface $redis) 374 | { 375 | $this->checkRedisVersionedCommand($redis, '1.0.0', function(RedisInterface $redis) { 376 | $params = [ 377 | 'SET_1' => 'T_SET_1', 378 | 'S1_E1' => 'T_S1_E1', 379 | 'SET_2' => 'T_SET_2', 380 | 'S2_E1' => 'T_S2_E1', 381 | 'SET_3' => 'T_SET_3', 382 | ]; 383 | 384 | return Promise::doResolve()->then(function () use ($redis, $params) { 385 | $redis->sAdd($params['SET_1'], $params['S1_E1']); 386 | $redis->sAdd($params['SET_2'], $params['S2_E1']); 387 | }) 388 | ->then(function () use ($redis, $params) { 389 | return $redis->sDiffStore($params['SET_3'], $params['SET_1'], $params['SET_2']); 390 | }) 391 | ->then(function ($value) use ($params) { 392 | $this->assertSame(1, $value); 393 | }); 394 | }); 395 | } 396 | 397 | } -------------------------------------------------------------------------------- /test/TModule/Command/RedisApiTransactionTest.php: -------------------------------------------------------------------------------- 1 | checkRedisVersionedCommand($redis, '2.0.0', function(RedisInterface $redis) { 22 | $params = [ 23 | 'U_KEY' => 'UNKNOWN_KEY' 24 | ]; 25 | return Promise::doResolve()->then(function () use ($redis, $params) { 26 | $redis->multi(); 27 | $redis->set($params['U_KEY'], 'OK'); 28 | $redis->get($params['U_KEY']); 29 | 30 | return $redis->discard(); 31 | }) 32 | ->then(function ($value) { 33 | $this->assertSame('OK', $value); 34 | }); 35 | }); 36 | } 37 | 38 | /** 39 | * @group passed 40 | * @dataProvider redisProvider 41 | * @param RedisInterface $redis 42 | */ 43 | public function testRedis_exec(RedisInterface $redis) 44 | { 45 | $this->checkRedisVersionedCommand($redis, '1.2.0', function(RedisInterface $redis) { 46 | $params = [ 47 | 'KEY' => 'T_KEY', 48 | 'VAL' => 'T_VAL', 49 | ]; 50 | return Promise::doResolve()->then(function () use ($redis, $params) { 51 | $redis->multi(); 52 | $redis->set($params['KEY'], $params['VAL']); 53 | $redis->get($params['KEY']); 54 | 55 | return $redis->exec(); 56 | }) 57 | ->then(function ($value) use ($params) { 58 | $this->assertSame([ 59 | 'OK', 60 | $params['VAL'], 61 | ], $value); 62 | }); 63 | }); 64 | } 65 | 66 | /** 67 | * @group passed 68 | * @dataProvider redisProvider 69 | * @param RedisInterface $redis 70 | */ 71 | public function testRedis_multi(RedisInterface $redis) 72 | { 73 | $this->checkRedisVersionedCommand($redis, '1.2.0', function(RedisInterface $redis) { 74 | $params = [ 75 | 76 | ]; 77 | return Promise::doResolve()->then(function () use ($redis, $params) { 78 | return $redis->multi(); 79 | }) 80 | ->then(function ($value) use ($redis) { 81 | $this->assertSame('OK', $value); 82 | $redis->discard(); 83 | }); 84 | }); 85 | } 86 | 87 | /** 88 | * @group passed 89 | * @dataProvider redisProvider 90 | * @param RedisInterface $redis 91 | */ 92 | public function testRedis_unWatch(RedisInterface $redis) 93 | { 94 | $this->checkRedisVersionedCommand($redis, '2.2.0', function(RedisInterface $redis) { 95 | $params = [ 96 | 'KEY_1' => 'T_KEY_1', 97 | 'KEY_2' => 'T_KEY_2', 98 | ]; 99 | return Promise::doResolve()->then(function () use ($redis, $params) { 100 | $redis->watch($params['KEY_1'], $params['KEY_2']); 101 | 102 | return $redis->unWatch(); 103 | }) 104 | ->then(function ($value) { 105 | $this->assertSame('OK', $value); 106 | }); 107 | }); 108 | } 109 | 110 | /** 111 | * @group passed 112 | * @dataProvider redisProvider 113 | * @param RedisInterface $redis 114 | */ 115 | public function testRedis_watch(RedisInterface $redis) 116 | { 117 | $this->checkRedisVersionedCommand($redis, '2.2.0', function(RedisInterface $redis) { 118 | $params = [ 119 | 'KEY_1' => 'T_KEY_1', 120 | 'KEY_2' => 'T_KEY_2', 121 | ]; 122 | return Promise::doResolve()->then(function () use ($redis, $params) { 123 | return $redis->watch($params['KEY_1'], $params['KEY_2']); 124 | }) 125 | ->then(function ($value) { 126 | $this->assertSame('OK', $value); 127 | }); 128 | }); 129 | } 130 | } -------------------------------------------------------------------------------- /test/TModule/_Support/RedisTrait.php: -------------------------------------------------------------------------------- 1 | checkRedisVersionedCommand($redis, '1.0.0', $scenario); 33 | } 34 | 35 | /** 36 | * @param RedisInterface $redis 37 | * @param string $minVersion 38 | * @param callable $scenario 39 | */ 40 | public function checkRedisVersionedCommand(RedisInterface $redis, $minVersion, callable $scenario) 41 | { 42 | $this->simulate(function(SimulationInterface $sim) use($redis, $minVersion, $scenario) { 43 | $loop = $sim->getLoop(); 44 | $redis->setLoop($loop); 45 | 46 | $redis->on('start', function(RedisInterface $redis) use($minVersion, $scenario, $sim) { 47 | return Promise::doResolve() 48 | ->then(function() use($redis) { 49 | return $redis->flushDb(); 50 | }) 51 | ->then(function() use($redis) { 52 | return $redis->info(); 53 | }) 54 | ->then(function($info) use($redis, $minVersion, $scenario, $sim) { 55 | if (!$info || !isset($info['server']) || !isset($info['server']['redis_version'])) { 56 | throw new Exception('Test skipped due to insufficient Redis version!'); 57 | } 58 | $current = $info['server']['redis_version']; 59 | $minimal = $minVersion; 60 | $cv = explode('.', $current); 61 | $mv = explode('.', $minimal); 62 | if (($mv[0] > $cv[0]) || ($mv[0] === $cv[0] && $mv[1] > $cv[1]) || ($mv[0] === $cv[0] && $mv[1] === $cv[1] && $mv[2] > $cv[2])) { 63 | throw new Exception( 64 | sprintf('Test skipped due to insufficient Redis version! Expected v%s, got v%s', $minimal, $current) 65 | ); 66 | } 67 | return Promise::doResolve($scenario($redis)) 68 | ->then(function() use($sim) { 69 | $sim->done(); 70 | }) 71 | ->failure(function($ex) use($sim) { 72 | $sim->fail((string)$ex); 73 | }); 74 | }) 75 | ->failure(function($ex) use($sim) { 76 | $sim->skip($ex->getMessage()); 77 | }); 78 | }); 79 | 80 | $sim->onStart(function() use($redis) { 81 | $redis->start(); 82 | }); 83 | $sim->onStop(function() use($redis) { 84 | $redis->stop(); 85 | }); 86 | }); 87 | } 88 | 89 | /** 90 | * @return mixed[][] 91 | */ 92 | public function redisProvider() 93 | { 94 | return [ 95 | [ $this->createRedis() ] 96 | ]; 97 | } 98 | 99 | /** 100 | * @param LoopInterface $loop 101 | * @return RedisInterface 102 | */ 103 | public function createRedis(LoopInterface $loop = null) 104 | { 105 | $loop = $loop === null ? new Loop(new SelectLoop()) : $loop; 106 | return new Redis(TEST_DB_REDIS_ENDPOINT, $loop); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /test/TUnit.php: -------------------------------------------------------------------------------- 1 | exactly(2); 34 | } 35 | 36 | /** 37 | * Creates a callback that must be called $amount times or the test will fail. 38 | * 39 | * @param $amount 40 | * @return callable|\PHPUnit_Framework_MockObject_MockObject 41 | */ 42 | public function expectCallableExactly($amount) 43 | { 44 | $mock = $this->createCallableMock(); 45 | $mock 46 | ->expects($this->exactly($amount)) 47 | ->method('__invoke'); 48 | 49 | return $mock; 50 | } 51 | 52 | /** 53 | * Creates a callback that must be called once. 54 | * 55 | * @return callable|\PHPUnit_Framework_MockObject_MockObject 56 | */ 57 | public function expectCallableOnce() 58 | { 59 | $mock = $this->createCallableMock(); 60 | $mock 61 | ->expects($this->once()) 62 | ->method('__invoke'); 63 | 64 | return $mock; 65 | } 66 | 67 | /** 68 | * Creates a callback that must be called twice. 69 | * 70 | * @return callable|\PHPUnit_Framework_MockObject_MockObject 71 | */ 72 | public function expectCallableTwice() 73 | { 74 | $mock = $this->createCallableMock(); 75 | $mock 76 | ->expects($this->exactly(2)) 77 | ->method('__invoke'); 78 | 79 | return $mock; 80 | } 81 | 82 | /** 83 | * Creates a callable that must not be called once. 84 | * 85 | * @return callable|\PHPUnit_Framework_MockObject_MockObject 86 | */ 87 | public function expectCallableNever() 88 | { 89 | $mock = $this->createCallableMock(); 90 | $mock 91 | ->expects($this->never()) 92 | ->method('__invoke'); 93 | 94 | return $mock; 95 | } 96 | 97 | /** 98 | * Creates a callable mock. 99 | * 100 | * @return callable|\PHPUnit_Framework_MockObject_MockObject 101 | */ 102 | public function createCallableMock() 103 | { 104 | return $this->getMock(Callback::class); 105 | } 106 | 107 | /** 108 | * @return LoopInterface|\PHPUnit_Framework_MockObject_MockObject 109 | */ 110 | public function createLoopMock() 111 | { 112 | return $this->getMock('Dazzle\Loop\LoopInterface'); 113 | } 114 | 115 | /** 116 | * @return LoopInterface|\PHPUnit_Framework_MockObject_MockObject 117 | */ 118 | public function createWritableLoopMock() 119 | { 120 | $loop = $this->createLoopMock(); 121 | $loop 122 | ->expects($this->once()) 123 | ->method('addWriteStream') 124 | ->will($this->returnCallback(function($stream, $listener) { 125 | call_user_func($listener, $stream); 126 | })); 127 | 128 | return $loop; 129 | } 130 | 131 | /** 132 | * @return LoopInterface|\PHPUnit_Framework_MockObject_MockObject 133 | */ 134 | public function createReadableLoopMock() 135 | { 136 | $loop = $this->createLoopMock(); 137 | $loop 138 | ->expects($this->once()) 139 | ->method('addReadStream') 140 | ->will($this->returnCallback(function($stream, $listener) { 141 | call_user_func($listener, $stream); 142 | })); 143 | 144 | return $loop; 145 | } 146 | 147 | /** 148 | * Check if protected property exists. 149 | * 150 | * @param object $object 151 | * @param string $property 152 | * @return bool 153 | */ 154 | public function existsProtectedProperty($object, $property) 155 | { 156 | $reflection = new ReflectionClass($object); 157 | return $reflection->hasProperty($property); 158 | } 159 | 160 | /** 161 | * Get protected property from given object via reflection. 162 | * 163 | * @param object $object 164 | * @param string $property 165 | * @return mixed 166 | */ 167 | public function getProtectedProperty($object, $property) 168 | { 169 | $reflection = new ReflectionClass($object); 170 | $reflection_property = $reflection->getProperty($property); 171 | $reflection_property->setAccessible(true); 172 | 173 | return $reflection_property->getValue($object); 174 | } 175 | 176 | /** 177 | * Set protected property on a given object via reflection. 178 | * 179 | * @param object $object 180 | * @param string $property 181 | * @param mixed $value 182 | * @return object 183 | */ 184 | public function setProtectedProperty($object, $property, $value) 185 | { 186 | $reflection = new ReflectionClass($object); 187 | $reflection_property = $reflection->getProperty($property); 188 | $reflection_property->setAccessible(true); 189 | $reflection_property->setValue($object, $value); 190 | 191 | return $object; 192 | } 193 | 194 | /** 195 | * Call protected method on a given object via reflection. 196 | * 197 | * @param object|string $objectOrClass 198 | * @param string $method 199 | * @param mixed[] $args 200 | * @return mixed 201 | */ 202 | public function callProtectedMethod($objectOrClass, $method, $args = []) 203 | { 204 | $reflection = new ReflectionClass($objectOrClass); 205 | $reflectionMethod = $reflection->getMethod($method); 206 | $reflectionMethod->setAccessible(true); 207 | $reflectionTarget = is_object($objectOrClass) ? $objectOrClass : null; 208 | 209 | return $reflectionMethod->invokeArgs($reflectionTarget, $args); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /test/_Simulation/Event.php: -------------------------------------------------------------------------------- 1 | name = $name; 24 | $this->data = $data; 25 | } 26 | 27 | /** 28 | * @return mixed 29 | */ 30 | public function name() 31 | { 32 | return $this->name; 33 | } 34 | 35 | /** 36 | * @return mixed[] 37 | */ 38 | public function data() 39 | { 40 | return $this->data; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/_Simulation/EventCollection.php: -------------------------------------------------------------------------------- 1 | loop = $loop; 95 | $this->scenario = function() {}; 96 | $this->state = static::STATE_PENDING; 97 | $this->events = []; 98 | $this->stateMessage = null; 99 | $this->skipMessage = null; 100 | $this->startCallback = function() {}; 101 | $this->stopCallback = function() {}; 102 | $this->stopFlags = false; 103 | } 104 | 105 | /** 106 | * 107 | */ 108 | public function __destruct() 109 | { 110 | $this->stop(); 111 | 112 | unset($loop); 113 | unset($this->scenario); 114 | unset($this->events); 115 | unset($this->state); 116 | unset($this->stateMessage); 117 | unset($this->startCallback); 118 | unset($this->stopCallback); 119 | unset($this->stopFlags); 120 | } 121 | 122 | /** 123 | * @param string $key 124 | * @param mixed $val 125 | */ 126 | public function setParam($key, $val) 127 | { 128 | $this->params[$key] = $val; 129 | } 130 | 131 | /** 132 | * @param string $key 133 | * @return mixed|null 134 | */ 135 | public function getParam($key) 136 | { 137 | return isset($this->params[$key]) ? $this->params[$key] : null; 138 | } 139 | 140 | /** 141 | * @param callable(SimulationInterface) $scenario 142 | */ 143 | public function setScenario(callable $scenario) 144 | { 145 | $this->scenario = $scenario; 146 | } 147 | 148 | /** 149 | * @return callable(SimulationInterface)|null $scenario 150 | */ 151 | public function getScenario() 152 | { 153 | return $this->scenario; 154 | } 155 | 156 | /** 157 | * @return LoopInterface 158 | */ 159 | public function getLoop() 160 | { 161 | return $this->loop; 162 | } 163 | 164 | /** 165 | * @return int 166 | */ 167 | public function getState() 168 | { 169 | return $this->state; 170 | } 171 | 172 | /** 173 | * @return int 174 | */ 175 | public function getStateMessage() 176 | { 177 | return $this->stateMessage; 178 | } 179 | 180 | /** 181 | * 182 | */ 183 | public function begin() 184 | { 185 | $this->start(); 186 | } 187 | 188 | /** 189 | * 190 | */ 191 | public function done() 192 | { 193 | $this->state = static::STATE_SUCCEED; 194 | $this->stop(); 195 | } 196 | 197 | /** 198 | * 199 | */ 200 | public function skip($message) 201 | { 202 | $this->stateMessage = $message; 203 | $this->state = static::STATE_SKIPPED; 204 | $this->stop(); 205 | } 206 | 207 | /** 208 | * 209 | */ 210 | public function fail($message) 211 | { 212 | $this->stateMessage = $message; 213 | $this->state = static::STATE_FAILED; 214 | $this->stop(); 215 | } 216 | 217 | /** 218 | * @param mixed $expected 219 | * @param mixed $actual 220 | * @param string $message 221 | */ 222 | public function assertSame($expected, $actual, $message = "Assertion failed, expected \"%s\" got \"%s\"") 223 | { 224 | if ($expected !== $actual) 225 | { 226 | $stringExpected = (is_object($expected) || is_array($expected)) ? json_encode($expected) : (string) $expected; 227 | $stringActual = (is_object($actual) || is_array($actual)) ? json_encode($actual) : (string) $actual; 228 | 229 | $this->fail(sprintf($message, $stringExpected, $stringActual)); 230 | } 231 | } 232 | 233 | /** 234 | * @param string $name 235 | * @param mixed $data 236 | */ 237 | public function expect($name, $data = []) 238 | { 239 | $this->events[] = new Event($name, $data); 240 | } 241 | 242 | /** 243 | * @return Event[] 244 | */ 245 | public function getExpectations() 246 | { 247 | return $this->events; 248 | } 249 | 250 | /** 251 | * @param callable $callable 252 | */ 253 | public function onStart(callable $callable) 254 | { 255 | $this->startCallback = $callable; 256 | } 257 | 258 | /** 259 | * @param callable $callable 260 | */ 261 | public function onStop(callable $callable) 262 | { 263 | $this->stopCallback = $callable; 264 | } 265 | 266 | /** 267 | * @param string $model 268 | * @param mixed[] $config 269 | * @return object 270 | */ 271 | public function reflect($model, $config = []) 272 | { 273 | foreach ($config as $key=>$value) 274 | { 275 | if ($value === 'Dazzle\Loop\Loop' || $value === 'Dazzle\Loop\LoopInterface') 276 | { 277 | $config[$key] = $this->getLoop(); 278 | } 279 | } 280 | 281 | return (new ReflectionClass($model))->newInstanceArgs($config); 282 | } 283 | 284 | /** 285 | * @throws Exception 286 | */ 287 | private function start() 288 | { 289 | $sim = $this; 290 | 291 | $scenario = $this->scenario; 292 | $scenario($sim); 293 | 294 | if ($this->stopFlags === true) 295 | { 296 | return; 297 | } 298 | 299 | $onStart = $this->startCallback; 300 | $loop = $this->loop; 301 | 302 | $loop->onStart(function() use($sim, $onStart) { 303 | $onStart($sim); 304 | }); 305 | $loop->addTimer(5, function() use($sim) { 306 | $sim->fail('Timeout for test has been reached.'); 307 | }); 308 | 309 | $loop->start(); 310 | } 311 | 312 | /** 313 | * 314 | */ 315 | private function stop() 316 | { 317 | if ($this->loop !== null && $this->loop->isRunning()) 318 | { 319 | $this->loop->stop(); 320 | } 321 | 322 | if ($this->stopFlags === false) 323 | { 324 | $callable = $this->stopCallback; 325 | $callable($this); 326 | } 327 | 328 | $this->stopFlags = true; 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /test/_Simulation/SimulationInterface.php: -------------------------------------------------------------------------------- 1 |