1<?php
2/**
3 * This file is part of the FreeDSx LDAP package.
4 *
5 * (c) Chad Sikorra <Chad.Sikorra@gmail.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11namespace FreeDSx\Ldap\Server\ServerRunner;
12
13use FreeDSx\Ldap\Exception\RuntimeException;
14use FreeDSx\Ldap\Protocol\Queue\ServerQueue;
15use FreeDSx\Ldap\Protocol\ServerProtocolHandler;
16use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface;
17use FreeDSx\Socket\SocketServer;
18
19/**
20 * Uses PNCTL to fork incoming requests and send them to the server protocol handler.
21 *
22 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
23 */
24class PcntlServerRunner implements ServerRunnerInterface
25{
26    /**
27     * @var SocketServer
28     */
29    protected $server;
30
31    /**
32     * @var array
33     */
34    protected $options;
35
36    /**
37     * @param array $options
38     */
39    public function __construct(array $options = [])
40    {
41        if (!extension_loaded('pcntl')) {
42            throw new RuntimeException('The PCNTL extension is needed to fork incoming requests, which is only available on Linux.');
43        }
44        $this->options = $options;
45    }
46
47    /**
48     * {@inheritdoc}
49     */
50    public function run(SocketServer $server): void
51    {
52        $this->server = $server;
53
54        while ($socket = $this->server->accept()) {
55            $pid = pcntl_fork();
56            if ($pid == -1) {
57                throw new RuntimeException('Unable to fork process.');
58            } elseif ($pid === 0) {
59                $serverProtocolHandler = new ServerProtocolHandler(
60                    new ServerQueue($socket),
61                    $this->constructRequestHandler(),
62                    $this->options
63                );
64                $serverProtocolHandler->handle();
65                $this->server->removeClient($socket);
66                exit;
67            }
68        }
69    }
70
71    /**
72     * Try to instantiate the user supplied request handler.
73     */
74    protected function constructRequestHandler(): RequestHandlerInterface
75    {
76        try {
77            return new $this->options['request_handler']();
78        } catch (\Throwable $e) {
79            throw new RuntimeException(sprintf(
80                'Unable to instantiate the request handler: "%s"',
81                $e->getMessage()
82            ), $e->getCode(), $e);
83        }
84    }
85}
86