Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[DI] Include ids from method map in known services#19690

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
ro0NL wants to merge2 commits intosymfony:2.7fromro0NL:di/include-method-map
Closed

[DI] Include ids from method map in known services#19690

ro0NL wants to merge2 commits intosymfony:2.7fromro0NL:di/include-method-map

Conversation

@ro0NL
Copy link
Contributor

QA
Branch?2.7
Bug fix?yes
New feature?no
BC breaks?no
Deprecations?no
Tests pass?yes
Fixed ticketscomma-separated list of tickets fixed by the PR, if any
LicenseMIT
Doc PRreference to the documentation PR, if any

Service id's mapped thru$this->methodMap are allowed for inget, but apart from that they are not used. Ie.has returns false, butget returns.

chalasr reacted with thumbs up emoji
@nicolas-grekas
Copy link
Member

nicolas-grekas commentedAug 22, 2016
edited
Loading

$this->methodMap is a compilation artifact, thus any services there is/must be in$this->services.

@nicolas-grekas
Copy link
Member

Too quick comment, I didn't mean$this->services but all theget*Service() methods.

publicfunctiongetServiceIds()
{
$ids =array();
$reversedMethodMap =array_change_key_case(array_flip($this->methodMap), \CASE_LOWER);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I don't think the changes on this method are legitimate:get*Service are the only "source", methodMap must be in sync with the correspond list and we don't need to merge it.

Copy link
ContributorAuthor

@ro0NLro0NLAug 22, 2016
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Tests proves it works... you cant imagine how creative users can be. Sorry, couldnt resist ;-)

$this->methodMap is a compilation artifact

What if we make it a feature?

Copy link
Member

@nicolas-grekasnicolas-grekasAug 22, 2016
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

you cant imagine how creative users can

except that as you spotted, it never worked :)

What if we make it feature?

then it should go on master and should have a supporting use case

Copy link
ContributorAuthor

@ro0NLro0NLAug 22, 2016
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

You're right, i didnt considerPhpDumper::generateMethodName on this, which shows method names are generated from id.

However, they can get suffixed due duplicate methods, but that doesnt mean the id should change as well (this is the key inmethodMap which can be perfectly unique).

I covered exactly that intests (imagine a suffixed/generated method though).

@stof
Copy link
Member

@nicolas-grekas technically, this change is needed, becauseget*Service methods may not allow to retrieve the original service id anymore in all case (as we change the generated name to exclude invalid chars and avoid conflicts). Symfony itself went creative regarding the method map, and forgot to check thatgetServiceIds keeps working with that.

On a side note, older versions were also buggy because the conversion between id to method name is ambiguous (multiple ids could generate the sameget*Service method, which is also why we made the change in the generation).

so 👍 for this bugfix

@ro0NL please add a test ensuring thatgetServiceIds works fine when the method map used a different name to avoid conflicts (seePhpDumperTest::testConflictingServiceIds for such a setup generating a different name)

@stof
Copy link
Member

Technically,get*Service methods arealso a compilation artifact. So maybe we should use the method instead to check for services.
Btw, this would be safer. Due to ambiguity in the generation of factory methods (multiple ids can generate the same method name),has could tell you that a service exist while the factory would actually be registering it under a different id, which will do weird things (the service will not be shared properly, and each call to get it will replace the shared instance of the real id)

@ro0NL
Copy link
ContributorAuthor

ro0NL commentedAug 22, 2016
edited
Loading

@stof are you proposing to revert the last commit, and be safe in all cases?

please add a test ensuring that getServiceIds works fine when the method map used a different name to avoid conflicts

If i understand correctly, i kinda did that inedd63a7#diff-8aeba2b88d0347062017ee160a6beb49R619

However i considered it from a user perspective.. (ie. map how you want)

@stof
Copy link
Member

If i understand correctly, I kinda did that inedd63a7#diff-8aeba2b88d0347062017ee160a6beb49R619

yeah indeed. I missed it sorry.

@ro0NL
Copy link
ContributorAuthor

I think we should be safe in all cases. Yes, it's a compilation artifact but it's more or less designed as a feature. The reverse lookup is needed for sure.

Leaving; lowercasing and merging remaining service ids. Both are related to "different method convention", so if we go safe now this wont bother us ever again :)

