joe 4 роки тому
батько
коміт
2ff3184fd9
81 змінених файлів з 4508 додано та 1765 видалено
  1. 2 1
      composer.json
  2. 63 2
      composer.lock
  3. 3 1
      vendor/composer/ClassLoader.php
  4. 326 1005
      vendor/composer/InstalledVersions.php
  5. 1 0
      vendor/composer/autoload_psr4.php
  6. 5 0
      vendor/composer/autoload_static.php
  7. 64 0
      vendor/composer/installed.json
  8. 738 755
      vendor/composer/installed.php
  9. 4 0
      vendor/pda/pheanstalk/.gitattributes
  10. 22 0
      vendor/pda/pheanstalk/.github/workflows/docs.yaml
  11. 11 0
      vendor/pda/pheanstalk/.gitignore
  12. 16 0
      vendor/pda/pheanstalk/.scrutinizer.yml
  13. 22 0
      vendor/pda/pheanstalk/LICENSE
  14. 234 0
      vendor/pda/pheanstalk/README.md
  15. 41 0
      vendor/pda/pheanstalk/composer.json
  16. 26 0
      vendor/pda/pheanstalk/docker-compose.yml
  17. 3 0
      vendor/pda/pheanstalk/dockerfiles/Dockerfile-beanstalkd
  18. 17 0
      vendor/pda/pheanstalk/dockerfiles/Dockerfile-phpunit
  19. 9 0
      vendor/pda/pheanstalk/phpcs.xml.dist
  20. 24 0
      vendor/pda/pheanstalk/phpdoc.dist.xml
  21. 47 0
      vendor/pda/pheanstalk/psalm.xml
  22. 57 0
      vendor/pda/pheanstalk/scripts/build_phar.php
  23. 45 0
      vendor/pda/pheanstalk/src/Command/AbstractCommand.php
  24. 48 0
      vendor/pda/pheanstalk/src/Command/BuryCommand.php
  25. 33 0
      vendor/pda/pheanstalk/src/Command/DeleteCommand.php
  26. 35 0
      vendor/pda/pheanstalk/src/Command/IgnoreCommand.php
  27. 18 0
      vendor/pda/pheanstalk/src/Command/JobCommand.php
  28. 43 0
      vendor/pda/pheanstalk/src/Command/KickCommand.php
  29. 44 0
      vendor/pda/pheanstalk/src/Command/KickJobCommand.php
  30. 26 0
      vendor/pda/pheanstalk/src/Command/ListTubeUsedCommand.php
  31. 24 0
      vendor/pda/pheanstalk/src/Command/ListTubesCommand.php
  32. 24 0
      vendor/pda/pheanstalk/src/Command/ListTubesWatchedCommand.php
  33. 55 0
      vendor/pda/pheanstalk/src/Command/PauseTubeCommand.php
  34. 69 0
      vendor/pda/pheanstalk/src/Command/PeekCommand.php
  35. 45 0
      vendor/pda/pheanstalk/src/Command/PeekJobCommand.php
  36. 98 0
      vendor/pda/pheanstalk/src/Command/PutCommand.php
  37. 61 0
      vendor/pda/pheanstalk/src/Command/ReleaseCommand.php
  38. 34 0
      vendor/pda/pheanstalk/src/Command/ReserveCommand.php
  39. 48 0
      vendor/pda/pheanstalk/src/Command/ReserveWithTimeoutCommand.php
  40. 25 0
      vendor/pda/pheanstalk/src/Command/StatsCommand.php
  41. 24 0
      vendor/pda/pheanstalk/src/Command/StatsJobCommand.php
  42. 24 0
      vendor/pda/pheanstalk/src/Command/StatsTubeCommand.php
  43. 37 0
      vendor/pda/pheanstalk/src/Command/TouchCommand.php
  44. 16 0
      vendor/pda/pheanstalk/src/Command/TubeCommand.php
  45. 28 0
      vendor/pda/pheanstalk/src/Command/UseCommand.php
  46. 25 0
      vendor/pda/pheanstalk/src/Command/WatchCommand.php
  47. 134 0
      vendor/pda/pheanstalk/src/Connection.php
  48. 52 0
      vendor/pda/pheanstalk/src/Contract/CommandInterface.php
  49. 9 0
      vendor/pda/pheanstalk/src/Contract/JobIdInterface.php
  50. 226 0
      vendor/pda/pheanstalk/src/Contract/PheanstalkInterface.php
  51. 44 0
      vendor/pda/pheanstalk/src/Contract/ResponseInterface.php
  52. 22 0
      vendor/pda/pheanstalk/src/Contract/ResponseParserInterface.php
  53. 13 0
      vendor/pda/pheanstalk/src/Contract/SocketFactoryInterface.php
  54. 42 0
      vendor/pda/pheanstalk/src/Contract/SocketInterface.php
  55. 12 0
      vendor/pda/pheanstalk/src/Exception.php
  56. 10 0
      vendor/pda/pheanstalk/src/Exception/ClientBadFormatException.php
  57. 12 0
      vendor/pda/pheanstalk/src/Exception/ClientException.php
  58. 12 0
      vendor/pda/pheanstalk/src/Exception/CommandException.php
  59. 20 0
      vendor/pda/pheanstalk/src/Exception/ConnectionException.php
  60. 9 0
      vendor/pda/pheanstalk/src/Exception/DeadlineSoonException.php
  61. 12 0
      vendor/pda/pheanstalk/src/Exception/JobNotFoundException.php
  62. 10 0
      vendor/pda/pheanstalk/src/Exception/JobTooBigException.php
  63. 12 0
      vendor/pda/pheanstalk/src/Exception/ServerBadFormatException.php
  64. 12 0
      vendor/pda/pheanstalk/src/Exception/ServerDrainingException.php
  65. 14 0
      vendor/pda/pheanstalk/src/Exception/ServerException.php
  66. 12 0
      vendor/pda/pheanstalk/src/Exception/ServerInternalErrorException.php
  67. 12 0
      vendor/pda/pheanstalk/src/Exception/ServerOutOfMemoryException.php
  68. 12 0
      vendor/pda/pheanstalk/src/Exception/ServerUnknownCommandException.php
  69. 12 0
      vendor/pda/pheanstalk/src/Exception/SocketException.php
  70. 55 0
      vendor/pda/pheanstalk/src/Job.php
  71. 27 0
      vendor/pda/pheanstalk/src/JobId.php
  72. 440 0
      vendor/pda/pheanstalk/src/Pheanstalk.php
  73. 60 0
      vendor/pda/pheanstalk/src/Response/ArrayResponse.php
  74. 105 0
      vendor/pda/pheanstalk/src/Socket/FileSocket.php
  75. 28 0
      vendor/pda/pheanstalk/src/Socket/FsockopenSocket.php
  76. 140 0
      vendor/pda/pheanstalk/src/Socket/SocketSocket.php
  77. 30 0
      vendor/pda/pheanstalk/src/Socket/StreamSocket.php
  78. 57 0
      vendor/pda/pheanstalk/src/Socket/WriteHistory.php
  79. 94 0
      vendor/pda/pheanstalk/src/SocketFactory.php
  80. 82 0
      vendor/pda/pheanstalk/src/YamlResponseParser.php
  81. 1 1
      vendor/services.php

+ 2 - 1
composer.json

@@ -40,7 +40,8 @@
         "topthink/think-multi-app": "^1.0",
         "topthink/think-template": "^2.0",
         "alibabacloud/dysmsapi": "^1.8",
-        "alipaysdk/easysdk": "^2.2"
+        "alipaysdk/easysdk": "^2.2",
+        "pda/pheanstalk": "^4.0"
     },
     "autoload": {
         "psr-4": {

+ 63 - 2
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "3d009b05ac9706ed1d4db16e7fcb6e91",
+    "content-hash": "8b32b790d656d038e194b27426d8d507",
     "packages": [
         {
             "name": "adbario/php-dot-notation",
@@ -1647,6 +1647,67 @@
             ],
             "time": "2018-07-02T15:55:56+00:00"
         },
+        {
+            "name": "pda/pheanstalk",
+            "version": "v4.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/pheanstalk/pheanstalk.git",
+                "reference": "6165573aad525d39b3ac8ae916214cb483a61263"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/pheanstalk/pheanstalk/zipball/6165573aad525d39b3ac8ae916214cb483a61263",
+                "reference": "6165573aad525d39b3ac8ae916214cb483a61263",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "php": ">=7.1.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Pheanstalk\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Paul Annesley",
+                    "email": "paul@annesley.cc",
+                    "homepage": "http://paul.annesley.cc/",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sam Mousa",
+                    "email": "sam@mousa.nl",
+                    "role": "Maintainer"
+                }
+            ],
+            "description": "PHP client for beanstalkd queue",
+            "homepage": "https://github.com/pheanstalk/pheanstalk",
+            "keywords": [
+                "beanstalkd"
+            ],
+            "support": {
+                "issues": "https://github.com/pheanstalk/pheanstalk/issues",
+                "source": "https://github.com/pheanstalk/pheanstalk/tree/v4.0.3"
+            },
+            "time": "2020-09-22T07:17:48+00:00"
+        },
         {
             "name": "phpoffice/phpexcel",
             "version": "1.8.2",
@@ -3719,5 +3780,5 @@
         "ext-mbstring": "*"
     },
     "platform-dev": [],
-    "plugin-api-version": "2.0.0"
+    "plugin-api-version": "2.1.0"
 }

+ 3 - 1
vendor/composer/ClassLoader.php

@@ -338,7 +338,7 @@ class ClassLoader
      * Loads the given class or interface.
      *
      * @param  string    $class The name of the class
-     * @return bool|null True if loaded, null otherwise
+     * @return true|null True if loaded, null otherwise
      */
     public function loadClass($class)
     {
@@ -347,6 +347,8 @@ class ClassLoader
 
             return true;
         }
