├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── examples └── example.php ├── phpunit.xml ├── src ├── Parallel │ ├── Parallel.php │ └── Storage │ │ ├── ApcuStorage.php │ │ ├── MemcachedStorage.php │ │ ├── RedisStorage.php │ │ └── StorageInterface.php └── autoloader.php └── tests ├── Integration ├── ApcuStorageTest.php ├── MemcachedStorageTest.php ├── ParallelTest.php └── RedisStorageTest.php └── VersionTest.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: cheprasov # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .git/ 3 | vendor/ 4 | test.php 5 | composer.lock 6 | tools/back/ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - '5.5' 4 | - '5.6' 5 | - '7.0' 6 | - '7.1' 7 | - hhvm 8 | 9 | matrix: 10 | allow_failures: 11 | - php: '7.1' 12 | - php: hhvm 13 | 14 | before_install: 15 | - PHP_INI=~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; 16 | - | 17 | if [[ $TRAVIS_PHP_VERSION =~ 5.[56] ]]; then 18 | echo "extension = memcached.so" >> $PHP_INI; 19 | echo yes | pecl install apcu-4.0.10; 20 | echo "apc.enabled = 1" >> $PHP_INI; 21 | echo "apc.enable_cli = 1" >> $PHP_INI; 22 | phpenv config-rm xdebug.ini; 23 | fi; 24 | - | 25 | if [[ $TRAVIS_PHP_VERSION =~ 7.[10] ]]; then 26 | apt-get install -y php-memcached; 27 | echo "extension = memcached.so" >> $PHP_INI; 28 | echo yes | pecl install apcu-5.1.2; 29 | echo "apc.enabled = 1" >> $PHP_INI; 30 | echo "apc.enable_cli = 1" >> $PHP_INI; 31 | fi; 32 | - | 33 | if [[ $TRAVIS_PHP_VERSION =~ 7.[0] ]]; then 34 | phpenv config-rm xdebug.ini; 35 | fi; 36 | 37 | install: 38 | - composer install 39 | 40 | services: 41 | - memcached 42 | - redis 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Alexander Cheprasov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Parallel v1.2.0 for PHP >= 5.5 2 | 3 | The class allows you to run multiple operations parallel in different thread and send results to the main process. Useful if you need to run multiple independent operations simultaneously, instead of sequential execution, or if you run several independent queries, for example, queries to different data bases. 4 | 5 | ## Communications 6 | 7 | Communications of data between processes is via one (or more) of storages: 8 | 9 | - APCu 10 | - Memcached 11 | - Redis 12 | 13 | ## Using 14 | 15 | ```php 16 | functions) 30 | 31 | // $Parallel = new Parallel(new \Parallel\Storage\MemcachedStorage([ 32 | // 'servers' => [['127.0.0.1', 11211]] 33 | // ])); 34 | 35 | // $Parallel = new Parallel(new \Parallel\Storage\RedisStorage([ 36 | // 'server' => 'tcp://127.0.0.1:6379' 37 | // ])); 38 | 39 | $time = microtime(true); 40 | 41 | // 1st operation 42 | $Parallel->run('foo', function() { 43 | // You can use Parallel inside run function by creating new objects Parallel. 44 | // Example: $Parallel = new Parallel(new \Parallel\Storage\ApcuStorage()); 45 | sleep(2); 46 | return ['hello' => 'world']; 47 | }); 48 | 49 | // 2nd operation 50 | $Parallel->run('obj', function() { 51 | sleep(2); 52 | return (object) ['a' => 1, 'b' => 2, 'c' => 3]; 53 | }); 54 | 55 | // 3th operation 56 | // do some thing ... 57 | sleep(2); 58 | 59 | // waiting for and and get results. 60 | // use wait() without parameters for wait all forks. Example: $Parallel->wait(); 61 | $result = $Parallel->wait(['foo', 'obj']); 62 | 63 | print_r($result); 64 | print_r(microtime(true) - $time); 65 | // 3 parallel operations by 2 seconds take about 2 seconds, instead 6 seconds. 66 | 67 | // Array 68 | // ( 69 | // [foo] => Array 70 | // ( 71 | // [hello] => world 72 | // ) 73 | // 74 | // [obj] => stdClass Object 75 | // ( 76 | // [a] => 1 77 | // [b] => 2 78 | // [c] => 3 79 | // ) 80 | // ) 81 | // 2.0130307674408 82 | ``` 83 | 84 | ## Installation 85 | 86 | ### Composer 87 | 88 | Download composer: 89 | 90 | wget -nc http://getcomposer.org/composer.phar 91 | 92 | and add dependency to your project: 93 | 94 | php composer.phar require cheprasov/php-parallel 95 | 96 | 97 | ## Running tests 98 | 99 | To run tests type in console: 100 | 101 | ./vendor/bin/phpunit 102 | 103 | 104 | ## Something doesn't work 105 | 106 | Feel free to fork project, fix bugs and finally request for pull 107 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cheprasov/php-parallel", 3 | "version": "1.2.0", 4 | "description": "The class allows you to run multiple operations parallel in different processes and send results to the main process. Useful if you need to run multiple independent operations simultaneously, instead of sequential execution, or if you run several independent queries, for example, queries to different data bases", 5 | "homepage": "http://github.com/cheprasov/php-parallel", 6 | "minimum-stability": "stable", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Alexander Cheprasov", 11 | "email": "cheprasov.84@ya.ru" 12 | } 13 | ], 14 | "autoload": { 15 | "psr-0": { 16 | "Parallel\\": "src" 17 | } 18 | }, 19 | "require": { 20 | "php": ">=5.5", 21 | "cheprasov/php-redis-client": "^1.4" 22 | }, 23 | "require-dev": { 24 | "phpunit/phpunit": "4.8.*" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/example.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | require (dirname(__DIR__).'/vendor/autoload.php'); 12 | 13 | use Parallel\Parallel; 14 | use Parallel\Storage\ApcuStorage; 15 | 16 | // EXAMPLE, how to run parallel 3 operations. 17 | 18 | // Using Parallel via ApcuStorage (APCu, see http://php.net/manual/ru/book.apcu.php) 19 | $Parallel = new Parallel(new ApcuStorage()); 20 | 21 | // if you have not APCu, you can use Memcached or Redis as Storage. 22 | // Note: you can't store objects in Memcached or Redis and you can't store binary strings (use functions) 23 | 24 | // $Parallel = new Parallel(new \Parallel\Storage\MemcachedStorage([ 25 | // 'servers' => [['127.0.0.1', 11211]] 26 | // ])); 27 | 28 | // $Parallel = new Parallel(new \Parallel\Storage\RedisStorage([ 29 | // 'server' => 'tcp://127.0.0.1:6379' 30 | // ])); 31 | 32 | $time = microtime(true); 33 | 34 | // 1st operation 35 | $Parallel->run('foo', function() { 36 | // You can use Parallel inside run function by creating new objects Parallel. 37 | // Example: $Parallel = new Parallel(new \Parallel\Storage\ApcuStorage()); 38 | sleep(2); 39 | return ['hello' => 'world']; 40 | }); 41 | 42 | // 2nd operation 43 | $Parallel->run('obj', function() { 44 | sleep(2); 45 | return (object) ['a' => 1, 'b' => 2, 'c' => 3]; 46 | }); 47 | 48 | // 3th operation 49 | // do some thing ... 50 | sleep(2); 51 | 52 | // waiting for and and get results. 53 | // use wait() without parameters for wait all forks. Example: $Parallel->wait(); 54 | $result = $Parallel->wait(['foo', 'obj']); 55 | 56 | print_r($result); 57 | print_r(microtime(true) - $time); 58 | // 3 parallel operations by 2 seconds take about 2 seconds, instead 6 seconds. 59 | 60 | // Array 61 | // ( 62 | // [foo] => Array 63 | // ( 64 | // [hello] => world 65 | // ) 66 | // 67 | // [obj] => stdClass Object 68 | // ( 69 | // [a] => 1 70 | // [b] => 2 71 | // [c] => 3 72 | // ) 73 | // ) 74 | // 2.0130307674408 75 | 76 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 24 | ./tests/ 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Parallel/Parallel.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Parallel; 12 | 13 | use Parallel\Storage\StorageInterface; 14 | 15 | class Parallel { 16 | 17 | const VERSION = '1.2.0'; 18 | 19 | /** 20 | * @var StorageInterface 21 | */ 22 | protected $Storage; 23 | 24 | /** 25 | * @var array 26 | */ 27 | protected $pids = []; 28 | 29 | /** 30 | * @var string 31 | */ 32 | protected $key; 33 | 34 | /** 35 | * @param StorageInterface $Storage 36 | */ 37 | public function __construct(StorageInterface $Storage) { 38 | $this->Storage = $Storage; 39 | $this->setKey(uniqid(posix_getpid() .'-', microtime(true)) .'-'. mt_rand(1, 9999)); 40 | } 41 | 42 | /** 43 | * Run a new fork with some name 44 | * @param string $name 45 | * @param \Closure|string|array $callback 46 | * @return bool 47 | */ 48 | public function run($name, $callback) { 49 | if (!is_callable($callback)) { 50 | throw new \InvalidArgumentException('$callback is not callable'); 51 | } 52 | 53 | // Because, we need to remove exists Instance before fork 54 | $this->Storage->setup(); 55 | 56 | $pid = pcntl_fork(); 57 | if ($pid === -1) { 58 | return false; 59 | } 60 | if ($pid) { 61 | // main fork 62 | $this->pids[$name] = $pid; 63 | return true; 64 | } else { 65 | $result = call_user_func($callback); 66 | $this->Storage->set($this->getKey(), $name, $result); 67 | exit(0); 68 | } 69 | } 70 | 71 | /** 72 | * Wait fork by names or all (without parameters) 73 | * @param string|string[]|null $names 74 | * @return array 75 | */ 76 | public function wait($names = null) { 77 | $namesArr = !isset($names) ? array_keys($this->pids) : (array) $names; 78 | foreach ($namesArr as $name) { 79 | if (!isset($this->pids[$name])) { 80 | continue; 81 | } 82 | pcntl_waitpid($this->pids[$name], $status); 83 | unset($this->pids[$name]); 84 | } 85 | $result = $this->Storage->get($this->key, $namesArr); 86 | $this->Storage->del($this->key, $namesArr); 87 | return is_string($names) ? $result[$names] : $result; 88 | } 89 | 90 | /** 91 | * @param string $key 92 | */ 93 | protected function setKey($key) { 94 | $this->key = $key; 95 | } 96 | 97 | /** 98 | * @return string 99 | */ 100 | protected function getKey() { 101 | return $this->key; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/Parallel/Storage/ApcuStorage.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Parallel\Storage; 12 | 13 | /** 14 | * @link http://php.net/manual/ru/book.apcu.php 15 | */ 16 | class ApcuStorage implements StorageInterface { 17 | 18 | /** 19 | * @param string $key 20 | * @param string $field 21 | * @return string 22 | */ 23 | protected function getKeyByField($key, $field) { 24 | return $key.':'.$field; 25 | } 26 | 27 | /** 28 | * @inheritdoc 29 | */ 30 | public function setup() { 31 | } 32 | 33 | /** 34 | * @inheritdoc 35 | */ 36 | public function set($key, $field, $value, $expire = 0) { 37 | return apcu_store($this->getKeyByField($key, $field), $value, $expire ?: null); 38 | } 39 | 40 | /** 41 | * @inheritdoc 42 | */ 43 | public function get($key, $fields) { 44 | if (is_string($fields)) { 45 | return apcu_fetch($this->getKeyByField($key, $fields)); 46 | } 47 | $result = array_map(function($field) use ($key) { 48 | return apcu_fetch($this->getKeyByField($key, $field)); 49 | }, $fields); 50 | return array_combine($fields, array_values($result)); 51 | } 52 | 53 | /** 54 | * @inheritdoc 55 | */ 56 | public function del($key, $fields) { 57 | if (is_string($fields)) { 58 | return (int) apcu_delete($this->getKeyByField($key, $fields)); 59 | } 60 | $data = array_map(function($field) use ($key) { 61 | return (int) apcu_delete($this->getKeyByField($key, $field)); 62 | }, $fields); 63 | 64 | $result = array_count_values($data); 65 | return empty($result[1]) ? 0 : $result[1]; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/Parallel/Storage/MemcachedStorage.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Parallel\Storage; 12 | 13 | class MemcachedStorage implements StorageInterface { 14 | 15 | /** 16 | * @var \Memcached 17 | */ 18 | protected $Memcached; 19 | 20 | /** 21 | * @var array 22 | */ 23 | protected $options; 24 | 25 | /** 26 | * @param array $options 27 | */ 28 | public function __construct($options) { 29 | $this->options = $options; 30 | } 31 | 32 | /** 33 | * @param \Memcached|null $Memcached 34 | */ 35 | public function setMemcached(\Memcached $Memcached = null) { 36 | $this->Memcached = $Memcached; 37 | } 38 | 39 | /** 40 | * @return \Memcached 41 | */ 42 | public function getMemcached() { 43 | if (!$this->Memcached) { 44 | $this->Memcached = new \Memcached(); 45 | $this->Memcached->addServers($this->options['servers']); 46 | } 47 | return $this->Memcached; 48 | } 49 | 50 | /** 51 | * @param string $key 52 | * @param string $field 53 | * @return string 54 | */ 55 | protected function getKeyByField($key, $field) { 56 | return $key.':'.$field; 57 | } 58 | 59 | /** 60 | * @param string $key 61 | * @return string 62 | */ 63 | protected function getFieldFromKey($key) { 64 | $data = explode(':', $key, 2); 65 | return end($data); 66 | } 67 | 68 | /** 69 | * @inheritdoc 70 | */ 71 | public function setup() { 72 | $this->setMemcached(null); 73 | } 74 | 75 | /** 76 | * @inheritdoc 77 | */ 78 | public function set($key, $field, $value, $expire = 0) { 79 | $serialized = $this->serialize($value); 80 | return $this->getMemcached()->set($this->getKeyByField($key, $field), $serialized, $expire ?: null); 81 | } 82 | 83 | /** 84 | * @inheritdoc 85 | */ 86 | public function get($key, $fields) { 87 | if (is_string($fields)) { 88 | $data = $this->getMemcached()->get($this->getKeyByField($key, $fields)); 89 | return $this->unserialize($data); 90 | } 91 | $data = $this->getMemcached()->getMulti(array_map(function($field) use ($key) { 92 | return $this->getKeyByField($key, $field); 93 | }, $fields)); 94 | $result = []; 95 | foreach ($data as $key => $value) { 96 | $result[$this->getFieldFromKey($key)] = $this->unserialize($value); 97 | } 98 | return $result; 99 | } 100 | 101 | /** 102 | * @inheritdoc 103 | */ 104 | public function del($key, $fields) { 105 | if (is_string($fields)) { 106 | return (int) $this->getMemcached()->delete($this->getKeyByField($key, $fields)); 107 | } 108 | // Because method does not work well 109 | $count = 0; 110 | foreach ($fields as $field) { 111 | $count += (int) $this->getMemcached()->delete($this->getKeyByField($key, $field)); 112 | } 113 | return $count; 114 | } 115 | 116 | /** 117 | * @param mixed $data 118 | * @return string 119 | */ 120 | protected function serialize($data) { 121 | return json_encode($data); 122 | } 123 | 124 | /** 125 | * @param string $data 126 | * @return mixed 127 | */ 128 | protected function unserialize($data) { 129 | return json_decode($data, true); 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/Parallel/Storage/RedisStorage.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Parallel\Storage; 12 | 13 | use RedisClient\ClientFactory; 14 | use RedisClient\RedisClient; 15 | 16 | class RedisStorage implements StorageInterface { 17 | 18 | /** 19 | * @var RedisClient 20 | */ 21 | protected $Redis; 22 | 23 | /** 24 | * @var array 25 | */ 26 | protected $options; 27 | 28 | /** 29 | * @param array $options 30 | */ 31 | public function __construct(array $options = []) { 32 | $this->options = $options; 33 | } 34 | 35 | /** 36 | * @param RedisClient|null $Redis 37 | */ 38 | public function setRedis(RedisClient $Redis = null) { 39 | $this->Redis = $Redis; 40 | } 41 | 42 | /** 43 | * @return RedisClient 44 | */ 45 | public function getRedis() { 46 | if (!$this->Redis) { 47 | $this->Redis = ClientFactory::create($this->options); 48 | } 49 | return $this->Redis; 50 | } 51 | 52 | /** 53 | * @inheritdoc 54 | */ 55 | public function setup() { 56 | $this->setRedis(null); 57 | } 58 | 59 | /** 60 | * @inheritdoc 61 | */ 62 | public function set($key, $field, $value, $expire = 0) { 63 | $serialized = $this->serialize($value); 64 | $result = $this->getRedis()->hset($key, $field, $serialized); 65 | if ($expire) { 66 | $this->getRedis()->expire($key, $expire); 67 | } 68 | return (bool) $result; 69 | } 70 | 71 | /** 72 | * @inheritdoc 73 | */ 74 | public function get($key, $fields) { 75 | if (is_string($fields)) { 76 | $data = $this->getRedis()->hget($key, $fields); 77 | return $this->unserialize($data); 78 | } 79 | $result = array_combine($fields, $this->getRedis()->hmget($key, $fields)); 80 | foreach ($result as $field => $value) { 81 | $result[$field] = $this->unserialize($value); 82 | } 83 | return $result; 84 | } 85 | 86 | /** 87 | * @inheritdoc 88 | */ 89 | public function del($key, $fields) { 90 | return $this->getRedis()->hdel($key, (array) $fields); 91 | } 92 | 93 | /** 94 | * @param mixed $data 95 | * @return string 96 | */ 97 | protected function serialize($data) { 98 | return json_encode($data); 99 | } 100 | 101 | /** 102 | * @param string $data 103 | * @return mixed 104 | */ 105 | protected function unserialize($data) { 106 | return json_decode($data, true); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/Parallel/Storage/StorageInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Parallel\Storage; 12 | 13 | interface StorageInterface { 14 | 15 | /** 16 | * @return void 17 | */ 18 | public function setup(); 19 | 20 | /** 21 | * @param string $key 22 | * @param string $field 23 | * @param string $value 24 | * @param int $expire 25 | * @return bool 26 | */ 27 | public function set($key, $field, $value, $expire = 0); 28 | 29 | /** 30 | * @param string $key 31 | * @param string|string[] $fields 32 | * @return array 33 | */ 34 | public function get($key, $fields); 35 | 36 | /** 37 | * @param string $key 38 | * @param string|string[] $fields 39 | * @return int Count of deleted fields 40 | */ 41 | public function del($key, $fields); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/autoloader.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Parallel; 12 | 13 | spl_autoload_register(function($class) { 14 | if (__NAMESPACE__.'\\' !== substr($class, 0, strlen(__NAMESPACE__.'\\'))) { 15 | return; 16 | } 17 | $classPath = __DIR__ .'/'. str_replace('\\', '/', $class) .'.php'; 18 | if (is_file($classPath)) { 19 | return include $classPath; 20 | } 21 | }, false, true); 22 | -------------------------------------------------------------------------------- /tests/Integration/ApcuStorageTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Test\Integration; 12 | use Parallel\Storage\ApcuStorage; 13 | 14 | /** 15 | * @see \Parallel\Storage\ApcuStorage 16 | */ 17 | class ApcuStorageTest extends \PHPUnit_Framework_TestCase { 18 | 19 | /** 20 | * @see \Parallel\Storage\ApcuStorage::set 21 | */ 22 | public function test_set() { 23 | $Storage = new ApcuStorage(); 24 | $success = null; 25 | 26 | $this->assertSame(true, $Storage->set('foo', 'bar1', 'hello world')); 27 | $this->assertSame('hello world', apcu_fetch('foo:bar1', $success)); 28 | $this->assertSame(true, $success); 29 | 30 | $this->assertSame(true, $Storage->set('foo', 'bar2', [1, 2, 3, 4, 5])); 31 | $this->assertSame([1, 2, 3, 4, 5], apcu_fetch('foo:bar2', $success)); 32 | $this->assertSame(true, $success); 33 | 34 | $this->assertSame(true, $Storage->set('foo', 'bar3', ['foo', 'bar', '3', 4, 5.678])); 35 | $this->assertSame(['foo', 'bar', '3', 4, 5.678], apcu_fetch('foo:bar3', $success)); 36 | $this->assertSame(true, $success); 37 | 38 | $this->assertSame(true, $Storage->set('foo', 'bar4', null)); 39 | $this->assertSame(null, apcu_fetch('foo:bar4', $success)); 40 | $this->assertSame(true, $success); 41 | 42 | $this->assertSame(true, $Storage->set('foo', 'bar5', [null, true, false])); 43 | $this->assertSame([null, true, false], apcu_fetch('foo:bar5', $success)); 44 | $this->assertSame(true, $success); 45 | 46 | $this->assertSame(true, $Storage->set('foo', 'bar6', ['foo' => 'bar', 'hello' => 'world'])); 47 | $this->assertSame(['foo' => 'bar', 'hello' => 'world'], apcu_fetch('foo:bar6', $success)); 48 | $this->assertSame(true, $success); 49 | 50 | $obj = (object) ['foo' => 'bar', 'hello' => 'world']; 51 | $this->assertSame(true, $Storage->set('foo', 'bar7', $obj)); 52 | $result = apcu_fetch('foo:bar7', $success); 53 | $this->assertInstanceOf(\stdClass::class, $result); 54 | $this->assertSame(true, $result->foo === 'bar'); 55 | $this->assertSame(true, $result->hello === 'world'); 56 | $this->assertSame(true, $success); 57 | } 58 | 59 | /** 60 | * @see \Parallel\Storage\ApcuStorage::get 61 | */ 62 | public function test_get() { 63 | $Storage = new ApcuStorage(); 64 | $success = null; 65 | 66 | $this->assertSame(true, apcu_store('foo1:bar0', '')); 67 | $this->assertSame('', $Storage->get('foo1', 'bar0')); 68 | 69 | $this->assertSame(true, apcu_store('foo1:bar1', [1, 2, 3, 4, 5])); 70 | $this->assertSame([1, 2, 3, 4, 5], $Storage->get('foo1', 'bar1')); 71 | 72 | $this->assertSame(true, apcu_store('foo1:bar2', [true, false, null, '4', 5, 6.789, 'hello' => 'world'])); 73 | $this->assertSame([true, false, null, '4', 5, 6.789, 'hello' => 'world'], $Storage->get('foo1', 'bar2')); 74 | 75 | $this->assertSame(true, apcu_store('foo1:bar3', 1)); 76 | $this->assertSame(true, apcu_store('foo1:bar4', '2')); 77 | $this->assertSame(true, apcu_store('foo1:bar5', 3.14159265)); 78 | $this->assertSame(true, apcu_store('foo1:bar6', 'hello world')); 79 | $this->assertSame(true, apcu_store('foo1:bar7', ['foo' => 'bar', 'hello' => 'world'])); 80 | $this->assertSame(true, apcu_store('foo1:bar8', null)); 81 | $this->assertSame(true, apcu_store('foo1:bar9', -42)); 82 | $this->assertSame(true, apcu_store('foo1:bar10', "\x00\x10\x13")); 83 | 84 | $this->assertSame([ 85 | 'bar3' => 1, 86 | 'bar4' => '2', 87 | 'bar5' => 3.14159265, 88 | ], $Storage->get('foo1', ['bar3', 'bar4', 'bar5'])); 89 | 90 | $this->assertSame([ 91 | 'bar6' => 'hello world', 92 | 'bar7' => ['foo' => 'bar', 'hello' => 'world'], 93 | ], $Storage->get('foo1', ['bar6', 'bar7'])); 94 | 95 | $this->assertSame([ 96 | 'bar3' => 1, 97 | 'bar4' => '2', 98 | 'bar5' => 3.14159265, 99 | 'bar6' => 'hello world', 100 | 'bar7' => ['foo' => 'bar', 'hello' => 'world'], 101 | 'bar8' => null, 102 | 'bar9' => -42, 103 | 'bar10' => "\x00\x10\x13", 104 | ], $Storage->get('foo1', ['bar3', 'bar4', 'bar5', 'bar6', 'bar7', 'bar8', 'bar9', 'bar10'])); 105 | 106 | $this->assertSame(true, apcu_store('foo1:bar11', (object) ['a' => 1, 'b' => '2', 'c' => 'bar'])); 107 | $result = $Storage->get('foo1', 'bar11'); 108 | $this->assertInstanceOf(\stdClass::class, $result); 109 | $this->assertSame(1, $result->a); 110 | $this->assertSame('2', $result->b); 111 | $this->assertSame('bar', $result->c); 112 | } 113 | 114 | 115 | /** 116 | * @see \Parallel\Storage\ApcuStorage::del 117 | */ 118 | public function test_del() { 119 | $Storage = new ApcuStorage(); 120 | $success = null; 121 | 122 | $this->assertSame(true, apcu_store('foo2:bar0', 'foo')); 123 | $this->assertSame('foo', apcu_fetch('foo2:bar0', $success)); 124 | $this->assertSame(1, $Storage->del('foo2', 'bar0')); 125 | $this->assertSame(false, apcu_fetch('foo2:bar0', $success)); 126 | $this->assertSame(false, $success); 127 | 128 | $this->assertSame(true, apcu_store('foo2:bar1', 'foo')); 129 | $this->assertSame(true, apcu_store('foo2:bar2', 'bar')); 130 | $this->assertSame(true, apcu_store('foo2:bar3', '123')); 131 | 132 | $this->assertSame('foo', apcu_fetch('foo2:bar1', $success)); 133 | $this->assertSame('bar', apcu_fetch('foo2:bar2', $success)); 134 | $this->assertSame('123', apcu_fetch('foo2:bar3', $success)); 135 | 136 | $this->assertSame(2, $Storage->del('foo2', ['bar1', 'bar2', 'bar2'])); 137 | 138 | $this->assertSame(false, apcu_fetch('foo2:bar1', $success)); 139 | $this->assertSame(false, $success); 140 | $this->assertSame(false, apcu_fetch('foo2:bar2', $success)); 141 | $this->assertSame(false, $success); 142 | $this->assertSame('123', apcu_fetch('foo2:bar3', $success)); 143 | 144 | $this->assertSame(1, $Storage->del('foo2', ['bar1', 'bar3'])); 145 | $this->assertSame(false, apcu_fetch('foo2:bar3', $success)); 146 | $this->assertSame(false, $success); 147 | } 148 | 149 | 150 | } 151 | -------------------------------------------------------------------------------- /tests/Integration/MemcachedStorageTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Test\Integration; 12 | use Parallel\Storage\MemcachedStorage; 13 | 14 | /** 15 | * @see \Parallel\Storage\MemcachedStorage 16 | */ 17 | class MemcachedStorageTest extends \PHPUnit_Framework_TestCase { 18 | 19 | /** 20 | * @return array 21 | */ 22 | protected function getTestServers() { 23 | return [explode(':', TEST_MEMCACHED_SERVER)]; 24 | } 25 | 26 | /** 27 | * @return \Memcached 28 | */ 29 | protected function getMemcached() { 30 | $Memcached = new \Memcached(); 31 | $Memcached->addServers($this->getTestServers()); 32 | $Memcached->flush(); 33 | return $Memcached; 34 | } 35 | 36 | /** 37 | * @see \Parallel\Storage\MemcachedStorage::set 38 | */ 39 | public function test_set() { 40 | $Memcached = $this->getMemcached(); 41 | $Memcached->flush(); 42 | $Storage = new MemcachedStorage(['servers' => $this->getTestServers()]); 43 | 44 | $this->assertSame(true, $Storage->set('foo', 'bar1', 'hello world')); 45 | $this->assertSame('"hello world"', $Memcached->get('foo:bar1')); 46 | 47 | $this->assertSame(true, $Storage->set('foo', 'bar2', [1, 2, 3, 4, 5])); 48 | $this->assertSame('[1,2,3,4,5]', $Memcached->get('foo:bar2')); 49 | 50 | $this->assertSame(true, $Storage->set('foo', 'bar3', ['foo', 'bar', '3', 4, 5.678])); 51 | $this->assertSame('["foo","bar","3",4,5.678]', $Memcached->get('foo:bar3')); 52 | 53 | $this->assertSame(true, $Storage->set('foo', 'bar4', null)); 54 | $this->assertSame('null', $Memcached->get('foo:bar4')); 55 | 56 | $this->assertSame(true, $Storage->set('foo', 'bar5', [null, true, false])); 57 | $this->assertSame('[null,true,false]', $Memcached->get('foo:bar5')); 58 | 59 | $this->assertSame(true, $Storage->set('foo', 'bar6', ['foo' => 'bar', 'hello' => 'world'])); 60 | $this->assertSame('{"foo":"bar","hello":"world"}', $Memcached->get('foo:bar6')); 61 | } 62 | 63 | /** 64 | * @see \Parallel\Storage\MemcachedStorage::get 65 | */ 66 | public function test_get() { 67 | $Memcached = $this->getMemcached(); 68 | $Memcached->flush(); 69 | $Storage = new MemcachedStorage(['servers' => $this->getTestServers()]); 70 | 71 | $this->assertSame(true, $Memcached->set('foo1:bar0', '')); 72 | $this->assertSame(null, $Storage->get('foo1', 'bar0')); 73 | 74 | $this->assertSame(true, $Memcached->set('foo1:bar1', '[1, 2, 3, 4, 5]')); 75 | $this->assertSame([1, 2, 3, 4, 5], $Storage->get('foo1', 'bar1')); 76 | 77 | $this->assertSame(true, $Memcached->set('foo1:bar2', '{"0":true, "1":false, "2":null, "3":"4", "4":5, "5":6.789, "hello":"world"}')); 78 | $this->assertSame([true, false, null, '4', 5, 6.789, 'hello' => 'world'], $Storage->get('foo1', 'bar2')); 79 | 80 | $this->assertSame(true, $Memcached->set('foo1:bar3', '1')); 81 | $this->assertSame(true, $Memcached->set('foo1:bar4', '"2"')); 82 | $this->assertSame(true, $Memcached->set('foo1:bar5', '3.14159265')); 83 | $this->assertSame(true, $Memcached->set('foo1:bar6', '"hello world"')); 84 | $this->assertSame(true, $Memcached->set('foo1:bar7', '{"foo":"bar", "hello":"world"}')); 85 | $this->assertSame(true, $Memcached->set('foo1:bar8', 'null')); 86 | $this->assertSame(true, $Memcached->set('foo1:bar9', '-42')); 87 | 88 | $this->assertSame([ 89 | 'bar3' => 1, 90 | 'bar4' => '2', 91 | 'bar5' => 3.14159265, 92 | ], $Storage->get('foo1', ['bar3', 'bar4', 'bar5'])); 93 | 94 | $this->assertSame([ 95 | 'bar6' => 'hello world', 96 | 'bar7' => ['foo' => 'bar', 'hello' => 'world'], 97 | ], $Storage->get('foo1', ['bar6', 'bar7'])); 98 | 99 | $this->assertSame([ 100 | 'bar3' => 1, 101 | 'bar4' => '2', 102 | 'bar5' => 3.14159265, 103 | 'bar6' => 'hello world', 104 | 'bar7' => ['foo' => 'bar', 'hello' => 'world'], 105 | 'bar8' => null, 106 | 'bar9' => -42, 107 | ], $Storage->get('foo1', ['bar3', 'bar4', 'bar5', 'bar6', 'bar7', 'bar8', 'bar9'])); 108 | } 109 | 110 | 111 | /** 112 | * @see \Parallel\Storage\MemcachedStorage::del 113 | */ 114 | public function test_del() { 115 | $Memcached = $this->getMemcached(); 116 | $Memcached->flush(); 117 | $Storage = new MemcachedStorage(['servers' => $this->getTestServers()]); 118 | 119 | $this->assertSame(true, $Memcached->set('foo2:bar0', 'foo')); 120 | $this->assertSame('foo', $Memcached->get('foo2:bar0')); 121 | $this->assertSame(1, $Storage->del('foo2', 'bar0')); 122 | $this->assertSame(false, $Memcached->get('foo2:bar0')); 123 | $this->assertSame(\Memcached::RES_NOTFOUND, $Memcached->getResultCode()); 124 | 125 | $this->assertSame(true, $Memcached->set('foo2:bar1', 'foo')); 126 | $this->assertSame(true, $Memcached->set('foo2:bar2', 'bar')); 127 | $this->assertSame(true, $Memcached->set('foo2:bar3', '123')); 128 | 129 | $this->assertSame('foo', $Memcached->get('foo2:bar1')); 130 | $this->assertSame('bar', $Memcached->get('foo2:bar2')); 131 | $this->assertSame('123', $Memcached->get('foo2:bar3')); 132 | 133 | $this->assertSame(2, $Storage->del('foo2', ['bar1', 'bar2', 'bar2'])); 134 | 135 | $this->assertSame(false, $Memcached->get('foo2:bar1')); 136 | $this->assertSame(\Memcached::RES_NOTFOUND, $Memcached->getResultCode()); 137 | $this->assertSame(false, $Memcached->get('foo2:bar2')); 138 | $this->assertSame(\Memcached::RES_NOTFOUND, $Memcached->getResultCode()); 139 | $this->assertSame('123', $Memcached->get('foo2:bar3')); 140 | 141 | $this->assertSame(1, $Storage->del('foo2', ['bar1', 'bar3'])); 142 | $this->assertSame(false, $Memcached->get('foo2:bar3')); 143 | $this->assertSame(\Memcached::RES_NOTFOUND, $Memcached->getResultCode()); 144 | } 145 | 146 | 147 | } 148 | -------------------------------------------------------------------------------- /tests/Integration/ParallelTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Test\Integration; 12 | use Parallel\Parallel; 13 | use Parallel\Storage\ApcuStorage; 14 | use Parallel\Storage\MemcachedStorage; 15 | use Parallel\Storage\RedisStorage; 16 | 17 | /** 18 | * @see \Parallel\Parallel 19 | */ 20 | class ParallelTest extends \PHPUnit_Framework_TestCase { 21 | 22 | /** 23 | * @return array 24 | */ 25 | public function getStorage() { 26 | return [ 27 | [function() {return new ApcuStorage();}], 28 | [function() {return new MemcachedStorage(['servers' => [explode(':', TEST_MEMCACHED_SERVER)]]);}], 29 | [function() {return 30 | new RedisStorage(['server' => TEST_REDIS_SERVER]);}], 31 | ]; 32 | } 33 | 34 | /** 35 | * @dataProvider getStorage 36 | * 37 | * @param \Closure $getStorage 38 | */ 39 | public function test_parallelWithStorage(\Closure $getStorage) { 40 | $Parallel = new Parallel($getStorage()); 41 | 42 | $time = microtime(true); 43 | for ($i = 1; $i <= 5; ++$i) { 44 | $Parallel->run('n:'. $i, function() use ($i) { 45 | sleep($i); 46 | return $i * $i; 47 | }); 48 | } 49 | sleep(4); 50 | $result = $Parallel->wait(['n:1', 'n:2', 'n:3', 'n:4', 'n:5']); 51 | $time = microtime(true) - $time; 52 | $this->assertGreaterThanOrEqual(5, $time); 53 | $this->assertLessThan(6, $time); 54 | 55 | $this->assertSame(['n:1' => 1, 'n:2' => 4, 'n:3' => 9, 'n:4' => 16, 'n:5' => 25], $result); 56 | } 57 | 58 | /** 59 | * @dataProvider getStorage 60 | * 61 | * @param \Closure $getStorage 62 | */ 63 | public function test_nestedParallelWithStorage(\Closure $getStorage) { 64 | $Parallel = new Parallel($getStorage()); 65 | 66 | $time = microtime(true); 67 | for ($i = 1; $i <= 2; ++$i) { 68 | $Parallel->run('n:'. $i, function() use ($getStorage) { 69 | $Parallel = new Parallel($getStorage()); 70 | $Parallel->run('n:1', function() { 71 | sleep(2); 72 | return 'foo'; 73 | }); 74 | $Parallel->run('n:2', function() { 75 | sleep(2); 76 | return 'bar'; 77 | }); 78 | // wait all 79 | return $Parallel->wait(); 80 | }); 81 | } 82 | sleep(2); 83 | $result = $Parallel->wait(['n:1', 'n:2']); 84 | $time = microtime(true) - $time; 85 | $this->assertGreaterThanOrEqual(2, $time); 86 | $this->assertLessThan(3, $time); 87 | 88 | $this->assertSame( 89 | ['n:1' => ['n:1' => 'foo', 'n:2' => 'bar'], 'n:2' => ['n:1' => 'foo', 'n:2' => 'bar']], 90 | $result 91 | ); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /tests/Integration/RedisStorageTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Test\Integration; 12 | use Parallel\Storage\RedisStorage; 13 | use RedisClient\ClientFactory; 14 | use RedisClient\RedisClient; 15 | 16 | /** 17 | * @see \Parallel\Storage\RedisStorage 18 | */ 19 | class RedisStorageTest extends \PHPUnit_Framework_TestCase { 20 | 21 | /** 22 | * @return RedisClient 23 | */ 24 | protected function getRedis() { 25 | return ClientFactory::create([ 26 | 'server' => TEST_REDIS_SERVER 27 | ]); 28 | } 29 | 30 | /** 31 | * @see \Parallel\Storage\RedisStorage::set 32 | */ 33 | public function test_set() { 34 | $Redis = $this->getRedis(); 35 | $Redis->flushdb(); 36 | $Storage = new RedisStorage(['server' => TEST_REDIS_SERVER]); 37 | 38 | $this->assertSame(true, $Storage->set('foo', 'bar1', 'hello world')); 39 | $this->assertSame(['bar1' => '"hello world"'], $Redis->hgetall('foo')); 40 | 41 | $this->assertSame(true, $Storage->set('foo', 'bar2', [1, 2, 3, 4, 5])); 42 | $this->assertSame(['bar1' => '"hello world"', 'bar2' => '[1,2,3,4,5]'], $Redis->hgetall('foo')); 43 | 44 | $this->assertSame(true, $Storage->set('foo', 'bar3', ['foo', 'bar', '3', 4, 5.678])); 45 | $this->assertSame('["foo","bar","3",4,5.678]', $Redis->hget('foo', 'bar3')); 46 | 47 | $this->assertSame(true, $Storage->set('foo', 'bar4', null)); 48 | $this->assertSame('null', $Redis->hget('foo', 'bar4')); 49 | 50 | $this->assertSame(true, $Storage->set('foo', 'bar5', [null, true, false])); 51 | $this->assertSame('[null,true,false]', $Redis->hget('foo', 'bar5')); 52 | 53 | $this->assertSame(true, $Storage->set('foo', 'bar6', ['foo' => 'bar', 'hello' => 'world'])); 54 | $this->assertSame('{"foo":"bar","hello":"world"}', $Redis->hget('foo', 'bar6')); 55 | } 56 | 57 | /** 58 | * @see \Parallel\Storage\RedisStorage::get 59 | */ 60 | public function test_get() { 61 | $Redis = $this->getRedis(); 62 | $Redis->flushdb(); 63 | $Storage = new RedisStorage(['server' => TEST_REDIS_SERVER]); 64 | 65 | $this->assertSame(1, $Redis->hset('foo1', 'bar0', '')); 66 | $this->assertSame(null, $Storage->get('foo1', 'bar0')); 67 | 68 | $this->assertSame(1, $Redis->hset('foo1', 'bar1', '[1, 2, 3, 4, 5]')); 69 | $this->assertSame([1, 2, 3, 4, 5], $Storage->get('foo1', 'bar1')); 70 | 71 | $this->assertSame(1, $Redis->hset('foo1', 'bar2', '{"0":true, "1":false, "2":null, "3":"4", "4":5, "5":6.789, "hello":"world"}')); 72 | $this->assertSame([true, false, null, '4', 5, 6.789, 'hello' => 'world'], $Storage->get('foo1', 'bar2')); 73 | 74 | $this->assertSame(1, $Redis->hset('foo1', 'bar3', '1')); 75 | $this->assertSame(1, $Redis->hset('foo1', 'bar4', '"2"')); 76 | $this->assertSame(1, $Redis->hset('foo1', 'bar5', '3.14159265')); 77 | $this->assertSame(1, $Redis->hset('foo1', 'bar6', '"hello world"')); 78 | $this->assertSame(1, $Redis->hset('foo1', 'bar7', '{"foo":"bar", "hello":"world"}')); 79 | $this->assertSame(1, $Redis->hset('foo1', 'bar8', 'null')); 80 | $this->assertSame(1, $Redis->hset('foo1', 'bar9', '-42')); 81 | 82 | $this->assertSame([ 83 | 'bar3' => 1, 84 | 'bar4' => '2', 85 | 'bar5' => 3.14159265, 86 | ], $Storage->get('foo1', ['bar3', 'bar4', 'bar5'])); 87 | 88 | $this->assertSame([ 89 | 'bar6' => 'hello world', 90 | 'bar7' => ['foo' => 'bar', 'hello' => 'world'], 91 | ], $Storage->get('foo1', ['bar6', 'bar7'])); 92 | 93 | $this->assertSame([ 94 | 'bar3' => 1, 95 | 'bar4' => '2', 96 | 'bar5' => 3.14159265, 97 | 'bar6' => 'hello world', 98 | 'bar7' => ['foo' => 'bar', 'hello' => 'world'], 99 | 'bar8' => null, 100 | 'bar9' => -42, 101 | ], $Storage->get('foo1', ['bar3', 'bar4', 'bar5', 'bar6', 'bar7', 'bar8', 'bar9'])); 102 | } 103 | 104 | 105 | /** 106 | * @see \Parallel\Storage\RedisStorage::del 107 | */ 108 | public function test_del() { 109 | $Redis = $this->getRedis(); 110 | $Redis->flushdb(); 111 | $Storage = new RedisStorage(['server' => TEST_REDIS_SERVER]); 112 | 113 | $this->assertSame(1, $Redis->hset('foo2', 'bar0', 'foo')); 114 | $this->assertSame('foo', $Redis->hget('foo2', 'bar0')); 115 | $this->assertSame(1, $Storage->del('foo2', 'bar0')); 116 | $this->assertSame(null, $Redis->hget('foo2', 'bar0')); 117 | 118 | $this->assertSame(1, $Redis->hset('foo2', 'bar1', 'foo')); 119 | $this->assertSame(1, $Redis->hset('foo2', 'bar2', 'bar')); 120 | $this->assertSame(1, $Redis->hset('foo2', 'bar3', '123')); 121 | 122 | $this->assertSame('foo', $Redis->hget('foo2', 'bar1')); 123 | $this->assertSame('bar', $Redis->hget('foo2', 'bar2')); 124 | $this->assertSame('123', $Redis->hget('foo2', 'bar3')); 125 | 126 | $this->assertSame(2, $Storage->del('foo2', ['bar1', 'bar2', 'bar2'])); 127 | 128 | $this->assertSame(null, $Redis->hget('foo2', 'bar1')); 129 | $this->assertSame(null, $Redis->hget('foo2', 'bar2')); 130 | $this->assertSame('123', $Redis->hget('foo2', 'bar3')); 131 | 132 | $this->assertSame(1, $Storage->del('foo2', ['bar1', 'bar3'])); 133 | $this->assertSame(null, $Redis->hget('foo2', 'bar3')); 134 | } 135 | 136 | 137 | } 138 | -------------------------------------------------------------------------------- /tests/VersionTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Test; 12 | 13 | use Parallel\Parallel; 14 | 15 | class VersionTest extends \PHPUnit_Framework_TestCase { 16 | 17 | public function test_version() { 18 | chdir(__DIR__.'/../'); 19 | $composer = json_decode(file_get_contents('./composer.json'), true); 20 | 21 | $this->assertSame(true, isset($composer['version'])); 22 | $this->assertSame( 23 | Parallel::VERSION, 24 | $composer['version'], 25 | 'Please, change version in composer.json' 26 | ); 27 | 28 | $readme = file('./README.md'); 29 | $this->assertSame( 30 | true, 31 | strpos($readme[0], 'Parallel v'.$composer['version']) > 0, 32 | 'Please, change version in README.md' 33 | ); 34 | 35 | } 36 | 37 | } 38 | --------------------------------------------------------------------------------