1
0
mirror of https://github.com/danog/MadelineProto.git synced 2024-11-30 04:08:59 +01:00

Misc improvements

This commit is contained in:
Daniil Gentili 2023-05-02 18:42:46 +02:00
parent d98c3422fc
commit 26b900cf61
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
22 changed files with 113 additions and 101 deletions

View File

@ -200,15 +200,15 @@ Want to add your own open-source project to this list? [Click here!](https://doc
* [Queues](https://docs.madelineproto.xyz/docs/USING_METHODS.html#queues)
* [Multiple method calls](https://docs.madelineproto.xyz/docs/USING_METHODS.html#multiple-method-calls)
* [FULL API Documentation with descriptions](https://docs.madelineproto.xyz/API_docs/methods/)
* [Logout](https://docs.madelineproto.xyz/logout.html)
* [Login](https://docs.madelineproto.xyz/docs/LOGIN.html)
* [Change 2FA password](https://docs.madelineproto.xyz/update2fa.html)
* [Get all chats, broadcast a message to all chats](https://docs.madelineproto.xyz/docs/DIALOGS.html)
* [Get the full participant list of a channel/group/supergroup](https://docs.madelineproto.xyz/getPwrChat.html)
* [Get full info about a user/chat/supergroup/channel](https://docs.madelineproto.xyz/getFullInfo.html)
* [Get info about a user/chat/supergroup/channel](https://docs.madelineproto.xyz/getInfo.html)
* [Get info about the currently logged-in user](https://docs.madelineproto.xyz/getSelf.html)
* [Upload or download files up to 2 GB](https://docs.madelineproto.xyz/docs/FILES.html)
* [Change 2FA password: update2FA](https://docs.madelineproto.xyz/update2fa.html)
* [Get all chats, broadcast a message to all chats: getDialogIds, getDialogs, getFullDialogs](https://docs.madelineproto.xyz/docs/DIALOGS.html)
* [Get the full participant list of a channel/group/supergroup: getPwrChat](https://docs.madelineproto.xyz/getPwrChat.html)
* [Get full info about a user/chat/supergroup/channel: getFullInfo](https://docs.madelineproto.xyz/getFullInfo.html)
* [Get info about a user/chat/supergroup/channel: getInfo](https://docs.madelineproto.xyz/getInfo.html)
* [Get the ID of a user/chat/supergroup/channel/update: getID](https://docs.madelineproto.xyz/getId.html)
* [Get info about the currently logged-in user: getSelf](https://docs.madelineproto.xyz/getSelf.html)
* [Upload or download files up to 2 GB: uploadFrom*, downloadTo*](https://docs.madelineproto.xyz/docs/FILES.html)
* [Make a phone call and play a song](https://docs.madelineproto.xyz/docs/CALLS.html)
* [Create a secret chat bot](https://docs.madelineproto.xyz/docs/SECRET_CHATS.html)
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account.declinePasswordReset.html" name="account.declinePasswordReset">Abort a pending 2FA password reset, see here for more info »: account.declinePasswordReset</a>

View File

@ -119,7 +119,7 @@
"test-php56": "tests/test-conversion.sh 5",
"cs": "PHP_CS_FIXER_IGNORE_ENV=1 php -d pcre.jit=0 vendor/bin/php-cs-fixer fix -v --diff --dry-run",
"cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php -d pcre.jit=0 vendor/bin/php-cs-fixer fix -v --diff",
"psalm": "psalm",
"psalm": "psalm.phar",
"docs": "php tools/build_docs.php",
"docs-fix": "tools/fix_docs.sh",
"test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text --config tests/phpunit.xml",

2
docs

@ -1 +1 @@
Subproject commit a6959a7fe20409d539ea8fafeafe780681f943b6
Subproject commit 0689f6a3df370f6626672149a01aa937415e91d8

View File

@ -57,7 +57,7 @@ class MyEventHandler extends EventHandler
* @see https://docs.madelineproto.xyz/docs/DATABASE.html
*/
protected static array $dbProperties = [
'dataStoredOnDb' => 'array',
'dataStoredOnDb' => [],
];
/**

View File

@ -4,9 +4,9 @@ declare(strict_types=1);
namespace danog\MadelineProto\Db;
use danog\MadelineProto\Settings\Database\DriverDatabaseAbstract;
use danog\MadelineProto\Settings\Database\Memory;
use danog\MadelineProto\Settings\Database\Mysql;
use danog\MadelineProto\Settings\Database\PersistentDatabaseAbstract;
use danog\MadelineProto\Settings\Database\Postgres;
use danog\MadelineProto\Settings\Database\Redis;
use danog\MadelineProto\Settings\Database\SerializerType;
@ -14,12 +14,13 @@ use danog\MadelineProto\Settings\DatabaseAbstract;
use InvalidArgumentException;
/**
* @psalm-type TOrmConfig=array{serializer?: SerializerType, enableCache?: bool, cacheTtl?: int}
* This factory class initializes the correct database backend for MadelineProto.
*/
abstract class DbPropertiesFactory
{
/**
* @param SerializerType | array{serializer?: SerializerType, enableCache?: bool, cacheTtl?: int} $config
* @param TOrmConfig|'array' $config
* @return DbType
* @internal
* @uses \danog\MadelineProto\Db\MemoryArray
@ -27,25 +28,22 @@ abstract class DbPropertiesFactory
* @uses \danog\MadelineProto\Db\PostgresArray
* @uses \danog\MadelineProto\Db\RedisArray
*/
public static function get(DatabaseAbstract $dbSettings, string $table, SerializerType|array $config, ?DbType $value = null)
public static function get(DatabaseAbstract $dbSettings, string $table, string|array $config, ?DbType $value = null)
{
if ($config === 'array') {
$config = [];
}
$dbSettingsCopy = clone $dbSettings;
$class = __NAMESPACE__;
if ($dbSettingsCopy instanceof PersistentDatabaseAbstract) {
if ($config instanceof SerializerType) {
$config = [
'serializer' => $config
];
}
$config = array_merge([
if ($dbSettingsCopy instanceof DriverDatabaseAbstract) {
$config = \array_merge([
'serializer' => $dbSettings->getSerializer(),
'enableCache' => true,
'cacheTtl' => $dbSettings->getCacheTtl(),
], $config);
$class = $dbSettings instanceof PersistentDatabaseAbstract && (!($config['enableCache'] ?? true) || !$config['cacheTtl'])
$class = $dbSettings instanceof DriverDatabaseAbstract && (!($config['enableCache'] ?? true) || !$config['cacheTtl'])
? __NAMESPACE__ . '\\NullCache'
: __NAMESPACE__;

View File

@ -15,8 +15,9 @@ use function Amp\Future\await;
*
* You will have to define a `$dbProperties` static array property, with a list of properties you want to store to a database.
*
* @see DbPropertiesFactory For a list of allowed property types
* @property array<string, DbPropertiesFactory::TYPE_*> $dbProperties
* @psalm-import-type TOrmConfig from DbPropertiesFactory
*
* @property array<string, TOrmConfig> $dbProperties
*/
trait DbPropertiesTrait
{

View File

@ -5,9 +5,10 @@ declare(strict_types=1);
namespace danog\MadelineProto\Db;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Settings\Database\PersistentDatabaseAbstract;
use danog\MadelineProto\Settings\Database\DriverDatabaseAbstract;
use danog\MadelineProto\Settings\Database\Memory;
use danog\MadelineProto\Settings\Database\SerializerType;
use danog\MadelineProto\Settings\DatabaseAbstract;
use danog\MadelineProto\SettingsAbstract;
use IteratorAggregate;
use ReflectionClass;
@ -29,7 +30,11 @@ use function Amp\Future\await;
abstract class DriverArray implements DbArray, IteratorAggregate
{
protected string $table;
protected PersistentDatabaseAbstract $dbSettings;
/** @var callable(mixed): mixed */
protected $serializer;
/** @var callable(string): mixed */
protected $deserializer;
protected DriverDatabaseAbstract $dbSettings;
use ArrayCacheTrait;
@ -77,7 +82,7 @@ abstract class DriverArray implements DbArray, IteratorAggregate
return $this->offsetGet($key) !== null;
}
public static function getInstance(string $table, DbType|array|null $previous, $settings): static
public static function getInstance(string $table, DbType|array|null $previous, DatabaseAbstract $settings): static
{
/** @var MysqlArray|PostgresArray|RedisArray */
$instance = new static();
@ -85,6 +90,7 @@ abstract class DriverArray implements DbArray, IteratorAggregate
$instance->dbSettings = $settings;
$instance->setCacheTtl($settings->getCacheTtl());
$instance->setSerializer($settings->getSerializer());
$instance->startCacheCleanupLoop();
@ -102,6 +108,22 @@ abstract class DriverArray implements DbArray, IteratorAggregate
return $instance;
}
protected function setSerializer(SerializerType $serializer): void
{
$this->serializer = match ($serializer) {
SerializerType::SERIALIZE => \serialize(...),
SerializerType::IGBINARY => \igbinary_serialize(...),
SerializerType::JSON => fn ($value) => \json_encode($value, JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),
SerializerType::STRING => strval(...),
};
$this->deserializer = match ($serializer) {
SerializerType::SERIALIZE => \unserialize(...),
SerializerType::IGBINARY => \igbinary_unserialize(...),
SerializerType::JSON => fn ($value) => \json_decode($value, true, 256, JSON_THROW_ON_ERROR),
SerializerType::STRING => fn ($v) => $v,
};
}
/**
* Rename table of old database, if the new one is not a temporary table name.
*
@ -215,30 +237,4 @@ abstract class DriverArray implements DbArray, IteratorAggregate
}
return \str_replace('NullCache\\', '', $instance::class);
}
/**
* Serialize retrieved value.
*/
protected function serialize(mixed $value): string
{
return match ($this->dbSettings->getSerializer()) {
SerializerType::SERIALIZE => \serialize($value),
SerializerType::IGBINARY => \igbinary_serialize($value),
SerializerType::JSON => json_encode($value, JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),
SerializerType::STRING => (string)$value,
};
}
/**
* Deserialize retrieved value.
*/
protected function unserialize(string $value): mixed
{
return match ($this->dbSettings->getSerializer()) {
SerializerType::SERIALIZE => \unserialize($value),
SerializerType::IGBINARY => \igbinary_unserialize($value),
SerializerType::JSON => json_decode($value, true, 256, JSON_THROW_ON_ERROR),
SerializerType::STRING => $value,
};
}
}

View File

@ -28,7 +28,7 @@ final class MemoryArray extends ArrayIterator implements DbArray
/**
* @param Memory $settings
*/
public static function getInstance(string $table, $previous, Memory $settings): static
public static function getInstance(string $table, DbType|array|null $previous, $settings): static
{
if ($previous instanceof MemoryArray) {
return $previous;

View File

@ -22,7 +22,6 @@ use PDO;
*/
class MysqlArray extends SqlArray
{
// Legacy
protected array $settings;

View File

@ -4,12 +4,13 @@ declare(strict_types=1);
namespace danog\MadelineProto\Db;
use Amp\Postgres\ByteA;
use Amp\Postgres\PostgresConfig;
use danog\MadelineProto\Db\Driver\Postgres;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Settings\Database\PersistentDatabaseAbstract;
use danog\MadelineProto\Settings\Database\Postgres as DatabasePostgres;
use danog\MadelineProto\Settings\Database\SerializerType;
use PDO;
/**
@ -22,7 +23,6 @@ use PDO;
*/
class PostgresArray extends SqlArray
{
// Legacy
protected array $settings;
@ -90,16 +90,21 @@ class PostgresArray extends SqlArray
}
}
protected function unserialize(string $value): mixed
protected function setSerializer(SerializerType $serializer): void
{
return parent::unserialize(\hex2bin($value));
$this->serializer = match ($serializer) {
SerializerType::SERIALIZE => fn ($v) => new ByteA(\serialize($v)),
SerializerType::IGBINARY => fn ($v) => new ByteA(\igbinary_serialize($v)),
SerializerType::JSON => fn ($value) => \json_encode($value, JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),
SerializerType::STRING => strval(...),
};
$this->deserializer = match ($serializer) {
SerializerType::SERIALIZE => \unserialize(...),
SerializerType::IGBINARY => \igbinary_unserialize(...),
SerializerType::JSON => fn ($value) => \json_decode($value, true, 256, JSON_THROW_ON_ERROR),
SerializerType::STRING => fn ($v) => $v,
};
}
protected function serialize(mixed $value): string
{
return \bin2hex(parent::serialize($value));
}
/**
* Create table for property.
*/
@ -111,7 +116,7 @@ class PostgresArray extends SqlArray
CREATE TABLE IF NOT EXISTS \"{$this->table}\"
(
\"key\" VARCHAR(255) PRIMARY KEY NOT NULL,
\"value\" TEXT NOT NULL
\"value\" BYTEA NOT NULL
);
");
}

View File

@ -85,7 +85,7 @@ class RedisArray extends DriverArray
$this->setCache($index, $value);
$this->db->set($this->rKey($index), $this->serialize($value));
$this->db->set($this->rKey($index), ($this->serializer)($value));
$this->setCache($index, $value);
}
@ -98,7 +98,7 @@ class RedisArray extends DriverArray
$value = $this->db->get($this->rKey($offset));
if ($value !== null && $value = $this->unserialize($value)) {
if ($value !== null && $value = ($this->deserializer)($value)) {
$this->setCache($offset, $value);
}
@ -123,7 +123,7 @@ class RedisArray extends DriverArray
$len = \strlen($this->rKey(''));
foreach ($request as $key) {
yield \substr($key, $len) => \unserialize($this->db->get($key));
yield \substr($key, $len) => ($this->deserializer)($this->db->get($key));
}
}

View File

@ -66,7 +66,7 @@ abstract class SqlArray extends DriverArray
public function getIterator(): \Traversable
{
foreach ($this->execute($this->queries[self::SQL_ITERATE]) as ['key' => $key, 'value' => $value]) {
yield $key => $this->unserialize($value);
yield $key => ($this->deserializer)($value);
}
}
@ -86,7 +86,7 @@ abstract class SqlArray extends DriverArray
return null;
}
$value = $this->unserialize($row['value']);
$value = ($this->deserializer)($row['value']);
$this->setCache($key, $value);
return $value;
@ -105,7 +105,7 @@ abstract class SqlArray extends DriverArray
$this->queries[self::SQL_SET],
[
'index' => $key,
'value' => $this->serialize($value),
'value' => ($this->serializer)($value),
],
);
$this->setCache($key, $value);

View File

@ -48,6 +48,7 @@ use danog\MadelineProto\MTProtoTools\UpdatesState;
use danog\MadelineProto\SecretChats\MessageHandler;
use danog\MadelineProto\SecretChats\ResponseHandler;
use danog\MadelineProto\SecretChats\SeqNoHandler;
use danog\MadelineProto\Settings\Database\SerializerType;
use danog\MadelineProto\Settings\TLSchema;
use danog\MadelineProto\TL\Conversion\BotAPI;
use danog\MadelineProto\TL\Conversion\BotAPIFiles;
@ -501,14 +502,12 @@ final class MTProto implements TLCallback, LoggerGetter
* @see DbPropertiesFactory
*/
protected static array $dbProperties = [
'chats' => [],
'full_chats' => [],
'sponsoredMessages' => [],
'channelParticipants' => [],
'usernames' => [],
'session' => [
'config' => ['enableCache' => false],
],
'chats' => ['serializer' => SerializerType::SERIALIZE],
'full_chats' => ['serializer' => SerializerType::SERIALIZE],
'sponsoredMessages' => ['serializer' => SerializerType::SERIALIZE],
'channelParticipants' => ['serializer' => SerializerType::SERIALIZE],
'usernames' => ['serializer' => SerializerType::SERIALIZE],
'session' => ['serializer' => SerializerType::SERIALIZE, 'enableCache' => false],
];
/**

View File

@ -25,6 +25,7 @@ use danog\MadelineProto\Db\DbPropertiesTrait;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\Settings\Database\SerializerType;
use danog\MadelineProto\TL\TLCallback;
use Revolt\EventLoop;
@ -68,7 +69,7 @@ final class MinDatabase implements TLCallback
* @see DbPropertiesFactory
*/
protected static array $dbProperties = [
'db' => [],
'db' => ['serializer' => SerializerType::SERIALIZE],
];
public function __construct(MTProto $API)

View File

@ -26,6 +26,7 @@ use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProto\OutgoingMessage;
use danog\MadelineProto\Settings\Database\SerializerType;
use danog\MadelineProto\TL\TLCallback;
use danog\MadelineProto\Tools;
use Webmozart\Assert\Assert;
@ -87,7 +88,7 @@ final class ReferenceDatabase implements TLCallback
* @see DbPropertiesFactory
*/
protected static array $dbProperties = [
'db' => [],
'db' => ['serializer' => SerializerType::SERIALIZE],
];
public function __construct(private MTProto $API)

View File

@ -82,6 +82,11 @@ final class Magic
*
*/
public static bool $can_getcwd = false;
/**
* Whether we can use igbinary.
*
*/
public static bool $can_use_igbinary = false;
/**
* Whether we've processed forks.
*
@ -224,6 +229,7 @@ final class Magic
Shutdown::init();
\set_error_handler(Exception::exceptionErrorHandler(...));
\set_exception_handler(Exception::exceptionHandler(...));
self::$can_use_igbinary = \function_exists('igbinary_serialize');
self::$isIpcWorker = \defined('MADELINE_WORKER_TYPE') ? MADELINE_WORKER_TYPE === 'madeline-ipc' : false;
// Important, obtain root relative to caller script
$backtrace = \debug_backtrace(0);

View File

@ -51,11 +51,9 @@ abstract class Serialization
/**
* Header for session files.
*/
const PHP_HEADER = '<?php __HALT_COMPILER();';
/**
* Serialization version.
*/
const VERSION = 2;
public const PHP_HEADER = '<?php __HALT_COMPILER();';
public const VERSION_OLD = 2;
public const VERSION_SERIALIZATION_AWARE = 3;
/**
* Unserialize session.
@ -217,7 +215,7 @@ abstract class Serialization
$unserialized = DbPropertiesFactory::get(
$settings,
$tableName,
SerializerType::SERIALIZE,
['serializer' => SerializerType::SERIALIZE],
$unserialized,
);
} else {

View File

@ -124,10 +124,11 @@ final class SessionPaths
Logger::log("Got exclusive lock of $path.lock...");
$object = Serialization::PHP_HEADER
.\chr(Serialization::VERSION)
.\chr(Serialization::VERSION_SERIALIZATION_AWARE)
.\chr(PHP_MAJOR_VERSION)
.\chr(PHP_MINOR_VERSION)
.\serialize($object);
.\chr(Magic::$can_use_igbinary ? 1 : 0)
.(Magic::$can_use_igbinary ? \igbinary_serialize($object) : \serialize($object));
write(
"$path.temp.php",
@ -170,7 +171,7 @@ final class SessionPaths
$file->seek($headerLen++);
$v = \ord($file->read(null, 1));
if ($v === Serialization::VERSION) {
if ($v >= Serialization::VERSION_OLD) {
$php = $file->read(null, 2);
$major = \ord($php[0]);
$minor = \ord($php[1]);
@ -179,7 +180,16 @@ final class SessionPaths
}
$headerLen += 2;
}
$unserialized = \unserialize($file->read(null, $size - $headerLen) ?? '');
$igbinary = false;
if ($v >= Serialization::VERSION_SERIALIZATION_AWARE) {
$igbinary = (bool) \ord($file->read(null, 1));
if ($igbinary && !Magic::$can_use_igbinary) {
throw Exception::extension('igbinary');
}
$headerLen++;
}
$unserialized = $file->read(null, $size - $headerLen) ?? '';
$unserialized = $igbinary ? \igbinary_unserialize($unserialized) : \unserialize($unserialized);
$file->close();
} finally {
$unlock();

View File

@ -4,12 +4,12 @@ declare(strict_types=1);
namespace danog\MadelineProto\Settings\Database;
use danog\MadelineProto\Settings\DatabaseAbstract as SettingsDatabaseAbstract;
use danog\MadelineProto\Settings\DatabaseAbstract;
/**
* Base class for database backends.
*/
abstract class PersistentDatabaseAbstract extends SettingsDatabaseAbstract
abstract class DriverDatabaseAbstract extends DatabaseAbstract
{
/**
* For how long to keep records in memory after last read, for cached backends.
@ -21,7 +21,7 @@ abstract class PersistentDatabaseAbstract extends SettingsDatabaseAbstract
protected string $password = '';
/**
* Which serializer strategy use by default
* Which serializer strategy to use by default.
*/
protected SerializerType $serializer = SerializerType::SERIALIZE;
@ -31,7 +31,6 @@ abstract class PersistentDatabaseAbstract extends SettingsDatabaseAbstract
'database',
'password',
'cache_ttl',
'serializer',
]) as $object => $array) {
if (isset($settings[$array])) {
$this->{$object}($settings[$array]);
@ -111,7 +110,6 @@ abstract class PersistentDatabaseAbstract extends SettingsDatabaseAbstract
*/
abstract public function setUri(string $uri): self;
public function getSerializer(): SerializerType
{
return $this->serializer;

View File

@ -7,7 +7,7 @@ namespace danog\MadelineProto\Settings\Database;
/**
* Redis backend settings.
*/
final class Redis extends PersistentDatabaseAbstract
final class Redis extends DriverDatabaseAbstract
{
/**
* Database number.

View File

@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace danog\MadelineProto\Settings\Database;

View File

@ -7,7 +7,7 @@ namespace danog\MadelineProto\Settings\Database;
/**
* Generic db backend settings.
*/
abstract class SqlAbstract extends PersistentDatabaseAbstract
abstract class SqlAbstract extends DriverDatabaseAbstract
{
/**
* Database name.