Skip to content
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

WIP: Yii2 implements PSR-11 #14107

Closed
wants to merge 6 commits into from
Closed

WIP: Yii2 implements PSR-11 #14107

wants to merge 6 commits into from

Conversation

SamMousa
Copy link
Contributor

@SamMousa SamMousa commented May 3, 2017

Q A
Is bugfix? no
New feature? yes
Breaks BC? no
Tests pass? yes

PSR-11 is accepted and Yii implements it.

@samdark samdark self-assigned this May 3, 2017
@samdark samdark added this to the 2.0.13 milestone May 3, 2017
@cebe
Copy link
Member

cebe commented May 4, 2017

NotFoundExceptionInterface and ContainerExceptionInterface also need to be considered to be compatible. There is more to it than just implementing the interface, behavior must match the spec too:

https://github.com/php-fig/fig-standards/blob/10bb43b1802c0427f8a4a5d1e6a84da83fa7724d/accepted/PSR-11-container.md#12-exceptions

@SamMousa
Copy link
Contributor Author

SamMousa commented May 4, 2017 via email

@Faryshta
Copy link
Contributor

Faryshta commented May 4, 2017

the signatures are not the same either

public function get($class, $params = [], $config = [])

public function get($id)

and they do different things too. while get() in psr-11 finds things already in the container, the get() method might build a new object, execute a callback, and if the first parameter is a class name then there is an implied set().

i don't think psr-11 is for yii2

@cebe
Copy link
Member

cebe commented May 4, 2017

the signatures are not the same either

$params and $config are optional, so the interface should be fine.

and they do different things too

if it matches the spec of PSR-11 that is fine, it can do more but it must support what is defined in the PSR to be compatible to it.

@SamMousa
Copy link
Contributor Author

SamMousa commented May 4, 2017

@samdark I'm working on this now.
While looking at the exceptions I noticed this in Container::get(): link

    throw new InvalidConfigException('Unexpected object definition type: ' . gettype($definition));

However this is part of a path that is unreachable. All definitions are validated when they are set inside the container, so this seems like a redundant check on a private variable.
Can I remove it?

Also, NotInstantiableException, something I added in 2.0.9 extends from InvalidConfigException, in hindsight this does not make much sense, can we "schedule" to change this in 2.1.0?
Not sure what the best base class is, but one candidate would be InvalidCallException.

@SamMousa
Copy link
Contributor Author

SamMousa commented May 4, 2017

@Faryshta I think we're fine with respect to get() implementation, this is from the spec:

  • get takes one mandatory parameter: an entry identifier, which MUST be a string.
    get can return anything (a mixed value), or throw a NotFoundExceptionInterface if the identifier
    is not known to the container. Two successive calls to get with the same
    identifier SHOULD return the same value. However, depending on the implementor
    design and/or user configuration, different values might be returned, so
    user SHOULD NOT rely on getting the same value on 2 successive calls.

While we support other argument types, users of the container will only pass in strings (since that is what the specs tell them to do), so other behavior is not relevant for them.
Our DI container might not only return singletons, but this is a recommendation (SHOULD) not a hard requirement (MUST). Furthermore, calling code SHOULD NOT assume that we always return singletons.

@wggwcn
Copy link

wggwcn commented May 5, 2017

watching it


namespace yii\di;

use \yii\base\InvalidConfigException as Base;
Copy link
Member

Choose a reason for hiding this comment

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

using Base as alias is not preferable. Using the full class name is better here

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not implement ContainerExceptionInterface at yii\base\InvalidConfigException after all, the init() method on most classes throws that exception already.

Copy link
Member

Choose a reason for hiding this comment

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

yii\base\InvalidConfigException has a wider meaning - it refers to any mis-configuration error, which is not mandatory triggered by DI.

Copy link
Contributor

@Faryshta Faryshta Sep 7, 2017

Choose a reason for hiding this comment

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

So? The PSR never says that only DI can throw said exceptions.

Besides as you say any configuration error will throw yii\base\InvalidConfigException which by the PSR.

A call to the get method with a non-existing id MUST throw a Psr\Container\NotFoundExceptionInterface.

