mirror of
https://github.com/danog/MadelineProto.git
synced 2024-11-30 09:58:59 +01:00
Merge pull request #1342 from danog/v8_save_updates_to_db
Support different serializers for each table
This commit is contained in:
commit
74847f0dec
16
README.md
16
README.md
@ -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>
|
||||
|
@ -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
2
docs
@ -1 +1 @@
|
||||
Subproject commit a6959a7fe20409d539ea8fafeafe780681f943b6
|
||||
Subproject commit 0689f6a3df370f6626672149a01aa937415e91d8
|
@ -24,6 +24,7 @@ use danog\MadelineProto\Settings;
|
||||
use danog\MadelineProto\Settings\Database\Mysql;
|
||||
use danog\MadelineProto\Settings\Database\Postgres;
|
||||
use danog\MadelineProto\Settings\Database\Redis;
|
||||
use danog\MadelineProto\Settings\Database\SerializerType;
|
||||
|
||||
// If a stable version of MadelineProto was installed via composer, load composer autoloader
|
||||
if (file_exists('vendor/autoload.php')) {
|
||||
@ -57,7 +58,7 @@ class MyEventHandler extends EventHandler
|
||||
* @see https://docs.madelineproto.xyz/docs/DATABASE.html
|
||||
*/
|
||||
protected static array $dbProperties = [
|
||||
'dataStoredOnDb' => 'array',
|
||||
'dataStoredOnDb' => ['serializer' => SerializerType::SERIALIZE],
|
||||
];
|
||||
|
||||
/**
|
||||
@ -94,6 +95,10 @@ class MyEventHandler extends EventHandler
|
||||
peer: self::ADMIN,
|
||||
message: "The bot was started!"
|
||||
);
|
||||
$this->messages->sendMedia(
|
||||
peer: self::ADMIN,
|
||||
media: 'BQACAgQAAxkDAAJbamRWnR-HEzE5AZi8rCd7u4QBVqkIAALkDQACkgABuVIjSOHLj_v1WC8E'
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Handle updates from supergroups and channels.
|
||||
|
@ -110,6 +110,7 @@ final class Blacklist {
|
||||
'Bool' => 'bool',
|
||||
'true' => 'bool',
|
||||
'InputMessage' => 'array|int',
|
||||
'InputMedia' => 'array|string',
|
||||
'InputCheckPasswordSRP' => 'string|array',
|
||||
'DataJSON' => 'mixed',
|
||||
'JSONValue' => 'mixed',
|
||||
@ -190,6 +191,9 @@ final class Blacklist {
|
||||
if ($type === 'InputMessage') {
|
||||
$base = "int|$base";
|
||||
}
|
||||
if ($type === 'InputMedia') {
|
||||
$base = "string|$base";
|
||||
}
|
||||
if ($isVector) {
|
||||
$base = "list<$base>";
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace danog\MadelineProto\Db;
|
||||
|
||||
use danog\MadelineProto\Settings\Database\DatabaseAbstract as DatabaseDatabaseAbstract;
|
||||
use danog\MadelineProto\Settings\Database\DriverDatabaseAbstract;
|
||||
use danog\MadelineProto\Settings\Database\Memory;
|
||||
use danog\MadelineProto\Settings\Database\Mysql;
|
||||
use danog\MadelineProto\Settings\Database\Postgres;
|
||||
@ -13,17 +13,15 @@ use danog\MadelineProto\Settings\DatabaseAbstract;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @psalm-import-type TOrmConfig from DbPropertiesTrait
|
||||
* This factory class initializes the correct database backend for MadelineProto.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class DbPropertiesFactory
|
||||
final class DbPropertiesFactory
|
||||
{
|
||||
/**
|
||||
* Indicates a simple K-V array stored in a database backend.
|
||||
* Values can be objects or other arrays, but when lots of nesting is required, it's best to split the array into multiple arrays.
|
||||
*/
|
||||
const TYPE_ARRAY = 'array';
|
||||
/**
|
||||
* @param self::TYPE_*|array $propertyType
|
||||
* @param TOrmConfig|'array' $config
|
||||
* @return DbType
|
||||
* @internal
|
||||
* @uses \danog\MadelineProto\Db\MemoryArray
|
||||
@ -31,41 +29,47 @@ abstract class DbPropertiesFactory
|
||||
* @uses \danog\MadelineProto\Db\PostgresArray
|
||||
* @uses \danog\MadelineProto\Db\RedisArray
|
||||
*/
|
||||
public static function get(DatabaseAbstract $dbSettings, string $table, $propertyType, ?DbType $value = null)
|
||||
public static function get(DatabaseAbstract $dbSettings, string $table, string|array $config, ?DbType $value = null)
|
||||
{
|
||||
$config = $propertyType['config'] ?? [];
|
||||
$propertyType = \is_array($propertyType) ? $propertyType['type'] : $propertyType;
|
||||
$propertyType = \strtolower($propertyType);
|
||||
$class = $dbSettings instanceof DatabaseDatabaseAbstract && (!($config['enableCache'] ?? true) || !$dbSettings->getCacheTtl())
|
||||
? __NAMESPACE__.'\\NullCache'
|
||||
: __NAMESPACE__;
|
||||
// Legacy
|
||||
if ($config === 'array') {
|
||||
$config = [];
|
||||
}
|
||||
$dbSettingsCopy = clone $dbSettings;
|
||||
$class = __NAMESPACE__;
|
||||
|
||||
if ($dbSettingsCopy instanceof DriverDatabaseAbstract) {
|
||||
$config = \array_merge([
|
||||
'serializer' => $dbSettings->getSerializer(),
|
||||
'enableCache' => true,
|
||||
'cacheTtl' => $dbSettings->getCacheTtl(),
|
||||
], $config);
|
||||
|
||||
$class = $dbSettings instanceof DriverDatabaseAbstract && (!($config['enableCache'] ?? true) || !$config['cacheTtl'])
|
||||
? __NAMESPACE__ . '\\NullCache'
|
||||
: __NAMESPACE__;
|
||||
|
||||
$dbSettingsCopy->setSerializer($config['serializer']);
|
||||
$dbSettingsCopy->setCacheTtl($config['cacheTtl']);
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case $dbSettings instanceof Memory:
|
||||
$class .= '\\Memory';
|
||||
$class .= '\\MemoryArray';
|
||||
break;
|
||||
case $dbSettings instanceof Mysql:
|
||||
$class .= '\\Mysql';
|
||||
$class .= '\\MysqlArray';
|
||||
break;
|
||||
case $dbSettings instanceof Postgres:
|
||||
$class .= '\\Postgres';
|
||||
$class .= '\\PostgresArray';
|
||||
break;
|
||||
case $dbSettings instanceof Redis:
|
||||
$class .= '\\Redis';
|
||||
$class .= '\\RedisArray';
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException('Unknown dbType: '.$dbSettings::class);
|
||||
throw new InvalidArgumentException('Unknown dbType: ' . $dbSettings::class);
|
||||
}
|
||||
|
||||
/** @var DbType $class */
|
||||
switch (\strtolower($propertyType)) {
|
||||
case self::TYPE_ARRAY:
|
||||
$class .= 'Array';
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException("Unknown $propertyType: {$propertyType}");
|
||||
}
|
||||
|
||||
return $class::getInstance($table, $value, $dbSettings);
|
||||
return $class::getInstance($table, $value, $dbSettingsCopy);
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ 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-type TOrmConfig=array{serializer?: SerializerType, enableCache?: bool, cacheTtl?: int}
|
||||
* @property array<string, TOrmConfig> $dbProperties
|
||||
*/
|
||||
trait DbPropertiesTrait
|
||||
{
|
||||
|
@ -4,12 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace danog\MadelineProto\Db;
|
||||
|
||||
use danog\MadelineProto\Settings\Database\DatabaseAbstract;
|
||||
use danog\MadelineProto\Settings\DatabaseAbstract;
|
||||
|
||||
interface DbType
|
||||
{
|
||||
/**
|
||||
* @param DatabaseAbstract $settings
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
@ -53,20 +53,6 @@ final class Postgres
|
||||
ENCODING utf8
|
||||
");
|
||||
}
|
||||
|
||||
$connection->query("
|
||||
CREATE OR REPLACE FUNCTION update_ts()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF row(NEW.*) IS DISTINCT FROM row(OLD.*) THEN
|
||||
NEW.ts = now();
|
||||
RETURN NEW;
|
||||
ELSE
|
||||
RETURN OLD;
|
||||
END IF;
|
||||
END;
|
||||
$$ language 'plpgsql'
|
||||
");
|
||||
$connection->close();
|
||||
} catch (Throwable $e) {
|
||||
Logger::log($e->getMessage(), Logger::ERROR);
|
||||
|
@ -5,10 +5,11 @@ declare(strict_types=1);
|
||||
namespace danog\MadelineProto\Db;
|
||||
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Settings\Database\DriverDatabaseAbstract;
|
||||
use danog\MadelineProto\Settings\Database\Memory;
|
||||
use danog\MadelineProto\SettingsAbstract;
|
||||
use danog\MadelineProto\Settings\Database\SerializerType;
|
||||
use danog\MadelineProto\Settings\DatabaseAbstract;
|
||||
use IteratorAggregate;
|
||||
use ReflectionClass;
|
||||
|
||||
use function Amp\async;
|
||||
use function Amp\Future\await;
|
||||
@ -27,6 +28,11 @@ use function Amp\Future\await;
|
||||
abstract class DriverArray implements DbArray, IteratorAggregate
|
||||
{
|
||||
protected string $table;
|
||||
/** @var callable(mixed): mixed */
|
||||
protected $serializer;
|
||||
/** @var callable(string): mixed */
|
||||
protected $deserializer;
|
||||
protected DriverDatabaseAbstract $dbSettings;
|
||||
|
||||
use ArrayCacheTrait;
|
||||
|
||||
@ -43,7 +49,7 @@ abstract class DriverArray implements DbArray, IteratorAggregate
|
||||
/**
|
||||
* Rename table.
|
||||
*/
|
||||
abstract protected function renameTable(string $from, string $to): void;
|
||||
abstract protected function moveDataFromTableToTable(string $from, string $to): void;
|
||||
|
||||
/**
|
||||
* Get the value of table.
|
||||
@ -74,15 +80,15 @@ 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();
|
||||
$instance->setTable($table);
|
||||
|
||||
/** @psalm-suppress UndefinedPropertyAssignment */
|
||||
$instance->dbSettings = $settings;
|
||||
$instance->setCacheTtl($settings->getCacheTtl());
|
||||
$instance->setSerializer($settings->getSerializer());
|
||||
|
||||
$instance->startCacheCleanupLoop();
|
||||
|
||||
@ -100,10 +106,25 @@ 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.
|
||||
*
|
||||
* Otherwise, simply change name of table in new database to match old table name.
|
||||
* If the new db has a temporary table name, change its table name to match the old table name.
|
||||
* Otherwise rename table of old database.
|
||||
*
|
||||
* @param self $new New db
|
||||
* @param DbArray|array|null $old Old db
|
||||
@ -114,7 +135,7 @@ abstract class DriverArray implements DbArray, IteratorAggregate
|
||||
if ($old->getTable() !== $new->getTable() &&
|
||||
!\str_starts_with($new->getTable(), 'tmp')
|
||||
) {
|
||||
$new->renameTable($old->getTable(), $new->getTable());
|
||||
$new->moveDataFromTableToTable($old->getTable(), $new->getTable());
|
||||
} else {
|
||||
$new->setTable($old->getTable());
|
||||
}
|
||||
@ -168,19 +189,6 @@ abstract class DriverArray implements DbArray, IteratorAggregate
|
||||
return ['table', 'dbSettings'];
|
||||
}
|
||||
|
||||
public function __wakeup(): void
|
||||
{
|
||||
if (isset($this->settings) && \is_array($this->settings)) {
|
||||
$clazz = (new ReflectionClass($this))->getProperty('dbSettings')->getType()->getName();
|
||||
/**
|
||||
* @var SettingsAbstract
|
||||
* @psalm-suppress UndefinedThisPropertyAssignment
|
||||
*/
|
||||
$this->dbSettings = new $clazz;
|
||||
$this->dbSettings->mergeArray($this->settings);
|
||||
unset($this->settings);
|
||||
}
|
||||
}
|
||||
final public function offsetExists($index): bool
|
||||
{
|
||||
return $this->isset($index);
|
||||
|
@ -28,7 +28,7 @@ final class MemoryArray extends ArrayIterator implements DbArray
|
||||
/**
|
||||
* @param Memory $settings
|
||||
*/
|
||||
public static function getInstance(string $table, $previous, $settings): static
|
||||
public static function getInstance(string $table, DbType|array|null $previous, $settings): static
|
||||
{
|
||||
if ($previous instanceof MemoryArray) {
|
||||
return $previous;
|
||||
|
@ -22,11 +22,6 @@ use PDO;
|
||||
*/
|
||||
class MysqlArray extends SqlArray
|
||||
{
|
||||
protected DatabaseMysql $dbSettings;
|
||||
|
||||
// Legacy
|
||||
protected array $settings;
|
||||
|
||||
/**
|
||||
* Initialize on startup.
|
||||
*/
|
||||
@ -108,7 +103,7 @@ class MysqlArray extends SqlArray
|
||||
");
|
||||
}
|
||||
|
||||
protected function renameTable(string $from, string $to): void
|
||||
protected function moveDataFromTableToTable(string $from, string $to): void
|
||||
{
|
||||
Logger::log("Moving data from {$from} to {$to}", Logger::WARNING);
|
||||
|
||||
|
@ -4,11 +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\Postgres as DatabasePostgres;
|
||||
use danog\MadelineProto\Settings\Database\SerializerType;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
@ -21,11 +23,6 @@ use PDO;
|
||||
*/
|
||||
class PostgresArray extends SqlArray
|
||||
{
|
||||
public DatabasePostgres $dbSettings;
|
||||
|
||||
// Legacy
|
||||
protected array $settings;
|
||||
|
||||
/**
|
||||
* Prepare statements.
|
||||
*
|
||||
@ -90,16 +87,21 @@ class PostgresArray extends SqlArray
|
||||
}
|
||||
}
|
||||
|
||||
protected function getValue(string $value): mixed
|
||||
protected function setSerializer(SerializerType $serializer): void
|
||||
{
|
||||
return \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 setValue(mixed $value): string
|
||||
{
|
||||
return \bin2hex(\serialize($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create table for property.
|
||||
*/
|
||||
@ -111,12 +113,12 @@ 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
|
||||
);
|
||||
");
|
||||
}
|
||||
|
||||
protected function renameTable(string $from, string $to): void
|
||||
protected function moveDataFromTableToTable(string $from, string $to): void
|
||||
{
|
||||
Logger::log("Moving data from {$from} to {$to}", Logger::WARNING);
|
||||
|
||||
|
@ -21,12 +21,8 @@ use danog\MadelineProto\Settings\Database\Redis as DatabaseRedis;
|
||||
*/
|
||||
class RedisArray extends DriverArray
|
||||
{
|
||||
protected DatabaseRedis $dbSettings;
|
||||
private RedisRedis $db;
|
||||
|
||||
// Legacy
|
||||
protected array $settings;
|
||||
|
||||
/**
|
||||
* Initialize on startup.
|
||||
*/
|
||||
@ -38,7 +34,7 @@ class RedisArray extends DriverArray
|
||||
{
|
||||
}
|
||||
|
||||
protected function renameTable(string $from, string $to): void
|
||||
protected function moveDataFromTableToTable(string $from, string $to): void
|
||||
{
|
||||
Logger::log("Moving data from {$from} to {$to}", Logger::WARNING);
|
||||
$from = "va:$from";
|
||||
@ -86,7 +82,7 @@ class RedisArray extends DriverArray
|
||||
|
||||
$this->setCache($index, $value);
|
||||
|
||||
$this->db->set($this->rKey($index), \serialize($value));
|
||||
$this->db->set($this->rKey($index), ($this->serializer)($value));
|
||||
$this->setCache($index, $value);
|
||||
}
|
||||
|
||||
@ -99,7 +95,7 @@ class RedisArray extends DriverArray
|
||||
|
||||
$value = $this->db->get($this->rKey($offset));
|
||||
|
||||
if ($value !== null && $value = \unserialize($value)) {
|
||||
if ($value !== null && $value = ($this->deserializer)($value)) {
|
||||
$this->setCache($offset, $value);
|
||||
}
|
||||
|
||||
@ -124,7 +120,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,8 +8,6 @@ use Amp\Sql\Pool;
|
||||
use Amp\Sql\Result;
|
||||
use PDO;
|
||||
|
||||
use function serialize;
|
||||
|
||||
/**
|
||||
* Generic SQL database backend.
|
||||
*
|
||||
@ -59,21 +57,6 @@ abstract class SqlArray extends DriverArray
|
||||
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Deserialize retrieved value.
|
||||
*/
|
||||
protected function getValue(string $value): mixed
|
||||
{
|
||||
return \unserialize($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize retrieved value.
|
||||
*/
|
||||
protected function setValue(mixed $value): string
|
||||
{
|
||||
return \serialize($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get iterator.
|
||||
@ -83,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->getValue($value);
|
||||
yield $key => ($this->deserializer)($value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,7 +86,7 @@ abstract class SqlArray extends DriverArray
|
||||
return null;
|
||||
}
|
||||
|
||||
$value = $this->getValue($row['value']);
|
||||
$value = ($this->deserializer)($row['value']);
|
||||
$this->setCache($key, $value);
|
||||
|
||||
return $value;
|
||||
@ -122,7 +105,7 @@ abstract class SqlArray extends DriverArray
|
||||
$this->queries[self::SQL_SET],
|
||||
[
|
||||
'index' => $key,
|
||||
'value' => $this->setValue($value),
|
||||
'value' => ($this->serializer)($value),
|
||||
],
|
||||
);
|
||||
$this->setCache($key, $value);
|
||||
|
@ -48,7 +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\Memory;
|
||||
use danog\MadelineProto\Settings\Database\SerializerType;
|
||||
use danog\MadelineProto\Settings\TLSchema;
|
||||
use danog\MadelineProto\TL\Conversion\BotAPI;
|
||||
use danog\MadelineProto\TL\Conversion\BotAPIFiles;
|
||||
@ -139,7 +139,7 @@ final class MTProto implements TLCallback, LoggerGetter
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const RELEASE = '7.0';
|
||||
const RELEASE = '8.0';
|
||||
/**
|
||||
* We're not logged in.
|
||||
*
|
||||
@ -502,15 +502,12 @@ final class MTProto implements TLCallback, LoggerGetter
|
||||
* @see DbPropertiesFactory
|
||||
*/
|
||||
protected static array $dbProperties = [
|
||||
'chats' => 'array',
|
||||
'full_chats' => 'array',
|
||||
'sponsoredMessages' => 'array',
|
||||
'channelParticipants' => 'array',
|
||||
'usernames' => 'array',
|
||||
'session' => [
|
||||
'type' => 'array',
|
||||
'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],
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -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' => 'array',
|
||||
'db' => ['serializer' => SerializerType::SERIALIZE],
|
||||
];
|
||||
|
||||
public function __construct(MTProto $API)
|
||||
|
@ -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' => 'array',
|
||||
'db' => ['serializer' => SerializerType::SERIALIZE],
|
||||
];
|
||||
|
||||
public function __construct(private MTProto $API)
|
||||
|
@ -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);
|
||||
|
File diff suppressed because one or more lines are too long
@ -76,10 +76,10 @@ interface Payments
|
||||
/**
|
||||
* Generate an [invoice deep link](https://core.telegram.org/api/links#invoice-links).
|
||||
*
|
||||
* @param array $invoice_media Invoice @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
|
||||
* @param string|array $invoice_media Invoice @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
|
||||
* @return array{_: 'payments.exportedInvoice', url: string} @see https://docs.madelineproto.xyz/API_docs/types/payments.ExportedInvoice.html
|
||||
*/
|
||||
public function exportInvoice(array $invoice_media): array;
|
||||
public function exportInvoice(array|string $invoice_media): array;
|
||||
|
||||
/**
|
||||
* Informs server about a purchase made through the App Store: for official applications only.
|
||||
|
@ -30,6 +30,7 @@ use Amp\TimeoutException;
|
||||
use danog\MadelineProto\Db\DbPropertiesFactory;
|
||||
use danog\MadelineProto\Db\DriverArray;
|
||||
use danog\MadelineProto\Ipc\Server;
|
||||
use danog\MadelineProto\Settings\Database\SerializerType;
|
||||
use danog\MadelineProto\Settings\DatabaseAbstract;
|
||||
use Revolt\EventLoop;
|
||||
use Throwable;
|
||||
@ -50,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.
|
||||
@ -216,7 +215,7 @@ abstract class Serialization
|
||||
$unserialized = DbPropertiesFactory::get(
|
||||
$settings,
|
||||
$tableName,
|
||||
DbPropertiesFactory::TYPE_ARRAY,
|
||||
['serializer' => SerializerType::SERIALIZE],
|
||||
$unserialized,
|
||||
);
|
||||
} else {
|
||||
|
@ -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();
|
||||
|
@ -4,14 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace danog\MadelineProto\Settings\Database;
|
||||
|
||||
use danog\MadelineProto\Settings\DatabaseAbstract as SettingsDatabaseAbstract;
|
||||
|
||||
use function time;
|
||||
use danog\MadelineProto\Settings\DatabaseAbstract;
|
||||
|
||||
/**
|
||||
* Base class for database backends.
|
||||
*/
|
||||
abstract class DatabaseAbstract extends SettingsDatabaseAbstract
|
||||
abstract class DriverDatabaseAbstract extends DatabaseAbstract
|
||||
{
|
||||
/**
|
||||
* For how long to keep records in memory after last read, for cached backends.
|
||||
@ -22,6 +20,11 @@ abstract class DatabaseAbstract extends SettingsDatabaseAbstract
|
||||
*/
|
||||
protected string $password = '';
|
||||
|
||||
/**
|
||||
* Which serializer strategy to use by default.
|
||||
*/
|
||||
protected SerializerType $serializer = SerializerType::SERIALIZE;
|
||||
|
||||
public function mergeArray(array $settings): void
|
||||
{
|
||||
foreach (self::toCamel([
|
||||
@ -106,4 +109,14 @@ abstract class DatabaseAbstract extends SettingsDatabaseAbstract
|
||||
* Set database URI.
|
||||
*/
|
||||
abstract public function setUri(string $uri): self;
|
||||
|
||||
public function getSerializer(): SerializerType
|
||||
{
|
||||
return $this->serializer;
|
||||
}
|
||||
|
||||
public function setSerializer(SerializerType $serializer): void
|
||||
{
|
||||
$this->serializer = $serializer;
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ namespace danog\MadelineProto\Settings\Database;
|
||||
/**
|
||||
* Redis backend settings.
|
||||
*/
|
||||
final class Redis extends DatabaseAbstract
|
||||
final class Redis extends DriverDatabaseAbstract
|
||||
{
|
||||
/**
|
||||
* Database number.
|
||||
|
11
src/Settings/Database/SerializerType.php
Normal file
11
src/Settings/Database/SerializerType.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace danog\MadelineProto\Settings\Database;
|
||||
|
||||
enum SerializerType
|
||||
{
|
||||
case SERIALIZE;
|
||||
case IGBINARY;
|
||||
case JSON;
|
||||
case STRING;
|
||||
}
|
@ -7,7 +7,7 @@ namespace danog\MadelineProto\Settings\Database;
|
||||
/**
|
||||
* Generic db backend settings.
|
||||
*/
|
||||
abstract class SqlAbstract extends DatabaseAbstract
|
||||
abstract class SqlAbstract extends DriverDatabaseAbstract
|
||||
{
|
||||
/**
|
||||
* Database name.
|
||||
|
Loading…
Reference in New Issue
Block a user