private function connect() { // If we're in the process of connecting already return that same promise if ($this->connectPromisor) { return $this->connectPromisor->promise(); } // If a read watcher exists we know we're already connected if ($this->readWatcher) { return new Success($this); } $this->state = self::STATE_CONNECTING; $this->connectPromisor = new Deferred(); $socketPromise = connect($this->uri, ["timeout" => $this->timeout]); $onWrite = function ($watcherId) { if ($this->outputBufferLength === 0) { disable($watcherId); return; } $bytes = @fwrite($this->socket, $this->outputBuffer); if ($bytes === 0) { $this->state = self::STATE_DISCONNECTED; $this->onError(new ConnectException("Connection went away (write)", $code = 1)); } else { $this->outputBuffer = (string) substr($this->outputBuffer, $bytes); $this->outputBufferLength -= $bytes; } }; $socketPromise->when(function ($error, $socket) use($onWrite) { $connectPromisor = $this->connectPromisor; $this->connectPromisor = null; if ($error) { $this->state = self::STATE_DISCONNECTED; $connectPromisor->fail(new ConnectException("Connection attempt failed", $code = 0, $error)); return; } $this->state = self::STATE_CONNECTED; $this->socket = $socket; foreach ($this->handlers["connect"] as $handler) { $pipelinedCommand = $handler(); if (!empty($pipelinedCommand)) { $this->outputBuffer = $pipelinedCommand . $this->outputBuffer; $this->outputBufferLength += strlen($pipelinedCommand); } } $this->readWatcher = onReadable($this->socket, function () { $read = fread($this->socket, 8192); if ($read != "") { $this->parser->append($read); } elseif (!is_resource($this->socket) || @feof($this->socket)) { $this->state = self::STATE_DISCONNECTED; $this->onError(new ConnectException("Connection went away (read)", $code = 2)); } }); $this->writeWatcher = onWritable($this->socket, $onWrite, ["enable" => !empty($this->outputBuffer)]); $connectPromisor->succeed(); }); return $this->connectPromisor->promise(); }
/** * @return Promise */ private function connect() { if ($this->promisor) { return $this->promisor->promise(); } // Already connected if (is_resource($this->socket)) { return new Success(); } $this->promisor = new \Amp\Deferred(); /** @var $socketPromise Promise */ $socketPromise = \Amp\Socket\connect($this->uri, ['timeout' => 1000]); $socketPromise->when(function ($error, $socket) { $promisor = $this->promisor; $this->promisor = null; $this->socket = $socket; $this->reader = \Amp\onReadable($this->socket, [$this, "onRead"]); $this->writer = \Amp\onWritable($this->socket, [$this, "onWrite"]); $promisor->succeed(); }); return $this->promisor->promise(); }
/** * @param string $uri * @param array $options */ public function __construct($uri, array $options = null) { if (is_array($options) || func_num_args() === 2) { trigger_error("Using the options array is deprecated and will be removed in the next version. " . "Please use the URI to pass options like that: tcp://localhost:6379?database=3&password=abc", E_USER_DEPRECATED); $options = $options ?: []; if (isset($options["password"])) { $this->password = $options["password"]; } } $this->applyUri($uri); $this->promisors = []; $this->patternPromisors = []; $this->connection = new Connection($this->uri); $this->connection->addEventHandler("response", function ($response) { if ($this->authPromisor) { if ($response instanceof Exception) { $this->authPromisor->fail($response); } else { $this->authPromisor->succeed($response); } $this->authPromisor = null; return; } switch ($response[0]) { case "message": foreach ($this->promisors[$response[1]] as $promisor) { $promisor->update($response[2]); } break; case "pmessage": foreach ($this->patternPromisors[$response[1]] as $promisor) { $promisor->update([$response[3], $response[2]]); } break; case "unsubscribe": if ($response[1] === null) { break; } foreach ($this->promisors[$response[1]] as $promisor) { $promisor->succeed(); } break; case "punsubscribe": if ($response[1] === null) { break; } foreach ($this->patternPromisors[$response[1]] as $promisor) { $promisor->succeed(); } break; default: break; } }); $this->connection->addEventHandler("error", function ($error) { if ($error) { // Fail any outstanding promises if ($this->authPromisor) { $this->authPromisor->fail($error); } while ($this->promisors) { $promisorGroup = array_shift($this->promisors); while ($promisorGroup) { $promisor = array_shift($promisorGroup); $promisor->fail($error); } } while ($this->patternPromisors) { $promisorGroup = array_shift($this->patternPromisors); while ($promisorGroup) { $promisor = array_shift($promisorGroup); $promisor->fail($error); } } } }); if (!empty($this->password)) { $this->connection->addEventHandler("connect", function () { // AUTH must be before any other command, so we unshift it here $this->authPromisor = new Deferred(); return "*2\r\n\$4\r\rAUTH\r\n\$" . strlen($this->password) . "\r\n{$this->password}\r\n"; }); } }