1
0
mirror of https://github.com/danog/MadelineProto.git synced 2024-11-26 17:24:40 +01:00

New ORM API

This commit is contained in:
Daniil Gentili 2023-08-13 15:47:21 +02:00
parent 2b76934531
commit d7a2ef38c3
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
30 changed files with 396 additions and 593 deletions

View File

@ -45,10 +45,6 @@
<ArgumentTypeCoercion>
<code>$eventHandler</code>
</ArgumentTypeCoercion>
<DeprecatedMethod>
<code>loop</code>
<code>loop</code>
</DeprecatedMethod>
<PossiblyNullReference>
<code>getMessage</code>
<code>isInited</code>
@ -64,33 +60,6 @@
</RiskyCast>
</file>
<file src="src/AsyncTools.php">
<DeprecatedMethod>
<code>self::call($a)</code>
<code>self::call($b)</code>
<code>self::call($callable)</code>
<code>self::call($promise)</code>
<code>self::call($promise)</code>
<code>self::timeout($promise, $timeout)</code>
</DeprecatedMethod>
<DocblockTypeContradiction>
<code>$callable instanceof Generator</code>
</DocblockTypeContradiction>
<InvalidReturnStatement>
<code>$callable</code>
</InvalidReturnStatement>
<InvalidReturnType>
<code><![CDATA[Future<T>]]></code>
</InvalidReturnType>
<MissingClosureReturnType>
<code><![CDATA[fn ($v) => $v instanceof Generator ? self::consumeGenerator($v) : $v]]></code>
</MissingClosureReturnType>
<MissingReturnType>
<code>all</code>
<code>any</code>
<code>first</code>
<code>some</code>
<code>wait</code>
</MissingReturnType>
<RedundantConditionGivenDocblockType>
<code>\is_callable($callable)</code>
</RedundantConditionGivenDocblockType>
@ -227,18 +196,13 @@
<code><![CDATA[$this->permAuthKey =& $this->API->datacenter->getDataCenterConnection($dc)->permAuthKey]]></code>
</UnsupportedReferenceUsage>
</file>
<file src="src/Db/ArrayCacheTrait.php">
<MissingParamType>
<code>$value</code>
</MissingParamType>
<MissingReturnType>
<code>getCache</code>
</MissingReturnType>
</file>
<file src="src/Db/DbArray.php">
<ImplementedReturnTypeMismatch>
<code><![CDATA[\Traversable<TKey, TValue>]]></code>
</ImplementedReturnTypeMismatch>
<file src="src/Db/CacheContainer.php">
<ParadoxicalCondition>
<code><![CDATA[isset($this->ttl[$index])]]></code>
</ParadoxicalCondition>
<PropertyNotSetInConstructor>
<code>$cacheTtl</code>
</PropertyNotSetInConstructor>
</file>
<file src="src/Db/Driver/Mysql.php">
<ArgumentTypeCoercion>
@ -268,9 +232,6 @@
<code>$settings</code>
<code>$settings</code>
</ArgumentTypeCoercion>
<LessSpecificReturnStatement>
<code>$instance</code>
</LessSpecificReturnStatement>
<MissingClosureParamType>
<code>$v</code>
<code>$value</code>
@ -285,14 +246,6 @@
<code>$dbSettings</code>
<code>$dbSettings</code>
<code>$dbSettings</code>
<code>$dbSettings</code>
<code>$dbSettings</code>
<code>$dbSettings</code>
<code>$dbSettings</code>
<code>$deserializer</code>
<code>$deserializer</code>
<code>$deserializer</code>
<code>$deserializer</code>
<code>$deserializer</code>
<code>$deserializer</code>
<code>$deserializer</code>
@ -301,25 +254,14 @@
<code>$serializer</code>
<code>$serializer</code>
<code>$serializer</code>
<code>$serializer</code>
<code>$serializer</code>
<code>$serializer</code>
<code>$serializer</code>
<code>$table</code>
<code>$table</code>
<code>$table</code>
<code>$table</code>
<code>$table</code>
<code>$table</code>
<code>$table</code>
<code>$table</code>
</MissingConstructor>
<MoreSpecificReturnType>
<code>static</code>
</MoreSpecificReturnType>
<PossibleRawObjectIteration>
<RawObjectIteration>
<code>$old</code>
</PossibleRawObjectIteration>
</RawObjectIteration>
<UndefinedMethod>
<code>setSettings</code>
</UndefinedMethod>
@ -349,14 +291,8 @@
</InvalidDocblockParamName>
<MissingConstructor>
<code>$pdo</code>
<code>$pdo</code>
</MissingConstructor>
</file>
<file src="src/Db/NullCache/NullCacheTrait.php">
<MissingParamType>
<code>$value</code>
</MissingParamType>
</file>
<file src="src/Db/PostgresArray.php">
<MissingClosureParamType>
<code>$v</code>
@ -411,9 +347,6 @@
<code>$db</code>
<code>$db</code>
<code>$db</code>
<code>$db</code>
<code>$db</code>
<code>$db</code>
</MissingConstructor>
<PossiblyNullArrayAccess>
<code><![CDATA[$row->fetchRow()['count']]]></code>
@ -434,10 +367,6 @@
<ArgumentTypeCoercion>
<code>$class</code>
</ArgumentTypeCoercion>
<DeprecatedMethod>
<code><![CDATA[Tools::call($this->getReportPeers())]]></code>
<code><![CDATA[Tools::call($this->getReportPeers())]]></code>
</DeprecatedMethod>
<PossiblyInvalidArgument>
<code><![CDATA[$this->wrapper->getAPI()]]></code>
</PossiblyInvalidArgument>
@ -720,19 +649,6 @@
</PropertyTypeCoercion>
</file>
<file src="src/InternalDoc.php">
<DeprecatedMethod>
<code>\danog\MadelineProto\AsyncTools::after($a, $b)</code>
<code>\danog\MadelineProto\AsyncTools::all($promises)</code>
<code>\danog\MadelineProto\AsyncTools::any($promises)</code>
<code>\danog\MadelineProto\AsyncTools::call($promise)</code>
<code>\danog\MadelineProto\AsyncTools::first($promises)</code>
<code>\danog\MadelineProto\AsyncTools::some($promises)</code>
<code>\danog\MadelineProto\AsyncTools::timeout($promise, $timeout)</code>
<code>\danog\MadelineProto\AsyncTools::timeoutWithDefault($promise, $timeout, $default)</code>
<code>\danog\MadelineProto\AsyncTools::wait($promise)</code>
<code>loop</code>
<code>loop</code>
</DeprecatedMethod>
<InvalidReturnStatement>
<code><![CDATA[$this->wrapper->getAPI()->getEventHandler($class)]]></code>
</InvalidReturnStatement>
@ -745,19 +661,14 @@
<MissingReturnType>
<code>MTProtoToTd</code>
<code>MTProtoToTdcli</code>
<code>all</code>
<code>any</code>
<code>completePhoneLogin</code>
<code>downloadToCallable</code>
<code>downloadToDir</code>
<code>downloadToStream</code>
<code>end</code>
<code>first</code>
<code>loop</code>
<code>phoneLogin</code>
<code>requestCall</code>
<code>requestSecretChat</code>
<code>some</code>
<code>start</code>
<code>tdToTdcli</code>
<code>upload</code>
@ -766,7 +677,6 @@
<code>uploadFromStream</code>
<code>uploadFromTgfile</code>
<code>uploadFromUrl</code>
<code>wait</code>
</MissingReturnType>
<PossiblyInvalidPropertyAssignmentValue>
<code><![CDATA[$this->account ??= new \danog\MadelineProto\Namespace\AbstractAPI('account')]]></code>
@ -789,9 +699,6 @@
<code><![CDATA[$this->upload ??= new \danog\MadelineProto\Namespace\AbstractAPI('upload')]]></code>
<code><![CDATA[$this->users ??= new \danog\MadelineProto\Namespace\AbstractAPI('users')]]></code>
</PossiblyInvalidPropertyAssignmentValue>
<PossiblyNullArgument>
<code>$callback</code>
</PossiblyNullArgument>
<PossiblyUndefinedMethod>
<code>setWrapper</code>
<code>setWrapper</code>
@ -822,7 +729,6 @@
<code>downloadToCallable</code>
<code>downloadToDir</code>
<code>downloadToFile</code>
<code>loop</code>
<code>methodCallAsyncRead</code>
<code>uploadFromCallable</code>
<code>uploadFromTgfile</code>
@ -1295,7 +1201,6 @@
<code><![CDATA[$this->chats]]></code>
</RawObjectIteration>
<RedundantCondition>
<code><![CDATA[$this->minDatabase?->sync()]]></code>
<code><![CDATA[isset($this->wrapper) && $this->isInited()]]></code>
<code><![CDATA[isset($this->wrapper) && isset(self::$references[$this->getSessionName()])]]></code>
</RedundantCondition>
@ -1305,9 +1210,6 @@
<code>\is_int($dc_id)</code>
<code><![CDATA[isset($this->datacenter)]]></code>
</RedundantConditionGivenDocblockType>
<TypeDoesNotContainNull>
<code><![CDATA[$this->minDatabase]]></code>
</TypeDoesNotContainNull>
<UnsupportedReferenceUsage>
<code><![CDATA[Lang::$current_lang =& Lang::$lang[$this->settings->getAppInfo()->getLangCode()]]]></code>
</UnsupportedReferenceUsage>
@ -1561,7 +1463,7 @@
<code>$db</code>
</PropertyNotSetInConstructor>
<RawObjectIteration>
<code><![CDATA[$this->API->chats]]></code>
<code><![CDATA[$this->db]]></code>
<code><![CDATA[$this->db]]></code>
</RawObjectIteration>
</file>
@ -1717,15 +1619,11 @@
</PossiblyUndefinedArrayOffset>
</file>
<file src="src/MyTelegramOrgWrapper.php">
<DeprecatedMethod>
<code>Tools::wait($callable())</code>
</DeprecatedMethod>
<MissingReturnType>
<code>completeLogin</code>
<code>createApp</code>
<code>getApp</code>
<code>hasApp</code>
<code>loop</code>
</MissingReturnType>
<PossiblyNullArgument>
<code><![CDATA[$this->settings]]></code>
@ -1832,9 +1730,6 @@
<code>$ipcSocket</code>
<code>$ipcSocket</code>
</NullableReturnStatement>
<PossiblyUndefinedMethod>
<code>$unserialized</code>
</PossiblyUndefinedMethod>
</file>
<file src="src/SessionPaths.php">
<LessSpecificReturnStatement>
@ -2454,13 +2349,4 @@
<code>$result</code>
</PossiblyUndefinedVariable>
</file>
<file src="src/loop.php">
<MissingClosureReturnType>
<code>function ($_) use ($callback) {</code>
</MissingClosureReturnType>
<PossiblyNullFunctionCall>
<code>$callback()</code>
<code>$callback()</code>
</PossiblyNullFunctionCall>
</file>
</files>

View File

@ -1,129 +0,0 @@
<?php declare(strict_types=1);
/**
* 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.
* MadelineProto 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/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\Db;
use danog\MadelineProto\Logger;
use Revolt\EventLoop;
/**
* Array caching trait.
*
* @internal
*/
trait ArrayCacheTrait
{
/**
* @var array<mixed>
*/
private array $cache = [];
/**
* @var array<int>
*/
private array $ttl = [];
private int $cacheTtl = 5 * 60;
/**
* Cache cleanup watcher ID.
*/
private ?string $cacheCleanupId = null;
protected function setCacheTtl(int $ttl): void
{
$this->cacheTtl = $ttl;
}
protected function getCache(string $key)
{
$this->ttl[$key] = \time() + $this->cacheTtl;
return $this->cache[$key];
}
protected function hasCache(string $key): bool
{
return isset($this->ttl[$key]);
}
/**
* Save item in cache.
*/
protected function setCache(string $key, $value): void
{
$this->cache[$key] = $value;
$this->ttl[$key] = \time() + $this->cacheTtl;
}
/**
* Remove key from cache.
*/
protected function unsetCache(string $key): void
{
unset($this->cache[$key], $this->ttl[$key]);
}
protected function startCacheCleanupLoop(): void
{
$this->cacheCleanupId = EventLoop::repeat(
\max(1, $this->cacheTtl / 5),
fn () => $this->cleanupCache(),
);
}
protected function stopCacheCleanupLoop(): void
{
if ($this->cacheCleanupId) {
EventLoop::cancel($this->cacheCleanupId);
$this->cacheCleanupId = null;
}
}
protected function clearCache(): void
{
$this->cache = [];
$this->ttl = [];
}
/**
* Remove all keys from cache.
*/
private function cleanupCache(): void
{
$newValues = [];
$newTtl = [];
$now = \time();
$oldCount = 0;
foreach ($this->ttl as $key => $ttl) {
if ($ttl < $now) {
$oldCount++;
} else {
$newTtl[$key] = $this->ttl[$key];
$newValues[$key] = $this->cache[$key];
}
}
$this->ttl = $newTtl;
$this->cache = $newValues;
Logger::log(
\sprintf(
'cache for table: %s; keys left: %s; keys removed: %s',
(string) $this,
\count($this->cache),
$oldCount,
),
Logger::VERBOSE,
);
}
}

148
src/Db/CacheContainer.php Normal file
View File

@ -0,0 +1,148 @@
<?php declare(strict_types=1);
/**
* 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 private License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* MadelineProto 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 private License for more details.
* You should have received a copy of the GNU General private License along with MadelineProto.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\Db;
use Amp\Sync\LocalMutex;
use Revolt\EventLoop;
use Traversable;
/** @internal */
final class CacheContainer
{
/**
* @var array<mixed>
*/
private array $cache = [];
/**
* @var array<int|true>
*/
private array $ttl = [];
private int $cacheTtl;
/**
* Cache cleanup watcher ID.
*/
private ?string $cacheCleanupId = null;
private LocalMutex $mutex;
public function __construct(
public DbType $inner
) {
$this->mutex = new LocalMutex;
}
public function __sleep()
{
$this->flushCache();
return ['cache', 'ttl', 'inner'];
}
public function startCacheCleanupLoop(int $cacheTtl): void
{
$this->cacheTtl = $cacheTtl;
if ($this->cacheCleanupId) {
EventLoop::cancel($this->cacheCleanupId);
}
$this->cacheCleanupId = EventLoop::repeat(
\max(1, $this->cacheTtl / 5),
fn () => $this->flushCache(),
);
}
public function stopCacheCleanupLoop(): void
{
if ($this->cacheCleanupId) {
EventLoop::cancel($this->cacheCleanupId);
$this->cacheCleanupId = null;
}
}
public function get(string|int $index): mixed
{
if (isset($this->ttl[$index])) {
return $this->cache[$index];
}
$result = $this->inner->offsetGet($index);
if (isset($this->ttl[$index])) {
return $this->cache[$index];
}
$this->ttl[$index] = \time() + $this->cacheTtl;
$this->cache[$index] = $result;
return $result;
}
public function set(string|int $key, mixed $value): void
{
$this->cache[$key] = $value;
$this->ttl[$key] = true;
}
public function getIterator(): Traversable
{
$this->flushCache();
return $this->inner->getIterator();
}
public function count(): int
{
$this->flushCache();
return $this->inner->count();
}
public function clear(): void
{
$lock = $this->mutex->acquire();
$this->cache = [];
$this->ttl = [];
$lock->release();
$this->inner->clear();
}
/**
* Flush all flushable keys.
*/
public function flushCache(): void
{
$lock = $this->mutex->acquire();
try {
$newValues = [];
$newTtl = [];
$now = \time();
foreach ($this->ttl as $key => &$ttl) {
if ($ttl === true) {
if ($this->cache[$key] === null) {
$this->inner->unset($key);
} else {
$this->inner->set($key, $this->cache[$key]);
}
$ttl = \time() + $this->cacheTtl;
} elseif ($ttl > $now) {
$newTtl[$key] = $this->ttl[$key];
$newValues[$key] = $this->cache[$key];
}
}
$this->ttl = $newTtl;
$this->cache = $newValues;
} finally {
$lock->release();
}
}
}

100
src/Db/CachedArray.php Normal file
View File

@ -0,0 +1,100 @@
<?php declare(strict_types=1);
/**
* 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.
* MadelineProto 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/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\Db;
use danog\MadelineProto\Settings\Database\DriverDatabaseAbstract;
use danog\MadelineProto\Settings\DatabaseAbstract;
use Traversable;
/**
* Array caching proxy.
*
* @internal
*
* @template TKey as array-key
* @template TValue
*
* @implements DbArray<TKey, TValue>
*/
final class CachedArray implements DbArray
{
use DbArrayTrait;
private CacheContainer $cache;
/**
* Get instance.
*/
public static function getInstance(string $table, DbType|null $previous, DatabaseAbstract $settings): DbType
{
$new = $settings->getDriverClass();
if ($previous === null) {
$previous = new self($new::getInstance($table, null, $settings));
} elseif ($previous instanceof self) {
$previous->cache->inner = $new::getInstance($table, $previous->cache->inner, $settings);
} else {
$previous = new self($new::getInstance($table, $previous, $settings));
}
if ($previous->cache->inner instanceof MemoryArray) {
$previous->cache->flushCache();
return $previous->cache->inner;
}
\assert($settings instanceof DriverDatabaseAbstract);
$previous->cache->startCacheCleanupLoop($settings->getCacheTtl());
return $previous;
}
public function __construct(DbType $inner)
{
$this->cache = new CacheContainer($inner);
}
public function __destruct()
{
$this->cache->stopCacheCleanupLoop();
}
public function count(): int
{
return $this->cache->count();
}
public function clear(): void
{
$this->cache->clear();
}
public function offsetGet(mixed $index): mixed
{
return $this->cache->get($index);
}
public function set(string|int $key, mixed $value): void
{
$this->cache->set($key, $value);
}
public function unset(string|int $key): void
{
$this->cache->set($key, null);
}
public function getIterator(): Traversable
{
return $this->cache->getIterator();
}
}

View File

@ -17,7 +17,6 @@
namespace danog\MadelineProto\Db;
use ArrayAccess;
use Countable;
/**
* DB array interface.
@ -26,40 +25,10 @@ use Countable;
* @template TValue
*
* @extends ArrayAccess<TKey, TValue>
* @extends DbType<TKey, TValue>
*/
interface DbArray extends DbType, ArrayAccess, Countable
interface DbArray extends DbType, ArrayAccess
{
/**
* Get Array copy.
*
* @psalm-return array<TKey, TValue>
*/
public function getArrayCopy(): array;
/**
* Check if element is set.
*
* @param TKey $key
*/
public function isset(string|int $key): bool;
/**
* Unset element.
*
* @param TKey $key
*/
public function unset(string|int $key): void;
/**
* Set element.
*
* @param TKey $key
* @param TValue $value
*/
public function set(string|int $key, mixed $value): void;
/**
* Get element.
*
* @param TKey $index
*/
public function offsetGet(mixed $index): mixed;
/**
* Set element.
*
@ -79,13 +48,9 @@ interface DbArray extends DbType, ArrayAccess, Countable
*/
public function offsetExists(mixed $index): bool;
/**
* Clear all elements.
*/
public function clear(): void;
/**
* Get iterator.
* Get Array copy.
*
* @return \Traversable<TKey, TValue>
* @psalm-return array<TKey, TValue>
*/
public function getIterator(): \Traversable;
public function getArrayCopy(): array;
}

View File

@ -14,52 +14,45 @@
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\Db\NullCache;
use RuntimeException;
namespace danog\MadelineProto\Db;
/**
* Trait that disables database caching.
*
* @internal
* DB array trait.
*/
trait NullCacheTrait
trait DbArrayTrait
{
protected function setCacheTtl(int $ttl): void
/**
* Check if key isset.
*
* @param mixed $key
* @return bool true if the offset exists, otherwise false
*/
final public function isset(string|int $key): bool
{
}
protected function hasCache(string $key): bool
{
return false;
return $this->offsetGet($key) !== null;
}
protected function getCache(string $key): void
/** @param string|int $index */
final public function offsetExists(mixed $index): bool
{
throw new RuntimeException('Not implemented!');
return $this->isset($index);
}
final public function offsetSet(mixed $index, mixed $value): void
{
$this->set($index, $value);
}
final public function offsetUnset(mixed $index): void
{
$this->unset($index);
}
/**
* Save item in cache.
* Get array copy.
*/
protected function setCache(string $key, $value): void
{
}
/**
* Remove key from cache.
*/
protected function unsetCache(string $key): void
{
}
protected function clearCache(): void
{
}
protected function startCacheCleanupLoop(): void
{
}
protected function stopCacheCleanupLoop(): void
final public function getArrayCopy(): array
{
return \iterator_to_array($this->getIterator());
}
}

View File

@ -18,13 +18,8 @@ namespace danog\MadelineProto\Db;
use danog\MadelineProto\Magic;
use danog\MadelineProto\Settings\Database\DriverDatabaseAbstract;
use danog\MadelineProto\Settings\Database\Memory;
use danog\MadelineProto\Settings\Database\Mysql;
use danog\MadelineProto\Settings\Database\Postgres;
use danog\MadelineProto\Settings\Database\Redis;
use danog\MadelineProto\Settings\Database\SerializerType;
use danog\MadelineProto\Settings\DatabaseAbstract;
use InvalidArgumentException;
/**
* This factory class initializes the correct database backend for MadelineProto.
@ -35,30 +30,27 @@ final class DbPropertiesFactory
{
/**
* @param array{serializer?: SerializerType, enableCache?: bool, cacheTtl?: int, innerMadelineProto?: bool, innerMadelineProtoSerializer?: SerializerType}|'array' $config
* @return DbType
* @internal
* @uses \danog\MadelineProto\Db\MemoryArray
* @uses \danog\MadelineProto\Db\MysqlArray
* @uses \danog\MadelineProto\Db\PostgresArray
* @uses \danog\MadelineProto\Db\RedisArray
*/
public static function get(DatabaseAbstract $dbSettings, string $table, string|array $config, ?DbType $value = null)
public static function get(DatabaseAbstract $dbSettings, string $table, string|array $config, ?DbType $value = null): DbArray
{
// Legacy
if ($config === 'array') {
$config = [];
}
$dbSettingsCopy = clone $dbSettings;
$class = __NAMESPACE__;
$dbSettings = clone $dbSettings;
if ($dbSettingsCopy instanceof DriverDatabaseAbstract) {
if ($dbSettings instanceof DriverDatabaseAbstract) {
$config = \array_merge([
'serializer' => $dbSettingsCopy->getSerializer() ?? (
'serializer' => $dbSettings->getSerializer() ?? (
Magic::$can_use_igbinary ? SerializerType::IGBINARY : SerializerType::SERIALIZE
),
'innerMadelineProto' => false,
'enableCache' => true,
'cacheTtl' => $dbSettingsCopy->getCacheTtl(),
'cacheTtl' => $dbSettings->getCacheTtl(),
], $config);
if ($config['innerMadelineProto']
@ -73,31 +65,12 @@ final class DbPropertiesFactory
);
}
$class = $dbSettings instanceof DriverDatabaseAbstract && (!($config['enableCache'] ?? true) || !$config['cacheTtl'])
? __NAMESPACE__ . '\\NullCache'
: __NAMESPACE__;
$dbSettingsCopy->setSerializer($config['serializer']);
$dbSettingsCopy->setCacheTtl($config['cacheTtl']);
$dbSettings->setSerializer($config['serializer']);
$dbSettings->setCacheTtl($config['cacheTtl']);
}
switch (true) {
case $dbSettings instanceof Memory:
$class .= '\\MemoryArray';
break;
case $dbSettings instanceof Mysql:
$class .= '\\MysqlArray';
break;
case $dbSettings instanceof Postgres:
$class .= '\\PostgresArrayBytea';
break;
case $dbSettings instanceof Redis:
$class .= '\\RedisArray';
break;
default:
throw new InvalidArgumentException('Unknown dbType: ' . $dbSettings::class);
}
/** @var DbType $class */
return $class::getInstance($table, $value, $dbSettingsCopy);
$result = CachedArray::getInstance($table, $value, $dbSettings);
\assert($result instanceof DbArray);
return $result;
}
}

View File

@ -16,9 +16,55 @@
namespace danog\MadelineProto\Db;
use Countable;
use danog\MadelineProto\Settings\DatabaseAbstract;
interface DbType
/**
* DB type interface.
*
* @template TKey as array-key
* @template TValue
*/
interface DbType extends Countable
{
public static function getInstance(string $table, DbType|array|null $previous, DatabaseAbstract $settings): static;
/**
* Check if element is set.
*
* @param TKey $key
*/
public function isset(string|int $key): bool;
/**
* Unset element.
*
* @param TKey $key
*/
public function unset(string|int $key): void;
/**
* Set element.
*
* @param TKey $key
* @param TValue $value
*/
public function set(string|int $key, mixed $value): void;
/**
* Get element.
*
* @param TKey $index
*/
public function offsetGet(mixed $index): mixed;
/**
* Clear all elements.
*/
public function clear(): void;
/**
* Get iterator.
*
* @return \Traversable<TKey, TValue>
*/
public function getIterator(): \Traversable;
/**
* Get instance.
*/
public static function getInstance(string $table, DbType|null $previous, DatabaseAbstract $settings): self;
}

View File

@ -19,10 +19,8 @@ namespace danog\MadelineProto\Db;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;
use danog\MadelineProto\Settings\Database\DriverDatabaseAbstract;
use danog\MadelineProto\Settings\Database\Memory;
use danog\MadelineProto\Settings\Database\SerializerType;
use danog\MadelineProto\Settings\DatabaseAbstract;
use IteratorAggregate;
use function Amp\async;
use function Amp\Future\await;
@ -35,10 +33,9 @@ use function Amp\Future\await;
* @template TKey as array-key
* @template TValue
*
* @implements IteratorAggregate<TKey, TValue>
* @implements DbArray<TKey, TValue>
* @implements DbType<TKey, TValue>
*/
abstract class DriverArray implements DbArray, IteratorAggregate
abstract class DriverArray implements DbType
{
protected string $table;
/** @var callable(mixed): mixed */
@ -47,8 +44,6 @@ abstract class DriverArray implements DbArray, IteratorAggregate
protected $deserializer;
protected DriverDatabaseAbstract $dbSettings;
use ArrayCacheTrait;
/**
* Initialize on startup.
*/
@ -82,13 +77,7 @@ abstract class DriverArray implements DbArray, IteratorAggregate
return $this;
}
/**
* Check if key isset.
*
* @param mixed $key
* @return bool true if the offset exists, otherwise false
*/
public function isset(string|int $key): bool
final public function isset(string|int $key): bool
{
return $this->offsetGet($key) !== null;
}
@ -96,7 +85,6 @@ abstract class DriverArray implements DbArray, IteratorAggregate
private function setSettings(DriverDatabaseAbstract $settings): void
{
$this->dbSettings = $settings;
$this->setCacheTtl($settings->getCacheTtl());
$this->setSerializer($settings->getSerializer() ?? (
Magic::$can_use_igbinary ? SerializerType::IGBINARY : SerializerType::SERIALIZE
));
@ -112,7 +100,7 @@ abstract class DriverArray implements DbArray, IteratorAggregate
}
}
public static function getInstance(string $table, DbType|array|null $previous, DatabaseAbstract $settings): static
public static function getInstance(string $table, DbType|null $previous, DatabaseAbstract $settings): DbType
{
/** @var MysqlArray|PostgresArray|RedisArray */
$instance = new static();
@ -122,8 +110,6 @@ abstract class DriverArray implements DbArray, IteratorAggregate
$instance->setSettings($settings);
$instance->startCacheCleanupLoop();
$instance->initConnection($settings);
$instance->prepareTable();
@ -165,14 +151,11 @@ abstract class DriverArray implements DbArray, IteratorAggregate
SerializerType::STRING => fn ($v) => $v,
};
}
private static function migrateDataToDb(self $new, DbArray|array|null $old): void
private static function migrateDataToDb(self $new, DbArray|null $old): void
{
$oldName = self::getMigrationName($old);
$newName = self::getMigrationName($new);
if (!empty($old) && $oldName !== $newName) {
if (!$old instanceof DbArray) {
$old = MemoryArray::getInstance('', $old, new Memory);
}
Logger::log("Converting $oldName to $newName", Logger::ERROR);
$counter = 0;
@ -186,7 +169,6 @@ abstract class DriverArray implements DbArray, IteratorAggregate
$promises = [];
Logger::log("Loading data to table {$newName}: $counter/$total", Logger::WARNING);
}
$new->clearCache();
}
if (self::getMigrationName($new, false) !== self::getMigrationName($old, false)) {
Logger::log("Dropping data from table {$oldName}", Logger::WARNING);
@ -196,11 +178,6 @@ abstract class DriverArray implements DbArray, IteratorAggregate
}
}
public function __destruct()
{
$this->stopCacheCleanupLoop();
}
/**
* Get the value of table.
*/
@ -216,30 +193,6 @@ abstract class DriverArray implements DbArray, IteratorAggregate
{
return ['table', 'dbSettings'];
}
final public function offsetExists($index): bool
{
return $this->isset($index);
}
final public function offsetSet(mixed $index, mixed $value): void
{
$this->set($index, $value);
}
final public function offsetUnset(mixed $index): void
{
$this->unset($index);
}
/**
* Get array copy.
*/
public function getArrayCopy(): array
{
return \iterator_to_array($this->getIterator());
}
private static function getMigrationName(DbType|array|null $instance, bool $include_serialization_type = true): ?string
{
if ($instance === null) {

View File

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

View File

@ -33,7 +33,7 @@ use PDO;
* @template TValue
* @extends SqlArray<TKey, TValue>
*/
class MysqlArray extends SqlArray
final class MysqlArray extends SqlArray
{
// We're forced to use quoting (just like PDO does internally when using prepares) because native MySQL prepares are extremely slow.
protected PDO $pdo;

View File

@ -1,34 +0,0 @@
<?php declare(strict_types=1);
/**
* 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.
* MadelineProto 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/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\Db\NullCache;
use danog\MadelineProto\Db\MysqlArray as DbMysqlArray;
/**
* MySQL database backend, no caching.
*
* @internal
*
* @template TKey as array-key
* @template TValue
*
* @extends DbMysqlArray<TKey, TValue>
*/
final class MysqlArray extends DbMysqlArray
{
use NullCacheTrait;
}

View File

@ -1,33 +0,0 @@
<?php declare(strict_types=1);
/**
* 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.
* MadelineProto 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/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\Db\NullCache;
use danog\MadelineProto\Db\PostgresArray as DbPostgresArray;
/**
* Postgres database backend, no caching.
*
* @template TKey as array-key
* @template TValue
*
* @extends DbPostgresArray<TKey, TValue>
* @internal
*/
final class PostgresArray extends DbPostgresArray
{
use NullCacheTrait;
}

View File

@ -1,33 +0,0 @@
<?php declare(strict_types=1);
/**
* 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.
* MadelineProto 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/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\Db\NullCache;
use danog\MadelineProto\Db\PostgresArrayBytea as DbPostgresArrayBytea;
/**
* Postgres database backend, no caching.
*
* @template TKey as array-key
* @template TValue
*
* @extends DbPostgresArrayBytea<TKey, TValue>
* @internal
*/
final class PostgresArrayBytea extends DbPostgresArrayBytea
{
use NullCacheTrait;
}

View File

@ -1,34 +0,0 @@
<?php declare(strict_types=1);
/**
* 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.
* MadelineProto 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/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\Db\NullCache;
use danog\MadelineProto\Db\RedisArray as DbRedisArray;
/**
* Redis database backend, no caching.
*
* @internal
*
* @template TKey as array-key
* @template TValue
*
* @extends DbRedisArray<TKey, TValue>
*/
final class RedisArray extends DbRedisArray
{
use NullCacheTrait;
}

View File

@ -29,7 +29,7 @@ use danog\MadelineProto\Settings\Database\SerializerType;
* @template TValue
* @extends PostgresArrayBytea<TKey, TValue>
*/
class PostgresArray extends PostgresArrayBytea
final class PostgresArray extends PostgresArrayBytea
{
/**
* Prepare statements.

View File

@ -30,7 +30,7 @@ use danog\MadelineProto\Settings\Database\Redis as DatabaseRedis;
* @template TValue
* @extends DriverArray<TKey, TValue>
*/
class RedisArray extends DriverArray
final class RedisArray extends DriverArray
{
private RedisRedis $db;
@ -87,27 +87,17 @@ class RedisArray extends DriverArray
}
public function set(string|int $key, mixed $value): void
{
if ($this->hasCache($key) && $this->getCache($key) === $value) {
return;
}
$this->setCache($key, $value);
$this->db->set($this->rKey($key), ($this->serializer)($value));
$this->setCache($key, $value);
}
public function offsetGet(mixed $offset): mixed
{
$offset = (string) $offset;
if ($this->hasCache($offset)) {
return $this->getCache($offset);
}
$value = $this->db->get($this->rKey($offset));
if ($value !== null && $value = ($this->deserializer)($value)) {
$this->setCache($offset, $value);
if ($value !== null) {
$value = ($this->deserializer)($value);
}
return $value;
@ -115,8 +105,6 @@ class RedisArray extends DriverArray
public function unset(string|int $key): void
{
$this->unsetCache($key);
$this->db->delete($this->rkey($key));
}
@ -152,7 +140,6 @@ class RedisArray extends DriverArray
*/
public function clear(): void
{
$this->clearCache();
$request = $this->db->scan($this->itKey());
$keys = [];

View File

@ -82,9 +82,6 @@ abstract class SqlArray extends DriverArray
public function offsetGet(mixed $key): mixed
{
$key = (string) $key;
if ($this->hasCache($key)) {
return $this->getCache($key);
}
$row = $this->execute($this->queries[self::SQL_GET], ['index' => $key])->fetchRow();
if ($row === null) {
@ -92,7 +89,6 @@ abstract class SqlArray extends DriverArray
}
$value = ($this->deserializer)($row['value']);
$this->setCache($key, $value);
return $value;
}
@ -100,11 +96,6 @@ abstract class SqlArray extends DriverArray
public function set(string|int $key, mixed $value): void
{
$key = (string) $key;
if ($this->hasCache($key) && $this->getCache($key) === $value) {
return;
}
$this->setCache($key, $value);
$this->execute(
$this->queries[self::SQL_SET],
@ -113,7 +104,6 @@ abstract class SqlArray extends DriverArray
'value' => ($this->serializer)($value),
],
);
$this->setCache($key, $value);
}
/**
@ -124,7 +114,6 @@ abstract class SqlArray extends DriverArray
public function unset(string|int $key): void
{
$key = (string) $key;
$this->unsetCache($key);
$this->execute(
$this->queries[self::SQL_UNSET],
@ -151,7 +140,6 @@ abstract class SqlArray extends DriverArray
*/
public function clear(): void
{
$this->clearCache();
$this->execute($this->queries[self::SQL_CLEAR]);
}

View File

@ -16,6 +16,7 @@
namespace danog\MadelineProto\Settings\Database;
use danog\MadelineProto\Db\MemoryArray;
use danog\MadelineProto\Settings\DatabaseAbstract;
/**
@ -23,4 +24,8 @@ use danog\MadelineProto\Settings\DatabaseAbstract;
*/
final class Memory extends DatabaseAbstract
{
public function getDriverClass(): string
{
return MemoryArray::class;
}
}

View File

@ -16,6 +16,8 @@
namespace danog\MadelineProto\Settings\Database;
use danog\MadelineProto\Db\MysqlArray;
/**
* MySQL backend settings.
*
@ -23,6 +25,10 @@ namespace danog\MadelineProto\Settings\Database;
*/
final class Mysql extends SqlAbstract
{
public function getDriverClass(): string
{
return MysqlArray::class;
}
public function mergeArray(array $settings): void
{
$settings = $settings['db']['mysql'] ?? [];

View File

@ -16,11 +16,17 @@
namespace danog\MadelineProto\Settings\Database;
use danog\MadelineProto\Db\PostgresArrayBytea;
/**
* Postgres backend settings.
*/
final class Postgres extends SqlAbstract
{
public function getDriverClass(): string
{
return PostgresArrayBytea::class;
}
public function mergeArray(array $settings): void
{
$settings = $settings['db']['postgres'] ?? [];

View File

@ -16,11 +16,17 @@
namespace danog\MadelineProto\Settings\Database;
use danog\MadelineProto\Db\RedisArray;
/**
* Redis backend settings.
*/
final class Redis extends DriverDatabaseAbstract
{
public function getDriverClass(): string
{
return RedisArray::class;
}
/**
* Database number.
*/

View File

@ -16,6 +16,7 @@
namespace danog\MadelineProto\Settings;
use danog\MadelineProto\Db\DbType;
use danog\MadelineProto\SettingsAbstract;
/**
@ -143,4 +144,7 @@ abstract class DatabaseAbstract extends SettingsAbstract
return $this;
}
/** @return class-string<DbType> */
abstract public function getDriverClass(): string;
}

View File

@ -344,4 +344,3 @@ foreach (json_decode(getenv('TEST_DESTINATION_GROUPS'), true) as $peer) {
$sentMessage = $MadelineProto->messages->sendMessage(['peer' => $peer, 'message' => $message, 'entities' => [['_' => 'inputMessageEntityMentionName', 'offset' => 0, 'length' => mb_strlen($message), 'user_id' => $mention]]]);
$MadelineProto->logger($sentMessage, Logger::NOTICE);
}
});

View File

@ -4,7 +4,7 @@
* Upgrade layer number.
*
* @param integer $layer Layer number
*
* @internal
*/
function layerUpgrade(int $layer): void
{

View File

@ -4,7 +4,7 @@ use danog\MadelineProto\Lang;
/**
* Merge extracted docs.
*
* @internal
*/
function mergeExtracted(): void
{

View File

@ -2,7 +2,7 @@
/**
* Load schema file names.
*
* @internal
*/
function loadSchemas(): array
{
@ -19,6 +19,7 @@ function loadSchemas(): array
* Return max available layer number.
*
* @param array $schemas Scheme array
* @internal
*
* @return integer
*/
@ -32,6 +33,7 @@ function maxLayer(array $schemas): int
* Init docs.
*
* @param array $layers Scheme array
* @internal
*
* @return array Documentation information for old docs
*/

View File

@ -30,6 +30,8 @@ if ($argc !== 3) {
*
* @param int $layer Layer number
*
* @internal
*
* @return void
*/
function getTL($layer)
@ -40,6 +42,7 @@ function getTL($layer)
return ['methods' => $layer->getMethods(), 'constructors' => $layer->getConstructors()];
}
/** @internal */
function getUrl($constructor, $type)
{
$orig = $constructor;

View File

@ -9,6 +9,7 @@ require 'vendor/autoload.php';
$class = new \ReflectionClass(Tools::class);
$methods = $class->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC);
/** @internal */
function ssort($a, $b)
{
return strlen($b->getName())-strlen($a->getName());

View File

@ -15,12 +15,7 @@ foreach ($classes as $class) {
}
$methods = array_unique($methods);
function ssort($a, $b)
{
return strlen($b->getName())-strlen($a->getName());
}
usort($methods, 'ssort');
usort($methods, fn ($a, $b) => strlen($b->getName())-strlen($a->getName()));
$find = [];
$replace = [];