$ids =array();
$reversedMethodMap =array_change_key_case(array_flip($this->methodMap), \CASE_LOWER);
foreach (get_class_methods($this)as$method) {
if (preg_match('/^get(.+)Service$/',$method,$match)) {
Copy link
Member

@nicolas-grekasnicolas-grekasAug 23, 2016
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

So, shouldn't we remove this and rely sololy on methodMap?

the full method could be reduced to the following implementation, isn't it?:

publicfunctiongetServiceIds(){returnarray_keys($this->methodMap +$this->services);}

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Depends how this methodshould work before/after compilation.

We could lazily load the method map when needed, which also happens when dumping. Im assuming the methods wont change during runtime anyway :)

publicfunctiongetServiceIds()
{
$ids =array();
$ids =array_keys($this->services +$this->methodMap);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Still thinking we should only return this, no need to look for getters.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

should be the other way around btw:$this->methodMap + $this->services to mimic current ordering (at prevent changing test cases, isn't it)?

ro0NL reacted with thumbs up emoji
@ro0NL
Copy link
ContributorAuthor

ro0NL commentedAug 24, 2016
edited
Loading

So, we have 2 issues then;

  • state before compilation (wemust depend on method_exists, get_class_method)
  • state after compilation (wecan depend on methodMap)

Where state depends onisCompiled/isFrozen. However, we dont actually compile the method map (ie. inContainer::compile, which is bit weird maybe). Now suddenly state depends on thePhpDumper.

Basically we have no clue methodMap is available by design :) there is noisDumped method or so. Hence, we cover both cases?

I tend to agree with@nicolas-grekas we should only consider state after compilation (although we can do both, like currently). However, i think we must keep it consistent, ie; removingget_class_methods ingetServiceIds, means removingmethod_exists inhas.

So perhaps compilingmethodMap should be moved toContainer::compile first, to make things clear. Or just lazy load it :)

@ro0NL
Copy link
ContributorAuthor

Here's a wild idea, makemethodMap truly a compilation artifact by moving it all to the dumped container.

Cleanup anything "method" related inContainer (iemethod_exists, etc.), and override the affected methods in the dumped container, think;

// ProjectServiceContainer extends Containerprotected$methodMap =array('id' =>'getFooService'// just dumped as is);publicfunctionhas($id){returnisset($this->methodMap[$id]) ||parent::has($id);}

This seems to follow current design of the dumper... ie. i think this is the way to go (methodMap is no more a hidden feature of theContainer).

@stof
Copy link
Member

stof commentedOct 1, 2016

@ro0NL your proposal does not work for get (it must go in the middle of the code), and can only work for has if we kill the optimization avoiding unnecessary strtolower calls.

@ro0NL
Copy link
ContributorAuthor

ro0NL commentedOct 1, 2016
edited
Loading

True,get needs a little refactoring, ie. it should use adoInitialize,doHas, etc. that we can override.

Im not sure this approach is worth the effort.. but i do think it's better design opposed toContainer being the almighty thing.

@stof
Copy link
Member

stof commentedOct 1, 2016

@ro0NL The little refactoring needed would involve dropping the performance optimization added a few months ago. This is not a good idea IMO.

@ro0NL
Copy link
ContributorAuthor

ro0NL commentedOct 1, 2016
edited
Loading

@stof this wouldnt work?

# class Container    public function has($id)    {        for ($i = 2;;) {            if ('service_container' === $id                || isset($this->aliases[$id])                || isset($this->services[$id])            ) {                return true;            }            if (--$i && $id !== $lcId = strtolower($id)) {                $id = $lcId;            } else {                if (isset($this->privates[$id])) {                    @trigger_error(sprintf('Checking for the existence of the "%s" private service is deprecated since Symfony 3.2 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED);                }-               return method_exists($this, 'get'.strtr($id, $this->underscoreMap).'Service');+               return $this->doHas($id);            }        }    }+   // extension point for performance (opposed to overriding has()))+   // ie. this technically finalizes has()+   protected function doHas($normalizedId)+   {+       return false;+   }

OverridedoHas in dumped containers checking the method map additionally.

@nicolas-grekas
Copy link
Member

Closing in favor of#20113

@stof
Copy link
Member

stof commentedOct 5, 2016

@ro0NL with extra overhead of the method call in a performance-sensitive code path (get is more sensitive than has btw)

@ro0NLro0NL deleted the di/include-method-map branchOctober 5, 2016 17:14
fabpot added a commit that referenced this pull requestOct 8, 2016
…for dumped containers (stof)This PR was merged into the 3.2-dev branch.Discussion----------Use the method map as authoritative list of factories for dumped containers| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | yes| New feature?  | no| BC breaks?    | only for weird broken cases| Deprecations? | yes (but only for people doing weird things)| Tests pass?   | yes| Fixed tickets |#11761,#19690| License       | MIT| Doc PR        | n/aThe initial implementation of the method factory discovery was based on a naming convention for factory methods. However, this naming convention allowed to generate the same name for multiple ids. In the meantime, a method map was introduced to solve this issue (and others).When accessing a service with a different id than the official one (thanks to ambiguities), this breaks the sharing of the service, as it creates a new instance each time and replaces the existing shared instance. This was also inconsistent between a dumped container (affected by this) and a non-dumped container (reporting a service not found error for the other id).The method map is now the authoritative way to discover available service factories. When the dumped container was generated with a method map (which is the case when using the dumper shipped in the component), the logic based on defined methods is not executed anymore. This forbids using another id than the real one to access the service (preventing to trigger the broken behavior). So this breaks BC for people being lucky (i.e. they were using the broken id only once and *before* any usage of the official id) and fixes a WTF bug for all others.When using a dumper which does not fill the method map, the old logic is still applied, but deprecation warnings are triggered on access to dumped services. Currently, this will trigger a deprecation warning for each new service instantiation. I have not found an easy way to trigger it only once (except adding a private property to remember we already triggered it, but is it worth it ?), but only people writing a project container by hand or writing their own dumper would ever see such deprecation anyway (as the core dumper generates the method map).Additionally, this makes ``getServiceIds`` faster by avoiding doing a regex match for each method in the class.Commits-------03b9108 Use the method map as authoritative list of factories for dumped containers
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@nicolas-grekasnicolas-grekasnicolas-grekas left review comments

Assignees

No one assigned

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

4 participants

@ro0NL@nicolas-grekas@stof@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp