-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for doctrine cache 2 #72
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check CI failures:
composer.lock
needs to be updated- need to check that lowest dependency ranges are tested
- potentially worth simply dropping
psr/cache:^1
anddoctrine/persistence:^2.3
entirely
Yeah, I think about it too. But drop persistence 2.3 means drop also ORM 2.11. What you think? |
IMO just bump aggressively 🤔 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CI failures indicate some trouble with PHP 7.4. Should we drop it?
As for the rest: lots of breaking changes 😱
Can we somehow isolate this to a smaller change set, without affecting caching and similar?
src/CacheFactory.php
Outdated
$instance = null; | ||
|
||
if (array_key_exists('instance', $config)) { | ||
$instance = is_string($config['instance']) ? $container->get($config['instance']) : $config['instance']; | ||
} | ||
|
||
switch ($config['class']) { | ||
case FilesystemCache::class: | ||
case PhpFileCache::class: | ||
$cache = new $config['class']($config['directory']); | ||
break; | ||
|
||
case PredisCache::class: | ||
assert($instance !== null); | ||
$cache = new PredisCache($instance); | ||
break; | ||
|
||
case ChainCache::class: | ||
$providers = array_map( | ||
function ($provider) use ($container): CacheProvider { | ||
return $this->createWithConfig($container, $provider); | ||
}, | ||
is_array($config['providers']) ? $config['providers'] : [] | ||
); | ||
$cache = new ChainCache($providers); | ||
break; | ||
|
||
default: | ||
$cache = $container->has($config['class']) ? $container->get($config['class']) : new $config['class'](); | ||
} | ||
|
||
if ($cache instanceof MemcachedCache) { | ||
assert($instance !== null); | ||
$cache->setMemcached($instance); | ||
} elseif ($cache instanceof RedisCache) { | ||
assert($instance !== null); | ||
$cache->setRedis($instance); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, this removal will break a lot of stuff 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want stay this code, then need return support doctrine/cache 1.x version to composer.json. But in 2.x anyway these such classes do not exists, so update to 2.x will break cache configuration for doctrine and need write own PSR compatibility factory for cache providers.
src/ConfigurationFactory.php
Outdated
//'metadata_cache' => 'array', | ||
//'query_cache' => 'array', | ||
//'result_cache' => 'array', | ||
//'hydration_cache' => 'array', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leftover?
src/ConfigurationFactory.php
Outdated
foreach (['metadata_cache', 'query_cache', 'result_cache', 'hydration_cache'] as $cacheKey) { | ||
if (isset($config[$cacheKey])) { | ||
$cache = $this->retrieveDependency( | ||
$container, | ||
$config[$cacheKey], | ||
'cache', | ||
CacheFactory::class | ||
); | ||
|
||
$this->processCacheImplementation( | ||
$configuration, | ||
$cache, | ||
\Closure::fromCallable([$configuration, str_replace('_', '', ucwords('set_' . $cacheKey, '_'))]) | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't think this should be refactored this way - makes it much harder to understand.
src/DriverFactory.php
Outdated
@@ -49,7 +49,7 @@ protected function createWithConfig(ContainerInterface $container, string $confi | |||
if ( | |||
$config['class'] !== AttributeDriver::class | |||
&& ! is_subclass_of($config['class'], AttributeDriver::class) | |||
&& is_subclass_of($config['class'], AnnotationDriver::class) | |||
&& $config['class'] === AnnotationDriver::class |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is_subclass_of()
should probably stay? We're checking if $config['class']
operates like an AnnotationDriver
, not if it IS the AnnotationDriver
🤔
@mrVrAlex are you still willing to work on this PR ? or should the community step up to finish it ? |
Yes, I will rebase this and will drop support doctrine persistence 2.x & cache 1.x as renovate bot want it) |
dccbbe3
to
61c69d6
Compare
I created opsway#2 to simplify this PR by not-adding Let me know if you'd rather have in another format. |
633580f
to
25c64cd
Compare
psalm.xml.dist
Outdated
<UndefinedClass> | ||
<errorLevel type="suppress"> | ||
<referencedClass name="Doctrine\Common\Cache\Cache"/> | ||
<referencedClass name="Doctrine\Common\Cache\CacheProvider"/> | ||
<referencedClass name="Doctrine\Common\Cache\Psr6\CacheAdapter"/> | ||
</errorLevel> | ||
</UndefinedClass> | ||
<UndefinedDocblockClass> | ||
<errorLevel type="suppress"> | ||
<referencedClass name="Doctrine\Common\Cache\Cache"/> | ||
</errorLevel> | ||
</UndefinedDocblockClass> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should go away before merging: the fact that we reference these classes, yet they are not explicit dependencies, is a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
those class referencesses are in instanceof
-operator to check package availability (before replace cache with bundled CacheAdapter
) and special ::class
consts in InvalidArgumentException::fromUnsupportedCache()
. So as for me its safe for optional dependency.
Bytheway, until doctrine/orm will not bump doctrine/dbal:^4.0
all project will have doctrine/cache
, but whats the reason to declare it as that package dependency.
As less error-prone approach I could inline suppression:
@psalm-suppress UndefinedClass, UnusedPsalmSuppress
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we say that we go with doctrine/cache:^2
, then we should most certainly not have any undefined classes at all.
If the support for doctrine/cache
requires referencing classes from doctrine/cache
, then the dependency should be declared in composer.json
.
This applies to instanceof
and ::class
references alike, but I'll check the patch in more detail when I have more time :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no its not about doctrine/cache:^2
anymore. We allow v1 - #72 (comment) - but drop logic specific for this version (i mean instantiation support in CacheFactory
). But stays logic for both versions:
- wrap into PSR-6 interface adapter required by ORM
- set namespace from
$config['doctrine']['cache'][$configKey]['namespace']
in case when no conventional cache service id"doctrine.cache.{$configKey}"
(assume its fully build for usage with adapter).
With knowledge that doctrine/cache:^2
is an intermediate solution for migrating to the PSR-6 cache interface. It would be great to make package optional dependency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doctrine/cache
should be moved from require
to require-dev
then, to satisfy psalm, and not impose it on users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, this will solve problem with ugly psalm suppression. It looks like ORM cache types are stable (metadata, query, result, hydration) so no problem that contributors may not aware about disappearing doctrine/cache
package.
But there is other blocker for optional dependency for newbies - composer-require-checker - which require add composer-require-checker.json
with list optional class lists:
{
"symbol-whitelist" : [
"Doctrine\\Common\\Cache\\Cache",
"Doctrine\\Common\\Cache\\CacheProvider",
"Doctrine\\Common\\Cache\\Psr6\\CacheAdapter"
]
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like composer-require-checker
struggle against complexity caused by optional dependencies, so I will return doctrine/cache
to required
dependencies
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/confused/correct.
The idea of composer-require-checker
is precisely to disallow the existence of symbols that aren't part of the dependency set.
@@ -42,126 +29,28 @@ protected function createWithConfig(ContainerInterface $container, string $confi | |||
throw OutOfBoundsException::forMissingConfigKey('class'); | |||
} | |||
|
|||
$instance = null; | |||
$cache = $container->has($config['class']) ? $container->get($config['class']) : new $config['class'](); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are no longer supporting the various cache instance types, new $config['class']
only remains useful/relevant for something like a NullCache
: we can probably always use $container->get()
for all scenarios.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also for old default cache implementation - 'array'. So its usefull to simplify migration by add config:
'doctrine' => [
'cache' => [
'array' => ['class' => \Symfony\Component\Cache\Adapter\ArrayAdapter::class],
],
],
without forcing adding useless services into container
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be a sufficient approach, but a void cache is preferable.
While it may be tempting to use array
as a default, it is risky to do so for caches other than DQL and metadata caches, because array
can break in long-running processes.
For result caches, a void/null cache would be best as a default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would let clients decide which caches to use for which purposes. Also caches are optional, so we can avoid create own implementation of void/null/blackhole cache or be locked to another vendor (e.g. symfony/cache).
Above in #72 (comment) its just example how simple could be migration for client who use our default config with 'array' cache and if we will left constructor call in CacheFactory
. We have no dependency on symfony/cache
.
src/ConfigurationFactory.php
Outdated
if (isset($config['metadata_cache'])) { | ||
$metadataCache = $this->retrieveDependency( | ||
$container, | ||
$config['metadata_cache'], | ||
'cache', | ||
CacheFactory::class | ||
); | ||
|
||
$this->processCacheImplementation( | ||
$metadataCache, | ||
$configuration->setMetadataCache(...), | ||
); | ||
} | ||
|
||
if (isset($config['query_cache'])) { | ||
$queryCache = $this->retrieveDependency( | ||
$container, | ||
$config['query_cache'], | ||
'cache', | ||
CacheFactory::class | ||
); | ||
|
||
$this->processCacheImplementation( | ||
$queryCache, | ||
$configuration->setQueryCache(...), | ||
); | ||
} | ||
|
||
if (isset($config['result_cache'])) { | ||
$resultCache = $this->retrieveDependency( | ||
$container, | ||
$config['result_cache'], | ||
'cache', | ||
CacheFactory::class | ||
); | ||
|
||
$this->processCacheImplementation( | ||
$configuration, | ||
$hydrationCache, | ||
[$configuration, 'setHydrationCache'], | ||
); | ||
$this->processCacheImplementation( | ||
$resultCache, | ||
$configuration->setResultCache(...), | ||
); | ||
} | ||
|
||
if (isset($config['hydration_cache'])) { | ||
$hydrationCache = $this->retrieveDependency( | ||
$container, | ||
$config['hydration_cache'], | ||
'cache', | ||
CacheFactory::class | ||
); | ||
|
||
$this->processCacheImplementation( | ||
$hydrationCache, | ||
$configuration->setHydrationCache(...), | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would probably be best to not have these conditionals, and always fallback to a null/void cache for the basic setup?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that setter-injection is for optional dependencies, so I think no need to force clients to provide a default cache implementation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but we can provide a default array or void cache for most of these, as stated below in #72 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
our dependencies now have no guaranteed cache implementation, so we can't
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 It's a shame psr/cache
v2 doesn't come with a null/void cache implementation since that's no doubt going to be the same for all implementations. Long shot but could it be added there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, I will try add null cache implementation to activate optional cache and to simplify ConfigurationFactory
. I do not think that psr/cache
may get any implementation even if it useful in real world.
src/ConfigurationFactory.php
Outdated
'metadata_cache' => 'array', | ||
'query_cache' => 'array', | ||
'result_cache' => 'array', | ||
'hydration_cache' => 'array', | ||
'metadata_cache' => null, | ||
'query_cache' => null, | ||
'result_cache' => null, | ||
'hydration_cache' => null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably declare a null/void/blackhole cache default
1. Doctrine (orm, dbal etc.) going to use PSR-6|PSR-16, so doctrine/cache drop own cache implementations which previously could be instantiated in CacheFactory according to config in "doctrine.cache.{$cacheType}". Now only 'class' key supported to either get as service from container or className to be instantiated. 2. ConfigurationFactory lost usage of cache by default, see "doctrine.configuration.{$entityManagerName}" keys: - 'metadata_cache' - 'query_cache' - 'result_cache' - 'hydration_cache' because no 'array' implementation 3. Left 'array' and 'filesystem' caches in full-config.php as example how to migrate to symfony/cache 4. Remove unused $configuration argument from ConfigurationFactory::processCacheImplementation()
5f27c40
to
5d7bd85
Compare
…ctory 1. doctrine/orm Configuration class and its parent from doctrine/dbal does not support null in cache setters 2. To avoid `if` in ConfigurationFactory create NullCache implementation of PSR-6 interface. Avoid Doctrine\Common\Cache\Psr6\CacheItem (create own NullCacheItem) because someday we can remove "doctrine/cache" dependency 3. No type-hinting in NullCache and item to be compatible with "psr/cache:^1.0.1"
5d7bd85
to
46da607
Compare
@Ocramius Any further remarks after last fixes? Soon in near future need add support for DBAL v4.0 )) |
Hello, what is the status of this PR ? when it will be released ? |
Ping 💯 |
Merged in #110, thank you @mrVrAlex @e-vil-dev |
Feature #71