init
This commit is contained in:
@@ -0,0 +1 @@
|
||||
<?php
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface Cache
|
||||
{
|
||||
public const STATS_HITS = 'hits';
|
||||
public const STATS_MISSES = 'misses';
|
||||
public const STATS_UPTIME = 'uptime';
|
||||
public const STATS_MEMORY_USAGE = 'memory_usage';
|
||||
public const STATS_MEMORY_AVAILABLE = 'memory_available';
|
||||
public const STATS_MEMORY_AVAILIABLE = 'memory_available';
|
||||
public function fetch($id);
|
||||
public function contains($id);
|
||||
public function save($id, $data, $lifeTime = 0);
|
||||
public function delete($id);
|
||||
public function getStats();
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use function array_combine;
|
||||
use function array_key_exists;
|
||||
use function array_map;
|
||||
use function sprintf;
|
||||
abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiOperationCache
|
||||
{
|
||||
public const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]';
|
||||
private $namespace = '';
|
||||
private $namespaceVersion;
|
||||
public function setNamespace($namespace)
|
||||
{
|
||||
$this->namespace = (string) $namespace;
|
||||
$this->namespaceVersion = null;
|
||||
}
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
public function fetch($id)
|
||||
{
|
||||
return $this->doFetch($this->getNamespacedId($id));
|
||||
}
|
||||
public function fetchMultiple(array $keys)
|
||||
{
|
||||
if (empty($keys)) {
|
||||
return [];
|
||||
}
|
||||
// note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys
|
||||
$namespacedKeys = array_combine($keys, array_map([$this, 'getNamespacedId'], $keys));
|
||||
$items = $this->doFetchMultiple($namespacedKeys);
|
||||
$foundItems = [];
|
||||
// no internal array function supports this sort of mapping: needs to be iterative
|
||||
// this filters and combines keys in one pass
|
||||
foreach ($namespacedKeys as $requestedKey => $namespacedKey) {
|
||||
if (!isset($items[$namespacedKey]) && !array_key_exists($namespacedKey, $items)) {
|
||||
continue;
|
||||
}
|
||||
$foundItems[$requestedKey] = $items[$namespacedKey];
|
||||
}
|
||||
return $foundItems;
|
||||
}
|
||||
public function saveMultiple(array $keysAndValues, $lifetime = 0)
|
||||
{
|
||||
$namespacedKeysAndValues = [];
|
||||
foreach ($keysAndValues as $key => $value) {
|
||||
$namespacedKeysAndValues[$this->getNamespacedId($key)] = $value;
|
||||
}
|
||||
return $this->doSaveMultiple($namespacedKeysAndValues, $lifetime);
|
||||
}
|
||||
public function contains($id)
|
||||
{
|
||||
return $this->doContains($this->getNamespacedId($id));
|
||||
}
|
||||
public function save($id, $data, $lifeTime = 0)
|
||||
{
|
||||
return $this->doSave($this->getNamespacedId($id), $data, $lifeTime);
|
||||
}
|
||||
public function deleteMultiple(array $keys)
|
||||
{
|
||||
return $this->doDeleteMultiple(array_map([$this, 'getNamespacedId'], $keys));
|
||||
}
|
||||
public function delete($id)
|
||||
{
|
||||
return $this->doDelete($this->getNamespacedId($id));
|
||||
}
|
||||
public function getStats()
|
||||
{
|
||||
return $this->doGetStats();
|
||||
}
|
||||
public function flushAll()
|
||||
{
|
||||
return $this->doFlush();
|
||||
}
|
||||
public function deleteAll()
|
||||
{
|
||||
$namespaceCacheKey = $this->getNamespaceCacheKey();
|
||||
$namespaceVersion = $this->getNamespaceVersion() + 1;
|
||||
if ($this->doSave($namespaceCacheKey, $namespaceVersion)) {
|
||||
$this->namespaceVersion = $namespaceVersion;
|
||||
return \true;
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
private function getNamespacedId(string $id) : string
|
||||
{
|
||||
$namespaceVersion = $this->getNamespaceVersion();
|
||||
return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion);
|
||||
}
|
||||
private function getNamespaceCacheKey() : string
|
||||
{
|
||||
return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace);
|
||||
}
|
||||
private function getNamespaceVersion() : int
|
||||
{
|
||||
if ($this->namespaceVersion !== null) {
|
||||
return $this->namespaceVersion;
|
||||
}
|
||||
$namespaceCacheKey = $this->getNamespaceCacheKey();
|
||||
$this->namespaceVersion = (int) $this->doFetch($namespaceCacheKey) ?: 1;
|
||||
return $this->namespaceVersion;
|
||||
}
|
||||
protected function doFetchMultiple(array $keys)
|
||||
{
|
||||
$returnValues = [];
|
||||
foreach ($keys as $key) {
|
||||
$item = $this->doFetch($key);
|
||||
if ($item === \false && !$this->doContains($key)) {
|
||||
continue;
|
||||
}
|
||||
$returnValues[$key] = $item;
|
||||
}
|
||||
return $returnValues;
|
||||
}
|
||||
protected abstract function doFetch($id);
|
||||
protected abstract function doContains($id);
|
||||
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
|
||||
{
|
||||
$success = \true;
|
||||
foreach ($keysAndValues as $key => $value) {
|
||||
if ($this->doSave($key, $value, $lifetime)) {
|
||||
continue;
|
||||
}
|
||||
$success = \false;
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
protected abstract function doSave($id, $data, $lifeTime = 0);
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
$success = \true;
|
||||
foreach ($keys as $key) {
|
||||
if ($this->doDelete($key)) {
|
||||
continue;
|
||||
}
|
||||
$success = \false;
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
protected abstract function doDelete($id);
|
||||
protected abstract function doFlush();
|
||||
protected abstract function doGetStats();
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface ClearableCache
|
||||
{
|
||||
public function deleteAll();
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface FlushableCache
|
||||
{
|
||||
public function flushAll();
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface MultiDeleteCache
|
||||
{
|
||||
public function deleteMultiple(array $keys);
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface MultiGetCache
|
||||
{
|
||||
public function fetchMultiple(array $keys);
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface MultiOperationCache extends MultiGetCache, MultiDeleteCache, MultiPutCache
|
||||
{
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface MultiPutCache
|
||||
{
|
||||
public function saveMultiple(array $keysAndValues, $lifetime = 0);
|
||||
}
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache\Psr6;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\Cache;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\ClearableCache;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\MultiDeleteCache;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\MultiGetCache;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\MultiPutCache;
|
||||
use MailPoetVendor\Psr\Cache\CacheItemInterface;
|
||||
use MailPoetVendor\Psr\Cache\CacheItemPoolInterface;
|
||||
use MailPoetVendor\Symfony\Component\Cache\DoctrineProvider as SymfonyDoctrineProvider;
|
||||
use function array_key_exists;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function current;
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function microtime;
|
||||
use function sprintf;
|
||||
use function strpbrk;
|
||||
use const PHP_VERSION_ID;
|
||||
final class CacheAdapter implements CacheItemPoolInterface
|
||||
{
|
||||
private const RESERVED_CHARACTERS = '{}()/\\@:';
|
||||
private $cache;
|
||||
private $deferredItems = [];
|
||||
public static function wrap(Cache $cache) : CacheItemPoolInterface
|
||||
{
|
||||
if ($cache instanceof DoctrineProvider && !$cache->getNamespace()) {
|
||||
return $cache->getPool();
|
||||
}
|
||||
if ($cache instanceof SymfonyDoctrineProvider && !$cache->getNamespace()) {
|
||||
$getPool = function () {
|
||||
// phpcs:ignore Squiz.Scope.StaticThisUsage.Found
|
||||
return $this->pool;
|
||||
};
|
||||
return $getPool->bindTo($cache, SymfonyDoctrineProvider::class)();
|
||||
}
|
||||
return new self($cache);
|
||||
}
|
||||
private function __construct(Cache $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
public function getCache() : Cache
|
||||
{
|
||||
return $this->cache;
|
||||
}
|
||||
public function getItem($key) : CacheItemInterface
|
||||
{
|
||||
assert(self::validKey($key));
|
||||
if (isset($this->deferredItems[$key])) {
|
||||
$this->commit();
|
||||
}
|
||||
$value = $this->cache->fetch($key);
|
||||
if (PHP_VERSION_ID >= 80000) {
|
||||
if ($value !== \false) {
|
||||
return new TypedCacheItem($key, $value, \true);
|
||||
}
|
||||
return new TypedCacheItem($key, null, \false);
|
||||
}
|
||||
if ($value !== \false) {
|
||||
return new CacheItem($key, $value, \true);
|
||||
}
|
||||
return new CacheItem($key, null, \false);
|
||||
}
|
||||
public function getItems(array $keys = []) : array
|
||||
{
|
||||
if ($this->deferredItems) {
|
||||
$this->commit();
|
||||
}
|
||||
assert(self::validKeys($keys));
|
||||
$values = $this->doFetchMultiple($keys);
|
||||
$items = [];
|
||||
if (PHP_VERSION_ID >= 80000) {
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $values)) {
|
||||
$items[$key] = new TypedCacheItem($key, $values[$key], \true);
|
||||
} else {
|
||||
$items[$key] = new TypedCacheItem($key, null, \false);
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $values)) {
|
||||
$items[$key] = new CacheItem($key, $values[$key], \true);
|
||||
} else {
|
||||
$items[$key] = new CacheItem($key, null, \false);
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
public function hasItem($key) : bool
|
||||
{
|
||||
assert(self::validKey($key));
|
||||
if (isset($this->deferredItems[$key])) {
|
||||
$this->commit();
|
||||
}
|
||||
return $this->cache->contains($key);
|
||||
}
|
||||
public function clear() : bool
|
||||
{
|
||||
$this->deferredItems = [];
|
||||
if (!$this->cache instanceof ClearableCache) {
|
||||
return \false;
|
||||
}
|
||||
return $this->cache->deleteAll();
|
||||
}
|
||||
public function deleteItem($key) : bool
|
||||
{
|
||||
assert(self::validKey($key));
|
||||
unset($this->deferredItems[$key]);
|
||||
return $this->cache->delete($key);
|
||||
}
|
||||
public function deleteItems(array $keys) : bool
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
assert(self::validKey($key));
|
||||
unset($this->deferredItems[$key]);
|
||||
}
|
||||
return $this->doDeleteMultiple($keys);
|
||||
}
|
||||
public function save(CacheItemInterface $item) : bool
|
||||
{
|
||||
return $this->saveDeferred($item) && $this->commit();
|
||||
}
|
||||
public function saveDeferred(CacheItemInterface $item) : bool
|
||||
{
|
||||
if (!$item instanceof CacheItem && !$item instanceof TypedCacheItem) {
|
||||
return \false;
|
||||
}
|
||||
$this->deferredItems[$item->getKey()] = $item;
|
||||
return \true;
|
||||
}
|
||||
public function commit() : bool
|
||||
{
|
||||
if (!$this->deferredItems) {
|
||||
return \true;
|
||||
}
|
||||
$now = microtime(\true);
|
||||
$itemsCount = 0;
|
||||
$byLifetime = [];
|
||||
$expiredKeys = [];
|
||||
foreach ($this->deferredItems as $key => $item) {
|
||||
$lifetime = ($item->getExpiry() ?? $now) - $now;
|
||||
if ($lifetime < 0) {
|
||||
$expiredKeys[] = $key;
|
||||
continue;
|
||||
}
|
||||
++$itemsCount;
|
||||
$byLifetime[(int) $lifetime][$key] = $item->get();
|
||||
}
|
||||
$this->deferredItems = [];
|
||||
switch (count($expiredKeys)) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
$this->cache->delete(current($expiredKeys));
|
||||
break;
|
||||
default:
|
||||
$this->doDeleteMultiple($expiredKeys);
|
||||
break;
|
||||
}
|
||||
if ($itemsCount === 1) {
|
||||
return $this->cache->save($key, $item->get(), (int) $lifetime);
|
||||
}
|
||||
$success = \true;
|
||||
foreach ($byLifetime as $lifetime => $values) {
|
||||
$success = $this->doSaveMultiple($values, $lifetime) && $success;
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
public function __destruct()
|
||||
{
|
||||
$this->commit();
|
||||
}
|
||||
private static function validKey($key) : bool
|
||||
{
|
||||
if (!is_string($key)) {
|
||||
throw new InvalidArgument(sprintf('Cache key must be string, "%s" given.', is_object($key) ? get_class($key) : gettype($key)));
|
||||
}
|
||||
if ($key === '') {
|
||||
throw new InvalidArgument('Cache key length must be greater than zero.');
|
||||
}
|
||||
if (strpbrk($key, self::RESERVED_CHARACTERS) !== \false) {
|
||||
throw new InvalidArgument(sprintf('Cache key "%s" contains reserved characters "%s".', $key, self::RESERVED_CHARACTERS));
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
private static function validKeys(array $keys) : bool
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
self::validKey($key);
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
private function doDeleteMultiple(array $keys) : bool
|
||||
{
|
||||
if ($this->cache instanceof MultiDeleteCache) {
|
||||
return $this->cache->deleteMultiple($keys);
|
||||
}
|
||||
$success = \true;
|
||||
foreach ($keys as $key) {
|
||||
$success = $this->cache->delete($key) && $success;
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
private function doFetchMultiple(array $keys) : array
|
||||
{
|
||||
if ($this->cache instanceof MultiGetCache) {
|
||||
return $this->cache->fetchMultiple($keys);
|
||||
}
|
||||
$values = [];
|
||||
foreach ($keys as $key) {
|
||||
$value = $this->cache->fetch($key);
|
||||
if (!$value) {
|
||||
continue;
|
||||
}
|
||||
$values[$key] = $value;
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
private function doSaveMultiple(array $keysAndValues, int $lifetime = 0) : bool
|
||||
{
|
||||
if ($this->cache instanceof MultiPutCache) {
|
||||
return $this->cache->saveMultiple($keysAndValues, $lifetime);
|
||||
}
|
||||
$success = \true;
|
||||
foreach ($keysAndValues as $key => $value) {
|
||||
$success = $this->cache->save($key, $value, $lifetime) && $success;
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache\Psr6;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
use MailPoetVendor\Psr\Cache\CacheItemInterface;
|
||||
use TypeError;
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function is_int;
|
||||
use function is_object;
|
||||
use function microtime;
|
||||
use function sprintf;
|
||||
final class CacheItem implements CacheItemInterface
|
||||
{
|
||||
private $key;
|
||||
private $value;
|
||||
private $isHit;
|
||||
private $expiry;
|
||||
public function __construct(string $key, $data, bool $isHit)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->value = $data;
|
||||
$this->isHit = $isHit;
|
||||
}
|
||||
public function getKey() : string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
public function get()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
public function isHit() : bool
|
||||
{
|
||||
return $this->isHit;
|
||||
}
|
||||
public function set($value) : self
|
||||
{
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
}
|
||||
public function expiresAt($expiration) : self
|
||||
{
|
||||
if ($expiration === null) {
|
||||
$this->expiry = null;
|
||||
} elseif ($expiration instanceof DateTimeInterface) {
|
||||
$this->expiry = (float) $expiration->format('U.u');
|
||||
} else {
|
||||
throw new TypeError(sprintf('Expected $expiration to be an instance of DateTimeInterface or null, got %s', is_object($expiration) ? get_class($expiration) : gettype($expiration)));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
public function expiresAfter($time) : self
|
||||
{
|
||||
if ($time === null) {
|
||||
$this->expiry = null;
|
||||
} elseif ($time instanceof DateInterval) {
|
||||
$this->expiry = microtime(\true) + DateTime::createFromFormat('U', 0)->add($time)->format('U.u');
|
||||
} elseif (is_int($time)) {
|
||||
$this->expiry = $time + microtime(\true);
|
||||
} else {
|
||||
throw new TypeError(sprintf('Expected $time to be either an integer, an instance of DateInterval or null, got %s', is_object($time) ? get_class($time) : gettype($time)));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
public function getExpiry() : ?float
|
||||
{
|
||||
return $this->expiry;
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache\Psr6;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\Cache;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\CacheProvider;
|
||||
use MailPoetVendor\Psr\Cache\CacheItemPoolInterface;
|
||||
use MailPoetVendor\Symfony\Component\Cache\Adapter\DoctrineAdapter as SymfonyDoctrineAdapter;
|
||||
use MailPoetVendor\Symfony\Contracts\Service\ResetInterface;
|
||||
use function rawurlencode;
|
||||
final class DoctrineProvider extends CacheProvider
|
||||
{
|
||||
private $pool;
|
||||
public static function wrap(CacheItemPoolInterface $pool) : Cache
|
||||
{
|
||||
if ($pool instanceof CacheAdapter) {
|
||||
return $pool->getCache();
|
||||
}
|
||||
if ($pool instanceof SymfonyDoctrineAdapter) {
|
||||
$getCache = function () {
|
||||
// phpcs:ignore Squiz.Scope.StaticThisUsage.Found
|
||||
return $this->provider;
|
||||
};
|
||||
return $getCache->bindTo($pool, SymfonyDoctrineAdapter::class)();
|
||||
}
|
||||
return new self($pool);
|
||||
}
|
||||
private function __construct(CacheItemPoolInterface $pool)
|
||||
{
|
||||
$this->pool = $pool;
|
||||
}
|
||||
public function getPool() : CacheItemPoolInterface
|
||||
{
|
||||
return $this->pool;
|
||||
}
|
||||
public function reset() : void
|
||||
{
|
||||
if ($this->pool instanceof ResetInterface) {
|
||||
$this->pool->reset();
|
||||
}
|
||||
$this->setNamespace($this->getNamespace());
|
||||
}
|
||||
protected function doFetch($id)
|
||||
{
|
||||
$item = $this->pool->getItem(rawurlencode($id));
|
||||
return $item->isHit() ? $item->get() : \false;
|
||||
}
|
||||
protected function doContains($id)
|
||||
{
|
||||
return $this->pool->hasItem(rawurlencode($id));
|
||||
}
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
$item = $this->pool->getItem(rawurlencode($id));
|
||||
if (0 < $lifeTime) {
|
||||
$item->expiresAfter($lifeTime);
|
||||
}
|
||||
return $this->pool->save($item->set($data));
|
||||
}
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return $this->pool->deleteItem(rawurlencode($id));
|
||||
}
|
||||
protected function doFlush()
|
||||
{
|
||||
return $this->pool->clear();
|
||||
}
|
||||
protected function doGetStats()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache\Psr6;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use InvalidArgumentException;
|
||||
use MailPoetVendor\Psr\Cache\InvalidArgumentException as PsrInvalidArgumentException;
|
||||
final class InvalidArgument extends InvalidArgumentException implements PsrInvalidArgumentException
|
||||
{
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Cache\Psr6;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
use MailPoetVendor\Psr\Cache\CacheItemInterface;
|
||||
use TypeError;
|
||||
use function get_debug_type;
|
||||
use function is_int;
|
||||
use function microtime;
|
||||
use function sprintf;
|
||||
final class TypedCacheItem implements CacheItemInterface
|
||||
{
|
||||
private ?float $expiry = null;
|
||||
public function __construct(private string $key, private mixed $value, private bool $isHit)
|
||||
{
|
||||
}
|
||||
public function getKey() : string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
public function get() : mixed
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
public function isHit() : bool
|
||||
{
|
||||
return $this->isHit;
|
||||
}
|
||||
public function set(mixed $value) : static
|
||||
{
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
}
|
||||
public function expiresAt($expiration) : static
|
||||
{
|
||||
if ($expiration === null) {
|
||||
$this->expiry = null;
|
||||
} elseif ($expiration instanceof DateTimeInterface) {
|
||||
$this->expiry = (float) $expiration->format('U.u');
|
||||
} else {
|
||||
throw new TypeError(sprintf('Expected $expiration to be an instance of DateTimeInterface or null, got %s', get_debug_type($expiration)));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
public function expiresAfter($time) : static
|
||||
{
|
||||
if ($time === null) {
|
||||
$this->expiry = null;
|
||||
} elseif ($time instanceof DateInterval) {
|
||||
$this->expiry = microtime(\true) + DateTime::createFromFormat('U', 0)->add($time)->format('U.u');
|
||||
} elseif (is_int($time)) {
|
||||
$this->expiry = $time + microtime(\true);
|
||||
} else {
|
||||
throw new TypeError(sprintf('Expected $time to be either an integer, an instance of DateInterval or null, got %s', get_debug_type($time)));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
public function getExpiry() : ?float
|
||||
{
|
||||
return $this->expiry;
|
||||
}
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Collections;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use Closure;
|
||||
use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
abstract class AbstractLazyCollection implements Collection
|
||||
{
|
||||
protected $collection;
|
||||
protected $initialized = \false;
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->count();
|
||||
}
|
||||
public function add($element)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->add($element);
|
||||
}
|
||||
public function clear()
|
||||
{
|
||||
$this->initialize();
|
||||
$this->collection->clear();
|
||||
}
|
||||
public function contains($element)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->contains($element);
|
||||
}
|
||||
public function isEmpty()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->isEmpty();
|
||||
}
|
||||
public function remove($key)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->remove($key);
|
||||
}
|
||||
public function removeElement($element)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->removeElement($element);
|
||||
}
|
||||
public function containsKey($key)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->containsKey($key);
|
||||
}
|
||||
public function get($key)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->get($key);
|
||||
}
|
||||
public function getKeys()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->getKeys();
|
||||
}
|
||||
public function getValues()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->getValues();
|
||||
}
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->initialize();
|
||||
$this->collection->set($key, $value);
|
||||
}
|
||||
public function toArray()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->toArray();
|
||||
}
|
||||
public function first()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->first();
|
||||
}
|
||||
public function last()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->last();
|
||||
}
|
||||
public function key()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->key();
|
||||
}
|
||||
public function current()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->current();
|
||||
}
|
||||
public function next()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->next();
|
||||
}
|
||||
public function exists(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->exists($p);
|
||||
}
|
||||
public function filter(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->filter($p);
|
||||
}
|
||||
public function forAll(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->forAll($p);
|
||||
}
|
||||
public function map(Closure $func)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->map($func);
|
||||
}
|
||||
public function partition(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->partition($p);
|
||||
}
|
||||
public function indexOf($element)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->indexOf($element);
|
||||
}
|
||||
public function slice($offset, $length = null)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->slice($offset, $length);
|
||||
}
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->getIterator();
|
||||
}
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->offsetExists($offset);
|
||||
}
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
$this->initialize();
|
||||
return $this->collection->offsetGet($offset);
|
||||
}
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->initialize();
|
||||
$this->collection->offsetSet($offset, $value);
|
||||
}
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
$this->initialize();
|
||||
$this->collection->offsetUnset($offset);
|
||||
}
|
||||
public function isInitialized()
|
||||
{
|
||||
return $this->initialized;
|
||||
}
|
||||
protected function initialize()
|
||||
{
|
||||
if ($this->initialized) {
|
||||
return;
|
||||
}
|
||||
$this->doInitialize();
|
||||
$this->initialized = \true;
|
||||
}
|
||||
protected abstract function doInitialize();
|
||||
}
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Collections;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use ArrayIterator;
|
||||
use Closure;
|
||||
use MailPoetVendor\Doctrine\Common\Collections\Expr\ClosureExpressionVisitor;
|
||||
use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
use function array_filter;
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_reverse;
|
||||
use function array_search;
|
||||
use function array_slice;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function current;
|
||||
use function end;
|
||||
use function in_array;
|
||||
use function key;
|
||||
use function next;
|
||||
use function reset;
|
||||
use function spl_object_hash;
|
||||
use function uasort;
|
||||
use const ARRAY_FILTER_USE_BOTH;
|
||||
class ArrayCollection implements Collection, Selectable
|
||||
{
|
||||
private $elements;
|
||||
public function __construct(array $elements = [])
|
||||
{
|
||||
$this->elements = $elements;
|
||||
}
|
||||
public function toArray()
|
||||
{
|
||||
return $this->elements;
|
||||
}
|
||||
public function first()
|
||||
{
|
||||
return reset($this->elements);
|
||||
}
|
||||
protected function createFrom(array $elements)
|
||||
{
|
||||
return new static($elements);
|
||||
}
|
||||
public function last()
|
||||
{
|
||||
return end($this->elements);
|
||||
}
|
||||
public function key()
|
||||
{
|
||||
return key($this->elements);
|
||||
}
|
||||
public function next()
|
||||
{
|
||||
return next($this->elements);
|
||||
}
|
||||
public function current()
|
||||
{
|
||||
return current($this->elements);
|
||||
}
|
||||
public function remove($key)
|
||||
{
|
||||
if (!isset($this->elements[$key]) && !array_key_exists($key, $this->elements)) {
|
||||
return null;
|
||||
}
|
||||
$removed = $this->elements[$key];
|
||||
unset($this->elements[$key]);
|
||||
return $removed;
|
||||
}
|
||||
public function removeElement($element)
|
||||
{
|
||||
$key = array_search($element, $this->elements, \true);
|
||||
if ($key === \false) {
|
||||
return \false;
|
||||
}
|
||||
unset($this->elements[$key]);
|
||||
return \true;
|
||||
}
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return $this->containsKey($offset);
|
||||
}
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (!isset($offset)) {
|
||||
$this->add($value);
|
||||
return;
|
||||
}
|
||||
$this->set($offset, $value);
|
||||
}
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
$this->remove($offset);
|
||||
}
|
||||
public function containsKey($key)
|
||||
{
|
||||
return isset($this->elements[$key]) || array_key_exists($key, $this->elements);
|
||||
}
|
||||
public function contains($element)
|
||||
{
|
||||
return in_array($element, $this->elements, \true);
|
||||
}
|
||||
public function exists(Closure $p)
|
||||
{
|
||||
foreach ($this->elements as $key => $element) {
|
||||
if ($p($key, $element)) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
public function indexOf($element)
|
||||
{
|
||||
return array_search($element, $this->elements, \true);
|
||||
}
|
||||
public function get($key)
|
||||
{
|
||||
return $this->elements[$key] ?? null;
|
||||
}
|
||||
public function getKeys()
|
||||
{
|
||||
return array_keys($this->elements);
|
||||
}
|
||||
public function getValues()
|
||||
{
|
||||
return array_values($this->elements);
|
||||
}
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return count($this->elements);
|
||||
}
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->elements[$key] = $value;
|
||||
}
|
||||
public function add($element)
|
||||
{
|
||||
$this->elements[] = $element;
|
||||
return \true;
|
||||
}
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->elements);
|
||||
}
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->elements);
|
||||
}
|
||||
public function map(Closure $func)
|
||||
{
|
||||
return $this->createFrom(array_map($func, $this->elements));
|
||||
}
|
||||
public function filter(Closure $p)
|
||||
{
|
||||
return $this->createFrom(array_filter($this->elements, $p, ARRAY_FILTER_USE_BOTH));
|
||||
}
|
||||
public function forAll(Closure $p)
|
||||
{
|
||||
foreach ($this->elements as $key => $element) {
|
||||
if (!$p($key, $element)) {
|
||||
return \false;
|
||||
}
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
public function partition(Closure $p)
|
||||
{
|
||||
$matches = $noMatches = [];
|
||||
foreach ($this->elements as $key => $element) {
|
||||
if ($p($key, $element)) {
|
||||
$matches[$key] = $element;
|
||||
} else {
|
||||
$noMatches[$key] = $element;
|
||||
}
|
||||
}
|
||||
return [$this->createFrom($matches), $this->createFrom($noMatches)];
|
||||
}
|
||||
public function __toString()
|
||||
{
|
||||
return self::class . '@' . spl_object_hash($this);
|
||||
}
|
||||
public function clear()
|
||||
{
|
||||
$this->elements = [];
|
||||
}
|
||||
public function slice($offset, $length = null)
|
||||
{
|
||||
return array_slice($this->elements, $offset, $length, \true);
|
||||
}
|
||||
public function matching(Criteria $criteria)
|
||||
{
|
||||
$expr = $criteria->getWhereExpression();
|
||||
$filtered = $this->elements;
|
||||
if ($expr) {
|
||||
$visitor = new ClosureExpressionVisitor();
|
||||
$filter = $visitor->dispatch($expr);
|
||||
$filtered = array_filter($filtered, $filter);
|
||||
}
|
||||
$orderings = $criteria->getOrderings();
|
||||
if ($orderings) {
|
||||
$next = null;
|
||||
foreach (array_reverse($orderings) as $field => $ordering) {
|
||||
$next = ClosureExpressionVisitor::sortByField($field, $ordering === Criteria::DESC ? -1 : 1, $next);
|
||||
}
|
||||
uasort($filtered, $next);
|
||||
}
|
||||
$offset = $criteria->getFirstResult();
|
||||
$length = $criteria->getMaxResults();
|
||||
if ($offset || $length) {
|
||||
$filtered = array_slice($filtered, (int) $offset, $length);
|
||||
}
|
||||
return $this->createFrom($filtered);
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Collections;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use ArrayAccess;
|
||||
use Closure;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
interface Collection extends Countable, IteratorAggregate, ArrayAccess
|
||||
{
|
||||
public function add($element);
|
||||
public function clear();
|
||||
public function contains($element);
|
||||
public function isEmpty();
|
||||
public function remove($key);
|
||||
public function removeElement($element);
|
||||
public function containsKey($key);
|
||||
public function get($key);
|
||||
public function getKeys();
|
||||
public function getValues();
|
||||
public function set($key, $value);
|
||||
public function toArray();
|
||||
public function first();
|
||||
public function last();
|
||||
public function key();
|
||||
public function current();
|
||||
public function next();
|
||||
public function exists(Closure $p);
|
||||
public function filter(Closure $p);
|
||||
public function forAll(Closure $p);
|
||||
public function map(Closure $func);
|
||||
public function partition(Closure $p);
|
||||
public function indexOf($element);
|
||||
public function slice($offset, $length = null);
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Collections;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Common\Collections\Expr\CompositeExpression;
|
||||
use MailPoetVendor\Doctrine\Common\Collections\Expr\Expression;
|
||||
use function array_map;
|
||||
use function strtoupper;
|
||||
class Criteria
|
||||
{
|
||||
public const ASC = 'ASC';
|
||||
public const DESC = 'DESC';
|
||||
private static $expressionBuilder;
|
||||
private $expression;
|
||||
private $orderings = [];
|
||||
private $firstResult;
|
||||
private $maxResults;
|
||||
public static function create()
|
||||
{
|
||||
return new static();
|
||||
}
|
||||
public static function expr()
|
||||
{
|
||||
if (self::$expressionBuilder === null) {
|
||||
self::$expressionBuilder = new ExpressionBuilder();
|
||||
}
|
||||
return self::$expressionBuilder;
|
||||
}
|
||||
public function __construct(?Expression $expression = null, ?array $orderings = null, $firstResult = null, $maxResults = null)
|
||||
{
|
||||
$this->expression = $expression;
|
||||
$this->setFirstResult($firstResult);
|
||||
$this->setMaxResults($maxResults);
|
||||
if ($orderings === null) {
|
||||
return;
|
||||
}
|
||||
$this->orderBy($orderings);
|
||||
}
|
||||
public function where(Expression $expression)
|
||||
{
|
||||
$this->expression = $expression;
|
||||
return $this;
|
||||
}
|
||||
public function andWhere(Expression $expression)
|
||||
{
|
||||
if ($this->expression === null) {
|
||||
return $this->where($expression);
|
||||
}
|
||||
$this->expression = new CompositeExpression(CompositeExpression::TYPE_AND, [$this->expression, $expression]);
|
||||
return $this;
|
||||
}
|
||||
public function orWhere(Expression $expression)
|
||||
{
|
||||
if ($this->expression === null) {
|
||||
return $this->where($expression);
|
||||
}
|
||||
$this->expression = new CompositeExpression(CompositeExpression::TYPE_OR, [$this->expression, $expression]);
|
||||
return $this;
|
||||
}
|
||||
public function getWhereExpression()
|
||||
{
|
||||
return $this->expression;
|
||||
}
|
||||
public function getOrderings()
|
||||
{
|
||||
return $this->orderings;
|
||||
}
|
||||
public function orderBy(array $orderings)
|
||||
{
|
||||
$this->orderings = array_map(static function (string $ordering) : string {
|
||||
return strtoupper($ordering) === Criteria::ASC ? Criteria::ASC : Criteria::DESC;
|
||||
}, $orderings);
|
||||
return $this;
|
||||
}
|
||||
public function getFirstResult()
|
||||
{
|
||||
return $this->firstResult;
|
||||
}
|
||||
public function setFirstResult($firstResult)
|
||||
{
|
||||
$this->firstResult = $firstResult;
|
||||
return $this;
|
||||
}
|
||||
public function getMaxResults()
|
||||
{
|
||||
return $this->maxResults;
|
||||
}
|
||||
public function setMaxResults($maxResults)
|
||||
{
|
||||
$this->maxResults = $maxResults;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Collections\Expr;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use ArrayAccess;
|
||||
use Closure;
|
||||
use RuntimeException;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_scalar;
|
||||
use function iterator_to_array;
|
||||
use function method_exists;
|
||||
use function preg_match;
|
||||
use function preg_replace_callback;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function strtoupper;
|
||||
use function substr;
|
||||
class ClosureExpressionVisitor extends ExpressionVisitor
|
||||
{
|
||||
public static function getObjectFieldValue($object, $field)
|
||||
{
|
||||
if (is_array($object)) {
|
||||
return $object[$field];
|
||||
}
|
||||
$accessors = ['get', 'is'];
|
||||
foreach ($accessors as $accessor) {
|
||||
$accessor .= $field;
|
||||
if (method_exists($object, $accessor)) {
|
||||
return $object->{$accessor}();
|
||||
}
|
||||
}
|
||||
if (preg_match('/^is[A-Z]+/', $field) === 1 && method_exists($object, $field)) {
|
||||
return $object->{$field}();
|
||||
}
|
||||
// __call should be triggered for get.
|
||||
$accessor = $accessors[0] . $field;
|
||||
if (method_exists($object, '__call')) {
|
||||
return $object->{$accessor}();
|
||||
}
|
||||
if ($object instanceof ArrayAccess) {
|
||||
return $object[$field];
|
||||
}
|
||||
if (isset($object->{$field})) {
|
||||
return $object->{$field};
|
||||
}
|
||||
// camelcase field name to support different variable naming conventions
|
||||
$ccField = preg_replace_callback('/_(.?)/', static function ($matches) {
|
||||
return strtoupper($matches[1]);
|
||||
}, $field);
|
||||
foreach ($accessors as $accessor) {
|
||||
$accessor .= $ccField;
|
||||
if (method_exists($object, $accessor)) {
|
||||
return $object->{$accessor}();
|
||||
}
|
||||
}
|
||||
return $object->{$field};
|
||||
}
|
||||
public static function sortByField($name, $orientation = 1, ?Closure $next = null)
|
||||
{
|
||||
if (!$next) {
|
||||
$next = static function () : int {
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
return static function ($a, $b) use($name, $next, $orientation) : int {
|
||||
$aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name);
|
||||
$bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name);
|
||||
if ($aValue === $bValue) {
|
||||
return $next($a, $b);
|
||||
}
|
||||
return ($aValue > $bValue ? 1 : -1) * $orientation;
|
||||
};
|
||||
}
|
||||
public function walkComparison(Comparison $comparison)
|
||||
{
|
||||
$field = $comparison->getField();
|
||||
$value = $comparison->getValue()->getValue();
|
||||
// shortcut for walkValue()
|
||||
switch ($comparison->getOperator()) {
|
||||
case Comparison::EQ:
|
||||
return static function ($object) use($field, $value) : bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value;
|
||||
};
|
||||
case Comparison::NEQ:
|
||||
return static function ($object) use($field, $value) : bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value;
|
||||
};
|
||||
case Comparison::LT:
|
||||
return static function ($object) use($field, $value) : bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value;
|
||||
};
|
||||
case Comparison::LTE:
|
||||
return static function ($object) use($field, $value) : bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value;
|
||||
};
|
||||
case Comparison::GT:
|
||||
return static function ($object) use($field, $value) : bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value;
|
||||
};
|
||||
case Comparison::GTE:
|
||||
return static function ($object) use($field, $value) : bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value;
|
||||
};
|
||||
case Comparison::IN:
|
||||
return static function ($object) use($field, $value) : bool {
|
||||
$fieldValue = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
|
||||
return in_array($fieldValue, $value, is_scalar($fieldValue));
|
||||
};
|
||||
case Comparison::NIN:
|
||||
return static function ($object) use($field, $value) : bool {
|
||||
$fieldValue = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
|
||||
return !in_array($fieldValue, $value, is_scalar($fieldValue));
|
||||
};
|
||||
case Comparison::CONTAINS:
|
||||
return static function ($object) use($field, $value) {
|
||||
return strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value) !== \false;
|
||||
};
|
||||
case Comparison::MEMBER_OF:
|
||||
return static function ($object) use($field, $value) : bool {
|
||||
$fieldValues = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
|
||||
if (!is_array($fieldValues)) {
|
||||
$fieldValues = iterator_to_array($fieldValues);
|
||||
}
|
||||
return in_array($value, $fieldValues, \true);
|
||||
};
|
||||
case Comparison::STARTS_WITH:
|
||||
return static function ($object) use($field, $value) : bool {
|
||||
return strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value) === 0;
|
||||
};
|
||||
case Comparison::ENDS_WITH:
|
||||
return static function ($object) use($field, $value) : bool {
|
||||
return $value === substr(ClosureExpressionVisitor::getObjectFieldValue($object, $field), -strlen($value));
|
||||
};
|
||||
default:
|
||||
throw new RuntimeException('Unknown comparison operator: ' . $comparison->getOperator());
|
||||
}
|
||||
}
|
||||
public function walkValue(Value $value)
|
||||
{
|
||||
return $value->getValue();
|
||||
}
|
||||
public function walkCompositeExpression(CompositeExpression $expr)
|
||||
{
|
||||
$expressionList = [];
|
||||
foreach ($expr->getExpressionList() as $child) {
|
||||
$expressionList[] = $this->dispatch($child);
|
||||
}
|
||||
switch ($expr->getType()) {
|
||||
case CompositeExpression::TYPE_AND:
|
||||
return $this->andExpressions($expressionList);
|
||||
case CompositeExpression::TYPE_OR:
|
||||
return $this->orExpressions($expressionList);
|
||||
default:
|
||||
throw new RuntimeException('Unknown composite ' . $expr->getType());
|
||||
}
|
||||
}
|
||||
private function andExpressions(array $expressions) : callable
|
||||
{
|
||||
return static function ($object) use($expressions) : bool {
|
||||
foreach ($expressions as $expression) {
|
||||
if (!$expression($object)) {
|
||||
return \false;
|
||||
}
|
||||
}
|
||||
return \true;
|
||||
};
|
||||
}
|
||||
private function orExpressions(array $expressions) : callable
|
||||
{
|
||||
return static function ($object) use($expressions) : bool {
|
||||
foreach ($expressions as $expression) {
|
||||
if ($expression($object)) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
return \false;
|
||||
};
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Collections\Expr;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class Comparison implements Expression
|
||||
{
|
||||
public const EQ = '=';
|
||||
public const NEQ = '<>';
|
||||
public const LT = '<';
|
||||
public const LTE = '<=';
|
||||
public const GT = '>';
|
||||
public const GTE = '>=';
|
||||
public const IS = '=';
|
||||
// no difference with EQ
|
||||
public const IN = 'IN';
|
||||
public const NIN = 'NIN';
|
||||
public const CONTAINS = 'CONTAINS';
|
||||
public const MEMBER_OF = 'MEMBER_OF';
|
||||
public const STARTS_WITH = 'STARTS_WITH';
|
||||
public const ENDS_WITH = 'ENDS_WITH';
|
||||
private $field;
|
||||
private $op;
|
||||
private $value;
|
||||
public function __construct($field, $operator, $value)
|
||||
{
|
||||
if (!$value instanceof Value) {
|
||||
$value = new Value($value);
|
||||
}
|
||||
$this->field = $field;
|
||||
$this->op = $operator;
|
||||
$this->value = $value;
|
||||
}
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
public function getOperator()
|
||||
{
|
||||
return $this->op;
|
||||
}
|
||||
public function visit(ExpressionVisitor $visitor)
|
||||
{
|
||||
return $visitor->walkComparison($this);
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Collections\Expr;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use RuntimeException;
|
||||
class CompositeExpression implements Expression
|
||||
{
|
||||
public const TYPE_AND = 'AND';
|
||||
public const TYPE_OR = 'OR';
|
||||
private $type;
|
||||
private $expressions = [];
|
||||
public function __construct($type, array $expressions)
|
||||
{
|
||||
$this->type = $type;
|
||||
foreach ($expressions as $expr) {
|
||||
if ($expr instanceof Value) {
|
||||
throw new RuntimeException('Values are not supported expressions as children of and/or expressions.');
|
||||
}
|
||||
if (!$expr instanceof Expression) {
|
||||
throw new RuntimeException('No expression given to CompositeExpression.');
|
||||
}
|
||||
$this->expressions[] = $expr;
|
||||
}
|
||||
}
|
||||
public function getExpressionList()
|
||||
{
|
||||
return $this->expressions;
|
||||
}
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
public function visit(ExpressionVisitor $visitor)
|
||||
{
|
||||
return $visitor->walkCompositeExpression($this);
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Collections\Expr;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface Expression
|
||||
{
|
||||
public function visit(ExpressionVisitor $visitor);
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Collections\Expr;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use RuntimeException;
|
||||
use function get_class;
|
||||
abstract class ExpressionVisitor
|
||||
{
|
||||
public abstract function walkComparison(Comparison $comparison);
|
||||
public abstract function walkValue(Value $value);
|
||||
public abstract function walkCompositeExpression(CompositeExpression $expr);
|
||||
public function dispatch(Expression $expr)
|
||||
{
|
||||
switch (\true) {
|
||||
case $expr instanceof Comparison:
|
||||
return $this->walkComparison($expr);
|
||||
case $expr instanceof Value:
|
||||
return $this->walkValue($expr);
|
||||
case $expr instanceof CompositeExpression:
|
||||
return $this->walkCompositeExpression($expr);
|
||||
default:
|
||||
throw new RuntimeException('Unknown Expression ' . get_class($expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Collections\Expr;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class Value implements Expression
|
||||
{
|
||||
private $value;
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
public function visit(ExpressionVisitor $visitor)
|
||||
{
|
||||
return $visitor->walkValue($this);
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Collections;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Common\Collections\Expr\Comparison;
|
||||
use MailPoetVendor\Doctrine\Common\Collections\Expr\CompositeExpression;
|
||||
use MailPoetVendor\Doctrine\Common\Collections\Expr\Value;
|
||||
use function func_get_args;
|
||||
class ExpressionBuilder
|
||||
{
|
||||
public function andX($x = null)
|
||||
{
|
||||
return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
|
||||
}
|
||||
public function orX($x = null)
|
||||
{
|
||||
return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args());
|
||||
}
|
||||
public function eq($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::EQ, new Value($value));
|
||||
}
|
||||
public function gt($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::GT, new Value($value));
|
||||
}
|
||||
public function lt($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::LT, new Value($value));
|
||||
}
|
||||
public function gte($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::GTE, new Value($value));
|
||||
}
|
||||
public function lte($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::LTE, new Value($value));
|
||||
}
|
||||
public function neq($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::NEQ, new Value($value));
|
||||
}
|
||||
public function isNull($field)
|
||||
{
|
||||
return new Comparison($field, Comparison::EQ, new Value(null));
|
||||
}
|
||||
public function in($field, array $values)
|
||||
{
|
||||
return new Comparison($field, Comparison::IN, new Value($values));
|
||||
}
|
||||
public function notIn($field, array $values)
|
||||
{
|
||||
return new Comparison($field, Comparison::NIN, new Value($values));
|
||||
}
|
||||
public function contains($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::CONTAINS, new Value($value));
|
||||
}
|
||||
public function memberOf($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::MEMBER_OF, new Value($value));
|
||||
}
|
||||
public function startsWith($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::STARTS_WITH, new Value($value));
|
||||
}
|
||||
public function endsWith($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::ENDS_WITH, new Value($value));
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Collections;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface Selectable
|
||||
{
|
||||
public function matching(Criteria $criteria);
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use function class_exists;
|
||||
use function interface_exists;
|
||||
use function is_array;
|
||||
use function is_file;
|
||||
use function reset;
|
||||
use function spl_autoload_functions;
|
||||
use function spl_autoload_register;
|
||||
use function spl_autoload_unregister;
|
||||
use function str_replace;
|
||||
use function stream_resolve_include_path;
|
||||
use function strpos;
|
||||
use function trait_exists;
|
||||
use function trigger_error;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const E_USER_DEPRECATED;
|
||||
@trigger_error(ClassLoader::class . ' is deprecated.', E_USER_DEPRECATED);
|
||||
class ClassLoader
|
||||
{
|
||||
protected $fileExtension = '.php';
|
||||
protected $namespace;
|
||||
protected $includePath;
|
||||
protected $namespaceSeparator = '\\';
|
||||
public function __construct($ns = null, $includePath = null)
|
||||
{
|
||||
$this->namespace = $ns;
|
||||
$this->includePath = $includePath;
|
||||
}
|
||||
public function setNamespaceSeparator($sep)
|
||||
{
|
||||
$this->namespaceSeparator = $sep;
|
||||
}
|
||||
public function getNamespaceSeparator()
|
||||
{
|
||||
return $this->namespaceSeparator;
|
||||
}
|
||||
public function setIncludePath($includePath)
|
||||
{
|
||||
$this->includePath = $includePath;
|
||||
}
|
||||
public function getIncludePath()
|
||||
{
|
||||
return $this->includePath;
|
||||
}
|
||||
public function setFileExtension($fileExtension)
|
||||
{
|
||||
$this->fileExtension = $fileExtension;
|
||||
}
|
||||
public function getFileExtension()
|
||||
{
|
||||
return $this->fileExtension;
|
||||
}
|
||||
public function register()
|
||||
{
|
||||
spl_autoload_register([$this, 'loadClass']);
|
||||
}
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister([$this, 'loadClass']);
|
||||
}
|
||||
public function loadClass($className)
|
||||
{
|
||||
if (self::typeExists($className)) {
|
||||
return \true;
|
||||
}
|
||||
if (!$this->canLoadClass($className)) {
|
||||
return \false;
|
||||
}
|
||||
require ($this->includePath !== null ? $this->includePath . DIRECTORY_SEPARATOR : '') . str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->fileExtension;
|
||||
return self::typeExists($className);
|
||||
}
|
||||
public function canLoadClass($className)
|
||||
{
|
||||
if ($this->namespace !== null && strpos($className, $this->namespace . $this->namespaceSeparator) !== 0) {
|
||||
return \false;
|
||||
}
|
||||
$file = str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->fileExtension;
|
||||
if ($this->includePath !== null) {
|
||||
return is_file($this->includePath . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
return stream_resolve_include_path($file) !== \false;
|
||||
}
|
||||
public static function classExists($className)
|
||||
{
|
||||
return self::typeExists($className, \true);
|
||||
}
|
||||
public static function getClassLoader($className)
|
||||
{
|
||||
foreach (spl_autoload_functions() as $loader) {
|
||||
if (!is_array($loader)) {
|
||||
continue;
|
||||
}
|
||||
$classLoader = reset($loader);
|
||||
if ($classLoader instanceof ClassLoader && $classLoader->canLoadClass($className)) {
|
||||
return $classLoader;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static function typeExists($type, $autoload = \false)
|
||||
{
|
||||
return class_exists($type, $autoload) || interface_exists($type, $autoload) || trait_exists($type, $autoload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use Exception;
|
||||
class CommonException extends Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface Comparable
|
||||
{
|
||||
public function compareTo($other);
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Proxy;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Common\Proxy\Exception\InvalidArgumentException;
|
||||
use MailPoetVendor\Doctrine\Common\Proxy\Exception\OutOfBoundsException;
|
||||
use MailPoetVendor\Doctrine\Common\Util\ClassUtils;
|
||||
use MailPoetVendor\Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use MailPoetVendor\Doctrine\Persistence\Mapping\ClassMetadataFactory;
|
||||
use function class_exists;
|
||||
use function file_exists;
|
||||
use function filemtime;
|
||||
use function in_array;
|
||||
abstract class AbstractProxyFactory
|
||||
{
|
||||
public const AUTOGENERATE_NEVER = 0;
|
||||
public const AUTOGENERATE_ALWAYS = 1;
|
||||
public const AUTOGENERATE_FILE_NOT_EXISTS = 2;
|
||||
public const AUTOGENERATE_EVAL = 3;
|
||||
public const AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED = 4;
|
||||
private const AUTOGENERATE_MODES = [self::AUTOGENERATE_NEVER, self::AUTOGENERATE_ALWAYS, self::AUTOGENERATE_FILE_NOT_EXISTS, self::AUTOGENERATE_EVAL, self::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED];
|
||||
private $metadataFactory;
|
||||
private $proxyGenerator;
|
||||
private $autoGenerate;
|
||||
private $definitions = [];
|
||||
public function __construct(ProxyGenerator $proxyGenerator, ClassMetadataFactory $metadataFactory, $autoGenerate)
|
||||
{
|
||||
$this->proxyGenerator = $proxyGenerator;
|
||||
$this->metadataFactory = $metadataFactory;
|
||||
$this->autoGenerate = (int) $autoGenerate;
|
||||
if (!in_array($this->autoGenerate, self::AUTOGENERATE_MODES, \true)) {
|
||||
throw InvalidArgumentException::invalidAutoGenerateMode($autoGenerate);
|
||||
}
|
||||
}
|
||||
public function getProxy($className, array $identifier)
|
||||
{
|
||||
$definition = $this->definitions[$className] ?? $this->getProxyDefinition($className);
|
||||
$fqcn = $definition->proxyClassName;
|
||||
$proxy = new $fqcn($definition->initializer, $definition->cloner);
|
||||
foreach ($definition->identifierFields as $idField) {
|
||||
if (!isset($identifier[$idField])) {
|
||||
throw OutOfBoundsException::missingPrimaryKeyValue($className, $idField);
|
||||
}
|
||||
$definition->reflectionFields[$idField]->setValue($proxy, $identifier[$idField]);
|
||||
}
|
||||
return $proxy;
|
||||
}
|
||||
public function generateProxyClasses(array $classes, $proxyDir = null)
|
||||
{
|
||||
$generated = 0;
|
||||
foreach ($classes as $class) {
|
||||
if ($this->skipClass($class)) {
|
||||
continue;
|
||||
}
|
||||
$proxyFileName = $this->proxyGenerator->getProxyFileName($class->getName(), $proxyDir);
|
||||
$this->proxyGenerator->generateProxyClass($class, $proxyFileName);
|
||||
$generated += 1;
|
||||
}
|
||||
return $generated;
|
||||
}
|
||||
public function resetUninitializedProxy(Proxy $proxy)
|
||||
{
|
||||
if ($proxy->__isInitialized()) {
|
||||
throw InvalidArgumentException::unitializedProxyExpected($proxy);
|
||||
}
|
||||
$className = ClassUtils::getClass($proxy);
|
||||
$definition = $this->definitions[$className] ?? $this->getProxyDefinition($className);
|
||||
$proxy->__setInitializer($definition->initializer);
|
||||
$proxy->__setCloner($definition->cloner);
|
||||
return $proxy;
|
||||
}
|
||||
private function getProxyDefinition($className)
|
||||
{
|
||||
$classMetadata = $this->metadataFactory->getMetadataFor($className);
|
||||
$className = $classMetadata->getName();
|
||||
// aliases and case sensitivity
|
||||
$this->definitions[$className] = $this->createProxyDefinition($className);
|
||||
$proxyClassName = $this->definitions[$className]->proxyClassName;
|
||||
if (!class_exists($proxyClassName, \false)) {
|
||||
$fileName = $this->proxyGenerator->getProxyFileName($className);
|
||||
switch ($this->autoGenerate) {
|
||||
case self::AUTOGENERATE_NEVER:
|
||||
require $fileName;
|
||||
break;
|
||||
case self::AUTOGENERATE_FILE_NOT_EXISTS:
|
||||
if (!file_exists($fileName)) {
|
||||
$this->proxyGenerator->generateProxyClass($classMetadata, $fileName);
|
||||
}
|
||||
require $fileName;
|
||||
break;
|
||||
case self::AUTOGENERATE_ALWAYS:
|
||||
$this->proxyGenerator->generateProxyClass($classMetadata, $fileName);
|
||||
require $fileName;
|
||||
break;
|
||||
case self::AUTOGENERATE_EVAL:
|
||||
$this->proxyGenerator->generateProxyClass($classMetadata, \false);
|
||||
break;
|
||||
case self::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED:
|
||||
if (!file_exists($fileName) || filemtime($fileName) < filemtime($classMetadata->getReflectionClass()->getFileName())) {
|
||||
$this->proxyGenerator->generateProxyClass($classMetadata, $fileName);
|
||||
}
|
||||
require $fileName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $this->definitions[$className];
|
||||
}
|
||||
protected abstract function skipClass(ClassMetadata $metadata);
|
||||
protected abstract function createProxyDefinition($className);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Proxy;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use Closure;
|
||||
use MailPoetVendor\Doctrine\Common\Proxy\Exception\InvalidArgumentException;
|
||||
use function call_user_func;
|
||||
use function file_exists;
|
||||
use function is_callable;
|
||||
use function ltrim;
|
||||
use function spl_autoload_register;
|
||||
use function str_replace;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
class Autoloader
|
||||
{
|
||||
public static function resolveFile($proxyDir, $proxyNamespace, $className)
|
||||
{
|
||||
if (strpos($className, $proxyNamespace) !== 0) {
|
||||
throw InvalidArgumentException::notProxyClass($className, $proxyNamespace);
|
||||
}
|
||||
// remove proxy namespace from class name
|
||||
$classNameRelativeToProxyNamespace = substr($className, strlen($proxyNamespace));
|
||||
// remove namespace separators from remaining class name
|
||||
$fileName = str_replace('\\', '', $classNameRelativeToProxyNamespace);
|
||||
return $proxyDir . DIRECTORY_SEPARATOR . $fileName . '.php';
|
||||
}
|
||||
public static function register($proxyDir, $proxyNamespace, $notFoundCallback = null)
|
||||
{
|
||||
$proxyNamespace = ltrim($proxyNamespace, '\\');
|
||||
if ($notFoundCallback !== null && !is_callable($notFoundCallback)) {
|
||||
throw InvalidArgumentException::invalidClassNotFoundCallback($notFoundCallback);
|
||||
}
|
||||
$autoloader = static function ($className) use($proxyDir, $proxyNamespace, $notFoundCallback) {
|
||||
if ($proxyNamespace === '') {
|
||||
return;
|
||||
}
|
||||
if (strpos($className, $proxyNamespace) !== 0) {
|
||||
return;
|
||||
}
|
||||
$file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);
|
||||
if ($notFoundCallback && !file_exists($file)) {
|
||||
call_user_func($notFoundCallback, $proxyDir, $proxyNamespace, $className);
|
||||
}
|
||||
require $file;
|
||||
};
|
||||
spl_autoload_register($autoloader);
|
||||
return $autoloader;
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Proxy\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Persistence\Proxy;
|
||||
use InvalidArgumentException as BaseInvalidArgumentException;
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function is_object;
|
||||
use function sprintf;
|
||||
class InvalidArgumentException extends BaseInvalidArgumentException implements ProxyException
|
||||
{
|
||||
public static function proxyDirectoryRequired()
|
||||
{
|
||||
return new self('You must configure a proxy directory. See docs for details');
|
||||
}
|
||||
public static function notProxyClass($className, $proxyNamespace)
|
||||
{
|
||||
return new self(sprintf('The class "%s" is not part of the proxy namespace "%s"', $className, $proxyNamespace));
|
||||
}
|
||||
public static function invalidPlaceholder($name)
|
||||
{
|
||||
return new self(sprintf('Provided placeholder for "%s" must be either a string or a valid callable', $name));
|
||||
}
|
||||
public static function proxyNamespaceRequired()
|
||||
{
|
||||
return new self('You must configure a proxy namespace');
|
||||
}
|
||||
public static function unitializedProxyExpected(Proxy $proxy)
|
||||
{
|
||||
return new self(sprintf('Provided proxy of type "%s" must not be initialized.', get_class($proxy)));
|
||||
}
|
||||
public static function invalidClassNotFoundCallback($callback)
|
||||
{
|
||||
$type = is_object($callback) ? get_class($callback) : gettype($callback);
|
||||
return new self(sprintf('Invalid \\$notFoundCallback given: must be a callable, "%s" given', $type));
|
||||
}
|
||||
public static function classMustNotBeAbstract($className)
|
||||
{
|
||||
return new self(sprintf('Unable to create a proxy for an abstract class "%s".', $className));
|
||||
}
|
||||
public static function classMustNotBeFinal($className)
|
||||
{
|
||||
return new self(sprintf('Unable to create a proxy for a final class "%s".', $className));
|
||||
}
|
||||
public static function classMustNotBeReadOnly($className)
|
||||
{
|
||||
return new self(sprintf('Unable to create a proxy for a readonly class "%s".', $className));
|
||||
}
|
||||
public static function invalidAutoGenerateMode($value) : self
|
||||
{
|
||||
return new self(sprintf('Invalid auto generate mode "%s" given.', $value));
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Proxy\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use OutOfBoundsException as BaseOutOfBoundsException;
|
||||
use function sprintf;
|
||||
class OutOfBoundsException extends BaseOutOfBoundsException implements ProxyException
|
||||
{
|
||||
public static function missingPrimaryKeyValue($className, $idField)
|
||||
{
|
||||
return new self(sprintf('Missing value for primary key %s on %s', $idField, $className));
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Proxy\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface ProxyException
|
||||
{
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Proxy\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use Throwable;
|
||||
use UnexpectedValueException as BaseUnexpectedValueException;
|
||||
use function sprintf;
|
||||
class UnexpectedValueException extends BaseUnexpectedValueException implements ProxyException
|
||||
{
|
||||
public static function proxyDirectoryNotWritable($proxyDirectory)
|
||||
{
|
||||
return new self(sprintf('Your proxy directory "%s" must be writable', $proxyDirectory));
|
||||
}
|
||||
public static function invalidParameterTypeHint($className, $methodName, $parameterName, ?Throwable $previous = null)
|
||||
{
|
||||
return new self(sprintf('The type hint of parameter "%s" in method "%s" in class "%s" is invalid.', $parameterName, $methodName, $className), 0, $previous);
|
||||
}
|
||||
public static function invalidReturnTypeHint($className, $methodName, ?Throwable $previous = null)
|
||||
{
|
||||
return new self(sprintf('The return type of method "%s" in class "%s" is invalid.', $methodName, $className), 0, $previous);
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Proxy;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use Closure;
|
||||
use MailPoetVendor\Doctrine\Persistence\Proxy as BaseProxy;
|
||||
interface Proxy extends BaseProxy
|
||||
{
|
||||
public function __setInitialized($initialized);
|
||||
public function __setInitializer(?Closure $initializer = null);
|
||||
public function __getInitializer();
|
||||
public function __setCloner(?Closure $cloner = null);
|
||||
public function __getCloner();
|
||||
public function __getLazyProperties();
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Proxy;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use ReflectionProperty;
|
||||
class ProxyDefinition
|
||||
{
|
||||
public $proxyClassName;
|
||||
public $identifierFields;
|
||||
public $reflectionFields;
|
||||
public $initializer;
|
||||
public $cloner;
|
||||
public function __construct($proxyClassName, array $identifierFields, array $reflectionFields, $initializer, $cloner)
|
||||
{
|
||||
$this->proxyClassName = $proxyClassName;
|
||||
$this->identifierFields = $identifierFields;
|
||||
$this->reflectionFields = $reflectionFields;
|
||||
$this->initializer = $initializer;
|
||||
$this->cloner = $cloner;
|
||||
}
|
||||
}
|
||||
+733
@@ -0,0 +1,733 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Proxy;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use BackedEnum;
|
||||
use MailPoetVendor\Doctrine\Common\Proxy\Exception\InvalidArgumentException;
|
||||
use MailPoetVendor\Doctrine\Common\Proxy\Exception\UnexpectedValueException;
|
||||
use MailPoetVendor\Doctrine\Common\Util\ClassUtils;
|
||||
use MailPoetVendor\Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use ReflectionIntersectionType;
|
||||
use ReflectionMethod;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionParameter;
|
||||
use ReflectionProperty;
|
||||
use ReflectionType;
|
||||
use ReflectionUnionType;
|
||||
use function array_combine;
|
||||
use function array_diff;
|
||||
use function array_key_exists;
|
||||
use function array_map;
|
||||
use function array_slice;
|
||||
use function array_unique;
|
||||
use function assert;
|
||||
use function bin2hex;
|
||||
use function call_user_func;
|
||||
use function chmod;
|
||||
use function class_exists;
|
||||
use function dirname;
|
||||
use function explode;
|
||||
use function file;
|
||||
use function file_put_contents;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function interface_exists;
|
||||
use function is_callable;
|
||||
use function is_dir;
|
||||
use function is_scalar;
|
||||
use function is_string;
|
||||
use function is_writable;
|
||||
use function lcfirst;
|
||||
use function ltrim;
|
||||
use function method_exists;
|
||||
use function mkdir;
|
||||
use function preg_match;
|
||||
use function preg_match_all;
|
||||
use function preg_replace;
|
||||
use function preg_split;
|
||||
use function random_bytes;
|
||||
use function rename;
|
||||
use function rtrim;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
use function strpos;
|
||||
use function strrev;
|
||||
use function strtolower;
|
||||
use function strtr;
|
||||
use function substr;
|
||||
use function trim;
|
||||
use function var_export;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const PHP_VERSION_ID;
|
||||
use const PREG_SPLIT_DELIM_CAPTURE;
|
||||
class ProxyGenerator
|
||||
{
|
||||
public const PATTERN_MATCH_ID_METHOD = <<<'EOT'
|
||||
((?(DEFINE)
|
||||
(?<type>\\?[a-z_\x7f-\xff][\w\x7f-\xff]*(?:\\[a-z_\x7f-\xff][\w\x7f-\xff]*)*)
|
||||
(?<intersection_type>(?&type)\s*&\s*(?&type))
|
||||
(?<union_type>(?:(?:\(\s*(?&intersection_type)\s*\))|(?&type))(?:\s*\|\s*(?:(?:\(\s*(?&intersection_type)\s*\))|(?&type)))+)
|
||||
)(?:public\s+)?(?:function\s+%s\s*\(\)\s*)\s*(?::\s*(?:(?&union_type)|(?&intersection_type)|(?:\??(?&type)))\s*)?{\s*return\s*\$this->%s;\s*})i
|
||||
EOT;
|
||||
private $proxyNamespace;
|
||||
private $proxyDirectory;
|
||||
protected $placeholders = ['baseProxyInterface' => Proxy::class, 'additionalProperties' => ''];
|
||||
protected $proxyClassTemplate = '<?php
|
||||
namespace <namespace>;
|
||||
<enumUseStatements>
|
||||
class <proxyShortClassName> extends \\<className> implements \\<baseProxyInterface>
|
||||
{
|
||||
public $__initializer__;
|
||||
public $__cloner__;
|
||||
public $__isInitialized__ = false;
|
||||
public static $lazyPropertiesNames = <lazyPropertiesNames>;
|
||||
public static $lazyPropertiesDefaults = <lazyPropertiesDefaults>;
|
||||
<additionalProperties>
|
||||
<constructorImpl>
|
||||
<magicGet>
|
||||
<magicSet>
|
||||
<magicIsset>
|
||||
<sleepImpl>
|
||||
<wakeupImpl>
|
||||
<cloneImpl>
|
||||
public function __load(): void
|
||||
{
|
||||
$this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', []);
|
||||
}
|
||||
public function __isInitialized(): bool
|
||||
{
|
||||
return $this->__isInitialized__;
|
||||
}
|
||||
public function __setInitialized($initialized): void
|
||||
{
|
||||
$this->__isInitialized__ = $initialized;
|
||||
}
|
||||
public function __setInitializer(?\\Closure $initializer = null): void
|
||||
{
|
||||
$this->__initializer__ = $initializer;
|
||||
}
|
||||
public function __getInitializer(): ?\\Closure
|
||||
{
|
||||
return $this->__initializer__;
|
||||
}
|
||||
public function __setCloner(?\\Closure $cloner = null): void
|
||||
{
|
||||
$this->__cloner__ = $cloner;
|
||||
}
|
||||
public function __getCloner(): ?\\Closure
|
||||
{
|
||||
return $this->__cloner__;
|
||||
}
|
||||
public function __getLazyProperties(): array
|
||||
{
|
||||
return self::$lazyPropertiesDefaults;
|
||||
}
|
||||
<methods>
|
||||
}
|
||||
';
|
||||
public function __construct($proxyDirectory, $proxyNamespace)
|
||||
{
|
||||
if (!$proxyDirectory) {
|
||||
throw InvalidArgumentException::proxyDirectoryRequired();
|
||||
}
|
||||
if (!$proxyNamespace) {
|
||||
throw InvalidArgumentException::proxyNamespaceRequired();
|
||||
}
|
||||
$this->proxyDirectory = $proxyDirectory;
|
||||
$this->proxyNamespace = $proxyNamespace;
|
||||
}
|
||||
public function setPlaceholder($name, $placeholder)
|
||||
{
|
||||
if (!is_string($placeholder) && !is_callable($placeholder)) {
|
||||
throw InvalidArgumentException::invalidPlaceholder($name);
|
||||
}
|
||||
$this->placeholders[$name] = $placeholder;
|
||||
}
|
||||
public function setProxyClassTemplate($proxyClassTemplate)
|
||||
{
|
||||
$this->proxyClassTemplate = (string) $proxyClassTemplate;
|
||||
}
|
||||
public function generateProxyClass(ClassMetadata $class, $fileName = \false)
|
||||
{
|
||||
$this->verifyClassCanBeProxied($class);
|
||||
preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches);
|
||||
$placeholderMatches = array_combine($placeholderMatches[0], $placeholderMatches[1]);
|
||||
$placeholders = [];
|
||||
foreach ($placeholderMatches as $placeholder => $name) {
|
||||
$placeholders[$placeholder] = $this->placeholders[$name] ?? [$this, 'generate' . $name];
|
||||
}
|
||||
foreach ($placeholders as &$placeholder) {
|
||||
if (!is_callable($placeholder)) {
|
||||
continue;
|
||||
}
|
||||
$placeholder = call_user_func($placeholder, $class);
|
||||
}
|
||||
$proxyCode = strtr($this->proxyClassTemplate, $placeholders);
|
||||
if (!$fileName) {
|
||||
$proxyClassName = $this->generateNamespace($class) . '\\' . $this->generateProxyShortClassName($class);
|
||||
if (!class_exists($proxyClassName)) {
|
||||
eval(substr($proxyCode, 5));
|
||||
}
|
||||
return;
|
||||
}
|
||||
$parentDirectory = dirname($fileName);
|
||||
if (!is_dir($parentDirectory) && @mkdir($parentDirectory, 0775, \true) === \false) {
|
||||
throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory);
|
||||
}
|
||||
if (!is_writable($parentDirectory)) {
|
||||
throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory);
|
||||
}
|
||||
$tmpFileName = $fileName . '.' . bin2hex(random_bytes(12));
|
||||
file_put_contents($tmpFileName, $proxyCode);
|
||||
@chmod($tmpFileName, 0664);
|
||||
rename($tmpFileName, $fileName);
|
||||
}
|
||||
private function verifyClassCanBeProxied(ClassMetadata $class)
|
||||
{
|
||||
if ($class->getReflectionClass()->isFinal()) {
|
||||
throw InvalidArgumentException::classMustNotBeFinal($class->getName());
|
||||
}
|
||||
if ($class->getReflectionClass()->isAbstract()) {
|
||||
throw InvalidArgumentException::classMustNotBeAbstract($class->getName());
|
||||
}
|
||||
if (PHP_VERSION_ID >= 80200 && $class->getReflectionClass()->isReadOnly()) {
|
||||
throw InvalidArgumentException::classMustNotBeReadOnly($class->getName());
|
||||
}
|
||||
}
|
||||
private function generateProxyShortClassName(ClassMetadata $class)
|
||||
{
|
||||
$proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace);
|
||||
$parts = explode('\\', strrev($proxyClassName), 2);
|
||||
return strrev($parts[0]);
|
||||
}
|
||||
private function generateNamespace(ClassMetadata $class)
|
||||
{
|
||||
$proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace);
|
||||
$parts = explode('\\', strrev($proxyClassName), 2);
|
||||
return strrev($parts[1]);
|
||||
}
|
||||
public function generateEnumUseStatements(ClassMetadata $class) : string
|
||||
{
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
return "\n";
|
||||
}
|
||||
$defaultProperties = $class->getReflectionClass()->getDefaultProperties();
|
||||
$lazyLoadedPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
|
||||
$enumClasses = [];
|
||||
foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
|
||||
$name = $property->getName();
|
||||
if (!in_array($name, $lazyLoadedPublicProperties, \true)) {
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($name, $defaultProperties) && $defaultProperties[$name] instanceof BackedEnum) {
|
||||
$enumClassNameParts = explode('\\', get_class($defaultProperties[$name]));
|
||||
$enumClasses[] = $enumClassNameParts[0];
|
||||
}
|
||||
}
|
||||
return implode("\n", array_map(static function ($className) {
|
||||
return 'use ' . $className . ';';
|
||||
}, array_unique($enumClasses))) . "\n";
|
||||
}
|
||||
private function generateClassName(ClassMetadata $class)
|
||||
{
|
||||
return ltrim($class->getName(), '\\');
|
||||
}
|
||||
private function generateLazyPropertiesNames(ClassMetadata $class)
|
||||
{
|
||||
$lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
|
||||
$values = [];
|
||||
foreach ($lazyPublicProperties as $name) {
|
||||
$values[$name] = null;
|
||||
}
|
||||
return var_export($values, \true);
|
||||
}
|
||||
private function generateLazyPropertiesDefaults(ClassMetadata $class)
|
||||
{
|
||||
return var_export($this->getLazyLoadedPublicProperties($class), \true);
|
||||
}
|
||||
private function generateConstructorImpl(ClassMetadata $class)
|
||||
{
|
||||
$constructorImpl = <<<'EOT'
|
||||
public function __construct(?\Closure $initializer = null, ?\Closure $cloner = null)
|
||||
{
|
||||
EOT;
|
||||
$toUnset = array_map(static function (string $name) : string {
|
||||
return '$this->' . $name;
|
||||
}, $this->getLazyLoadedPublicPropertiesNames($class));
|
||||
return $constructorImpl . ($toUnset === [] ? '' : ' unset(' . implode(', ', $toUnset) . ");\n") . <<<'EOT'
|
||||
$this->__initializer__ = $initializer;
|
||||
$this->__cloner__ = $cloner;
|
||||
}
|
||||
EOT;
|
||||
}
|
||||
private function generateMagicGet(ClassMetadata $class)
|
||||
{
|
||||
$lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
|
||||
$reflectionClass = $class->getReflectionClass();
|
||||
$hasParentGet = \false;
|
||||
$returnReference = '';
|
||||
$inheritDoc = '';
|
||||
$name = '$name';
|
||||
$parametersString = '$name';
|
||||
$returnTypeHint = null;
|
||||
if ($reflectionClass->hasMethod('__get')) {
|
||||
$hasParentGet = \true;
|
||||
$inheritDoc = '{@inheritDoc}';
|
||||
$methodReflection = $reflectionClass->getMethod('__get');
|
||||
if ($methodReflection->returnsReference()) {
|
||||
$returnReference = '& ';
|
||||
}
|
||||
$methodParameters = $methodReflection->getParameters();
|
||||
$name = '$' . $methodParameters[0]->getName();
|
||||
$parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name']);
|
||||
$returnTypeHint = $this->getMethodReturnType($methodReflection);
|
||||
}
|
||||
if (empty($lazyPublicProperties) && !$hasParentGet) {
|
||||
return '';
|
||||
}
|
||||
$magicGet = <<<EOT
|
||||
public function {$returnReference}__get({$parametersString}){$returnTypeHint}
|
||||
{
|
||||
EOT;
|
||||
if (!empty($lazyPublicProperties)) {
|
||||
$magicGet .= <<<'EOT'
|
||||
if (\array_key_exists($name, self::$lazyPropertiesNames)) {
|
||||
$this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]);
|
||||
EOT;
|
||||
if ($returnTypeHint === ': void') {
|
||||
$magicGet .= "\n return;";
|
||||
} else {
|
||||
$magicGet .= "\n return \$this->\$name;";
|
||||
}
|
||||
$magicGet .= <<<'EOT'
|
||||
}
|
||||
EOT;
|
||||
}
|
||||
if ($hasParentGet) {
|
||||
$magicGet .= <<<'EOT'
|
||||
$this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]);
|
||||
EOT;
|
||||
if ($returnTypeHint === ': void') {
|
||||
$magicGet .= <<<'EOT'
|
||||
parent::__get($name);
|
||||
return;
|
||||
EOT;
|
||||
} elseif ($returnTypeHint === ': never') {
|
||||
$magicGet .= <<<'EOT'
|
||||
parent::__get($name);
|
||||
EOT;
|
||||
} else {
|
||||
$magicGet .= <<<'EOT'
|
||||
return parent::__get($name);
|
||||
EOT;
|
||||
}
|
||||
} else {
|
||||
$magicGet .= sprintf(<<<EOT
|
||||
trigger_error(sprintf('Undefined property: %%s::\$%%s', __CLASS__, %s), E_USER_NOTICE);
|
||||
EOT
|
||||
, $name);
|
||||
}
|
||||
return $magicGet . "\n }";
|
||||
}
|
||||
private function generateMagicSet(ClassMetadata $class)
|
||||
{
|
||||
$lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
|
||||
$reflectionClass = $class->getReflectionClass();
|
||||
$hasParentSet = \false;
|
||||
$inheritDoc = '';
|
||||
$parametersString = '$name, $value';
|
||||
$returnTypeHint = null;
|
||||
if ($reflectionClass->hasMethod('__set')) {
|
||||
$hasParentSet = \true;
|
||||
$inheritDoc = '{@inheritDoc}';
|
||||
$methodReflection = $reflectionClass->getMethod('__set');
|
||||
$parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name', 'value']);
|
||||
$returnTypeHint = $this->getMethodReturnType($methodReflection);
|
||||
}
|
||||
if (empty($lazyPublicProperties) && !$hasParentSet) {
|
||||
return '';
|
||||
}
|
||||
$magicSet = <<<EOT
|
||||
public function __set({$parametersString}){$returnTypeHint}
|
||||
{
|
||||
EOT;
|
||||
if (!empty($lazyPublicProperties)) {
|
||||
$magicSet .= <<<'EOT'
|
||||
if (\array_key_exists($name, self::$lazyPropertiesNames)) {
|
||||
$this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]);
|
||||
$this->$name = $value;
|
||||
return;
|
||||
}
|
||||
EOT;
|
||||
}
|
||||
if ($hasParentSet) {
|
||||
$magicSet .= <<<'EOT'
|
||||
$this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]);
|
||||
EOT;
|
||||
if ($returnTypeHint === ': void') {
|
||||
$magicSet .= <<<'EOT'
|
||||
parent::__set($name, $value);
|
||||
return;
|
||||
EOT;
|
||||
} elseif ($returnTypeHint === ': never') {
|
||||
$magicSet .= <<<'EOT'
|
||||
parent::__set($name, $value);
|
||||
EOT;
|
||||
} else {
|
||||
$magicSet .= <<<'EOT'
|
||||
return parent::__set($name, $value);
|
||||
EOT;
|
||||
}
|
||||
} else {
|
||||
$magicSet .= ' $this->$name = $value;';
|
||||
}
|
||||
return $magicSet . "\n }";
|
||||
}
|
||||
private function generateMagicIsset(ClassMetadata $class)
|
||||
{
|
||||
$lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
|
||||
$hasParentIsset = $class->getReflectionClass()->hasMethod('__isset');
|
||||
$parametersString = '$name';
|
||||
$returnTypeHint = null;
|
||||
if ($hasParentIsset) {
|
||||
$methodReflection = $class->getReflectionClass()->getMethod('__isset');
|
||||
$parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name']);
|
||||
$returnTypeHint = $this->getMethodReturnType($methodReflection);
|
||||
}
|
||||
if (empty($lazyPublicProperties) && !$hasParentIsset) {
|
||||
return '';
|
||||
}
|
||||
$inheritDoc = $hasParentIsset ? '{@inheritDoc}' : '';
|
||||
$magicIsset = <<<EOT
|
||||
public function __isset({$parametersString}){$returnTypeHint}
|
||||
{
|
||||
EOT;
|
||||
if (!empty($lazyPublicProperties)) {
|
||||
$magicIsset .= <<<'EOT'
|
||||
if (\array_key_exists($name, self::$lazyPropertiesNames)) {
|
||||
$this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]);
|
||||
return isset($this->$name);
|
||||
}
|
||||
EOT;
|
||||
}
|
||||
if ($hasParentIsset) {
|
||||
$magicIsset .= <<<'EOT'
|
||||
$this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]);
|
||||
return parent::__isset($name);
|
||||
EOT;
|
||||
} else {
|
||||
$magicIsset .= ' return false;';
|
||||
}
|
||||
return $magicIsset . "\n }";
|
||||
}
|
||||
private function generateSleepImpl(ClassMetadata $class)
|
||||
{
|
||||
$reflectionClass = $class->getReflectionClass();
|
||||
$hasParentSleep = $reflectionClass->hasMethod('__sleep');
|
||||
$inheritDoc = $hasParentSleep ? '{@inheritDoc}' : '';
|
||||
$returnTypeHint = $hasParentSleep ? $this->getMethodReturnType($reflectionClass->getMethod('__sleep')) : '';
|
||||
$sleepImpl = <<<EOT
|
||||
public function __sleep(){$returnTypeHint}
|
||||
{
|
||||
EOT;
|
||||
if ($hasParentSleep) {
|
||||
return $sleepImpl . <<<'EOT'
|
||||
$properties = array_merge(['__isInitialized__'], parent::__sleep());
|
||||
if ($this->__isInitialized__) {
|
||||
$properties = array_diff($properties, array_keys(self::$lazyPropertiesNames));
|
||||
}
|
||||
return $properties;
|
||||
}
|
||||
EOT;
|
||||
}
|
||||
$allProperties = ['__isInitialized__'];
|
||||
foreach ($class->getReflectionClass()->getProperties() as $prop) {
|
||||
assert($prop instanceof ReflectionProperty);
|
||||
if ($prop->isStatic()) {
|
||||
continue;
|
||||
}
|
||||
$allProperties[] = $prop->isPrivate() ? "\x00" . $prop->getDeclaringClass()->getName() . "\x00" . $prop->getName() : $prop->getName();
|
||||
}
|
||||
$lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
|
||||
$protectedProperties = array_diff($allProperties, $lazyPublicProperties);
|
||||
foreach ($allProperties as &$property) {
|
||||
$property = var_export($property, \true);
|
||||
}
|
||||
foreach ($protectedProperties as &$property) {
|
||||
$property = var_export($property, \true);
|
||||
}
|
||||
$allProperties = implode(', ', $allProperties);
|
||||
$protectedProperties = implode(', ', $protectedProperties);
|
||||
return $sleepImpl . <<<EOT
|
||||
if (\$this->__isInitialized__) {
|
||||
return [{$allProperties}];
|
||||
}
|
||||
return [{$protectedProperties}];
|
||||
}
|
||||
EOT;
|
||||
}
|
||||
private function generateWakeupImpl(ClassMetadata $class)
|
||||
{
|
||||
$reflectionClass = $class->getReflectionClass();
|
||||
$hasParentWakeup = $reflectionClass->hasMethod('__wakeup');
|
||||
$unsetPublicProperties = [];
|
||||
foreach ($this->getLazyLoadedPublicPropertiesNames($class) as $lazyPublicProperty) {
|
||||
$unsetPublicProperties[] = '$this->' . $lazyPublicProperty;
|
||||
}
|
||||
$shortName = $this->generateProxyShortClassName($class);
|
||||
$inheritDoc = $hasParentWakeup ? '{@inheritDoc}' : '';
|
||||
$returnTypeHint = $hasParentWakeup ? $this->getMethodReturnType($reflectionClass->getMethod('__wakeup')) : '';
|
||||
$wakeupImpl = <<<EOT
|
||||
public function __wakeup(){$returnTypeHint}
|
||||
{
|
||||
if ( ! \$this->__isInitialized__) {
|
||||
\$this->__initializer__ = function ({$shortName} \$proxy) {
|
||||
\$proxy->__setInitializer(null);
|
||||
\$proxy->__setCloner(null);
|
||||
\$existingProperties = get_object_vars(\$proxy);
|
||||
foreach (\$proxy::\$lazyPropertiesDefaults as \$property => \$defaultValue) {
|
||||
if ( ! array_key_exists(\$property, \$existingProperties)) {
|
||||
\$proxy->\$property = \$defaultValue;
|
||||
}
|
||||
}
|
||||
};
|
||||
EOT;
|
||||
if (!empty($unsetPublicProperties)) {
|
||||
$wakeupImpl .= "\n unset(" . implode(', ', $unsetPublicProperties) . ');';
|
||||
}
|
||||
$wakeupImpl .= "\n }";
|
||||
if ($hasParentWakeup) {
|
||||
$wakeupImpl .= "\n parent::__wakeup();";
|
||||
}
|
||||
$wakeupImpl .= "\n }";
|
||||
return $wakeupImpl;
|
||||
}
|
||||
private function generateCloneImpl(ClassMetadata $class)
|
||||
{
|
||||
$reflectionClass = $class->getReflectionClass();
|
||||
$hasParentClone = $reflectionClass->hasMethod('__clone');
|
||||
$returnTypeHint = $hasParentClone ? $this->getMethodReturnType($reflectionClass->getMethod('__clone')) : '';
|
||||
$inheritDoc = $hasParentClone ? '{@inheritDoc}' : '';
|
||||
$callParentClone = $hasParentClone ? "\n parent::__clone();\n" : '';
|
||||
return <<<EOT
|
||||
public function __clone(){$returnTypeHint}
|
||||
{
|
||||
\$this->__cloner__ && \$this->__cloner__->__invoke(\$this, '__clone', []);
|
||||
{$callParentClone} }
|
||||
EOT;
|
||||
}
|
||||
private function generateMethods(ClassMetadata $class)
|
||||
{
|
||||
$methods = '';
|
||||
$methodNames = [];
|
||||
$reflectionMethods = $class->getReflectionClass()->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||
$skippedMethods = ['__sleep' => \true, '__clone' => \true, '__wakeup' => \true, '__get' => \true, '__set' => \true, '__isset' => \true];
|
||||
foreach ($reflectionMethods as $method) {
|
||||
$name = $method->getName();
|
||||
if ($method->isConstructor() || isset($skippedMethods[strtolower($name)]) || isset($methodNames[$name]) || $method->isFinal() || $method->isStatic() || !$method->isPublic()) {
|
||||
continue;
|
||||
}
|
||||
$methodNames[$name] = \true;
|
||||
$methods .= "\n /**\n" . " * {@inheritDoc}\n" . " */\n" . ' public function ';
|
||||
if ($method->returnsReference()) {
|
||||
$methods .= '&';
|
||||
}
|
||||
$methods .= $name . '(' . $this->buildParametersString($method->getParameters()) . ')';
|
||||
$methods .= $this->getMethodReturnType($method);
|
||||
$methods .= "\n" . ' {' . "\n";
|
||||
if ($this->isShortIdentifierGetter($method, $class)) {
|
||||
$identifier = lcfirst(substr($name, 3));
|
||||
$fieldType = $class->getTypeOfField($identifier);
|
||||
$cast = in_array($fieldType, ['integer', 'smallint'], \true) ? '(int) ' : '';
|
||||
$methods .= ' if ($this->__isInitialized__ === false) {' . "\n";
|
||||
$methods .= ' ';
|
||||
$methods .= $this->shouldProxiedMethodReturn($method) ? 'return ' : '';
|
||||
$methods .= $cast . ' parent::' . $method->getName() . "();\n";
|
||||
$methods .= ' }' . "\n\n";
|
||||
}
|
||||
$invokeParamsString = implode(', ', $this->getParameterNamesForInvoke($method->getParameters()));
|
||||
$callParamsString = implode(', ', $this->getParameterNamesForParentCall($method->getParameters()));
|
||||
$methods .= "\n \$this->__initializer__ " . '&& $this->__initializer__->__invoke($this, ' . var_export($name, \true) . ', [' . $invokeParamsString . ']);' . "\n\n " . ($this->shouldProxiedMethodReturn($method) ? 'return ' : '') . 'parent::' . $name . '(' . $callParamsString . ');' . "\n" . ' }' . "\n";
|
||||
}
|
||||
return $methods;
|
||||
}
|
||||
public function getProxyFileName($className, $baseDirectory = null)
|
||||
{
|
||||
$baseDirectory = $baseDirectory ?: $this->proxyDirectory;
|
||||
return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . Proxy::MARKER . str_replace('\\', '', $className) . '.php';
|
||||
}
|
||||
private function isShortIdentifierGetter($method, ClassMetadata $class)
|
||||
{
|
||||
$identifier = lcfirst(substr($method->getName(), 3));
|
||||
$startLine = $method->getStartLine();
|
||||
$endLine = $method->getEndLine();
|
||||
$cheapCheck = $method->getNumberOfParameters() === 0 && substr($method->getName(), 0, 3) === 'get' && in_array($identifier, $class->getIdentifier(), \true) && $class->hasField($identifier) && $endLine - $startLine <= 4;
|
||||
if ($cheapCheck) {
|
||||
$code = file($method->getFileName());
|
||||
$code = trim(implode(' ', array_slice($code, $startLine - 1, $endLine - $startLine + 1)));
|
||||
$pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier);
|
||||
if (preg_match($pattern, $code)) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
private function getLazyLoadedPublicPropertiesNames(ClassMetadata $class) : array
|
||||
{
|
||||
$properties = [];
|
||||
foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
|
||||
$name = $property->getName();
|
||||
if (!$class->hasField($name) && !$class->hasAssociation($name) || $class->isIdentifier($name)) {
|
||||
continue;
|
||||
}
|
||||
$properties[] = $name;
|
||||
}
|
||||
return $properties;
|
||||
}
|
||||
private function getLazyLoadedPublicProperties(ClassMetadata $class)
|
||||
{
|
||||
$defaultProperties = $class->getReflectionClass()->getDefaultProperties();
|
||||
$lazyLoadedPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
|
||||
$defaultValues = [];
|
||||
foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
|
||||
$name = $property->getName();
|
||||
if (!in_array($name, $lazyLoadedPublicProperties, \true)) {
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($name, $defaultProperties)) {
|
||||
$defaultValues[$name] = $defaultProperties[$name];
|
||||
} elseif (method_exists($property, 'getType')) {
|
||||
$propertyType = $property->getType();
|
||||
if ($propertyType !== null && $propertyType->allowsNull()) {
|
||||
$defaultValues[$name] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $defaultValues;
|
||||
}
|
||||
private function buildParametersString(array $parameters, array $renameParameters = [])
|
||||
{
|
||||
$parameterDefinitions = [];
|
||||
$i = -1;
|
||||
foreach ($parameters as $param) {
|
||||
assert($param instanceof ReflectionParameter);
|
||||
$i++;
|
||||
$parameterDefinition = '';
|
||||
$parameterType = $this->getParameterType($param);
|
||||
if ($parameterType !== null) {
|
||||
$parameterDefinition .= $parameterType . ' ';
|
||||
}
|
||||
if ($param->isPassedByReference()) {
|
||||
$parameterDefinition .= '&';
|
||||
}
|
||||
if ($param->isVariadic()) {
|
||||
$parameterDefinition .= '...';
|
||||
}
|
||||
$parameterDefinition .= '$' . ($renameParameters ? $renameParameters[$i] : $param->getName());
|
||||
$parameterDefinition .= $this->getParameterDefaultValue($param);
|
||||
$parameterDefinitions[] = $parameterDefinition;
|
||||
}
|
||||
return implode(', ', $parameterDefinitions);
|
||||
}
|
||||
private function getParameterType(ReflectionParameter $parameter)
|
||||
{
|
||||
if (!$parameter->hasType()) {
|
||||
return null;
|
||||
}
|
||||
$declaringFunction = $parameter->getDeclaringFunction();
|
||||
assert($declaringFunction instanceof ReflectionMethod);
|
||||
return $this->formatType($parameter->getType(), $declaringFunction, $parameter);
|
||||
}
|
||||
private function getParameterDefaultValue(ReflectionParameter $parameter)
|
||||
{
|
||||
if (!$parameter->isDefaultValueAvailable()) {
|
||||
return '';
|
||||
}
|
||||
if (PHP_VERSION_ID < 80100 || is_scalar($parameter->getDefaultValue())) {
|
||||
return ' = ' . var_export($parameter->getDefaultValue(), \true);
|
||||
}
|
||||
$value = rtrim(substr(explode('$' . $parameter->getName() . ' = ', (string) $parameter, 2)[1], 0, -2));
|
||||
if (strpos($value, '\\') !== \false || strpos($value, '::') !== \false) {
|
||||
$value = preg_split("/('(?:[^'\\\\]*+(?:\\\\.)*+)*+')/", $value, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
foreach ($value as $i => $part) {
|
||||
if ($i % 2 === 0) {
|
||||
$value[$i] = preg_replace('/(?<![a-zA-Z0-9_\\x7f-\\xff\\\\])[a-zA-Z0-9_\\x7f-\\xff]++(?:\\\\[a-zA-Z0-9_\\x7f-\\xff]++|::)++/', '\\\\\\0', $part);
|
||||
}
|
||||
}
|
||||
$value = implode('', $value);
|
||||
}
|
||||
return ' = ' . $value;
|
||||
}
|
||||
private function getParameterNamesForInvoke(array $parameters)
|
||||
{
|
||||
return array_map(static function (ReflectionParameter $parameter) {
|
||||
return '$' . $parameter->getName();
|
||||
}, $parameters);
|
||||
}
|
||||
private function getParameterNamesForParentCall(array $parameters)
|
||||
{
|
||||
return array_map(static function (ReflectionParameter $parameter) {
|
||||
$name = '';
|
||||
if ($parameter->isVariadic()) {
|
||||
$name .= '...';
|
||||
}
|
||||
$name .= '$' . $parameter->getName();
|
||||
return $name;
|
||||
}, $parameters);
|
||||
}
|
||||
private function getMethodReturnType(ReflectionMethod $method)
|
||||
{
|
||||
if (!$method->hasReturnType()) {
|
||||
return '';
|
||||
}
|
||||
return ': ' . $this->formatType($method->getReturnType(), $method);
|
||||
}
|
||||
private function shouldProxiedMethodReturn(ReflectionMethod $method)
|
||||
{
|
||||
if (!$method->hasReturnType()) {
|
||||
return \true;
|
||||
}
|
||||
return !in_array(strtolower($this->formatType($method->getReturnType(), $method)), ['void', 'never'], \true);
|
||||
}
|
||||
private function formatType(ReflectionType $type, ReflectionMethod $method, ?ReflectionParameter $parameter = null)
|
||||
{
|
||||
if ($type instanceof ReflectionUnionType) {
|
||||
return implode('|', array_map(function (ReflectionType $unionedType) use($method, $parameter) {
|
||||
if ($unionedType instanceof ReflectionIntersectionType) {
|
||||
return '(' . $this->formatType($unionedType, $method, $parameter) . ')';
|
||||
}
|
||||
return $this->formatType($unionedType, $method, $parameter);
|
||||
}, $type->getTypes()));
|
||||
}
|
||||
if ($type instanceof ReflectionIntersectionType) {
|
||||
return implode('&', array_map(function (ReflectionType $intersectedType) use($method, $parameter) {
|
||||
return $this->formatType($intersectedType, $method, $parameter);
|
||||
}, $type->getTypes()));
|
||||
}
|
||||
assert($type instanceof ReflectionNamedType);
|
||||
$name = $type->getName();
|
||||
$nameLower = strtolower($name);
|
||||
if ($nameLower === 'static') {
|
||||
$name = 'static';
|
||||
}
|
||||
if ($nameLower === 'self') {
|
||||
$name = $method->getDeclaringClass()->getName();
|
||||
}
|
||||
if ($nameLower === 'parent') {
|
||||
$name = $method->getDeclaringClass()->getParentClass()->getName();
|
||||
}
|
||||
if (!$type->isBuiltin() && !class_exists($name) && !interface_exists($name) && $name !== 'static') {
|
||||
if ($parameter !== null) {
|
||||
throw UnexpectedValueException::invalidParameterTypeHint($method->getDeclaringClass()->getName(), $method->getName(), $parameter->getName());
|
||||
}
|
||||
throw UnexpectedValueException::invalidReturnTypeHint($method->getDeclaringClass()->getName(), $method->getName());
|
||||
}
|
||||
if (!$type->isBuiltin() && $name !== 'static') {
|
||||
$name = '\\' . $name;
|
||||
}
|
||||
if ($type->allowsNull() && !in_array($name, ['mixed', 'null'], \true) && ($parameter === null || !$parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== null)) {
|
||||
$name = '?' . $name;
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Util;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Persistence\Proxy;
|
||||
use ReflectionClass;
|
||||
use function get_class;
|
||||
use function get_parent_class;
|
||||
use function ltrim;
|
||||
use function rtrim;
|
||||
use function strrpos;
|
||||
use function substr;
|
||||
class ClassUtils
|
||||
{
|
||||
public static function getRealClass($className)
|
||||
{
|
||||
$pos = strrpos($className, '\\' . Proxy::MARKER . '\\');
|
||||
if ($pos === \false) {
|
||||
return $className;
|
||||
}
|
||||
return substr($className, $pos + Proxy::MARKER_LENGTH + 2);
|
||||
}
|
||||
public static function getClass($object)
|
||||
{
|
||||
return self::getRealClass(get_class($object));
|
||||
}
|
||||
public static function getParentClass($className)
|
||||
{
|
||||
return get_parent_class(self::getRealClass($className));
|
||||
}
|
||||
public static function newReflectionClass($className)
|
||||
{
|
||||
return new ReflectionClass(self::getRealClass($className));
|
||||
}
|
||||
public static function newReflectionObject($object)
|
||||
{
|
||||
return self::newReflectionClass(self::getClass($object));
|
||||
}
|
||||
public static function generateProxyClassName($className, $proxyNamespace)
|
||||
{
|
||||
return rtrim($proxyNamespace, '\\') . '\\' . Proxy::MARKER . '\\' . ltrim($className, '\\');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\Common\Util;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use ArrayIterator;
|
||||
use ArrayObject;
|
||||
use DateTimeInterface;
|
||||
use MailPoetVendor\Doctrine\Common\Collections\Collection;
|
||||
use MailPoetVendor\Doctrine\Persistence\Proxy;
|
||||
use stdClass;
|
||||
use function array_keys;
|
||||
use function count;
|
||||
use function end;
|
||||
use function explode;
|
||||
use function extension_loaded;
|
||||
use function get_class;
|
||||
use function html_entity_decode;
|
||||
use function ini_get;
|
||||
use function ini_set;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function method_exists;
|
||||
use function ob_end_clean;
|
||||
use function ob_get_contents;
|
||||
use function ob_start;
|
||||
use function spl_object_hash;
|
||||
use function strip_tags;
|
||||
use function var_dump;
|
||||
final class Debug
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
public static function dump($var, $maxDepth = 2, $stripTags = \true, $echo = \true)
|
||||
{
|
||||
$html = ini_get('html_errors');
|
||||
if ($html !== \true) {
|
||||
ini_set('html_errors', 'on');
|
||||
}
|
||||
if (extension_loaded('xdebug')) {
|
||||
ini_set('xdebug.var_display_max_depth', $maxDepth);
|
||||
}
|
||||
$var = self::export($var, $maxDepth);
|
||||
ob_start();
|
||||
var_dump($var);
|
||||
$dump = ob_get_contents();
|
||||
ob_end_clean();
|
||||
$dumpText = $stripTags ? strip_tags(html_entity_decode($dump)) : $dump;
|
||||
ini_set('html_errors', $html);
|
||||
if ($echo) {
|
||||
echo $dumpText;
|
||||
}
|
||||
return $dumpText;
|
||||
}
|
||||
public static function export($var, $maxDepth)
|
||||
{
|
||||
$return = null;
|
||||
$isObj = is_object($var);
|
||||
if ($var instanceof Collection) {
|
||||
$var = $var->toArray();
|
||||
}
|
||||
if (!$maxDepth) {
|
||||
return is_object($var) ? get_class($var) : (is_array($var) ? 'Array(' . count($var) . ')' : $var);
|
||||
}
|
||||
if (is_array($var)) {
|
||||
$return = [];
|
||||
foreach ($var as $k => $v) {
|
||||
$return[$k] = self::export($v, $maxDepth - 1);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
if (!$isObj) {
|
||||
return $var;
|
||||
}
|
||||
$return = new stdClass();
|
||||
if ($var instanceof DateTimeInterface) {
|
||||
$return->__CLASS__ = get_class($var);
|
||||
$return->date = $var->format('c');
|
||||
$return->timezone = $var->getTimezone()->getName();
|
||||
return $return;
|
||||
}
|
||||
$return->__CLASS__ = ClassUtils::getClass($var);
|
||||
if ($var instanceof Proxy) {
|
||||
$return->__IS_PROXY__ = \true;
|
||||
$return->__PROXY_INITIALIZED__ = $var->__isInitialized();
|
||||
}
|
||||
if ($var instanceof ArrayObject || $var instanceof ArrayIterator) {
|
||||
$return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1);
|
||||
}
|
||||
return self::fillReturnWithClassAttributes($var, $return, $maxDepth);
|
||||
}
|
||||
private static function fillReturnWithClassAttributes($var, stdClass $return, $maxDepth)
|
||||
{
|
||||
$clone = (array) $var;
|
||||
foreach (array_keys($clone) as $key) {
|
||||
$aux = explode("\x00", $key);
|
||||
$name = end($aux);
|
||||
if ($aux[0] === '') {
|
||||
$name .= ':' . ($aux[1] === '*' ? 'protected' : $aux[1] . ':private');
|
||||
}
|
||||
$return->{$name} = self::export($clone[$key], $maxDepth - 1);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
public static function toString($obj)
|
||||
{
|
||||
return method_exists($obj, '__toString') ? (string) $obj : get_class($obj) . '@' . spl_object_hash($obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
final class ArrayParameterType
|
||||
{
|
||||
public const INTEGER = ParameterType::INTEGER + Connection::ARRAY_PARAM_OFFSET;
|
||||
public const STRING = ParameterType::STRING + Connection::ARRAY_PARAM_OFFSET;
|
||||
public const ASCII = ParameterType::ASCII + Connection::ARRAY_PARAM_OFFSET;
|
||||
public const BINARY = ParameterType::BINARY + Connection::ARRAY_PARAM_OFFSET;
|
||||
public static function toElementParameterType(int $type) : int
|
||||
{
|
||||
return $type - Connection::ARRAY_PARAM_OFFSET;
|
||||
}
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\ArrayParameters;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use Throwable;
|
||||
interface Exception extends Throwable
|
||||
{
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\ArrayParameters\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\ArrayParameters\Exception;
|
||||
use LogicException;
|
||||
use function sprintf;
|
||||
class MissingNamedParameter extends LogicException implements Exception
|
||||
{
|
||||
public static function new(string $name) : self
|
||||
{
|
||||
return new self(sprintf('Named parameter "%s" does not have a bound value.', $name));
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\ArrayParameters\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\ArrayParameters\Exception;
|
||||
use LogicException;
|
||||
use function sprintf;
|
||||
class MissingPositionalParameter extends LogicException implements Exception
|
||||
{
|
||||
public static function new(int $index) : self
|
||||
{
|
||||
return new self(sprintf('Positional parameter at index %d does not have a bound value.', $index));
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Cache;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\FetchUtils;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Result;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function reset;
|
||||
final class ArrayResult implements Result
|
||||
{
|
||||
private array $data;
|
||||
private int $columnCount = 0;
|
||||
private int $num = 0;
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
if (count($data) === 0) {
|
||||
return;
|
||||
}
|
||||
$this->columnCount = count($data[0]);
|
||||
}
|
||||
public function fetchNumeric()
|
||||
{
|
||||
$row = $this->fetch();
|
||||
if ($row === \false) {
|
||||
return \false;
|
||||
}
|
||||
return array_values($row);
|
||||
}
|
||||
public function fetchAssociative()
|
||||
{
|
||||
return $this->fetch();
|
||||
}
|
||||
public function fetchOne()
|
||||
{
|
||||
$row = $this->fetch();
|
||||
if ($row === \false) {
|
||||
return \false;
|
||||
}
|
||||
return reset($row);
|
||||
}
|
||||
public function fetchAllNumeric() : array
|
||||
{
|
||||
return FetchUtils::fetchAllNumeric($this);
|
||||
}
|
||||
public function fetchAllAssociative() : array
|
||||
{
|
||||
return FetchUtils::fetchAllAssociative($this);
|
||||
}
|
||||
public function fetchFirstColumn() : array
|
||||
{
|
||||
return FetchUtils::fetchFirstColumn($this);
|
||||
}
|
||||
public function rowCount() : int
|
||||
{
|
||||
return count($this->data);
|
||||
}
|
||||
public function columnCount() : int
|
||||
{
|
||||
return $this->columnCount;
|
||||
}
|
||||
public function free() : void
|
||||
{
|
||||
$this->data = [];
|
||||
}
|
||||
private function fetch()
|
||||
{
|
||||
if (!isset($this->data[$this->num])) {
|
||||
return \false;
|
||||
}
|
||||
return $this->data[$this->num++];
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Cache;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception;
|
||||
class CacheException extends Exception
|
||||
{
|
||||
public static function noCacheKey()
|
||||
{
|
||||
return new self('No cache key was set.');
|
||||
}
|
||||
public static function noResultDriverConfigured()
|
||||
{
|
||||
return new self('Trying to cache a query but no result driver is configured.');
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Cache;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\Cache;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\Psr6\DoctrineProvider;
|
||||
use MailPoetVendor\Doctrine\DBAL\Types\Type;
|
||||
use MailPoetVendor\Doctrine\Deprecations\Deprecation;
|
||||
use MailPoetVendor\Psr\Cache\CacheItemPoolInterface;
|
||||
use TypeError;
|
||||
use function get_class;
|
||||
use function hash;
|
||||
use function serialize;
|
||||
use function sha1;
|
||||
use function sprintf;
|
||||
class QueryCacheProfile
|
||||
{
|
||||
private ?CacheItemPoolInterface $resultCache = null;
|
||||
private $lifetime;
|
||||
private $cacheKey;
|
||||
public function __construct($lifetime = 0, $cacheKey = null, ?object $resultCache = null)
|
||||
{
|
||||
$this->lifetime = $lifetime;
|
||||
$this->cacheKey = $cacheKey;
|
||||
if ($resultCache instanceof CacheItemPoolInterface) {
|
||||
$this->resultCache = $resultCache;
|
||||
} elseif ($resultCache instanceof Cache) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', 'Passing an instance of %s to %s as $resultCache is deprecated. Pass an instance of %s instead.', Cache::class, __METHOD__, CacheItemPoolInterface::class);
|
||||
$this->resultCache = CacheAdapter::wrap($resultCache);
|
||||
} elseif ($resultCache !== null) {
|
||||
throw new TypeError(sprintf('$resultCache: Expected either null or an instance of %s or %s, got %s.', CacheItemPoolInterface::class, Cache::class, get_class($resultCache)));
|
||||
}
|
||||
}
|
||||
public function getResultCache() : ?CacheItemPoolInterface
|
||||
{
|
||||
return $this->resultCache;
|
||||
}
|
||||
public function getResultCacheDriver()
|
||||
{
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', '%s is deprecated, call getResultCache() instead.', __METHOD__);
|
||||
return $this->resultCache !== null ? DoctrineProvider::wrap($this->resultCache) : null;
|
||||
}
|
||||
public function getLifetime()
|
||||
{
|
||||
return $this->lifetime;
|
||||
}
|
||||
public function getCacheKey()
|
||||
{
|
||||
if ($this->cacheKey === null) {
|
||||
throw CacheException::noCacheKey();
|
||||
}
|
||||
return $this->cacheKey;
|
||||
}
|
||||
public function generateCacheKeys($sql, $params, $types, array $connectionParams = [])
|
||||
{
|
||||
if (isset($connectionParams['password'])) {
|
||||
unset($connectionParams['password']);
|
||||
}
|
||||
$realCacheKey = 'query=' . $sql . '¶ms=' . serialize($params) . '&types=' . serialize($types) . '&connectionParams=' . hash('sha256', serialize($connectionParams));
|
||||
// should the key be automatically generated using the inputs or is the cache key set?
|
||||
$cacheKey = $this->cacheKey ?? sha1($realCacheKey);
|
||||
return [$cacheKey, $realCacheKey];
|
||||
}
|
||||
public function setResultCache(CacheItemPoolInterface $cache) : QueryCacheProfile
|
||||
{
|
||||
return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache);
|
||||
}
|
||||
public function setResultCacheDriver(Cache $cache)
|
||||
{
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', '%s is deprecated, call setResultCache() instead.', __METHOD__);
|
||||
return new QueryCacheProfile($this->lifetime, $this->cacheKey, CacheAdapter::wrap($cache));
|
||||
}
|
||||
public function setCacheKey($cacheKey)
|
||||
{
|
||||
return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCache);
|
||||
}
|
||||
public function setLifetime($lifetime)
|
||||
{
|
||||
return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCache);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
final class ColumnCase
|
||||
{
|
||||
public const UPPER = 1;
|
||||
public const LOWER = 2;
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\Cache;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||
use MailPoetVendor\Doctrine\Common\Cache\Psr6\DoctrineProvider;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Middleware;
|
||||
use MailPoetVendor\Doctrine\DBAL\Logging\SQLLogger;
|
||||
use MailPoetVendor\Doctrine\DBAL\Schema\SchemaManagerFactory;
|
||||
use MailPoetVendor\Doctrine\Deprecations\Deprecation;
|
||||
use MailPoetVendor\Psr\Cache\CacheItemPoolInterface;
|
||||
use function func_num_args;
|
||||
class Configuration
|
||||
{
|
||||
private array $middlewares = [];
|
||||
protected $sqlLogger;
|
||||
private ?CacheItemPoolInterface $resultCache = null;
|
||||
protected $resultCacheImpl;
|
||||
protected $schemaAssetsFilter;
|
||||
protected $autoCommit = \true;
|
||||
private bool $disableTypeComments = \false;
|
||||
private ?SchemaManagerFactory $schemaManagerFactory = null;
|
||||
public function __construct()
|
||||
{
|
||||
$this->schemaAssetsFilter = static function () : bool {
|
||||
return \true;
|
||||
};
|
||||
}
|
||||
public function setSQLLogger(?SQLLogger $logger = null) : void
|
||||
{
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4967', '%s is deprecated, use setMiddlewares() and Logging\\Middleware instead.', __METHOD__);
|
||||
$this->sqlLogger = $logger;
|
||||
}
|
||||
public function getSQLLogger() : ?SQLLogger
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4967', '%s is deprecated.', __METHOD__);
|
||||
return $this->sqlLogger;
|
||||
}
|
||||
public function getResultCache() : ?CacheItemPoolInterface
|
||||
{
|
||||
return $this->resultCache;
|
||||
}
|
||||
public function getResultCacheImpl() : ?Cache
|
||||
{
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', '%s is deprecated, call getResultCache() instead.', __METHOD__);
|
||||
return $this->resultCacheImpl;
|
||||
}
|
||||
public function setResultCache(CacheItemPoolInterface $cache) : void
|
||||
{
|
||||
$this->resultCacheImpl = DoctrineProvider::wrap($cache);
|
||||
$this->resultCache = $cache;
|
||||
}
|
||||
public function setResultCacheImpl(Cache $cacheImpl) : void
|
||||
{
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', '%s is deprecated, call setResultCache() instead.', __METHOD__);
|
||||
$this->resultCacheImpl = $cacheImpl;
|
||||
$this->resultCache = CacheAdapter::wrap($cacheImpl);
|
||||
}
|
||||
public function setSchemaAssetsFilter(?callable $callable = null) : void
|
||||
{
|
||||
if (func_num_args() < 1) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5483', 'Not passing an argument to %s is deprecated.', __METHOD__);
|
||||
} elseif ($callable === null) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5483', 'Using NULL as a schema asset filter is deprecated.' . ' Use a callable that always returns true instead.');
|
||||
}
|
||||
$this->schemaAssetsFilter = $callable;
|
||||
}
|
||||
public function getSchemaAssetsFilter() : ?callable
|
||||
{
|
||||
return $this->schemaAssetsFilter;
|
||||
}
|
||||
public function setAutoCommit(bool $autoCommit) : void
|
||||
{
|
||||
$this->autoCommit = $autoCommit;
|
||||
}
|
||||
public function getAutoCommit() : bool
|
||||
{
|
||||
return $this->autoCommit;
|
||||
}
|
||||
public function setMiddlewares(array $middlewares) : self
|
||||
{
|
||||
$this->middlewares = $middlewares;
|
||||
return $this;
|
||||
}
|
||||
public function getMiddlewares() : array
|
||||
{
|
||||
return $this->middlewares;
|
||||
}
|
||||
public function getSchemaManagerFactory() : ?SchemaManagerFactory
|
||||
{
|
||||
return $this->schemaManagerFactory;
|
||||
}
|
||||
public function setSchemaManagerFactory(SchemaManagerFactory $schemaManagerFactory) : self
|
||||
{
|
||||
$this->schemaManagerFactory = $schemaManagerFactory;
|
||||
return $this;
|
||||
}
|
||||
public function getDisableTypeComments() : bool
|
||||
{
|
||||
return $this->disableTypeComments;
|
||||
}
|
||||
public function setDisableTypeComments(bool $disableTypeComments) : self
|
||||
{
|
||||
$this->disableTypeComments = $disableTypeComments;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,842 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use Closure;
|
||||
use MailPoetVendor\Doctrine\Common\EventManager;
|
||||
use MailPoetVendor\Doctrine\DBAL\Cache\ArrayResult;
|
||||
use MailPoetVendor\Doctrine\DBAL\Cache\CacheException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Cache\QueryCacheProfile;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\API\ExceptionConverter;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Connection as DriverConnection;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\ServerInfoAwareConnection;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Statement as DriverStatement;
|
||||
use MailPoetVendor\Doctrine\DBAL\Event\TransactionBeginEventArgs;
|
||||
use MailPoetVendor\Doctrine\DBAL\Event\TransactionCommitEventArgs;
|
||||
use MailPoetVendor\Doctrine\DBAL\Event\TransactionRollBackEventArgs;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\ConnectionLost;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\DriverException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\InvalidArgumentException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use MailPoetVendor\Doctrine\DBAL\Query\Expression\ExpressionBuilder;
|
||||
use MailPoetVendor\Doctrine\DBAL\Query\QueryBuilder;
|
||||
use MailPoetVendor\Doctrine\DBAL\Schema\AbstractSchemaManager;
|
||||
use MailPoetVendor\Doctrine\DBAL\Schema\DefaultSchemaManagerFactory;
|
||||
use MailPoetVendor\Doctrine\DBAL\Schema\LegacySchemaManagerFactory;
|
||||
use MailPoetVendor\Doctrine\DBAL\Schema\SchemaManagerFactory;
|
||||
use MailPoetVendor\Doctrine\DBAL\SQL\Parser;
|
||||
use MailPoetVendor\Doctrine\DBAL\Types\Type;
|
||||
use MailPoetVendor\Doctrine\Deprecations\Deprecation;
|
||||
use LogicException;
|
||||
use MailPoetVendor\SensitiveParameter;
|
||||
use Throwable;
|
||||
use Traversable;
|
||||
use function array_key_exists;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use function key;
|
||||
use function method_exists;
|
||||
use function sprintf;
|
||||
class Connection
|
||||
{
|
||||
public const PARAM_INT_ARRAY = ArrayParameterType::INTEGER;
|
||||
public const PARAM_STR_ARRAY = ArrayParameterType::STRING;
|
||||
public const PARAM_ASCII_STR_ARRAY = ArrayParameterType::ASCII;
|
||||
public const ARRAY_PARAM_OFFSET = 100;
|
||||
protected $_conn;
|
||||
protected $_config;
|
||||
protected $_eventManager;
|
||||
protected $_expr;
|
||||
private bool $autoCommit = \true;
|
||||
private int $transactionNestingLevel = 0;
|
||||
private $transactionIsolationLevel;
|
||||
private bool $nestTransactionsWithSavepoints = \false;
|
||||
private array $params;
|
||||
private ?AbstractPlatform $platform = null;
|
||||
private ?ExceptionConverter $exceptionConverter = null;
|
||||
private ?Parser $parser = null;
|
||||
protected $_schemaManager;
|
||||
protected $_driver;
|
||||
private bool $isRollbackOnly = \false;
|
||||
private SchemaManagerFactory $schemaManagerFactory;
|
||||
public function __construct( array $params, Driver $driver, ?Configuration $config = null, ?EventManager $eventManager = null)
|
||||
{
|
||||
$this->_driver = $driver;
|
||||
$this->params = $params;
|
||||
// Create default config and event manager if none given
|
||||
$config ??= new Configuration();
|
||||
$eventManager ??= new EventManager();
|
||||
$this->_config = $config;
|
||||
$this->_eventManager = $eventManager;
|
||||
if (isset($params['platform'])) {
|
||||
if (!$params['platform'] instanceof Platforms\AbstractPlatform) {
|
||||
throw Exception::invalidPlatformType($params['platform']);
|
||||
}
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5699', 'The "platform" connection parameter is deprecated.' . ' Use a driver middleware that would instantiate the platform instead.');
|
||||
$this->platform = $params['platform'];
|
||||
$this->platform->setEventManager($this->_eventManager);
|
||||
$this->platform->setDisableTypeComments($config->getDisableTypeComments());
|
||||
}
|
||||
$this->_expr = $this->createExpressionBuilder();
|
||||
$this->autoCommit = $config->getAutoCommit();
|
||||
$schemaManagerFactory = $config->getSchemaManagerFactory();
|
||||
if ($schemaManagerFactory === null) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5812', 'Not configuring a schema manager factory is deprecated.' . ' Use %s which is going to be the default in DBAL 4.', DefaultSchemaManagerFactory::class);
|
||||
$schemaManagerFactory = new LegacySchemaManagerFactory();
|
||||
}
|
||||
$this->schemaManagerFactory = $schemaManagerFactory;
|
||||
}
|
||||
public function getParams()
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
public function getDatabase()
|
||||
{
|
||||
$platform = $this->getDatabasePlatform();
|
||||
$query = $platform->getDummySelectSQL($platform->getCurrentDatabaseExpression());
|
||||
$database = $this->fetchOne($query);
|
||||
assert(is_string($database) || $database === null);
|
||||
return $database;
|
||||
}
|
||||
public function getDriver()
|
||||
{
|
||||
return $this->_driver;
|
||||
}
|
||||
public function getConfiguration()
|
||||
{
|
||||
return $this->_config;
|
||||
}
|
||||
public function getEventManager()
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', '%s is deprecated.', __METHOD__);
|
||||
return $this->_eventManager;
|
||||
}
|
||||
public function getDatabasePlatform()
|
||||
{
|
||||
if ($this->platform === null) {
|
||||
$this->platform = $this->detectDatabasePlatform();
|
||||
$this->platform->setEventManager($this->_eventManager);
|
||||
$this->platform->setDisableTypeComments($this->_config->getDisableTypeComments());
|
||||
}
|
||||
return $this->platform;
|
||||
}
|
||||
public function createExpressionBuilder() : ExpressionBuilder
|
||||
{
|
||||
return new ExpressionBuilder($this);
|
||||
}
|
||||
public function getExpressionBuilder()
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4515', 'Connection::getExpressionBuilder() is deprecated,' . ' use Connection::createExpressionBuilder() instead.');
|
||||
return $this->_expr;
|
||||
}
|
||||
public function connect()
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4966', 'Public access to Connection::connect() is deprecated.');
|
||||
if ($this->_conn !== null) {
|
||||
return \false;
|
||||
}
|
||||
try {
|
||||
$this->_conn = $this->_driver->connect($this->params);
|
||||
} catch (Driver\Exception $e) {
|
||||
throw $this->convertException($e);
|
||||
}
|
||||
if ($this->autoCommit === \false) {
|
||||
$this->beginTransaction();
|
||||
}
|
||||
if ($this->_eventManager->hasListeners(Events::postConnect)) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated. Implement a middleware instead.', Events::postConnect);
|
||||
$eventArgs = new Event\ConnectionEventArgs($this);
|
||||
$this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
private function detectDatabasePlatform() : AbstractPlatform
|
||||
{
|
||||
$version = $this->getDatabasePlatformVersion();
|
||||
if ($version !== null) {
|
||||
assert($this->_driver instanceof VersionAwarePlatformDriver);
|
||||
return $this->_driver->createDatabasePlatformForVersion($version);
|
||||
}
|
||||
return $this->_driver->getDatabasePlatform();
|
||||
}
|
||||
private function getDatabasePlatformVersion()
|
||||
{
|
||||
// Driver does not support version specific platforms.
|
||||
if (!$this->_driver instanceof VersionAwarePlatformDriver) {
|
||||
return null;
|
||||
}
|
||||
// Explicit platform version requested (supersedes auto-detection).
|
||||
if (isset($this->params['serverVersion'])) {
|
||||
return $this->params['serverVersion'];
|
||||
}
|
||||
if (isset($this->params['primary']) && isset($this->params['primary']['serverVersion'])) {
|
||||
return $this->params['primary']['serverVersion'];
|
||||
}
|
||||
// If not connected, we need to connect now to determine the platform version.
|
||||
if ($this->_conn === null) {
|
||||
try {
|
||||
$this->connect();
|
||||
} catch (Exception $originalException) {
|
||||
if (!isset($this->params['dbname'])) {
|
||||
throw $originalException;
|
||||
}
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5707', 'Relying on a fallback connection used to determine the database platform while connecting' . ' to a non-existing database is deprecated. Either use an existing database name in' . ' connection parameters or omit the database name if the platform' . ' and the server configuration allow that.');
|
||||
// The database to connect to might not yet exist.
|
||||
// Retry detection without database name connection parameter.
|
||||
$params = $this->params;
|
||||
unset($this->params['dbname']);
|
||||
try {
|
||||
$this->connect();
|
||||
} catch (Exception $fallbackException) {
|
||||
// Either the platform does not support database-less connections
|
||||
// or something else went wrong.
|
||||
throw $originalException;
|
||||
} finally {
|
||||
$this->params = $params;
|
||||
}
|
||||
$serverVersion = $this->getServerVersion();
|
||||
// Close "temporary" connection to allow connecting to the real database again.
|
||||
$this->close();
|
||||
return $serverVersion;
|
||||
}
|
||||
}
|
||||
return $this->getServerVersion();
|
||||
}
|
||||
private function getServerVersion()
|
||||
{
|
||||
$connection = $this->getWrappedConnection();
|
||||
// Automatic platform version detection.
|
||||
if ($connection instanceof ServerInfoAwareConnection) {
|
||||
try {
|
||||
return $connection->getServerVersion();
|
||||
} catch (Driver\Exception $e) {
|
||||
throw $this->convertException($e);
|
||||
}
|
||||
}
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4750', 'Not implementing the ServerInfoAwareConnection interface in %s is deprecated', get_class($connection));
|
||||
// Unable to detect platform version.
|
||||
return null;
|
||||
}
|
||||
public function isAutoCommit()
|
||||
{
|
||||
return $this->autoCommit === \true;
|
||||
}
|
||||
public function setAutoCommit($autoCommit)
|
||||
{
|
||||
$autoCommit = (bool) $autoCommit;
|
||||
// Mode not changed, no-op.
|
||||
if ($autoCommit === $this->autoCommit) {
|
||||
return;
|
||||
}
|
||||
$this->autoCommit = $autoCommit;
|
||||
// Commit all currently active transactions if any when switching auto-commit mode.
|
||||
if ($this->_conn === null || $this->transactionNestingLevel === 0) {
|
||||
return;
|
||||
}
|
||||
$this->commitAll();
|
||||
}
|
||||
public function fetchAssociative(string $query, array $params = [], array $types = [])
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->fetchAssociative();
|
||||
}
|
||||
public function fetchNumeric(string $query, array $params = [], array $types = [])
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->fetchNumeric();
|
||||
}
|
||||
public function fetchOne(string $query, array $params = [], array $types = [])
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->fetchOne();
|
||||
}
|
||||
public function isConnected()
|
||||
{
|
||||
return $this->_conn !== null;
|
||||
}
|
||||
public function isTransactionActive()
|
||||
{
|
||||
return $this->transactionNestingLevel > 0;
|
||||
}
|
||||
private function addCriteriaCondition(array $criteria, array &$columns, array &$values, array &$conditions) : void
|
||||
{
|
||||
$platform = $this->getDatabasePlatform();
|
||||
foreach ($criteria as $columnName => $value) {
|
||||
if ($value === null) {
|
||||
$conditions[] = $platform->getIsNullExpression($columnName);
|
||||
continue;
|
||||
}
|
||||
$columns[] = $columnName;
|
||||
$values[] = $value;
|
||||
$conditions[] = $columnName . ' = ?';
|
||||
}
|
||||
}
|
||||
public function delete($table, array $criteria, array $types = [])
|
||||
{
|
||||
if (count($criteria) === 0) {
|
||||
throw InvalidArgumentException::fromEmptyCriteria();
|
||||
}
|
||||
$columns = $values = $conditions = [];
|
||||
$this->addCriteriaCondition($criteria, $columns, $values, $conditions);
|
||||
return $this->executeStatement('DELETE FROM ' . $table . ' WHERE ' . implode(' AND ', $conditions), $values, is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types);
|
||||
}
|
||||
public function close()
|
||||
{
|
||||
$this->_conn = null;
|
||||
$this->transactionNestingLevel = 0;
|
||||
}
|
||||
public function setTransactionIsolation($level)
|
||||
{
|
||||
$this->transactionIsolationLevel = $level;
|
||||
return $this->executeStatement($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level));
|
||||
}
|
||||
public function getTransactionIsolation()
|
||||
{
|
||||
return $this->transactionIsolationLevel ??= $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel();
|
||||
}
|
||||
public function update($table, array $data, array $criteria, array $types = [])
|
||||
{
|
||||
$columns = $values = $conditions = $set = [];
|
||||
foreach ($data as $columnName => $value) {
|
||||
$columns[] = $columnName;
|
||||
$values[] = $value;
|
||||
$set[] = $columnName . ' = ?';
|
||||
}
|
||||
$this->addCriteriaCondition($criteria, $columns, $values, $conditions);
|
||||
if (is_string(key($types))) {
|
||||
$types = $this->extractTypeValues($columns, $types);
|
||||
}
|
||||
$sql = 'UPDATE ' . $table . ' SET ' . implode(', ', $set) . ' WHERE ' . implode(' AND ', $conditions);
|
||||
return $this->executeStatement($sql, $values, $types);
|
||||
}
|
||||
public function insert($table, array $data, array $types = [])
|
||||
{
|
||||
if (count($data) === 0) {
|
||||
return $this->executeStatement('INSERT INTO ' . $table . ' () VALUES ()');
|
||||
}
|
||||
$columns = [];
|
||||
$values = [];
|
||||
$set = [];
|
||||
foreach ($data as $columnName => $value) {
|
||||
$columns[] = $columnName;
|
||||
$values[] = $value;
|
||||
$set[] = '?';
|
||||
}
|
||||
return $this->executeStatement('INSERT INTO ' . $table . ' (' . implode(', ', $columns) . ')' . ' VALUES (' . implode(', ', $set) . ')', $values, is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types);
|
||||
}
|
||||
private function extractTypeValues(array $columnList, array $types) : array
|
||||
{
|
||||
$typeValues = [];
|
||||
foreach ($columnList as $columnName) {
|
||||
$typeValues[] = $types[$columnName] ?? ParameterType::STRING;
|
||||
}
|
||||
return $typeValues;
|
||||
}
|
||||
public function quoteIdentifier($str)
|
||||
{
|
||||
return $this->getDatabasePlatform()->quoteIdentifier($str);
|
||||
}
|
||||
public function quote($value, $type = ParameterType::STRING)
|
||||
{
|
||||
$connection = $this->getWrappedConnection();
|
||||
[$value, $bindingType] = $this->getBindingInfo($value, $type);
|
||||
return $connection->quote($value, $bindingType);
|
||||
}
|
||||
public function fetchAllNumeric(string $query, array $params = [], array $types = []) : array
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->fetchAllNumeric();
|
||||
}
|
||||
public function fetchAllAssociative(string $query, array $params = [], array $types = []) : array
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->fetchAllAssociative();
|
||||
}
|
||||
public function fetchAllKeyValue(string $query, array $params = [], array $types = []) : array
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->fetchAllKeyValue();
|
||||
}
|
||||
public function fetchAllAssociativeIndexed(string $query, array $params = [], array $types = []) : array
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->fetchAllAssociativeIndexed();
|
||||
}
|
||||
public function fetchFirstColumn(string $query, array $params = [], array $types = []) : array
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->fetchFirstColumn();
|
||||
}
|
||||
public function iterateNumeric(string $query, array $params = [], array $types = []) : Traversable
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->iterateNumeric();
|
||||
}
|
||||
public function iterateAssociative(string $query, array $params = [], array $types = []) : Traversable
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->iterateAssociative();
|
||||
}
|
||||
public function iterateKeyValue(string $query, array $params = [], array $types = []) : Traversable
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->iterateKeyValue();
|
||||
}
|
||||
public function iterateAssociativeIndexed(string $query, array $params = [], array $types = []) : Traversable
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->iterateAssociativeIndexed();
|
||||
}
|
||||
public function iterateColumn(string $query, array $params = [], array $types = []) : Traversable
|
||||
{
|
||||
return $this->executeQuery($query, $params, $types)->iterateColumn();
|
||||
}
|
||||
public function prepare(string $sql) : Statement
|
||||
{
|
||||
$connection = $this->getWrappedConnection();
|
||||
try {
|
||||
$statement = $connection->prepare($sql);
|
||||
} catch (Driver\Exception $e) {
|
||||
throw $this->convertExceptionDuringQuery($e, $sql);
|
||||
}
|
||||
return new Statement($this, $statement, $sql);
|
||||
}
|
||||
public function executeQuery(string $sql, array $params = [], $types = [], ?QueryCacheProfile $qcp = null) : Result
|
||||
{
|
||||
if ($qcp !== null) {
|
||||
return $this->executeCacheQuery($sql, $params, $types, $qcp);
|
||||
}
|
||||
$connection = $this->getWrappedConnection();
|
||||
$logger = $this->_config->getSQLLogger();
|
||||
if ($logger !== null) {
|
||||
$logger->startQuery($sql, $params, $types);
|
||||
}
|
||||
try {
|
||||
if (count($params) > 0) {
|
||||
if ($this->needsArrayParameterConversion($params, $types)) {
|
||||
[$sql, $params, $types] = $this->expandArrayParameters($sql, $params, $types);
|
||||
}
|
||||
$stmt = $connection->prepare($sql);
|
||||
$this->bindParameters($stmt, $params, $types);
|
||||
$result = $stmt->execute();
|
||||
} else {
|
||||
$result = $connection->query($sql);
|
||||
}
|
||||
return new Result($result, $this);
|
||||
} catch (Driver\Exception $e) {
|
||||
throw $this->convertExceptionDuringQuery($e, $sql, $params, $types);
|
||||
} finally {
|
||||
if ($logger !== null) {
|
||||
$logger->stopQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp) : Result
|
||||
{
|
||||
$resultCache = $qcp->getResultCache() ?? $this->_config->getResultCache();
|
||||
if ($resultCache === null) {
|
||||
throw CacheException::noResultDriverConfigured();
|
||||
}
|
||||
$connectionParams = $this->params;
|
||||
unset($connectionParams['platform'], $connectionParams['password'], $connectionParams['url']);
|
||||
[$cacheKey, $realKey] = $qcp->generateCacheKeys($sql, $params, $types, $connectionParams);
|
||||
$item = $resultCache->getItem($cacheKey);
|
||||
if ($item->isHit()) {
|
||||
$value = $item->get();
|
||||
if (!is_array($value)) {
|
||||
$value = [];
|
||||
}
|
||||
if (isset($value[$realKey])) {
|
||||
return new Result(new ArrayResult($value[$realKey]), $this);
|
||||
}
|
||||
} else {
|
||||
$value = [];
|
||||
}
|
||||
$data = $this->fetchAllAssociative($sql, $params, $types);
|
||||
$value[$realKey] = $data;
|
||||
$item->set($value);
|
||||
$lifetime = $qcp->getLifetime();
|
||||
if ($lifetime > 0) {
|
||||
$item->expiresAfter($lifetime);
|
||||
}
|
||||
$resultCache->save($item);
|
||||
return new Result(new ArrayResult($data), $this);
|
||||
}
|
||||
public function executeStatement($sql, array $params = [], array $types = [])
|
||||
{
|
||||
$connection = $this->getWrappedConnection();
|
||||
$logger = $this->_config->getSQLLogger();
|
||||
if ($logger !== null) {
|
||||
$logger->startQuery($sql, $params, $types);
|
||||
}
|
||||
try {
|
||||
if (count($params) > 0) {
|
||||
if ($this->needsArrayParameterConversion($params, $types)) {
|
||||
[$sql, $params, $types] = $this->expandArrayParameters($sql, $params, $types);
|
||||
}
|
||||
$stmt = $connection->prepare($sql);
|
||||
$this->bindParameters($stmt, $params, $types);
|
||||
return $stmt->execute()->rowCount();
|
||||
}
|
||||
return $connection->exec($sql);
|
||||
} catch (Driver\Exception $e) {
|
||||
throw $this->convertExceptionDuringQuery($e, $sql, $params, $types);
|
||||
} finally {
|
||||
if ($logger !== null) {
|
||||
$logger->stopQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
public function getTransactionNestingLevel()
|
||||
{
|
||||
return $this->transactionNestingLevel;
|
||||
}
|
||||
public function lastInsertId($name = null)
|
||||
{
|
||||
if ($name !== null) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', 'The usage of Connection::lastInsertId() with a sequence name is deprecated.');
|
||||
}
|
||||
try {
|
||||
return $this->getWrappedConnection()->lastInsertId($name);
|
||||
} catch (Driver\Exception $e) {
|
||||
throw $this->convertException($e);
|
||||
}
|
||||
}
|
||||
public function transactional(Closure $func)
|
||||
{
|
||||
$this->beginTransaction();
|
||||
try {
|
||||
$res = $func($this);
|
||||
$this->commit();
|
||||
return $res;
|
||||
} catch (Throwable $e) {
|
||||
$this->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints)
|
||||
{
|
||||
if (!$nestTransactionsWithSavepoints) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5383', <<<'DEPRECATION'
|
||||
Nesting transactions without enabling savepoints is deprecated.
|
||||
Call %s::setNestTransactionsWithSavepoints(true) to enable savepoints.
|
||||
DEPRECATION
|
||||
, self::class);
|
||||
}
|
||||
if ($this->transactionNestingLevel > 0) {
|
||||
throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction();
|
||||
}
|
||||
$this->nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints;
|
||||
}
|
||||
public function getNestTransactionsWithSavepoints()
|
||||
{
|
||||
return $this->nestTransactionsWithSavepoints;
|
||||
}
|
||||
protected function _getNestedTransactionSavePointName()
|
||||
{
|
||||
return 'DOCTRINE_' . $this->transactionNestingLevel;
|
||||
}
|
||||
public function beginTransaction()
|
||||
{
|
||||
$connection = $this->getWrappedConnection();
|
||||
++$this->transactionNestingLevel;
|
||||
$logger = $this->_config->getSQLLogger();
|
||||
if ($this->transactionNestingLevel === 1) {
|
||||
if ($logger !== null) {
|
||||
$logger->startQuery('"START TRANSACTION"');
|
||||
}
|
||||
$connection->beginTransaction();
|
||||
if ($logger !== null) {
|
||||
$logger->stopQuery();
|
||||
}
|
||||
} elseif ($this->nestTransactionsWithSavepoints) {
|
||||
if ($logger !== null) {
|
||||
$logger->startQuery('"SAVEPOINT"');
|
||||
}
|
||||
$this->createSavepoint($this->_getNestedTransactionSavePointName());
|
||||
if ($logger !== null) {
|
||||
$logger->stopQuery();
|
||||
}
|
||||
} else {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5383', <<<'DEPRECATION'
|
||||
Nesting transactions without enabling savepoints is deprecated.
|
||||
Call %s::setNestTransactionsWithSavepoints(true) to enable savepoints.
|
||||
DEPRECATION
|
||||
, self::class);
|
||||
}
|
||||
$eventManager = $this->getEventManager();
|
||||
if ($eventManager->hasListeners(Events::onTransactionBegin)) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onTransactionBegin);
|
||||
$eventManager->dispatchEvent(Events::onTransactionBegin, new TransactionBeginEventArgs($this));
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
public function commit()
|
||||
{
|
||||
if ($this->transactionNestingLevel === 0) {
|
||||
throw ConnectionException::noActiveTransaction();
|
||||
}
|
||||
if ($this->isRollbackOnly) {
|
||||
throw ConnectionException::commitFailedRollbackOnly();
|
||||
}
|
||||
$result = \true;
|
||||
$connection = $this->getWrappedConnection();
|
||||
if ($this->transactionNestingLevel === 1) {
|
||||
$result = $this->doCommit($connection);
|
||||
} elseif ($this->nestTransactionsWithSavepoints) {
|
||||
$this->releaseSavepoint($this->_getNestedTransactionSavePointName());
|
||||
}
|
||||
--$this->transactionNestingLevel;
|
||||
$eventManager = $this->getEventManager();
|
||||
if ($eventManager->hasListeners(Events::onTransactionCommit)) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onTransactionCommit);
|
||||
$eventManager->dispatchEvent(Events::onTransactionCommit, new TransactionCommitEventArgs($this));
|
||||
}
|
||||
if ($this->autoCommit !== \false || $this->transactionNestingLevel !== 0) {
|
||||
return $result;
|
||||
}
|
||||
$this->beginTransaction();
|
||||
return $result;
|
||||
}
|
||||
private function doCommit(DriverConnection $connection)
|
||||
{
|
||||
$logger = $this->_config->getSQLLogger();
|
||||
if ($logger !== null) {
|
||||
$logger->startQuery('"COMMIT"');
|
||||
}
|
||||
$result = $connection->commit();
|
||||
if ($logger !== null) {
|
||||
$logger->stopQuery();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
private function commitAll() : void
|
||||
{
|
||||
while ($this->transactionNestingLevel !== 0) {
|
||||
if ($this->autoCommit === \false && $this->transactionNestingLevel === 1) {
|
||||
// When in no auto-commit mode, the last nesting commit immediately starts a new transaction.
|
||||
// Therefore we need to do the final commit here and then leave to avoid an infinite loop.
|
||||
$this->commit();
|
||||
return;
|
||||
}
|
||||
$this->commit();
|
||||
}
|
||||
}
|
||||
public function rollBack()
|
||||
{
|
||||
if ($this->transactionNestingLevel === 0) {
|
||||
throw ConnectionException::noActiveTransaction();
|
||||
}
|
||||
$connection = $this->getWrappedConnection();
|
||||
$logger = $this->_config->getSQLLogger();
|
||||
if ($this->transactionNestingLevel === 1) {
|
||||
if ($logger !== null) {
|
||||
$logger->startQuery('"ROLLBACK"');
|
||||
}
|
||||
$this->transactionNestingLevel = 0;
|
||||
$connection->rollBack();
|
||||
$this->isRollbackOnly = \false;
|
||||
if ($logger !== null) {
|
||||
$logger->stopQuery();
|
||||
}
|
||||
if ($this->autoCommit === \false) {
|
||||
$this->beginTransaction();
|
||||
}
|
||||
} elseif ($this->nestTransactionsWithSavepoints) {
|
||||
if ($logger !== null) {
|
||||
$logger->startQuery('"ROLLBACK TO SAVEPOINT"');
|
||||
}
|
||||
$this->rollbackSavepoint($this->_getNestedTransactionSavePointName());
|
||||
--$this->transactionNestingLevel;
|
||||
if ($logger !== null) {
|
||||
$logger->stopQuery();
|
||||
}
|
||||
} else {
|
||||
$this->isRollbackOnly = \true;
|
||||
--$this->transactionNestingLevel;
|
||||
}
|
||||
$eventManager = $this->getEventManager();
|
||||
if ($eventManager->hasListeners(Events::onTransactionRollBack)) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onTransactionRollBack);
|
||||
$eventManager->dispatchEvent(Events::onTransactionRollBack, new TransactionRollBackEventArgs($this));
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
public function createSavepoint($savepoint)
|
||||
{
|
||||
$platform = $this->getDatabasePlatform();
|
||||
if (!$platform->supportsSavepoints()) {
|
||||
throw ConnectionException::savepointsNotSupported();
|
||||
}
|
||||
$this->executeStatement($platform->createSavePoint($savepoint));
|
||||
}
|
||||
public function releaseSavepoint($savepoint)
|
||||
{
|
||||
$logger = $this->_config->getSQLLogger();
|
||||
$platform = $this->getDatabasePlatform();
|
||||
if (!$platform->supportsSavepoints()) {
|
||||
throw ConnectionException::savepointsNotSupported();
|
||||
}
|
||||
if (!$platform->supportsReleaseSavepoints()) {
|
||||
if ($logger !== null) {
|
||||
$logger->stopQuery();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ($logger !== null) {
|
||||
$logger->startQuery('"RELEASE SAVEPOINT"');
|
||||
}
|
||||
$this->executeStatement($platform->releaseSavePoint($savepoint));
|
||||
if ($logger === null) {
|
||||
return;
|
||||
}
|
||||
$logger->stopQuery();
|
||||
}
|
||||
public function rollbackSavepoint($savepoint)
|
||||
{
|
||||
$platform = $this->getDatabasePlatform();
|
||||
if (!$platform->supportsSavepoints()) {
|
||||
throw ConnectionException::savepointsNotSupported();
|
||||
}
|
||||
$this->executeStatement($platform->rollbackSavePoint($savepoint));
|
||||
}
|
||||
public function getWrappedConnection()
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4966', 'Connection::getWrappedConnection() is deprecated.' . ' Use Connection::getNativeConnection() to access the native connection.');
|
||||
$this->connect();
|
||||
return $this->_conn;
|
||||
}
|
||||
public function getNativeConnection()
|
||||
{
|
||||
$this->connect();
|
||||
if (!method_exists($this->_conn, 'getNativeConnection')) {
|
||||
throw new LogicException(sprintf('The driver connection %s does not support accessing the native connection.', get_class($this->_conn)));
|
||||
}
|
||||
return $this->_conn->getNativeConnection();
|
||||
}
|
||||
public function createSchemaManager() : AbstractSchemaManager
|
||||
{
|
||||
return $this->schemaManagerFactory->createSchemaManager($this);
|
||||
}
|
||||
public function getSchemaManager()
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4515', 'Connection::getSchemaManager() is deprecated, use Connection::createSchemaManager() instead.');
|
||||
return $this->_schemaManager ??= $this->createSchemaManager();
|
||||
}
|
||||
public function setRollbackOnly()
|
||||
{
|
||||
if ($this->transactionNestingLevel === 0) {
|
||||
throw ConnectionException::noActiveTransaction();
|
||||
}
|
||||
$this->isRollbackOnly = \true;
|
||||
}
|
||||
public function isRollbackOnly()
|
||||
{
|
||||
if ($this->transactionNestingLevel === 0) {
|
||||
throw ConnectionException::noActiveTransaction();
|
||||
}
|
||||
return $this->isRollbackOnly;
|
||||
}
|
||||
public function convertToDatabaseValue($value, $type)
|
||||
{
|
||||
return Type::getType($type)->convertToDatabaseValue($value, $this->getDatabasePlatform());
|
||||
}
|
||||
public function convertToPHPValue($value, $type)
|
||||
{
|
||||
return Type::getType($type)->convertToPHPValue($value, $this->getDatabasePlatform());
|
||||
}
|
||||
private function bindParameters(DriverStatement $stmt, array $params, array $types) : void
|
||||
{
|
||||
// Check whether parameters are positional or named. Mixing is not allowed.
|
||||
if (is_int(key($params))) {
|
||||
$bindIndex = 1;
|
||||
foreach ($params as $key => $value) {
|
||||
if (isset($types[$key])) {
|
||||
$type = $types[$key];
|
||||
[$value, $bindingType] = $this->getBindingInfo($value, $type);
|
||||
} else {
|
||||
if (array_key_exists($key, $types)) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5550', 'Using NULL as prepared statement parameter type is deprecated.' . 'Omit or use ParameterType::STRING instead');
|
||||
}
|
||||
$bindingType = ParameterType::STRING;
|
||||
}
|
||||
$stmt->bindValue($bindIndex, $value, $bindingType);
|
||||
++$bindIndex;
|
||||
}
|
||||
} else {
|
||||
// Named parameters
|
||||
foreach ($params as $name => $value) {
|
||||
if (isset($types[$name])) {
|
||||
$type = $types[$name];
|
||||
[$value, $bindingType] = $this->getBindingInfo($value, $type);
|
||||
} else {
|
||||
if (array_key_exists($name, $types)) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5550', 'Using NULL as prepared statement parameter type is deprecated.' . 'Omit or use ParameterType::STRING instead');
|
||||
}
|
||||
$bindingType = ParameterType::STRING;
|
||||
}
|
||||
$stmt->bindValue($name, $value, $bindingType);
|
||||
}
|
||||
}
|
||||
}
|
||||
private function getBindingInfo($value, $type) : array
|
||||
{
|
||||
if (is_string($type)) {
|
||||
$type = Type::getType($type);
|
||||
}
|
||||
if ($type instanceof Type) {
|
||||
$value = $type->convertToDatabaseValue($value, $this->getDatabasePlatform());
|
||||
$bindingType = $type->getBindingType();
|
||||
} else {
|
||||
$bindingType = $type ?? ParameterType::STRING;
|
||||
}
|
||||
return [$value, $bindingType];
|
||||
}
|
||||
public function createQueryBuilder()
|
||||
{
|
||||
return new Query\QueryBuilder($this);
|
||||
}
|
||||
public final function convertExceptionDuringQuery(Driver\Exception $e, string $sql, array $params = [], array $types = []) : DriverException
|
||||
{
|
||||
return $this->handleDriverException($e, new Query($sql, $params, $types));
|
||||
}
|
||||
public final function convertException(Driver\Exception $e) : DriverException
|
||||
{
|
||||
return $this->handleDriverException($e, null);
|
||||
}
|
||||
private function expandArrayParameters(string $sql, array $params, array $types) : array
|
||||
{
|
||||
$this->parser ??= $this->getDatabasePlatform()->createSQLParser();
|
||||
$visitor = new ExpandArrayParameters($params, $types);
|
||||
$this->parser->parse($sql, $visitor);
|
||||
return [$visitor->getSQL(), $visitor->getParameters(), $visitor->getTypes()];
|
||||
}
|
||||
private function needsArrayParameterConversion(array $params, array $types) : bool
|
||||
{
|
||||
if (is_string(key($params))) {
|
||||
return \true;
|
||||
}
|
||||
foreach ($types as $type) {
|
||||
if ($type === ArrayParameterType::INTEGER || $type === ArrayParameterType::STRING || $type === ArrayParameterType::ASCII || $type === ArrayParameterType::BINARY) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
private function handleDriverException(Driver\Exception $driverException, ?Query $query) : DriverException
|
||||
{
|
||||
$this->exceptionConverter ??= $this->_driver->getExceptionConverter();
|
||||
$exception = $this->exceptionConverter->convert($driverException, $query);
|
||||
if ($exception instanceof ConnectionLost) {
|
||||
$this->close();
|
||||
}
|
||||
return $exception;
|
||||
}
|
||||
public function executeUpdate(string $sql, array $params = [], array $types = []) : int
|
||||
{
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4163', '%s is deprecated, please use executeStatement() instead.', __METHOD__);
|
||||
return $this->executeStatement($sql, $params, $types);
|
||||
}
|
||||
public function query(string $sql) : Result
|
||||
{
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4163', '%s is deprecated, please use executeQuery() instead.', __METHOD__);
|
||||
return $this->executeQuery($sql);
|
||||
}
|
||||
public function exec(string $sql) : int
|
||||
{
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4163', '%s is deprecated, please use executeStatement() instead.', __METHOD__);
|
||||
return $this->executeStatement($sql);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class ConnectionException extends Exception
|
||||
{
|
||||
public static function commitFailedRollbackOnly()
|
||||
{
|
||||
return new self('Transaction commit failed because the transaction has been marked for rollback only.');
|
||||
}
|
||||
public static function noActiveTransaction()
|
||||
{
|
||||
return new self('There is no active transaction.');
|
||||
}
|
||||
public static function savepointsNotSupported()
|
||||
{
|
||||
return new self('Savepoints are not supported by this driver.');
|
||||
}
|
||||
public static function mayNotAlterNestedTransactionWithSavepointsInTransaction()
|
||||
{
|
||||
return new self('May not alter the nested transaction with savepoints behavior while a transaction is open.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\API\ExceptionConverter;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Connection as DriverConnection;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Exception;
|
||||
use MailPoetVendor\Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use MailPoetVendor\Doctrine\DBAL\Schema\AbstractSchemaManager;
|
||||
use MailPoetVendor\SensitiveParameter;
|
||||
interface Driver
|
||||
{
|
||||
public function connect( array $params);
|
||||
public function getDatabasePlatform();
|
||||
public function getSchemaManager(Connection $conn, AbstractPlatform $platform);
|
||||
public function getExceptionConverter() : ExceptionConverter;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver\API;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Exception;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\DriverException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Query;
|
||||
interface ExceptionConverter
|
||||
{
|
||||
public function convert(Exception $exception, ?Query $query) : DriverException;
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver\API\MySQL;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Exception;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\ConnectionException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\ConnectionLost;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\DatabaseDoesNotExist;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\DeadlockException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\DriverException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\InvalidFieldNameException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\LockWaitTimeoutException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\NonUniqueFieldNameException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\NotNullConstraintViolationException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\SyntaxErrorException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\TableExistsException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\TableNotFoundException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Query;
|
||||
final class ExceptionConverter implements ExceptionConverterInterface
|
||||
{
|
||||
public function convert(Exception $exception, ?Query $query) : DriverException
|
||||
{
|
||||
switch ($exception->getCode()) {
|
||||
case 1008:
|
||||
return new DatabaseDoesNotExist($exception, $query);
|
||||
case 1213:
|
||||
return new DeadlockException($exception, $query);
|
||||
case 1205:
|
||||
return new LockWaitTimeoutException($exception, $query);
|
||||
case 1050:
|
||||
return new TableExistsException($exception, $query);
|
||||
case 1051:
|
||||
case 1146:
|
||||
return new TableNotFoundException($exception, $query);
|
||||
case 1216:
|
||||
case 1217:
|
||||
case 1451:
|
||||
case 1452:
|
||||
case 1701:
|
||||
return new ForeignKeyConstraintViolationException($exception, $query);
|
||||
case 1062:
|
||||
case 1557:
|
||||
case 1569:
|
||||
case 1586:
|
||||
return new UniqueConstraintViolationException($exception, $query);
|
||||
case 1054:
|
||||
case 1166:
|
||||
case 1611:
|
||||
return new InvalidFieldNameException($exception, $query);
|
||||
case 1052:
|
||||
case 1060:
|
||||
case 1110:
|
||||
return new NonUniqueFieldNameException($exception, $query);
|
||||
case 1064:
|
||||
case 1149:
|
||||
case 1287:
|
||||
case 1341:
|
||||
case 1342:
|
||||
case 1343:
|
||||
case 1344:
|
||||
case 1382:
|
||||
case 1479:
|
||||
case 1541:
|
||||
case 1554:
|
||||
case 1626:
|
||||
return new SyntaxErrorException($exception, $query);
|
||||
case 1044:
|
||||
case 1045:
|
||||
case 1046:
|
||||
case 1049:
|
||||
case 1095:
|
||||
case 1142:
|
||||
case 1143:
|
||||
case 1227:
|
||||
case 1370:
|
||||
case 1429:
|
||||
case 2002:
|
||||
case 2005:
|
||||
case 2054:
|
||||
return new ConnectionException($exception, $query);
|
||||
case 2006:
|
||||
return new ConnectionLost($exception, $query);
|
||||
case 1048:
|
||||
case 1121:
|
||||
case 1138:
|
||||
case 1171:
|
||||
case 1252:
|
||||
case 1263:
|
||||
case 1364:
|
||||
case 1566:
|
||||
return new NotNullConstraintViolationException($exception, $query);
|
||||
}
|
||||
return new DriverException($exception, $query);
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use Exception as BaseException;
|
||||
use Throwable;
|
||||
abstract class AbstractException extends BaseException implements Exception
|
||||
{
|
||||
private ?string $sqlState = null;
|
||||
public function __construct($message, $sqlState = null, $code = 0, ?Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->sqlState = $sqlState;
|
||||
}
|
||||
public function getSQLState()
|
||||
{
|
||||
return $this->sqlState;
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\Connection;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\API\ExceptionConverter;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\API\MySQL;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception;
|
||||
use MailPoetVendor\Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
|
||||
use MailPoetVendor\Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use MailPoetVendor\Doctrine\DBAL\Platforms\MariaDb1027Platform;
|
||||
use MailPoetVendor\Doctrine\DBAL\Platforms\MariaDb1043Platform;
|
||||
use MailPoetVendor\Doctrine\DBAL\Platforms\MariaDb1052Platform;
|
||||
use MailPoetVendor\Doctrine\DBAL\Platforms\MariaDb1060Platform;
|
||||
use MailPoetVendor\Doctrine\DBAL\Platforms\MySQL57Platform;
|
||||
use MailPoetVendor\Doctrine\DBAL\Platforms\MySQL80Platform;
|
||||
use MailPoetVendor\Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use MailPoetVendor\Doctrine\DBAL\Schema\MySQLSchemaManager;
|
||||
use MailPoetVendor\Doctrine\DBAL\VersionAwarePlatformDriver;
|
||||
use MailPoetVendor\Doctrine\Deprecations\Deprecation;
|
||||
use function assert;
|
||||
use function preg_match;
|
||||
use function stripos;
|
||||
use function version_compare;
|
||||
abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver
|
||||
{
|
||||
public function createDatabasePlatformForVersion($version)
|
||||
{
|
||||
$mariadb = stripos($version, 'mariadb') !== \false;
|
||||
if ($mariadb) {
|
||||
$mariaDbVersion = $this->getMariaDbMysqlVersionNumber($version);
|
||||
if (version_compare($mariaDbVersion, '10.6.0', '>=')) {
|
||||
return new MariaDb1060Platform();
|
||||
}
|
||||
if (version_compare($mariaDbVersion, '10.5.2', '>=')) {
|
||||
return new MariaDb1052Platform();
|
||||
}
|
||||
if (version_compare($mariaDbVersion, '10.4.3', '>=')) {
|
||||
return new MariaDb1043Platform();
|
||||
}
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6110', 'Support for MariaDB < 10.4 is deprecated and will be removed in DBAL 4.' . ' Consider upgrading to a more recent version of MariaDB.');
|
||||
if (version_compare($mariaDbVersion, '10.2.7', '>=')) {
|
||||
return new MariaDb1027Platform();
|
||||
}
|
||||
} else {
|
||||
$oracleMysqlVersion = $this->getOracleMysqlVersionNumber($version);
|
||||
if (version_compare($oracleMysqlVersion, '8', '>=')) {
|
||||
if (!version_compare($version, '8.0.0', '>=')) {
|
||||
Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/dbal/pull/5779', 'Version detection logic for MySQL will change in DBAL 4. ' . 'Please specify the version as the server reports it, e.g. "8.0.31" instead of "8".');
|
||||
}
|
||||
return new MySQL80Platform();
|
||||
}
|
||||
if (version_compare($oracleMysqlVersion, '5.7.9', '>=')) {
|
||||
if (!version_compare($version, '5.7.9', '>=')) {
|
||||
Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/dbal/pull/5779', 'Version detection logic for MySQL will change in DBAL 4. ' . 'Please specify the version as the server reports it, e.g. "5.7.40" instead of "5.7".');
|
||||
}
|
||||
return new MySQL57Platform();
|
||||
}
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5072', 'MySQL 5.6 support is deprecated and will be removed in DBAL 4.' . ' Consider upgrading to MySQL 5.7 or later.');
|
||||
}
|
||||
return $this->getDatabasePlatform();
|
||||
}
|
||||
private function getOracleMysqlVersionNumber(string $versionString) : string
|
||||
{
|
||||
if (preg_match('/^(?P<major>\\d+)(?:\\.(?P<minor>\\d+)(?:\\.(?P<patch>\\d+))?)?/', $versionString, $versionParts) === 0) {
|
||||
throw Exception::invalidPlatformVersionSpecified($versionString, '<major_version>.<minor_version>.<patch_version>');
|
||||
}
|
||||
$majorVersion = $versionParts['major'];
|
||||
$minorVersion = $versionParts['minor'] ?? 0;
|
||||
$patchVersion = $versionParts['patch'] ?? null;
|
||||
if ($majorVersion === '5' && $minorVersion === '7') {
|
||||
$patchVersion ??= '9';
|
||||
}
|
||||
return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
|
||||
}
|
||||
private function getMariaDbMysqlVersionNumber(string $versionString) : string
|
||||
{
|
||||
if (stripos($versionString, 'MariaDB') === 0) {
|
||||
Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/dbal/pull/5779', 'Version detection logic for MySQL will change in DBAL 4. ' . 'Please specify the version as the server reports it, ' . 'e.g. "10.9.3-MariaDB" instead of "mariadb-10.9".');
|
||||
}
|
||||
if (preg_match('/^(?:5\\.5\\.5-)?(mariadb-)?(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)/i', $versionString, $versionParts) === 0) {
|
||||
throw Exception::invalidPlatformVersionSpecified($versionString, '^(?:5\\.5\\.5-)?(mariadb-)?<major_version>.<minor_version>.<patch_version>');
|
||||
}
|
||||
return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch'];
|
||||
}
|
||||
public function getDatabasePlatform()
|
||||
{
|
||||
return new MySQLPlatform();
|
||||
}
|
||||
public function getSchemaManager(Connection $conn, AbstractPlatform $platform)
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5458', 'AbstractMySQLDriver::getSchemaManager() is deprecated.' . ' Use MySQLPlatform::createSchemaManager() instead.');
|
||||
assert($platform instanceof AbstractMySQLPlatform);
|
||||
return new MySQLSchemaManager($conn, $platform);
|
||||
}
|
||||
public function getExceptionConverter() : ExceptionConverter
|
||||
{
|
||||
return new MySQL\ExceptionConverter();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\ParameterType;
|
||||
interface Connection
|
||||
{
|
||||
public function prepare(string $sql) : Statement;
|
||||
public function query(string $sql) : Result;
|
||||
public function quote($value, $type = ParameterType::STRING);
|
||||
public function exec(string $sql) : int;
|
||||
public function lastInsertId($name = null);
|
||||
public function beginTransaction();
|
||||
public function commit();
|
||||
public function rollBack();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use Throwable;
|
||||
interface Exception extends Throwable
|
||||
{
|
||||
public function getSQLState();
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver\Exception;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\AbstractException;
|
||||
use function sprintf;
|
||||
final class UnknownParameterType extends AbstractException
|
||||
{
|
||||
public static function new($type) : self
|
||||
{
|
||||
return new self(sprintf('Unknown parameter type, %d given.', $type));
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
final class FetchUtils
|
||||
{
|
||||
public static function fetchOne(Result $result)
|
||||
{
|
||||
$row = $result->fetchNumeric();
|
||||
if ($row === \false) {
|
||||
return \false;
|
||||
}
|
||||
return $row[0];
|
||||
}
|
||||
public static function fetchAllNumeric(Result $result) : array
|
||||
{
|
||||
$rows = [];
|
||||
while (($row = $result->fetchNumeric()) !== \false) {
|
||||
$rows[] = $row;
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
public static function fetchAllAssociative(Result $result) : array
|
||||
{
|
||||
$rows = [];
|
||||
while (($row = $result->fetchAssociative()) !== \false) {
|
||||
$rows[] = $row;
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
public static function fetchFirstColumn(Result $result) : array
|
||||
{
|
||||
$rows = [];
|
||||
while (($row = $result->fetchOne()) !== \false) {
|
||||
$rows[] = $row;
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver;
|
||||
interface Middleware
|
||||
{
|
||||
public function wrap(Driver $driver) : Driver;
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver\Middleware;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Connection;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Result;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\ServerInfoAwareConnection;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Statement;
|
||||
use MailPoetVendor\Doctrine\DBAL\ParameterType;
|
||||
use MailPoetVendor\Doctrine\Deprecations\Deprecation;
|
||||
use LogicException;
|
||||
use function get_class;
|
||||
use function method_exists;
|
||||
use function sprintf;
|
||||
abstract class AbstractConnectionMiddleware implements ServerInfoAwareConnection
|
||||
{
|
||||
private Connection $wrappedConnection;
|
||||
public function __construct(Connection $wrappedConnection)
|
||||
{
|
||||
$this->wrappedConnection = $wrappedConnection;
|
||||
}
|
||||
public function prepare(string $sql) : Statement
|
||||
{
|
||||
return $this->wrappedConnection->prepare($sql);
|
||||
}
|
||||
public function query(string $sql) : Result
|
||||
{
|
||||
return $this->wrappedConnection->query($sql);
|
||||
}
|
||||
public function quote($value, $type = ParameterType::STRING)
|
||||
{
|
||||
return $this->wrappedConnection->quote($value, $type);
|
||||
}
|
||||
public function exec(string $sql) : int
|
||||
{
|
||||
return $this->wrappedConnection->exec($sql);
|
||||
}
|
||||
public function lastInsertId($name = null)
|
||||
{
|
||||
if ($name !== null) {
|
||||
Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', 'The usage of Connection::lastInsertId() with a sequence name is deprecated.');
|
||||
}
|
||||
return $this->wrappedConnection->lastInsertId($name);
|
||||
}
|
||||
public function beginTransaction()
|
||||
{
|
||||
return $this->wrappedConnection->beginTransaction();
|
||||
}
|
||||
public function commit()
|
||||
{
|
||||
return $this->wrappedConnection->commit();
|
||||
}
|
||||
public function rollBack()
|
||||
{
|
||||
return $this->wrappedConnection->rollBack();
|
||||
}
|
||||
public function getServerVersion()
|
||||
{
|
||||
if (!$this->wrappedConnection instanceof ServerInfoAwareConnection) {
|
||||
throw new LogicException('The underlying connection is not a ServerInfoAwareConnection');
|
||||
}
|
||||
return $this->wrappedConnection->getServerVersion();
|
||||
}
|
||||
public function getNativeConnection()
|
||||
{
|
||||
if (!method_exists($this->wrappedConnection, 'getNativeConnection')) {
|
||||
throw new LogicException(sprintf('The driver connection %s does not support accessing the native connection.', get_class($this->wrappedConnection)));
|
||||
}
|
||||
return $this->wrappedConnection->getNativeConnection();
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver\Middleware;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\Connection;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\API\ExceptionConverter;
|
||||
use MailPoetVendor\Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use MailPoetVendor\Doctrine\DBAL\VersionAwarePlatformDriver;
|
||||
use MailPoetVendor\Doctrine\Deprecations\Deprecation;
|
||||
use MailPoetVendor\SensitiveParameter;
|
||||
abstract class AbstractDriverMiddleware implements VersionAwarePlatformDriver
|
||||
{
|
||||
private Driver $wrappedDriver;
|
||||
public function __construct(Driver $wrappedDriver)
|
||||
{
|
||||
$this->wrappedDriver = $wrappedDriver;
|
||||
}
|
||||
public function connect( array $params)
|
||||
{
|
||||
return $this->wrappedDriver->connect($params);
|
||||
}
|
||||
public function getDatabasePlatform()
|
||||
{
|
||||
return $this->wrappedDriver->getDatabasePlatform();
|
||||
}
|
||||
public function getSchemaManager(Connection $conn, AbstractPlatform $platform)
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5458', 'AbstractDriverMiddleware::getSchemaManager() is deprecated.' . ' Use AbstractPlatform::createSchemaManager() instead.');
|
||||
return $this->wrappedDriver->getSchemaManager($conn, $platform);
|
||||
}
|
||||
public function getExceptionConverter() : ExceptionConverter
|
||||
{
|
||||
return $this->wrappedDriver->getExceptionConverter();
|
||||
}
|
||||
public function createDatabasePlatformForVersion($version)
|
||||
{
|
||||
if ($this->wrappedDriver instanceof VersionAwarePlatformDriver) {
|
||||
return $this->wrappedDriver->createDatabasePlatformForVersion($version);
|
||||
}
|
||||
return $this->wrappedDriver->getDatabasePlatform();
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver\Middleware;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Result;
|
||||
abstract class AbstractResultMiddleware implements Result
|
||||
{
|
||||
private Result $wrappedResult;
|
||||
public function __construct(Result $result)
|
||||
{
|
||||
$this->wrappedResult = $result;
|
||||
}
|
||||
public function fetchNumeric()
|
||||
{
|
||||
return $this->wrappedResult->fetchNumeric();
|
||||
}
|
||||
public function fetchAssociative()
|
||||
{
|
||||
return $this->wrappedResult->fetchAssociative();
|
||||
}
|
||||
public function fetchOne()
|
||||
{
|
||||
return $this->wrappedResult->fetchOne();
|
||||
}
|
||||
public function fetchAllNumeric() : array
|
||||
{
|
||||
return $this->wrappedResult->fetchAllNumeric();
|
||||
}
|
||||
public function fetchAllAssociative() : array
|
||||
{
|
||||
return $this->wrappedResult->fetchAllAssociative();
|
||||
}
|
||||
public function fetchFirstColumn() : array
|
||||
{
|
||||
return $this->wrappedResult->fetchFirstColumn();
|
||||
}
|
||||
public function rowCount() : int
|
||||
{
|
||||
return $this->wrappedResult->rowCount();
|
||||
}
|
||||
public function columnCount() : int
|
||||
{
|
||||
return $this->wrappedResult->columnCount();
|
||||
}
|
||||
public function free() : void
|
||||
{
|
||||
$this->wrappedResult->free();
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver\Middleware;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Result;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Statement;
|
||||
use MailPoetVendor\Doctrine\DBAL\ParameterType;
|
||||
use MailPoetVendor\Doctrine\Deprecations\Deprecation;
|
||||
use function func_num_args;
|
||||
abstract class AbstractStatementMiddleware implements Statement
|
||||
{
|
||||
private Statement $wrappedStatement;
|
||||
public function __construct(Statement $wrappedStatement)
|
||||
{
|
||||
$this->wrappedStatement = $wrappedStatement;
|
||||
}
|
||||
public function bindValue($param, $value, $type = ParameterType::STRING)
|
||||
{
|
||||
if (func_num_args() < 3) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindValue() is deprecated.' . ' Pass the type corresponding to the parameter being bound.');
|
||||
}
|
||||
return $this->wrappedStatement->bindValue($param, $value, $type);
|
||||
}
|
||||
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null)
|
||||
{
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5563', '%s is deprecated. Use bindValue() instead.', __METHOD__);
|
||||
if (func_num_args() < 3) {
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindParam() is deprecated.' . ' Pass the type corresponding to the parameter being bound.');
|
||||
}
|
||||
return $this->wrappedStatement->bindParam($param, $variable, $type, $length);
|
||||
}
|
||||
public function execute($params = null) : Result
|
||||
{
|
||||
return $this->wrappedStatement->execute($params);
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface Result
|
||||
{
|
||||
public function fetchNumeric();
|
||||
public function fetchAssociative();
|
||||
public function fetchOne();
|
||||
public function fetchAllNumeric() : array;
|
||||
public function fetchAllAssociative() : array;
|
||||
public function fetchFirstColumn() : array;
|
||||
public function rowCount() : int;
|
||||
public function columnCount() : int;
|
||||
public function free() : void;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
interface ServerInfoAwareConnection extends Connection
|
||||
{
|
||||
public function getServerVersion();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Driver;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\DBAL\ParameterType;
|
||||
interface Statement
|
||||
{
|
||||
public function bindValue($param, $value, $type = ParameterType::STRING);
|
||||
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null);
|
||||
public function execute($params = null) : Result;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Common\EventManager;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\IBMDB2;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\Mysqli;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\OCI8;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\PDO;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\PgSQL;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\SQLite3;
|
||||
use MailPoetVendor\Doctrine\DBAL\Driver\SQLSrv;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception\MalformedDsnException;
|
||||
use MailPoetVendor\Doctrine\DBAL\Tools\DsnParser;
|
||||
use MailPoetVendor\Doctrine\Deprecations\Deprecation;
|
||||
use MailPoetVendor\SensitiveParameter;
|
||||
use function array_keys;
|
||||
use function array_merge;
|
||||
use function is_a;
|
||||
final class DriverManager
|
||||
{
|
||||
private const DRIVER_MAP = ['pdo_mysql' => PDO\MySQL\Driver::class, 'pdo_sqlite' => PDO\SQLite\Driver::class, 'pdo_pgsql' => PDO\PgSQL\Driver::class, 'pdo_oci' => PDO\OCI\Driver::class, 'oci8' => OCI8\Driver::class, 'ibm_db2' => IBMDB2\Driver::class, 'pdo_sqlsrv' => PDO\SQLSrv\Driver::class, 'mysqli' => Mysqli\Driver::class, 'pgsql' => PgSQL\Driver::class, 'sqlsrv' => SQLSrv\Driver::class, 'sqlite3' => SQLite3\Driver::class];
|
||||
private static array $driverSchemeAliases = [
|
||||
'db2' => 'ibm_db2',
|
||||
'mssql' => 'pdo_sqlsrv',
|
||||
'mysql' => 'pdo_mysql',
|
||||
'mysql2' => 'pdo_mysql',
|
||||
// Amazon RDS, for some weird reason
|
||||
'postgres' => 'pdo_pgsql',
|
||||
'postgresql' => 'pdo_pgsql',
|
||||
'pgsql' => 'pdo_pgsql',
|
||||
'sqlite' => 'pdo_sqlite',
|
||||
'sqlite3' => 'pdo_sqlite',
|
||||
];
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
public static function getConnection( array $params, ?Configuration $config = null, ?EventManager $eventManager = null) : Connection
|
||||
{
|
||||
// create default config and event manager, if not set
|
||||
$config ??= new Configuration();
|
||||
$eventManager ??= new EventManager();
|
||||
$params = self::parseDatabaseUrl($params);
|
||||
// URL support for PrimaryReplicaConnection
|
||||
if (isset($params['primary'])) {
|
||||
$params['primary'] = self::parseDatabaseUrl($params['primary']);
|
||||
}
|
||||
if (isset($params['replica'])) {
|
||||
foreach ($params['replica'] as $key => $replicaParams) {
|
||||
$params['replica'][$key] = self::parseDatabaseUrl($replicaParams);
|
||||
}
|
||||
}
|
||||
$driver = self::createDriver($params['driver'] ?? null, $params['driverClass'] ?? null);
|
||||
foreach ($config->getMiddlewares() as $middleware) {
|
||||
$driver = $middleware->wrap($driver);
|
||||
}
|
||||
$wrapperClass = $params['wrapperClass'] ?? Connection::class;
|
||||
if (!is_a($wrapperClass, Connection::class, \true)) {
|
||||
throw Exception::invalidWrapperClass($wrapperClass);
|
||||
}
|
||||
return new $wrapperClass($params, $driver, $config, $eventManager);
|
||||
}
|
||||
public static function getAvailableDrivers() : array
|
||||
{
|
||||
return array_keys(self::DRIVER_MAP);
|
||||
}
|
||||
private static function createDriver(?string $driver, ?string $driverClass) : Driver
|
||||
{
|
||||
if ($driverClass === null) {
|
||||
if ($driver === null) {
|
||||
throw Exception::driverRequired();
|
||||
}
|
||||
if (!isset(self::DRIVER_MAP[$driver])) {
|
||||
throw Exception::unknownDriver($driver, array_keys(self::DRIVER_MAP));
|
||||
}
|
||||
$driverClass = self::DRIVER_MAP[$driver];
|
||||
} elseif (!is_a($driverClass, Driver::class, \true)) {
|
||||
throw Exception::invalidDriverClass($driverClass);
|
||||
}
|
||||
return new $driverClass();
|
||||
}
|
||||
private static function parseDatabaseUrl( array $params) : array
|
||||
{
|
||||
if (!isset($params['url'])) {
|
||||
return $params;
|
||||
}
|
||||
Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5843', 'The "url" connection parameter is deprecated. Please use %s to parse a database url before calling %s.', DsnParser::class, self::class);
|
||||
$parser = new DsnParser(self::$driverSchemeAliases);
|
||||
try {
|
||||
$parsedParams = $parser->parse($params['url']);
|
||||
} catch (MalformedDsnException $e) {
|
||||
throw new Exception('Malformed parameter "url".', 0, $e);
|
||||
}
|
||||
if (isset($parsedParams['driver'])) {
|
||||
// The requested driver from the URL scheme takes precedence
|
||||
// over the default custom driver from the connection parameters (if any).
|
||||
unset($params['driverClass']);
|
||||
}
|
||||
$params = array_merge($params, $parsedParams);
|
||||
// If a schemeless connection URL is given, we require a default driver or default custom driver
|
||||
// as connection parameter.
|
||||
if (!isset($params['driverClass']) && !isset($params['driver'])) {
|
||||
throw Exception::driverRequired($params['url']);
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Event\Listeners;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Common\EventSubscriber;
|
||||
use MailPoetVendor\Doctrine\DBAL\Event\ConnectionEventArgs;
|
||||
use MailPoetVendor\Doctrine\DBAL\Events;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception;
|
||||
class SQLSessionInit implements EventSubscriber
|
||||
{
|
||||
protected $sql;
|
||||
public function __construct($sql)
|
||||
{
|
||||
$this->sql = $sql;
|
||||
}
|
||||
public function postConnect(ConnectionEventArgs $args)
|
||||
{
|
||||
$args->getConnection()->executeStatement($this->sql);
|
||||
}
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return [Events::postConnect];
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Event\Listeners;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Common\EventSubscriber;
|
||||
use MailPoetVendor\Doctrine\DBAL\Event\ConnectionEventArgs;
|
||||
use MailPoetVendor\Doctrine\DBAL\Events;
|
||||
use MailPoetVendor\Doctrine\DBAL\Exception;
|
||||
class SQLiteSessionInit implements EventSubscriber
|
||||
{
|
||||
public function postConnect(ConnectionEventArgs $args)
|
||||
{
|
||||
$args->getConnection()->executeStatement('PRAGMA foreign_keys=ON');
|
||||
}
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return [Events::postConnect];
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Event;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class TransactionBeginEventArgs extends TransactionEventArgs
|
||||
{
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Event;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
class TransactionCommitEventArgs extends TransactionEventArgs
|
||||
{
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
namespace MailPoetVendor\Doctrine\DBAL\Event;
|
||||
if (!defined('ABSPATH')) exit;
|
||||
use MailPoetVendor\Doctrine\Common\EventArgs;
|
||||
use MailPoetVendor\Doctrine\DBAL\Connection;
|
||||
abstract class TransactionEventArgs extends EventArgs
|
||||
{
|
||||
private Connection $connection;
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
public function getConnection() : Connection
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user