1 | <?php |
||
38 | class Telnet extends \Thread implements ConsoleInterface |
||
39 | { |
||
40 | |||
41 | /** |
||
42 | * The configuration parameter name for the port to listen to. |
||
43 | * |
||
44 | * @var string |
||
45 | */ |
||
46 | const PARAM_PORT = 'port'; |
||
47 | |||
48 | /** |
||
49 | * The configuration parameter name for the IP address to listen to. |
||
50 | * |
||
51 | * @var string |
||
52 | */ |
||
53 | const PARAM_ADDRESS = 'address'; |
||
54 | |||
55 | /** |
||
56 | * appserver.io written in ASCII art. |
||
57 | * |
||
58 | * @var string |
||
59 | */ |
||
60 | protected static $logo = ' _ |
||
61 | ____ _____ ____ ________ ______ _____ _____(_)___ |
||
62 | / __ `/ __ \/ __ \/ ___/ _ \/ ___/ | / / _ \/ ___/ / __ \ |
||
63 | / /_/ / /_/ / /_/ (__ ) __/ / | |/ / __/ / / / /_/ / |
||
64 | \__,_/ .___/ .___/____/\___/_/ |___/\___/_(_)/_/\____/ |
||
65 | /_/ /_/ |
||
66 | |||
67 | '; |
||
68 | |||
69 | /** |
||
70 | * Initialize and start the management console. |
||
71 | * |
||
72 | * @param \AppserverIo\Psr\ApplicationServer\ApplicationServerInterface $applicationServer The reference to the server |
||
73 | * @param \AppserverIo\Psr\Cli\Configuration\ConsoleConfigurationInterface $consoleNode The console configuration |
||
74 | * |
||
75 | * @return void |
||
76 | */ |
||
77 | public function __construct(ApplicationServerInterface $applicationServer, ConsoleConfigurationInterface $consoleNode) |
||
78 | { |
||
79 | $this->applicationServer = $applicationServer; |
||
80 | $this->consoleNode = $consoleNode; |
||
81 | $this->start(PTHREADS_INHERIT_ALL); |
||
82 | } |
||
83 | |||
84 | /** |
||
85 | * Return's the console name. |
||
86 | * |
||
87 | * @return string The console name |
||
88 | */ |
||
89 | public function getName() |
||
90 | { |
||
91 | return $this->consoleNode->getName(); |
||
92 | } |
||
93 | |||
94 | /** |
||
95 | * Returns the port to listen to. |
||
96 | * |
||
97 | * @return integer The port to listen to |
||
98 | */ |
||
99 | protected function getPort() |
||
100 | { |
||
101 | return $this->consoleNode->getParam(Telnet::PARAM_PORT); |
||
102 | } |
||
103 | |||
104 | /** |
||
105 | * Returns the IP address to listen to. |
||
106 | * |
||
107 | * @return integer The IP address to listen to |
||
108 | */ |
||
109 | protected function getAddress() |
||
110 | { |
||
111 | return $this->consoleNode->getParam(Telnet::PARAM_ADDRESS); |
||
112 | } |
||
113 | |||
114 | /** |
||
115 | * Shutdown handler that checks for fatal/user errors. |
||
116 | * |
||
117 | * @return void |
||
118 | */ |
||
119 | public function shutdown() |
||
120 | { |
||
121 | // check if there was a fatal error caused shutdown |
||
122 | if ($lastError = error_get_last()) { |
||
123 | // initialize type + message |
||
124 | $type = 0; |
||
125 | $message = ''; |
||
126 | // extract the last error values |
||
127 | extract($lastError); |
||
128 | // query whether we've a fatal/user error |
||
129 | if ($type === E_ERROR || $type === E_USER_ERROR) { |
||
130 | echo $message . PHP_EOL; |
||
131 | } |
||
132 | } |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Stop the console and closes all connections. |
||
137 | * |
||
138 | * @return void |
||
139 | */ |
||
140 | public function stop() |
||
141 | { |
||
142 | $this->kill(); |
||
143 | } |
||
144 | |||
145 | /** |
||
146 | * The thread's run() method that runs asynchronously. |
||
147 | * |
||
148 | * @return void |
||
149 | * @link http://www.php.net/manual/en/thread.run.php |
||
150 | */ |
||
151 | public function run() |
||
152 | { |
||
153 | |||
154 | // register a shutdown handler for controlled shutdown |
||
155 | register_shutdown_function(array(&$this, 'shutdown')); |
||
156 | |||
157 | // we need the autloader again |
||
158 | require SERVER_AUTOLOADER; |
||
159 | |||
160 | // create a reference to the application server instance |
||
161 | $applicationServer = $this->applicationServer; |
||
162 | |||
163 | // initialize the event loop and the socket server |
||
164 | $loop = \React\EventLoop\Factory::create(); |
||
165 | $socket = new \React\Socket\Server($loop); |
||
166 | |||
167 | // wait for connections |
||
168 | $socket->on('connection', function ($conn) use ($applicationServer) { |
||
169 | // wait for user input => usually a command |
||
170 | $conn->on('data', function ($data) use ($conn, $applicationServer) { |
||
171 | try { |
||
172 | // extract command name and parameters |
||
173 | $params = Arguments::split($data); |
||
174 | $commandName = array_shift($params); |
||
175 | |||
176 | try { |
||
177 | // initialize and execute the command |
||
178 | $command = CommandFactory::factory($commandName, array($conn, $applicationServer)); |
||
179 | $command->execute($params); |
||
180 | |||
|
|||
181 | } catch (\ReflectionException $re) { |
||
182 | $conn->write(sprintf("Unknown command %sERROR\n", $commandName)); |
||
183 | } |
||
184 | |||
185 | } catch (\Exception $e) { |
||
186 | $conn->write("{$e->__toString()}ERROR\n"); |
||
187 | } |
||
188 | }); |
||
189 | }); |
||
190 | |||
191 | // listen to the management socket |
||
192 | $socket->listen($this->getPort(), $this->getAddress()); |
||
193 | |||
194 | // start the event loop and the socket server, but disable warnings as some React warnings cannot (or won't) be dealt with. |
||
195 | // Specifically the warning if a client disconnects unexpectedly or does not even connect to begin with ("Interrupted system call") is unevitable |
||
196 | // @see https://github.com/reactphp/react/pull/297 |
||
197 | // @see https://github.com/reactphp/react/issues/296 |
||
198 | // @see http://php.net/manual/de/function.stream-select.php |
||
199 | $currentReportingLevel = error_reporting(); |
||
200 | error_reporting(E_ALL ^ E_WARNING); |
||
201 | $loop->run(); |
||
202 | error_reporting($currentReportingLevel); |
||
203 | } |
||
204 | } |
||
205 |