+
+        return null;
     }
 
     /**

+ 326 - 1005
vendor/composer/InstalledVersions.php

@@ -1,1016 +1,337 @@
 <?php
 
-
-
-
-
-
-
-
-
-
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
 
 namespace Composer;
 
 use Composer\Autoload\ClassLoader;
 use Composer\Semver\VersionParser;
 
-
-
-
-
-
+/**
+ * This class is copied in every Composer installed project and available to all
+ *
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
+ *
+ * To require it's presence, you can require `composer-runtime-api ^2.0`
+ */
 class InstalledVersions
 {
-private static $installed = array (
-  'root' => 
-  array (
-    'pretty_version' => 'dev-master',
-    'version' => 'dev-master',
-    'aliases' => 
-    array (
-    ),
-    'reference' => 'e9936b7848d32f25ff5761d416432dc0090b9efb',
-    'name' => 'topthink/think',
-  ),
-  'versions' => 
-  array (
-    'adbario/php-dot-notation' => 
-    array (
-      'pretty_version' => '2.2.0',
-      'version' => '2.2.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'eee4fc81296531e6aafba4c2bbccfc5adab1676e',
-    ),
-    'alibabacloud/client' => 
-    array (
-      'pretty_version' => '1.5.30',
-      'version' => '1.5.30.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '1f497bb79835b84094318a70b672eb88260f2682',
-    ),
-    'alibabacloud/dysmsapi' => 
-    array (
-      'pretty_version' => '1.8.958',
-      'version' => '1.8.958.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '1715a5e4c10520116cf531ed20c287ae488b3489',
-    ),
-    'alibabacloud/tea' => 
-    array (
-      'pretty_version' => '3.1.21',
-      'version' => '3.1.21.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '379faffe240ee97134cf3f796cb28059f9fb7fa9',
-    ),
-    'alibabacloud/tea-fileform' => 
-    array (
-      'pretty_version' => '0.3.4',
-      'version' => '0.3.4.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '4bf0c75a045c8115aa8cb1a394bd08d8bb833181',
-    ),
-    'alipaysdk/easysdk' => 
-    array (
-      'pretty_version' => '2.2.0',
-      'version' => '2.2.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '7a1cfa83c7e140bded957498ea072c77611e6480',
-    ),
-    'aliyuncs/oss-sdk-php' => 
-    array (
-      'pretty_version' => 'v2.3.0',
-      'version' => '2.3.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'e69f57916678458642ac9d2fd341ae78a56996c8',
-    ),
-    'clagiordano/weblibs-configmanager' => 
-    array (
-      'pretty_version' => 'v1.1.0',
-      'version' => '1.1.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'ecf584f5b3a27929175ff0abdba52f0131bef795',
-    ),
-    'danielstjules/stringy' => 
-    array (
-      'pretty_version' => '3.1.0',
-      'version' => '3.1.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'df24ab62d2d8213bbbe88cc36fc35a4503b4bd7e',
-    ),
-    'dh2y/think-qrcode' => 
-    array (
-      'pretty_version' => '2.0',
-      'version' => '2.0.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '977d032afa27b1852f5fc5441fad2497f6db7ff5',
-    ),
-    'doctrine/cache' => 
-    array (
-      'pretty_version' => 'v1.4.4',
-      'version' => '1.4.4.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '6433826dd02c9e5be8a127320dc13e7e6625d020',
-    ),
-    'firebase/php-jwt' => 
-    array (
-      'pretty_version' => 'v5.0.0',
-      'version' => '5.0.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '9984a4d3a32ae7673d6971ea00bae9d0a1abba0e',
-    ),
-    'guzzle/batch' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/cache' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/common' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/guzzle' => 
-    array (
-      'pretty_version' => 'v3.9.3',
-      'version' => '3.9.3.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '0645b70d953bc1c067bbc8d5bc53194706b628d9',
-    ),
-    'guzzle/http' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/inflection' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/iterator' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/log' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/parser' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-async' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-backoff' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-cache' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-cookie' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-curlauth' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-error-response' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-history' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-log' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-md5' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-mock' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-oauth' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/service' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/stream' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzlehttp/guzzle' => 
-    array (
-      'pretty_version' => '6.3.3',
-      'version' => '6.3.3.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '407b0cb880ace85c9b63c5f9551db498cb2d50ba',
-    ),
-    'guzzlehttp/promises' => 
-    array (
-      'pretty_version' => 'v1.3.1',
-      'version' => '1.3.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'a59da6cf61d80060647ff4d3eb2c03a2bc694646',
-    ),
-    'guzzlehttp/psr7' => 
-    array (
-      'pretty_version' => '1.5.2',
-      'version' => '1.5.2.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '9f83dded91781a01c63574e387eaa769be769115',
-    ),
-    'league/flysystem' => 
-    array (
-      'pretty_version' => '1.0.57',
-      'version' => '1.0.57.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a',
-    ),
-    'league/flysystem-cached-adapter' => 
-    array (
-      'pretty_version' => '1.0.9',
-      'version' => '1.0.9.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '08ef74e9be88100807a3b92cc9048a312bf01d6f',
-    ),
-    'monolog/monolog' => 
-    array (
-      'pretty_version' => '1.24.0',
-      'version' => '1.24.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266',
-    ),
-    'mtdowling/jmespath.php' => 
-    array (
-      'pretty_version' => '2.5.0',
-      'version' => '2.5.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '52168cb9472de06979613d365c7f1ab8798be895',
-    ),
-    'nesbot/carbon' => 
-    array (
-      'pretty_version' => '2.20.0',
-      'version' => '2.20.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'bc671b896c276795fad8426b0aa24e8ade0f2498',
-    ),
-    'opis/closure' => 
-    array (
-      'pretty_version' => '3.4.1',
-      'version' => '3.4.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'e79f851749c3caa836d7ccc01ede5828feb762c7',
-    ),
-    'overtrue/socialite' => 
-    array (
-      'pretty_version' => '1.3.0',
-      'version' => '1.3.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'fda55f0acef43a144799b1957a8f93d9f5deffce',
-    ),
-    'overtrue/wechat' => 
-    array (
-      'pretty_version' => '3.3.33',
-      'version' => '3.3.33.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '78e5476df330754040d1c400d0bca640d5b77cb7',
-    ),
-    'paragonie/random_compat' => 
-    array (
-      'pretty_version' => 'v9.99.99',
-      'version' => '9.99.99.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95',
-    ),
-    'phpoffice/phpexcel' => 
-    array (
-      'pretty_version' => '1.8.2',
-      'version' => '1.8.2.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '1441011fb7ecdd8cc689878f54f8b58a6805f870',
-    ),
-    'pimple/pimple' => 
-    array (
-      'pretty_version' => 'v3.2.3',
-      'version' => '3.2.3.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '9e403941ef9d65d20cba7d54e29fe906db42cf32',
-    ),
-    'psr/cache' => 
-    array (
-      'pretty_version' => '1.0.1',
-      'version' => '1.0.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'd11b50ad223250cf17b86e38383413f5a6764bf8',
-    ),
-    'psr/container' => 
-    array (
-      'pretty_version' => '1.0.0',
-      'version' => '1.0.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'b7ce3b176482dbbc1245ebf52b181af44c2cf55f',
-    ),
-    'psr/http-message' => 
-    array (
-      'pretty_version' => '1.0.1',
-      'version' => '1.0.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
-    ),
-    'psr/http-message-implementation' => 
-    array (
-      'provided' => 
-      array (
-        0 => '1.0',
-      ),
-    ),
-    'psr/log' => 
-    array (
-      'pretty_version' => '1.1.2',
-      'version' => '1.1.2.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '446d54b4cb6bf489fc9d75f55843658e6f25d801',
-    ),
-    'psr/log-implementation' => 
-    array (
-      'provided' => 
-      array (
-        0 => '1.0.0',
-      ),
-    ),
-    'psr/simple-cache' => 
-    array (
-      'pretty_version' => '1.0.1',
-      'version' => '1.0.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b',
-    ),
-    'qcloud/cos-sdk-v5' => 
-    array (
-      'pretty_version' => 'v1.3.3',
-      'version' => '1.3.3.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'cd1b9cefa04521eaf125a82eb53552d9a87aae4d',
-    ),
-    'qiniu/php-sdk' => 
-    array (
-      'pretty_version' => 'v7.2.9',
-      'version' => '7.2.9.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'afe7d8715d8a688b1d8d8cdf031240d2363dad90',
-    ),
-    'ralouphie/getallheaders' => 
-    array (
-      'pretty_version' => '2.0.5',
-      'version' => '2.0.5.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '5601c8a83fbba7ef674a7369456d12f1e0d0eafa',
-    ),
-    'songshenzong/support' => 
-    array (
-      'pretty_version' => '2.0.5',
-      'version' => '2.0.5.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '34973c04ffcf226e503f1c3a69d30ac49f7621f6',
-    ),
-    'spatie/macroable' => 
-    array (
-      'pretty_version' => '1.0.0',
-      'version' => '1.0.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '74b0d189ce75142f1706aad834d5a428dfc7c3c3',
-    ),
-    'symfony/event-dispatcher' => 
-    array (
-      'pretty_version' => 'v2.8.50',
-      'version' => '2.8.50.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'a77e974a5fecb4398833b0709210e3d5e334ffb0',
-    ),
-    'symfony/http-foundation' => 
-    array (
-      'pretty_version' => 'v3.4.28',
-      'version' => '3.4.28.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '677ae5e892b081e71a665bfa7dd90fe61800c00e',
-    ),
-    'symfony/polyfill-mbstring' => 
-    array (
-      'pretty_version' => 'v1.11.0',
-      'version' => '1.11.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'fe5e94c604826c35a32fa832f35bd036b6799609',
-    ),
-    'symfony/polyfill-php70' => 
-    array (
-      'pretty_version' => 'v1.11.0',
-      'version' => '1.11.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'bc4858fb611bda58719124ca079baff854149c89',
-    ),
-    'symfony/polyfill-php72' => 
-    array (
-      'pretty_version' => 'v1.11.0',
-      'version' => '1.11.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'ab50dcf166d5f577978419edd37aa2bb8eabce0c',
-    ),
-    'symfony/process' => 
-    array (
-      'pretty_version' => 'v4.3.2',
-      'version' => '4.3.2.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '856d35814cf287480465bb7a6c413bb7f5f5e69c',
-    ),
-    'symfony/psr-http-message-bridge' => 
-    array (
-      'pretty_version' => 'v1.2.0',
-      'version' => '1.2.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '9ab9d71f97d5c7d35a121a7fb69f74fee95cd0ad',
-    ),
-    'symfony/translation' => 
-    array (
-      'pretty_version' => 'v4.3.2',
-      'version' => '4.3.2.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '934ab1d18545149e012aa898cf02e9f23790f7a0',
-    ),
-    'symfony/translation-contracts' => 
-    array (
-      'pretty_version' => 'v1.1.5',
-      'version' => '1.1.5.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'cb4b18ad7b92a26e83b65dde940fab78339e6f3c',
-    ),
-    'symfony/translation-implementation' => 
-    array (
-      'provided' => 
-      array (
-        0 => '1.0',
-      ),
-    ),
-    'symfony/var-dumper' => 
-    array (
-      'pretty_version' => 'v4.3.1',
-      'version' => '4.3.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'f974f448154928d2b5fb7c412bd23b81d063f34b',
-    ),
-    'topthink/framework' => 
-    array (
-      'pretty_version' => 'v6.0.0',
-      'version' => '6.0.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '79c555aab0313d1a33ddcdb3c395f2c47f37f597',
-    ),
-    'topthink/think' => 
-    array (
-      'pretty_version' => 'dev-master',
-      'version' => 'dev-master',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'e9936b7848d32f25ff5761d416432dc0090b9efb',
-    ),
-    'topthink/think-captcha' => 
-    array (
-      'pretty_version' => 'v3.0.1',
-      'version' => '3.0.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '9fc0c627d773f6a54a8dd142ebf358f746557a48',
-    ),
-    'topthink/think-factory' => 
-    array (
-      'pretty_version' => 'v1.0.1',
-      'version' => '1.0.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'b8080a6472aae1cff47ceb8c30feec3c2835364b',
-    ),
-    'topthink/think-helper' => 
-    array (
-      'pretty_version' => 'v3.1.3',
-      'version' => '3.1.3.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '4d85dfd3778623bbb1de3648f1dcd0c82f4439f4',
-    ),
-    'topthink/think-image' => 
-    array (
-      'pretty_version' => 'v1.0.7',
-      'version' => '1.0.7.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '8586cf47f117481c6d415b20f7dedf62e79d5512',
-    ),
-    'topthink/think-multi-app' => 
-    array (
-      'pretty_version' => 'v1.0.11',
-      'version' => '1.0.11.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '215f4a6bb88e53ad41b448c61957336eb55ce6f9',
-    ),
-    'topthink/think-orm' => 
-    array (
-      'pretty_version' => 'v2.0.27',
-      'version' => '2.0.27.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '02affaaccade2cdd8bbb2d2f5d15e46113e6eb50',
-    ),
-    'topthink/think-queue' => 
-    array (
-      'pretty_version' => 'v3.0.2',
-      'version' => '3.0.2.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'c34b983abce9427fca7e30ac983b75041f436ad0',
-    ),
-    'topthink/think-template' => 
-    array (
-      'pretty_version' => 'v2.0.7',
-      'version' => '2.0.7.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'e98bdbb4a4c94b442f17dfceba81e0134d4fbd19',
-    ),
-    'topthink/think-view' => 
-    array (
-      'pretty_version' => 'v1.0.13',
-      'version' => '1.0.13.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '90803b73f781db5d42619082c4597afc58b2d4c5',
-    ),
-    'workerman/channel' => 
-    array (
-      'pretty_version' => 'v1.0.5',
-      'version' => '1.0.5.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '0836a9a413c6e8425ee36307d95e2e49cc380f50',
-    ),
-    'workerman/workerman' => 
-    array (
-      'pretty_version' => 'v3.5.19',
-      'version' => '3.5.19.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '4e5c24073b431fd950287efbfb5cc9b4c0fc7367',
-    ),
-    'xaboy/form-builder' => 
-    array (
-      'pretty_version' => '1.2.10',
-      'version' => '1.2.10.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '198c5f066499eef8b005f5d504fcb6120fa3ac04',
-    ),
-    'xin/container' => 
-    array (
-      'pretty_version' => '2.0.1',
-      'version' => '2.0.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '97bb67f87dd851545938a1f2fe0ffbd379e3ff81',
-    ),
-    'xin/helper' => 
-    array (
-      'pretty_version' => '1.0.0',
-      'version' => '1.0.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '02a58132dae2aea2d1c0b8e66f55125969224747',
-    ),
-  ),
-);
-private static $canGetVendors;
-private static $installedByVendor = array();
-
-
-
-
-
-
-
-public static function getInstalledPackages()
-{
-$packages = array();
-foreach (self::getInstalled() as $installed) {
-$packages[] = array_keys($installed['versions']);
-}
-
-
-if (1 === \count($packages)) {
-return $packages[0];
-}
-
-return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
-}
-
-
-
-
-
-
-
-
-
-public static function isInstalled($packageName)
-{
-foreach (self::getInstalled() as $installed) {
-if (isset($installed['versions'][$packageName])) {
-return true;
-}
-}
-
-return false;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-public static function satisfies(VersionParser $parser, $packageName, $constraint)
-{
-$constraint = $parser->parseConstraints($constraint);
-$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
-
-return $provided->matches($constraint);
-}
-
-
-
-
-
-
-
-
-
-
-public static function getVersionRanges($packageName)
-{
-foreach (self::getInstalled() as $installed) {
-if (!isset($installed['versions'][$packageName])) {
-continue;
-}
-
-$ranges = array();
-if (isset($installed['versions'][$packageName]['pretty_version'])) {
-$ranges[] = $installed['versions'][$packageName]['pretty_version'];
-}
-if (array_key_exists('aliases', $installed['versions'][$packageName])) {
-$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
-}
-if (array_key_exists('replaced', $installed['versions'][$packageName])) {
-$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
-}
-if (array_key_exists('provided', $installed['versions'][$packageName])) {
-$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
-}
-
-return implode(' || ', $ranges);
-}
-
-throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
-}
-
-
-
-
-
-public static function getVersion($packageName)
-{
-foreach (self::getInstalled() as $installed) {
-if (!isset($installed['versions'][$packageName])) {
-continue;
-}
-
-if (!isset($installed['versions'][$packageName]['version'])) {
-return null;
-}
-
-return $installed['versions'][$packageName]['version'];
-}
-
-throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
-}
-
-
-
-
-
-public static function getPrettyVersion($packageName)
-{
-foreach (self::getInstalled() as $installed) {
-if (!isset($installed['versions'][$packageName])) {
-continue;
-}
-
-if (!isset($installed['versions'][$packageName]['pretty_version'])) {
-return null;
-}
-
-return $installed['versions'][$packageName]['pretty_version'];
-}
-
-throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
-}
-
-
-
-
-
-public static function getReference($packageName)
-{
-foreach (self::getInstalled() as $installed) {
-if (!isset($installed['versions'][$packageName])) {
-continue;
-}
-
-if (!isset($installed['versions'][$packageName]['reference'])) {
-return null;
-}
-
-return $installed['versions'][$packageName]['reference'];
-}
-
-throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
-}
-
-
-
-
-
-public static function getRootPackage()
-{
-$installed = self::getInstalled();
-
-return $installed[0]['root'];
-}
-
-
-
-
-
-
-
-public static function getRawData()
-{
-return self::$installed;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-public static function reload($data)
-{
-self::$installed = $data;
-self::$installedByVendor = array();
-}
-
-
-
-
-private static function getInstalled()
-{
-if (null === self::$canGetVendors) {
-self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
-}
-
-$installed = array();
-
-if (self::$canGetVendors) {
-foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
-if (isset(self::$installedByVendor[$vendorDir])) {
-$installed[] = self::$installedByVendor[$vendorDir];
-} elseif (is_file($vendorDir.'/composer/installed.php')) {
-$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
-}
-}
-}
-
-$installed[] = self::$installed;
-
-return $installed;
-}
+    private static $installed;
+    private static $canGetVendors;
+    private static $installedByVendor = array();
+
+    /**
+     * Returns a list of all package names which are present, either by being installed, replaced or provided
+     *
+     * @return string[]
+     * @psalm-return list<string>
+     */
+    public static function getInstalledPackages()
+    {
+        $packages = array();
+        foreach (self::getInstalled() as $installed) {
+            $packages[] = array_keys($installed['versions']);
+        }
+
+        if (1 === \count($packages)) {
+            return $packages[0];
+        }
+
+        return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+    }
+
+    /**
+     * Returns a list of all package names with a specific type e.g. 'library'
+     *
+     * @param  string   $type
+     * @return string[]
+     * @psalm-return list<string>
+     */
+    public static function getInstalledPackagesByType($type)
+    {
+        $packagesByType = array();
+
+        foreach (self::getInstalled() as $installed) {
+            foreach ($installed['versions'] as $name => $package) {
+                if (isset($package['type']) && $package['type'] === $type) {
+                    $packagesByType[] = $name;
+                }
+            }
+        }
+
+        return $packagesByType;
+    }
+
+    /**
+     * Checks whether the given package is installed
+     *
+     * This also returns true if the package name is provided or replaced by another package
+     *
+     * @param  string $packageName
+     * @param  bool   $includeDevRequirements
+     * @return bool
+     */
+    public static function isInstalled($packageName, $includeDevRequirements = true)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (isset($installed['versions'][$packageName])) {
+                return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks whether the given package satisfies a version constraint
+     *
+     * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
+     *
+     *   Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
+     *
+     * @param  VersionParser $parser      Install composer/semver to have access to this class and functionality
+     * @param  string        $packageName
+     * @param  string|null   $constraint  A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
+     * @return bool
+     */
+    public static function satisfies(VersionParser $parser, $packageName, $constraint)
+    {
+        $constraint = $parser->parseConstraints($constraint);
+        $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+        return $provided->matches($constraint);
+    }
+
+    /**
+     * Returns a version constraint representing all the range(s) which are installed for a given package
+     *
+     * It is easier to use this via isInstalled() with the $constraint argument if you need to check
+     * whether a given version of a package is installed, and not just whether it exists
+     *
+     * @param  string $packageName
+     * @return string Version constraint usable with composer/semver
+     */
+    public static function getVersionRanges($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            $ranges = array();
+            if (isset($installed['versions'][$packageName]['pretty_version'])) {
+                $ranges[] = $installed['versions'][$packageName]['pretty_version'];
+            }
+            if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+                $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+            }
+            if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+                $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+            }
+            if (array_key_exists('provided', $installed['versions'][$packageName])) {
+                $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+            }
+
+            return implode(' || ', $ranges);
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+     */
+    public static function getVersion($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            if (!isset($installed['versions'][$packageName]['version'])) {
+                return null;
+            }
+
+            return $installed['versions'][$packageName]['version'];
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+     */
+    public static function getPrettyVersion($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+                return null;
+            }
+
+            return $installed['versions'][$packageName]['pretty_version'];
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
+     */
+    public static function getReference($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            if (!isset($installed['versions'][$packageName]['reference'])) {
+                return null;
+            }
+
+            return $installed['versions'][$packageName]['reference'];
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
+     */
+    public static function getInstallPath($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @return array
+     * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}
+     */
+    public static function getRootPackage()
+    {
+        $installed = self::getInstalled();
+
+        return $installed[0]['root'];
+    }
+
+    /**
+     * Returns the raw installed.php data for custom implementations
+     *
+     * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
+     * @return array[]
+     * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}
+     */
+    public static function getRawData()
+    {
+        @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+        if (null === self::$installed) {
+            // only require the installed.php file if this file is loaded from its dumped location,
+            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+            if (substr(__DIR__, -8, 1) !== 'C') {
+                self::$installed = include __DIR__ . '/installed.php';
+            } else {
+                self::$installed = array();
+            }
+        }
+
+        return self::$installed;
+    }
+
+    /**
+     * Returns the raw data of all installed.php which are currently loaded for custom implementations
+     *
+     * @return array[]
+     * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
+     */
+    public static function getAllRawData()
+    {
+        return self::getInstalled();
+    }
+
+    /**
+     * Lets you reload the static array from another file
+     *
+     * This is only useful for complex integrations in which a project needs to use
+     * this class but then also needs to execute another project's autoloader in process,
+     * and wants to ensure both projects have access to their version of installed.php.
+     *
+     * A typical case would be PHPUnit, where it would need to make sure it reads all
+     * the data it needs from this class, then call reload() with
+     * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
+     * the project in which it runs can then also use this class safely, without
+     * interference between PHPUnit's dependencies and the project's dependencies.
+     *
+     * @param  array[] $data A vendor/composer/installed.php data set
+     * @return void
+     *
+     * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>} $data
+     */
+    public static function reload($data)
+    {
+        self::$installed = $data;
+        self::$installedByVendor = array();
+    }
+
+    /**
+     * @return array[]
+     * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
+     */
+    private static function getInstalled()
+    {
+        if (null === self::$canGetVendors) {
+            self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+        }
+
+        $installed = array();
+
+        if (self::$canGetVendors) {
+            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+                if (isset(self::$installedByVendor[$vendorDir])) {
+                    $installed[] = self::$installedByVendor[$vendorDir];
+                } elseif (is_file($vendorDir.'/composer/installed.php')) {
+                    $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
+                    if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
+                        self::$installed = $installed[count($installed) - 1];
+                    }
+                }
+            }
+        }
+
+        if (null === self::$installed) {
+            // only require the installed.php file if this file is loaded from its dumped location,
+            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+            if (substr(__DIR__, -8, 1) !== 'C') {
+                self::$installed = require __DIR__ . '/installed.php';
+            } else {
+                self::$installed = array();
+            }
+        }
+        $installed[] = self::$installed;
+
+        return $installed;
+    }
 }

+ 1 - 0
vendor/composer/autoload_psr4.php

@@ -36,6 +36,7 @@ return array(
     'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
     'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
     'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
+    'Pheanstalk\\' => array($vendorDir . '/pda/pheanstalk/src'),
     'Overtrue\\Socialite\\' => array($vendorDir . '/overtrue/socialite/src'),
     'Opis\\Closure\\' => array($vendorDir . '/opis/closure/src'),
     'OSS\\' => array($vendorDir . '/aliyuncs/oss-sdk-php/src/OSS'),

+ 5 - 0
vendor/composer/autoload_static.php

@@ -86,6 +86,7 @@ class ComposerStaticInitf16474ac994ccc25392f403933800b79
             'Psr\\Http\\Message\\' => 17,
             'Psr\\Container\\' => 14,
             'Psr\\Cache\\' => 10,
+            'Pheanstalk\\' => 11,
         ),
         'O' => 
         array (
@@ -264,6 +265,10 @@ class ComposerStaticInitf16474ac994ccc25392f403933800b79
         array (
             0 => __DIR__ . '/..' . '/psr/cache/src',
         ),
+        'Pheanstalk\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/pda/pheanstalk/src',
+        ),
         'Overtrue\\Socialite\\' => 
         array (
             0 => __DIR__ . '/..' . '/overtrue/socialite/src',

+ 64 - 0
vendor/composer/installed.json

@@ -1716,6 +1716,70 @@
             ],
             "install-path": "../paragonie/random_compat"
         },
