friendship ended with social-app. php is my new best friend
1<?php 2 3namespace React\Socket; 4 5use React\EventLoop\Loop; 6use React\EventLoop\LoopInterface; 7use React\Promise\Promise; 8 9final class TimeoutConnector implements ConnectorInterface 10{ 11 private $connector; 12 private $timeout; 13 private $loop; 14 15 /** 16 * @param ConnectorInterface $connector 17 * @param float $timeout 18 * @param ?LoopInterface $loop 19 */ 20 public function __construct(ConnectorInterface $connector, $timeout, $loop = null) 21 { 22 if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 23 throw new \InvalidArgumentException('Argument #3 ($loop) expected null|React\EventLoop\LoopInterface'); 24 } 25 26 $this->connector = $connector; 27 $this->timeout = $timeout; 28 $this->loop = $loop ?: Loop::get(); 29 } 30 31 public function connect($uri) 32 { 33 $promise = $this->connector->connect($uri); 34 35 $loop = $this->loop; 36 $time = $this->timeout; 37 return new Promise(function ($resolve, $reject) use ($loop, $time, $promise, $uri) { 38 $timer = null; 39 $promise = $promise->then(function ($v) use (&$timer, $loop, $resolve) { 40 if ($timer) { 41 $loop->cancelTimer($timer); 42 } 43 $timer = false; 44 $resolve($v); 45 }, function ($v) use (&$timer, $loop, $reject) { 46 if ($timer) { 47 $loop->cancelTimer($timer); 48 } 49 $timer = false; 50 $reject($v); 51 }); 52 53 // promise already resolved => no need to start timer 54 if ($timer === false) { 55 return; 56 } 57 58 // start timeout timer which will cancel the pending promise 59 $timer = $loop->addTimer($time, function () use ($time, &$promise, $reject, $uri) { 60 $reject(new \RuntimeException( 61 'Connection to ' . $uri . ' timed out after ' . $time . ' seconds (ETIMEDOUT)', 62 \defined('SOCKET_ETIMEDOUT') ? \SOCKET_ETIMEDOUT : 110 63 )); 64 65 // Cancel pending connection to clean up any underlying resources and references. 66 // Avoid garbage references in call stack by passing pending promise by reference. 67 assert(\method_exists($promise, 'cancel')); 68 $promise->cancel(); 69 $promise = null; 70 }); 71 }, function () use (&$promise) { 72 // Cancelling this promise will cancel the pending connection, thus triggering the rejection logic above. 73 // Avoid garbage references in call stack by passing pending promise by reference. 74 assert(\method_exists($promise, 'cancel')); 75 $promise->cancel(); 76 $promise = null; 77 }); 78 } 79}