UserlandDaemon   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 123
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 13
lcom 1
cbo 8
dl 0
loc 123
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A run() 0 23 4
A processConnectionPool() 0 21 5
A shutdown() 0 14 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PHPFastCGI\FastCGIDaemon\Driver\Userland;
6
7
use PHPFastCGI\FastCGIDaemon\DaemonInterface;
8
use PHPFastCGI\FastCGIDaemon\DaemonOptions;
9
use PHPFastCGI\FastCGIDaemon\DaemonTrait;
10
use PHPFastCGI\FastCGIDaemon\Driver\Userland\Connection\ConnectionPoolInterface;
11
use PHPFastCGI\FastCGIDaemon\Driver\Userland\ConnectionHandler\ConnectionHandler;
12
use PHPFastCGI\FastCGIDaemon\Driver\Userland\ConnectionHandler\ConnectionHandlerFactoryInterface;
13
use PHPFastCGI\FastCGIDaemon\Driver\Userland\Exception\UserlandDaemonException;
14
use PHPFastCGI\FastCGIDaemon\Exception\ShutdownException;
15
use PHPFastCGI\FastCGIDaemon\KernelInterface;
16
17
/**
18
 * The standard implementation of the DaemonInterface is constructed from a
19
 * connection pool and a factory class to generate connection handlers.
20
 */
21
final class UserlandDaemon implements DaemonInterface
22
{
23
    use DaemonTrait;
24
25
    /**
26
     * @var KernelInterface
27
     */
28
    private $kernel;
29
30
    /**
31
     * @var DaemonOptions
32
     */
33
    private $daemonOptions;
34
35
    /**
36
     * @var ConnectionPoolInterface
37
     */
38
    private $connectionPool;
39
40
    /**
41
     * @var ConnectionHandlerFactoryInterface
42
     */
43
    private $connectionHandlerFactory;
44
45
    /**
46
     * @var ConnectionHandler[]
47
     */
48
    private $connectionHandlers;
49
50
    /**
51
     * Constructor.
52
     *
53
     * @param KernelInterface                   $kernel                   The kernel for the daemon to use
54
     * @param DaemonOptions                     $daemonOptions            The daemon configuration
55
     * @param ConnectionPoolInterface           $connectionPool           The connection pool to accept connections from
56
     * @param ConnectionHandlerFactoryInterface $connectionHandlerFactory A factory class for producing connection handlers
57
     */
58
    public function __construct(KernelInterface $kernel, DaemonOptions $daemonOptions, ConnectionPoolInterface $connectionPool, ConnectionHandlerFactoryInterface $connectionHandlerFactory)
59
    {
60
        $this->kernel                   = $kernel;
61
        $this->daemonOptions            = $daemonOptions;
62
        $this->connectionPool           = $connectionPool;
63
        $this->connectionHandlerFactory = $connectionHandlerFactory;
64
65
        $this->connectionHandlers = [];
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71
    public function run(): void
72
    {
73
        $this->setupDaemon($this->daemonOptions);
74
        $this->daemonOptions->getOption(DaemonOptions::LOGGER)->notice('Daemon is running and ready to accept connections');
75
76
        try {
77
            while (1) {
78
                $this->processConnectionPool();
79
80
                $this->checkDaemonLimits();
81
            }
82
        } catch (ShutdownException $exception) {
83
            $this->daemonOptions->getOption(DaemonOptions::LOGGER)->notice($exception->getMessage());
84
85
            $this->shutdown();
86
        } catch (\Exception $exception) {
87
            $this->daemonOptions->getOption(DaemonOptions::LOGGER)->emergency($exception->getMessage());
88
89
            $this->connectionPool->close();
90
91
            throw $exception;
92
        }
93
    }
94
95
    /**
96
     * Wait for connections in the pool to become readable. Create connection
97
     * handlers for new connections and trigger the ready method when there is
98
     * data for the handlers to receive. Clean up closed connections.
99
     *
100
     * @throws \Exception
101
     */
102
    private function processConnectionPool(): void
103
    {
104
        $readableConnections = $this->connectionPool->getReadableConnections(5);
105
106
        foreach ($readableConnections as $id => $connection) {
107
            if (!isset($this->connectionHandlers[$id])) {
108
                $this->connectionHandlers[$id] = $this->connectionHandlerFactory->createConnectionHandler($this->kernel, $connection);
109
            }
110
111
            try {
112
                $statusCodes = $this->connectionHandlers[$id]->ready();
113
                $this->considerStatusCodes($statusCodes);
114
            } catch (UserlandDaemonException $exception) {
115
                $this->daemonOptions->getOption(DaemonOptions::LOGGER)->error($exception->getMessage());
116
            }
117
118
            if ($this->connectionHandlers[$id]->isClosed()) {
119
                unset($this->connectionHandlers[$id]);
120
            }
121
        }
122
    }
123
124
    /**
125
     * Gracefully shutdown the daemon.
126
     *
127
     * @throws \Exception
128
     */
129
    private function shutdown(): void
130
    {
131
        $this->connectionPool->shutdown();
132
133
        foreach ($this->connectionHandlers as $connectionHandler) {
134
            $connectionHandler->shutdown();
135
        }
136
137
        while ($this->connectionPool->count() > 0) {
138
            $this->processConnectionPool();
139
        }
140
141
        $this->connectionPool->close();
142
    }
143
}
144