So $di->get('db', [/* invalid params or config */]) must throw Psr\Container\NotFoundExceptionInterface

composer.json Outdated
@@ -78,7 +78,8 @@
"bower-asset/jquery": "2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable",
"bower-asset/jquery.inputmask": "~3.2.2 | ~3.3.3",
"bower-asset/punycode": "1.3.*",
"bower-asset/yii2-pjax": "~2.0.1"
"bower-asset/yii2-pjax": "~2.0.1",
"psr/container": "^1.0"
Copy link
Member

Choose a reason for hiding this comment

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

I'd rather not pollute composer.json for such 'nice-to-have' features

Copy link
Member

Choose a reason for hiding this comment

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

That's fine. It's an interface and there's no other way to solve it rather than requiring it.

Copy link
Member

Choose a reason for hiding this comment

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

true

@samdark
Copy link
Member

samdark commented Jun 25, 2017

However this is part of a path that is unreachable. All definitions are validated when they are set inside the container, so this seems like a redundant check on a private variable.
Can I remove it?

Yes.

@samdark
Copy link
Member

samdark commented Jun 25, 2017

Also, NotInstantiableException, something I added in 2.0.9 extends from InvalidConfigException, in hindsight this does not make much sense, can we "schedule" to change this in 2.1.0?
Not sure what the best base class is, but one candidate would be InvalidCallException.

What would extend PSR NotFoundExceptionInterface then?

@samdark
Copy link
Member

samdark commented Jun 25, 2017

Overall signatures and behavior are compatible. I see no reason not to merge it.

@SamMousa
Copy link
Contributor Author

SamMousa commented Jun 26, 2017

What would extend PSR NotFoundExceptionInterface then?

NotInstantiableException will still implement it. It just won't be derived from InvalidConfigException (since it is not invalid config, but an invalid call that is causing it). This is entirely optional and mostly academical ;-)

@SamMousa
Copy link
Contributor Author

@samdark Could you check why the tests are not working? I've done a reinstall of composer.lock since it had a merge conflict. Now it seems composer install is failing for all tests.

Side note: do we really need composer.lock in our repository? Does it even do anything?
https://getcomposer.org/doc/02-libraries.md#lock-file

@samdark
Copy link
Member

samdark commented Jun 26, 2017

Probably lock file is in damaged state. Delete it, re-run composer update (ideally w/ PHP 5.4) and commit changes. We need it to run code coverage and code analysis tools. They're not able to install global composer plugins.

@SamMousa
Copy link
Contributor Author

SamMousa commented Jun 26, 2017

Where do i get PHP5.4? Wayback Machine? :-D

This was originally a joke, I thought there would be an official docker image with PHP5.4.... Turns out I was wrong

@samdark
Copy link
Member

samdark commented Jun 26, 2017

php.net :) I can take care of it.

@SamMousa
Copy link
Contributor Author

Okay, great, google gave me this docker image: https://github.com/alterway/docker-php/blob/master/doc-php-cli.md

@samdark
Copy link
Member

samdark commented Jun 26, 2017

These would do as well.

@SamMousa
Copy link
Contributor Author

@samdark I have reinstalled all using a php 5.4 environment, still doesn't seem to be working.

@samdark
Copy link
Member

samdark commented Jun 26, 2017

composer.lock.zip

@SamMousa
Copy link
Contributor Author

@samdark I give up; it seems the composer.lock you posted results in a wrong phpunit version... I'd appreciate it if you could fix it 8-)

@samdark
Copy link
Member

samdark commented Jun 27, 2017

Locally I did. Container unit tests are giving errors...

@cebe
Copy link
Member

cebe commented Jul 12, 2017

Overall signatures and behavior are compatible. I see no reason not to merge it.

  • from the spec: If has($id) returns false, get($id) MUST throw a NotFoundExceptionInterface.

    The container currently does not have this behavior. We instantiate classes even if there is no definition and has() returns false on them.

    However, the description of the has() method in the interface is not as strict:

    has($id) returning true does not mean that get($id) will not throw an exception.
    It does however mean that get($id) will not throw a NotFoundExceptionInterface.

  • from the spec: A call to the get method with a non-existing id MUST throw a Psr\Container\NotFoundExceptionInterface.

    I do not see where this is implemented right now.

  • from the spec: Packages providing a PSR container implementation should declare that they provide psr/container-implementation 1.0.0.

    a "provide" section should be added to the composer.json in / as well as in /framework