+        {
+            "name": "pda/pheanstalk",
+            "version": "v4.0.3",
+            "version_normalized": "4.0.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/pheanstalk/pheanstalk.git",
+                "reference": "6165573aad525d39b3ac8ae916214cb483a61263"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/pheanstalk/pheanstalk/zipball/6165573aad525d39b3ac8ae916214cb483a61263",
+                "reference": "6165573aad525d39b3ac8ae916214cb483a61263",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "php": ">=7.1.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7"
+            },
+            "time": "2020-09-22T07:17:48+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Pheanstalk\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Paul Annesley",
+                    "email": "paul@annesley.cc",
+                    "homepage": "http://paul.annesley.cc/",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sam Mousa",
+                    "email": "sam@mousa.nl",
+                    "role": "Maintainer"
+                }
+            ],
+            "description": "PHP client for beanstalkd queue",
+            "homepage": "https://github.com/pheanstalk/pheanstalk",
+            "keywords": [
+                "beanstalkd"
+            ],
+            "support": {
+                "issues": "https://github.com/pheanstalk/pheanstalk/issues",
+                "source": "https://github.com/pheanstalk/pheanstalk/tree/v4.0.3"
+            },
+            "install-path": "../pda/pheanstalk"
+        },
         {
             "name": "phpoffice/phpexcel",
             "version": "1.8.2",

+ 738 - 755
vendor/composer/installed.php

@@ -1,757 +1,740 @@
-<?php return array (
-  'root' => 
-  array (
-    'pretty_version' => 'dev-master',
-    'version' => 'dev-master',
-    'aliases' => 
-    array (
+<?php return array(
+    'root' => array(
+        'pretty_version' => 'dev-master',
+        'version' => 'dev-master',
+        'type' => 'project',
+        'install_path' => __DIR__ . '/../../',
+        'aliases' => array(),
+        'reference' => '75b0b2abd4ffb432dae387387ec6637c1880cd6d',
+        'name' => 'topthink/think',
+        'dev' => true,
+    ),
+    'versions' => array(
+        'adbario/php-dot-notation' => array(
+            'pretty_version' => '2.2.0',
+            'version' => '2.2.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../adbario/php-dot-notation',
+            'aliases' => array(),
+            'reference' => 'eee4fc81296531e6aafba4c2bbccfc5adab1676e',
+            'dev_requirement' => false,
+        ),
+        'alibabacloud/client' => array(
+            'pretty_version' => '1.5.30',
+            'version' => '1.5.30.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../alibabacloud/client',
+            'aliases' => array(),
+            'reference' => '1f497bb79835b84094318a70b672eb88260f2682',
+            'dev_requirement' => false,
+        ),
+        'alibabacloud/dysmsapi' => array(
+            'pretty_version' => '1.8.958',
+            'version' => '1.8.958.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../alibabacloud/dysmsapi',
+            'aliases' => array(),
+            'reference' => '1715a5e4c10520116cf531ed20c287ae488b3489',
+            'dev_requirement' => false,
+        ),
+        'alibabacloud/tea' => array(
+            'pretty_version' => '3.1.21',
+            'version' => '3.1.21.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../alibabacloud/tea',
+            'aliases' => array(),
+            'reference' => '379faffe240ee97134cf3f796cb28059f9fb7fa9',
+            'dev_requirement' => false,
+        ),
+        'alibabacloud/tea-fileform' => array(
+            'pretty_version' => '0.3.4',
+            'version' => '0.3.4.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../alibabacloud/tea-fileform',
+            'aliases' => array(),
+            'reference' => '4bf0c75a045c8115aa8cb1a394bd08d8bb833181',
+            'dev_requirement' => false,
+        ),
+        'alipaysdk/easysdk' => array(
+            'pretty_version' => '2.2.0',
+            'version' => '2.2.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../alipaysdk/easysdk',
+            'aliases' => array(),
+            'reference' => '7a1cfa83c7e140bded957498ea072c77611e6480',
+            'dev_requirement' => false,
+        ),
+        'aliyuncs/oss-sdk-php' => array(
+            'pretty_version' => 'v2.3.0',
+            'version' => '2.3.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../aliyuncs/oss-sdk-php',
+            'aliases' => array(),
+            'reference' => 'e69f57916678458642ac9d2fd341ae78a56996c8',
+            'dev_requirement' => false,
+        ),
+        'clagiordano/weblibs-configmanager' => array(
+            'pretty_version' => 'v1.1.0',
+            'version' => '1.1.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../clagiordano/weblibs-configmanager',
+            'aliases' => array(),
+            'reference' => 'ecf584f5b3a27929175ff0abdba52f0131bef795',
+            'dev_requirement' => false,
+        ),
+        'danielstjules/stringy' => array(
+            'pretty_version' => '3.1.0',
+            'version' => '3.1.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../danielstjules/stringy',
+            'aliases' => array(),
+            'reference' => 'df24ab62d2d8213bbbe88cc36fc35a4503b4bd7e',
+            'dev_requirement' => false,
+        ),
+        'dh2y/think-qrcode' => array(
+            'pretty_version' => '2.0',
+            'version' => '2.0.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../dh2y/think-qrcode',
+            'aliases' => array(),
+            'reference' => '977d032afa27b1852f5fc5441fad2497f6db7ff5',
+            'dev_requirement' => false,
+        ),
+        'doctrine/cache' => array(
+            'pretty_version' => 'v1.4.4',
+            'version' => '1.4.4.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../doctrine/cache',
+            'aliases' => array(),
+            'reference' => '6433826dd02c9e5be8a127320dc13e7e6625d020',
+            'dev_requirement' => false,
+        ),
+        'firebase/php-jwt' => array(
+            'pretty_version' => 'v5.0.0',
+            'version' => '5.0.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../firebase/php-jwt',
+            'aliases' => array(),
+            'reference' => '9984a4d3a32ae7673d6971ea00bae9d0a1abba0e',
+            'dev_requirement' => false,
+        ),
+        'guzzle/batch' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/cache' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/common' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/guzzle' => array(
+            'pretty_version' => 'v3.9.3',
+            'version' => '3.9.3.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../guzzle/guzzle',
+            'aliases' => array(),
+            'reference' => '0645b70d953bc1c067bbc8d5bc53194706b628d9',
+            'dev_requirement' => false,
+        ),
+        'guzzle/http' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/inflection' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/iterator' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/log' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/parser' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/plugin' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/plugin-async' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/plugin-backoff' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/plugin-cache' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/plugin-cookie' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/plugin-curlauth' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/plugin-error-response' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/plugin-history' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/plugin-log' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/plugin-md5' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/plugin-mock' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/plugin-oauth' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/service' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzle/stream' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => 'v3.9.3',
+            ),
+        ),
+        'guzzlehttp/guzzle' => array(
+            'pretty_version' => '6.3.3',
+            'version' => '6.3.3.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
+            'aliases' => array(),
+            'reference' => '407b0cb880ace85c9b63c5f9551db498cb2d50ba',
+            'dev_requirement' => false,
+        ),
+        'guzzlehttp/promises' => array(
+            'pretty_version' => 'v1.3.1',
+            'version' => '1.3.1.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../guzzlehttp/promises',
+            'aliases' => array(),
+            'reference' => 'a59da6cf61d80060647ff4d3eb2c03a2bc694646',
+            'dev_requirement' => false,
+        ),
+        'guzzlehttp/psr7' => array(
+            'pretty_version' => '1.5.2',
+            'version' => '1.5.2.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../guzzlehttp/psr7',
+            'aliases' => array(),
+            'reference' => '9f83dded91781a01c63574e387eaa769be769115',
+            'dev_requirement' => false,
+        ),
+        'league/flysystem' => array(
+            'pretty_version' => '1.0.57',
+            'version' => '1.0.57.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../league/flysystem',
+            'aliases' => array(),
+            'reference' => '0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a',
+            'dev_requirement' => false,
+        ),
+        'league/flysystem-cached-adapter' => array(
+            'pretty_version' => '1.0.9',
+            'version' => '1.0.9.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../league/flysystem-cached-adapter',
+            'aliases' => array(),
+            'reference' => '08ef74e9be88100807a3b92cc9048a312bf01d6f',
+            'dev_requirement' => false,
+        ),
+        'monolog/monolog' => array(
+            'pretty_version' => '1.24.0',
+            'version' => '1.24.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../monolog/monolog',
+            'aliases' => array(),
+            'reference' => 'bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266',
+            'dev_requirement' => false,
+        ),
+        'mtdowling/jmespath.php' => array(
+            'pretty_version' => '2.5.0',
+            'version' => '2.5.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../mtdowling/jmespath.php',
+            'aliases' => array(),
+            'reference' => '52168cb9472de06979613d365c7f1ab8798be895',
+            'dev_requirement' => false,
+        ),
+        'nesbot/carbon' => array(
+            'pretty_version' => '2.20.0',
+            'version' => '2.20.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../nesbot/carbon',
+            'aliases' => array(),
+            'reference' => 'bc671b896c276795fad8426b0aa24e8ade0f2498',
+            'dev_requirement' => false,
+        ),
+        'opis/closure' => array(
+            'pretty_version' => '3.4.1',
+            'version' => '3.4.1.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../opis/closure',
+            'aliases' => array(),
+            'reference' => 'e79f851749c3caa836d7ccc01ede5828feb762c7',
+            'dev_requirement' => false,
+        ),
+        'overtrue/socialite' => array(
+            'pretty_version' => '1.3.0',
+            'version' => '1.3.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../overtrue/socialite',
+            'aliases' => array(),
+            'reference' => 'fda55f0acef43a144799b1957a8f93d9f5deffce',
+            'dev_requirement' => false,
+        ),
+        'overtrue/wechat' => array(
+            'pretty_version' => '3.3.33',
+            'version' => '3.3.33.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../overtrue/wechat',
+            'aliases' => array(),
+            'reference' => '78e5476df330754040d1c400d0bca640d5b77cb7',
+            'dev_requirement' => false,
+        ),
+        'paragonie/random_compat' => array(
+            'pretty_version' => 'v9.99.99',
+            'version' => '9.99.99.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../paragonie/random_compat',
+            'aliases' => array(),
+            'reference' => '84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95',
+            'dev_requirement' => false,
+        ),
+        'pda/pheanstalk' => array(
+            'pretty_version' => 'v4.0.3',
+            'version' => '4.0.3.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../pda/pheanstalk',
+            'aliases' => array(),
+            'reference' => '6165573aad525d39b3ac8ae916214cb483a61263',
+            'dev_requirement' => false,
+        ),
+        'phpoffice/phpexcel' => array(
+            'pretty_version' => '1.8.2',
+            'version' => '1.8.2.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpoffice/phpexcel',
+            'aliases' => array(),
+            'reference' => '1441011fb7ecdd8cc689878f54f8b58a6805f870',
+            'dev_requirement' => false,
+        ),
+        'pimple/pimple' => array(
+            'pretty_version' => 'v3.2.3',
+            'version' => '3.2.3.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../pimple/pimple',
+            'aliases' => array(),
+            'reference' => '9e403941ef9d65d20cba7d54e29fe906db42cf32',
+            'dev_requirement' => false,
+        ),
+        'psr/cache' => array(
+            'pretty_version' => '1.0.1',
+            'version' => '1.0.1.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/cache',
+            'aliases' => array(),
+            'reference' => 'd11b50ad223250cf17b86e38383413f5a6764bf8',
+            'dev_requirement' => false,
+        ),
+        'psr/container' => array(
+            'pretty_version' => '1.0.0',
+            'version' => '1.0.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/container',
+            'aliases' => array(),
+            'reference' => 'b7ce3b176482dbbc1245ebf52b181af44c2cf55f',
+            'dev_requirement' => false,
+        ),
+        'psr/http-message' => array(
+            'pretty_version' => '1.0.1',
+            'version' => '1.0.1.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/http-message',
+            'aliases' => array(),
+            'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
+            'dev_requirement' => false,
+        ),
+        'psr/http-message-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.0',
+            ),
+        ),
+        'psr/log' => array(
+            'pretty_version' => '1.1.2',
+            'version' => '1.1.2.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/log',
+            'aliases' => array(),
+            'reference' => '446d54b4cb6bf489fc9d75f55843658e6f25d801',
+            'dev_requirement' => false,
+        ),
+        'psr/log-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.0.0',
+            ),
+        ),
+        'psr/simple-cache' => array(
+            'pretty_version' => '1.0.1',
+            'version' => '1.0.1.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/simple-cache',
+            'aliases' => array(),
+            'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b',
+            'dev_requirement' => false,
+        ),
+        'qcloud/cos-sdk-v5' => array(
+            'pretty_version' => 'v1.3.3',
+            'version' => '1.3.3.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../qcloud/cos-sdk-v5',
+            'aliases' => array(),
+            'reference' => 'cd1b9cefa04521eaf125a82eb53552d9a87aae4d',
+            'dev_requirement' => false,
+        ),
+        'qiniu/php-sdk' => array(
+            'pretty_version' => 'v7.2.9',
+            'version' => '7.2.9.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../qiniu/php-sdk',
+            'aliases' => array(),
+            'reference' => 'afe7d8715d8a688b1d8d8cdf031240d2363dad90',
+            'dev_requirement' => false,
+        ),
+        'ralouphie/getallheaders' => array(
+            'pretty_version' => '2.0.5',
+            'version' => '2.0.5.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../ralouphie/getallheaders',
+            'aliases' => array(),
+            'reference' => '5601c8a83fbba7ef674a7369456d12f1e0d0eafa',
+            'dev_requirement' => false,
+        ),
+        'songshenzong/support' => array(
+            'pretty_version' => '2.0.5',
+            'version' => '2.0.5.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../songshenzong/support',
+            'aliases' => array(),
+            'reference' => '34973c04ffcf226e503f1c3a69d30ac49f7621f6',
+            'dev_requirement' => false,
+        ),
+        'spatie/macroable' => array(
+            'pretty_version' => '1.0.0',
+            'version' => '1.0.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../spatie/macroable',
+            'aliases' => array(),
+            'reference' => '74b0d189ce75142f1706aad834d5a428dfc7c3c3',
+            'dev_requirement' => false,
+        ),
+        'symfony/event-dispatcher' => array(
+            'pretty_version' => 'v2.8.50',
+            'version' => '2.8.50.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/event-dispatcher',
+            'aliases' => array(),
+            'reference' => 'a77e974a5fecb4398833b0709210e3d5e334ffb0',
+            'dev_requirement' => false,
+        ),
+        'symfony/http-foundation' => array(
+            'pretty_version' => 'v3.4.28',
+            'version' => '3.4.28.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/http-foundation',
+            'aliases' => array(),
+            'reference' => '677ae5e892b081e71a665bfa7dd90fe61800c00e',
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-mbstring' => array(
+            'pretty_version' => 'v1.11.0',
+            'version' => '1.11.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
+            'aliases' => array(),
+            'reference' => 'fe5e94c604826c35a32fa832f35bd036b6799609',
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-php70' => array(
+            'pretty_version' => 'v1.11.0',
+            'version' => '1.11.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-php70',
+            'aliases' => array(),
+            'reference' => 'bc4858fb611bda58719124ca079baff854149c89',
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-php72' => array(
+            'pretty_version' => 'v1.11.0',
+            'version' => '1.11.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-php72',
+            'aliases' => array(),
+            'reference' => 'ab50dcf166d5f577978419edd37aa2bb8eabce0c',
+            'dev_requirement' => false,
+        ),
+        'symfony/process' => array(
+            'pretty_version' => 'v4.3.2',
+            'version' => '4.3.2.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/process',
+            'aliases' => array(),
+            'reference' => '856d35814cf287480465bb7a6c413bb7f5f5e69c',
+            'dev_requirement' => false,
+        ),
+        'symfony/psr-http-message-bridge' => array(
+            'pretty_version' => 'v1.2.0',
+            'version' => '1.2.0.0',
+            'type' => 'symfony-bridge',
+            'install_path' => __DIR__ . '/../symfony/psr-http-message-bridge',
+            'aliases' => array(),
+            'reference' => '9ab9d71f97d5c7d35a121a7fb69f74fee95cd0ad',
+            'dev_requirement' => false,
+        ),
+        'symfony/translation' => array(
+            'pretty_version' => 'v4.3.2',
+            'version' => '4.3.2.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/translation',
+            'aliases' => array(),
+            'reference' => '934ab1d18545149e012aa898cf02e9f23790f7a0',
+            'dev_requirement' => false,
+        ),
+        'symfony/translation-contracts' => array(
+            'pretty_version' => 'v1.1.5',
+            'version' => '1.1.5.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/translation-contracts',
+            'aliases' => array(),
+            'reference' => 'cb4b18ad7b92a26e83b65dde940fab78339e6f3c',
+            'dev_requirement' => false,
+        ),
+        'symfony/translation-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.0',
+            ),
+        ),
+        'symfony/var-dumper' => array(
+            'pretty_version' => 'v4.3.1',
+            'version' => '4.3.1.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/var-dumper',
+            'aliases' => array(),
+            'reference' => 'f974f448154928d2b5fb7c412bd23b81d063f34b',
+            'dev_requirement' => false,
+        ),
+        'topthink/framework' => array(
+            'pretty_version' => 'v6.0.0',
+            'version' => '6.0.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../topthink/framework',
+            'aliases' => array(),
+            'reference' => '79c555aab0313d1a33ddcdb3c395f2c47f37f597',
+            'dev_requirement' => false,
+        ),
+        'topthink/think' => array(
+            'pretty_version' => 'dev-master',
+            'version' => 'dev-master',
+            'type' => 'project',
+            'install_path' => __DIR__ . '/../../',
+            'aliases' => array(),
+            'reference' => '75b0b2abd4ffb432dae387387ec6637c1880cd6d',
+            'dev_requirement' => false,
+        ),
+        'topthink/think-captcha' => array(
+            'pretty_version' => 'v3.0.1',
+            'version' => '3.0.1.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../topthink/think-captcha',
+            'aliases' => array(),
+            'reference' => '9fc0c627d773f6a54a8dd142ebf358f746557a48',
+            'dev_requirement' => false,
+        ),
+        'topthink/think-factory' => array(
+            'pretty_version' => 'v1.0.1',
+            'version' => '1.0.1.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../topthink/think-factory',
+            'aliases' => array(),
+            'reference' => 'b8080a6472aae1cff47ceb8c30feec3c2835364b',
+            'dev_requirement' => false,
+        ),
+        'topthink/think-helper' => array(
+            'pretty_version' => 'v3.1.3',
+            'version' => '3.1.3.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../topthink/think-helper',
+            'aliases' => array(),
+            'reference' => '4d85dfd3778623bbb1de3648f1dcd0c82f4439f4',
+            'dev_requirement' => false,
+        ),
+        'topthink/think-image' => array(
+            'pretty_version' => 'v1.0.7',
+            'version' => '1.0.7.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../topthink/think-image',
+            'aliases' => array(),
+            'reference' => '8586cf47f117481c6d415b20f7dedf62e79d5512',
+            'dev_requirement' => false,
+        ),
+        'topthink/think-multi-app' => array(
+            'pretty_version' => 'v1.0.11',
+            'version' => '1.0.11.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../topthink/think-multi-app',
+            'aliases' => array(),
+            'reference' => '215f4a6bb88e53ad41b448c61957336eb55ce6f9',
+            'dev_requirement' => false,
+        ),
+        'topthink/think-orm' => array(
+            'pretty_version' => 'v2.0.27',
+            'version' => '2.0.27.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../topthink/think-orm',
+            'aliases' => array(),
+            'reference' => '02affaaccade2cdd8bbb2d2f5d15e46113e6eb50',
+            'dev_requirement' => false,
+        ),
+        'topthink/think-queue' => array(
+            'pretty_version' => 'v3.0.2',
+            'version' => '3.0.2.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../topthink/think-queue',
+            'aliases' => array(),
+            'reference' => 'c34b983abce9427fca7e30ac983b75041f436ad0',
+            'dev_requirement' => false,
+        ),
+        'topthink/think-template' => array(
+            'pretty_version' => 'v2.0.7',
+            'version' => '2.0.7.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../topthink/think-template',
+            'aliases' => array(),
+            'reference' => 'e98bdbb4a4c94b442f17dfceba81e0134d4fbd19',
+            'dev_requirement' => false,
+        ),
+        'topthink/think-view' => array(
+            'pretty_version' => 'v1.0.13',
+            'version' => '1.0.13.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../topthink/think-view',
+            'aliases' => array(),
+            'reference' => '90803b73f781db5d42619082c4597afc58b2d4c5',
+            'dev_requirement' => false,
+        ),
+        'workerman/channel' => array(
+            'pretty_version' => 'v1.0.5',
+            'version' => '1.0.5.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../workerman/channel',
+            'aliases' => array(),
+            'reference' => '0836a9a413c6e8425ee36307d95e2e49cc380f50',
+            'dev_requirement' => false,
+        ),
+        'workerman/workerman' => array(
+            'pretty_version' => 'v3.5.19',
+            'version' => '3.5.19.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../workerman/workerman',
+            'aliases' => array(),
+            'reference' => '4e5c24073b431fd950287efbfb5cc9b4c0fc7367',
+            'dev_requirement' => false,
+        ),
+        'xaboy/form-builder' => array(
+            'pretty_version' => '1.2.10',
+            'version' => '1.2.10.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../xaboy/form-builder',
+            'aliases' => array(),
+            'reference' => '198c5f066499eef8b005f5d504fcb6120fa3ac04',
+            'dev_requirement' => false,
+        ),
+        'xin/container' => array(
+            'pretty_version' => '2.0.1',
+            'version' => '2.0.1.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../xin/container',
+            'aliases' => array(),
+            'reference' => '97bb67f87dd851545938a1f2fe0ffbd379e3ff81',
+            'dev_requirement' => false,
+        ),
+        'xin/helper' => array(
+            'pretty_version' => '1.0.0',
+            'version' => '1.0.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../xin/helper',
+            'aliases' => array(),
+            'reference' => '02a58132dae2aea2d1c0b8e66f55125969224747',
+            'dev_requirement' => false,
+        ),
     ),
-    'reference' => 'e9936b7848d32f25ff5761d416432dc0090b9efb',
-    'name' => 'topthink/think',
-  ),
-  'versions' => 
-  array (
-    'adbario/php-dot-notation' => 
-    array (
-      'pretty_version' => '2.2.0',
-      'version' => '2.2.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'eee4fc81296531e6aafba4c2bbccfc5adab1676e',
-    ),
-    'alibabacloud/client' => 
-    array (
-      'pretty_version' => '1.5.30',
-      'version' => '1.5.30.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '1f497bb79835b84094318a70b672eb88260f2682',
-    ),
-    'alibabacloud/dysmsapi' => 
-    array (
-      'pretty_version' => '1.8.958',
-      'version' => '1.8.958.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '1715a5e4c10520116cf531ed20c287ae488b3489',
-    ),
-    'alibabacloud/tea' => 
-    array (
-      'pretty_version' => '3.1.21',
-      'version' => '3.1.21.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '379faffe240ee97134cf3f796cb28059f9fb7fa9',
-    ),
-    'alibabacloud/tea-fileform' => 
-    array (
-      'pretty_version' => '0.3.4',
-      'version' => '0.3.4.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '4bf0c75a045c8115aa8cb1a394bd08d8bb833181',
-    ),
-    'alipaysdk/easysdk' => 
-    array (
-      'pretty_version' => '2.2.0',
-      'version' => '2.2.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '7a1cfa83c7e140bded957498ea072c77611e6480',
-    ),
-    'aliyuncs/oss-sdk-php' => 
-    array (
-      'pretty_version' => 'v2.3.0',
-      'version' => '2.3.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'e69f57916678458642ac9d2fd341ae78a56996c8',
-    ),
-    'clagiordano/weblibs-configmanager' => 
-    array (
-      'pretty_version' => 'v1.1.0',
-      'version' => '1.1.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'ecf584f5b3a27929175ff0abdba52f0131bef795',
-    ),
-    'danielstjules/stringy' => 
-    array (
-      'pretty_version' => '3.1.0',
-      'version' => '3.1.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'df24ab62d2d8213bbbe88cc36fc35a4503b4bd7e',
-    ),
-    'dh2y/think-qrcode' => 
-    array (
-      'pretty_version' => '2.0',
-      'version' => '2.0.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '977d032afa27b1852f5fc5441fad2497f6db7ff5',
-    ),
-    'doctrine/cache' => 
-    array (
-      'pretty_version' => 'v1.4.4',
-      'version' => '1.4.4.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '6433826dd02c9e5be8a127320dc13e7e6625d020',
-    ),
-    'firebase/php-jwt' => 
-    array (
-      'pretty_version' => 'v5.0.0',
-      'version' => '5.0.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '9984a4d3a32ae7673d6971ea00bae9d0a1abba0e',
-    ),
-    'guzzle/batch' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/cache' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/common' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/guzzle' => 
-    array (
-      'pretty_version' => 'v3.9.3',
-      'version' => '3.9.3.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '0645b70d953bc1c067bbc8d5bc53194706b628d9',
-    ),
-    'guzzle/http' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/inflection' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/iterator' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/log' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/parser' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-async' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-backoff' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-cache' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-cookie' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-curlauth' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-error-response' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-history' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-log' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-md5' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-mock' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/plugin-oauth' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/service' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzle/stream' => 
-    array (
-      'replaced' => 
-      array (
-        0 => 'v3.9.3',
-      ),
-    ),
-    'guzzlehttp/guzzle' => 
-    array (
-      'pretty_version' => '6.3.3',
-      'version' => '6.3.3.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '407b0cb880ace85c9b63c5f9551db498cb2d50ba',
-    ),
-    'guzzlehttp/promises' => 
-    array (
-      'pretty_version' => 'v1.3.1',
-      'version' => '1.3.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'a59da6cf61d80060647ff4d3eb2c03a2bc694646',
-    ),
-    'guzzlehttp/psr7' => 
-    array (
-      'pretty_version' => '1.5.2',
-      'version' => '1.5.2.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '9f83dded91781a01c63574e387eaa769be769115',
-    ),
-    'league/flysystem' => 
-    array (
-      'pretty_version' => '1.0.57',
-      'version' => '1.0.57.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a',
-    ),
-    'league/flysystem-cached-adapter' => 
-    array (
-      'pretty_version' => '1.0.9',
-      'version' => '1.0.9.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '08ef74e9be88100807a3b92cc9048a312bf01d6f',
-    ),
-    'monolog/monolog' => 
-    array (
-      'pretty_version' => '1.24.0',
-      'version' => '1.24.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266',
-    ),
-    'mtdowling/jmespath.php' => 
-    array (
-      'pretty_version' => '2.5.0',
-      'version' => '2.5.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '52168cb9472de06979613d365c7f1ab8798be895',
-    ),
-    'nesbot/carbon' => 
-    array (
-      'pretty_version' => '2.20.0',
-      'version' => '2.20.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'bc671b896c276795fad8426b0aa24e8ade0f2498',
-    ),
-    'opis/closure' => 
-    array (
-      'pretty_version' => '3.4.1',
-      'version' => '3.4.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'e79f851749c3caa836d7ccc01ede5828feb762c7',
-    ),
-    'overtrue/socialite' => 
-    array (
-      'pretty_version' => '1.3.0',
-      'version' => '1.3.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'fda55f0acef43a144799b1957a8f93d9f5deffce',
-    ),
-    'overtrue/wechat' => 
-    array (
-      'pretty_version' => '3.3.33',
-      'version' => '3.3.33.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '78e5476df330754040d1c400d0bca640d5b77cb7',
-    ),
-    'paragonie/random_compat' => 
-    array (
-      'pretty_version' => 'v9.99.99',
-      'version' => '9.99.99.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95',
-    ),
-    'phpoffice/phpexcel' => 
-    array (
-      'pretty_version' => '1.8.2',
-      'version' => '1.8.2.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '1441011fb7ecdd8cc689878f54f8b58a6805f870',
-    ),
-    'pimple/pimple' => 
-    array (
-      'pretty_version' => 'v3.2.3',
-      'version' => '3.2.3.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '9e403941ef9d65d20cba7d54e29fe906db42cf32',
-    ),
-    'psr/cache' => 
-    array (
-      'pretty_version' => '1.0.1',
-      'version' => '1.0.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'd11b50ad223250cf17b86e38383413f5a6764bf8',
-    ),
-    'psr/container' => 
-    array (
-      'pretty_version' => '1.0.0',
-      'version' => '1.0.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'b7ce3b176482dbbc1245ebf52b181af44c2cf55f',
-    ),
-    'psr/http-message' => 
-    array (
-      'pretty_version' => '1.0.1',
-      'version' => '1.0.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
-    ),
-    'psr/http-message-implementation' => 
-    array (
-      'provided' => 
-      array (
-        0 => '1.0',
-      ),
-    ),
-    'psr/log' => 
-    array (
-      'pretty_version' => '1.1.2',
-      'version' => '1.1.2.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '446d54b4cb6bf489fc9d75f55843658e6f25d801',
-    ),
-    'psr/log-implementation' => 
-    array (
-      'provided' => 
-      array (
-        0 => '1.0.0',
-      ),
-    ),
-    'psr/simple-cache' => 
-    array (
-      'pretty_version' => '1.0.1',
-      'version' => '1.0.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b',
-    ),
-    'qcloud/cos-sdk-v5' => 
-    array (
-      'pretty_version' => 'v1.3.3',
-      'version' => '1.3.3.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'cd1b9cefa04521eaf125a82eb53552d9a87aae4d',
-    ),
-    'qiniu/php-sdk' => 
-    array (
-      'pretty_version' => 'v7.2.9',
-      'version' => '7.2.9.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'afe7d8715d8a688b1d8d8cdf031240d2363dad90',
-    ),
-    'ralouphie/getallheaders' => 
-    array (
-      'pretty_version' => '2.0.5',
-      'version' => '2.0.5.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '5601c8a83fbba7ef674a7369456d12f1e0d0eafa',
-    ),
-    'songshenzong/support' => 
-    array (
-      'pretty_version' => '2.0.5',
-      'version' => '2.0.5.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '34973c04ffcf226e503f1c3a69d30ac49f7621f6',
-    ),
-    'spatie/macroable' => 
-    array (
-      'pretty_version' => '1.0.0',
-      'version' => '1.0.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '74b0d189ce75142f1706aad834d5a428dfc7c3c3',
-    ),
-    'symfony/event-dispatcher' => 
-    array (
-      'pretty_version' => 'v2.8.50',
-      'version' => '2.8.50.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'a77e974a5fecb4398833b0709210e3d5e334ffb0',
-    ),
-    'symfony/http-foundation' => 
-    array (
-      'pretty_version' => 'v3.4.28',
-      'version' => '3.4.28.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '677ae5e892b081e71a665bfa7dd90fe61800c00e',
-    ),
-    'symfony/polyfill-mbstring' => 
-    array (
-      'pretty_version' => 'v1.11.0',
-      'version' => '1.11.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'fe5e94c604826c35a32fa832f35bd036b6799609',
-    ),
-    'symfony/polyfill-php70' => 
-    array (
-      'pretty_version' => 'v1.11.0',
-      'version' => '1.11.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'bc4858fb611bda58719124ca079baff854149c89',
-    ),
-    'symfony/polyfill-php72' => 
-    array (
-      'pretty_version' => 'v1.11.0',
-      'version' => '1.11.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'ab50dcf166d5f577978419edd37aa2bb8eabce0c',
-    ),
-    'symfony/process' => 
-    array (
-      'pretty_version' => 'v4.3.2',
-      'version' => '4.3.2.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '856d35814cf287480465bb7a6c413bb7f5f5e69c',
-    ),
-    'symfony/psr-http-message-bridge' => 
-    array (
-      'pretty_version' => 'v1.2.0',
-      'version' => '1.2.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '9ab9d71f97d5c7d35a121a7fb69f74fee95cd0ad',
-    ),
-    'symfony/translation' => 
-    array (
-      'pretty_version' => 'v4.3.2',
-      'version' => '4.3.2.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '934ab1d18545149e012aa898cf02e9f23790f7a0',
-    ),
-    'symfony/translation-contracts' => 
-    array (
-      'pretty_version' => 'v1.1.5',
-      'version' => '1.1.5.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'cb4b18ad7b92a26e83b65dde940fab78339e6f3c',
-    ),
-    'symfony/translation-implementation' => 
-    array (
-      'provided' => 
-      array (
-        0 => '1.0',
-      ),
-    ),
-    'symfony/var-dumper' => 
-    array (
-      'pretty_version' => 'v4.3.1',
-      'version' => '4.3.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'f974f448154928d2b5fb7c412bd23b81d063f34b',
-    ),
-    'topthink/framework' => 
-    array (
-      'pretty_version' => 'v6.0.0',
-      'version' => '6.0.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '79c555aab0313d1a33ddcdb3c395f2c47f37f597',
-    ),
-    'topthink/think' => 
-    array (
-      'pretty_version' => 'dev-master',
-      'version' => 'dev-master',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'e9936b7848d32f25ff5761d416432dc0090b9efb',
-    ),
-    'topthink/think-captcha' => 
-    array (
-      'pretty_version' => 'v3.0.1',
-      'version' => '3.0.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '9fc0c627d773f6a54a8dd142ebf358f746557a48',
-    ),
-    'topthink/think-factory' => 
-    array (
-      'pretty_version' => 'v1.0.1',
-      'version' => '1.0.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'b8080a6472aae1cff47ceb8c30feec3c2835364b',
-    ),
-    'topthink/think-helper' => 
-    array (
-      'pretty_version' => 'v3.1.3',
-      'version' => '3.1.3.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '4d85dfd3778623bbb1de3648f1dcd0c82f4439f4',
-    ),
-    'topthink/think-image' => 
-    array (
-      'pretty_version' => 'v1.0.7',
-      'version' => '1.0.7.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '8586cf47f117481c6d415b20f7dedf62e79d5512',
-    ),
-    'topthink/think-multi-app' => 
-    array (
-      'pretty_version' => 'v1.0.11',
-      'version' => '1.0.11.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '215f4a6bb88e53ad41b448c61957336eb55ce6f9',
-    ),
-    'topthink/think-orm' => 
-    array (
-      'pretty_version' => 'v2.0.27',
-      'version' => '2.0.27.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '02affaaccade2cdd8bbb2d2f5d15e46113e6eb50',
-    ),
-    'topthink/think-queue' => 
-    array (
-      'pretty_version' => 'v3.0.2',
-      'version' => '3.0.2.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'c34b983abce9427fca7e30ac983b75041f436ad0',
-    ),
-    'topthink/think-template' => 
-    array (
-      'pretty_version' => 'v2.0.7',
-      'version' => '2.0.7.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => 'e98bdbb4a4c94b442f17dfceba81e0134d4fbd19',
-    ),
-    'topthink/think-view' => 
-    array (
-      'pretty_version' => 'v1.0.13',
-      'version' => '1.0.13.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '90803b73f781db5d42619082c4597afc58b2d4c5',
-    ),
-    'workerman/channel' => 
-    array (
-      'pretty_version' => 'v1.0.5',
-      'version' => '1.0.5.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '0836a9a413c6e8425ee36307d95e2e49cc380f50',
-    ),
-    'workerman/workerman' => 
-    array (
-      'pretty_version' => 'v3.5.19',
-      'version' => '3.5.19.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '4e5c24073b431fd950287efbfb5cc9b4c0fc7367',
-    ),
-    'xaboy/form-builder' => 
-    array (
-      'pretty_version' => '1.2.10',
-      'version' => '1.2.10.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '198c5f066499eef8b005f5d504fcb6120fa3ac04',
-    ),
-    'xin/container' => 
-    array (
-      'pretty_version' => '2.0.1',
-      'version' => '2.0.1.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '97bb67f87dd851545938a1f2fe0ffbd379e3ff81',
-    ),
-    'xin/helper' => 
-    array (
-      'pretty_version' => '1.0.0',
-      'version' => '1.0.0.0',
-      'aliases' => 
-      array (
-      ),
-      'reference' => '02a58132dae2aea2d1c0b8e66f55125969224747',
-    ),
-  ),
 );

+ 4 - 0
vendor/pda/pheanstalk/.gitattributes

@@ -0,0 +1,4 @@
+tests/ export-ignore
+phpunit.xml.dist export-ignore
+doc/ export-ignore
+.travis.yml export-ignore

+ 22 - 0
vendor/pda/pheanstalk/.github/workflows/docs.yaml

@@ -0,0 +1,22 @@
+name: Build docs
+on:
+  push:
+    branches:
+      - master
+jobs:
+  phpdocumentor:
+    name: Build phpdocumentor docs
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v2
+#        THIS IS DISABLED ON PHPDOCUMENTOR 3 HAS A WORKING BUILD
+#      - uses: actions/checkout@v2
+#        with:
+#          path: docs
+#          ref: gh-pages
+#      - run: php --version
+#      - name: Setup git
+#        run: git config --global user.email "pheanstalk@action.github.com" && git config --global user.name "Github Action"
+#      - run: git add . && git commit -a -m "Automated build" && git push
+#        working-directory: docs

+ 11 - 0
vendor/pda/pheanstalk/.gitignore

@@ -0,0 +1,11 @@
+pheanstalk.phar
+
+# Composer lock file; do not commit, this is a library not an app.
+# See: http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
+/composer.lock
+
+# Composer installs dependencies here.
+vendor/
+# Test coverage is stored here
+tests/coverage
+.idea

+ 16 - 0
vendor/pda/pheanstalk/.scrutinizer.yml

