-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[DI] Randomize private services #19683
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
Conversation
@@ -839,6 +839,7 @@ public function __construct() | |||
|
|||
$code .= "\n \$this->services = array();\n"; | |||
$code .= $this->addMethodMap(); | |||
$code .= $this->addPrivateServices(); |
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.
Needed to trigger deprecations in get
for a compiled container when dumped.
This is basically a big copy/paste from the original Also, please note that in Symfony 4, private services will be implemented by using a randomly generated service ID. So implementing a complete new API for private services isn't needed. |
This is about separating (concerns), not copy-paste. Basically this makes About 4.x changes.. that is eventually still future talk. I compare against the current codebase. Even then, if you have a "random id" you need to be able to get it internally, without deprecations. That would only cleanup |
Im not even sure we need random id's, if it's bulletproof already. |
Using random id's makes it possible to use same logic, it be like using a normal id except you don't know the id. Is it possible to benchmark these changes with Blackfire.io? I'm worried performance could be degraded. |
A compiler would generate random IDs for private services and fix the service definitions that depend on them. No need to change anything in the |
@wouterj Fair enough 👍 I just dont understand how that reflects dumping..;
@sstok i think it's boosted (we bypass |
As said by @sstok, the only difference between a private and a public service will be that you don't know the ID of the private one (as it's automatically generated). |
You could start by randomizing them already, and use those. But keep an alias array with private services, pointing to the actual id 👍 |
Based on the current design;
|
If requested service is the randomized ID, it's an internal call and thus should not trigger deprecations. If it uses the original service ID, it's a call from outside world and the deprecation should be triggered (and randomized service returned). E.g: public function get($id, ...)
{
if (isset($this->privates[$id])) {
@trigger_error(..., E_USER_DEPRECATED);
$id = $this->privates[$id];
}
// ... behave like normal, $id is either a public service or the randomized ID
} |
Ok, that clarifies. Leaving a minor leak whenever the user has a random id some way, some how. We could live with that, i guess 👍 i personally favor the separation by design though. Still; |
The internal
We don't check for random ID (what's random?), we check if the ID exists in the array that contains generated IDs by the compiler. |
Thanks @wouterj this was really clarifying, and probably makes a separate method obsolete. What about if we do it right now? I really dont think we should bridge it with workarounds (no offense). edit: about the leak; whenever you have |
The |
@ro0NL: Even if your work looks great, @wouterj 's approach has the benefits of keeping things simple and pragmatic to fix the related issue. But that's only my opinion, a core member is more legitimate to hint you on this. Just saying you should probably don't work too hard on this right now ;) |
Im just playing around with randomizing now.. eventually i want to revert what i did to However, the tests are updated to the right behavior. That was was an effort, yes. I understand @wouterj 's approach and motivation, im only proposing a structural solution to consider as we can currently still target master. |
@fabpot as this was your idea more or less.. WDYT? Tests are failing nicely so far;
We have the map now in |
$this->idMap = array(); | ||
foreach ($container->getDefinitions() as $id => $definition) { | ||
if (!$definition->isPublic()) { | ||
$this->idMap[$id] = $this->randomizer ? (string) call_user_func($this->randomizer, $id) : md5(uniqid($id)); |
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.
uniqid
should not be used here, it's anything but unique and actually causes collisions very easily.
See https://github.com/symfony/symfony/blob/master/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php#L291 for how it's done there.
Status: Needs work |
@sstok would it make sense to mark a random definition (old private) as public after compiling? Currently we deprecated This doesnt make sense to me: |
Yeah, I don't think we need to do this in 3.x |
I dont mind :) i just dont like how we work with privates right now. And i do think this is the cleanest solution eventually, as the container does not have to care about privates. Currently leading to all sorts of quirkiness, as you might have noticed. But yes, the pass can be added on top of #19708 in 4.x. From the component pov i would prefer this though (understanding the scope is wider, however we do advertise with standalone components ;-)). |
* | ||
* @author Roland Franssen <franssen.roland@gmail.com> | ||
*/ | ||
class RandomizePrivateServiceIdentifiers implements CompilerPassInterface |
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.
IMO, randomization shouldn't be handled by a compiler pass, but by the PhpDumper directly. Otherwise it won't be applied without the compiler, and many apps won't have it enabled when migrating to 4.0
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 considered this. But eventually chose to separate it into a compiler pass (yes; making it an optional feature). Without compiling you have a normal container.
Thing is i dont really like the concept of coupliing privates with a container. The container shouldnt care about privates (it's just a normal service from that perspective). Hence $this->privates
could be totally unneeded...
No need to bypass get
or checking private definitions in PhpDumper
. We work with the 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.
What you call a normal container doesn't have private services. The container shouldn't care about private services, right, but the PhpDumper should (and already does). So I still think that the PhpDumper should do this.
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 disagree :) i think we have compiler passes for that (optional) or otherwise Container::compile
(by design).
A dumper (eg. PhpDumper
) dumps the given container as is. Like others do, YAML, XML etc. I dont know why PhpDumer
would randomize your services, whereas XmlDumper
doesnt.
Currently it only deals with privates to hack the dumped container (ie. output), saying; here's a list of privates for you to validate business logic (which it shouldnt care about :)).
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.
Because it makes no sense to randomize with XmlDumper
and in fact would be wrong: it would rename services for no reason and make dumps harder and not accurate.
What matters is having dumpers deal with private services, which XmlDumper
does by adding the public="false"
attribute, and PhpDumper
doing some randomization. That! would make sense.
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.
Reconsidered :)
Because it makes no sense to randomize with XmlDumper and in fact would be wrong: it would rename services for no reason and make dumps harder and not accurate.
My first thought was, yeah this totally counts for a PhpDumper
as well. Which in some ways is still true, but i guess you're right. Randomizing could be the way a php dumper deals with privates by design (whereas xml adds public="false"
👍 ).
Do we need to consider DX here?
- Make it depend on some debug flag?
- Have 2 implementations (think
DebugPhpDumper
) Forget about randomizing, and compile different method names (thinkgetPrivate(.+)Service
)- Nah.. this makes the
Container
work with "privates" again 😕
- Nah.. this makes the
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.
Not sure how DX is involved here. So maybe but I don't what it'd mean :)
Forget about randomizing, and compile different method names
maybe :) the goal is to have private services be hidden from the outside, no interference with the public interface.
How it's done is an implementation detail We should chose the simplest one... :)
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 chose the simplest one
True.
DX, in the sense that you may not want to have random id's in the output, or at least customize/mock it in some way (think setRandomizer
).
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 maybe the simplest and most DX friendly is to make private services leak a bit by reserving their id, throwing an exception whenever one uses or fetches an id that collides with a private service.
…ces internally (nicolas-grekas) This PR was merged into the 3.2-dev branch. Discussion ---------- [DI] Dont use Container::get() when fetching private services internally | Q | A | ------------- | --- | Branch? | master | Bug fix? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #19683, #19682, #19680 | License | MIT As spotted by @wouterj, we forgot to remove the deprecation notice when doing internal calls to get private services. Yet, we don't need to get through this `get()` method, because we can already resolve many things at compile time for private services. This will provide another small performance optim, and fix the issue. Commits ------- a9c79fb [DI] Dont use Container::get() when fetching private services internally
So, following the discussion above, my stand on this is:
This means not randomizing private services, even in 4.0. |
👍 eventually it comes down to track private id's yes/no. Like currently.. Personally i'd favor randomizing id's in the But it doesnt matter much i guess. |
Alternative approach compared to #19682
Thanks to @wouterj for explaining this approach.
Theory of operation;
private id
=>random id
The deprecation now only happens after compilation(!). This can be seen a design choice, i guess.