friendship ended with social-app. php is my new best friend
1<?php 2 3namespace React\Socket; 4 5use Evenement\EventEmitter; 6use Exception; 7use OverflowException; 8 9/** 10 * The `LimitingServer` decorator wraps a given `ServerInterface` and is responsible 11 * for limiting and keeping track of open connections to this server instance. 12 * 13 * Whenever the underlying server emits a `connection` event, it will check its 14 * limits and then either 15 * - keep track of this connection by adding it to the list of 16 * open connections and then forward the `connection` event 17 * - or reject (close) the connection when its limits are exceeded and will 18 * forward an `error` event instead. 19 * 20 * Whenever a connection closes, it will remove this connection from the list of 21 * open connections. 22 * 23 * ```php 24 * $server = new React\Socket\LimitingServer($server, 100); 25 * $server->on('connection', function (React\Socket\ConnectionInterface $connection) { 26 * $connection->write('hello there!' . PHP_EOL); 27 * … 28 * }); 29 * ``` 30 * 31 * See also the `ServerInterface` for more details. 32 * 33 * @see ServerInterface 34 * @see ConnectionInterface 35 */ 36class LimitingServer extends EventEmitter implements ServerInterface 37{ 38 private $connections = array(); 39 private $server; 40 private $limit; 41 42 private $pauseOnLimit = false; 43 private $autoPaused = false; 44 private $manuPaused = false; 45 46 /** 47 * Instantiates a new LimitingServer. 48 * 49 * You have to pass a maximum number of open connections to ensure 50 * the server will automatically reject (close) connections once this limit 51 * is exceeded. In this case, it will emit an `error` event to inform about 52 * this and no `connection` event will be emitted. 53 * 54 * ```php 55 * $server = new React\Socket\LimitingServer($server, 100); 56 * $server->on('connection', function (React\Socket\ConnectionInterface $connection) { 57 * $connection->write('hello there!' . PHP_EOL); 58 * … 59 * }); 60 * ``` 61 * 62 * You MAY pass a `null` limit in order to put no limit on the number of 63 * open connections and keep accepting new connection until you run out of 64 * operating system resources (such as open file handles). This may be 65 * useful if you do not want to take care of applying a limit but still want 66 * to use the `getConnections()` method. 67 * 68 * You can optionally configure the server to pause accepting new 69 * connections once the connection limit is reached. In this case, it will 70 * pause the underlying server and no longer process any new connections at 71 * all, thus also no longer closing any excessive connections. 72 * The underlying operating system is responsible for keeping a backlog of 73 * pending connections until its limit is reached, at which point it will 74 * start rejecting further connections. 75 * Once the server is below the connection limit, it will continue consuming 76 * connections from the backlog and will process any outstanding data on 77 * each connection. 78 * This mode may be useful for some protocols that are designed to wait for 79 * a response message (such as HTTP), but may be less useful for other 80 * protocols that demand immediate responses (such as a "welcome" message in 81 * an interactive chat). 82 * 83 * ```php 84 * $server = new React\Socket\LimitingServer($server, 100, true); 85 * $server->on('connection', function (React\Socket\ConnectionInterface $connection) { 86 * $connection->write('hello there!' . PHP_EOL); 87 * … 88 * }); 89 * ``` 90 * 91 * @param ServerInterface $server 92 * @param int|null $connectionLimit 93 * @param bool $pauseOnLimit 94 */ 95 public function __construct(ServerInterface $server, $connectionLimit, $pauseOnLimit = false) 96 { 97 $this->server = $server; 98 $this->limit = $connectionLimit; 99 if ($connectionLimit !== null) { 100 $this->pauseOnLimit = $pauseOnLimit; 101 } 102 103 $this->server->on('connection', array($this, 'handleConnection')); 104 $this->server->on('error', array($this, 'handleError')); 105 } 106 107 /** 108 * Returns an array with all currently active connections 109 * 110 * ```php 111 * foreach ($server->getConnection() as $connection) { 112 * $connection->write('Hi!'); 113 * } 114 * ``` 115 * 116 * @return ConnectionInterface[] 117 */ 118 public function getConnections() 119 { 120 return $this->connections; 121 } 122 123 public function getAddress() 124 { 125 return $this->server->getAddress(); 126 } 127 128 public function pause() 129 { 130 if (!$this->manuPaused) { 131 $this->manuPaused = true; 132 133 if (!$this->autoPaused) { 134 $this->server->pause(); 135 } 136 } 137 } 138 139 public function resume() 140 { 141 if ($this->manuPaused) { 142 $this->manuPaused = false; 143 144 if (!$this->autoPaused) { 145 $this->server->resume(); 146 } 147 } 148 } 149 150 public function close() 151 { 152 $this->server->close(); 153 } 154 155 /** @internal */ 156 public function handleConnection(ConnectionInterface $connection) 157 { 158 // close connection if limit exceeded 159 if ($this->limit !== null && \count($this->connections) >= $this->limit) { 160 $this->handleError(new \OverflowException('Connection closed because server reached connection limit')); 161 $connection->close(); 162 return; 163 } 164 165 $this->connections[] = $connection; 166 $that = $this; 167 $connection->on('close', function () use ($that, $connection) { 168 $that->handleDisconnection($connection); 169 }); 170 171 // pause accepting new connections if limit exceeded 172 if ($this->pauseOnLimit && !$this->autoPaused && \count($this->connections) >= $this->limit) { 173 $this->autoPaused = true; 174 175 if (!$this->manuPaused) { 176 $this->server->pause(); 177 } 178 } 179 180 $this->emit('connection', array($connection)); 181 } 182 183 /** @internal */ 184 public function handleDisconnection(ConnectionInterface $connection) 185 { 186 unset($this->connections[\array_search($connection, $this->connections)]); 187 188 // continue accepting new connection if below limit 189 if ($this->autoPaused && \count($this->connections) < $this->limit) { 190 $this->autoPaused = false; 191 192 if (!$this->manuPaused) { 193 $this->server->resume(); 194 } 195 } 196 } 197 198 /** @internal */ 199 public function handleError(\Exception $error) 200 { 201 $this->emit('error', array($error)); 202 } 203}