@@ -0,0 +1,16 @@
+filter:
+  paths: [src/*]
+checks:
+  php:
+    code_rating: true
+    duplication: true
+tools:
+  external_code_coverage:
+    runs: 1
+build:
+  nodes:
+    analysis:
+      tests:
+        override:
+          - phpcs-run
+          - php-scrutinizer-run

+ 22 - 0
vendor/pda/pheanstalk/LICENSE

@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (c) 2008–2015 Paul Annesley
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+

+ 234 - 0
vendor/pda/pheanstalk/README.md

@@ -0,0 +1,234 @@
+Pheanstalk
+==========
+
+[![Latest Stable Version](https://img.shields.io/packagist/v/pda/pheanstalk.svg)](https://packagist.org/packages/pda/pheanstalk)
+[![Total Downloads](https://img.shields.io/packagist/dt/pda/pheanstalk.svg)](https://packagist.org/pda/pheanstalk)
+[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/pheanstalk/pheanstalk/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/pheanstalk/pheanstalk/?branch=master)
+[![Code Coverage](https://scrutinizer-ci.com/g/pheanstalk/pheanstalk/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/pheanstalk/pheanstalk/?branch=master)
+[![Build Status](https://travis-ci.org/pheanstalk/pheanstalk.svg?branch=master)](https://travis-ci.org/pheanstalk/pheanstalk)
+
+Pheanstalk is a pure PHP 7.1+ client for the [beanstalkd workqueue][1].  It has
+been actively developed, and used in production by many, since late 2008.
+
+Created by [Paul Annesley][2], Pheanstalk is rigorously unit tested and written
+using encapsulated, maintainable object oriented design.  Community feedback,
+bug reports and patches has led to a stable 1.0 release in 2010, a 2.0 release
+in 2013, and a 3.0 release in 2014.
+
+Pheanstalk 3.0 introduces PHP namespaces, PSR-1 and PSR-2 coding standards,
+and PSR-4 autoloader standard.
+
+beanstalkd up to the latest version 1.10 is supported.  All commands and
+responses specified in the [protocol documentation][4] for beanstalkd 1.3 are
+implemented.
+
+  [1]: https://beanstalkd.github.io/
+  [2]: https://paul.annesley.cc/
+  [3]: https://github.com/sammousa
+  [4]: https://github.com/kr/beanstalkd/tree/v1.3/doc/protocol.txt?raw=true
+  
+Pheanstalk 4
+-------------
+In 2018 [Sam Mousa][3] took on the responsibility of maintaining Pheanstalk.
+
+Pheanstalk 4.0 drops support for older PHP versions. It contains the following changes (among other things):
+- Strict PHP type hinting
+- Value objects for Job IDs
+- Functions without side effects
+- Dropped support for persistent connections
+- Add support for multiple socket implementations (streams extension, socket extension, fsockopen)
+
+### Dropping support persistent connections
+Persistent connections are a feature where a TCP connection is kept alive between different requests to reduce overhead
+from TCP connection set up. When reusing TCP connections we must always guarantee that the application protocol, in this
+case beanstalks' protocol is in a proper state. This is hard, and in some cases impossible; at the very least this means
+we must do some tests which cause roundtrips.
+Consider for example a connection that has just sent the command `PUT 0 4000`. The beanstalk server is now going to read
+4000 bytes, but if the PHP script crashes during this write the next request get assigned this TCP socket.
+Now to reset the connection to a known state it used to subscribe to the default tube: `use default`.
+Since the beanstalk server is expecting 4000 bytes, it will just write this command to the job and wait for more bytes..
+
+To prevent these kinds of issues the simplest solution is to not use persistent connections.
+
+### Dropped connection handling
+Depending on the socket implementation used we might not be able to enable TCP keepalive. If we do not have TCP keepalive
+there is no way for us to detect dropped connections, the underlying OS may wait up to 15 minutes to decide that a TCP
+connection where no packets are being sent is disconnected. 
+When using a socket implementation that supports read timeouts, like `SocketSocket` which uses the socket extension we 
+use read and write timeouts to detect broken connections; the issue with the beanstalk protocol is that it allows for
+no packets to be sent for extended periods of time. Solutions are to either catch these connection exceptions and reconnect
+or use `reserveWithTimeout()` with a timeout that is less than the read / write timeouts.  
+
+Example code for a job runner could look like this (this is real production code):
+```php
+while(true) {
+    $job = $beanstalk->reserveWithTimeout(50);
+    $this->stdout('.', Console::FG_CYAN);
+    if (isset($job)) {
+        $this->ensureDatabase($db);
+        try {
+            /** @var HookTask $task */
+            $task = $taskFactory->createFromJson($job->getData());
+
+            $commandBus->handle($task);
+            $this->stdout("Deleting job: {$job->getId()}\n", Console::FG_GREEN);
+            $beanstalk->delete($job);
+        } catch (\Throwable $t) {
+            \Yii::error($t);
+            $this->stderr("\n{$t->getMessage()}\n", Console::FG_RED);
+            $this->stderr("{$t->getTraceAsString()}\n", Console::FG_RED);
+
+            $this->stdout("Burying job: {$job->getId()}\n", Console::FG_YELLOW);
+            $beanstalk->bury($job);
+        }
+    }
+}
+```
+Here connection errors will cause the process to exit (and be restarted by a task manager).   
+
+### Functions with side effects
+In version 4 functions with side effects have been removed, functions like `putInTube` internally did several things:
+1. Switch to the tube
+2. Put the job in the new tube
+
+In this example, the tube changes meaning that the connection is now in a different state. This is not intuitive and forces
+any user of the connection to always switch / check the current tube.
+Another issue with this approach is that it is harder to deal with errors. If an exception occurs it is unclear whether 
+we did or did not switch tube.
+
+
+Migration to v4
+-------------
+A migration should in most cases be relatively simple:
+- Change the constructor, either use the static constructor, use a DI container to construct the dependencies, or manually 
+instantiate them.
+- Change instances of `reserve()` with a timeout to `reserveWithTimeout(int $timeout)` since `reserve()` no longer accepts a `timeout` parameter.
+- Run your tests, or use a static analyzer to test for calls to functions that no longer exist.
+- Make sure that you handle connection exceptions (this is not new to V4, only in V4 you will get more of them due to the
+default usage of a socket implementation that has read / write timeouts).
+
+
+
+
+
+Installation with Composer
+-------------
+
+Install pheanstalk as a dependency with composer:
+
+```bash
+composer require pda/pheanstalk
+```
+
+
+Usage Example
+-------------
+
+#### Producer 
+
+```php
+<?php
+require __DIR__ . '/vendor/autoload.php';
+
+use Pheanstalk\Pheanstalk;
+
+$pheanstalk = Pheanstalk::create('127.0.0.1');
+
+// Queue a Job
+$pheanstalk
+  ->useTube('testtube')
+  ->put("job payload goes here\n");
+
+$pheanstalk
+    ->useTube('testtube')
+    ->put(
+        json_encode(['test' => 'data']),  // encode data in payload
+        Pheanstalk::DEFAULT_PRIORITY,     // default priority
+        30, // delay by 30s
+        60  // beanstalk will retry job after 60s
+     );
+
+```
+
+
+#### Consumer / Worker
+```php
+<?php
+require __DIR__ . '/vendor/autoload.php';
+use Pheanstalk\Pheanstalk;
+
+$pheanstalk = Pheanstalk::create('127.0.0.1');
+
+// we want jobs from 'testtube' only.
+$pheanstalk->watch('testtube');
+
+// this hangs until a Job is produced.
+$job = $pheanstalk->reserve();
+
+try {
+    $jobPayload = $job->getData();
+    // do work.
+
+    sleep(2);
+    // If it's going to take a long time, periodically
+    // tell beanstalk we're alive to stop it rescheduling the job.
+    $pheanstalk->touch($job);
+    sleep(2);
+
+    // eventually we're done, delete job.
+    $pheanstalk->delete($job);
+}
+catch(\Exception $e) {
+    // handle exception.
+    // and let some other worker retry.
+    $pheanstalk->release($job); 
+}
+```
+
+
+Running the tests
+-----------------
+
+If you have docker-compose installed running tests is as simple as:
+```sh
+> composer test
+```
+
+If you don't then you manually need to set up a beanstalk server and run:
+```sh
+> vendor/bin/phpunit
+```
+
+Contributors
+------------
+
+  * [Paul Annesley](https://github.com/pda)
+  * [Lachlan Donald](https://github.com/lox)
+  * [Joakim Bick](https://github.com/minimoe)
+  * [Vyacheslav](https://github.com/SlNPacifist)
+  * [leprechaun](https://github.com/leprechaun)
+  * [Peter McArthur](https://github.com/ptrmcrthr)
+  * [robbiehudson](https://github.com/robbiehudson)
+  * [Geoff Catlin](https://github.com/gcatlin)
+  * [Steven Lewis](https://github.com/srjlewis)
+  * [Lars Yencken](https://github.com/larsyencken)
+  * [Josh Butts](https://github.com/jimbojsb)
+  * [Henry Smith](https://github.com/h2s)
+  * [Javier Spagnoletti](https://github.com/phansys)
+  * [Graham Campbell](https://github.com/GrahamCampbell)
+  * [Thomas Tourlourat](https://github.com/armetiz)
+  * [Matthieu Napoli](https://github.com/mnapoli)
+  * [Christoph](https://github.com/xrstf)
+  * [James Hamilton](https://github.com/mrjameshamilton)
+  * [Hannes Van De Vreken](https://github.com/hannesvdvreken)
+  * [Yaniv Davidovitch](https://github.com/YanivD)
+  * [Sam Mousa](https://github.com/sammousa)
+  * .. [more?](https://github.com/pda/pheanstalk/contributors) Let me know if you're missing.
+
+
+License
+-------
+
+© Paul Annesley
+
+Released under the [The MIT License](http://www.opensource.org/licenses/mit-license.php)

+ 41 - 0
vendor/pda/pheanstalk/composer.json

@@ -0,0 +1,41 @@
+{
+    "name": "pda/pheanstalk",
+    "type": "library",
+    "description": "PHP client for beanstalkd queue",
+    "keywords": ["beanstalkd"],
+    "homepage": "https://github.com/pheanstalk/pheanstalk",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Paul Annesley",
+            "email": "paul@annesley.cc",
+            "homepage": "http://paul.annesley.cc/",
+            "role": "Developer"
+        },
+        {
+            "name": "Sam Mousa",
+            "email": "sam@mousa.nl",
+            "role": "Maintainer"
+        }
+    ],
+    "require": {
+        "php": ">=7.1.0",
+        "ext-mbstring": "*"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^7"
+    },
+    "autoload": {
+        "psr-4": {
+            "Pheanstalk\\": "src/"
+        }
+    },
+    "scripts": {
+        "phpunit": "docker-compose run --rm phpunit",
+        "psalm": "@php vendor/bin/psalm --show-info=false src",
+        "test": [
+            "@composer install",
+            "@phpunit"
+        ]
+    }
+}

+ 26 - 0
vendor/pda/pheanstalk/docker-compose.yml

@@ -0,0 +1,26 @@
+version: "3"
+services:
+  beanstalk:
+    build:
+      context: ./dockerfiles
+      dockerfile: Dockerfile-beanstalkd
+  phpunit:
+    build:
+      context: ./dockerfiles
+      dockerfile: Dockerfile-phpunit
+    environment:
+      SERVER_HOST: beanstalk
+    depends_on:
+      - beanstalk
+    volumes:
+      - ./:/app:ro
+      - ./tests/coverage:/app/tests/coverage:rw
+    entrypoint: [
+      "/sbin/tini",
+      "/usr/bin/phpdbg",
+      "-qrr",
+      "/app/vendor/bin/phpunit",
+      "--coverage-html",
+      "/app/tests/coverage"
+    ]
+    #command: /app

+ 3 - 0
vendor/pda/pheanstalk/dockerfiles/Dockerfile-beanstalkd

@@ -0,0 +1,3 @@
+FROM alpine
+RUN apk add --update --no-cache beanstalkd
+ENTRYPOINT /usr/bin/beanstalkd

+ 17 - 0
vendor/pda/pheanstalk/dockerfiles/Dockerfile-phpunit

@@ -0,0 +1,17 @@
+FROM alpine
+RUN apk add --update --no-cache \
+    tini \
+    curl \
+    git \
+    php7 \
+    php7-json \
+    php7-phar \
+    php7-mbstring \
+    php7-openssl \
+    php7-phpdbg \
+    php7-curl \
+    php7-dom \
+    php7-ctype \
+    php7-sockets \
+    php7-tokenizer
+WORKDIR /app

+ 9 - 0
vendor/pda/pheanstalk/phpcs.xml.dist

@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<ruleset>
+    <file>src</file>
+    <file>tests/Pheanstalk</file>
+    <exclude-pattern>tests/coverage/*</exclude-pattern>
+    <rule ref="PSR2" />
+    <rule ref="Generic.Arrays.DisallowLongArraySyntax.Found"/>
+
+</ruleset>

+ 24 - 0
vendor/pda/pheanstalk/phpdoc.dist.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<phpdocumentor
+        configVersion="3"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xmlns="http://www.phpdoc.org"
+        xsi:noNamespaceSchemaLocation="vendor/phpdocumentor/phpdocumentor/data/xsd/phpdoc.xsd"
+>
+    <paths>
+        <output>docs</output>
+        <cache>cache</cache>
+    </paths>
+    <version number="3.0.0">
+        <api>
+            <source dsn=".">
+                <path>src</path>
+            </source>
+            <extensions>
+                <extension>php</extension>
+            </extensions>
+            <default-package-name>Pheanstalk</default-package-name>
+        </api>
+    </version>
+    <template name="default"/>
+</phpdocumentor>

+ 47 - 0
vendor/pda/pheanstalk/psalm.xml

@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<psalm
+    totallyTyped="false"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns="https://getpsalm.org/schema/config"
+    xsi:schemaLocation="https://getpsalm.org/schema/config file://vendor/vimeo/psalm/config.xsd"
+>
+    <projectFiles>
+        <directory name="src" />
+        <ignoreFiles>
+            <directory name="vendor" />
+        </ignoreFiles>
+    </projectFiles>
+
+    <issueHandlers>
+        <LessSpecificReturnType errorLevel="info" />
+
+        <!-- level 3 issues - slightly lazy code writing, but provably low false-negatives -->
+
+        <DeprecatedMethod errorLevel="info" />
+        <DeprecatedProperty errorLevel="info" />
+        <DeprecatedClass errorLevel="info" />
+        <DeprecatedConstant errorLevel="info" />
+        <DeprecatedInterface errorLevel="info" />
+        <DeprecatedTrait errorLevel="info" />
+
+        <MissingClosureReturnType errorLevel="info" />
+        <MissingReturnType errorLevel="info" />
+        <MissingPropertyType errorLevel="info" />
+        <InvalidDocblock errorLevel="info" />
+        <MisplacedRequiredParam errorLevel="info" />
+
+        <PropertyNotSetInConstructor errorLevel="info" />
+        <MissingConstructor errorLevel="info" />
+        <MissingClosureParamType errorLevel="info" />
+        <MissingParamType errorLevel="info" />
+
+        <RedundantCondition errorLevel="info" />
+
+        <DocblockTypeContradiction errorLevel="info" />
+        <RedundantConditionGivenDocblockType errorLevel="info" />
+
+        <UnresolvableInclude errorLevel="info" />
+
+        <RawObjectIteration errorLevel="info" />
+    </issueHandlers>
+</psalm>

+ 57 - 0
vendor/pda/pheanstalk/scripts/build_phar.php

@@ -0,0 +1,57 @@
+#!/usr/bin/env php
+<?php
+
+define('BASE_DIR', realpath(__DIR__.'/..'));
+define('PHAR_FILENAME', 'pheanstalk.phar');
+define('PHAR_FULLPATH', BASE_DIR.'/'.PHAR_FILENAME);
+
+// ----------------------------------------
+
+reexecute_if_phar_readonly($argv);
+delete_existing_pheanstalk_phar();
+build_pheanstalk_phar();
+verify_pheanstalk_phar();
+exit(0);
+
+// ----------------------------------------
+
+// See: http://www.php.net/manual/en/phar.configuration.php#ini.phar.readonly
+function reexecute_if_phar_readonly($argv)
+{
+    if (ini_get('phar.readonly') && !in_array('--ignore-readonly', $argv)) {
+        $command = sprintf(
+            'php -d phar.readonly=0 %s --ignore-readonly',
+            implode(' ', $argv)
+        );
+
+        echo "Phar configured readonly in php.ini; attempting to re-execute:\n";
+        echo "$command\n";
+
+        passthru($command, $exitStatus);
+        exit($exitStatus);
+    }
+}
+
+function delete_existing_pheanstalk_phar()
+{
+    if (file_exists(PHAR_FULLPATH)) {
+        printf("- Deleting existing %s\n", PHAR_FILENAME);
+        unlink(PHAR_FULLPATH);
+    }
+}
+
+function build_pheanstalk_phar()
+{
+    printf("- Building %s from %s\n", PHAR_FILENAME, BASE_DIR);
+    $phar = new Phar(PHAR_FULLPATH);
+    $phar->buildFromDirectory(BASE_DIR);
+    $phar->setStub(
+        $phar->createDefaultStub('vendor/autoload.php')
+    );
+}
+
+function verify_pheanstalk_phar()
+{
+    $phar = new Phar(PHAR_FULLPATH);
+    printf("- %s built with %d files.\n", PHAR_FILENAME, $phar->count());
+}

+ 45 - 0
vendor/pda/pheanstalk/src/Command/AbstractCommand.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\CommandInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception\CommandException;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * Common functionality for Command implementations.
+ */
+abstract class AbstractCommand implements CommandInterface
+{
+    public function hasData(): bool
+    {
+        return false;
+    }
+
+    public function getData(): string
+    {
+        throw new CommandException('Command has no data');
+    }
+
+    public function getDataLength(): int
+    {
+        throw new CommandException('Command has no data');
+    }
+
+    public function getResponseParser(): ResponseParserInterface
+    {
+        if ($this instanceof ResponseParserInterface) {
+            return $this;
+        }
+        throw new \RuntimeException('Concrete implementation must implement `ResponseParser` or override this method');
+    }
+
+    /**
+     * Creates a Response for the given data.
+     */
+    protected function createResponse(string $name, array $data = []): ArrayResponse
+    {
+        return new ArrayResponse($name, $data);
+    }
+}

+ 48 - 0
vendor/pda/pheanstalk/src/Command/BuryCommand.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\JobIdInterface;
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'bury' command.
+ * Puts a job into a 'buried' state, revived only by 'kick' command.
+ */
+class BuryCommand extends JobCommand implements ResponseParserInterface
+{
+    private $priority;
+
+    public function __construct(JobIdInterface $job, int $priority)
+    {
+        parent::__construct($job);
+        $this->priority = $priority;
+    }
+
+    public function getCommandLine(): string
+    {
+        return sprintf(
+            'bury %u %u',
+            $this->jobId,
+            $this->priority
+        );
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if ($responseLine == ResponseInterface::RESPONSE_NOT_FOUND) {
+            throw new Exception\JobNotFoundException(sprintf(
+                '%s: Job %u is not reserved or does not exist.',
+                $responseLine,
+                $this->jobId
+            ));
+        } elseif ($responseLine == ResponseInterface::RESPONSE_BURIED) {
+            return $this->createResponse(ResponseInterface::RESPONSE_BURIED);
+        } else {
+            throw new Exception('Unhandled response: '.$responseLine);
+        }
+    }
+}

+ 33 - 0
vendor/pda/pheanstalk/src/Command/DeleteCommand.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'delete' command.
+ * Permanently deletes an already-reserved job.
+ */
+class DeleteCommand extends JobCommand implements ResponseParserInterface
+{
+    public function getCommandLine(): string
+    {
+        return 'delete '.$this->jobId;
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if ($responseLine == ResponseInterface::RESPONSE_NOT_FOUND) {
+            throw new Exception\JobNotFoundException(sprintf(
+                'Cannot delete job %u: %s',
+                $this->jobId,
+                $responseLine
+            ));
+        }
+
+        return $this->createResponse($responseLine);
+    }
+}

+ 35 - 0
vendor/pda/pheanstalk/src/Command/IgnoreCommand.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'ignore' command.
+ * Removes a tube from the watch list to reserve jobs from.
+ */
+class IgnoreCommand extends TubeCommand implements ResponseParserInterface
+{
+    public function getCommandLine(): string
+    {
+        return 'ignore '.$this->tube;
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if (preg_match('#^WATCHING (\d+)$#', $responseLine, $matches)) {
+            return $this->createResponse('WATCHING', [
+                'count' => (int) $matches[1],
+            ]);
+        } elseif ($responseLine == ResponseInterface::RESPONSE_NOT_IGNORED) {
+            throw new Exception\ServerException(
+                $responseLine.': cannot ignore last tube in watchlist'
+            );
+        } else {
+            throw new Exception('Unhandled response: '.$responseLine);
+        }
+    }
+}

+ 18 - 0
vendor/pda/pheanstalk/src/Command/JobCommand.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\JobIdInterface;
+
+/**
+ * A command that is executed against a single job
+ */
+abstract class JobCommand extends AbstractCommand
+{
+    protected $jobId;
+
+    public function __construct(JobIdInterface $subject)
+    {
+        $this->jobId = $subject->getId();
+    }
+}

+ 43 - 0
vendor/pda/pheanstalk/src/Command/KickCommand.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'kick' command.
+ *
+ * Kicks buried or delayed jobs into a 'ready' state.
+ * If there are buried jobs, it will kick up to $max of them.
+ * Otherwise, it will kick up to $max delayed jobs.
+ */
+class KickCommand extends AbstractCommand implements ResponseParserInterface
+{
+    private $max;
+
+    /**
+     * @param int $max The maximum number of jobs to kick
+     */
+    public function __construct(int $max)
+    {
+        $this->max = $max;
+    }
+
+    public function getCommandLine(): string
+    {
+        return 'kick '.$this->max;
+    }
+
+    /* (non-phpdoc)
+     * @see ResponseParser::parseResponse()
+     */
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        list($code, $count) = explode(' ', $responseLine);
+
+        return $this->createResponse($code, [
+            'kicked' => (int) $count,
+        ]);
+    }
+}

+ 44 - 0
vendor/pda/pheanstalk/src/Command/KickJobCommand.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'kick-job' command.
+ *
+ * Kicks a specific buried or delayed job into a 'ready' state.
+ *
+ * A variant of kick that operates with a single job. If the given job
+ * exists and is in a buried or delayed state, it will be moved to the
+ * ready queue of the the same tube where it currently belongs.
+ *
+ */
+class KickJobCommand extends JobCommand implements ResponseParserInterface
+{
+    public function getCommandLine(): string
+    {
+        return 'kick-job '.$this->jobId;
+    }
+
+    /* (non-phpdoc)
+     * @see ResponseParser::parseResponse()
+     */
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if ($responseLine == ResponseInterface::RESPONSE_NOT_FOUND) {
+            throw new Exception\JobNotFoundException(sprintf(
+                '%s: Job %d does not exist or is not in a kickable state.',
+                $responseLine,
+                $this->jobId
+            ));
+        } elseif ($responseLine == ResponseInterface::RESPONSE_KICKED) {
+            return $this->createResponse(ResponseInterface::RESPONSE_KICKED);
+        } else {
+            throw new Exception('Unhandled response: '.$responseLine);
+        }
+    }
+}

+ 26 - 0
vendor/pda/pheanstalk/src/Command/ListTubeUsedCommand.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'list-tube-used' command.
+ *
+ * Returns the tube currently being used by the client.
+ */
+class ListTubeUsedCommand extends AbstractCommand implements ResponseParserInterface
+{
+    public function getCommandLine(): string
+    {
+        return 'list-tube-used';
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        return $this->createResponse('USING', [
+            'tube' => preg_replace('#^USING (.+)$#', '$1', $responseLine),
+        ]);
+    }
+}

+ 24 - 0
vendor/pda/pheanstalk/src/Command/ListTubesCommand.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\YamlResponseParser;
+
+/**
+ * The 'list-tubes' command.
+ *
+ * List all existing tubes.
+ */
+class ListTubesCommand extends AbstractCommand
+{
+    public function getCommandLine(): string
+    {
+        return 'list-tubes';
+    }
+
+    public function getResponseParser(): ResponseParserInterface
+    {
+        return new YamlResponseParser(YamlResponseParser::MODE_LIST);
+    }
+}

+ 24 - 0
vendor/pda/pheanstalk/src/Command/ListTubesWatchedCommand.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\YamlResponseParser;
+
+/**
+ * The 'list-tubes-watched' command.
+ *
+ * Lists the tubes on the watchlist.
+ */
+class ListTubesWatchedCommand extends AbstractCommand
+{
+    public function getCommandLine(): string
+    {
+        return 'list-tubes-watched';
+    }
+
+    public function getResponseParser(): ResponseParserInterface
+    {
+        return new YamlResponseParser(YamlResponseParser::MODE_LIST);
+    }
+}

+ 55 - 0
vendor/pda/pheanstalk/src/Command/PauseTubeCommand.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'pause-tube' command.
+ *
+ * Temporarily prevent jobs being reserved from the given tube.
+ */
+class PauseTubeCommand extends TubeCommand implements ResponseParserInterface
+{
+    /**
+     * @var int
+     */
+    private $delay;
+
+    /**
+     * @param string $tube  The tube to pause
+     * @param int    $delay Seconds before jobs may be reserved from this queue.
+     */
+    public function __construct(string $tube, int $delay)
+    {
+        parent::__construct($tube);
+        $this->delay = $delay;
+    }
+
+    public function getCommandLine(): string
+    {
+        return sprintf(
+            'pause-tube %s %u',
+            $this->tube,
+            $this->delay
+        );
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if ($responseLine == ResponseInterface::RESPONSE_NOT_FOUND) {
+            throw new Exception\ServerException(sprintf(
+                '%s: tube %s does not exist.',
+                $responseLine,
+                $this->tube
+            ));
+        } elseif ($responseLine == ResponseInterface::RESPONSE_PAUSED) {
+            return $this->createResponse(ResponseInterface::RESPONSE_PAUSED);
+        } else {
+            throw new Exception('Unhandled response: "'.$responseLine.'"');
+        }
+    }
+}

+ 69 - 0
vendor/pda/pheanstalk/src/Command/PeekCommand.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'peek', 'peek-ready', 'peek-delayed' and 'peek-buried' commands.
+ *
+ * The peek commands let the client inspect a job in the system. There are four
+ * variations. All but the first (peek) operate only on the currently used tube.
+ */
+class PeekCommand extends AbstractCommand implements ResponseParserInterface
+{
+    const TYPE_ID = 'id';
+    const TYPE_READY = 'ready';
+    const TYPE_DELAYED = 'delayed';
+    const TYPE_BURIED = 'buried';
+
+    private const SUBCOMMANDS = [
+        self::TYPE_READY,
+        self::TYPE_DELAYED,
+        self::TYPE_BURIED,
+    ];
+
+    /**
+     * @var string
+     */
+    private $subcommand;
+
+    public function __construct(string $peekSubject)
+    {
+        if (in_array($peekSubject, self::SUBCOMMANDS)) {
+            $this->subcommand = $peekSubject;
+        } else {
+            throw new Exception\CommandException(sprintf(
+                'Invalid peek subject: %s',
+                $peekSubject
+            ));
+        }
+    }
+
+    public function getCommandLine(): string
+    {
+        return sprintf('peek-%s', $this->subcommand);
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if ($responseLine == ResponseInterface::RESPONSE_NOT_FOUND) {
+            return $this->createResponse(ResponseInterface::RESPONSE_NOT_FOUND);
+        }
+
+        if (preg_match('#^FOUND (\d+) \d+$#', $responseLine, $matches)) {
+            return $this->createResponse(
+                ResponseInterface::RESPONSE_FOUND,
+                [
+                    'id'      => (int) $matches[1],
+                    'jobdata' => $responseData,
+                ]
+            );
+        }
+
+        throw new Exception\ServerException("Unexpected response");
+    }
+}

+ 45 - 0
vendor/pda/pheanstalk/src/Command/PeekJobCommand.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'peek' command.
+ *
+ * The peek command let the client inspect a specific job in the system.
+ */
+class PeekJobCommand extends JobCommand implements ResponseParserInterface
+{
+    public function getCommandLine(): string
+    {
+        return sprintf('peek %u', $this->jobId);
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if ($responseLine == ResponseInterface::RESPONSE_NOT_FOUND) {
+            $message = sprintf(
+                '%s: Job %u does not exist.',
+                $responseLine,
+                $this->jobId
+            );
+            throw new Exception\JobNotFoundException($message);
+        }
+
+        if (preg_match('#^FOUND (\d+) \d+$#', $responseLine, $matches)) {
+            return $this->createResponse(
+                ResponseInterface::RESPONSE_FOUND,
+                [
+                    'id'      => (int) $matches[1],
+                    'jobdata' => $responseData,
+                ]
+            );
+        }
+
+        throw new Exception\ServerException("Unexpected response: " . $responseLine);
+    }
+}

+ 98 - 0
vendor/pda/pheanstalk/src/Command/PutCommand.php

@@ -0,0 +1,98 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'put' command.
+ *
+ * Inserts a job into the client's currently used tube.
+ *
+ * @see UseCommand
+ */
+class PutCommand extends AbstractCommand implements ResponseParserInterface
+{
+    private $data;
+    private $priority;
+    private $delay;
+    private $ttr;
+
+    /**
+     * Puts a job on the queue.
+     *
+     * @param string $data     The job data
+     * @param int    $priority From 0 (most urgent) to 0xFFFFFFFF (least urgent)
+     * @param int    $delay    Seconds to wait before job becomes ready
+     * @param int    $ttr      Time To Run: seconds a job can be reserved for
+     */
+    public function __construct(string $data, int $priority, int $delay, int $ttr)
+    {
+        $this->data = $data;
+        $this->priority = $priority;
+        $this->delay = $delay;
+        $this->ttr = $ttr;
+    }
+
+    public function getCommandLine(): string
+    {
+        return sprintf(
+            'put %u %u %u %u',
+            $this->priority,
+            $this->delay,
+            $this->ttr,
+            $this->getDataLength()
+        );
+    }
+
+    public function hasData(): bool
+    {
+        return true;
+    }
+
+    public function getData(): string
+    {
+        return $this->data;
+    }
+
+    public function getDataLength(): int
+    {
+        return mb_strlen($this->data, '8bit');
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if (preg_match('#^INSERTED (\d+)$#', $responseLine, $matches)) {
+            return $this->createResponse('INSERTED', [
+                'id' => (int) $matches[1],
+            ]);
+        } elseif (preg_match('#^BURIED (\d)+$#', $responseLine, $matches)) {
+            throw new Exception\ServerOutOfMemoryException(sprintf(
+                '%s: server ran out of memory trying to grow the priority queue data structure.',
+                $responseLine
+            ));
+        } elseif (preg_match('#^JOB_TOO_BIG$#', $responseLine)) {
+            throw new Exception\JobTooBigException(sprintf(
+                '%s: job data exceeds server-enforced limit',
+                $responseLine
+            ));
+        } elseif (preg_match('#^EXPECTED_CRLF#', $responseLine)) {
+            throw new Exception\ClientBadFormatException(sprintf(
+                '%s: CRLF expected',
+                $responseLine
+            ));
+        } elseif (preg_match('#^DRAINING#', $responseLine)) {
+            throw new Exception\ServerDrainingException(sprintf(
+                '%s: server is in drain mode and no longer accepting new jobs',
+                $responseLine
+            ));
+        } else {
+            throw new Exception(sprintf(
+                'Unhandled response: %s',
+                $responseLine
+            ));
+        }
+    }
+}

+ 61 - 0
vendor/pda/pheanstalk/src/Command/ReleaseCommand.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\JobIdInterface;
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'release' command.
+ *
+ * Releases a reserved job back onto the ready queue.
+ */
+class ReleaseCommand extends JobCommand implements ResponseParserInterface
+{
+    private $priority;
+    private $delay;
+
+    public function __construct(JobIdInterface $job, int $priority, int $delay)
+    {
+        parent::__construct($job);
+        $this->priority = $priority;
+        $this->delay = $delay;
+    }
+
+    /* (non-phpdoc)
+     * @see Command::getCommandLine()
+     */
+    public function getCommandLine(): string
+    {
+        return sprintf(
+            'release %u %u %u',
+            $this->jobId,
+            $this->priority,
+            $this->delay
+        );
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if ($responseLine == ResponseInterface::RESPONSE_BURIED) {
+            throw new Exception\ServerOutOfMemoryException(sprintf(
+                'Job %u %s: out of memory trying to grow data structure',
+                $this->jobId,
+                $responseLine
+            ));
+        }
+
+        if ($responseLine == ResponseInterface::RESPONSE_NOT_FOUND) {
+            throw new Exception\JobNotFoundException(sprintf(
+                'Job %u %s: does not exist or is not reserved by client',
+                $this->jobId,
+                $responseLine
+            ));
+        }
+
+        return $this->createResponse($responseLine);
+    }
+}

+ 34 - 0
vendor/pda/pheanstalk/src/Command/ReserveCommand.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception\DeadlineSoonException;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'reserve' command.
+ *
+ * Reserves/locks a ready job in a watched tube.
+ */
+class ReserveCommand extends AbstractCommand implements ResponseParserInterface
+{
+    public function getCommandLine(): string
+    {
+        return 'reserve';
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if ($responseLine === ResponseInterface::RESPONSE_DEADLINE_SOON) {
+            throw new DeadlineSoonException();
+        }
+
+        list($code, $id) = explode(' ', $responseLine);
+        return $this->createResponse($code, [
+            'id'      => (int) $id,
+            'jobdata' => $responseData,
+        ]);
+    }
+}

+ 48 - 0
vendor/pda/pheanstalk/src/Command/ReserveWithTimeoutCommand.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'reserve' command.
+ * Reserves/locks a ready job in a watched tube.
+ */
+class ReserveWithTimeoutCommand extends AbstractCommand implements ResponseParserInterface
+{
+    private $timeout;
+
+    /**
+     * A timeout value of 0 will cause the server to immediately return either a
+     * response or TIMED_OUT.  A positive value of timeout will limit the amount of
+     * time the client will block on the reserve request until a job becomes
+     * available.
+     */
+    public function __construct(int $timeout)
+    {
+        $this->timeout = $timeout;
+    }
+
+    public function getCommandLine(): string
+    {
+        return sprintf('reserve-with-timeout %s', $this->timeout);
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if ($responseLine === ResponseInterface::RESPONSE_DEADLINE_SOON
+            || $responseLine === ResponseInterface::RESPONSE_TIMED_OUT
+        ) {
+            return $this->createResponse($responseLine);
+        }
+
+        list($code, $id) = explode(' ', $responseLine);
+
+        return $this->createResponse($code, [
+            'id'      => (int) $id,
+            'jobdata' => $responseData,
+        ]);
+    }
+}

+ 25 - 0
vendor/pda/pheanstalk/src/Command/StatsCommand.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\YamlResponseParser;
+
+/**
+ * The 'stats' command.
+ *
+ * Statistical information about the system as a whole.
+ */
+class StatsCommand extends AbstractCommand
+{
+    public function getCommandLine(): string
+    {
+        return 'stats';
+    }
+
+    public function getResponseParser(): \Pheanstalk\Contract\ResponseParserInterface
+    {
+        return new YamlResponseParser(
+            YamlResponseParser::MODE_DICT
+        );
+    }
+}

+ 24 - 0
vendor/pda/pheanstalk/src/Command/StatsJobCommand.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\YamlResponseParser;
+
+/**
+ * The 'stats-job' command.
+ *
+ * Gives statistical information about the specified job if it exists.
+ */
+class StatsJobCommand extends JobCommand
+{
+    public function getCommandLine(): string
+    {
+        return sprintf('stats-job %u', $this->jobId);
+    }
+
+    public function getResponseParser(): ResponseParserInterface
+    {
+        return new YamlResponseParser(YamlResponseParser::MODE_DICT);
+    }
+}

+ 24 - 0
vendor/pda/pheanstalk/src/Command/StatsTubeCommand.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\YamlResponseParser;
+
+/**
+ * The 'stats-tube' command.
+ * Gives statistical information about the specified tube if it exists.
+ */
+class StatsTubeCommand extends TubeCommand
+{
+    public function getCommandLine(): string
+    {
+        return sprintf('stats-tube %s', $this->tube);
+    }
+
+    public function getResponseParser(): \Pheanstalk\Contract\ResponseParserInterface
+    {
+        return new YamlResponseParser(
+            YamlResponseParser::MODE_DICT
+        );
+    }
+}

+ 37 - 0
vendor/pda/pheanstalk/src/Command/TouchCommand.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'touch' command.
+ * The 'touch' command allows a worker to request more time to work on a job.
+ * This is useful for jobs that potentially take a long time, but you still want
+ * the benefits of a TTR pulling a job away from an unresponsive worker.  A worker
+ * may periodically tell the server that it's still alive and processing a job
+ * (e.g. it may do this on DEADLINE_SOON).
+ */
+class TouchCommand extends JobCommand implements ResponseParserInterface
+{
+    public function getCommandLine(): string
+    {
+        return sprintf('touch %u', $this->jobId);
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if ($responseLine == ResponseInterface::RESPONSE_NOT_FOUND) {
+            throw new Exception\JobNotFoundException(sprintf(
+                'Job %u %s: does not exist or is not reserved by client',
+                $this->jobId,
+                $responseLine
+            ));
+        }
+
+        return $this->createResponse($responseLine);
+    }
+}

+ 16 - 0
vendor/pda/pheanstalk/src/Command/TubeCommand.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+/**
+ * A command that is executed against a tube
+ */
+abstract class TubeCommand extends AbstractCommand
+{
+    protected $tube;
+
+    public function __construct(string $tube)
+    {
+        $this->tube = $tube;
+    }
+}

+ 28 - 0
vendor/pda/pheanstalk/src/Command/UseCommand.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'use' command.
+ *
+ * The "use" command is for producers. Subsequent put commands will put jobs into
+ * the tube specified by this command. If no use command has been issued, jobs
+ * will be put into the tube named "default".
+ */
+class UseCommand extends TubeCommand implements ResponseParserInterface
+{
+    public function getCommandLine(): string
+    {
+        return 'use '.$this->tube;
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        return $this->createResponse('USING', [
+            'tube' => preg_replace('#^USING (.+)$#', '$1', $responseLine),
+        ]);
+    }
+}

+ 25 - 0
vendor/pda/pheanstalk/src/Command/WatchCommand.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Pheanstalk\Command;
+
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * The 'watch' command.
+ * Adds a tube to the watchlist to reserve jobs from.
+ */
+class WatchCommand extends TubeCommand implements ResponseParserInterface
+{
+    public function getCommandLine(): string
+    {
+        return 'watch '.$this->tube;
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        return $this->createResponse('WATCHING', [
+            'count' => preg_replace('#^WATCHING (.+)$#', '$1', $responseLine),
+        ]);
+    }
+}

+ 134 - 0
vendor/pda/pheanstalk/src/Connection.php

@@ -0,0 +1,134 @@
+<?php
+
+namespace Pheanstalk;
+
+use Pheanstalk\Contract\CommandInterface;
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\SocketFactoryInterface;
+use Pheanstalk\Contract\SocketInterface;
+use Pheanstalk\Exception\ServerBadFormatException;
+use Pheanstalk\Exception\ServerDrainingException;
+use Pheanstalk\Exception\ServerInternalErrorException;
+use Pheanstalk\Exception\ServerOutOfMemoryException;
+use Pheanstalk\Exception\ServerUnknownCommandException;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * A connection to a beanstalkd server, backed by any type of socket.
+ *
+ */
+class Connection
+{
+    const CRLF = "\r\n";
+    const CRLF_LENGTH = 2;
+    const DEFAULT_CONNECT_TIMEOUT = 2;
+
+    // responses which are global errors, mapped to their exception classes
+    private static $errorResponses = [
+        ResponseInterface::RESPONSE_OUT_OF_MEMORY   => ServerOutOfMemoryException::class,
+        ResponseInterface::RESPONSE_INTERNAL_ERROR  => ServerInternalErrorException::class,
+        ResponseInterface::RESPONSE_DRAINING        => ServerDrainingException::class,
+        ResponseInterface::RESPONSE_BAD_FORMAT      => ServerBadFormatException::class,
+        ResponseInterface::RESPONSE_UNKNOWN_COMMAND => ServerUnknownCommandException::class,
+    ];
+
+    // responses which are followed by data
+    private static $dataResponses = [
+        ResponseInterface::RESPONSE_RESERVED,
+        ResponseInterface::RESPONSE_FOUND,
+        ResponseInterface::RESPONSE_OK,
+    ];
+
+    /**
+     * @var SocketFactoryInterface
+     */
+    private $factory;
+
+    /**
+     * @var ?SocketInterface
+     */
+    private $socket;
+
+    public function __construct(SocketFactoryInterface $factory)
+    {
+        $this->factory = $factory;
+    }
+
+    /**
+     * Disconnect the socket.
+     * Subsequent socket operations will create a new connection.
+     */
+    public function disconnect()
+    {
+        if (isset($this->socket)) {
+            $this->socket->disconnect();
+            $this->socket = null;
+        }
+    }
+
+    /**
+     * @throws Exception\ClientException
+     */
+    public function dispatchCommand(CommandInterface $command): ArrayResponse
+    {
+        $socket = $this->getSocket();
+
+        $to_send = $command->getCommandLine().self::CRLF;
+
+        if ($command->hasData()) {
+            $to_send .= $command->getData().self::CRLF;
+        }
+
+        $socket->write($to_send);
+
+        $responseLine = $socket->getLine();
+        $responseName = preg_replace('#^(\S+).*$#s', '$1', $responseLine);
+
+        if (isset(self::$errorResponses[$responseName])) {
+            $exceptionClass = self::$errorResponses[$responseName];
+
+            throw new $exceptionClass(sprintf(
+                "%s in response to '%s'",
+                $responseName,
+                $command->getCommandLine()
+            ));
+        }
+
+        if (in_array($responseName, self::$dataResponses)) {
+            $dataLength = preg_replace('#^.*\b(\d+)$#', '$1', $responseLine);
+            $data = $socket->read((int) $dataLength);
+            $crlf = $socket->read(self::CRLF_LENGTH);
+            if ($crlf !== self::CRLF) {
+                throw new Exception\ClientException(sprintf(
+                    'Expected %u bytes of CRLF after %u bytes of data',
+                    self::CRLF_LENGTH,
+                    $dataLength
+                ));
+            }
+        } else {
+            $data = null;
+        }
+
+        return $command
+            ->getResponseParser()
+            ->parseResponse($responseLine, $data);
+    }
+
+    // ----------------------------------------
+
+    /**
+     * Socket handle for the connection to beanstalkd.
+     *
+     * @throws Exception\ConnectionException
+     *
+     * @return SocketInterface
+     */
+    private function getSocket()
+    {
+        if (!isset($this->socket)) {
+            $this->socket = $this->factory->create();
+        }
+
+        return $this->socket;
+    }
+}

+ 52 - 0
vendor/pda/pheanstalk/src/Contract/CommandInterface.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace Pheanstalk\Contract;
+
+/**
+ * A command to be sent to the beanstalkd server, and response processing logic.
+ *
+ * @author  Paul Annesley
+ */
+interface CommandInterface
+{
+    const COMMAND_PUT = 'put';
+    const COMMAND_USE = 'use';
+    const COMMAND_RESERVE = 'reserve';
+    const COMMAND_DELETE = 'delete';
+    const COMMAND_RELEASE = 'release';
+    const COMMAND_BURY = 'bury';
+    const COMMAND_WATCH = 'watch';
+    const COMMAND_IGNORE = 'ignore';
+    const COMMAND_PEEK = 'peek';
+    const COMMAND_KICK = 'kick';
+    const COMMAND_STATS_JOB = 'stats-job';
+    const COMMAND_STATS = 'stats';
+    const COMMAND_LIST_TUBES = 'list-tubes';
+    const COMMAND_LIST_TUBE_USED = 'list-tube-used';
+    const COMMAND_LIST_TUBES_WATCHED = 'list-tubes-watched';
+
+    /**
+     * The command line, without trailing CRLF.
+     */
+    public function getCommandLine(): string;
+
+    /**
+     * Whether the command is followed by data.
+     */
+    public function hasData(): bool;
+
+    /**
+     * The binary data to follow the command.
+     */
+    public function getData(): string;
+
+    /**
+     * The length of the binary data in bytes.
+     */
+    public function getDataLength(): int;
+
+    /**
+     * The response parser for the command.
+     */
+    public function getResponseParser(): ResponseParserInterface;
+}

+ 9 - 0
vendor/pda/pheanstalk/src/Contract/JobIdInterface.php

@@ -0,0 +1,9 @@
+<?php
+
+
+namespace Pheanstalk\Contract;
+
+interface JobIdInterface
+{
+    public function getId(): int;
+}

+ 226 - 0
vendor/pda/pheanstalk/src/Contract/PheanstalkInterface.php

@@ -0,0 +1,226 @@
+<?php
+
+namespace Pheanstalk\Contract;
+
+use Pheanstalk\Job;
+
+interface PheanstalkInterface
+{
+    const DEFAULT_PORT = 11300;
+    const DEFAULT_DELAY = 0; // no delay
+    const DEFAULT_PRIORITY = 1024; // most urgent: 0, least urgent: 4294967295
+    const DEFAULT_TTR = 60; // 1 minute
+    const DEFAULT_TUBE = 'default';
+
+    // ----------------------------------------
+
+    /**
+     * Puts a job into a 'buried' state, revived only by 'kick' command.
+     */
+    public function bury(JobIdInterface $job, int $priority = self::DEFAULT_PRIORITY): void;
+
+    /**
+     * Permanently deletes a job.
+     */
+    public function delete(JobIdInterface $job): void;
+
+    /**
+     * Remove the specified tube from the watchlist.
+     *
+     * Does not execute an IGNORE command if the specified tube is not in the
+     * cached watchlist.
+     *
+     * @param string $tube
+     *
+     * @return $this
+     */
+    public function ignore(string $tube): self;
+
+    /**
+     * Kicks buried or delayed jobs into a 'ready' state.
+     * If there are buried jobs, it will kick up to $max of them.
+     * Otherwise, it will kick up to $max delayed jobs.
+     *
+     * @param int $max The maximum jobs to kick
+     *
+     * @return int Number of jobs kicked
+     */
+    public function kick(int $max): int;
+
+    /**
+     * A variant of kick that operates with a single job. If the given job
+     * exists and is in a buried or delayed state, it will be moved to the
+     * ready queue of the the same tube where it currently belongs.
+     */
+    public function kickJob(JobIdInterface $job): void;
+
+    /**
+     * The names of all tubes on the server.
+     *
+     * @return string[]
+     */
+    public function listTubes(): array;
+
+    /**
+     * The names of the tubes being watched, to reserve jobs from.
+     *
+     * Returns the cached watchlist if $askServer is false (the default),
+     * or queries the server for the watchlist if $askServer is true.
+     *
+     * @param bool $askServer
+     *
+     * @return string[]
+     */
+    public function listTubesWatched(bool $askServer = false): array;
+
+    /**
+     * The name of the current tube used for publishing jobs to.
+     *
+     * Returns the cached value if $askServer is false (the default),
+     * or queries the server for the currently used tube if $askServer
+     * is true.
+     */
+    public function listTubeUsed(bool $askServer = false): string;
+
+    /**
+     * Temporarily prevent jobs being reserved from the given tube.
+     *
+     * @param string $tube  The tube to pause
+     * @param int    $delay Seconds before jobs may be reserved from this queue.
+     */
+    public function pauseTube(string $tube, int $delay): void;
+
+    /**
+     * Resume jobs for a given paused tube.
+     * @param string $tube The tube to resume
+     */
+    public function resumeTube(string $tube): void;
+
+    /**
+     * Inspect a job in the system, regardless of what tube it is in.
+     */
+    public function peek(JobIdInterface $job): Job;
+
+    /**
+     * Inspect the next ready job in the currently used tube.
+     */
+    public function peekReady(): ?Job;
+
+    /**
+     * Inspect the shortest-remaining-delayed job in the currently used tube.
+     * @return ?Job
+     */
+    public function peekDelayed(): ?Job;
+
+    /**
+     * Inspect the next job in the list of buried jobs in the currently used tube.
+     */
+    public function peekBuried(): ?Job;
+
+    /**
+     * Puts a job on the queue.
+     *
+     * @param string $data     The job data
+     * @param int    $priority From 0 (most urgent) to 0xFFFFFFFF (least urgent)
+     * @param int    $delay    Seconds to wait before job becomes ready
+     * @param int    $ttr      Time To Run: seconds a job can be reserved for
+     */
+    public function put(
+        string $data,
+        int $priority = self::DEFAULT_PRIORITY,
+        int $delay = self::DEFAULT_DELAY,
+        int $ttr = self::DEFAULT_TTR
+    ): Job;
+
+    /**
+     * Puts a reserved job back into the ready queue.
+     *
+     * Marks the jobs state as "ready" to be run by any client.
+     * It is normally used when the job fails because of a transitory error.
+     *
+     * @param JobIdInterface $job
+     * @param int $priority From 0 (most urgent) to 0xFFFFFFFF (least urgent)
+     * @param int $delay Seconds to wait before job becomes ready
+     */
+    public function release(
+        JobIdInterface $job,
+        int $priority = self::DEFAULT_PRIORITY,
+        int $delay = self::DEFAULT_DELAY
+    ): void;
+
+    /**
+     * Reserves/locks a ready job in a watched tube.
+     */
+    public function reserve(): ?Job;
+
+    /**
+     * Reserves/locks a ready job in a watched tube, uses the 'reserve-with-timeout' instead of 'reserve'.
+     *
+     * A timeout value of 0 will cause the server to immediately return either a
+     * response or TIMED_OUT.  A positive value of timeout will limit the amount of
+     * time the client will block on the reserve request until a job becomes
+     * available.
+     */
+    public function reserveWithTimeout(int $timeout): ?Job;
+
+    /**
+     * Gives statistical information about the specified job if it exists.
+     */
+    public function statsJob(JobIdInterface $job): ResponseInterface;
+
+    /**
+     * Gives statistical information about the specified tube if it exists.
+     */
+    public function statsTube(string $tube): ResponseInterface;
+
+    /**
+     * Gives statistical information about the beanstalkd system as a whole.
+     */
+    public function stats(): ResponseInterface;
+
+    /**
+     * Allows a worker to request more time to work on a job.
+     *
+     * This is useful for jobs that potentially take a long time, but you still want
+     * the benefits of a TTR pulling a job away from an unresponsive worker.  A worker
+     * may periodically tell the server that it's still alive and processing a job
+     * (e.g. it may do this on DEADLINE_SOON).
+     *
+     */
+    public function touch(JobIdInterface $job): void;
+
+    /**
+     * Change to the specified tube name for publishing jobs to.
+     * This method would be called 'use' if it were not a PHP reserved word.
+     *
+     * Does not execute a USE command if the client is already using the
+     * specified tube.
+     *
+     * @param string $tube
+     *
+     * @return $this
+     */
+    public function useTube(string $tube): self;
+
+    /**
+     * Add the specified tube to the watchlist, to reserve jobs from.
+     *
+     * Does not execute a WATCH command if the client is already watching the
+     * specified tube.
+     *
+     * @param string $tube
+     *
+     * @return $this
+     */
+    public function watch(string $tube): self;
+
+    /**
+     * Adds the specified tube to the watchlist, to reserve jobs from, and
+     * ignores any other tubes remaining on the watchlist.
+     *
+     * @param string $tube
+     *
+     * @return $this
+     */
+    public function watchOnly(string $tube): self;
+}

+ 44 - 0
vendor/pda/pheanstalk/src/Contract/ResponseInterface.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Pheanstalk\Contract;
+
+/**
+ * A response from the beanstalkd server.
+ *
+ * @author  Paul Annesley
+ */
+interface ResponseInterface extends \ArrayAccess, \Traversable
+{
+    // global error responses
+    const RESPONSE_OUT_OF_MEMORY = 'OUT_OF_MEMORY';
+    const RESPONSE_INTERNAL_ERROR = 'INTERNAL_ERROR';
+    const RESPONSE_DRAINING = 'DRAINING';
+    const RESPONSE_BAD_FORMAT = 'BAD_FORMAT';
+    const RESPONSE_UNKNOWN_COMMAND = 'UNKNOWN_COMMAND';
+
+    // command responses
+    const RESPONSE_INSERTED = 'INSERTED';
+    const RESPONSE_BURIED = 'BURIED';
+    const RESPONSE_EXPECTED_CRLF = 'EXPECTED_CRLF';
+    const RESPONSE_JOB_TOO_BIG = 'JOB_TOO_BIG';
+    const RESPONSE_USING = 'USING';
+    const RESPONSE_DEADLINE_SOON = 'DEADLINE_SOON';
+    const RESPONSE_RESERVED = 'RESERVED';
+    const RESPONSE_DELETED = 'DELETED';
+    const RESPONSE_NOT_FOUND = 'NOT_FOUND';
+    const RESPONSE_RELEASED = 'RELEASED';
+    const RESPONSE_WATCHING = 'WATCHING';
+    const RESPONSE_NOT_IGNORED = 'NOT_IGNORED';
+    const RESPONSE_FOUND = 'FOUND';
+    const RESPONSE_KICKED = 'KICKED';
+    const RESPONSE_OK = 'OK';
+    const RESPONSE_TIMED_OUT = 'TIMED_OUT';
+    const RESPONSE_TOUCHED = 'TOUCHED';
+    const RESPONSE_PAUSED = 'PAUSED';
+
+    /**
+     * The name of the response.
+     * @return string
+     */
+    public function getResponseName(): string;
+}

+ 22 - 0
vendor/pda/pheanstalk/src/Contract/ResponseParserInterface.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace Pheanstalk\Contract;
+
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * A parser for response data sent from the beanstalkd server.
+ *
+ * @author  Paul Annesley
+ */
+interface ResponseParserInterface
+{
+    /**
+     * Parses raw response data into a Response object.
+     *
+     * @param string $responseLine Without trailing CRLF
+     * @param string $responseData (null if no data)
+     * @return ArrayResponse
+     */
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse;
+}

+ 13 - 0
vendor/pda/pheanstalk/src/Contract/SocketFactoryInterface.php

@@ -0,0 +1,13 @@
+<?php
+
+
+namespace Pheanstalk\Contract;
+
+interface SocketFactoryInterface
+{
+    /**
+     * This function must return a connected socket that is ready for reading / writing.
+     * @return SocketInterface
+     */
+    public function create(): SocketInterface;
+}

+ 42 - 0
vendor/pda/pheanstalk/src/Contract/SocketInterface.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace Pheanstalk\Contract;
+
+/**
+ * A mockable wrapper around PHP "socket" or "file pointer" access.
+ *
+ * Only the subset of socket actions required by Pheanstalk are provided.
+ *
+ * @author  Paul Annesley
+ */
+interface SocketInterface
+{
+    /**
+     * Writes data to the socket.
+     *
+     * @param string $data
+     *
+     * @return void
+     */
+    public function write(string $data): void;
+
+    /**
+     * Reads up to $length bytes from the socket.
+     *
+     * @return string
+     */
+    public function read(int $length): string;
+
+    /**
+     * Reads up to the next new-line.
+     * Trailing whitespace is trimmed.
+     *
+     * @param int
+     */
+    public function getLine(): string;
+
+    /**
+     * Disconnect the socket; subsequent usage of the socket will fail.
+     */
+    public function disconnect(): void;
+}

+ 12 - 0
vendor/pda/pheanstalk/src/Exception.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Pheanstalk;
+
+/**
+ * An exception originating from the Pheanstalk package.
+ *
+ * @author  Paul Annesley
+ */
+class Exception extends \Exception
+{
+}

+ 10 - 0
vendor/pda/pheanstalk/src/Exception/ClientBadFormatException.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+/**
+ * Indicates that the client has sent unexpected data
+ */
+class ClientBadFormatException extends ClientException
+{
+}

+ 12 - 0
vendor/pda/pheanstalk/src/Exception/ClientException.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+use Pheanstalk\Exception;
+
+/**
+ * An exception originating from the beanstalkd client.
+ */
+class ClientException extends Exception
+{
+}

+ 12 - 0
vendor/pda/pheanstalk/src/Exception/CommandException.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+/**
+ * An exception relating to a Command.
+ *
+ * @author  Paul Annesley
+ */
+class CommandException extends ClientException
+{
+}

+ 20 - 0
vendor/pda/pheanstalk/src/Exception/ConnectionException.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+/**
+ * An exception relating to the client connection to the beanstalkd server.
+ *
+ * @author  Paul Annesley
+ */
+class ConnectionException extends ClientException
+{
+    /**
+     * @param int    $errno  The connection error code
+     * @param string $errstr The connection error message
+     */
+    public function __construct($errno, $errstr)
+    {
+        parent::__construct(sprintf('Socket error %d: %s', $errno, $errstr));
+    }
+}

+ 9 - 0
vendor/pda/pheanstalk/src/Exception/DeadlineSoonException.php

@@ -0,0 +1,9 @@
+<?php
+
+
+namespace Pheanstalk\Exception;
+
+class DeadlineSoonException extends ClientException
+{
+
+}

+ 12 - 0
vendor/pda/pheanstalk/src/Exception/JobNotFoundException.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+/**
+ * Indicates that the given job was not found by the server
+ *
+ * inherits from ServerException for backwards compatibility
+ */
+class JobNotFoundException extends ServerException
+{
+}

+ 10 - 0
vendor/pda/pheanstalk/src/Exception/JobTooBigException.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+/**
+ * Indicates that the given job body is larger then the servers configured max-job-size
+ */
+class JobTooBigException extends ClientException
+{
+}

+ 12 - 0
vendor/pda/pheanstalk/src/Exception/ServerBadFormatException.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+/**
+ * An exception originating as a beanstalkd server error.
+ *
+ * @author  Paul Annesley
+ */
+class ServerBadFormatException extends ServerException
+{
+}

+ 12 - 0
vendor/pda/pheanstalk/src/Exception/ServerDrainingException.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+/**
+ * An exception originating as a beanstalkd server error.
+ *
+ * @author  Paul Annesley
+ */
+class ServerDrainingException extends ServerException
+{
+}

+ 14 - 0
vendor/pda/pheanstalk/src/Exception/ServerException.php

@@ -0,0 +1,14 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+use Pheanstalk\Exception;
+
+/**
+ * An exception originating as a beanstalkd server error.
+ *
+ * @author  Paul Annesley
+ */
+class ServerException extends Exception
+{
+}

+ 12 - 0
vendor/pda/pheanstalk/src/Exception/ServerInternalErrorException.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+/**
+ * An exception originating as a beanstalkd server error.
+ *
+ * @author  Paul Annesley
+ */
+class ServerInternalErrorException extends ServerException
+{
+}

+ 12 - 0
vendor/pda/pheanstalk/src/Exception/ServerOutOfMemoryException.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+/**
+ * An exception originating as a beanstalkd server error.
+ *
+ * @author  Paul Annesley
+ */
+class ServerOutOfMemoryException extends ServerException
+{
+}

+ 12 - 0
vendor/pda/pheanstalk/src/Exception/ServerUnknownCommandException.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+/**
+ * An exception originating as a beanstalkd server error.
+ *
+ * @author  Paul Annesley
+ */
+class ServerUnknownCommandException extends ServerException
+{
+}

+ 12 - 0
vendor/pda/pheanstalk/src/Exception/SocketException.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Pheanstalk\Exception;
+
+/**
+ * An exception relating to the connection socket.
+ *
+ * @author  Paul Annesley
+ */
+class SocketException extends ClientException
+{
+}

+ 55 - 0
vendor/pda/pheanstalk/src/Job.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace Pheanstalk;
+
+use Pheanstalk\Contract\JobIdInterface;
+
+/**
+ * A job in a beanstalkd server.
+ */
+class Job implements JobIdInterface
+{
+    const STATUS_READY = 'ready';
+    const STATUS_RESERVED = 'reserved';
+    const STATUS_DELAYED = 'delayed';
+    const STATUS_BURIED = 'buried';
+
+    /**
+     * @var int
+     */
+    private $id;
+    /**
+     * @var string
+     */
+    private $data;
+
+    /**
+     * @param int    $id   The job ID
+     * @param string $data The job data
+     */
+    public function __construct(int $id, string $data)
+    {
+        $this->id = $id;
+        $this->data = $data;
+    }
+
+    /**
+     * The job ID, unique on the beanstalkd server.
+     *
+     * @return int
+     */
+    public function getId(): int
+    {
+        return $this->id;
+    }
+
+    /**
+     * The job data.
+     *
+     * @return string
+     */
+    public function getData(): string
+    {
+        return $this->data;
+    }
+}

+ 27 - 0
vendor/pda/pheanstalk/src/JobId.php

@@ -0,0 +1,27 @@
+<?php
+
+
+namespace Pheanstalk;
+
+use Pheanstalk\Contract\JobIdInterface;
+
+/**
+ * This class implements a value object for beanstalkd job IDs.
+ */
+class JobId implements JobIdInterface
+{
+    private $id;
+
+    public function __construct(int $id)
+    {
+        if ($id < 0) {
+            throw new \InvalidArgumentException('Id must be >= 0');
+        }
+        $this->id = $id;
+    }
+
+    public function getId(): int
+    {
+        return $this->id;
+    }
+}

+ 440 - 0
vendor/pda/pheanstalk/src/Pheanstalk.php

@@ -0,0 +1,440 @@
+<?php
+
+namespace Pheanstalk;
+
+use Pheanstalk\Contract\CommandInterface;
+use Pheanstalk\Contract\JobIdInterface;
+use Pheanstalk\Contract\PheanstalkInterface;
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\SocketFactoryInterface;
+use Pheanstalk\Exception\DeadlineSoonException;
+
+/**
+ * Pheanstalk is a PHP client for the beanstalkd workqueue.
+ */
+class Pheanstalk implements PheanstalkInterface
+{
+    /**
+     * @var Connection
+     */
+    private $connection;
+    /**
+     * @var ?string
+     */
+    private $using = PheanstalkInterface::DEFAULT_TUBE;
+    /**
+     * @var array<string,bool>
+     */
+    private $watching = [PheanstalkInterface::DEFAULT_TUBE => true];
+
+    public function __construct(Connection $connection)
+    {
+        $this->connection = $connection;
+    }
+
+    /**
+     * Static constructor that uses autodetection to choose an underlying socket implementation
+     * @param string $host
+     * @param int $port
+     * @param int $connectTimeout
+     * @return Pheanstalk
+     */
+    public static function create(string $host, int $port = 11300, int $connectTimeout = 10)
+    {
+        return self::createWithFactory(new SocketFactory($host, $port, $connectTimeout));
+    }
+
+    /**
+     * Static constructor that uses a given socket factory for underlying connections
+     * @param SocketFactoryInterface $factory
+     * @return Pheanstalk
+     */
+    public static function createWithFactory(SocketFactoryInterface $factory)
+    {
+        return new self(new Connection($factory));
+    }
+
+    // ----------------------------------------
+
+    /**
+     * {@inheritdoc}
+     */
+    public function bury(JobIdInterface $job, int $priority = PheanstalkInterface::DEFAULT_PRIORITY): void
+    {
+        $this->dispatch(new Command\BuryCommand($job, $priority));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function delete(JobIdInterface $job): void
+    {
+        $this->dispatch(new Command\DeleteCommand($job));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function ignore(string $tube): PheanstalkInterface
+    {
+        if (isset($this->watching[$tube])) {
+            $this->dispatch(new Command\IgnoreCommand($tube));
+            unset($this->watching[$tube]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function kick(int $max): int
+    {
+        $response = $this->dispatch(new Command\KickCommand($max));
+
+        return $response['kicked'];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function kickJob(JobIdInterface $job): void
+    {
+        $this->dispatch(new Command\KickJobCommand($job));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function listTubes(): array
+    {
+        return (array)$this->dispatch(
+            new Command\ListTubesCommand()
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function listTubesWatched(bool $askServer = false): array
+    {
+        if ($askServer) {
+            $response = (array)$this->dispatch(
+                new Command\ListTubesWatchedCommand()
+            );
+            $this->watching = array_fill_keys($response, true);
+        }
+
+        return array_keys($this->watching);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function listTubeUsed(bool $askServer = false): string
+    {
+        if ($askServer) {
+            $response = $this->dispatch(
+                new Command\ListTubeUsedCommand()
+            );
+            $this->using = $response['tube'];
+        }
+
+        return $this->using;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function pauseTube(string $tube, int $delay): void
+    {
+        $this->dispatch(new Command\PauseTubeCommand($tube, $delay));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function resumeTube(string $tube): void
+    {
+        // Pause a tube with zero delay will resume the tube
+        $this->pauseTube($tube, 0);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function peek(JobIdInterface $job): Job
+    {
+        $response = $this->dispatch(
+            new Command\PeekJobCommand($job)
+        );
+
+        return new Job($response['id'], $response['jobdata']);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function peekReady(): ?Job
+    {
+        $response = $this->dispatch(
+            new Command\PeekCommand(Command\PeekCommand::TYPE_READY)
+        );
+        if ($response->getResponseName() === ResponseInterface::RESPONSE_NOT_FOUND) {
+            return null;
+        }
+
+        return new Job($response['id'], $response['jobdata']);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function peekDelayed(): ?Job
+    {
+        $response = $this->dispatch(
+            new Command\PeekCommand(Command\PeekCommand::TYPE_DELAYED)
+        );
+        if ($response->getResponseName() === ResponseInterface::RESPONSE_NOT_FOUND) {
+            return null;
+        }
+
+        return new Job($response['id'], $response['jobdata']);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function peekBuried(): ?Job
+    {
+        $response = $this->dispatch(
+            new Command\PeekCommand(Command\PeekCommand::TYPE_BURIED)
+        );
+        if ($response->getResponseName() === ResponseInterface::RESPONSE_NOT_FOUND) {
+            return null;
+        }
+
+        return new Job($response['id'], $response['jobdata']);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function put(
+        string $data,
+        int $priority = PheanstalkInterface::DEFAULT_PRIORITY,
+        int $delay = PheanstalkInterface::DEFAULT_DELAY,
+        int $ttr = PheanstalkInterface::DEFAULT_TTR
+    ): Job {
+        $response = $this->dispatch(
+            new Command\PutCommand($data, $priority, $delay, $ttr)
+        );
+
+        return new Job($response['id'], $data);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function release(
+        JobIdInterface $job,
+        int $priority = PheanstalkInterface::DEFAULT_PRIORITY,
+        int $delay = PheanstalkInterface::DEFAULT_DELAY
+    ): void {
+        $this->dispatch(
+            new Command\ReleaseCommand($job, $priority, $delay)
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function reserve(): Job
+    {
+        $response = $this->dispatch(
+            new Command\ReserveCommand()
+        );
+
+        return new Job($response['id'], $response['jobdata']);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function reserveWithTimeout(int $timeout): ?Job
+    {
+        $response = $this->dispatch(
+            new Command\ReserveWithTimeoutCommand($timeout)
+        );
+
+        if ($response->getResponseName() === ResponseInterface::RESPONSE_DEADLINE_SOON) {
+            throw new DeadlineSoonException();
+        }
+
+        if ($response->getResponseName() === ResponseInterface::RESPONSE_TIMED_OUT) {
+            return null;
+        }
+
+        return new Job($response['id'], $response['jobdata']);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function statsJob(JobIdInterface $job): ResponseInterface
+    {
+        return $this->dispatch(new Command\StatsJobCommand($job));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function statsTube(string $tube): ResponseInterface
+    {
+        return $this->dispatch(new Command\StatsTubeCommand($tube));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function stats(): ResponseInterface
+    {
+        return $this->dispatch(new Command\StatsCommand());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function touch(JobIdInterface $job): void
+    {
+        $this->dispatch(new Command\TouchCommand($job));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function useTube(string $tube): PheanstalkInterface
+    {
+        if ($this->using !== $tube) {
+            $this->dispatch(new Command\UseCommand($tube));
+            $this->using = $tube;
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function watch(string $tube): PheanstalkInterface
+    {
+        if (!isset($this->watching[$tube])) {
+            $this->dispatch(new Command\WatchCommand($tube));
+            $this->watching[$tube] = true;
+        }
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function watchOnly(string $tube): PheanstalkInterface
+    {
+        $this->watch($tube);
+
+        $ignoreTubes = array_diff_key($this->watching, [$tube => true]);
+        foreach ($ignoreTubes as $ignoreTube => $true) {
+            $this->ignore($ignoreTube);
+        }
+
+        return $this;
+    }
+
+    // ----------------------------------------
+
+    /**
+     * Dispatches the specified command to the connection object.
+     *
+     * If a SocketException occurs, the connection is reset, and the command is
+     * re-attempted once.
+     *
+     * @param CommandInterface $command
+     *
+     * @return ResponseInterface
+     */
+    private function dispatch($command)
+    {
+        try {
+            $response = $this->connection->dispatchCommand($command);
+        } catch (Exception\SocketException $e) {
+            $this->reconnect();
+            $response = $this->connection->dispatchCommand($command);
+        }
+
+        return $response;
+    }
+
+    /**
+     * Creates a new connection object, based on the existing connection object,
+     * and re-establishes the used tube and watchlist.
+     */
+    private function reconnect()
+    {
+        $this->connection->disconnect();
+
+        if ($this->using !== PheanstalkInterface::DEFAULT_TUBE) {
+            $this->dispatch(new Command\UseCommand($this->using));
+        }
+
+        foreach ($this->watching as $tube => $true) {
+            if ($tube != PheanstalkInterface::DEFAULT_TUBE) {
+                unset($this->watching[$tube]);
+                $this->watch($tube);
+            }
+        }
+
+        if (!isset($this->watching[PheanstalkInterface::DEFAULT_TUBE])) {
+            $this->ignore(PheanstalkInterface::DEFAULT_TUBE);
+        }
+    }
+
+    /**
+     * @param string $tube The tube to use during execution
+     * @param \Closure $closure Closure to execute while using the specified tube
+     * @return mixed the return value of the closure.
+     * @internal This is marked as internal since it is not part of a stabilized interface.
+     */
+    public function withUsedTube(string $tube, \Closure $closure)
+    {
+        $used = $this->listTubeUsed();
+        try {
+            $this->useTube($tube);
+            return $closure($this);
+        } finally {
+            $this->useTube($used);
+        }
+    }
+
+    /**
+     * @param string $tube The tube to watch during execution
+     * @param \Closure $closure Closure to execute while using the specified tube
+     * @return mixed the return value of the closure.
+     * @internal This is marked as internal since it is not part of a stabilized interface.
+     */
+    public function withWatchedTube(string $tube, \Closure $closure)
+    {
+        $watched = $this->listTubesWatched();
+        try {
+            $this->watchOnly($tube);
+            return $closure($this);
+        } finally {
+            foreach ($watched as $tube) {
+                $this->watch($tube);
+            }
+            if (!in_array($tube, $watched)) {
+                $this->ignore($tube);
+            }
+        }
+    }
+}

+ 60 - 0
vendor/pda/pheanstalk/src/Response/ArrayResponse.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace Pheanstalk\Response;
+
+use Pheanstalk\Contract\ResponseInterface;
+
+/**
+ * A response with an ArrayObject interface to key => value data.
+ *
+ * @author  Paul Annesley
+ */
+class ArrayResponse extends \ArrayObject implements ResponseInterface
+{
+    private $name;
+
+    /**
+     * @param string $name
+     * @param array  $data
+     */
+    public function __construct(string $name, array $data)
+    {
+        $this->name = $name;
+        parent::__construct($data);
+    }
+
+    public function getResponseName(): string
+    {
+        return $this->name;
+    }
+
+    /**
+     * Object property access to ArrayObject data.
+     */
+    public function __get($property)
+    {
+        $key = $this->transformPropertyName($property);
+
+        return $this[$key] ?? null;
+    }
+
+    /**
+     * Object property access to ArrayObject data.
+     */
+    public function __isset($property)
+    {
+        $key = $this->transformPropertyName($property);
+
+        return isset($this[$key]);
+    }
+
+    // ----------------------------------------
+
+    /**
+     * Tranform underscored property name to hyphenated array key.
+     */
+    private function transformPropertyName(string $propertyName): string
+    {
+        return str_replace('_', '-', $propertyName);
+    }
+}

+ 105 - 0
vendor/pda/pheanstalk/src/Socket/FileSocket.php

@@ -0,0 +1,105 @@
+<?php
+
+
+namespace Pheanstalk\Socket;
+
+use Pheanstalk\Contract\SocketInterface;
+use Pheanstalk\Exception\SocketException;
+
+/**
+ * A Socket implementation using the standard file functions.
+ */
+abstract class FileSocket implements SocketInterface
+{
+    /** @var ?resource */
+    protected $socket;
+
+    /**
+     * Writes data to the socket.
+     *
+     * @param string $data
+     *
+     * @return void
+     */
+    public function write(string $data): void
+    {
+        $this->checkClosed();
+        $retries = 0;
+        error_clear_last();
+        while (!empty($data) && $retries < 10) {
+            $written = fwrite($this->socket, $data);
+
+            if ($written === false) {
+                $this->throwException();
+            } elseif ($written === 0) {
+                $retries++;
+                continue;
+            }
+            $data = substr($data, $written);
+        }
+
+        if (!empty($data)) {
+            throw new SocketException('Write failed');
+        }
+    }
+
+    private function throwException()
+    {
+        if (null === $error = error_get_last()) {
+            throw new SocketException('Unknown error');
+        }
+        throw new SocketException($error['message'], $error['type']);
+    }
+
+    private function checkClosed()
+    {
+        if (!isset($this->socket)) {
+            throw new SocketException('The connection was closed');
+        }
+    }
+
+    /**
+     * Reads up to $length bytes from the socket.
+     *
+     * @return string
+     */
+    public function read(int $length): string
+    {
+        $this->checkClosed();
+        $buffer = '';
+        while (mb_strlen($buffer, '8BIT') < $length) {
+            $result = fread($this->socket, $length - mb_strlen($buffer, '8BIT'));
+            if ($result === false) {
+                $this->throwException();
+            }
+            $buffer .= $result;
+        }
+        return $buffer;
+    }
+
+    /**
+     * Reads up to the next new-line.
+     * Trailing whitespace is trimmed.
+     *
+     * @param int
+     */
+    public function getLine(): string
+    {
+        $this->checkClosed();
+        $result = fgets($this->socket, 8192);
+        if ($result === false) {
+            $this->throwException();
+        }
+        return rtrim($result);
+    }
+
+    /**
+     * Disconnect the socket; subsequent usage of the socket will fail.
+     */
+    public function disconnect(): void
+    {
+        $this->checkClosed();
+        fclose($this->socket);
+        $this->socket = null;
+    }
+}

+ 28 - 0
vendor/pda/pheanstalk/src/Socket/FsockopenSocket.php

@@ -0,0 +1,28 @@
+<?php
+
+
+namespace Pheanstalk\Socket;
+
+use Pheanstalk\Exception\ConnectionException;
+
+/**
+ * A Socket implementation using the fsockopen
+ */
+class FsockopenSocket extends FileSocket
+{
+    public function __construct(
+        string $host,
+        int $port,
+        int $connectTimeout
+    ) {
+
+        if (!function_exists('fsockopen')) {
+            throw new \Exception('Fsockopen not found');
+        }
+
+        $this->socket = @fsockopen($host, $port, $error, $errorMessage, $connectTimeout);
+        if ($this->socket === false) {
+            throw new ConnectionException($error, $errorMessage);
+        }
+    }
+}

+ 140 - 0
vendor/pda/pheanstalk/src/Socket/SocketSocket.php

@@ -0,0 +1,140 @@
+<?php
+
+
+namespace Pheanstalk\Socket;
+
+use Pheanstalk\Contract\SocketInterface;
+use Pheanstalk\Exception\ConnectionException;
+use Pheanstalk\Exception\SocketException;
+
+/**
+ * A Socket implementation using the Sockets extension
+ */
+class SocketSocket implements SocketInterface
+{
+    /** @var resource */
+    private $socket;
+
+    public function __construct(
+        string $host,
+        int $port,
+        int $connectTimeout
+    ) {
+        if (!extension_loaded('sockets')) {
+            throw new \Exception('Sockets extension not found');
+        }
+
+        $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+        if ($this->socket === false) {
+            $this->throwException();
+        }
+
+        $timeout = [
+            'sec' => $connectTimeout,
+            'usec' => 0
+        ];
+
+        $sendTimeout = socket_get_option($this->socket, SOL_SOCKET, SO_SNDTIMEO);
+        $receiveTimeout = socket_get_option($this->socket, SOL_SOCKET, SO_RCVTIMEO);
+        socket_set_option($this->socket, SOL_SOCKET, SO_KEEPALIVE, 1);
+        socket_set_option($this->socket, SOL_SOCKET, SO_SNDTIMEO, $timeout);
+        socket_set_option($this->socket, SOL_SOCKET, SO_RCVTIMEO, $timeout);
+        if (socket_set_block($this->socket) === false) {
+            throw new ConnectionException(0, "Failed to set socket to blocking mode");
+        }
+
+        $addresses = gethostbynamel($host);
+        if ($addresses === false) {
+            throw new ConnectionException(0, "Could not resolve hostname $host");
+        }
+        if (@socket_connect($this->socket, $addresses[0], $port) === false) {
+            $error = socket_last_error($this->socket);
+            throw new ConnectionException($error, socket_strerror($error));
+        };
+
+        socket_set_option($this->socket, SOL_SOCKET, SO_SNDTIMEO, $sendTimeout);
+        socket_set_option($this->socket, SOL_SOCKET, SO_RCVTIMEO, $receiveTimeout);
+    }
+
+    /**
+     * Writes data to the socket.
+     *
+     * @param string $data
+     *
+     * @return void
+     */
+    public function write(string $data): void
+    {
+        $this->checkClosed();
+        while (!empty($data)) {
+            $written = socket_write($this->socket, $data);
+            if ($written === false) {
+                $this->throwException();
+            }
+            $data = substr($data, $written);
+        }
+    }
+
+    private function throwException()
+    {
+        $error = socket_last_error($this->socket);
+        throw new SocketException(socket_strerror($error), $error);
+    }
+
+    private function checkClosed()
+    {
+        if (!isset($this->socket)) {
+            throw new SocketException('The connection was closed');
+        }
+    }
+
+    /**
+     * Reads up to $length bytes from the socket.
+     *
+     * @return string
+     */
+    public function read(int $length): string
+    {
+        $this->checkClosed();
+
+        $buffer = '';
+        while (mb_strlen($buffer, '8BIT') < $length) {
+            $result = socket_read($this->socket, $length - mb_strlen($buffer, '8BIT'));
+            if ($result === false) {
+                $this->throwException();
+            }
+            $buffer .= $result;
+        }
+
+        return $buffer;
+    }
+
+    public function getLine(): string
+    {
+        $this->checkClosed();
+
+        $buffer = '';
+        // Reading stops at \r or \n. In case it stopped at \r we must continue reading.
+        while (substr($buffer, -1, 1) !== "\n") {
+            $result = socket_read($this->socket, 1024, PHP_NORMAL_READ);
+            if ($result === false) {
+                $this->throwException();
+            }
+            $buffer .= $result;
+        }
+
+
+
+        return rtrim($buffer);
+    }
+
+    /**
+     * Disconnect the socket; subsequent usage of the socket will fail.
+     */
+    public function disconnect(): void
+    {
+        $this->checkClosed();
+        socket_close($this->socket);
+        unset($this->socket);
+    }
+}

+ 30 - 0
vendor/pda/pheanstalk/src/Socket/StreamSocket.php

@@ -0,0 +1,30 @@
+<?php
+
+
+namespace Pheanstalk\Socket;
+
+use Pheanstalk\Exception\ConnectionException;
+
+/**
+ * A Socket implementation using the Streams extension
+ */
+class StreamSocket extends FileSocket
+{
+    public function __construct(
+        string $host,
+        int $port,
+        int $connectTimeout
+    ) {
+        $addresses = gethostbynamel($host);
+        if ($addresses === false) {
+            throw new ConnectionException(0, "Could not resolve hostname $host");
+        }
+        $target = "tcp://{$addresses[0]}:$port";
+
+        $context = stream_context_create();
+        $this->socket = @stream_socket_client($target, $error, $errorMessage, $connectTimeout, STREAM_CLIENT_CONNECT, $context);
+        if ($this->socket === false) {
+            throw new ConnectionException($errorMessage, $error);
+        }
+    }
+}

+ 57 - 0
vendor/pda/pheanstalk/src/Socket/WriteHistory.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace Pheanstalk\Socket;
+
+/**
+ * A limited history of recent socket write length/success.
+ *
+ * Facilitates retrying zero-length writes a limited number of times,
+ * avoiding infinite loops.
+ *
+ * Based on a patch from https://github.com/leprechaun
+ * https://github.com/pda/pheanstalk/pull/24
+ *
+ * A bitfield could be used instead of an array for efficiency.
+ *
+ * @author  Paul Annesley
+ */
+class WriteHistory
+{
+    private $limit;
+    private $data = [];
+
+    public function __construct(int $limit)
+    {
+        $this->limit = $limit;
+    }
+
+    /**
+     * Whether the history has reached its limit of entries.
+     */
+    public function isFull(): bool
+    {
+        return count($this->data) >= $this->limit;
+    }
+
+    public function hasWrites(): bool
+    {
+        return (bool) array_sum($this->data);
+    }
+
+    public function isFullWithNoWrites(): bool
+    {
+        return $this->isFull() && !$this->hasWrites();
+    }
+
+    /**
+     * Logs the return value from a write call.
+     */
+    public function log($write): void
+    {
+        if ($this->isFull()) {
+            array_shift($this->data);
+        }
+
+        $this->data[] = (int) $write;
+    }
+}

+ 94 - 0
vendor/pda/pheanstalk/src/SocketFactory.php

@@ -0,0 +1,94 @@
+<?php
+
+
+namespace Pheanstalk;
+
+use Pheanstalk\Contract\SocketFactoryInterface;
+use Pheanstalk\Contract\SocketInterface;
+use Pheanstalk\Socket\FsockopenSocket;
+use Pheanstalk\Socket\SocketSocket;
+use Pheanstalk\Socket\StreamSocket;
+
+class SocketFactory implements SocketFactoryInterface
+{
+    public const AUTODETECT = 0;
+    public const STREAM = 1;
+    public const SOCKET = 2;
+    public const FSOCKOPEN = 3;
+
+    private $timeout;
+    private $host;
+    private $port;
+    /** @var int */
+    private $implementation;
+
+    public function __construct(string $host, int $port, int $timeout = 10, $implementation = self::AUTODETECT)
+    {
+        $this->host = $host;
+        $this->port = $port;
+        $this->timeout = $timeout;
+        $this->setImplementation($implementation);
+    }
+
+    public function getImplementation(): int
+    {
+        return $this->implementation;
+    }
+
+    public function setImplementation(int $implementation)
+    {
+        if ($implementation === self::AUTODETECT) {
+            // Prefer socket
+            if (extension_loaded('sockets')) {
+                $this->implementation = self::SOCKET;
+                return;
+            }
+
+            // Then fall back to stream
+            if (function_exists('stream_socket_client')) {
+                $this->implementation = self::STREAM;
+                return;
+            }
+
+            // Then fall back to fsockopen
+            if (function_exists('fsockopen')) {
+                $this->implementation = self::FSOCKOPEN;
+            }
+        } else {
+            $this->implementation = $implementation;
+        }
+    }
+
+    private function createStreamSocket(): StreamSocket
+    {
+        return new StreamSocket($this->host, $this->port, $this->timeout);
+    }
+
+    private function createSocketSocket(): SocketSocket
+    {
+        return new SocketSocket($this->host, $this->port, $this->timeout);
+    }
+
+    private function createFsockopenSocket(): FsockopenSocket
+    {
+        return new FsockopenSocket($this->host, $this->port, $this->timeout);
+    }
+
+    /**
+     * This function must return a connected socket that is ready for reading / writing.
+     * @return SocketInterface
+     */
+    public function create(): SocketInterface
+    {
+        switch ($this->implementation) {
+            case self::SOCKET:
+                return $this->createSocketSocket();
+            case self::STREAM:
+                return $this->createStreamSocket();
+            case self::FSOCKOPEN:
+                return $this->createFsockopenSocket();
+            default:
+                throw new \RuntimeException("Unknown implementation");
+        }
+    }
+}

+ 82 - 0
vendor/pda/pheanstalk/src/YamlResponseParser.php

@@ -0,0 +1,82 @@
+<?php
+
+namespace Pheanstalk;
+
+use Pheanstalk\Contract\ResponseInterface;
+use Pheanstalk\Contract\ResponseParserInterface;
+use Pheanstalk\Exception\ClientException;
+use Pheanstalk\Response\ArrayResponse;
+
+/**
+ * A response parser for commands that return a subset of YAML.
+ *
+ * Expected response is 'OK', 'NOT_FOUND' response is also handled.
+ * Parser expects either a YAML list or dictionary, depending on mode.
+ *
+ * @author  Paul Annesley
+ */
+class YamlResponseParser implements ResponseParserInterface
+{
+    const MODE_LIST = 'list';
+    const MODE_DICT = 'dict';
+
+    private $mode;
+
+    /**
+     * @param string $mode self::MODE_*
+     */
+    public function __construct(string $mode)
+    {
+        if (!in_array($mode, [self::MODE_DICT, self::MODE_LIST])) {
+            throw new \InvalidArgumentException('Invalid mode');
+        }
+        $this->mode = $mode;
+    }
+
+    public function parseResponse(string $responseLine, ?string $responseData): ArrayResponse
+    {
+        if ($responseLine === ResponseInterface::RESPONSE_NOT_FOUND) {
+            throw new Exception\ServerException(sprintf(
+                'Server reported %s',
+                $responseLine
+            ));
+        }
+
+        if (!preg_match('#^OK \d+$#', $responseLine)) {
+            throw new Exception\ServerException(sprintf(
+                'Unhandled response: "%s"',
+                $responseLine
+            ));
+        }
+
+        $lines = array_filter(explode("\n", $responseData), function ($line) {
+            return !empty($line) && $line !== '---';
+        });
+
+        return $this->mode === self::MODE_LIST ? $this->parseList($lines) : $this->parseDictionary($lines);
+    }
+
+    private function parseList(array $lines): ArrayResponse
+    {
+        $data = [];
+        foreach ($lines as $line) {
+            if (strncmp($line, '- ', 2) !== 0) {
+                throw new ClientException("YAML parse error for line: $line" . print_r($lines, true));
+            }
+            $data[] = substr($line, 2);
+        }
+
+        return new ArrayResponse('OK', $data);
+    }
+    private function parseDictionary(array $lines): ArrayResponse
+    {
+        $data = [];
+        foreach ($lines as $line) {
+            if (!preg_match('#(\S+):\s*(.*)#', $line, $matches)) {
+                throw new ClientException("YAML parse error for line: $line");
+            }
+            $data[$matches[1]] = $matches[2];
+        }
+        return new ArrayResponse('OK', $data);
+    }
+}

+ 1 - 1
vendor/services.php

@@ -1,5 +1,5 @@
 <?php 
-// This file is automatically generated at:2021-06-04 20:40:56
+// This file is automatically generated at:2021-07-01 12:32:27
 declare (strict_types = 1);
 return array (
   0 => 'think\\captcha\\CaptchaService',