diff --git a/composer.json b/composer.json index d54cb119..fc1c7cab 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "keywords": ["socket"], "license": "MIT", "require": { - "php": ">=5.3.0", + "php": ">=5.5.0", "evenement/evenement": "~2.0|~1.0", "react/event-loop": "0.4.*|0.3.*", "react/stream": "0.4.*|0.3.*" diff --git a/src/ConnectionFactory.php b/src/ConnectionFactory.php new file mode 100644 index 00000000..909ce72f --- /dev/null +++ b/src/ConnectionFactory.php @@ -0,0 +1,73 @@ +map = $map; + $this->defaultDefinition = $defaultDefinition; + } + + /** + * @inheritdoc + */ + public function createConnection($socket, LoopInterface $loop) + { + if (!is_resource($socket)) { + throw new \InvalidArgumentException("Socket must be of type resource."); + } + + $meta = stream_get_meta_data($socket); + $key = $meta['stream_type']; + $definition = isset($this->map[$key]) ? $this->map[$key] : $this->defaultDefinition; + return $this->constructConnection($definition, $socket, $loop); + + } + + /** + * @param string|Closure $definition A class name or \Closure that constructs the desired class. + * @param $socket A PHP stream resource. + * @param LoopInterface $loop The loop to use as the second argument for the connection constructor / Closure. + * @throws \InvalidArgumentException when the definition cannot be resolved. + * @throws \UnexpectedValueException when the definition doesn't return an instance of ConnectionInterface. + */ + protected function constructConnection($definition, $socket, LoopInterface $loop) + { + if (is_string($definition) && class_exists($definition)) { + $result = new $definition($socket, $loop); + } elseif ($definition instanceof \Closure) { + $result = $definition($socket, $loop); + } else { + throw new \InvalidArgumentException("Definition must either be a class name or a closure."); + } + + if (!$result instanceof ConnectionInterface) { + throw new \UnexpectedValueException("Result must implement ConnectionInterface"); + } + + return $result; + } + +} \ No newline at end of file diff --git a/src/ConnectionFactoryInterface.php b/src/ConnectionFactoryInterface.php new file mode 100644 index 00000000..d5355a31 --- /dev/null +++ b/src/ConnectionFactoryInterface.php @@ -0,0 +1,18 @@ +loop = $loop; + $this->factory = $factory ?: new ConnectionFactory(); } + /** + * @inheritdoc + */ public function listen($port, $host = '127.0.0.1') { if (strpos($host, ':') !== false) { @@ -43,6 +58,12 @@ public function listen($port, $host = '127.0.0.1') }); } + /** + * Handles a new connection by configuring the socket, + * constructing the connection object and emitting the connection event. + * @todo Check if this should be protected instead of public. + * @param resource $socket + */ public function handleConnection($socket) { stream_set_blocking($socket, 0); @@ -66,8 +87,14 @@ public function shutdown() $this->removeAllListeners(); } + /** + * Constructs a connection object given a socket. + * @todo Check if this should be protected instead of public. + * @param resource $socket The PHP stream resource. + * @return ConnectionInterface + */ public function createConnection($socket) { - return new Connection($socket, $this->loop); + return $this->factory->createConnection($socket, $this->loop); } } diff --git a/src/ServerInterface.php b/src/ServerInterface.php index 85ab0ed5..4f61fb73 100644 --- a/src/ServerInterface.php +++ b/src/ServerInterface.php @@ -7,7 +7,20 @@ /** Emits the connection event */ interface ServerInterface extends EventEmitterInterface { + /** + * @param int $port The port number to listen on. + * @param string $host The address to bind on. + * @return void + */ public function listen($port, $host = '127.0.0.1'); + + /** + * @return int + */ public function getPort(); + + /** + * @return void + */ public function shutdown(); } diff --git a/tests/ConnectionFactoryTest.php b/tests/ConnectionFactoryTest.php new file mode 100644 index 00000000..5e202220 --- /dev/null +++ b/tests/ConnectionFactoryTest.php @@ -0,0 +1,35 @@ + ConnectionStub::class, + 'STDIO' => Connection::class, + ]); + + $this->assertInstanceOf(ConnectionInterface::class, $factory->createConnection(fopen('php://memory', 'w+'), $loop)); + $this->assertInstanceOf(ConnectionStub::class, $factory->createConnection(fopen('php://memory', 'w+'), $loop)); + + $this->assertInstanceOf(ConnectionInterface::class, $factory->createConnection(fopen(__FILE__, 'r'), $loop)); + + $this->assertInstanceOf(Connection::class, $factory->createConnection(fopen(__FILE__, 'r'), $loop)); + + } +}