Serialization and database improvements
This commit is contained in:
parent
0001a45cd2
commit
89da6d5a45
@ -6,7 +6,8 @@
|
||||
"homepage": "https://docs.madelineproto.xyz",
|
||||
"keywords": ["telegram", "mtproto", "protocol", "bytes", "messenger", "client", "PHP", "video", "stickers", "audio", "files", "GB"],
|
||||
"conflict": {
|
||||
"krakjoe/pthreads-polyfill": "*"
|
||||
"krakjoe/pthreads-polyfill": "*",
|
||||
"ext-pthreads": "*"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4.0",
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Settings\Logger as SettingsLogger;
|
||||
|
||||
/**
|
||||
@ -31,13 +30,11 @@ class API extends InternalDoc
|
||||
use \danog\MadelineProto\ApiWrappers\Start;
|
||||
use \danog\MadelineProto\ApiWrappers\Templates;
|
||||
/**
|
||||
* Session path.
|
||||
* Session paths.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $session = '';
|
||||
public SessionPaths $session;
|
||||
|
||||
/**
|
||||
* Instance of MadelineProto.
|
||||
@ -95,7 +92,7 @@ class API extends InternalDoc
|
||||
/**
|
||||
* Global session unlock callback.
|
||||
*
|
||||
* @var callable
|
||||
* @var ?callable
|
||||
*/
|
||||
private $unlock;
|
||||
|
||||
@ -111,10 +108,11 @@ class API extends InternalDoc
|
||||
public function __magic_construct(string $session, $settings = []): void
|
||||
{
|
||||
$settings = Settings::parseFromLegacy($settings);
|
||||
$this->session = new SessionPaths($session);
|
||||
$this->wrapper = new APIWrapper($this, $this->exportNamespace());
|
||||
|
||||
Magic::classExists();
|
||||
$this->setInitPromise($this->__construct_async($session, $settings));
|
||||
Magic::classExists(true);
|
||||
$this->setInitPromise($this->internalInitAPI($settings));
|
||||
foreach (\get_class_vars(APIFactory::class) as $key => $var) {
|
||||
if (\in_array($key, ['namespace', 'API', 'lua', 'async', 'asyncAPIPromise', 'methods'])) {
|
||||
continue;
|
||||
@ -127,21 +125,20 @@ class API extends InternalDoc
|
||||
/**
|
||||
* Async constructor function.
|
||||
*
|
||||
* @param string $session Session name
|
||||
* @param Settings|SettingsEmpty $settings Settings
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function __construct_async(string $session, SettingsAbstract $settings): \Generator
|
||||
private function internalInitAPI(SettingsAbstract $settings): \Generator
|
||||
{
|
||||
Logger::constructorFromSettings($settings instanceof SettingsEmpty
|
||||
? new SettingsLogger
|
||||
: $settings->getLogger());
|
||||
$this->session = $session = Tools::absolute($session);
|
||||
[$unserialized, $this->unlock] = yield from Serialization::legacyUnserialize($session);
|
||||
|
||||
[$unserialized, $this->unlock] = yield from Serialization::legacyUnserialize($this->session);
|
||||
if ($unserialized) {
|
||||
$unserialized->storage = $unserialized->storage ?? [];
|
||||
$unserialized->session = $session;
|
||||
$unserialized->session = $this->session;
|
||||
APIWrapper::link($this, $unserialized);
|
||||
APIWrapper::link($this->wrapper, $this);
|
||||
AbstractAPIFactory::link($this->wrapper->getFactory(), $this);
|
||||
@ -256,7 +253,7 @@ class API extends InternalDoc
|
||||
* @param API[] $instances Instances of madeline
|
||||
* @param string[]|string $eventHandler Event handler(s)
|
||||
*
|
||||
* @return Promise
|
||||
* @return void
|
||||
*/
|
||||
public static function startAndLoopMulti(array $instances, $eventHandler): void
|
||||
{
|
||||
|
@ -35,10 +35,8 @@ final class APIWrapper
|
||||
|
||||
/**
|
||||
* Session path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $session = '';
|
||||
public SessionPaths $session;
|
||||
|
||||
/**
|
||||
* Getting API ID flag.
|
||||
@ -169,7 +167,7 @@ final class APIWrapper
|
||||
*/
|
||||
public function getIpcPath(): string
|
||||
{
|
||||
return (new SessionPaths($this->session))->getIpcPath();
|
||||
return $this->session->getIpcPath();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,37 +177,18 @@ final class APIWrapper
|
||||
*/
|
||||
public function serialize(): Promise
|
||||
{
|
||||
if (!$this->session) {
|
||||
Logger::log("Not serializing, no session");
|
||||
return new Success();
|
||||
}
|
||||
if ($this->API instanceof FastAPI) {
|
||||
Logger::log("Not serializing, IPC client");
|
||||
return new Success();
|
||||
if ($this->API === null && !$this->gettingApiId) {
|
||||
return new Success(false);
|
||||
}
|
||||
return Tools::callFork((function (): \Generator {
|
||||
if (isset($this->API->flushSettings) && $this->API->flushSettings) {
|
||||
$this->API->flushSettings = false;
|
||||
$this->API->__construct($this->API->settings);
|
||||
}
|
||||
if ($this->API === null && !$this->gettingApiId) {
|
||||
return false;
|
||||
}
|
||||
if ($this->API) {
|
||||
yield from $this->API->initAsynchronously();
|
||||
}
|
||||
$this->serialized = \time();
|
||||
$realpaths = new SessionPaths($this->session);
|
||||
Logger::log('Waiting for exclusive lock of serialization lockfile...');
|
||||
$unlock = yield Tools::flock($realpaths->getLockPath(), LOCK_EX);
|
||||
Logger::log('Lock acquired, serializing');
|
||||
try {
|
||||
$wrote = yield put($realpaths->getTempPath(), \serialize($this));
|
||||
yield renameAsync($realpaths->getTempPath(), $realpaths->getSessionPath());
|
||||
} finally {
|
||||
$unlock();
|
||||
}
|
||||
Logger::log('Done serializing');
|
||||
|
||||
$wrote = yield put($this->session->getTempPath(), \serialize($this));
|
||||
yield renameAsync($this->session->getTempPath(), $this->session->getSessionPath());
|
||||
|
||||
Logger::log('Saved session!');
|
||||
return $wrote;
|
||||
})());
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ abstract class DriverArray implements DbArray
|
||||
throw new \RuntimeException('Native isset not support promises. Use isset method');
|
||||
}
|
||||
|
||||
abstract protected function initConnection($settings): \Generator;
|
||||
abstract public function initConnection($settings): \Generator;
|
||||
|
||||
/**
|
||||
* @param self $new
|
||||
|
@ -188,7 +188,7 @@ class MysqlArray extends SqlArray
|
||||
* @param DatabaseMysql $settings
|
||||
* @return \Generator
|
||||
*/
|
||||
protected function initConnection($settings): \Generator
|
||||
public function initConnection($settings): \Generator
|
||||
{
|
||||
if (!isset($this->db)) {
|
||||
$this->db = yield from Mysql::getConnection($settings);
|
||||
|
@ -28,7 +28,7 @@ class PostgresArray extends SqlArray
|
||||
* @param DatabasePostgres $settings
|
||||
* @return \Generator
|
||||
*/
|
||||
protected function initConnection($settings): \Generator
|
||||
public function initConnection($settings): \Generator
|
||||
{
|
||||
if (!isset($this->db)) {
|
||||
$this->db = yield from Postgres::getConnection($settings);
|
||||
|
@ -49,7 +49,7 @@ class RedisArray extends SqlArray
|
||||
* @param DatabaseRedis $settings
|
||||
* @return \Generator
|
||||
*/
|
||||
protected function initConnection($settings): \Generator
|
||||
public function initConnection($settings): \Generator
|
||||
{
|
||||
if (!isset($this->db)) {
|
||||
$this->db = yield from Redis::getConnection($settings);
|
||||
|
@ -34,12 +34,6 @@ class Magic
|
||||
* @var array
|
||||
*/
|
||||
public static $storage = [];
|
||||
/**
|
||||
* Whether has threads.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public static $has_thread = false;
|
||||
/**
|
||||
* Whether this system is bigendian.
|
||||
*
|
||||
@ -292,10 +286,9 @@ class Magic
|
||||
throw Exception::extension($extension);
|
||||
}
|
||||
}
|
||||
self::$has_thread = \class_exists(\Thread::class) && \method_exists(\Thread::class, 'getCurrentThread');
|
||||
self::$BIG_ENDIAN = \pack('L', 1) === \pack('N', 1);
|
||||
self::$bigint = PHP_INT_SIZE < 8;
|
||||
self::$ipv6 = (bool) \strlen(@\file_get_contents('http://ipv6.google.com', false, \stream_context_create(['http' => ['timeout' => 1]]))) > 0;
|
||||
self::$ipv6 = (bool) \strlen(@\file_get_contents('http://ipv6.google.com/', false, \stream_context_create(['http' => ['timeout' => 1]]))) > 0;
|
||||
\preg_match('/const V = (\\d+);/', @\file_get_contents('https://raw.githubusercontent.com/danog/MadelineProto/master/src/danog/MadelineProto/MTProto.php'), $matches);
|
||||
if (isset($matches[1]) && \danog\MadelineProto\MTProto::V < (int) $matches[1]) {
|
||||
throw new \danog\MadelineProto\Exception(\hex2bin(\danog\MadelineProto\Lang::$current_lang['v_error']), 0, null, 'MadelineProto', 1);
|
||||
|
@ -32,16 +32,15 @@ class Serialization
|
||||
/**
|
||||
* Unserialize legacy session.
|
||||
*
|
||||
* @param string $session Session name
|
||||
* @param SessionPaths $session Session name
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public static function legacyUnserialize(string $session): \Generator
|
||||
public static function legacyUnserialize(SessionPaths $session): \Generator
|
||||
{
|
||||
$realpaths = new SessionPaths($session);
|
||||
if (yield exists($realpaths->getSessionPath())) {
|
||||
if (yield exists($session->getSessionPath())) {
|
||||
Logger::log('Waiting for exclusive session lock...');
|
||||
$warningId = Loop::delay(1000, static function () use (&$warningId) {
|
||||
Logger::log("It seems like the session is busy.");
|
||||
@ -54,23 +53,17 @@ class Serialization
|
||||
Loop::unreference($warningId);
|
||||
});
|
||||
Loop::unreference($warningId);
|
||||
$unlockGlobal = yield Tools::flock($realpaths->getSessionLockPath(), LOCK_EX, 1);
|
||||
$unlock = yield Tools::flock($session->getLockPath(), LOCK_EX, 1);
|
||||
Loop::cancel($warningId);
|
||||
$tempId = Shutdown::addCallback($unlockGlobal = static function () use ($unlockGlobal) {
|
||||
$tempId = Shutdown::addCallback($unlock = static function () use ($unlock) {
|
||||
Logger::log("Unlocking exclusive session lock!");
|
||||
$unlockGlobal();
|
||||
$unlock();
|
||||
Logger::log("Unlocked exclusive session lock!");
|
||||
});
|
||||
Logger::log("Got exclusive session lock!");
|
||||
|
||||
Logger::log('Waiting for shared lock of serialization lockfile...');
|
||||
$unlock = yield Tools::flock($realpaths->getLockPath(), LOCK_SH);
|
||||
Logger::log('Shared lock acquired, deserializing...');
|
||||
try {
|
||||
$tounserialize = yield get($realpaths->getSessionPath());
|
||||
} finally {
|
||||
$unlock();
|
||||
}
|
||||
$tounserialize = yield get($session->getSessionPath());
|
||||
|
||||
Magic::classExists();
|
||||
try {
|
||||
$unserialized = \unserialize($tounserialize);
|
||||
@ -133,7 +126,7 @@ class Serialization
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['deserialization_error']);
|
||||
}
|
||||
Shutdown::removeCallback($tempId);
|
||||
return [$unserialized, $unlockGlobal];
|
||||
return [$unserialized, $unlock];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,10 +28,6 @@ class SessionPaths
|
||||
* Session path.
|
||||
*/
|
||||
private string $sessionPath;
|
||||
/**
|
||||
* Global session lock path.
|
||||
*/
|
||||
private string $slockPath;
|
||||
/**
|
||||
* Session lock path.
|
||||
*/
|
||||
@ -53,7 +49,6 @@ class SessionPaths
|
||||
{
|
||||
$session = Tools::absolute($session);
|
||||
$this->sessionPath = $session;
|
||||
$this->slockPath = "$session.slock";
|
||||
$this->lockPath = "$session.lock";
|
||||
$this->ipcPath = "$session.ipc";
|
||||
$this->tempPath = "$session.temp.session";
|
||||
@ -78,16 +73,6 @@ class SessionPaths
|
||||
return $this->sessionPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get global session lock path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSessionLockPath(): string
|
||||
{
|
||||
return $this->slockPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lock path.
|
||||
*
|
||||
|
@ -783,6 +783,27 @@ abstract class Tools extends StrTools
|
||||
{
|
||||
return Magic::$altervista;
|
||||
}
|
||||
/**
|
||||
* Checks private property exists in an object.
|
||||
*
|
||||
* @param object $obj Object
|
||||
* @param string $var Attribute name
|
||||
*
|
||||
* @psalm-suppress InvalidScope
|
||||
*
|
||||
* @return bool
|
||||
* @access public
|
||||
*/
|
||||
public static function hasVar($obj, string $var): bool
|
||||
{
|
||||
return \Closure::bind(
|
||||
function () use ($var) {
|
||||
return isset($this->{$var});
|
||||
},
|
||||
$obj,
|
||||
\get_class($obj)
|
||||
)->__invoke();
|
||||
}
|
||||
/**
|
||||
* Accesses a private variable from an object.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user