-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Dsn] New component #24267
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
[Dsn] New component #24267
Conversation
66d1891
to
6802f09
Compare
0a9c8f2
to
1beaa92
Compare
@jderusse thanks for submitting this proposal! I have a quick question about the long-term vision of this component. Would it also handle the other DSN used in modern Symfony apps: Doctrine DBAL DSN and Swiftmailer DSN? And would it also be useful for the new Queue/AMPQ components that will be included in the future? |
@javiereguiluz yes, the purpose of this component is to centralize usage of DSN parsing. It could be LDAP too Moreover, with the usage of env variables over arameters.yml, Dsn would be a better alternative to declare endpoint
This PR was about Redis & Memcached because code is already here, and is just about copy/paste. |
* | ||
* @author Nicolas Grekas <p@tchwork.com> | ||
*/ | ||
class MemcachedConnectionFactory |
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.
shouldn't they implement an interface?
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.
each factory has a different return type, so I see no point creating an interface (the return type would have to be mixed
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.
the input could also vary (string
in redis, array
or string
in memchached).
namespace Symfony\Component\Dsn\Exception; | ||
|
||
/** | ||
* Interface for exceptions. |
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 comment is uselesse
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.
fixed
* | ||
* @author Jérémy Derussé <jeremy@derusse.com> | ||
*/ | ||
class ConnectionFactory |
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.
please make it final
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.
👎 for having everything static here
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.
although we don't usually make things final by default, I think we should here, because there is no "type" to inherite from, only behavior to composer.
So 👍 for final
.
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.
👎 for non-static, there is no OOP here, just constructors.
} | ||
if (0 === strpos($dsn, 'memcached://')) { | ||
return static::TYPE_MEMCACHED; | ||
} |
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 implementation is hard to extend / maintainance
when we will want to have 3rd and 4th dsn type, you would just add more if's, but when one wants to use component from outside, he won't have nice way to add more guessers.
what about some indicator classes created and registered in factory class as a list, each of them specialized in recognizing only one type and answering "yes" or "no" ? then, here we would just iterate over the list
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.
Instead of hardcoding a list of supported Factories, an other solution would be ot use instances (instead of static calls), definig a common interface with 2 methods supports
and createConnection(mixed, array): mixed
and use a strategy pattern to iterrate over a list of injected factories.
- do we really need OOP in factories?
- interfaces with mixed/mixed seems useless
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.
then at least mark this class as final
, maybe even @internal
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.
Adding guesser on user's side is not needed: these are factories, they don't adhere to any contract, exactly like constructors. Instead, they ship behaviors, that users would then wire in their DI layer.
If they have more DSN factories, they'll use these factories directly. No need to create any OOP design here, that'd we completely useless and wouldn't solve any problem, just create new ones.
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 red your comments from 15 minutes ago. and especially for that:
then at least mark this class as
final
, maybe even@internal
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.
@internal
would defeat the purpose of the component by stating "don't use it" to users.
class ConnectionFactory | ||
{ | ||
const TYPE_REDIS = 1; | ||
const TYPE_MEMCACHED = 2; |
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 this would be logged anywhere for some reason, 2
is magic value. I advice to make value matching constant, like MEMCACHED
here
/** | ||
* Factory for Memcached connections. | ||
* | ||
* @author Nicolas Grekas <p@tchwork.com> |
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 new code. is he an author here indeed?
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 is code extracted from the Cache component, so he is.
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.
cool. then we shall use this in Cache component ;)
lack of that confused me
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.
please read the PR description. It says it is the next step.
* | ||
* @throws \ErrorException When invalid options or servers are provided | ||
*/ | ||
public static function createConnection($dsns, array $options = 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.
👎 for 100+ lines in one method. please decouple
/** | ||
* @dataProvider provideBadOptions | ||
* @expectedException \ErrorException | ||
* @expectedExceptionMessage constant(): Couldn't find constant Memcached:: |
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.
using annotations is considered as bad practice for expectations.
please use ->expectException
(or ->setExpectedException
if lower phpunit is needed)
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.
The issue is we would have to use both of them (as lower PHPUnit is needed for older PHP versions, but PHPUnit 6 removed expectException
entirely).
The annotation works in all versions.
And we use them in most places in the Symfony testsuite btw.
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.
PHPUnit 6 removed setExpectedException
, not expectException
.
Is there a need to support PHPUnit 4, 5, and 6 ?
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, due to PHP versions being supported (PHPUnit 5 does not support PHP 5.5, which is our min version in Symfony 3.x, and so must be covered in CI)
ad501b1
to
df8a505
Compare
clearly no, these are factories, they belong to the same world as constructors: no contract there. |
a17f50e
to
668c870
Compare
I'm against any kind of DSN, and I find it bad DX because every time I have to look up on how to build it up. If every property stands for itself, then I can simply call |
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.
LGTM, let's create this component asap to unlock the corresponding PRs on Cache and Lock
|
||
/** | ||
* @dataProvider provideInvalidDsn | ||
* @expectedException InvalidArgumentException |
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.
should be FQCN
|
||
/** | ||
* @dataProvider provideInvalidDsn | ||
* @expectedException InvalidArgumentException |
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.
should be FQCN
I just merged PR #24323 with this one. It'll help to understand the problem that this new component try to solve. |
src/Symfony/Component/Dsn/README.md
Outdated
Dsn Component | ||
============= | ||
|
||
Provides utilities for handle Data Source Names. |
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.
for creating connections using DSN (Data Source Names) strings.
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.
fixed
About the name of the component, if "dsn" is not clear enough, what about "dsn-factories"? or "connection-factories"? |
@@ -77,6 +77,7 @@ | ||
"suggest": { | ||
"ext-apcu": "For best performance of the system caches", | ||
"symfony/console": "For using the console commands", | ||
"symfony/dsn": "For using the configuring locks and cache with Dsn", |
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 think this should be moved to "require", so that DSN works out of the box in fwb.
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.
at least must be in require-dev, so that tests can pass
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.
moved into require
section
@@ -1704,9 +1705,13 @@ private function registerLockConfiguration(array $config, ContainerBuilder $cont | ||
break; | ||
case $usedEnvs || preg_match('#^[a-z]++://#', $storeDsn): | ||
if (!$container->hasDefinition($connectionDefinitionId = $container->hash($storeDsn))) { | ||
if (!class_exists(ConnectionFactory::class)) { | ||
throw new LogicException('Lock stores cannot be configured as the Dsn component is not installed.'); |
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.
should be removed once/if dsn is added as a requirement in composer.json
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.
removed (same for CachePoolPass)
|
||
switch ($type) { | ||
case ConnectionFactory::TYPE_MEMCACHED: | ||
return MemcachedConnectionFactory::createConnection($dsn, $options); |
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.
bad name, this has been renamed now
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.
fixed
@@ -0,0 +1,13 @@ | ||
Dsn Component |
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.
- Dsn Component
+ DSN Component
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.
The component's name is really Dsn Component
. See @javiereguiluz comment
👍 for dsn-factories |
|
I personnaly it is, because Loch & Cache are not the only use cases of Redis/memcached connections, and this allows creating them easily using DSN, for any other use case. Just my 2cts :) |
Having the other PR here does not change my point of view. This is not about a DSN component. Changing the name might make it a bit better, but still, I've a hard time thinking about this "code sharing" between several component as being a component by itself. Still 👎 about this change, and certainly 👎 for rushing it just before code freeze. |
OK, so let's close this. |
This PR was merged into the 3.4 branch. Discussion ---------- [Lock] Use cache connection factories in lock | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | no (feature removal) | BC breaks? | no (if merged in 3.4) | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | An alternative to #24267 to share code between cache and lock. Commits ------- 95358ac Share connection factories between cache and lock
This PR was merged into the 3.4 branch. Discussion ---------- [Lock] Use cache connection factories in lock | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | no (feature removal) | BC breaks? | no (if merged in 3.4) | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | An alternative to symfony/symfony#24267 to share code between cache and lock. Commits ------- 95358ac98f Share connection factories between cache and lock
This PR was merged into the 3.4 branch. Discussion ---------- [DoctrineBridge] Deprecate dbal session handler | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | no <!-- don't forget updating src/**/CHANGELOG.md files --> | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | #20501 | License | MIT | Doc PR | The DbalSessionHandler misses all the improvements from the PdoSessionHandler (lock modes, delayed GC, configurable naming). The only advantage it had was the ability to work with non-pdo drivers. But since DBAL requires PDO now as well (doctrine/dbal@36df682) even that is not really relevant anymore. Stofs argument in #20501 (comment) sound like a new feature that can be implemented separately. Ref. #24267 Commits ------- ffc74eb [DoctrineBridge] Deprecate DbalSessionHandler
A new Component to handle DSN and retrieve a parameterized connection.
Such logic is actually duplicated in Cache and Lock component.
Other DSN could be handled in the future like pdo
When merged, the next steps are (can not be done in this PR because coimpoent is not released and not available in packagist => could not be used as a dependency)
TODO
class
in MemcachedFactory too