From eb679338c2f590d793cd5d596624da4d11735159 Mon Sep 17 00:00:00 2001 From: Petra Barus Date: Sat, 16 Jan 2016 14:42:30 +0700 Subject: [PATCH 1/8] PHPCS Fix. --- .travis.yml | 4 + composer.json | 7 +- composer.lock | 709 +++++++++++++----- ruleset.xml | 63 ++ .../ActiveRecordDeferredEventBehavior.php | 36 +- .../ActiveRecordDeferredEventHandler.php | 27 +- ...tiveRecordDeferredEventRoutingBehavior.php | 18 +- src/Behaviors/DeferredEventBehavior.php | 95 +-- src/Behaviors/DeferredEventHandler.php | 45 +- src/Behaviors/DeferredEventInterface.php | 11 +- .../DeferredEventRoutingBehavior.php | 49 +- src/Behaviors/DeferredEventTrait.php | 66 +- src/Console/Controller.php | 189 +++-- src/Event.php | 27 + src/Job.php | 36 +- src/MultipleQueue.php | 101 --- src/Queue.php | 195 +++-- src/Queues/DbQueue.php | 180 +++++ src/Queues/MemoryQueue.php | 43 +- src/Queues/MultipleQueue.php | 112 +++ src/{ => Queues}/SqsQueue.php | 42 +- src/Strategies/RandomStrategy.php | 24 +- src/Strategies/Strategy.php | 44 +- src/Strategies/WeightedStrategy.php | 37 +- src/Web/Controller.php | 60 +- src/Worker/Controller.php | 30 +- tests/EventTest.php | 6 + tests/Queues/DbQueueTest.php | 17 + tests/{ => Queues}/MultipleQueueTest.php | 4 +- tests/Queues/SqsQueueTest.php | 6 + 30 files changed, 1618 insertions(+), 665 deletions(-) create mode 100644 ruleset.xml create mode 100644 src/Event.php delete mode 100644 src/MultipleQueue.php create mode 100644 src/Queues/DbQueue.php create mode 100644 src/Queues/MultipleQueue.php rename src/{ => Queues}/SqsQueue.php (84%) create mode 100644 tests/EventTest.php create mode 100644 tests/Queues/DbQueueTest.php rename tests/{ => Queues}/MultipleQueueTest.php (96%) create mode 100644 tests/Queues/SqsQueueTest.php diff --git a/.travis.yml b/.travis.yml index 37012c2..78b3aa8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,10 @@ php: - 5.6 - hhvm - nightly + +services: + - mysql + - redis-server install: - travis_retry composer self-update && composer --version diff --git a/composer.json b/composer.json index 6f65e58..5c9974c 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,12 @@ }, "require-dev": { "phpunit/phpunit": "4.6.*", - "phpunit/dbunit": ">=1.2" + "phpunit/dbunit": ">=1.2", + "phpunit/php-code-coverage": "2.2.4", + "fzaninotto/faker": "dev-master", + "flow/jsonpath": "dev-master", + "yiisoft/yii2-coding-standards": "*", + "squizlabs/php_codesniffer": "2.*" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 54f848b..d8fc37a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "4c1a5a00aa038095f1b5cb834661cdd8", + "hash": "5cb39cc3e6c770274ffc19769c86e64c", + "content-hash": "efa2d9ab89c590fc4acf4ce84dc28dc7", "packages": [ { "name": "aws/aws-sdk-php", @@ -12,32 +13,38 @@ "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "22e2b5d3f5e98f85297111de5229e2868b923faf" + "reference": "dff1c76551777fcc5890d2d5324f9f9e5cdd4e3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/22e2b5d3f5e98f85297111de5229e2868b923faf", - "reference": "22e2b5d3f5e98f85297111de5229e2868b923faf", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/dff1c76551777fcc5890d2d5324f9f9e5cdd4e3d", + "reference": "dff1c76551777fcc5890d2d5324f9f9e5cdd4e3d", "shasum": "" }, "require": { - "guzzlehttp/guzzle": ">=5.3|~6.0.1|~6.1", + "guzzlehttp/guzzle": "~5.3|~6.0.1|~6.1", "guzzlehttp/promises": "~1.0", "guzzlehttp/psr7": "~1.0", "mtdowling/jmespath.php": "~2.2", "php": ">=5.5" }, "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", "behat/behat": "~3.0", + "doctrine/cache": "~1.4", "ext-dom": "*", "ext-json": "*", "ext-openssl": "*", "ext-pcre": "*", "ext-simplexml": "*", "ext-spl": "*", - "phpunit/phpunit": "~4.0" + "nette/neon": "^2.3", + "phpunit/phpunit": "~4.0|~5.0" }, "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", "ext-curl": "To send requests using cURL", "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages" }, @@ -77,19 +84,19 @@ "s3", "sdk" ], - "time": "2015-06-29 22:51:35" + "time": "2016-01-15 02:01:24" }, { "name": "bower-asset/jquery", "version": "2.1.4", "source": { "type": "git", - "url": "https://github.com/jquery/jquery.git", + "url": "https://github.com/jquery/jquery-dist.git", "reference": "7751e69b615c6eca6f783a81e292a55725af6b85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jquery/jquery/zipball/7751e69b615c6eca6f783a81e292a55725af6b85", + "url": "https://api.github.com/repos/jquery/jquery-dist/zipball/7751e69b615c6eca6f783a81e292a55725af6b85", "reference": "7751e69b615c6eca6f783a81e292a55725af6b85", "shasum": "" }, @@ -125,16 +132,16 @@ }, { "name": "bower-asset/jquery.inputmask", - "version": "3.1.63", + "version": "3.2.5", "source": { "type": "git", "url": "https://github.com/RobinHerbots/jquery.inputmask.git", - "reference": "c40c7287eadc31e341ebbf0c02352eb55b9cbc48" + "reference": "d08264a678865849c808359d126e3bddb9ec87a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/RobinHerbots/jquery.inputmask/zipball/c40c7287eadc31e341ebbf0c02352eb55b9cbc48", - "reference": "c40c7287eadc31e341ebbf0c02352eb55b9cbc48", + "url": "https://api.github.com/repos/RobinHerbots/jquery.inputmask/zipball/d08264a678865849c808359d126e3bddb9ec87a6", + "reference": "d08264a678865849c808359d126e3bddb9ec87a6", "shasum": "" }, "require": { @@ -143,23 +150,17 @@ "type": "bower-asset-library", "extra": { "bower-asset-main": [ - "./dist/inputmask/jquery.inputmask.js", - "./dist/inputmask/jquery.inputmask.extensions.js", - "./dist/inputmask/jquery.inputmask.date.extensions.js", - "./dist/inputmask/jquery.inputmask.numeric.extensions.js", - "./dist/inputmask/jquery.inputmask.phone.extensions.js", - "./dist/inputmask/jquery.inputmask.regex.extensions.js" + "./dist/inputmask/inputmask.js" ], "bower-asset-ignore": [ - "**/.*", - "qunit/", - "nuget/", - "tools/", - "js/", - "*.md", - "build.properties", - "build.xml", - "jquery.inputmask.jquery.json" + "**/*", + "!dist/*", + "!dist/inputmask/*", + "!dist/min/*", + "!dist/min/inputmask/*", + "!extra/bindings/*", + "!extra/dependencyLibs/*", + "!extra/phone-codes/*" ] }, "license": [ @@ -209,12 +210,12 @@ "source": { "type": "git", "url": "https://github.com/yiisoft/jquery-pjax.git", - "reference": "3f20897307cca046fca5323b318475ae9dac0ca0" + "reference": "3bceef03fa80ceee8da5854fa3249df1afc871cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/3f20897307cca046fca5323b318475ae9dac0ca0", - "reference": "3f20897307cca046fca5323b318475ae9dac0ca0", + "url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/3bceef03fa80ceee8da5854fa3249df1afc871cd", + "reference": "3bceef03fa80ceee8da5854fa3249df1afc871cd", "shasum": "" }, "require": { @@ -227,6 +228,7 @@ ".travis.yml", "Gemfile", "Gemfile.lock", + "CONTRIBUTING.md", "vendor/", "script/", "test/" @@ -237,8 +239,7 @@ }, "license": [ "MIT" - ], - "time": "2015-03-08 21:03:11" + ] }, { "name": "cebe/markdown", @@ -350,12 +351,12 @@ "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "9c43893a8e580d8401e4210cebe038d850341012" + "reference": "74fc3909170d66f3e93719ae025b79faea1f49f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9c43893a8e580d8401e4210cebe038d850341012", - "reference": "9c43893a8e580d8401e4210cebe038d850341012", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/74fc3909170d66f3e93719ae025b79faea1f49f5", + "reference": "74fc3909170d66f3e93719ae025b79faea1f49f5", "shasum": "" }, "require": { @@ -371,12 +372,12 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.0-dev" + "dev-master": "6.1-dev" } }, "autoload": { "files": [ - "src/functions.php" + "src/functions_include.php" ], "psr-4": { "GuzzleHttp\\": "src/" @@ -404,7 +405,7 @@ "rest", "web service" ], - "time": "2015-06-25 01:00:39" + "time": "2015-12-29 21:28:50" }, { "name": "guzzlehttp/promises", @@ -412,12 +413,12 @@ "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "2ee5bc7f1a92efecc90da7f6711a53a7be26b5b7" + "reference": "b1e1c0d55f8083c71eda2c28c12a228d708294ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/2ee5bc7f1a92efecc90da7f6711a53a7be26b5b7", - "reference": "2ee5bc7f1a92efecc90da7f6711a53a7be26b5b7", + "url": "https://api.github.com/repos/guzzle/promises/zipball/b1e1c0d55f8083c71eda2c28c12a228d708294ea", + "reference": "b1e1c0d55f8083c71eda2c28c12a228d708294ea", "shasum": "" }, "require": { @@ -437,7 +438,7 @@ "GuzzleHttp\\Promise\\": "src/" }, "files": [ - "src/functions.php" + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -455,20 +456,20 @@ "keywords": [ "promise" ], - "time": "2015-06-24 16:16:25" + "time": "2015-10-15 22:28:00" }, { "name": "guzzlehttp/psr7", - "version": "1.1.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "af0e1758de355eb113917ad79c3c0e3604bce4bd" + "reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/af0e1758de355eb113917ad79c3c0e3604bce4bd", - "reference": "af0e1758de355eb113917ad79c3c0e3604bce4bd", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/4d0bdbe1206df7440219ce14c972aa57cc5e4982", + "reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982", "shasum": "" }, "require": { @@ -492,7 +493,7 @@ "GuzzleHttp\\Psr7\\": "src/" }, "files": [ - "src/functions.php" + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -513,7 +514,7 @@ "stream", "uri" ], - "time": "2015-06-24 19:55:15" + "time": "2015-11-03 01:34:55" }, { "name": "jeremeamia/SuperClosure", @@ -521,21 +522,21 @@ "source": { "type": "git", "url": "https://github.com/jeremeamia/super_closure.git", - "reference": "155fadced44802618435130ed7e16e44a2575e65" + "reference": "29a88be2a4846d27c1613aed0c9071dfad7b5938" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jeremeamia/super_closure/zipball/155fadced44802618435130ed7e16e44a2575e65", - "reference": "155fadced44802618435130ed7e16e44a2575e65", + "url": "https://api.github.com/repos/jeremeamia/super_closure/zipball/29a88be2a4846d27c1613aed0c9071dfad7b5938", + "reference": "29a88be2a4846d27c1613aed0c9071dfad7b5938", "shasum": "" }, "require": { - "nikic/php-parser": "~1.2", - "php": ">=5.4" + "nikic/php-parser": "^1.2|^2.0", + "php": ">=5.4", + "symfony/polyfill-php56": "^1.0" }, "require-dev": { - "codeclimate/php-test-reporter": "~0.1.2", - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "^4.0|^5.0" }, "type": "library", "extra": { @@ -571,20 +572,20 @@ "serialize", "tokenizer" ], - "time": "2015-06-09 19:13:35" + "time": "2015-12-05 17:17:57" }, { "name": "mtdowling/jmespath.php", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "a7d99d0c836e69d27b7bfca1d33ca2759fba3289" + "reference": "192f93e43c2c97acde7694993ab171b3de284093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a7d99d0c836e69d27b7bfca1d33ca2759fba3289", - "reference": "a7d99d0c836e69d27b7bfca1d33ca2759fba3289", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/192f93e43c2c97acde7694993ab171b3de284093", + "reference": "192f93e43c2c97acde7694993ab171b3de284093", "shasum": "" }, "require": { @@ -626,36 +627,42 @@ "json", "jsonpath" ], - "time": "2015-05-27 17:21:31" + "time": "2016-01-05 18:25:05" }, { "name": "nikic/php-parser", - "version": "1.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "fd7ee2e083860f40a1f7f51a3db449b5a734bab5" + "reference": "94f10d3c505cf1631c551824218da96a39af9592" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/fd7ee2e083860f40a1f7f51a3db449b5a734bab5", - "reference": "fd7ee2e083860f40a1f7f51a3db449b5a734bab5", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/94f10d3c505cf1631c551824218da96a39af9592", + "reference": "94f10d3c505cf1631c551824218da96a39af9592", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.3" + "php": ">=5.4" }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "bin": [ + "bin/php-parse" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "2.0-dev" } }, "autoload": { - "files": [ - "lib/bootstrap.php" - ] + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -671,7 +678,7 @@ "parser", "php" ], - "time": "2015-06-20 10:34:10" + "time": "2016-01-15 21:05:36" }, { "name": "psr/http-message", @@ -722,36 +729,144 @@ ], "time": "2015-05-04 20:22:00" }, + { + "name": "symfony/polyfill-php56", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php56.git", + "reference": "00a7781ba12cc2d0f303a845af4d0b7c91f85f07" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/00a7781ba12cc2d0f303a845af4d0b7c91f85f07", + "reference": "00a7781ba12cc2d0f303a845af4d0b7c91f85f07", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php56\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-13 07:16:33" + }, + { + "name": "symfony/polyfill-util", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-util.git", + "reference": "3c73bcdad66642261a08921ca19a78525feb3673" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/3c73bcdad66642261a08921ca19a78525feb3673", + "reference": "3c73bcdad66642261a08921ca19a78525feb3673", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Util\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony utilities for portability of PHP codes", + "homepage": "https://symfony.com", + "keywords": [ + "compat", + "compatibility", + "polyfill", + "shim" + ], + "time": "2016-01-13 07:16:33" + }, { "name": "symfony/process", "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/symfony/Process.git", - "reference": "d9df194a6e3a4bd54474438b50212b539fc69451" + "url": "https://github.com/symfony/process.git", + "reference": "1cba8f04eb0a9e38184a8003e927a5fb2d62158a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/d9df194a6e3a4bd54474438b50212b539fc69451", - "reference": "d9df194a6e3a4bd54474438b50212b539fc69451", + "url": "https://api.github.com/repos/symfony/process/zipball/1cba8f04eb0a9e38184a8003e927a5fb2d62158a", + "reference": "1cba8f04eb0a9e38184a8003e927a5fb2d62158a", "shasum": "" }, "require": { "php": ">=5.5.9" }, - "require-dev": { - "symfony/phpunit-bridge": "~2.8|~3.0" - }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -769,7 +884,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2015-05-15 14:16:35" + "time": "2016-01-06 10:01:46" }, { "name": "yiisoft/yii2", @@ -777,20 +892,21 @@ "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-framework.git", - "reference": "533b9a5983e1ea6dfd1604dd9cfb32d0a0eccc35" + "reference": "5311873ebad98abc446d49f9c33c1070a0f6ba73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/533b9a5983e1ea6dfd1604dd9cfb32d0a0eccc35", - "reference": "533b9a5983e1ea6dfd1604dd9cfb32d0a0eccc35", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/5311873ebad98abc446d49f9c33c1070a0f6ba73", + "reference": "5311873ebad98abc446d49f9c33c1070a0f6ba73", "shasum": "" }, "require": { "bower-asset/jquery": "2.1.*@stable | 1.11.*@stable", - "bower-asset/jquery.inputmask": "3.1.*", + "bower-asset/jquery.inputmask": "~3.2.2", "bower-asset/punycode": "1.3.*", "bower-asset/yii2-pjax": ">=2.0.1", "cebe/markdown": "~1.0.0 | ~1.1.0", + "ext-ctype": "*", "ext-mbstring": "*", "ezyang/htmlpurifier": "4.6.*", "lib-pcre": "*", @@ -849,6 +965,11 @@ "name": "Paul Klimov", "email": "klimov.paul@gmail.com", "role": "Core framework development" + }, + { + "name": "Dmitry Naumenko", + "email": "d.naumenko.a@gmail.com", + "role": "Core framework development" } ], "description": "Yii PHP Framework Version 2", @@ -857,7 +978,7 @@ "framework", "yii2" ], - "time": "2015-06-28 15:20:37" + "time": "2016-01-16 06:37:36" }, { "name": "yiisoft/yii2-composer", @@ -865,12 +986,12 @@ "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-composer.git", - "reference": "6b2a2b3bb83d4b3dd791f76e64c48973ce01bac6" + "reference": "574dcb1d101ae55be230e0c00a2428af6ec4c5c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/6b2a2b3bb83d4b3dd791f76e64c48973ce01bac6", - "reference": "6b2a2b3bb83d4b3dd791f76e64c48973ce01bac6", + "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/574dcb1d101ae55be230e0c00a2428af6ec4c5c1", + "reference": "574dcb1d101ae55be230e0c00a2428af6ec4c5c1", "shasum": "" }, "require": { @@ -904,7 +1025,7 @@ "extension installer", "yii2" ], - "time": "2015-06-11 19:55:58" + "time": "2015-12-01 20:06:03" } ], "packages-dev": [ @@ -962,6 +1083,94 @@ ], "time": "2015-06-14 21:17:01" }, + { + "name": "flow/jsonpath", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/FlowCommunications/JSONPath.git", + "reference": "e064398010089f9efb46044f85ee63c458ae89e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FlowCommunications/JSONPath/zipball/e064398010089f9efb46044f85ee63c458ae89e1", + "reference": "e064398010089f9efb46044f85ee63c458ae89e1", + "shasum": "" + }, + "require-dev": { + "peekmo/jsonpath": "dev-master", + "phpunit/phpunit": "dev-master" + }, + "type": "library", + "autoload": { + "psr-0": { + "Flow\\JSONPath": "src/", + "Flow\\JSONPath\\Test": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Frank", + "email": "stephen@flowsa.com" + } + ], + "description": "JSONPath implementation for parsing, searching and flattening arrays", + "time": "2015-10-12 07:39:47" + }, + { + "name": "fzaninotto/faker", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/fzaninotto/Faker.git", + "reference": "de2ed3bbe68254efeff68db60092fc8ed5298055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/de2ed3bbe68254efeff68db60092fc8ed5298055", + "reference": "de2ed3bbe68254efeff68db60092fc8ed5298055", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "ext-intl": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~1.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "time": "2016-01-08 14:16:18" + }, { "name": "phpdocumentor/reflection-docblock", "version": "2.0.4", @@ -1017,18 +1226,20 @@ "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "5a355f91730c845301a9e28f91c8a5053353c496" + "reference": "e55e3e32a870bd4f05425fa4f717b52bd40e5659" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/5a355f91730c845301a9e28f91c8a5053353c496", - "reference": "5a355f91730c845301a9e28f91c8a5053353c496", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e55e3e32a870bd4f05425fa4f717b52bd40e5659", + "reference": "e55e3e32a870bd4f05425fa4f717b52bd40e5659", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "~2.0", - "sebastian/comparator": "~1.1" + "sebastian/comparator": "~1.1", + "sebastian/recursion-context": "~1.0" }, "require-dev": { "phpspec/phpspec": "~2.0" @@ -1036,7 +1247,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.5.x-dev" } }, "autoload": { @@ -1069,7 +1280,7 @@ "spy", "stub" ], - "time": "2015-05-20 16:00:43" + "time": "2015-12-28 13:26:33" }, { "name": "phpunit/dbunit", @@ -1077,40 +1288,36 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/dbunit.git", - "reference": "a0b24d7b98844377d6915381e956e3a6573b8844" + "reference": "76adbf67d800f9e275e4bb70f1106d02f152c7a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/dbunit/zipball/a0b24d7b98844377d6915381e956e3a6573b8844", - "reference": "a0b24d7b98844377d6915381e956e3a6573b8844", + "url": "https://api.github.com/repos/sebastianbergmann/dbunit/zipball/76adbf67d800f9e275e4bb70f1106d02f152c7a4", + "reference": "76adbf67d800f9e275e4bb70f1106d02f152c7a4", "shasum": "" }, "require": { "ext-pdo": "*", "ext-simplexml": "*", - "php": ">=5.3.3", - "phpunit/phpunit": "~4.0", - "symfony/yaml": "~2.1" + "php": ">=5.4", + "phpunit/phpunit": "~4|~5", + "symfony/yaml": "~2.1|~3.0" }, "bin": [ - "composer/bin/dbunit" + "dbunit" ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "classmap": [ - "PHPUnit/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "", - "../../symfony/yaml/" - ], "license": [ "BSD-3-Clause" ], @@ -1128,20 +1335,20 @@ "testing", "xunit" ], - "time": "2015-06-21 13:05:35" + "time": "2015-11-03 14:01:44" }, { "name": "phpunit/php-code-coverage", - "version": "dev-master", + "version": "2.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "df88fdf48a61af29cc309545f9c8d425c63b8852" + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/df88fdf48a61af29cc309545f9c8d425c63b8852", - "reference": "df88fdf48a61af29cc309545f9c8d425c63b8852", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", "shasum": "" }, "require": { @@ -1149,7 +1356,7 @@ "phpunit/php-file-iterator": "~1.3", "phpunit/php-text-template": "~1.2", "phpunit/php-token-stream": "~1.3", - "sebastian/environment": "~1.0", + "sebastian/environment": "^1.3.2", "sebastian/version": "~1.0" }, "require-dev": { @@ -1190,7 +1397,7 @@ "testing", "xunit" ], - "time": "2015-06-21 07:51:27" + "time": "2015-10-06 15:47:00" }, { "name": "phpunit/php-file-iterator", @@ -1282,16 +1489,16 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.6", + "version": "1.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "83fe1bdc5d47658b727595c14da140da92b3d66d" + "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/83fe1bdc5d47658b727595c14da140da92b3d66d", - "reference": "83fe1bdc5d47658b727595c14da140da92b3d66d", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b", + "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b", "shasum": "" }, "require": { @@ -1319,7 +1526,7 @@ "keywords": [ "timer" ], - "time": "2015-06-13 07:35:30" + "time": "2015-06-21 08:01:12" }, { "name": "phpunit/php-token-stream", @@ -1327,12 +1534,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "7a9b0969488c3c54fd62b4d504b3ec758fd005d9" + "reference": "cab6c6fefee93d7b7c3a01292a0fe0884ea66644" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/7a9b0969488c3c54fd62b4d504b3ec758fd005d9", - "reference": "7a9b0969488c3c54fd62b4d504b3ec758fd005d9", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/cab6c6fefee93d7b7c3a01292a0fe0884ea66644", + "reference": "cab6c6fefee93d7b7c3a01292a0fe0884ea66644", "shasum": "" }, "require": { @@ -1368,7 +1575,7 @@ "keywords": [ "tokenizer" ], - "time": "2015-06-19 03:43:16" + "time": "2015-09-23 14:46:55" }, { "name": "phpunit/phpunit", @@ -1444,22 +1651,23 @@ }, { "name": "phpunit/phpunit-mock-objects", - "version": "dev-master", + "version": "2.3.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "a1304c50e6982bf6d1fa5131ee276bbc6229d80b" + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a1304c50e6982bf6d1fa5131ee276bbc6229d80b", - "reference": "a1304c50e6982bf6d1fa5131ee276bbc6229d80b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", "shasum": "" }, "require": { - "doctrine/instantiator": "~1.0,>=1.0.2", + "doctrine/instantiator": "^1.0.2", "php": ">=5.3.3", - "phpunit/php-text-template": "~1.2" + "phpunit/php-text-template": "~1.2", + "sebastian/exporter": "~1.2" }, "require-dev": { "phpunit/phpunit": "~4.4" @@ -1495,7 +1703,7 @@ "mock", "xunit" ], - "time": "2015-06-21 08:04:07" + "time": "2015-10-02 06:51:40" }, { "name": "sebastian/comparator", @@ -1503,12 +1711,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "abd05e77d364138c0532a1b78a13c4d78311f546" + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/abd05e77d364138c0532a1b78a13c4d78311f546", - "reference": "abd05e77d364138c0532a1b78a13c4d78311f546", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", "shasum": "" }, "require": { @@ -1522,7 +1730,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -1559,7 +1767,7 @@ "compare", "equality" ], - "time": "2015-06-21 07:54:35" + "time": "2015-07-26 15:48:44" }, { "name": "sebastian/diff", @@ -1567,24 +1775,24 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "6899b3e33bfbd386d88b5eea5f65f563e8793051" + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/6899b3e33bfbd386d88b5eea5f65f563e8793051", - "reference": "6899b3e33bfbd386d88b5eea5f65f563e8793051", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "phpunit/phpunit": "~4.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.4-dev" } }, "autoload": { @@ -1607,11 +1815,11 @@ } ], "description": "Diff implementation", - "homepage": "http://www.github.com/sebastianbergmann/diff", + "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ "diff" ], - "time": "2015-06-22 14:15:55" + "time": "2015-12-08 07:14:41" }, { "name": "sebastian/environment", @@ -1619,12 +1827,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "bc66c3bbe6e5822c34395049e110566491cafa3b" + "reference": "6e7133793a8e5a5714a551a8324337374be209df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/bc66c3bbe6e5822c34395049e110566491cafa3b", - "reference": "bc66c3bbe6e5822c34395049e110566491cafa3b", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e7133793a8e5a5714a551a8324337374be209df", + "reference": "6e7133793a8e5a5714a551a8324337374be209df", "shasum": "" }, "require": { @@ -1661,7 +1869,7 @@ "environment", "hhvm" ], - "time": "2015-06-21 13:07:52" + "time": "2015-12-02 08:37:27" }, { "name": "sebastian/exporter", @@ -1669,12 +1877,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "7ae5513327cb536431847bcc0c10edba2701064e" + "reference": "f88f8936517d54ae6d589166810877fb2015d0a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", - "reference": "7ae5513327cb536431847bcc0c10edba2701064e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f88f8936517d54ae6d589166810877fb2015d0a2", + "reference": "f88f8936517d54ae6d589166810877fb2015d0a2", "shasum": "" }, "require": { @@ -1682,12 +1890,13 @@ "sebastian/recursion-context": "~1.0" }, "require-dev": { + "ext-mbstring": "*", "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.3.x-dev" } }, "autoload": { @@ -1727,20 +1936,20 @@ "export", "exporter" ], - "time": "2015-06-21 07:55:53" + "time": "2015-08-09 04:23:41" }, { "name": "sebastian/global-state", - "version": "dev-master", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "23af31f402993cfd94e99cbc4b782e9a78eb0e97" + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23af31f402993cfd94e99cbc4b782e9a78eb0e97", - "reference": "23af31f402993cfd94e99cbc4b782e9a78eb0e97", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", "shasum": "" }, "require": { @@ -1778,7 +1987,7 @@ "keywords": [ "global state" ], - "time": "2015-06-21 15:11:22" + "time": "2015-10-12 03:26:01" }, { "name": "sebastian/recursion-context", @@ -1786,12 +1995,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "994d4a811bafe801fb06dccbee797863ba2792ba" + "reference": "913401df809e99e4f47b27cdd781f4a258d58791" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/994d4a811bafe801fb06dccbee797863ba2792ba", - "reference": "994d4a811bafe801fb06dccbee797863ba2792ba", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", + "reference": "913401df809e99e4f47b27cdd781f4a258d58791", "shasum": "" }, "require": { @@ -1831,7 +2040,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2015-06-21 08:04:50" + "time": "2015-11-11 19:50:13" }, { "name": "sebastian/version", @@ -1869,35 +2078,112 @@ "time": "2015-06-21 13:59:46" }, { - "name": "symfony/yaml", - "version": "2.8.x-dev", + "name": "squizlabs/php_codesniffer", + "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "f248a72777f3fec2bcafdce3ccd94086250448e1" + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "8e126925fd3e5d896ec574567af49150fc7e46b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/f248a72777f3fec2bcafdce3ccd94086250448e1", - "reference": "f248a72777f3fec2bcafdce3ccd94086250448e1", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/8e126925fd3e5d896ec574567af49150fc7e46b1", + "reference": "8e126925fd3e5d896ec574567af49150fc7e46b1", "shasum": "" }, "require": { - "php": ">=5.3.9" + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.1.2" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7|~3.0.0" + "phpunit/phpunit": "~4.0" + }, + "bin": [ + "scripts/phpcs", + "scripts/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "classmap": [ + "CodeSniffer.php", + "CodeSniffer/CLI.php", + "CodeSniffer/Exception.php", + "CodeSniffer/File.php", + "CodeSniffer/Fixer.php", + "CodeSniffer/Report.php", + "CodeSniffer/Reporting.php", + "CodeSniffer/Sniff.php", + "CodeSniffer/Tokens.php", + "CodeSniffer/Reports/", + "CodeSniffer/Tokenizers/", + "CodeSniffer/DocGenerators/", + "CodeSniffer/Standards/AbstractPatternSniff.php", + "CodeSniffer/Standards/AbstractScopeSniff.php", + "CodeSniffer/Standards/AbstractVariableSniff.php", + "CodeSniffer/Standards/IncorrectPatternException.php", + "CodeSniffer/Standards/Generic/Sniffs/", + "CodeSniffer/Standards/MySource/Sniffs/", + "CodeSniffer/Standards/PEAR/Sniffs/", + "CodeSniffer/Standards/PSR1/Sniffs/", + "CodeSniffer/Standards/PSR2/Sniffs/", + "CodeSniffer/Standards/Squiz/Sniffs/", + "CodeSniffer/Standards/Zend/Sniffs/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "http://www.squizlabs.com/php-codesniffer", + "keywords": [ + "phpcs", + "standards" + ], + "time": "2016-01-13 23:15:29" + }, + { + "name": "symfony/yaml", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "82287b794641153ae1837b9288c3ae90cecdf5c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/82287b794641153ae1837b9288c3ae90cecdf5c4", + "reference": "82287b794641153ae1837b9288c3ae90cecdf5c4", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.1-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1915,12 +2201,83 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2015-06-19 15:09:14" + "time": "2016-01-13 16:23:43" + }, + { + "name": "yiisoft/yii2-coding-standards", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-coding-standards.git", + "reference": "5c4fa33f645927f705b5f1fab7ce836f80eb71fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-coding-standards/zipball/5c4fa33f645927f705b5f1fab7ce836f80eb71fc", + "reference": "5c4fa33f645927f705b5f1fab7ce836f80eb71fc", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com", + "homepage": "http://www.yiiframework.com/", + "role": "Founder and project lead" + }, + { + "name": "Alexander Makarov", + "email": "sam@rmcreative.ru", + "homepage": "http://rmcreative.ru/", + "role": "Core framework development" + }, + { + "name": "Maurizio Domba", + "homepage": "http://mdomba.info/", + "role": "Core framework development" + }, + { + "name": "Carsten Brandt", + "email": "mail@cebe.cc", + "homepage": "http://cebe.cc/", + "role": "Core framework development" + }, + { + "name": "Timur Ruziev", + "email": "resurtm@gmail.com", + "homepage": "http://resurtm.com/", + "role": "Core framework development" + }, + { + "name": "Paul Klimov", + "email": "klimov.paul@gmail.com", + "role": "Core framework development" + } + ], + "description": "Yii PHP Framework Version 2 - Coding standard tools", + "homepage": "http://www.yiiframework.com/", + "keywords": [ + "codesniffer", + "framework", + "yii" + ], + "time": "2015-04-03 06:38:48" } ], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": { + "fzaninotto/faker": 20, + "flow/jsonpath": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/ruleset.xml b/ruleset.xml new file mode 100644 index 0000000..58316ed --- /dev/null +++ b/ruleset.xml @@ -0,0 +1,63 @@ + + + UrbanIndo Coding Standard + + */tests/* + */test/* + */data/* + */config/* + */views/* + */migrations/* + */messages/id/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */migrations/* + + + */migrations/* + + + \ No newline at end of file diff --git a/src/Behaviors/ActiveRecordDeferredEventBehavior.php b/src/Behaviors/ActiveRecordDeferredEventBehavior.php index 8046c16..8292cdb 100644 --- a/src/Behaviors/ActiveRecordDeferredEventBehavior.php +++ b/src/Behaviors/ActiveRecordDeferredEventBehavior.php @@ -1,5 +1,4 @@ @@ -13,19 +12,23 @@ /** * ActiveRecordDeferredEventBehavior is deferred event behavior handler for * ActiveRecord. - * + * * Due to SuperClosure limitation to serialize classes like PDO, this will * only pass the class, primary key, or attributes to the closure. The closure * then will operate on the object that refetched from the database from primary * key or object whose attribute repopulated in case of EVENT_AFTER_DELETE. - * + * * @property-read ActiveRecord $owner the owner. - * + * * @author Petra Barus * @since 2015.02.25 */ -class ActiveRecordDeferredEventBehavior extends DeferredEventBehavior { +class ActiveRecordDeferredEventBehavior extends DeferredEventBehavior +{ + /** + * @var array + */ public $events = [ ActiveRecord::EVENT_AFTER_INSERT, ActiveRecord::EVENT_AFTER_UPDATE, @@ -36,7 +39,8 @@ class ActiveRecordDeferredEventBehavior extends DeferredEventBehavior { * Default events that usually use deferred. * @return array */ - public static function getDefaultEvents() { + public static function getDefaultEvents() + { return [ ActiveRecord::EVENT_AFTER_INSERT, ActiveRecord::EVENT_AFTER_UPDATE, @@ -46,14 +50,17 @@ public static function getDefaultEvents() { /** * Call the behavior owner to handle the deferred event. - * + * * Since there is a limitation on the SuperClosure on PDO, the closure will * operate the object that is re-fetched from the database using primary key. * In the case of the after delete, since the row is already deleted from - * the table, the closure will operate from the object whose attributes - * @param \yii\base\Event $event + * the table, the closure will operate from the object whose attributes. + * @param \yii\base\Event $event The event. + * @return void + * @throws \Exception Exception. */ - public function postDeferredEvent($event) { + public function postDeferredEvent(\yii\base\Event $event) + { $class = get_class($this->owner); $eventName = $event->name; $handlers = ($this->_hasEventHandlers) ? $this->events : false; @@ -66,7 +73,7 @@ public function postDeferredEvent($event) { if ($eventName == ActiveRecord::EVENT_AFTER_DELETE) { $attributes = $this->owner->getAttributes(); $this->queue->post(new \UrbanIndo\Yii2\Queue\Job([ - 'route' => function() use ($class, $attributes, $handlers, $eventName, $serializer, $scenario) { + 'route' => function () use ($class, $attributes, $handlers, $eventName, $serializer, $scenario) { $object = \Yii::createObject($class); /* @var $object ActiveRecord */ $object->scenario = $scenario; @@ -87,14 +94,14 @@ public function postDeferredEvent($event) { /* @var $object DeferredEventInterface */ return $object->handleDeferredEvent($eventName); } else { - throw new \Exception("Model is not instance of DeferredEventInterface"); + throw new \Exception('Model is not instance of DeferredEventInterface'); } } ])); } else { $pk = $this->owner->getPrimaryKey(); $this->queue->post(new \UrbanIndo\Yii2\Queue\Job([ - 'route' => function() use ($class, $pk, $handlers, $eventName, $serializer, $scenario) { + 'route' => function () use ($class, $pk, $handlers, $eventName, $serializer, $scenario) { $object = $class::findOne($pk); if ($object === null) { throw new \Exception("Model #{$pk} is not found"); @@ -116,11 +123,10 @@ public function postDeferredEvent($event) { /* @var $object DeferredEventInterface */ return $object->handleDeferredEvent($eventName); } else { - throw new \Exception("Model is not instance of DeferredEventInterface"); + throw new \Exception('Model is not instance of DeferredEventInterface'); } } ])); } } - } diff --git a/src/Behaviors/ActiveRecordDeferredEventHandler.php b/src/Behaviors/ActiveRecordDeferredEventHandler.php index c41d6b5..4279d94 100644 --- a/src/Behaviors/ActiveRecordDeferredEventHandler.php +++ b/src/Behaviors/ActiveRecordDeferredEventHandler.php @@ -1,5 +1,4 @@ @@ -12,19 +11,26 @@ /** * DeferredActiveRecordEventHandler is deferred event behavior handler for * ActiveRecord. - * + * * Due to SuperClosure limitation to serialize classes like PDO, this will * only pass the class, primary key, or attributes to the closure. The closure * then will operate on the object that refetched from the database from primary * key or object whose attribute repopulated in case of EVENT_AFTER_DELETE. - * + * * @property-read ActiveRecord $owner the owner. - * + * * @author Petra Barus */ -abstract class ActiveRecordDeferredEventHandler extends DeferredEventHandler { +abstract class ActiveRecordDeferredEventHandler extends DeferredEventHandler +{ - public function deferEvent($event) { + /** + * @param \yii\base\Event $event The event to handle. + * @return void + * @throws \Exception Exception. + */ + public function deferEvent(\yii\base\Event $event) + { $class = get_class($this->owner); $pk = $this->owner->getPrimaryKey(); $attributes = $this->owner->getAttributes(); @@ -37,20 +43,21 @@ public function deferEvent($event) { /* @var $queue Queue */ if ($eventName == ActiveRecord::EVENT_AFTER_DELETE) { $queue->post(new \UrbanIndo\Yii2\Queue\Job([ - 'route' => function() use ($class, $pk, $attributes, $handler, $eventName, $scenario) { + 'route' => function () use ($class, $pk, $attributes, $handler, $eventName, $scenario) { $object = \Yii::createObject($class); /* @var $object ActiveRecord */ $object->setAttributes($attributes, false); $object->scenario = $scenario; $handler->handleEvent($object); } - ])); + ])); + } else { $queue->post(new \UrbanIndo\Yii2\Queue\Job([ - 'route' => function() use ($class, $pk, $attributes, $handler, $eventName, $scenario) { + 'route' => function () use ($class, $pk, $attributes, $handler, $eventName, $scenario) { $object = $class::findOne($pk); if ($object === null) { - throw new \Exception("Model is not found"); + throw new \Exception('Model is not found'); } $object->scenario = $scenario; /* @var $object ActiveRecord */ diff --git a/src/Behaviors/ActiveRecordDeferredEventRoutingBehavior.php b/src/Behaviors/ActiveRecordDeferredEventRoutingBehavior.php index a587d73..159cec1 100644 --- a/src/Behaviors/ActiveRecordDeferredEventRoutingBehavior.php +++ b/src/Behaviors/ActiveRecordDeferredEventRoutingBehavior.php @@ -1,7 +1,7 @@ * @since 2015.02.25 */ @@ -13,17 +13,18 @@ /** * ActiveRecordDeferredRoutingBehavior provides matching between controller in * task worker with the appropriate event. - * + * * @property-read ActiveRecord $owner the owner. - * + * * @author Petra Barus * @since 2015.02.25 */ -class ActiveRecordDeferredEventRoutingBehavior extends DeferredEventRoutingBehavior { +class ActiveRecordDeferredEventRoutingBehavior extends DeferredEventRoutingBehavior +{ /** * The attribute name. - * @var type + * @var string */ public $pkAttribute = 'id'; @@ -33,7 +34,12 @@ class ActiveRecordDeferredEventRoutingBehavior extends DeferredEventRoutingBehav */ public $addPkToData = true; - public function routeEvent($event) { + /** + * @param \yii\base\Event $event The event to handle. + * @return void + */ + public function routeEvent(\yii\base\Event $event) + { /* @var $owner ActiveRecord */ $eventName = $event->name; diff --git a/src/Behaviors/DeferredEventBehavior.php b/src/Behaviors/DeferredEventBehavior.php index b4ad46a..3e51791 100644 --- a/src/Behaviors/DeferredEventBehavior.php +++ b/src/Behaviors/DeferredEventBehavior.php @@ -1,74 +1,73 @@ * @since 2015.02.25 */ namespace UrbanIndo\Yii2\Queue\Behaviors; -use Yii; -use Exception; +use UrbanIndo\Yii2\Queue\Queue; /** * DeferredEventBehavior post a deferred code on event call. - * + * * To use this, attach the behavior on the model, and implements the * DeferredEventInterface. - * + * * NOTE: Due to some limitation on the superclosure, the model shouldn't have * unserializable class instances such as PDO etc. - * + * * @property-read DeferredEventInterface $owner the owner of this behavior. - * + * * @author Petra Barus * @since 2015.02.25 */ -class DeferredEventBehavior extends \yii\base\Behavior { +class DeferredEventBehavior extends \yii\base\Behavior +{ /** * The queue that post the deferred event. - * @var \UrbanIndo\Yii2\Queue\Queue + * @var string|array|Queue */ public $queue = 'queue'; /** * List events that handled by the behavior. - * - * This has two formats. The first one is "index", - * + * + * This has two formats. The first one is "index", + * * [self::EVENT_AFTER_SAVE, EVENT_AFTER_VALIDATE]] - * - * and the second one is "key=>value". e.g. - * + * + * and the second one is "key=>value". e.g. + * * [ - * self::EVENT_AFTER_SAVE => 'deferAfterSave', + * self::EVENT_AFTER_SAVE => 'deferAfterSave', * self::EVENT_AFTER_VALIDATE => 'deferAfterValidate' * ] - * + * * For the first one, the object should implement DeferredEventInterface. * As for the second one, the handler will use the respective method of the * event. - * + * * e.g. - * + * * [ - * self::EVENT_AFTER_SAVE => 'deferAfterSave', + * self::EVENT_AFTER_SAVE => 'deferAfterSave', * self::EVENT_AFTER_VALIDATE => 'deferAfterValidate' * ] - * + * * the model should implement - * + * * public function deferAfterSave(){ * } - * + * * Note that the method doesn't receive $event just like any event handler. * This is because the $event object can be too large for the queue. * Also note that object that run the method is a clone. - * - * @var type + * + * @var array */ public $events = []; @@ -80,7 +79,7 @@ class DeferredEventBehavior extends \yii\base\Behavior { /** * Whether has serialized event.handler. - * @var \SuperClosure\Serializer + * @var \SuperClosure\Serializer */ protected $_serializer; @@ -88,29 +87,31 @@ class DeferredEventBehavior extends \yii\base\Behavior { * Declares event handlers for the [[owner]]'s events. * @return array */ - public function events() { + public function events() + { parent::events(); if (!$this->_hasEventHandlers) { return array_fill_keys($this->events, 'postDeferredEvent'); } else { - return array_fill_keys(array_keys($this->events), - 'postDeferredEvent'); + return array_fill_keys( + array_keys($this->events), + 'postDeferredEvent' + ); } } /** * Initialize the queue. - * @throws \Exception + * @return void */ - public function init() { + public function init() + { parent::init(); - $queueName = $this->queue; - $this->queue = Yii::$app->get($queueName); - if (!$this->queue instanceof \UrbanIndo\Yii2\Queue\Queue) { - throw new \Exception("Can not found queue component named '{$queueName}'"); - } - $this->_hasEventHandlers = !\yii\helpers\ArrayHelper::isIndexed($this->events, - true); + $this->queue = \yii\di\Instance::ensure($this->queue, Queue::className()); + $this->_hasEventHandlers = !\yii\helpers\ArrayHelper::isIndexed( + $this->events, + true + ); if ($this->_hasEventHandlers) { foreach ($this->events as $attr => $handler) { if (is_callable($handler)) { @@ -125,12 +126,15 @@ public function init() { /** * Call the behavior owner to handle the deferred event. - * @param \yii\base\Event $event + * @param \yii\base\Event $event The event to process. + * @return void + * @throws \Exception When the sender is not DeferredEventInterface. */ - public function postDeferredEvent($event) { + public function postDeferredEvent(\yii\base\Event $event) + { $object = clone $this->owner; if (!$this->_hasEventHandlers && !$object instanceof DeferredEventInterface) { - throw new \Exception("Model is not instance of DeferredEventInterface"); + throw new \Exception('Model is not instance of DeferredEventInterface'); } $handlers = ($this->_hasEventHandlers) ? $this->events : false; $eventName = $event->name; @@ -140,7 +144,7 @@ public function postDeferredEvent($event) { $serializer = null; } $this->queue->post(new \UrbanIndo\Yii2\Queue\Job([ - 'route' => function() use ($object, $eventName, $handlers, $serializer) { + 'route' => function () use ($object, $eventName, $handlers, $serializer) { if ($handlers) { $handler = $handlers[$eventName]; if ($serializer !== null) { @@ -157,10 +161,11 @@ public function postDeferredEvent($event) { /* @var $object DeferredEventInterface */ return $object->handleDeferredEvent($eventName); } else { - throw new \Exception("Model doesn't have handlers for the event or is not instance of DeferredEventInterface"); + throw new \Exception( + "Model doesn't have handlers for the event or is not instance of DeferredEventInterface" + ); } } ])); } - } diff --git a/src/Behaviors/DeferredEventHandler.php b/src/Behaviors/DeferredEventHandler.php index a0eacf9..47dd7af 100644 --- a/src/Behaviors/DeferredEventHandler.php +++ b/src/Behaviors/DeferredEventHandler.php @@ -1,21 +1,23 @@ */ + namespace UrbanIndo\Yii2\Queue\Behaviors; use Yii; +use UrbanIndo\Yii2\Queue\Queue; /** * DeferredEventHandler handles the event inside the behavior instance, instead * of inside the model. - * + * * @author Petra Barus */ -abstract class DeferredEventHandler extends \yii\base\Behavior { +abstract class DeferredEventHandler extends \yii\base\Behavior +{ /** * The queue that post the deferred event. @@ -25,27 +27,36 @@ abstract class DeferredEventHandler extends \yii\base\Behavior { /** * Declares the events of the object that is being handled. - * + * * @var array */ public $events = []; - public function init() { - $queueName = $this->queue; - $this->queue = Yii::$app->get($queueName); - if (!$this->queue instanceof \UrbanIndo\Yii2\Queue\Queue) { - throw new \Exception("Can not found queue component named '{$queueName}'"); - } + /** + * @return void + */ + public function init() + { + parent::init(); + $this->queue = \yii\di\Instance::ensure($this->queue, Queue::className()); } /** * @inheritdoc + * @return array */ - public function events() { + public function events() + { return array_fill_keys($this->events, 'deferEvent'); } - public function deferEvent($event) { + /** + * @param \yii\base\Event $event The event to handle. + * @return array + */ + public function deferEvent(\yii\base\Event $event) + { + $event; //unused $owner = clone $this->owner; $queue = $this->queue; $handler = clone $this; @@ -53,12 +64,16 @@ public function deferEvent($event) { $handler->owner = null; /* @var $queue Queue */ $queue->post(new \UrbanIndo\Yii2\Queue\Job([ - 'route' => function() use ($owner, $handler) { + 'route' => function () use ($owner, $handler) { return $handler->handleEvent($owner); } ])); } + /** + * Handle event. + * @param mixed $owner The owner of the behavior. + * @return void + */ abstract public function handleEvent($owner); - } diff --git a/src/Behaviors/DeferredEventInterface.php b/src/Behaviors/DeferredEventInterface.php index 6725fcd..e350431 100644 --- a/src/Behaviors/DeferredEventInterface.php +++ b/src/Behaviors/DeferredEventInterface.php @@ -1,8 +1,7 @@ * @since 2015.02.25 */ @@ -12,14 +11,16 @@ /** * DeferredEventInterface provides method interface for handling the deferred * event. - * + * * @author Petra Barus * @since 2015.02.25 */ -interface DeferredEventInterface { +interface DeferredEventInterface +{ /** - * @param string $event the name of the event. + * @param string $eventName The name of the event. + * @return void */ public function handleDeferredEvent($eventName); } diff --git a/src/Behaviors/DeferredEventRoutingBehavior.php b/src/Behaviors/DeferredEventRoutingBehavior.php index 37b0bef..6775562 100644 --- a/src/Behaviors/DeferredEventRoutingBehavior.php +++ b/src/Behaviors/DeferredEventRoutingBehavior.php @@ -1,5 +1,4 @@ @@ -8,39 +7,40 @@ namespace UrbanIndo\Yii2\Queue\Behaviors; -use Yii; use yii\db\ActiveRecord; +use UrbanIndo\Yii2\Queue\Queue; /** * DeferredEventRoutingBehavior provides matching between controller in * task worker with the appropriate event. - * + * * @property-read ActiveRecord $owner the owner. - * + * * @author Petra Barus * @since 2015.02.25 */ -class DeferredEventRoutingBehavior extends \yii\base\Behavior { +class DeferredEventRoutingBehavior extends \yii\base\Behavior +{ /** * The queue that post the deferred event. - * @var \UrbanIndo\Yii2\Queue\Queue + * @var string|array|Queue */ public $queue = 'queue'; /** * List events that handler and the appropriate routing. The routing can be * generated via callable or array. - * + * * e.g. - * + * * [ - * self::EVENT_AFTER_SAVE => ['test/index'], + * self::EVENT_AFTER_SAVE => ['test/index'], * self::EVENT_AFTER_VALIDATE => ['test/index'] * ] - * + * * or - * + * * [ * self::EVENT_AFTER_SAVE => function($model) { * return ['test/index', 'id' => $model->id]; @@ -48,35 +48,38 @@ class DeferredEventRoutingBehavior extends \yii\base\Behavior { * self::EVENT_AFTER_VALIDATE => function($model) { * return ['test/index', 'id' => $model->id]; * } - * ] - * - * @var type + * ] + * + * @var array */ public $events = []; /** * Initialize the queue. - * @throws \Exception + * @return void */ - public function init() { + public function init() + { parent::init(); - $queueName = $this->queue; - $this->queue = Yii::$app->get($queueName); - if (!$this->queue instanceof \UrbanIndo\Yii2\Queue\Queue) { - throw new \Exception("Can not found queue component named '{$queueName}'"); - } + $this->queue = \yii\di\Instance::ensure($this->queue, Queue::className()); } /** * Declares event handlers for the [[owner]]'s events. * @return array */ - public function events() { + public function events() + { parent::events(); return array_fill_keys(array_keys($this->events), 'routeEvent'); } - public function routeEvent($event) { + /** + * @param \yii\base\Event $event The event to handle. + * @return void + */ + public function routeEvent(\yii\base\Event $event) + { $eventName = $event->name; $handler = $this->events[$eventName]; if (is_callable($handler)) { diff --git a/src/Behaviors/DeferredEventTrait.php b/src/Behaviors/DeferredEventTrait.php index 81f808f..4b79c5d 100644 --- a/src/Behaviors/DeferredEventTrait.php +++ b/src/Behaviors/DeferredEventTrait.php @@ -1,5 +1,4 @@ @@ -8,36 +7,44 @@ namespace UrbanIndo\Yii2\Queue\Behaviors; +use UrbanIndo\Yii2\Queue\Job; +use UrbanIndo\Yii2\Queue\Queue; + /** * ActiveRecordDeferredEventBehavior is deferred event function for active record. - * + * * Due to SuperClosure limitation to serialize classes like PDO, this will * only pass the class, primary key. - * + * * @author Petra Barus * @since 2015.06.12 */ -trait DeferredEventTrait { +trait DeferredEventTrait +{ /** - * @return \UrbanIndo\Yii2\Queue\Queue + * @return Queue */ - public function getQueue() { + public function getQueue() + { return \Yii::$app->queue; } /** - * Defer event - * + * Defer event. + * * To use this, attach the behavior and call - * + * * $model->deferAction(function($model) { * $model->doSomething(); * }); - * @param callable $callback + * + * @param \Closure $callback The callback. + * @return void */ - public function deferAction($callback) { + public function deferAction(\Closure $callback) + { if ($this instanceof ActiveRecord) { $job = $this->deferActiveRecordAction($callback); } else if ($this instanceof \yii\base\Model) { @@ -49,18 +56,28 @@ public function deferAction($callback) { $queue->post($job); } - private function serializeCallback($callback) { + /** + * @param \Closure $callback The callback. + * @return array + */ + private function serializeCallback(\Closure $callback) + { $serializer = new \SuperClosure\Serializer(); $serialized = $serializer->serialize($callback); return [$serializer, $serialized]; } - private function deferActiveRecordAction($callback) { + /** + * @param \Closure $callback The callback. + * @return array + */ + private function deferActiveRecordAction(\Closure $callback) + { $class = get_class($this); $pk = $this->getPrimaryKey(); list($serializer, $serialized) = $this->serializeCallback($callback); - return new \UrbanIndo\Yii2\Queue\Job([ - 'route' => function() use ($class, $pk, $serialized, $serializer) { + return new Job([ + 'route' => function () use ($class, $pk, $serialized, $serializer) { $model = $class::findOne($pk); $unserialized = $serializer->unserialize($serialized); call_user_func($unserialized, $model); @@ -68,12 +85,17 @@ private function deferActiveRecordAction($callback) { ]); } - private function deferModelAction($callback) { + /** + * @param \Closure $callback The callback to defer. + * @return Job + */ + private function deferModelAction(\Closure $callback) + { $class = get_class($this); $attributes = $this->getAttributes(); list($serializer, $serialized) = $this->serializeCallback($callback); return new \UrbanIndo\Yii2\Queue\Job([ - 'route' => function() use ($class, $attributes, $serialized, $serializer) { + 'route' => function () use ($class, $attributes, $serialized, $serializer) { $model = new $class; $model->setAttributes($attributes, false); $unserialized = $serializer->unserialize($serialized); @@ -82,15 +104,19 @@ private function deferModelAction($callback) { ]); } - private function deferObject($callback) { + /** + * @param \Closure $callback The callback. + * @return Job + */ + private function deferObject(\Closure $callback) + { $object = $this; list($serializer, $serialized) = $this->serializeCallback($callback); return new \UrbanIndo\Yii2\Queue\Job([ - 'route' => function() use ($object, $serialized, $serializer) { + 'route' => function () use ($object, $serialized, $serializer) { $unserialized = $serializer->unserialize($serialized); call_user_func($unserialized, $object); } ]); } - } diff --git a/src/Console/Controller.php b/src/Console/Controller.php index 84af830..507b25d 100644 --- a/src/Console/Controller.php +++ b/src/Console/Controller.php @@ -1,8 +1,7 @@ * @since 2015.02.24 */ @@ -10,81 +9,81 @@ namespace UrbanIndo\Yii2\Queue\Console; use UrbanIndo\Yii2\Queue\Job; +use UrbanIndo\Yii2\Queue\Queue; /** * QueueController handles console command for running the queue. - * + * * To use the controller, update the controllerMap. - * + * * return [ * // ... * 'controllerMap' => [ * 'queue' => 'UrbanIndo\Yii2\Queue\Console\QueueController' * ], * ]; - * + * * To run - * + * * yii queue * * @author Petra Barus * @since 2015.02.24 */ -class Controller extends \yii\console\Controller { +class Controller extends \yii\console\Controller +{ /** - * @var string the name of the queue component. default to 'queue'. + * @var string|array|Queue the name of the queue component. default to 'queue'. */ public $queue = 'queue'; - /* + /** * @var string the name of the command. */ private $_name = 'queue'; - + /** - * @var \UrbanIndo\Yii2\Queue\Queue stores the queue component. + * @return void */ - private $_queue = null; + public function init() + { + parent::init(); + $this->queue = \yii\di\Instance::ensure($this->queue, Queue::className()); + } /** * @inheritdoc + * @param string $actionID The action id of the current request. + * @return array the names of the options valid for the action */ - public function options($actionID) { - return array_merge(parent::options($actionID), - [ + public function options($actionID) + { + return array_merge(parent::options($actionID), [ 'queue' ]); } - /** - * Returns the queue component. - * - * @return \UrbanIndo\Yii2\Queue\Queue - */ - protected function getQueue() { - if (!isset($this->_queue)) { - $this->_queue = \Yii::$app->get($this->queue); - } - return $this->_queue; - } - /** * Returns the script path. * @return string */ - protected function getScriptPath() { + protected function getScriptPath() + { return getcwd() . DIRECTORY_SEPARATOR . $_SERVER['argv'][0]; } /** * This will continuously run new subprocesses to fetch job from the queue. - * - * @param string $cwd the working directory. - * @param integer $timeout timeout. - * @param array $env the environment to passed to the sub process. The format for each element is 'KEY=VAL' + * + * @param string $cwd The working directory. + * @param integer $timeout Timeout. + * @param array $env The environment to passed to the sub process. + * The format for each element is 'KEY=VAL'. + * @return void */ - public function actionListen($cwd = null, $timeout = null, $env = []) { + public function actionListen($cwd = null, $timeout = null, $env = []) + { $this->stdout("Listening to queue...\n"); $this->initSignalHandler(); $command = PHP_BINARY . " {$this->getScriptPath()} {$this->_name}/run"; @@ -93,20 +92,30 @@ public function actionListen($cwd = null, $timeout = null, $env = []) { $this->stdout("Running new process...\n"); $this->runQueueFetching($command, $cwd, $timeout, $env); } - $this->stdout("Exiting..."); + $this->stdout("Exiting...\n"); } /** * Run the queue fetching process. - * @param string $command the command. - * @param string $cwd the working directory - * @param integer $timeout the timeout - * @param array $env the environment to be passed. - */ - protected function runQueueFetching($command, $cwd = null, $timeout = null, - $env = []) { - $process = new \Symfony\Component\Process\Process($command, - isset($cwd) ? $cwd : getcwd(), $env, null, $timeout); + * @param string $command The command. + * @param string $cwd The working directory. + * @param integer $timeout The timeout. + * @param array $env The environment to be passed. + * @return void + */ + protected function runQueueFetching( + $command, + $cwd = null, + $timeout = null, + array $env = [] + ) { + $process = new \Symfony\Component\Process\Process( + $command, + isset($cwd) ? $cwd : getcwd(), + $env, + null, + $timeout + ); $process->setTimeout($timeout); $process->setIdleTimeout(null); $process->run(); @@ -123,18 +132,20 @@ protected function runQueueFetching($command, $cwd = null, $timeout = null, /** * Initialize signal handler for the process. + * @return void */ - protected function initSignalHandler() { + protected function initSignalHandler() + { $signalHandler = function ($signal) { switch ($signal) { case SIGTERM: - $this->stderr("Caught SIGTERM"); + $this->stderr('Caught SIGTERM'); exit; case SIGKILL: - $this->stderr("Caught SIGKILL"); + $this->stderr('Caught SIGKILL'); exit; case SIGINT: - $this->stderr("Caught SIGINT"); + $this->stderr('Caught SIGINT'); exit; } }; @@ -144,13 +155,14 @@ protected function initSignalHandler() { /** * Fetch a job from the queue. + * @return void */ - public function actionRun() { - $queue = $this->getQueue(); - $job = $queue->fetch(); + public function actionRun() + { + $job = $this->queue->fetch(); if ($job !== false) { $this->stdout("Running job #: {$job->id}" . PHP_EOL); - $queue->run($job); + $this->queue->run($job); } else { $this->stdout("No job\n"); } @@ -158,31 +170,39 @@ public function actionRun() { /** * Post a job to the queue. - * @param string $route the route. - * @param string $data the data in JSON format. + * @param string $route The route. + * @param string $data The data in JSON format. + * @return void */ - public function actionPost($route, $data = '{}') { + public function actionPost($route, $data = '{}') + { $this->stdout("Posting job to queue...\n"); $job = $this->createJob($route, $data); - $this->getQueue()->post($job); + $this->queue->post($job); } /** * Run a task without going to queue. - * + * * This is useful to test the task controller. - * - * @param string $route the route. - * @param string $data the data in JSON format. + * + * @param string $route The route. + * @param string $data The data in JSON format. + * @return void */ - public function actionRunTask($route, $data = '{}') { - $this->stdout("Running task queue..."); + public function actionRunTask($route, $data = '{}') + { + $this->stdout('Running task queue...'); $job = $this->createJob($route, $data); - $this->getQueue()->run($job); + $this->queue->run($job); } - public function actionTest() { - $this->getQueue()->post(new Job([ + /** + * @return void + */ + public function actionTest() + { + $this->queue->post(new Job([ 'route' => 'test/test', 'data' => ['halohalo' => 10, 'test2' => 100], ])); @@ -190,12 +210,13 @@ public function actionTest() { /** * Create a job from route and data. - * - * @param string $route the route. - * @param string $data the JSON data. + * + * @param string $route The route. + * @param string $data The JSON data. * @return Job */ - protected function createJob($route, $data = '{}') { + protected function createJob($route, $data = '{}') + { return new Job([ 'route' => $route, 'data' => \yii\helpers\Json::decode($data), @@ -204,14 +225,15 @@ protected function createJob($route, $data = '{}') { /** * Peek messages from queue that are still active. - * - * @param integer number of messages. + * + * @param integer $count Number of messages to peek. + * @return void */ - public function actionPeek($count = 1) { - $this->stdout("Peeking queue..."); - $queue = $this->getQueue(); + public function actionPeek($count = 1) + { + $this->stdout('Peeking queue...'); for ($i = 0; $i < $count; $i++) { - $job = $queue->fetch(); + $job = $this->queue->fetch(); if ($job !== false) { $this->stdout("Peeking job #: {$job->id}" . PHP_EOL); $this->stdout(\yii\helpers\Json::encode($job)); @@ -221,12 +243,14 @@ public function actionPeek($count = 1) { /** * Purging messages from queue that are still active. - * - * @param integer number of messages. + * + * @param integer $count Number of messages to delete. + * @return void */ - public function actionPurge($count = 1) { - $this->stdout("Purging queue..."); - $queue = $this->getQueue(); + public function actionPurge($count = 1) + { + $this->stdout('Purging queue...'); + $queue = $this->queue; for ($i = 0; $i < $count; $i++) { $job = $queue->fetch(); if ($job !== false) { @@ -238,10 +262,11 @@ public function actionPurge($count = 1) { /** * Sets the name of the command. This should be overriden in the config. - * @param string $value the value. + * @param string $value The value. + * @return void */ - public function setName($value) { + public function setName($value) + { $this->_name = $value; } - } diff --git a/src/Event.php b/src/Event.php new file mode 100644 index 0000000..fc4c7b4 --- /dev/null +++ b/src/Event.php @@ -0,0 +1,27 @@ + + * @since 2016.01.16 + */ + +namespace UrbanIndo\Yii2\Queue; + +/** + * @author Petra Barus + * @since 2016.01.16 + */ +class Event extends \yii\base\Event +{ + + /** + * @var Job + */ + public $job; + + /** + * Whether the next process should continue or not. + * @var boolean + */ + public $isValid = true; +} diff --git a/src/Job.php b/src/Job.php index 86b4242..05e3eb8 100644 --- a/src/Job.php +++ b/src/Job.php @@ -1,8 +1,7 @@ * @since 2015.02.24 */ @@ -11,13 +10,21 @@ /** * Job is model for a job message. - * + * * @author Petra Barus * @since 2015.02.24 */ -class Job extends \yii\base\Object { +class Job extends \yii\base\Object +{ + /** + * When the job is regular job using routing. + */ const TYPE_REGULAR = 0; + + /** + * When the job contains closure. + */ const TYPE_CALLABLE = 1; /** @@ -29,7 +36,7 @@ class Job extends \yii\base\Object { /** * Stores the header. * This can be different for each queue provider. - * + * * @var header */ public $header = []; @@ -38,31 +45,36 @@ class Job extends \yii\base\Object { * The route for the job. * This can either be string that represents the controller/action or * a anonymous function that will be executed. - * - * @var mixed + * + * @var string|\Closure */ public $route; + + /** + * @var array + */ public $data = []; /** * whether the task is callable. * @return boolean */ - public function isCallable() { + public function isCallable() + { return is_callable($this->route); } /** * Run the callable task. - * + * * The callable should return true if the job is going to be deleted from * queue. - * + * * @return boolean */ - public function runCallable() { + public function runCallable() + { $return = call_user_func_array($this->route, $this->data); return $return !== false; } - } diff --git a/src/MultipleQueue.php b/src/MultipleQueue.php deleted file mode 100644 index 9f956c8..0000000 --- a/src/MultipleQueue.php +++ /dev/null @@ -1,101 +0,0 @@ - - * @since 2015.02.25 - */ - -namespace UrbanIndo\Yii2\Queue; - -use UrbanIndo\Yii2\Queue\Job; - -/** - * MultipleQueue is a queue abstraction that handles multiple queue at once. - * - * @author Petra Barus - * @since 2015.02.25 - */ -class MultipleQueue extends Queue { - - /** - * Stores the queue. - * @var Queue[] - */ - public $queues = []; - - /** - * The job fetching strategy. - * @var Strategies\Strategy - */ - public $strategy; - - /** - * Initialize the queue. - */ - public function init() { - parent::init(); - $queueObjects = []; - foreach ($this->queues as $id => $queue) { - $queueObjects[$id] = \Yii::createObject($queue); - } - $this->queues = $queueObjects; - if (!isset($this->strategy)) { - $this->strategy = [ - 'class' => Strategies\RandomStrategy::class, - ]; - } - $this->strategy = \Yii::createObject($this->strategy); - $this->strategy->setQueue($this); - } - - /** - * @param integer $index - * @return Queue|null the queue or null if not exists. - */ - public function getQueue($index) { - return \yii\helpers\ArrayHelper::getValue($this->queues, $index); - } - - /** - * Delete the job. - * @param Job $job - * @return boolean whether the operation succeed. - */ - public function delete(Job $job) { - return $this->strategy->delete($job); - } - - /** - * Return next job from the queue. - * @return Job - */ - public function fetch() { - return $this->strategy->fetch(); - } - - /** - * Post new job to the queue. - * @param Job $job the job. - * @return boolean whether operation succeed. - */ - public function post(Job &$job) { - return $this->postToQueue($job, 0); - } - - /** - * Post new job to a specific queue. - * @param Job $job the job. - * @param integer $index the queue index. - * @return boolean whether operation succeed. - */ - public function postToQueue(Job &$job, $index) { - $queue = $this->getQueue($index); - if ($queue === null) { - return false; - } - return $queue->post($job); - } - -} diff --git a/src/Queue.php b/src/Queue.php index c1987de..e0188c3 100644 --- a/src/Queue.php +++ b/src/Queue.php @@ -1,48 +1,87 @@ * @since 2015.02.24 */ namespace UrbanIndo\Yii2\Queue; +use Exception; + /** * Queue provides basic functionality for queue provider. * @author Petra Barus * @since 2015.02.24 */ -abstract class Queue extends \yii\base\Component { +abstract class Queue extends \yii\base\Component +{ + + /** + * Json serializer. + */ + const SERIALIZER_JSON = 'json'; + + /** + * PHP serializer. + */ + const SERIALIZER_PHP = 'php'; + + /** + * Event executed before a job is posted to the queue. + */ + const EVENT_BEFORE_POST = 'beforePost'; + + /** + * Event executed before a job is posted to the queue. + */ + const EVENT_AFTER_POST = 'afterPost'; + + /** + * Event executed before a job is being fetched from the queue. + */ + const EVENT_BEFORE_FETCH = 'beforeFetch'; + + /** + * Event executed after a job is being fetched from the queue. + */ + const EVENT_AFTER_FETCH = 'afterFetch'; + + /** + * Event executed before a job is being deleted from the queue. + */ + const EVENT_BEFORE_DELETE = 'beforeDelete'; + + /** + * Event executed after a job is being deleted from the queue. + */ + const EVENT_AFTER_DELETE = 'afterDelete'; /** * The module where the task is located. - * + * * To add the module, create a new module in the config * e.g. create a module named 'task'. - * + * * 'modules' => [ * 'task' => [ * 'class' => 'app\modules\task\Module', * ] * ] - * + * * and then add the module to the queue config. - * + * * 'components' => [ * 'queue' => [ * 'module' => 'task' * ] * ] + * * @var \yii\base\Module */ public $module; - const SERIALIZER_JSON = 'json'; - - const SERIALIZER_PHP = 'php'; - /** * Choose the serializer. * @var string @@ -51,31 +90,71 @@ abstract class Queue extends \yii\base\Component { /** * Initializes the module. + * @return void */ - public function init() { + public function init() + { parent::init(); $this->module = \Yii::$app->getModule($this->module); } /** - * Post new job to the queue. - * @param Job $job the job. - * @return boolean whether operation succeed. + * Post new job to the queue. This will trigger event EVENT_BEFORE_POST and + * EVENT_AFTER_POST. + * + * @param Job $job The job. + * @return boolean Whether operation succeed. + */ + public function post(Job &$job) + { + $this->trigger(self::EVENT_BEFORE_POST, $beforeEvent = new Event(['job' => $job])); + if (!$beforeEvent->isValid) { + return false; + } + + $return = $this->postJob($job); + if (!$return) { + return false; + } + + $this->trigger(self::EVENT_AFTER_POST, new Event(['job' => $job])); + return true; + } + + /** + * Post new job to the queue. Override this for queue implementation. + * + * @param Job $job The job. + * @return boolean Whether operation succeed. */ - public abstract function post(Job &$job); + abstract protected function postJob(Job &$job); /** - * Return next job from the queue. - * @return Job + * Return next job from the queue. This will trigger event EVENT_BEFORE_FETCH + * and event EVENT_AFTER_FETCH + * + * @return Job|boolean the job or false if not found. + */ + public function fetch() + { + return $this->fetchJob(); + } + + /** + * Return next job from the queue. Override this for queue implementation. + * @return Job|boolean the job or false if not found. */ - public abstract function fetch(); + abstract protected function fetchJob(); /** * Run the job. - * - * @param Job $job + * + * @param Job $job The job to be executed. + * @return void + * @throws \yii\base\Exception Exception. */ - public function run(Job $job) { + public function run(Job $job) + { \Yii::info('Running job', 'yii2queue'); try { if ($job->isCallable()) { @@ -86,7 +165,7 @@ public function run(Job $job) { } catch (\Exception $e) { if ($job->isCallable()) { if (isset($job->header['signature']) && isset($job->header['signature']['route'])) { - $id = $job->id . " " . \yii\helpers\Json::encode($job->header['signature']['route']); + $id = $job->id . ' ' . \yii\helpers\Json::encode($job->header['signature']['route']); } else { $id = $job->id . ' callable'; } @@ -94,7 +173,12 @@ public function run(Job $job) { $id = $job->route; } \Yii::error("Fatal Error: Error running route '{$id}'. Message: {$e->getMessage()}", 'yii2queue'); - throw new \yii\base\Exception("Error running route '{$id}'. Message: {$e->getMessage()}. File: {$e->getFile()}[{$e->getLine()}]. Stack Trace: {$e->getTraceAsString()}", 500); + throw new \yii\base\Exception( + "Error running route '{$id}'. " . + "Message: {$e->getMessage()}. " . + "File: {$e->getFile()}[{$e->getLine()}]. Stack Trace: {$e->getTraceAsString()}", + 500 + ); } if ($retval !== false) { \Yii::info('Deleting job', 'yii2queue'); @@ -103,20 +187,34 @@ public function run(Job $job) { } /** - * Delete the job. - * @param Job $job + * Delete the job. This will trigger event EVENT_BEFORE_DELETE and + * EVENT_AFTER_DELETE. + * + * @param Job $job The job to delete. + * @return boolean whether the operation succeed. + */ + public function delete(Job $job) + { + return $this->deleteJob($job); + } + + /** + * Delete the job. Override this for the queue implementation. + * + * @param Job $job The job to delete. * @return boolean whether the operation succeed. */ - public abstract function delete(Job $job); + abstract protected function deleteJob(Job $job); /** * Deserialize job to be executed. - * - * @param string $message the json string - * @return \UrbanIndo\Yii2\Queue\Job the job - * @throws \yii\base\Exception if there is no route detected. + * + * @param string $message The json string. + * @return Job The job. + * @throws \yii\base\Exception If there is no route detected. */ - protected function deserialize($message) { + protected function deserialize($message) + { $job = $this->deserializeMessage($message); if (!isset($job['route'])) { throw new \yii\base\Exception('No route detected'); @@ -137,13 +235,14 @@ protected function deserialize($message) { return $obj; } - /** - * - * @param type $array - * @return type + /** + * @param array $array The message to be deserialize. + * @return array + * @throws Exception Exception. */ - protected function deserializeMessage($array) { - switch($this->serializer) { + protected function deserializeMessage($array) + { + switch ($this->serializer) { case self::SERIALIZER_PHP: $data = unserialize($array); break; @@ -159,15 +258,16 @@ protected function deserializeMessage($array) { /** * Pack job so that it can be send. - * - * @param Job $job the job. + * + * @param Job $job The job to serialize. * @return string JSON string. */ - protected function serialize(Job $job) { + protected function serialize(Job $job) + { $return = []; if ($job->isCallable()) { $return['type'] = Job::TYPE_CALLABLE; - $serializer = new \SuperClosure\Serializer(); + $serializer = new \SuperClosure\Serializer(); $return['route'] = $serializer->serialize($job->route); } else { $return['type'] = Job::TYPE_REGULAR; @@ -178,12 +278,13 @@ protected function serialize(Job $job) { } /** - * - * @param type $array - * @return type + * @param mixed $array Array to serialize. + * @return array + * @throws Exception When the message cannot be deserialized. */ - protected function serializeMessage($array) { - switch($this->serializer) { + protected function serializeMessage($array) + { + switch ($this->serializer) { case self::SERIALIZER_PHP: $data = serialize($array); break; diff --git a/src/Queues/DbQueue.php b/src/Queues/DbQueue.php new file mode 100644 index 0000000..d2a2dc7 --- /dev/null +++ b/src/Queues/DbQueue.php @@ -0,0 +1,180 @@ + + * @since 2016.01.16 + */ + +namespace UrbanIndo\Yii2\Queue; + +use UrbanIndo\Yii2\Queue\Job; + +/** + * DbQueue provides Yii2 database storing for Queue. + * + * @author Petra Barus + * @since 2016.01.16 + */ +class DbQueue extends Queue +{ + /** + * Status when the job is ready. + */ + const STATUS_READY = 0; + + /** + * Status when the job is being runned by the worker. + */ + const STATUS_ACTIVE = 1; + + /** + * Status when the job is deleted. + */ + const STATUS_DELETED = 2; + + /** + * The database used for the queue. + * + * This will use default `db` component from Yii application. + * @var string|\yii\db\Connection + */ + public $db = 'db'; + + /** + * The name of the table to store the queue. + * + * The table should be pre-created as follows: + * + * ```php + * CREATE TABLE queue ( + * id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT + * status TINYINT NOT NULL DEFAULT 0 + * timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP + * data BLOB + * ); + * ``` + * @var string + */ + public $tableName = '{{%queue}}'; + + /** + * Whether to do hard delete of the deleted job, instead of just flagging the + * status. + * @var boolean + */ + public $hardDelete = true; + + /** + * @return void + */ + public function init() + { + parent::init(); + $this->db = \yii\di\Instance::ensure($this->db, \yii\db\Connection::className()); + } + + /** + * Creates the table if needed. + * @return boolean + */ + public function createTable() + { + return $this->db->createCommand()->createTable($this->tableName, [ + 'id' => 'BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT', + 'timestamp' => 'DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP', + 'data' => 'LONGBLOB' + ])->execute(); + } + + /** + * Return next job from the queue. + * @return Job|boolean the job or false if not found. + */ + protected function fetchJob() + { + //Avoiding multiple job. + $transaction = $this->db->beginTransaction(); + $row = $this->fetchLatestRow(); + if ($row == false || !$this->flagRunningRow($row)) { + $transaction->rollBack(); + return false; + } + $transaction->commit(); + + $job = $this->deserialize($row['data']); + $job->id = $row['id']; + $job->header['timestamp'] = $row['timestamp']; + + return $job; + } + + /** + * Fetch latest ready job from the table. + * @return array + */ + private function fetchLatestRow() + { + return (new \yii\db\Query()) + ->select('*') + ->from($this->tableName) + ->where(['status' => self::STATUS_READY]) + ->orderBy(['id' => SORT_DESC]) + ->limit(1) + ->one($this->db); + } + + /** + * Flag a row as running. This will update the row ID and status if ready. + * + * @param array $row The row to update. + * @return boolean Whether successful or not. + */ + private function flagRunningRow(array $row) + { + $updated = $this->db->createCommand() + ->update( + $this->tableName, + ['status' => self::STATUS_ACTIVE], + [ + 'id' => $row['id'], + 'status' => self::STATUS_READY, + ] + )->execute(); + return $updated == 1; + } + + /** + * Post new job to the queue. This contains implementation for database. + * + * @param Job $job The job to post. + * @return boolean whether operation succeed. + */ + protected function postJob(Job &$job) + { + return $this->db->createCommand()->insert($this->tableName, [ + 'data' => $this->serialize($job), + ]) == 1; + } + + /** + * Delete the job. Override this for the queue implementation. + * + * @param Job $job The job to delete. + * @return boolean whether the operation succeed. + */ + public function deleteJob(Job $job) + { + if ($this->hardDelete) { + return $this->db->createCommand()->delete($this->tableName, [ + 'id' => $job->id, + ])->execute() == 1; + } else { + return $this->db->createCommand()->update( + $this->tableName, + ['status' => self::STATUS_DELETED], + ['id' => $job->id] + ); + } + } +} diff --git a/src/Queues/MemoryQueue.php b/src/Queues/MemoryQueue.php index a8a18be..92ec096 100644 --- a/src/Queues/MemoryQueue.php +++ b/src/Queues/MemoryQueue.php @@ -1,23 +1,25 @@ * @since 2015.06.01 */ + namespace UrbanIndo\Yii2\Queue\Queues; use UrbanIndo\Yii2\Queue\Job; /** * MemoryQueue stores queue in the local variable. - * + * * This will only work for one request. - * + * * @author Petra Barus * @since 2015.06.01 */ -class MemoryQueue extends \UrbanIndo\Yii2\Queue\Queue { +class MemoryQueue extends \UrbanIndo\Yii2\Queue\Queue +{ /** * @var Job[] @@ -25,11 +27,12 @@ class MemoryQueue extends \UrbanIndo\Yii2\Queue\Queue { private $_jobs = []; /** - * @param Job $job - * @return boolean + * @param Job $job The job to delete. + * @return boolean Whether the deletion succeed. */ - public function delete(Job $job) { - foreach($this->_jobs as $key => $val) { + public function deleteJob(Job $job) + { + foreach ($this->_jobs as $key => $val) { if ($val->id == $job->id) { unset($this->_jobs[$key]); $this->_jobs = array_values($this->_jobs); @@ -40,9 +43,10 @@ public function delete(Job $job) { } /** - * @return Job + * @return Job The job fetched from queue. */ - public function fetch() { + public function fetchJob() + { if ($this->getQueueLength() == 0) { return false; } @@ -51,9 +55,11 @@ public function fetch() { } /** - * @param Job $job + * @param Job $job The job to be posted to the queueu. + * @return boolean Whether the post succeed. */ - public function post(Job &$job) { + public function postJob(Job &$job) + { $job->id = mt_rand(0, 65535); $this->_jobs[] = $job; return true; @@ -63,21 +69,26 @@ public function post(Job &$job) { * Returns the number of job. * @return integer */ - public function getQueueLength() { + public function getQueueLength() + { return count($this->_jobs); } /** - * + * Empty the whole queue items. + * @return void */ - public function emptyQueue() { + public function emptyQueue() + { $this->_jobs = []; } /** + * Returns the jobs posted to the queue. * @return Job[] */ - public function getJobs() { + public function getJobs() + { return $this->_jobs; } } diff --git a/src/Queues/MultipleQueue.php b/src/Queues/MultipleQueue.php new file mode 100644 index 0000000..d88e286 --- /dev/null +++ b/src/Queues/MultipleQueue.php @@ -0,0 +1,112 @@ + + * @since 2015.02.25 + */ + +namespace UrbanIndo\Yii2\Queue\Queues; + +use UrbanIndo\Yii2\Queue\Job; +use UrbanIndo\Yii2\Queue\Strategies\Strategy; +use UrbanIndo\Yii2\Queue\Strategies\RandomStrategy; + +/** + * MultipleQueue is a queue abstraction that handles multiple queue at once. + * + * @author Petra Barus + * @since 2015.02.25 + */ +class MultipleQueue extends \UrbanIndo\Yii2\Queue\Queue +{ + + /** + * Stores the queue. + * @var Queue[] + */ + public $queues = []; + + /** + * The job fetching strategy. + * @var \UrbanIndo\Yii2\Queue\Strategies\Strategy + */ + public $strategy = ['class' => RandomStrategy::class]; + + /** + * Initialize the queue. + * @return void + * @throws \yii\base\InvalidConfigException If the strategy doesn't implement + * UrbanIndo\Yii2\Queue\Strategies\Strategy. + */ + public function init() + { + parent::init(); + $queueObjects = []; + foreach ($this->queues as $id => $queue) { + $queueObjects[$id] = \Yii::createObject($queue); + } + $this->queues = $queueObjects; + if (is_array($this->strategy)) { + $this->strategy = \Yii::createObject($this->strategy); + } else if ($this->strategy instanceof Strategy) { + throw new \yii\base\InvalidConfigException( + 'The strategy field have to implement UrbanIndo\Yii2\Queue\Strategies\Strategy' + ); + } + $this->strategy->setQueue($this); + } + + /** + * @param integer $index The index of the queue. + * @return Queue|null the queue or null if not exists. + */ + public function getQueue($index) + { + return \yii\helpers\ArrayHelper::getValue($this->queues, $index); + } + + /** + * Delete the job. + * @param Job $job The job. + * @return boolean Whether the operation succeed. + */ + protected function deleteJob(Job $job) + { + return $this->strategy->delete($job); + } + + /** + * Return next job from the queue. + * @return Job|boolean The job fetched or false if not found. + */ + protected function fetchJob() + { + return $this->strategy->fetch(); + } + + /** + * Post new job to the queue. + * @param Job $job The job. + * @return boolean Whether operation succeed. + */ + protected function postJob(Job &$job) + { + return $this->postToQueue($job, 0); + } + + /** + * Post new job to a specific queue. + * @param Job $job The job. + * @param integer $index The queue index. + * @return boolean Whether operation succeed. + */ + public function postToQueue(Job &$job, $index) + { + $queue = $this->getQueue($index); + if ($queue === null) { + return false; + } + return $queue->post($job); + } +} diff --git a/src/SqsQueue.php b/src/Queues/SqsQueue.php similarity index 84% rename from src/SqsQueue.php rename to src/Queues/SqsQueue.php index d70800c..7883559 100644 --- a/src/SqsQueue.php +++ b/src/Queues/SqsQueue.php @@ -1,8 +1,7 @@ * @since 2015.02.24 */ @@ -18,7 +17,8 @@ * @author Petra Barus * @since 2015.02.24 */ -class SqsQueue extends Queue { +class SqsQueue extends Queue +{ /** * The SQS url. @@ -28,7 +28,7 @@ class SqsQueue extends Queue { /** * The config for SqsClient. - * + * * This will be used for SqsClient::factory($config); * @var array */ @@ -42,8 +42,10 @@ class SqsQueue extends Queue { /** * Initialize the queue component. + * @return void */ - public function init() { + public function init() + { parent::init(); $this->_client = SqsClient::factory($this->config); } @@ -52,7 +54,8 @@ public function init() { * Return next job from the queue. * @return Job|boolean the job or false if not found. */ - public function fetch() { + public function fetchJob() + { $message = $this->_client->receiveMessage([ 'QueueUrl' => $this->url, 'AttributeNames' => ['ApproximateReceiveCount'], @@ -67,11 +70,12 @@ public function fetch() { /** * Create job from SQS message. - * - * @param array $message the message. + * + * @param array $message The message. * @return \UrbanIndo\Yii2\Queue\Job */ - private function createJobFromMessage($message) { + private function createJobFromMessage($message) + { $job = $this->deserialize($message['Body']); $job->header['ReceiptHandle'] = $message['ReceiptHandle']; $job->id = $message['MessageId']; @@ -80,11 +84,12 @@ private function createJobFromMessage($message) { /** * Post the job to queue. - * - * @param Job $job the job model. + * + * @param Job $job The job posted to the queue. * @return boolean whether operation succeed. */ - public function post(Job &$job) { + public function postJob(Job &$job) + { $model = $this->_client->sendMessage([ 'QueueUrl' => $this->url, 'MessageBody' => $this->serialize($job), @@ -99,11 +104,12 @@ public function post(Job &$job) { /** * Delete the job from the queue. - * - * @param Job $job + * + * @param Job $job The job to be deleted. * @return boolean whether the operation succeed. */ - public function delete(Job $job) { + public function deleteJob(Job $job) + { if (!empty($job->header['ReceiptHandle'])) { $receiptHandle = $job->header['ReceiptHandle']; $response = $this->_client->deleteMessage([ @@ -118,11 +124,11 @@ public function delete(Job $job) { /** * Returns the SQS client used. - * + * * @return \Aws\Sqs\SqsClient */ - public function getClient() { + public function getClient() + { return $this->_client; } - } diff --git a/src/Strategies/RandomStrategy.php b/src/Strategies/RandomStrategy.php index c1137ee..0330fe2 100644 --- a/src/Strategies/RandomStrategy.php +++ b/src/Strategies/RandomStrategy.php @@ -1,23 +1,29 @@ * @since 2015.02.25 */ namespace UrbanIndo\Yii2\Queue\Strategies; +use UrbanIndo\Yii2\Queue\Job; + /** * RandomStrategy provides random choosing of the queue for getting the job. - * + * * @author Petra Barus * @since 2015.02.25 */ -class RandomStrategy extends Strategy { - - public function init() { +class RandomStrategy extends Strategy +{ + + /** + * @return void + */ + public function init() + { parent::init(); srand(); } @@ -30,9 +36,10 @@ public function init() { /** * Returns the job. - * @return \UrbanIndo\Yii2\Queue\Job|boolean the job or false if not found. + * @return Job|boolean the job or false if not found. */ - protected function getJobFromQueues() { + protected function getJobFromQueues() + { $attempt = 0; $count = count($this->_queue->queues); while ($attempt < $this->maxAttempt) { @@ -46,5 +53,4 @@ protected function getJobFromQueues() { } return false; } - } diff --git a/src/Strategies/Strategy.php b/src/Strategies/Strategy.php index 51192bb..9ac50cf 100644 --- a/src/Strategies/Strategy.php +++ b/src/Strategies/Strategy.php @@ -1,22 +1,28 @@ * @since 2015.02.25 */ namespace UrbanIndo\Yii2\Queue\Strategies; +use UrbanIndo\Yii2\Queue\Queues\MultipleQueue; +use UrbanIndo\Yii2\Queue\Job; + /** * Strategy is abstract class fo all strategy that is used for MultipleQueue. - * + * * @author Petra Barus * @since 2015.02.25 */ -abstract class Strategy extends \yii\base\Object { +abstract class Strategy extends \yii\base\Object +{ + /** + * Additional header for the job. + */ const HEADER_MULTIPLE_QUEUE_INDEX = 'MultipleQueueIndex'; /** @@ -27,9 +33,11 @@ abstract class Strategy extends \yii\base\Object { /** * Sets the queue. - * @param \UrbanIndo\Yii2\Queue\MultipleQueue $queue + * @param MultipleQueue $queue The queue. + * @return void */ - public function setQueue(\UrbanIndo\Yii2\Queue\MultipleQueue $queue) { + public function setQueue(MultipleQueue $queue) + { $this->_queue = $queue; } @@ -37,32 +45,37 @@ public function setQueue(\UrbanIndo\Yii2\Queue\MultipleQueue $queue) { * Implement this for the strategy of getting job from the queue. * @return mixed tuple of job and the queue index. */ - protected abstract function getJobFromQueues(); + abstract protected function getJobFromQueues(); /** * Returns the job. - * @return \UrbanIndo\Yii2\Queue\Job|boolean the job or false if not found. + * @return Job|boolean The job or false if not found. */ - public function fetch() { + public function fetch() + { $return = $this->getJobFromQueues(); if ($return === false) { return false; } list($job, $index) = $return; - /* @var $job \UrbanIndo\Yii2\Queue\Job */ + /* @var $job Job */ $job->header[self::HEADER_MULTIPLE_QUEUE_INDEX] = $index; return $job; } /** * Delete the job from the queue. - * - * @param \UrbanIndo\Yii2\Queue\Job $job + * + * @param Job $job The job. * @return boolean whether the operation succeed. */ - public function delete(\UrbanIndo\Yii2\Queue\Job $job) { - $index = \yii\helpers\ArrayHelper::getValue($job->header, - self::HEADER_MULTIPLE_QUEUE_INDEX, null); + public function delete(Job $job) + { + $index = \yii\helpers\ArrayHelper::getValue( + $job->header, + self::HEADER_MULTIPLE_QUEUE_INDEX, + null + ); if (!isset($index)) { return false; } @@ -72,5 +85,4 @@ public function delete(\UrbanIndo\Yii2\Queue\Job $job) { } return $queue->delete($job); } - } diff --git a/src/Strategies/WeightedStrategy.php b/src/Strategies/WeightedStrategy.php index 4442930..4fc75df 100644 --- a/src/Strategies/WeightedStrategy.php +++ b/src/Strategies/WeightedStrategy.php @@ -1,8 +1,7 @@ * @since 2015.02.25 */ @@ -11,31 +10,36 @@ /** * WeightedStrategy that will put weight to the queues. - * + * * @author Petra Barus * @since 2015.02.25 */ -class WeightedStrategy extends Strategy { +class WeightedStrategy extends Strategy +{ /** * List of weights. - * - * The weight will be wielded to queue with the sampe index number. And the + * + * The weight will be wielded to queue with the sampe index number. And the * number of the weight should be the same with the number of the queue. - * + * * For example, * [10, 8, 5, 2] - * + * * means, if the queue 0 will have weight 10, 1 will have 8, and so on. - * - * In other words, the weight will NOT be automatically sorted descending + * + * In other words, the weight will NOT be automatically sorted descending * and NOT be sliced to number of queue. - * + * * @var array */ public $weight = []; - public function init() { + /** + * @return void + */ + public function init() + { parent::init(); // sort($this->weight, SORT_DESC); // $this->weight = array_slice($this->weight, 0, @@ -46,7 +50,8 @@ public function init() { * Implement this for the strategy of getting job from the queue. * @return mixed tuple of job and the queue index. */ - protected function getJobFromQueues() { + protected function getJobFromQueues() + { $index = self::weightedRandom($this->weight); $count = count($this->_queue->queues); while ($index < $count) { @@ -64,10 +69,11 @@ protected function getJobFromQueues() { /** * Return weighted random. * - * @param array $array array of value and weight. + * @param array $array Array of value and weight. * @return string the value. */ - private static function weightedRandom($array) { + private static function weightedRandom($array) + { $rand = mt_rand(1, (int) array_sum($array)); foreach ($array as $key => $value) { $rand -= $value; @@ -76,5 +82,4 @@ private static function weightedRandom($array) { } } } - } diff --git a/src/Web/Controller.php b/src/Web/Controller.php index 62517f5..a502a35 100644 --- a/src/Web/Controller.php +++ b/src/Web/Controller.php @@ -1,39 +1,63 @@ + */ namespace UrbanIndo\Yii2\Queue\Web; use UrbanIndo\Yii2\Queue\Job; +use UrbanIndo\Yii2\Queue\Queue; +use UrbanIndo\Yii2\Queue\Queues\MultipleQueue; /** * QueueController is a web controller to post job via url. - * + * * To use this use a controller map. - * + * * 'controllerMap' => [ * 'queue' => 'UrbanIndo\Yii2\Queue\Web\Controller', * ] - * + * * And then send a POST to the endpoint - * + * * curl -XPOST http://example.com/queue --data route=test/test --data={"data": "data"} - * + * * @author Petra Barus * @author Adinata */ -class Controller extends \yii\web\Controller { +class Controller extends \yii\web\Controller +{ + /** + * Disable class file. + * @var boolean + */ public $enableCsrfValidation = false; - public $queueComponent = 'queue'; - public function init() { + /** + * The queue to process. + * @var string|array|\UrbanIndo\Yii2\Queue\Queue + */ + public $queue = 'queue'; + + /** + * @return void + */ + public function init() + { parent::init(); \Yii::$app->getResponse()->format = 'json'; + $this->queue = \yii\di\Instance::ensure($this->queue, Queue::className()); } /** * @return Job + * @throws \yii\web\ServerErrorHttpException When malformed request. */ - private function createJobFromRequest() { + private function createJobFromRequest() + { $route = \Yii::$app->getRequest()->post('route'); $data = \Yii::$app->getRequest()->post('data', []); @@ -44,6 +68,7 @@ private function createJobFromRequest() { if (is_string($data)) { $data = \yii\helpers\Json::decode($data); } + return new Job([ 'route' => $route, 'data' => $data @@ -53,12 +78,13 @@ private function createJobFromRequest() { /** * Endpoint to post a job to queue. * @return mixed + * @throws \yii\web\ServerErrorHttpException When failed to post. */ - public function actionPost() { + public function actionPost() + { $job = $this->createJobFromRequest(); /* @var $queue \UrbanIndo\Yii2\Queue\Queue */ - $queue = \Yii::$app->get($this->queueComponent); - if ($queue->post($job)) { + if ($this->queue->post($job)) { return ['status' => 'okay', 'jobId' => $job->id]; } else { throw new \yii\web\ServerErrorHttpException('Failed to post job'); @@ -68,15 +94,18 @@ public function actionPost() { /** * Endpoint to post a job to multiple queue. * @return mixed + * @throws \InvalidArgumentException Queue has to be instance of \UrbanIndo\Yii2\Queue\MultipleQueue. + * @throws \yii\web\ServerErrorHttpException When failed to post the job. */ - public function actionPostToQueue() { + public function actionPostToQueue() + { $job = $this->createJobFromRequest(); $index = \Yii::$app->getRequest()->post('index'); if (!isset($index)) { throw new \InvalidArgumentException('Index needed'); } - $queue = \Yii::$app->get($this->queueComponent); - if (!$queue instanceof \UrbanIndo\Yii2\Queue\MultipleQueue) { + $queue = $this->queue; + if (!$queue instanceof MultipleQueue) { throw new \InvalidArgumentException('Queue is not instance of \UrbanIndo\Yii2\Queue\MultipleQueue'); } /* @var $queue MultipleQueue */ @@ -87,5 +116,4 @@ public function actionPostToQueue() { throw new \yii\web\ServerErrorHttpException('Failed to post job'); } } - } diff --git a/src/Worker/Controller.php b/src/Worker/Controller.php index e472e50..afbb416 100644 --- a/src/Worker/Controller.php +++ b/src/Worker/Controller.php @@ -1,8 +1,7 @@ * @since 2015.02.24 */ @@ -13,15 +12,16 @@ /** * Controller is base class for task controllers. - * + * * The usage is pretty much the same with the web or the console. The different * is that if the action return false, the job will not deleted. Otherwise * the job will be deleted from the queue. - * + * * @author Petra Barus * @since 2015.02.24 */ -abstract class Controller extends \yii\base\Controller { +abstract class Controller extends \yii\base\Controller +{ /** * Stores all params even some elements are not assigned in the action method. @@ -33,7 +33,8 @@ abstract class Controller extends \yii\base\Controller { * Returns action params from the queue job. * @return array */ - public function getActionParams() { + public function getActionParams() + { return $this->_params; } @@ -42,12 +43,13 @@ public function getActionParams() { * This method is invoked by [[Action]] when it begins to run with the given parameters. * This method will first bind the parameters with the [[options()|options]] * available to the action. It then validates the given arguments. - * @param \yii\base\Action $action the action to be bound with parameters - * @param array $params the parameters to be bound to the action + * @param \yii\base\Action $action The action To be bound with parameters. + * @param array $params The parameters to be bound to the action. * @return array the valid parameters that the action can run with. - * @throws \yii\base\Exception if there are unknown options or missing arguments + * @throws \Exception If there are unknown options or missing arguments. */ - public function bindActionParams($action, $params) { + public function bindActionParams($action, $params) + { $this->_params = $params; if ($action instanceof InlineAction) { @@ -73,12 +75,12 @@ public function bindActionParams($action, $params) { } if (!empty($missing)) { - throw new \Exception(\Yii::t('yii', - 'Missing required arguments: {params}', - ['params' => implode(', ', $missing)])); + throw new \Exception( + \Yii::t('yii', 'Missing required arguments: {params}', [ + 'params' => implode(', ', $missing)]) + ); } return $args; } - } diff --git a/tests/EventTest.php b/tests/EventTest.php new file mode 100644 index 0000000..42ea505 --- /dev/null +++ b/tests/EventTest.php @@ -0,0 +1,6 @@ + + */ +class DbQueueTest +{ + //put your code here +} diff --git a/tests/MultipleQueueTest.php b/tests/Queues/MultipleQueueTest.php similarity index 96% rename from tests/MultipleQueueTest.php rename to tests/Queues/MultipleQueueTest.php index 63fbac9..5c6a12d 100644 --- a/tests/MultipleQueueTest.php +++ b/tests/Queues/MultipleQueueTest.php @@ -4,7 +4,7 @@ class MultipleQueueTest extends PHPUnit_Framework_TestCase { public function test() { $queue = Yii::createObject([ - 'class' => '\UrbanIndo\Yii2\Queue\MultipleQueue', + 'class' => '\UrbanIndo\Yii2\Queue\Queues\MultipleQueue', 'queues' => [ [ 'class' => '\UrbanIndo\Yii2\Queue\Queues\MemoryQueue' @@ -24,7 +24,7 @@ public function test() { ] ]); - $this->assertTrue($queue instanceof UrbanIndo\Yii2\Queue\MultipleQueue); + $this->assertTrue($queue instanceof UrbanIndo\Yii2\Queue\Queues\MultipleQueue); /* @var $queue UrbanIndo\Yii2\Queue\MultipleQueue */ $this->assertCount(4, $queue->queues); foreach($queue->queues as $tqueue) { diff --git a/tests/Queues/SqsQueueTest.php b/tests/Queues/SqsQueueTest.php new file mode 100644 index 0000000..83ff268 --- /dev/null +++ b/tests/Queues/SqsQueueTest.php @@ -0,0 +1,6 @@ + Date: Sun, 17 Jan 2016 06:07:24 +0700 Subject: [PATCH 2/8] Event for queue. --- src/Event.php | 6 +++++ src/Queue.php | 40 ++++++++++++++++++++++++++++++-- tests/EventTest.php | 56 +++++++++++++++++++++++++++++++++++++++++++-- tests/QueueTest.php | 2 +- 4 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/Event.php b/src/Event.php index fc4c7b4..720a2b3 100644 --- a/src/Event.php +++ b/src/Event.php @@ -19,6 +19,12 @@ class Event extends \yii\base\Event */ public $job; + /** + * The return value after a job is being executed. + * @var mixed + */ + public $returnValue; + /** * Whether the next process should continue or not. * @var boolean diff --git a/src/Queue.php b/src/Queue.php index e0188c3..f44e13d 100644 --- a/src/Queue.php +++ b/src/Queue.php @@ -57,6 +57,16 @@ abstract class Queue extends \yii\base\Component * Event executed after a job is being deleted from the queue. */ const EVENT_AFTER_DELETE = 'afterDelete'; + + /** + * Event executed before a job is being executed. + */ + const EVENT_BEFORE_RUN = 'beforeRun'; + + /** + * Event executed after a job is being executed. + */ + const EVENT_AFTER_RUN = 'afterRun'; /** * The module where the task is located. @@ -137,7 +147,15 @@ abstract protected function postJob(Job &$job); */ public function fetch() { - return $this->fetchJob(); + $this->trigger(self::EVENT_BEFORE_FETCH); + + $job = $this->fetchJob(); + if ($job == false) { + return false; + } + + $this->trigger(self::EVENT_AFTER_FETCH, new Event(['job' => $job])); + return $job; } /** @@ -155,6 +173,10 @@ abstract protected function fetchJob(); */ public function run(Job $job) { + $this->trigger(self::EVENT_BEFORE_RUN, $beforeEvent = new Event(['job' => $job])); + if (!$beforeEvent->isValid) { + return false; + } \Yii::info('Running job', 'yii2queue'); try { if ($job->isCallable()) { @@ -180,6 +202,9 @@ public function run(Job $job) 500 ); } + + $this->trigger(self::EVENT_AFTER_RUN, new Event(['job' => $job, 'returnValue' => $retval])); + if ($retval !== false) { \Yii::info('Deleting job', 'yii2queue'); $this->delete($job); @@ -195,7 +220,18 @@ public function run(Job $job) */ public function delete(Job $job) { - return $this->deleteJob($job); + $this->trigger(self::EVENT_BEFORE_DELETE, $beforeEvent = new Event(['job' => $job])); + if (!$beforeEvent->isValid) { + return false; + } + + $return = $this->deleteJob($job); + if (!$return) { + return false; + } + + $this->trigger(self::EVENT_AFTER_DELETE, new Event(['job' => $job])); + return true; } /** diff --git a/tests/EventTest.php b/tests/EventTest.php index 42ea505..5ae0f64 100644 --- a/tests/EventTest.php +++ b/tests/EventTest.php @@ -1,6 +1,58 @@ counter += 1; + } + ); + + \yii\base\Event::on( + \UrbanIndo\Yii2\Queue\Queue::className(), + \UrbanIndo\Yii2\Queue\Queue::EVENT_AFTER_FETCH, + function ($event) { + $this->counter += 2; + } + ); + + \yii\base\Event::on( + \UrbanIndo\Yii2\Queue\Queue::className(), + \UrbanIndo\Yii2\Queue\Queue::EVENT_AFTER_DELETE, + function ($event) { + $this->counter += 3; + } + ); + + $queue = Yii::createObject([ + 'class' => '\UrbanIndo\Yii2\Queue\Queues\MemoryQueue' + ]); + + $this->assertEquals($this->counter, 0); + + /* @var $queue \UrbanIndo\Yii2\Queue\Queues\MemoryQueue */ + $queue->post(new UrbanIndo\Yii2\Queue\Job([ + 'route' => function() { + //Do something + } + ])); + + $this->assertEquals($this->counter, 1); + + $job = $queue->fetch(); + + $this->assertEquals($this->counter, 3); + + $queue->delete($job); + + $this->assertEquals($this->counter, 6); + } + } diff --git a/tests/QueueTest.php b/tests/QueueTest.php index 3255c9e..a34e89d 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -4,7 +4,7 @@ class QueueTest extends PHPUnit_Framework_TestCase { public function testQueueCatchingException() { $this->setExpectedException(\yii\base\Exception::class); - $queue = Yii::createObject([ + $queue = Yii::createObject([ 'class' => '\UrbanIndo\Yii2\Queue\Queues\MemoryQueue' ]); From c7a4a9fe194dd48663d6116813e024d448165865 Mon Sep 17 00:00:00 2001 From: Petra Barus Date: Sun, 17 Jan 2016 10:19:28 +0700 Subject: [PATCH 3/8] Add DbQueue. --- .travis.yml | 6 ++ composer.json | 2 + composer.lock | 114 +++++++++++++++++++-- src/Queue.php | 2 +- src/Queues/DbQueue.php | 64 ++++++++---- tests/Queues/DbQueueTest.php | 186 ++++++++++++++++++++++++++++++++--- tests/TestCase.php | 22 +++++ tests/bootstrap.php | 2 + 8 files changed, 358 insertions(+), 40 deletions(-) create mode 100644 tests/TestCase.php diff --git a/.travis.yml b/.travis.yml index 78b3aa8..655a2d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,12 @@ php: services: - mysql - redis-server + +before_script: + #MySQL database init + - mysql -uroot -e "CREATE DATABASE IF NOT EXISTS test;" + - mysql -uroot -e "CREATE USER 'test'@'localhost' IDENTIFIED BY 'test';" + - mysql -uroot -e "GRANT ALL PRIVILEGES ON test.* TO 'test'@'localhost' IDENTIFIED BY 'test';" install: - travis_retry composer self-update && composer --version diff --git a/composer.json b/composer.json index 5c9974c..fd4c0d8 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,8 @@ "fzaninotto/faker": "dev-master", "flow/jsonpath": "dev-master", "yiisoft/yii2-coding-standards": "*", + "yiisoft/yii2-redis": "*", + "videlalvaro/php-amqplib": "2.5.*", "squizlabs/php_codesniffer": "2.*" }, "autoload": { diff --git a/composer.lock b/composer.lock index d8fc37a..41d62b6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "5cb39cc3e6c770274ffc19769c86e64c", - "content-hash": "efa2d9ab89c590fc4acf4ce84dc28dc7", + "hash": "c637eb0027aa1bf468f9e2e10a16e1a6", + "content-hash": "9d73559f94bf56532d4f4ed86b85a47f", "packages": [ { "name": "aws/aws-sdk-php", @@ -13,12 +13,12 @@ "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "dff1c76551777fcc5890d2d5324f9f9e5cdd4e3d" + "reference": "c84939b433da639702020ecd5d5674b5baaf3b0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/dff1c76551777fcc5890d2d5324f9f9e5cdd4e3d", - "reference": "dff1c76551777fcc5890d2d5324f9f9e5cdd4e3d", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c84939b433da639702020ecd5d5674b5baaf3b0c", + "reference": "c84939b433da639702020ecd5d5674b5baaf3b0c", "shasum": "" }, "require": { @@ -84,7 +84,7 @@ "s3", "sdk" ], - "time": "2016-01-15 02:01:24" + "time": "2016-01-16 20:25:08" }, { "name": "bower-asset/jquery", @@ -2203,6 +2203,60 @@ "homepage": "https://symfony.com", "time": "2016-01-13 16:23:43" }, + { + "name": "videlalvaro/php-amqplib", + "version": "v2.5.2", + "source": { + "type": "git", + "url": "https://github.com/videlalvaro/php-amqplib.git", + "reference": "eb8f94d97c8e79900accf77343dbd7eca7f58506" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/videlalvaro/php-amqplib/zipball/eb8f94d97c8e79900accf77343dbd7eca7f58506", + "reference": "eb8f94d97c8e79900accf77343dbd7eca7f58506", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "ext-mbstring": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "suggest": { + "ext-sockets": "Use AMQPSocketConnection" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "PhpAmqpLib\\": "PhpAmqpLib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "Alvaro Videla" + } + ], + "description": "This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", + "homepage": "https://github.com/videlalvaro/php-amqplib/", + "keywords": [ + "message", + "queue", + "rabbitmq" + ], + "time": "2015-08-11 12:30:09" + }, { "name": "yiisoft/yii2-coding-standards", "version": "dev-master", @@ -2270,6 +2324,54 @@ "yii" ], "time": "2015-04-03 06:38:48" + }, + { + "name": "yiisoft/yii2-redis", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-redis.git", + "reference": "d358e58a905f06b43a6b092ec8a014466be17e42" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-redis/zipball/d358e58a905f06b43a6b092ec8a014466be17e42", + "reference": "d358e58a905f06b43a6b092ec8a014466be17e42", + "shasum": "" + }, + "require": { + "yiisoft/yii2": ">=2.0.4" + }, + "type": "yii2-extension", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "yii\\redis\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Carsten Brandt", + "email": "mail@cebe.cc" + } + ], + "description": "Redis Cache, Session and ActiveRecord for the Yii framework", + "keywords": [ + "active-record", + "cache", + "redis", + "session", + "yii2" + ], + "time": "2015-12-24 11:25:55" } ], "aliases": [], diff --git a/src/Queue.php b/src/Queue.php index f44e13d..786deb2 100644 --- a/src/Queue.php +++ b/src/Queue.php @@ -175,7 +175,7 @@ public function run(Job $job) { $this->trigger(self::EVENT_BEFORE_RUN, $beforeEvent = new Event(['job' => $job])); if (!$beforeEvent->isValid) { - return false; + return; } \Yii::info('Running job', 'yii2queue'); try { diff --git a/src/Queues/DbQueue.php b/src/Queues/DbQueue.php index d2a2dc7..56d627d 100644 --- a/src/Queues/DbQueue.php +++ b/src/Queues/DbQueue.php @@ -6,17 +6,32 @@ * @since 2016.01.16 */ -namespace UrbanIndo\Yii2\Queue; +namespace UrbanIndo\Yii2\Queue\Queues; use UrbanIndo\Yii2\Queue\Job; /** * DbQueue provides Yii2 database storing for Queue. * + * The schema of the table should follow: + * + * CREATE TABLE queue ( + * id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT + * status TINYINT NOT NULL DEFAULT 0 + * timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP + * data BLOB + * ); + * + * The queue works under the asumption that the `id` fields is AUTO_INCREMENT and + * the `timestamp` will be set using current timestamp. + * + * For other implementation, override the `fetchLatestRow` method and `postJob` + * method. + * * @author Petra Barus * @since 2016.01.16 */ -class DbQueue extends Queue +class DbQueue extends \UrbanIndo\Yii2\Queue\Queue { /** * Status when the job is ready. @@ -44,14 +59,14 @@ class DbQueue extends Queue /** * The name of the table to store the queue. * - * The table should be pre-created as follows: + * The table should be pre-created as follows for MySQL: * * ```php * CREATE TABLE queue ( * id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT * status TINYINT NOT NULL DEFAULT 0 * timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP - * data BLOB + * data LONGBLOB * ); * ``` * @var string @@ -74,19 +89,6 @@ public function init() $this->db = \yii\di\Instance::ensure($this->db, \yii\db\Connection::className()); } - /** - * Creates the table if needed. - * @return boolean - */ - public function createTable() - { - return $this->db->createCommand()->createTable($this->tableName, [ - 'id' => 'BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT', - 'timestamp' => 'DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP', - 'data' => 'LONGBLOB' - ])->execute(); - } - /** * Return next job from the queue. * @return Job|boolean the job or false if not found. @@ -111,9 +113,13 @@ protected function fetchJob() /** * Fetch latest ready job from the table. + * + * Due to the use of AUTO_INCREMENT ID, this will fetch the job with the + * largest ID. + * * @return array */ - private function fetchLatestRow() + protected function fetchLatestRow() { return (new \yii\db\Query()) ->select('*') @@ -130,7 +136,7 @@ private function fetchLatestRow() * @param array $row The row to update. * @return boolean Whether successful or not. */ - private function flagRunningRow(array $row) + protected function flagRunningRow(array $row) { $updated = $this->db->createCommand() ->update( @@ -153,8 +159,9 @@ private function flagRunningRow(array $row) protected function postJob(Job &$job) { return $this->db->createCommand()->insert($this->tableName, [ + 'timestamp' => new \yii\db\Expression('NOW()'), 'data' => $this->serialize($job), - ]) == 1; + ])->execute() == 1; } /** @@ -174,7 +181,22 @@ public function deleteJob(Job $job) $this->tableName, ['status' => self::STATUS_DELETED], ['id' => $job->id] - ); + )->execute() == 1; } } + + /** + * Restore job from active to ready. + * + * @param Job $job The job to restore. + * @return boolean whether the operation succeed. + */ + public function restoreJob(Job $job) + { + return $this->db->createCommand()->update( + $this->tableName, + ['status' => self::STATUS_READY], + ['id' => $job->id] + )->execute() == 1; + } } diff --git a/tests/Queues/DbQueueTest.php b/tests/Queues/DbQueueTest.php index 35a45be..fd7cc78 100644 --- a/tests/Queues/DbQueueTest.php +++ b/tests/Queues/DbQueueTest.php @@ -1,17 +1,179 @@ - */ -class DbQueueTest +class DbQueueTest extends TestCase { - //put your code here + + public $counter = 0; + + protected function setUp() + { + parent::setUp(); + $this->counter = 0; + $faker = Faker\Factory::create(); + $tableName = 'queue_' . $faker->firstNameMale; + $this->mockApplication([ + 'components' => [ + 'db' => [ + 'class' => '\yii\db\Connection', + 'dsn' => 'mysql:host=127.0.0.1;dbname=test', + 'username' => 'test', + 'password' => 'test', + ], + 'queue' => [ + 'class' => '\UrbanIndo\Yii2\Queue\Queues\DbQueue', + 'tableName' => $tableName, + ] + ] + ]); + + Yii::$app->db->createCommand() + ->createTable($tableName, [ + 'id' => 'BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT', + 'status' => 'TINYINT NOT NULL DEFAULT 0', + 'timestamp' => 'DATETIME NOT NULL', + 'data' => 'LONGBLOB', + ])->execute(); + + } + + protected function tearDown() + { + parent::tearDown(); + Yii::$app->db->createCommand() + ->dropTable(Yii::$app->queue->tableName); + } + + /** + * + * @return DbQueue + */ + protected function getQueue() + { + return Yii::$app->queue; + } + + protected function countTable($condition = null) { + $query = (new yii\db\Query) + ->select('COUNT(*)') + ->from($this->getQueue()->tableName); + if ($condition) { + $query->where($condition); + } + return $query->scalar(); + } + + public function testPost() + { + $queue = $this->getQueue(); + $db = Yii::$app->db; + $tableName = $queue->tableName; + + $this->assertEquals(0, $this->countTable()); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + $this->counter += 1; + }])); + + $this->assertEquals(1, $this->countTable()); + + $this->assertEquals(1, $this->countTable(['status' => DbQueue::STATUS_READY])); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + $this->counter += 2; + }])); + + $this->assertEquals(2, $this->countTable()); + + $this->assertEquals(2, $this->countTable(['status' => DbQueue::STATUS_READY])); + } + + public function testFetch() + { + $queue = $this->getQueue(); + $db = Yii::$app->db; + $tableName = $queue->tableName; + + $this->assertEquals(0, $this->countTable()); + + $job = $queue->fetch(); + + $this->assertFalse($job); + + $this->assertEquals(0, $this->countTable(['status' => DbQueue::STATUS_ACTIVE])); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + $this->counter += 1; + }])); + + $job = $queue->fetch(); + + $this->assertEquals(1, $this->countTable(['status' => DbQueue::STATUS_ACTIVE])); + + $this->assertTrue($job instanceof UrbanIndo\Yii2\Queue\Job); + } + + public function testHardDelete() + { + $queue = $this->getQueue(); + $db = Yii::$app->db; + $tableName = $queue->tableName; + + $this->assertEquals(0, $this->countTable()); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + $this->counter += 1; + }])); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + $this->counter += 1; + }])); + + $this->assertEquals(2, $this->countTable()); + + $job = $queue->fetch(); + + $this->assertEquals(2, $this->countTable()); + + $queue->delete($job); + + $this->assertEquals(1, $this->countTable()); + + } + + public function testSoftDelete() + { + $queue = $this->getQueue(); + $queue->hardDelete = false; + $db = Yii::$app->db; + $tableName = $queue->tableName; + + $this->assertEquals(0, $this->countTable()); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + $this->counter += 1; + }])); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + $this->counter += 1; + }])); + + $this->assertEquals(2, $this->countTable(['status' => DbQueue::STATUS_READY])); + $this->assertEquals(2, $this->countTable()); + + $job = $queue->fetch(); + + $this->assertEquals(1, $this->countTable(['status' => DbQueue::STATUS_READY])); + $this->assertEquals(1, $this->countTable(['status' => DbQueue::STATUS_ACTIVE])); + $this->assertEquals(2, $this->countTable()); + + $queue->delete($job); + + $this->assertEquals(2, $this->countTable()); + $this->assertEquals(1, $this->countTable(['status' => DbQueue::STATUS_READY])); + $this->assertEquals(0, $this->countTable(['status' => DbQueue::STATUS_ACTIVE])); + $this->assertEquals(1, $this->countTable(['status' => DbQueue::STATUS_DELETED])); + + } } diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..2de6b2b --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,22 @@ + 'testapp', + 'basePath' => __DIR__, + 'vendorPath' => __DIR__ . '/../vendor', + ], $config)); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 95b7348..9a632eb 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -3,6 +3,8 @@ require dirname(__FILE__) . '/../vendor/autoload.php'; require dirname(__FILE__) . '/../vendor/yiisoft/yii2/Yii.php'; +require dirname(__FILE__) . '/TestCase.php'; + $config = [ 'id' => 'Yii2 Queue Test', 'basePath' => dirname(__FILE__), From 7606805196f0103c6e56e449c2364cfae24c1f92 Mon Sep 17 00:00:00 2001 From: Petra Barus Date: Sun, 17 Jan 2016 12:06:45 +0700 Subject: [PATCH 4/8] Add release methood. --- src/Queue.php | 46 +++++++++++++++++++++++++++++++++++- src/Queues/DbQueue.php | 4 ++-- src/Queues/MemoryQueue.php | 15 +++++++++++- src/Queues/MultipleQueue.php | 19 +++++++++++++-- src/Queues/SqsQueue.php | 23 +++++++++++++++++- tests/Queues/DbQueueTest.php | 3 +++ 6 files changed, 103 insertions(+), 7 deletions(-) diff --git a/src/Queue.php b/src/Queue.php index 786deb2..84f3177 100644 --- a/src/Queue.php +++ b/src/Queue.php @@ -58,6 +58,16 @@ abstract class Queue extends \yii\base\Component */ const EVENT_AFTER_DELETE = 'afterDelete'; + /** + * Event executed before a job is being released from the queue. + */ + const EVENT_BEFORE_RELEASE = 'beforeRelease'; + + /** + * Event executed after a job is being released from the queue. + */ + const EVENT_AFTER_RELEASE = 'afterRelease'; + /** * Event executed before a job is being executed. */ @@ -137,7 +147,7 @@ public function post(Job &$job) * @param Job $job The job. * @return boolean Whether operation succeed. */ - abstract protected function postJob(Job &$job); + abstract protected function postJob(Job $job); /** * Return next job from the queue. This will trigger event EVENT_BEFORE_FETCH @@ -208,6 +218,9 @@ public function run(Job $job) if ($retval !== false) { \Yii::info('Deleting job', 'yii2queue'); $this->delete($job); +// } else { +// \Yii::info('Releasing job', 'yii2queue'); +// $this->release($job); } } @@ -241,6 +254,37 @@ public function delete(Job $job) * @return boolean whether the operation succeed. */ abstract protected function deleteJob(Job $job); + + /** + * Release the job. This will trigger event EVENT_BEFORE_RELEASE and + * EVENT_AFTER_RELEASE. + * + * @param Job $job The job to delete. + * @return boolean whether the operation succeed. + */ + public function release(Job $job) + { + $this->trigger(self::EVENT_BEFORE_RELEASE, $beforeEvent = new Event(['job' => $job])); + if (!$beforeEvent->isValid) { + return false; + } + + $return = $this->releaseJob($job); + if (!$return) { + return false; + } + + $this->trigger(self::EVENT_AFTER_RELEASE, new Event(['job' => $job])); + return true; + } + + /** + * Release the job. Override this for the queue implementation. + * + * @param Job $job The job to release. + * @return boolean whether the operation succeed. + */ + abstract protected function releaseJob(Job $job); /** * Deserialize job to be executed. diff --git a/src/Queues/DbQueue.php b/src/Queues/DbQueue.php index 56d627d..8e88635 100644 --- a/src/Queues/DbQueue.php +++ b/src/Queues/DbQueue.php @@ -156,7 +156,7 @@ protected function flagRunningRow(array $row) * @param Job $job The job to post. * @return boolean whether operation succeed. */ - protected function postJob(Job &$job) + protected function postJob(Job $job) { return $this->db->createCommand()->insert($this->tableName, [ 'timestamp' => new \yii\db\Expression('NOW()'), @@ -191,7 +191,7 @@ public function deleteJob(Job $job) * @param Job $job The job to restore. * @return boolean whether the operation succeed. */ - public function restoreJob(Job $job) + public function releaseJob(Job $job) { return $this->db->createCommand()->update( $this->tableName, diff --git a/src/Queues/MemoryQueue.php b/src/Queues/MemoryQueue.php index 92ec096..9c2e36c 100644 --- a/src/Queues/MemoryQueue.php +++ b/src/Queues/MemoryQueue.php @@ -58,7 +58,7 @@ public function fetchJob() * @param Job $job The job to be posted to the queueu. * @return boolean Whether the post succeed. */ - public function postJob(Job &$job) + public function postJob(Job $job) { $job->id = mt_rand(0, 65535); $this->_jobs[] = $job; @@ -91,4 +91,17 @@ public function getJobs() { return $this->_jobs; } + + /** + * Release the job. + * + * @param Job $job The job to release. + * @return boolean whether the operation succeed. + */ + protected function releaseJob(Job $job) + { + $this->_jobs[] = $job; + return true; + } + } diff --git a/src/Queues/MultipleQueue.php b/src/Queues/MultipleQueue.php index d88e286..9250171 100644 --- a/src/Queues/MultipleQueue.php +++ b/src/Queues/MultipleQueue.php @@ -9,6 +9,7 @@ namespace UrbanIndo\Yii2\Queue\Queues; use UrbanIndo\Yii2\Queue\Job; +use UrbanIndo\Yii2\Queue\Queue; use UrbanIndo\Yii2\Queue\Strategies\Strategy; use UrbanIndo\Yii2\Queue\Strategies\RandomStrategy; @@ -18,7 +19,7 @@ * @author Petra Barus * @since 2015.02.25 */ -class MultipleQueue extends \UrbanIndo\Yii2\Queue\Queue +class MultipleQueue extends Queue { /** @@ -90,7 +91,7 @@ protected function fetchJob() * @param Job $job The job. * @return boolean Whether operation succeed. */ - protected function postJob(Job &$job) + protected function postJob(Job $job) { return $this->postToQueue($job, 0); } @@ -109,4 +110,18 @@ public function postToQueue(Job &$job, $index) } return $queue->post($job); } + + /** + * Release the job. + * + * @param Job $job The job to release. + * @return boolean whether the operation succeed. + */ + protected function releaseJob(Job $job) + { + $index = $job->header[self::HEADER_MULTIPLE_QUEUE_INDEX]; + $queue = $this->getQueue($index); + $queue->release($job); + } + } diff --git a/src/Queues/SqsQueue.php b/src/Queues/SqsQueue.php index 7883559..1c3b698 100644 --- a/src/Queues/SqsQueue.php +++ b/src/Queues/SqsQueue.php @@ -88,7 +88,7 @@ private function createJobFromMessage($message) * @param Job $job The job posted to the queue. * @return boolean whether operation succeed. */ - public function postJob(Job &$job) + public function postJob(Job $job) { $model = $this->_client->sendMessage([ 'QueueUrl' => $this->url, @@ -121,6 +121,27 @@ public function deleteJob(Job $job) return false; } } + + /** + * Release the job. + * + * @param Job $job The job to release. + * @return boolean whether the operation succeed. + */ + public function releaseJob(Job $job) + { + if (!empty($job->header['ReceiptHandle'])) { + $receiptHandle = $job->header['ReceiptHandle']; + $response = $this->_client->changeMessageVisibility([ + 'QueueUrl' => $this->url, + 'ReceiptHandle' => $receiptHandle, + 'VisibilityTimeout' => 0, + ]); + return $response !== null; + } else { + return false; + } + } /** * Returns the SQS client used. diff --git a/tests/Queues/DbQueueTest.php b/tests/Queues/DbQueueTest.php index fd7cc78..151b159 100644 --- a/tests/Queues/DbQueueTest.php +++ b/tests/Queues/DbQueueTest.php @@ -28,6 +28,9 @@ protected function setUp() ] ]); + if (in_array($tableName, Yii::$app->db->getSchema()->getTableNames())) { + Yii::$app->db->createCommand()->dropTable($tableName)->execute(); + } Yii::$app->db->createCommand() ->createTable($tableName, [ 'id' => 'BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT', From 848490e789de2c68e07469c913608112bbfedbde Mon Sep 17 00:00:00 2001 From: Petra Barus Date: Mon, 18 Jan 2016 16:43:33 +0700 Subject: [PATCH 5/8] Add Redis queue Add test for run method Add purge method Add size method. --- README.md | 23 ++- src/Queue.php | 27 +++- src/Queues/DbQueue.php | 22 +++ src/Queues/MemoryQueue.php | 39 ++--- src/Queues/MultipleQueue.php | 26 +++- src/Queues/RedisQueue.php | 120 +++++++++++++++ src/Queues/SqsQueue.php | 36 +++++ .../ActiveRecordDeferredEventBehaviorTest.php | 16 +- .../ActiveRecordDeferredEventHandlerTest.php | 14 +- ...RecordDeferredEventRoutingBehaviorTest.php | 12 +- tests/Behaviors/DeferredEventBehaviorTest.php | 8 +- tests/Behaviors/DeferredEventHandlerTest.php | 14 +- .../DeferredEventRoutingBehaviorTest.php | 10 +- tests/QueueTest.php | 4 +- tests/Queues/DbQueueTest.php | 85 +++++++++-- tests/Queues/RedisQueueTest.php | 142 ++++++++++++++++++ 16 files changed, 524 insertions(+), 74 deletions(-) create mode 100644 src/Queues/RedisQueue.php create mode 100644 tests/Queues/RedisQueueTest.php diff --git a/README.md b/README.md index 8c22d74..85f9393 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,9 @@ or add to the require section of your `composer.json` file. +To use Redis queue or RabbitMQ, you have to add `yiisoft/yii2-redis:*` or +`videlalvaro/php-amqplib: 2.5.*` respectively. + ## Setting Up After the installation, first step is to set the console controller. @@ -54,7 +57,7 @@ the task in the component. For example, queue using AWS SQS ```php 'components' => [ 'queue' => [ - 'class' => 'UrbanIndo\Yii2\Queue\SqsQueue', + 'class' => 'UrbanIndo\Yii2\Queue\Queues\SqsQueue', 'module' => 'task', 'url' => 'https://sqs.ap-southeast-1.amazonaws.com/123456789012/queue', 'config' => [ @@ -66,6 +69,22 @@ the task in the component. For example, queue using AWS SQS ] ``` +Or using Database queue + +```php +'components' => [ + 'db' => [ + //the db component + ], + 'queue' => [ + 'class' => 'UrbanIndo\Yii2\Queue\Queues\DbQueue', + 'db' => 'db', + 'tableName' => 'queue', + 'module' => 'task', + ] +] +``` + ## Usage ### Creating A Worker @@ -236,5 +255,5 @@ To run the tests, in the root directory execute below. ## Road Map -- Add more queue provider such as MySQL, Redis, MemCache, IronMQ, RabbitMQ. +- Add more queue provider such as Redis, MemCache, IronMQ, RabbitMQ. - Add priority queue. diff --git a/src/Queue.php b/src/Queue.php index 84f3177..a3e08a1 100644 --- a/src/Queue.php +++ b/src/Queue.php @@ -107,6 +107,13 @@ abstract class Queue extends \yii\base\Component * @var string */ public $serializer = 'json'; + + /** + * This will release automatically on execution failure. i.e. when + * the `run` method returns false or catch exception. + * @var boolean + */ + public $releaseOnFailure = true; /** * Initializes the module. @@ -205,6 +212,9 @@ public function run(Job $job) $id = $job->route; } \Yii::error("Fatal Error: Error running route '{$id}'. Message: {$e->getMessage()}", 'yii2queue'); + if ($this->releaseOnFailure) { + $this->release($job); + } throw new \yii\base\Exception( "Error running route '{$id}'. " . "Message: {$e->getMessage()}. " . @@ -218,9 +228,8 @@ public function run(Job $job) if ($retval !== false) { \Yii::info('Deleting job', 'yii2queue'); $this->delete($job); -// } else { -// \Yii::info('Releasing job', 'yii2queue'); -// $this->release($job); + } else if ($this->releaseOnFailure) { + $this->release($job); } } @@ -377,4 +386,16 @@ protected function serializeMessage($array) } return $data; } + + /** + * Returns the number of queue size. + * @return integer + */ + abstract public function getSize(); + + /** + * Purge the whole queue. + * @return boolean + */ + abstract public function purge(); } diff --git a/src/Queues/DbQueue.php b/src/Queues/DbQueue.php index 8e88635..ff03f85 100644 --- a/src/Queues/DbQueue.php +++ b/src/Queues/DbQueue.php @@ -199,4 +199,26 @@ public function releaseJob(Job $job) ['id' => $job->id] )->execute() == 1; } + + /** + * Returns the number of queue size. + * @return integer + */ + public function getSize() + { + return (new \yii\db\Query()) + ->select('*') + ->from($this->tableName) + ->where(['status' => self::STATUS_READY]) + ->count('*', $this->db); + } + + /** + * Purge the whole queue. + * @return boolean + */ + public function purge() + { + return false; + } } diff --git a/src/Queues/MemoryQueue.php b/src/Queues/MemoryQueue.php index 9c2e36c..7d49243 100644 --- a/src/Queues/MemoryQueue.php +++ b/src/Queues/MemoryQueue.php @@ -47,7 +47,7 @@ public function deleteJob(Job $job) */ public function fetchJob() { - if ($this->getQueueLength() == 0) { + if ($this->getSize() == 0) { return false; } $job = array_pop($this->_jobs); @@ -65,24 +65,6 @@ public function postJob(Job $job) return true; } - /** - * Returns the number of job. - * @return integer - */ - public function getQueueLength() - { - return count($this->_jobs); - } - - /** - * Empty the whole queue items. - * @return void - */ - public function emptyQueue() - { - $this->_jobs = []; - } - /** * Returns the jobs posted to the queue. * @return Job[] @@ -104,4 +86,23 @@ protected function releaseJob(Job $job) return true; } + /** + * Returns the number of queue size. + * @return integer + */ + public function getSize() + { + return count($this->_jobs); + } + + /** + * Purge the whole queue. + * @return boolean + */ + public function purge() + { + $this->_jobs = []; + return true; + } + } diff --git a/src/Queues/MultipleQueue.php b/src/Queues/MultipleQueue.php index 9250171..4752877 100644 --- a/src/Queues/MultipleQueue.php +++ b/src/Queues/MultipleQueue.php @@ -121,7 +121,29 @@ protected function releaseJob(Job $job) { $index = $job->header[self::HEADER_MULTIPLE_QUEUE_INDEX]; $queue = $this->getQueue($index); - $queue->release($job); + return $queue->release($job); + } + + /** + * Returns the total number of all queue size. + * @return integer + */ + public function getSize() + { + return array_sum(array_map(function (Queue $queue) { + return $queue->getSize(); + }, $this->queues)); + } + + /** + * Purge the whole queue. + * @return boolean + */ + public function purge() + { + foreach ($this->queues as $queue) { + $queue->purge(); + } + return true; } - } diff --git a/src/Queues/RedisQueue.php b/src/Queues/RedisQueue.php new file mode 100644 index 0000000..e664de8 --- /dev/null +++ b/src/Queues/RedisQueue.php @@ -0,0 +1,120 @@ + + * @since 2016.01.16 + */ + +namespace UrbanIndo\Yii2\Queue\Queues; + +use yii\redis\Connection; +use UrbanIndo\Yii2\Queue\Job; + +/** + * RedisQueue provides Redis storing for Queue. + * + * This uses `yiisoft/yii2-redis` extension that doesn't shipped in the default + * composer dependency. To use this you have to manually add `yiisoft/yii2-redis` + * in the `composer.json`. + * + * @author Petra Barus + * @since 2016.01.16 + */ +class RedisQueue extends \UrbanIndo\Yii2\Queue\Queue +{ + /** + * Stores the redis connection. + * @var string|array|Connection + */ + public $db = 'redis'; + + /** + * The name of the key to store the queue. + * @var string + */ + public $key = 'queue'; + + /** + * @return void + */ + public function init() + { + parent::init(); + $this->db = \yii\di\Instance::ensure($this->db, Connection::className()); + } + + /** + * Delete the job. + * + * @param Job $job The job to delete. + * @return boolean whether the operation succeed. + */ + public function deleteJob(Job $job) + { + return true; + } + + /** + * Return next job from the queue. + * @return Job|boolean the job or false if not found. + */ + protected function fetchJob() + { + $json = $this->db->lpop($this->key); + if ($json == false) { + return false; + } + $data = \yii\helpers\Json::decode($json); + $job = $this->deserialize($data['data']); + $job->id = $data['id']; + $job->header['serialized'] = $data['data']; + return $job; + } + + /** + * Post new job to the queue. This contains implementation for database. + * + * @param Job $job The job to post. + * @return boolean whether operation succeed. + */ + protected function postJob(Job $job) + { + return $this->db->rpush($this->key, \yii\helpers\Json::encode([ + 'id' => uniqid('queue_', true), + 'data' => $this->serialize($job), + ])); + } + + /** + * Put back job to the queue. + * + * @param Job $job The job to restore. + * @return boolean whether the operation succeed. + */ + protected function releaseJob(Job $job) + { + return $this->db->rpush($this->key, \yii\helpers\Json::encode([ + 'id' => $job->id, + 'data' => $job->header['serialized'], + ])); + } + + /** + * Returns the total number of all queue size. + * @return integer + */ + public function getSize() + { + return $this->db->llen($this->key); + } + + /** + * Purge the whole queue. + * @return boolean + */ + public function purge() + { + return $this->db->del($this->key); + } +} diff --git a/src/Queues/SqsQueue.php b/src/Queues/SqsQueue.php index 1c3b698..eea0c34 100644 --- a/src/Queues/SqsQueue.php +++ b/src/Queues/SqsQueue.php @@ -33,6 +33,13 @@ class SqsQueue extends Queue * @var array */ public $config = []; + + /** + * Due to ability of the queue message to be visible automatically after + * a certain of time, this is not required. + * @var boolean + */ + public $releaseOnFailure = false; /** * Stores the SQS client. @@ -152,4 +159,33 @@ public function getClient() { return $this->_client; } + + /** + * Returns the number of queue size. + * @return integer + */ + public function getSize() + { + $response = $this->getClient()->getQueueAttributes([ + 'QueueUrl' => $this->url, + 'AttributeNames' => [ + 'ApproximateNumberOfMessages' + ] + ]); + $attributes = $response->get('Attributes'); + return \yii\helpers\ArrayHelper::getValue($attributes, 'ApproximateNumberOfMessages', 0); + } + + /** + * Purge the whole queue. + * @return boolean + */ + public function purge() + { + $response = $this->getClient()->getQueueAttributes([ + 'QueueUrl' => $this->url, + ]); + return $response !== null; + } + } diff --git a/tests/Behaviors/ActiveRecordDeferredEventBehaviorTest.php b/tests/Behaviors/ActiveRecordDeferredEventBehaviorTest.php index 6d55f31..7c4876d 100644 --- a/tests/Behaviors/ActiveRecordDeferredEventBehaviorTest.php +++ b/tests/Behaviors/ActiveRecordDeferredEventBehaviorTest.php @@ -7,30 +7,30 @@ protected function setUp() { 'id' => 'pk', 'name' => 'string', ])->execute(); - Yii::$app->queue->emptyQueue(); + Yii::$app->queue->purge(); } public function testEventHandler() { $queue = Yii::$app->queue; /* @var $queue \UrbanIndo\Yii2\Queue\Queues\MemoryQueue */ - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $object1 = new TestActiveRecord(); $this->assertTrue($object1 instanceof TestActiveRecord); $object1->id = 1; $object1->name = 'start'; $object1->save(); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $job = $queue->fetch(); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $queue->run($job); $sameObject1 = TestActiveRecord::findOne(1); $this->assertEquals('done', $sameObject1->name); // $object1->name = 'test'; $object1->save(false); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $job = $queue->fetch(); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $queue->run($job); $sameObject1 = TestActiveRecord::findOne(1); $this->assertEquals('updated', $sameObject1->name); @@ -41,9 +41,9 @@ public function testEventHandler() { $object2->name = 'start'; $object2->scenario = 'test'; $object2->save(); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $job = $queue->fetch(); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $queue->run($job); $sameObject2 = TestActiveRecord::findOne(2); $this->assertEquals('test', $sameObject2->name); diff --git a/tests/Behaviors/ActiveRecordDeferredEventHandlerTest.php b/tests/Behaviors/ActiveRecordDeferredEventHandlerTest.php index 13c7758..b27cade 100644 --- a/tests/Behaviors/ActiveRecordDeferredEventHandlerTest.php +++ b/tests/Behaviors/ActiveRecordDeferredEventHandlerTest.php @@ -7,34 +7,34 @@ public static function setUpBeforeClass() { 'id' => 'pk', 'name' => 'string', ])->execute(); - Yii::$app->queue->emptyQueue(); + Yii::$app->queue->purge(); } public function testEventHandlerInActiveRecord() { $queue = Yii::$app->queue; /* @var $queue \UrbanIndo\Yii2\Queue\Queues\MemoryQueue */ - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $object1 = new ActiveRecordDeferredEventHandlerTestActiveRecord(); $object1->id = 1; $object1->name = 'test'; $object1->save(false); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $job = $queue->fetch(); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $queue->run($job); $object1->refresh(); $this->assertEquals('done', $object1->name); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $object2 = new ActiveRecordDeferredEventHandlerTestActiveRecord(); $object2->id = 2; $object2->name = 'test'; $object2->scenario = 'test'; $object2->save(false); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $job = $queue->fetch(); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $queue->run($job); $object2->refresh(); $this->assertEquals('test', $object2->name); diff --git a/tests/Behaviors/ActiveRecordDeferredEventRoutingBehaviorTest.php b/tests/Behaviors/ActiveRecordDeferredEventRoutingBehaviorTest.php index 1ca2d8e..e5b8b4a 100644 --- a/tests/Behaviors/ActiveRecordDeferredEventRoutingBehaviorTest.php +++ b/tests/Behaviors/ActiveRecordDeferredEventRoutingBehaviorTest.php @@ -7,34 +7,34 @@ protected function setUp() { 'id' => 'pk', 'name' => 'string', ])->execute(); - Yii::$app->queue->emptyQueue(); + Yii::$app->queue->purge(); } public function testEventRouting() { $queue = Yii::$app->queue; /* @var $queue \UrbanIndo\Yii2\Queue\Queues\MemoryQueue */ - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $model = new DeferredEventRoutingBehaviorTestActiveRecord(); $model->id = 5; $model->save(false); $model->trigger('eventTest'); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $job = $queue->fetch(); $this->assertEquals('test/index', $job->route); $this->assertFalse($job->isCallable()); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $this->assertEquals([ 'id' => 5, 'scenario' => 'default', ], $job->data); $model->trigger('eventTest2'); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $job = $queue->fetch(); $this->assertEquals('test/halo', $job->route); $this->assertFalse($job->isCallable()); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $this->assertEquals([ 'halo' => 5, 'scenario' => 'default', diff --git a/tests/Behaviors/DeferredEventBehaviorTest.php b/tests/Behaviors/DeferredEventBehaviorTest.php index b812a46..e229262 100644 --- a/tests/Behaviors/DeferredEventBehaviorTest.php +++ b/tests/Behaviors/DeferredEventBehaviorTest.php @@ -8,22 +8,22 @@ protected function setUp() { 'id' => 'pk', 'name' => 'string', ])->execute(); - Yii::$app->queue->emptyQueue(); + Yii::$app->queue->purge(); } public function testEventHandler() { $queue = Yii::$app->queue; /* @var $queue \UrbanIndo\Yii2\Queue\Queues\MemoryQueue */ - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $model = new TestModel(); $model->recordId = 1; $model->createRecord(); $model->triggerEvent(); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $job = $queue->fetch(); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $queue->run($job); $sameModel = DeferredEventBehaviorTestActiveRecord::findOne($model->recordId); diff --git a/tests/Behaviors/DeferredEventHandlerTest.php b/tests/Behaviors/DeferredEventHandlerTest.php index 3c79d6f..ddf6f6d 100644 --- a/tests/Behaviors/DeferredEventHandlerTest.php +++ b/tests/Behaviors/DeferredEventHandlerTest.php @@ -7,13 +7,13 @@ public static function setUpBeforeClass() { 'id' => 'pk', 'name' => 'string', ])->execute(); - Yii::$app->queue->emptyQueue(); + Yii::$app->queue->purge(); } public function testEventHandlerInSimpleComponent() { $queue = Yii::$app->queue; /* @var $queue \UrbanIndo\Yii2\Queue\Queues\MemoryQueue */ - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $component = new DeferredEventHandlerTestComponent(); $component->recordId = 1; $component->triggerEvent(); @@ -22,9 +22,9 @@ public function testEventHandlerInSimpleComponent() { $this->assertNotNull($model); $this->assertEquals('test', $model->name); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $job = $queue->fetch(); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $queue->run($job); $model->refresh(); @@ -34,7 +34,7 @@ public function testEventHandlerInSimpleComponent() { public function testEventHandlerInSimpleModel() { $queue = Yii::$app->queue; /* @var $queue \UrbanIndo\Yii2\Queue\Queues\MemoryQueue */ - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $model = new DeferredEventHandlerTestModel(); $model->recordId = 2; $model->triggerEvent(); @@ -43,9 +43,9 @@ public function testEventHandlerInSimpleModel() { $this->assertNotNull($model); $this->assertEquals('test', $model->name); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $job = $queue->fetch(); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $queue->run($job); $model->refresh(); diff --git a/tests/Behaviors/DeferredEventRoutingBehaviorTest.php b/tests/Behaviors/DeferredEventRoutingBehaviorTest.php index 71757fa..4b8bd61 100644 --- a/tests/Behaviors/DeferredEventRoutingBehaviorTest.php +++ b/tests/Behaviors/DeferredEventRoutingBehaviorTest.php @@ -6,25 +6,25 @@ public function testEventRouting() { $queue = Yii::$app->queue; /* @var $queue \UrbanIndo\Yii2\Queue\Queues\MemoryQueue */ - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $model = new DeferredEventRoutingBehaviorTestModel(); $model->trigger('eventTest'); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $model->id = 5; $job = $queue->fetch(); $this->assertEquals('test/index', $job->route); $this->assertFalse($job->isCallable()); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $this->assertEquals([ 'id' => 1, 'test' => 2, ], $job->data); $model->trigger('eventTest2'); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $job = $queue->fetch(); $this->assertEquals('test/halo', $job->route); $this->assertFalse($job->isCallable()); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $this->assertEquals([ 'halo' => 5 ], $job->data); diff --git a/tests/QueueTest.php b/tests/QueueTest.php index a34e89d..fcf5bb0 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -14,9 +14,9 @@ public function testQueueCatchingException() { throw new \Exception('Test'); } ])); - $this->assertEquals(1, $queue->getQueueLength()); + $this->assertEquals(1, $queue->getSize()); $job = $queue->fetch(); - $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals(0, $queue->getSize()); $queue->run($job); } } diff --git a/tests/Queues/DbQueueTest.php b/tests/Queues/DbQueueTest.php index 151b159..91e5025 100644 --- a/tests/Queues/DbQueueTest.php +++ b/tests/Queues/DbQueueTest.php @@ -4,13 +4,13 @@ class DbQueueTest extends TestCase { - - public $counter = 0; + + static $counter = 0; protected function setUp() { parent::setUp(); - $this->counter = 0; + DbQueueTest::$counter = 0; $faker = Faker\Factory::create(); $tableName = 'queue_' . $faker->firstNameMale; $this->mockApplication([ @@ -76,7 +76,7 @@ public function testPost() $this->assertEquals(0, $this->countTable()); $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { - $this->counter += 1; + DbQueueTest::$counter += 1; }])); $this->assertEquals(1, $this->countTable()); @@ -84,7 +84,7 @@ public function testPost() $this->assertEquals(1, $this->countTable(['status' => DbQueue::STATUS_READY])); $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { - $this->counter += 2; + DbQueueTest::$counter += 1; }])); $this->assertEquals(2, $this->countTable()); @@ -117,6 +117,45 @@ public function testFetch() $this->assertTrue($job instanceof UrbanIndo\Yii2\Queue\Job); } + public function testRun() + { + $queue = $this->getQueue(); + $db = Yii::$app->db; + $tableName = $queue->tableName; + + $this->assertEquals(0, $this->countTable()); + + $job = $queue->fetch(); + + $this->assertFalse($job); + + $this->assertEquals(0, $this->countTable(['status' => DbQueue::STATUS_ACTIVE])); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + DbQueueTest::$counter += 1; + }])); + + $job = $queue->fetch(); + + $this->assertEquals(1, $this->countTable(['status' => DbQueue::STATUS_ACTIVE])); + + $this->assertTrue($job instanceof UrbanIndo\Yii2\Queue\Job); + + $queue->run($job); + + $this->assertEquals(1, DbQueueTest::$counter); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + DbQueueTest::$counter += 2; + }])); + + $job = $queue->fetch(); + + $queue->run($job); + + $this->assertEquals(3, DbQueueTest::$counter); + } + public function testHardDelete() { $queue = $this->getQueue(); @@ -126,11 +165,11 @@ public function testHardDelete() $this->assertEquals(0, $this->countTable()); $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { - $this->counter += 1; + DbQueueTest::$counter += 1; }])); $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { - $this->counter += 1; + DbQueueTest::$counter += 1; }])); $this->assertEquals(2, $this->countTable()); @@ -155,11 +194,11 @@ public function testSoftDelete() $this->assertEquals(0, $this->countTable()); $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { - $this->counter += 1; + DbQueueTest::$counter += 1; }])); $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { - $this->counter += 1; + DbQueueTest::$counter += 1; }])); $this->assertEquals(2, $this->countTable(['status' => DbQueue::STATUS_READY])); @@ -179,4 +218,32 @@ public function testSoftDelete() $this->assertEquals(1, $this->countTable(['status' => DbQueue::STATUS_DELETED])); } + + public function testRelease() + { + $queue = $this->getQueue(); + $db = Yii::$app->db; + $tableName = $queue->tableName; + + $this->assertEquals(0, $this->countTable()); + + $job = $queue->fetch(); + + $this->assertFalse($job); + + $this->assertEquals(0, $this->countTable(['status' => DbQueue::STATUS_ACTIVE])); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + DbQueueTest::$counter += 1; + }])); + + $job = $queue->fetch(); + $this->assertTrue($job instanceof UrbanIndo\Yii2\Queue\Job); + + $this->assertEquals(1, $this->countTable(['status' => DbQueue::STATUS_ACTIVE])); + + $queue->release($job); + + $this->assertEquals(1, $this->countTable(['status' => DbQueue::STATUS_READY])); + } } diff --git a/tests/Queues/RedisQueueTest.php b/tests/Queues/RedisQueueTest.php new file mode 100644 index 0000000..13330bd --- /dev/null +++ b/tests/Queues/RedisQueueTest.php @@ -0,0 +1,142 @@ +firstNameMale; + $this->mockApplication([ + 'components' => [ + 'redis' => [ + 'class' => '\yii\redis\Connection', + 'hostname' => 'localhost', + 'port' => 6379, + ], + 'queue' => [ + 'class' => '\UrbanIndo\Yii2\Queue\Queues\RedisQueue', + 'key' => $queueName, + ] + ] + ]); + } + + /** + * + * @return \UrbanIndo\Yii2\Queue\Queues\RedisQueue + */ + public function getQueue() + { + return Yii::$app->queue; + } + + public function getCountItems() + { + $queue = $this->getQueue(); + $key = $queue->key; + return Yii::$app->redis->llen($key); + } + + public function testPost() + { + $queue = $this->getQueue(); + $this->assertEquals(0, $this->getCountItems()); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + RedisQueueTest::$counter += 1; + }])); + $this->assertEquals(1, $this->getCountItems()); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + RedisQueueTest::$counter += 1; + }])); + $this->assertEquals(2, $this->getCountItems()); + } + + public function testFetch() + { + $queue = $this->getQueue(); + $key = $queue->key; + $this->assertEquals(0, $this->getCountItems()); + + $job = $queue->fetch(); + $this->assertFalse($job); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + RedisQueueTest::$counter += 1; + }])); + $this->assertEquals(1, $this->getCountItems()); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + RedisQueueTest::$counter += 1; + }])); + $this->assertEquals(2, $this->getCountItems()); + + $job = $queue->fetch(); + $this->assertTrue($job instanceof \UrbanIndo\Yii2\Queue\Job); + + $this->assertEquals(1, $this->getCountItems()); + } + + public function testRun() + { + $queue = $this->getQueue(); + + $job = $queue->fetch(); + + $this->assertFalse($job); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + RedisQueueTest::$counter += 1; + }])); + + $job = $queue->fetch(); + + $this->assertTrue($job instanceof UrbanIndo\Yii2\Queue\Job); + + $queue->run($job); + + $this->assertEquals(1, RedisQueueTest::$counter); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + RedisQueueTest::$counter += 2; + }])); + + $job = $queue->fetch(); + + $queue->run($job); + + $this->assertEquals(3, RedisQueueTest::$counter); + } + + public function testRelease() + { + $queue = $this->getQueue(); + $key = $queue->key; + $this->assertEquals(0, $this->getCountItems()); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + RedisQueueTest::$counter += 1; + }])); + $this->assertEquals(1, $this->getCountItems()); + + $job = $queue->fetch(); + + $this->assertTrue($job instanceof \UrbanIndo\Yii2\Queue\Job); + + $this->assertEquals(0, $this->getCountItems()); + + $queue->release($job); + + $this->assertEquals(1, $this->getCountItems()); + + $job = $queue->fetch(); + + $this->assertEquals(0, $this->getCountItems()); + } + +} From 6bc8f0a0b7f51ca8535db4699556b6bfdd574850 Mon Sep 17 00:00:00 2001 From: Petra Barus Date: Tue, 19 Jan 2016 13:21:57 +0700 Subject: [PATCH 6/8] Add memory queue test. --- tests/Queues/MemoryQueueTest.php | 158 +++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 tests/Queues/MemoryQueueTest.php diff --git a/tests/Queues/MemoryQueueTest.php b/tests/Queues/MemoryQueueTest.php new file mode 100644 index 0000000..0c23142 --- /dev/null +++ b/tests/Queues/MemoryQueueTest.php @@ -0,0 +1,158 @@ +mockApplication([ + 'components' => [ + 'queue' => [ + 'class' => '\UrbanIndo\Yii2\Queue\Queues\MemoryQueue', + ] + ] + ]); + } + + /** + * + * @return \UrbanIndo\Yii2\Queue\Queues\MemoryQueue + */ + protected function getQueue() + { + return Yii::$app->queue; + } + + public function testPost() + { + $queue = $this->getQueue(); + + $this->assertEquals(0, $queue->getSize()); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + self::$counter += 1; + }])); + + $this->assertEquals(1, $queue->getSize()); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + self::$counter += 1; + }])); + + $this->assertEquals(2, $queue->getSize()); + } + + public function testFetch() + { + $queue = $this->getQueue(); + + $this->assertEquals(0, $queue->getSize()); + + $job = $queue->fetch(); + + $this->assertFalse($job); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + $this->counter += 1; + }])); + + $this->assertEquals(1, $queue->getSize()); + + $job = $queue->fetch(); + + $this->assertEquals(0, $queue->getSize()); + + $this->assertTrue($job instanceof UrbanIndo\Yii2\Queue\Job); + } + + public function testRun() + { + $queue = $this->getQueue(); + + $this->assertEquals(0, $queue->getSize()); + + $job = $queue->fetch(); + + $this->assertFalse($job); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + self::$counter += 1; + }])); + + $job = $queue->fetch(); + + $this->assertTrue($job instanceof UrbanIndo\Yii2\Queue\Job); + + $queue->run($job); + + $this->assertEquals(1, self::$counter); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + self::$counter += 2; + }])); + + $job = $queue->fetch(); + + $queue->run($job); + + $this->assertEquals(3, self::$counter); + } + + public function testDelete() + { + $queue = $this->getQueue(); + + $this->assertEquals(0, $queue->getSize()); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + self::$counter += 1; + }])); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + self::$counter += 1; + }])); + + $this->assertEquals(2, $queue->getSize()); + + $job = $queue->fetch(); + + $this->assertEquals(1, $queue->getSize()); + + $queue->delete($job); + + $this->assertEquals(1, $queue->getSize()); + + } + + public function testRelease() + { + $queue = $this->getQueue(); + + $this->assertEquals(0, $queue->getSize()); + + $job = $queue->fetch(); + + $this->assertFalse($job); + + $queue->post(new UrbanIndo\Yii2\Queue\Job(['route' => function () { + self::$counter += 1; + }])); + + $job = $queue->fetch(); + + $this->assertEquals(0, $queue->getSize()); + + $this->assertTrue($job instanceof UrbanIndo\Yii2\Queue\Job); + + $queue->release($job); + + $this->assertEquals(1, $queue->getSize()); + + } +} From a2ad08213fedd36471124dc999a00b861312566e Mon Sep 17 00:00:00 2001 From: Petra Barus Date: Tue, 19 Jan 2016 13:28:04 +0700 Subject: [PATCH 7/8] Fix travis scrript. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 655a2d2..918fcc6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,8 @@ services: - mysql - redis-server +script: ./vendor/bin/phpunit + before_script: #MySQL database init - mysql -uroot -e "CREATE DATABASE IF NOT EXISTS test;" From dd795feb7c774d5decad5b986f0309940e8760e5 Mon Sep 17 00:00:00 2001 From: Petra Barus Date: Thu, 4 Feb 2016 10:58:06 +0700 Subject: [PATCH 8/8] Fix sqsqueue namespace and changelog. --- CHANGELOG.md | 8 +++++++- README.md | 13 ++++++------- src/Queues/SqsQueue.php | 8 ++++---- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 482db00..f935390 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Change Log All notable changes to this project will be documented in this file. +## 1.3.0 +- Added implementation for DbQueue and RedisQueue. +- Added events for queue. +- Added `purge` method for queue. +- Refactoring code. + ## 1.2.3 - Passing scenario for model and active record. @@ -25,7 +31,7 @@ All notable changes to this project will be documented in this file. ## 2015-02-25 ### Changed -- Shorten `postJob`, `getJob`, `deleteJob`, `runJob` method name to `post`, +- Shorten `postJob`, `getJob`, `deleteJob`, `runJob` method name to `post`, `fetch`, `delete`, `run`. ### Fixed diff --git a/README.md b/README.md index 85f9393..ea67bd3 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ or add to the require section of your `composer.json` file. -To use Redis queue or RabbitMQ, you have to add `yiisoft/yii2-redis:*` or +To use Redis queue or RabbitMQ, you have to add `yiisoft/yii2-redis:*` or `videlalvaro/php-amqplib: 2.5.*` respectively. ## Setting Up @@ -96,7 +96,7 @@ e.g. ```php class FooController extends UrbanIndo\Yii2\Queue\Worker\Controller { - + public function actionBar($param1, $param2){ echo $param1; } @@ -111,7 +111,7 @@ e.g. ```php class FooController extends UrbanIndo\Yii2\Queue\Worker\Controller { - + public function actionBar($param1, $param2){ try { } catch (\Exception $ex){ @@ -171,7 +171,7 @@ To use this, add behavior in a component and implement the defined event handler [ 'class' => \UrbanIndo\Yii2\Queue\Behaviors\DeferredEventBehavior::class, 'events' => [ - self::EVENT_AFTER_VALIDATE => 'deferAfterValidate', + self::EVENT_AFTER_VALIDATE => 'deferAfterValidate', ] ] ]); @@ -201,7 +201,7 @@ object whose attributes are assigned from the attributes of the original object. ### Web End Point -We can use web endpoint to use the queue by adding `\UrbanIndo\Yii2\Queue\Web\Controller` +We can use web endpoint to use the queue by adding `\UrbanIndo\Yii2\Queue\Web\Controller` to the controller map. For example @@ -255,5 +255,4 @@ To run the tests, in the root directory execute below. ## Road Map -- Add more queue provider such as Redis, MemCache, IronMQ, RabbitMQ. -- Add priority queue. +- Add more queue provider such as MemCache, IronMQ, RabbitMQ. diff --git a/src/Queues/SqsQueue.php b/src/Queues/SqsQueue.php index eea0c34..ba4ab18 100644 --- a/src/Queues/SqsQueue.php +++ b/src/Queues/SqsQueue.php @@ -6,7 +6,7 @@ * @since 2015.02.24 */ -namespace UrbanIndo\Yii2\Queue; +namespace UrbanIndo\Yii2\Queue\Queues; use \Aws\Sqs\SqsClient; use UrbanIndo\Yii2\Queue\Job; @@ -17,7 +17,7 @@ * @author Petra Barus * @since 2015.02.24 */ -class SqsQueue extends Queue +class SqsQueue extends \UrbanIndo\Yii2\Queue\Queue { /** @@ -33,7 +33,7 @@ class SqsQueue extends Queue * @var array */ public $config = []; - + /** * Due to ability of the queue message to be visible automatically after * a certain of time, this is not required. @@ -128,7 +128,7 @@ public function deleteJob(Job $job) return false; } } - + /** * Release the job. *