Implement socket interface

This commit is contained in:
Daniil Gentili 2018-03-04 17:42:48 +01:00
parent c218517673
commit e9dc0ba6f6
15 changed files with 234 additions and 33 deletions

View File

@ -55,7 +55,7 @@ if (!is_object($Lua)) {
}
$offset = 0;
while (true) {
$updates = $Lua->MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
$updates = $Lua->MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
foreach ($updates as $update) {
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
$Lua->madeline_update_callback($update['update']);

View File

@ -56,7 +56,7 @@ if (!is_object($Lua)) {
$offset = 0;
while (true) {
$updates = $Lua->MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
$updates = $Lua->MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
foreach ($updates as $update) {
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
$Lua->tdcli_update_callback($update['update']);

View File

@ -107,7 +107,7 @@ if (stripos(readline('Do you want to handle incoming calls? (y/n): '), 'y') !==
$howmany = readline('How many calls would you like me to handle? ');
$offset = 0;
while ($howmany > 0) {
$updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
$updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
foreach ($updates as $update) {
\danog\MadelineProto\Logger::log($update);
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
@ -135,7 +135,7 @@ if (stripos(readline('Do you want to make the secret chat tests? (y/n): '), 'y')
\danog\MadelineProto\Logger::log($sentMessage, \danog\MadelineProto\Logger::NOTICE);
/*
while (true) {
$updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
$updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
//\danog\MadelineProto\Logger::log($updates);
foreach ($updates as $update) {
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id

View File

@ -1,3 +1,6 @@
<?php
require 'vendor/autoload.php';
$handler = new \danog\MadelineProto\Server(['type' => AF_INET, 'protocol' => 0, 'address' => 'localhost', 'port' => 8005]);
$handler->start();

View File

@ -120,13 +120,13 @@ If not, see <http://www.gnu.org/licenses/>.
if (!extension_loaded('pthreads')) {
if (extension_loaded('sockets')) {
class Socket
class SocketBase
{
private $sock;
public function __construct(int $domain, int $type, int $protocol)
public function __construct($sock)
{
$this->sock = socket_create($domain, $type, $protocol);
$this->sock = $sock;
}
public function __destruct()
@ -170,7 +170,11 @@ if (!extension_loaded('pthreads')) {
public function accept()
{
return socket_accept($this->sock);
if ($socket = socket_accept($this->sock)) {
return new SocketBase($socket);
} else {
return $socket;
}
}
public function connect(string $address, int $port = 0)
@ -222,6 +226,13 @@ if (!extension_loaded('pthreads')) {
return $port ? ['host' => $address, 'port' => $port] : ['host' => $address];
}
}
class Socket extends SocketBase
{
public function __construct(int $domain, int $type, int $protocol)
{
parent::__construct(socket_create($domain, $type, $protocol));
}
}
} else {
define('AF_INET', 0);
define('AF_INET6', 1);

View File

@ -22,7 +22,6 @@ class API extends APIFactory
public function __magic_construct($params = [])
{
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
set_exception_handler(['\\danog\\MadelineProto\\Serialization', 'serialize_all']);
if (is_string($params)) {
$realpaths = Serialization::realpaths($params);
if (file_exists($realpaths['file'])) {

View File

@ -236,13 +236,10 @@ class Connection
}
return $wrote;
break;
case 'udp':
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
break;
default:
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_invalid']);
break;
}
}

View File

@ -23,7 +23,9 @@ class Server
public function __construct($settings)
{
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
$this->settings = $settings;
$this->main = getmypid();
}
public function start()
@ -32,15 +34,24 @@ class Server
pcntl_signal(SIGINT, [$this, 'sig_handler']);
pcntl_signal(SIGCHLD, [$this, 'sig_handler']);
$this->sock = new Socket($this->settings['type'], SOCK_STREAM, $this->settings['protocol']);
$this->sock = new \Socket($this->settings['type'], SOCK_STREAM, $this->settings['protocol']);
$this->sock->bind($this->settings['address'], $this->settings['port']);
$this->sock->listen();
$this->sock->setBlocking(true);
$timeout = 2;
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
while (true) {
$this->handle($this->sock->accept());
pcntl_signal_dispatch();
try {
if ($sock = $this->sock->accept()) {
$this->handle($sock);
}
} catch (\danog\MadelineProto\Exception $e) {
}
}
}
private function handle($socket)
{
$pid = pcntl_fork();
@ -49,16 +60,21 @@ class Server
} elseif ($pid) {
return $this->pids[] = $pid;
}
$handler = new \danog\MadelineProto\Server\Handler($socket);
$handler = new \danog\MadelineProto\Server\Handler($socket, 'tcp_abridged', null, null, null, null, null);
$handler->loop();
die;
}
public function __destruct()
{
foreach ($this->pid as $pid) {
pcntl_wait($pid);
if (!\danog\MadelineProto\Logger::$is_fork) {
\danog\MadelineProto\Logger::log('Shutting main process down');
foreach ($this->pids as $pid) {
pcntl_wait($pid);
}
return;
}
\danog\MadelineProto\Logger::log('Shutting fork '.getmypid().' down');
}
public function sig_handler($sig)
@ -66,6 +82,7 @@ class Server
switch ($sig) {
case SIGTERM:
case SIGINT:
Logger::log('Got SIGTERM/SIGINT in '.getmypid());
exit();
case SIGCHLD:

View File

@ -16,24 +16,123 @@ namespace danog\MadelineProto\Server;
/*
* Socket handler for server
*/
class Handler
class Handler extends \danog\MadelineProto\Connection
{
private $socket;
use \danog\MadelineProto\TL\TL;
use \danog\MadelineProto\Tools;
private $madeline;
public function __construct($socket)
public function __magic_construct($socket, $extra, $ip, $port, $protocol, $timeout, $ipv6)
{
$this->socket = $socket;
$this->sock = $socket;
$this->sock->setBlocking(true);
$this->protocol = $protocol;
$this->construct_TL(['socket' => __DIR__.'/../TL_socket.tl']);
}
public function __destruct() {
unset($this->sock);
$this->destruct_madeline();
exit();
}
public function destruct_madeline() {
if ($this->madeline !== null) {
$this->madeline->settings['logger'] = ['logger' => 0];
$this->madeline->serialize();
unset($this->madeline);
return true;
}
return false;
}
public function loop()
{
}
while (true) {
$request_id = 0;
try {
$message = $this->read_message();
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
continue;
}
if ($message === null) {
continue;
}
try {
$message = $this->deserialize($message, ['type' => '', 'datacenter' => '']);
if ($message['_'] !== 'socketMessageRequest') {
throw new \danog\MadelineProto\Exception('Invalid object received');
}
$request_id = $message['request_id'];
$this->send_response($request_id, $this->on_request($request_id, $message['method'], $message['args']));
} catch (\danog\MadelineProto\TL\Exception $e) {
$this->send_exception($request_id, $e);
continue;
} catch (\danog\MadelineProto\Exception $e) {
$this->send_exception($request_id, $e);
continue;
} catch (\danog\MadelineProto\RPCErrorException $e) {
$this->send_exception($request_id, $e);
continue;
} catch (\DOMException $e) {
$this->send_exception($request_id, $e);
continue;
}
public function read_payload()
{
}
}
public function on_request($method, $args) {
if (count($method) === 0 || count($method) > 2) {
throw new \danog\MadelineProto\Exception('Invalid method called');
}
if ($method[0] === '__construct') {
if (count($args) === 1 && is_array($args[0])) {
$args[0]['logger'] = ['logger' => 4, 'logger_param' => [$this, 'logger']];
$args[0]['updates']['callback'] = [$this, 'update_handler'];
} else if (count($args) === 2 && is_array($args[1])) {
$args[1]['logger'] = ['logger' => 4, 'logger_param' => [$this, 'logger']];
$args[1]['updates']['callback'] = [$this, 'update_handler'];
}
$this->madeline = new \danog\MadelineProto\API(...$args);
return true;
}
if ($method[0] === '__destruct') {
return $this->destruct_madeline();
}
if ($this->madeline === null) {
throw new \danog\MadelineProto\Exception('__construct was not called');
}
foreach ($args as &$arg) {
if (is_array($arg) && isset($arg['_'])){
if ($arg['_'] === 'callback' && isset($arg['callback']) && !method_exists($this, $arg['callback'])) {
$arg = [$this, $arg['callback']];
}
if ($arg['_'] === 'stream' && isset($arg['stream_id'])) {
$arg = fopen('madelineSocket://', 'r+b', false, Handler::getContext($this, $arg['stream_id']));
}
}
}
if (count($method) === 1) {
return $this->madeline->{$method[0]}(...$args);
}
if (count($method) === 2) {
return $this->madeline->{$method[0]}->{$method[1]}(...$args);
}
}
public function send_exception($request_id, $e) {
echo $e;
//$this->send_message($this->serialize_object(['type' => 'socketMessageException'], ['request_id' => $request_id, 'exception' => $e]));
}
public function send_response($request_id, $response) {
$this->send_message($this->serialize_object(['type' => 'socketMessageResponse'], ['request_id' => $request_id, 'data' => $response]));
}
public function send_data($stream_id, $data) {
$this->send_message($this->serialize_object(['type' => 'socketMessageRawData'], ['stream_id' => $stream_id, 'data' => $data]));
}
public function logger($message, $level) {
public function write_payload($payload)
{
}
public function update_handler($update) {
$this->send_message($this->serialize_object(['type' => 'socketMessageUpdate'], ['data' => $update]));
}
public function __call($method, $args) {
$this->send_message($this->serialize_object(['type' => 'socketMessageRequest'], ['request_id' => 0, 'method' => $method, 'args' => $args]));
}
}

View File

@ -0,0 +1,57 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
The PWRTelegram API is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\Server;
class Stream
{
const WRAPPER_NAME = 'madelineSocket';
public $context;
private $_handler;
private $_stream_id;
private static $_isRegistered = false;
public static function getContext($handler, $stream_id)
{
if (!self::$_isRegistered) {
stream_wrapper_register(self::WRAPPER_NAME, get_class());
self::$_isRegistered = true;
}
return stream_context_create(array(self::WRAPPER_NAME => ['handler' => $handler, $stream_id]));
}
public function stream_open($path, $mode, $options, &$opened_path)
{
$opt = stream_context_get_options($this->context);
if (!is_array($opt[self::WRAPPER_NAME]) ||
!isset($opt[self::WRAPPER_NAME]['handler']) ||
!($opt[self::WRAPPER_NAME]['handler'] instanceof Handler)
!isset($opt[self::WRAPPER_NAME]['stream_id']) ||
!is_integer($opt[self::WRAPPER_NAME]['stream_id'])) return false;
$this->_handler = $opt[self::WRAPPER_NAME]['handler'];
$this->_stream_id = $opt[self::WRAPPER_NAME]['stream_id'];
return true;
}
public function stream_write($data)
{
$this->handler->send_data($this->_stream_id, $data);
}
public function stream_lock($mode) {
}
}

View File

@ -0,0 +1,18 @@
dataJSON#7d748d04 data:string = DataJSON;
socketMessageRequest request_id:int method:vector<string> args:vector<%DataJSON> = SocketMessage;
socketMessageResponse request_id:int data:%DataJSON = SocketMessage;
socketMessageException request_id:int exception:SocketException = SocketMessage;
socketMessageUpdate data:%DataJSON = SocketMessage;
socketMessageLog flags:# thread:flags.0?true process:flags.1?true additional:flags.2?string file:flags.3?string level:int data:string = SocketMessage;
socketMessageRawData stream_id:int data:bytes = SocketMessage;
socketException message:string code:int trace:%SocketTLTrace = SocketException;
socketRPCErrorException flags:# rpc_message:flags.0?string message:flags.1?string code:int trace:%SocketTLTrace = SocketException;
socketTLException message:string code:int trace:%SocketTLTrace = SocketException;
socketDOMException message:string code:int trace:%SocketTLTrace = SocketException;
socketTLTrace frames:vector<%SocketTLFrame> = SocketTLTrace;
socketTLFrame flags:# file:flags.0?string line:flags.1?string function:flags.2?string args:flags.3?string tl_param:flags.4?string = SocketTLFrame;

View File

@ -113,7 +113,7 @@ if (stripos(readline('Do you want to handle incoming calls? (y/n): '), 'y') !==
$howmany = readline('How many calls would you like me to handle? ');
$offset = 0;
while ($howmany > 0) {
$updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
$updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
foreach ($updates as $update) {
\danog\MadelineProto\Logger::log($update);
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
@ -141,7 +141,7 @@ if (stripos(readline('Do you want to make the secret chat tests? (y/n): '), 'y')
\danog\MadelineProto\Logger::log($sentMessage, \danog\MadelineProto\Logger::NOTICE);
/*
while (true) {
$updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
$updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
//\danog\MadelineProto\Logger::log($updates);
foreach ($updates as $update) {
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id

View File

@ -64,7 +64,7 @@ Created by [Daniil Gentili](mention:@danogentili) (@daniilgentili) using the [Ma
echo 'Bot started.'.PHP_EOL;
while (true) {
$updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
$updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
foreach ($updates as $update) {
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
switch ($update['update']['_']) {

View File

@ -107,7 +107,7 @@ Note that the query must be terminated by a \$
Created by @danogentili (@daniilgentili) using the daniil.it/MadelineProto PHP MTProto client.";
while (true) {
$updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
$updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
foreach ($updates as $update) {
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
try {

View File

@ -109,7 +109,7 @@ function recurse($array, $prefix = '')
}
$offset = 0;
while (true) {
$updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
$updates = $MadelineProto->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
foreach ($updates as $update) {
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
switch ($update['update']['_']) {