Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?php
2
 
3
namespace React\Socket;
4
 
5
use Evenement\EventEmitter;
6
use React\EventLoop\LoopInterface;
7
use InvalidArgumentException;
8
use RuntimeException;
9
 
10
/**
11
 * The `UnixServer` class implements the `ServerInterface` and
12
 * is responsible for accepting plaintext connections on unix domain sockets.
13
 *
14
 * ```php
15
 * $server = new React\Socket\UnixServer('unix:///tmp/app.sock', $loop);
16
 * ```
17
 *
18
 * See also the `ServerInterface` for more details.
19
 *
20
 * @see ServerInterface
21
 * @see ConnectionInterface
22
 */
23
final class UnixServer extends EventEmitter implements ServerInterface
24
{
25
    private $master;
26
    private $loop;
27
    private $listening = false;
28
 
29
    /**
30
     * Creates a plaintext socket server and starts listening on the given unix socket
31
     *
32
     * This starts accepting new incoming connections on the given address.
33
     * See also the `connection event` documented in the `ServerInterface`
34
     * for more details.
35
     *
36
     * ```php
37
     * $server = new React\Socket\UnixServer('unix:///tmp/app.sock', $loop);
38
     * ```
39
     *
40
     * @param string        $path
41
     * @param LoopInterface $loop
42
     * @param array         $context
43
     * @throws InvalidArgumentException if the listening address is invalid
44
     * @throws RuntimeException if listening on this address fails (already in use etc.)
45
     */
46
    public function __construct($path, LoopInterface $loop, array $context = array())
47
    {
48
        $this->loop = $loop;
49
 
50
        if (\strpos($path, '://') === false) {
51
            $path = 'unix://' . $path;
52
        } elseif (\substr($path, 0, 7) !== 'unix://') {
53
            throw new \InvalidArgumentException('Given URI "' . $path . '" is invalid');
54
        }
55
 
56
        $this->master = @\stream_socket_server(
57
            $path,
58
            $errno,
59
            $errstr,
60
            \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN,
61
            \stream_context_create(array('socket' => $context))
62
        );
63
        if (false === $this->master) {
64
            // PHP does not seem to report errno/errstr for Unix domain sockets (UDS) right now.
65
            // This only applies to UDS server sockets, see also https://3v4l.org/NAhpr.
66
            // Parse PHP warning message containing unknown error, HHVM reports proper info at least.
67
            if ($errno === 0 && $errstr === '') {
68
                $error = \error_get_last();
69
                if (\preg_match('/\(([^\)]+)\)|\[(\d+)\]: (.*)/', $error['message'], $match)) {
70
                    $errstr = isset($match[3]) ? $match['3'] : $match[1];
71
                    $errno = isset($match[2]) ? (int)$match[2] : 0;
72
                }
73
            }
74
 
75
            throw new \RuntimeException('Failed to listen on Unix domain socket "' . $path . '": ' . $errstr, $errno);
76
        }
77
        \stream_set_blocking($this->master, 0);
78
 
79
        $this->resume();
80
    }
81
 
82
    public function getAddress()
83
    {
84
        if (!\is_resource($this->master)) {
85
            return null;
86
        }
87
 
88
        return 'unix://' . \stream_socket_get_name($this->master, false);
89
    }
90
 
91
    public function pause()
92
    {
93
        if (!$this->listening) {
94
            return;
95
        }
96
 
97
        $this->loop->removeReadStream($this->master);
98
        $this->listening = false;
99
    }
100
 
101
    public function resume()
102
    {
103
        if ($this->listening || !is_resource($this->master)) {
104
            return;
105
        }
106
 
107
        $that = $this;
108
        $this->loop->addReadStream($this->master, function ($master) use ($that) {
109
            $newSocket = @\stream_socket_accept($master, 0);
110
            if (false === $newSocket) {
111
                $that->emit('error', array(new \RuntimeException('Error accepting new connection')));
112
 
113
                return;
114
            }
115
            $that->handleConnection($newSocket);
116
        });
117
        $this->listening = true;
118
    }
119
 
120
    public function close()
121
    {
122
        if (!\is_resource($this->master)) {
123
            return;
124
        }
125
 
126
        $this->pause();
127
        \fclose($this->master);
128
        $this->removeAllListeners();
129
    }
130
 
131
    /** @internal */
132
    public function handleConnection($socket)
133
    {
134
        $connection = new Connection($socket, $this->loop);
135
        $connection->unix = true;
136
 
137
        $this->emit('connection', array(
138
            $connection
139
        ));
140
    }
141
}