@SamMousa
Copy link
Contributor Author

@cebe Can you double check the exception stuff? Note that NotInstantiableException extends the PSR exception.

@klimov-paul
Copy link
Member

Applying ContainerInterface over existing Container produces potential interface compatibility error.
At the ContainerInterface get() and has() methods have first parameter named $id, while same methods of Container use $class name. Compare:

interface ContainerInterface
{
     public function get($id)
}

with:

class Container extends Component
{
    public function get($class ...)
    {
    }
}

While during inheritance change of the method argument is allowed, it may end with E_STRICT error at some environment. We already have similar issues like #13538 about slight inconsistencies around method redeclaration, which can be spoted only in particular strict environment.

To make things right signature of the Container methods should be changed like following:

class Container extends Component
{
    public function get($id, $params = [], $config = [])
    {
    }
}

which actually a BC break.

@SamMousa
Copy link
Contributor Author

SamMousa commented Sep 7, 2017

@klimov-paul as far as I know the parameter name is different, the type is not.
They are both string identifiers for something that you want from your container.
Doing $container->get(SomeClass::class); in no way guarantees the type of object. Using the class name as identifier is just a common convention that works well.

@klimov-paul
Copy link
Member

I can not say that current implementation of the Container actually matches ContainerInterface, since ContainerInterface::get() expects to throw NotFoundExceptionInterface in case no definition is found for given $id, while Container::get() attempt to build not existing definition.

At the present state of this PR Container::get() thows NotFoundExceptionInterface only in case definition is interface or abstract class, while this should refer to ContainerExceptionInterface.

@@ -16,7 +17,7 @@
* @author Sam Mousa <sam@mousa.nl>
* @since 2.0.9
*/
class NotInstantiableException extends InvalidConfigException
class NotInstantiableException extends InvalidConfigException implements NotFoundExceptionInterface
Copy link
Member

Choose a reason for hiding this comment

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

This does not seem to be appropriate: NotFoundExceptionInterface means there is NO definition of the entity inside the container, while NotInstantiableException means the definition is invalid, pointing to something, which can not be intantiated like interface or absract class.
These exeptions can not be made equal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The PSR does not have something for not instantiable.


namespace yii\di;

use \yii\base\InvalidConfigException as Base;
Copy link
Member

Choose a reason for hiding this comment

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

yii\base\InvalidConfigException has a wider meaning - it refers to any mis-configuration error, which is not mandatory triggered by DI.

@klimov-paul
Copy link
Member

klimov-paul commented Sep 7, 2017

Doing $container->get(SomeClass::class); in no way guarantees the type of object. Using the class name as identifier is just a common convention that works well.

Sorry, but I do not understand what are you referring to.
Method signature involves both arguments name and type.

@klimov-paul
Copy link
Member

I suggest applying PSR-11 to be performed at 2.1 branch in appropriate matter, including methods signature change, exception hierarchy revision and logic adjustments, instead of patching 2.0.x by all cost.

@SamMousa
Copy link
Contributor Author

SamMousa commented Sep 7, 2017

I don't mind waiting for 2.1 for this, just not all your arguments are valid.

It is fine to provide a container that has default definitions for ids that happen to be class names. That does not break the spec, it just means that the container is preconfigured.

I wasn't aware that the name of an argument was relevant; if it is that's something that can be easily fixed of course.

@samdark
Copy link
Member

samdark commented Sep 7, 2017

I agree that it makes sense to do the change in 2.1 where we can actually adjust semantics correctly, add features necessary and change method signatures. I'm closing the pull request. Let's use #14112 for future discussion.

@samdark samdark closed this Sep 7, 2017
@cebe cebe removed this from the 2.0.13 milestone Sep 8, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants