Alexandru,

I agree 99% with your comments regarding what should be considered good practice when dealing with DI or a DIC. That's what one should be doing.

However, that does not in any way say that people *will* do that, and as noted most existing DIC implementations have the option to behave otherwise. You say the correct approach is to inject a factory service and get a one-off object out of that. I (mostly) agree. But as written, the spec explicitly says that it could be used as a factory, but I as a consumer cannot tell if what I'm getting is a factory or not.

A factory-only container (legal in PSR-11 right now) would be mostly incompatible with a database connection, or any other object that is backed by an IO socket directly or indirectly. Fun times. :-)

As Pedro well-explains, that means in any non-ideal circumstances (ideal: everything in the container is completely stateless both directly and indirectly) the behavior of the service I get from the container may vary depending on whether it's shared or not, and I as a consumer can't tell the difference until it results in subtle effects on my application that will be hard to track down.

--Larry Garfield

On 11/08/2016 09:02 AM, Alexandru Pătrănescu wrote:
Hi Pedro,

You articulated your problem well. Thanks.

If you have a need to use a container so that it must return a shared or new instance than using the PSR-11 interface directly is not a solution.
Just as everyone said, PSR-11 is not addressing this problem.

I said this another time in another thread:

Having in mind the fact that you can't rely on obtaining a shared/new instance from the container, can you still use the PSR-11 interface? My answer is yes, considering some practiced that people might say are good practices anyway:

Use the container to inject stateless services only.
Use as much as possible only stateless services so that you will need only one instance of it in a php process. You should separate state: value objects should be different class than service objects. Value object keep state, service objects execute logic using state as state as input/output.

If you really need to have a service with state, don't inject it directly but inject the stateless service object that can give you the instance you need. That is a factory in case you always need to create a new instance or is a registry in case you need a multiton pattern or a singletons wrapper in case you really need to be sure you are using the same instance like for an event manager.

So you see, you can build some tools to wrap a PSR-11 interface. You will not use it directly for all cases but probably there won't be so many cases where you have non-stateless services if you try to do it.
This is how I use the container-interop interface and it's working.
Anyway, you should build your classes agnostics of PSR-11. Only the "application" classes will use it to wire things up.

Hope I was helpful with my point of view.

Regards,
Alex


On Tue, Nov 8, 2016 at 2:55 PM, Pedro Cordeiro <[email protected] <mailto:[email protected]>> wrote:

    Hi, Matthew!

    I understand everything you said. About the configuration
    responsibility, this PSR's scope and the fact that it's coherent
    to have non-shared services in some contexts. I never disagreed
    with any of that.

    I'll try to be more objective here.

    Given two containers:

    a) "ContainerShared", that implements PSR-11 and ONLY returns
    shared instances, no option to configure non-shared services;
    b) "ContainerFactory", that implements PSR-11 and ONLY returns
    non-shared instances, acts as a factory always.

    Do you agree that:

    1) These implementations are perfectly fine PSR-11
    implementations, as it's phrased right now?
    2) These implementations are incompatible between themselves for
    pretty much every use case, including zend-expressive, that is
    currently the most successful case of container interoperability?
    3) The incompatibility between these two containers is not an
    application/configuration issue, but plain lack of
    interoperability between them?

    If you agree with (1), (2) and (3), the final question that stands
    is: is it still OK to have container implementations that are NOT
    interoperable implementing an interoperability standard?

    I understand zend-servicemanager is a coherent implementation,
    that allows for both shared and non-shared services, and that
    configuring which services are shared is up to the application. I
    don't disagree.
    I understand that most current container implementations allow for
    both kinds of services, shared and non-shared, and that it'd be up
    to the application to configure which is which. I don't disagree.

    But certain implementations (as presented above) could still be
    100% incompatible for its consumers - not because *I*, the
    application developer, configured something wrong (a bug), but
    because the *CONSUMING* standard (PSR-11) simply allows for
    non-interoperable container implementations.

    If *YOU*, zend-expressive developer, were to develop a truly
    agnostic container consumption implementation based on PSR-11,
    you'd have to document something to the effect of: "while
    zend-expressive is compatible with PSR-11 containers in general,
    it's not meant to be used with factory-only containers. Choose
    accordingly.". THAT is what strikes me as something weird to have.

    I'd choose (3) over (1), but (1) is better than (2). Enforce
    shared-only services or allow shared/exclusive services to
    coexist, explicitly disallowing factory-only implementations. If
    you can't do either, having factory-only containers should be
    highly discouraged on the META document and the standard shouldn't
    even mention the possibility of having non-shared services (even
    though it'd allow by omission).

    If you disallow factory-only implementations, you can truly blame
    the application configuration for any misuse of the container. But
    if you allow factory-only implementations to coexist with
    shared-only implementations, you end up with a non-interoperable
    interoperability standard.

    2016-11-07 21:34 GMT-02:00 Matthew Weier O'Phinney
    <[email protected] <mailto:[email protected]>>:

        On Fri, Nov 4, 2016 at 7:55 PM, Larry Garfield
        <[email protected] <mailto:[email protected]>> wrote:
        > Pedro did a fine job of explaining the sort of use case
        where I'd expect to
        > see potential issues with a lack of clarity about whether a
        service is
        > shared or not.  Frankly I don't find the "out of scope"
        argument to be
        > compelling; I as a consumer care, therefore it is in scope
        to me as a
        > consumer.
        >
        > The only compelling argument I've seen so far is the BC
        issue for existing
        > container implementations, which is a valid consideration.
        >
        > To that end, I'd prefer to get input from Fabien and Taylor,
        as David
        > suggests, before weighing in on the listed options.

        I'll weigh in, as we're a project that provides a concept of
        shared vs
        unshared services.

        zend-servicemanager shares services by default. That's the
        expected
        use case 99% of the time. However, there are occasionally
        services you
        may not want to share, and for that we have the ability to mark
        services as *not* shared. One example already raised in this
        thread is
        event manager instances; these are specific to each service
        composing
        them, but are themselves injected with a shared event manager
        instance
        for sharing listeners across event manager instances.

        Another situation is with some of our plugins: validators,
        filters,
        etc. that may be stateful, and require separate instances as many
        instances with different configuration may be in play.

        From a consumer perspective, this is something *you* are in
        control
        of. It is something specific to *your* application, where
        *you* have
        made the decision of what container you are using, and thus have
        control over the *configuration* of it.

        From a practical standpoint, consider Expressive. We provide
        functionality for setting up three different container
        implementations
        currently: Aura.Di, Pimple, and zend-servicemanager. We
        provide some
        functionality for bridging configuration between them in the
        file that
        creates the container instance. But final application
        configuration
        and container configuration *is up to you, the developer*.

        So far, it's not posed any problems. If you do not want an
        instance
        shared, you configure it that way.

        From the *API* perspective, it doesn't matter, because the
        code that
        consumes the container is only exercising the
        container-interop API,
        and assumes that *you*, the developer, have configured the
        container
        the way you need to.

        As such, I totally agree with the statement that whether or
        not the
        instance returned by get() is shared or not is out of scope;
        that's in
        scope for a PSR that details *configuring* a container, as the
        point
        at which you decide whether or not an instance is shared is during
        configuration, not *consumption*. PSR-11 is about *consuming*
        services
        from a container.


        > On 11/04/2016 12:19 PM, Pedro Cordeiro wrote:
        >
        > Hi, David.
        >
        > Thank you again for your response.
        >
        > I understand your point of view. You don't think there is
        anything the ORM
        > should do to prevent fetching a non-shared instance, because
        that would be
        > the responsibility of the application. I kind of agree, to a
        limited scope.
        >
        > But if you allow me to press on this issue just a little
        longer... it still
        > doesn't change the fact that I can still have two different
        containers:
        > ContainerShared, that only configures/fetches shared
        services, and
        > ContainerFactory, that only fetches new instances. They
        would both be PSR-11
        > compliant, and still, incompatible for most cases. And then,
        it wouldn't be
        > a configuration issue (a bug) on the application end, it
        would be a
        > component incompatibility - which kind of defeats the point
        of having a
        > interoperability standard. It'd still be a little odd saying
        something like
        > "this ORM can be used with PSR-11 compatible containers,
        except for
        > containers that don't have the option to configure shared
        services (like
        > ContainerFactory)".
        >
        > Choosing a standardized component is not a configuration
        bug, right? Should
        > we let two PSR-11 implementations be incompatible for many
        (if not most)
        > cases, in the name of backwards compatibility (with
        containers that still
        > don't extend PSR-11)?
        >
        > You tell me this concern is out of scope. I don't disagree.
        I'm just saying
        > that, IMO, the current scope is not enough to assure two
        different
        > implementations are actually compatible - and thus,
        interoperable, which is
        > kind of the point here. And there is something we could do
        to fix this,
        > without broadening the scope -- limit what kind of services
        (shared or
        > exclusive) "get" can return. This leads us to a different
        issue, which is
        > compatibility with current implementations.
        >
        > To be completely honest here, with best practices in mind, I
        can't even
        > think of why a framework component (the ORM, in this case)
        should even be
        > container-aware. It should have its dependencies declared
        directly in the
        > constructor (or in separate setters) and just trust someone
        else (the
        > application) will inject them. A container should be just
        one of the
        > framework's components, not part of its core/kernel.
        >
        > Em sexta-feira, 4 de novembro de 2016 13:45:29 UTC-2, David
        Négrier
        > escreveu:
        >>
        >> Hi Pedro,
        >>
        >> I understand your example (about the event dispatcher that
        MUST be
        >> shared), but still, I disagree this is an issue with PSR-11.
        >>
        >> You say:
        >>
        >> Now, my consumer, the ORM, is completely container
        agnostic. It only
        >> works, however, with SOME specific implementations (that
        ALWAYS return
        >> shared instances), and fails on SOME others (that ALLOWS
        for factories). I
        >> don't know (and can't control) how the entries were set,
        because I deferred
        >> this wiring to the application realm, by design. I don't
        know (and can't
        >> control) which container this is. If I can't guarantee it
        will work with ANY
        >> PSR-11 implementation, I cannot depend on the contract
        (PSR-11) - I'll have
        >> to depend of an implementation, which is bad.
        >>
        >> When you say: It only works, however, with SOME specific
        implementations
        >> (that ALWAYS return shared instances), and fails on SOME
        others (that ALLOWS
        >> for factories), I disagree. If we use "solution 3" I'm
        proposing above, you
        >> should really write: It only works, however, with SOME specific
        >> configurations (that ALWAYS return shared instances), and
        fails on SOME
        >> others (for containers that CAN provide non-shared services
        and that have
        >> been configured accordingly)
        >>
        >> Now, you say: I don't know (and can't control) how the
        entries were set,
        >> because I deferred this wiring to the application realm, by
        design.
        >>
        >> When you say "I", I suppose you mean "the ORM author", am I
        right? Of
        >> course, you are right to say the ORM author can't control
        if the event
        >> dispatcher is shared or not. And it is not its job. And I'm
        sure you agree
        >> with me if I say that the ORM is not using
        ContainerInterface directly (it
        >> is the container how creates the ORM's EntityManager and
        injects the event
        >> dispatcher in it.
        >>
        >> So in the end, the wiring of the app is deferred to the
        application realm
        >> (i.e. the end user). If the end user badly screws up its
        configuration and
        >> decides to configure a non-shared event dispatcher, that's
        its problem (it's
        >> a bug), not a problem with PSR-11. Of course, with a more
        restrictive PSR-11
        >> (that would force shared services only), this kind of bug
        would not be
        >> possible. But we loose compatibility with all the
        containers out there and
        >> it's just not worth it in my opinion.
        >>
        >> Finally, we will inevitably end up working on a PSR that
        defines how we
        >> put things in a container. In this PSR, we will need to be
        very strict
        >> regarding the fact that we set a shared or a non-shared
        service.
        >> If you haven't done so yet, you should definitely take a
        look at
        >> container-interop/service-provider. This project proposes a
        way to put
        >> things into a container (and I hope will be the basis of a
        future PSR). I
        >> just submitted a PR (here:
        >>
        https://github.com/container-interop/service-provider/pull/33/files
        <https://github.com/container-interop/service-provider/pull/33/files>)
        that
        >> makes sure that containers consuming a service provider
        MUST provide shared
        >> services. So even if PSR-11 allows for non-shared service,
        >> container-interop/service-provider explicitly forbids it
        for services
        >> provided in service providers.
        >>
        >> TL;DR; I understand your sample about the event dispatcher
        being a
        >> non-shared service. This would definitely be an issue. I
        just disagree that
        >> it is a concern with PSR-11. It is a concern with either
        the user
        >> configuring the container, or the next PSR to come that
        defines how we put
        >> things into a container.
        >>
        >> Does it make more sense?
        >>
        >> ++
        >> David.
        >>
        >>
        >> Le jeudi 3 novembre 2016 10:58:51 UTC+1, Pedro Cordeiro a
        écrit :
        >>>
        >>> > The typical use case for ContainerInterface is to allow
        a consumer
        >>> > (like a router) to be "container agnostic".
        >>>
        >>> I completely agree. I can't think of a use case where a
        router would
        >>> depend on having either shared instances or exclusive
        instances of a
        >>> controller, so I'll change this example a little.
        Hopefully this answers
        >>> @Matthieu's question too.
        >>>
        >>> Let's say I have a framework component (and ORM, maybe)
        that triggers
        >>> events (to log queries, maybe). This part of my framework
        fetches an event
        >>> dispatcher (let's say, symfony's) from the registry, and
        it HAS to be a
        >>> shared entry (otherwise, I'll just dispatch an event on a
        new instance that
        >>> has no listeners).
        >>>
        >>> Now, my consumer, the ORM, is completely container
        agnostic. It only
        >>> works, however, with SOME specific implementations (that
        ALWAYS return
        >>> shared instances), and fails on SOME others (that ALLOWS
        for factories). I
        >>> don't know (and can't control) how the entries were set,
        because I deferred
        >>> this wiring to the application realm, by design. I don't
        know (and can't
        >>> control) which container this is. If I can't guarantee it
        will work with ANY
        >>> PSR-11 implementation, I cannot depend on the contract
        (PSR-11) - I'll have
        >>> to depend of an implementation, which is bad.
        >>>
        >>> The only instances where I could depend on a 100% generic
        PSR-11
        >>> container is when I don't care at all if the service being
        retrieved is
        >>> exclusive or shared. And there only needs to be ONE place
        where I need a
        >>> specific kind of instance, and my entire framework will
        depend on a specific
        >>> implementation. The event dispatcher service is one
        example where I need a
        >>> shared instance -- the ORM could be another. I don't want
        new connections
        >>> being opened, I don't want two separate units of work with
        two different
        >>> in-memory caches. If I fetch my ORM twice from the
        container, I expect to be
        >>> given the same instance, not two different separate
        instances. If I fetch it
        >>> twice from separate consumers, I still expect to be given
        the same instance.
        >>>
        >>> Now, I'll reiterate that I understand strengthening the
        SHOULD to a MUST
        >>> would greatly hinder this PSR's adoption. I just want to
        know if you guys
        >>> understand my point and if you guys still disagree that
        this is an issue,
        >>> and why. I don't know if I'm failing to see it, but as far
        as I can think
        >>> about it, not knowing what kind of instance the container
        will yield would
        >>> also greatly restrict the possible uses and hinder its
        adoption -- and I'm
        >>> not even talking about things out of scope here, because
        I'm failing to see
        >>> its usefulness even for consumer-only frameworks, like
        shown on the previous
        >>> examples.
        >>>
        >>> Em quarta-feira, 2 de novembro de 2016 15:45:49 UTC-2,
        David Négrier
        >>> escreveu:
        >>>>
        >>>> This is a continuation from the discussion about whether
        PSR-11
        >>>> containers should always return the same instance.
        >>>> See
        https://groups.google.com/forum/#!topic/php-fig/L8rDUwRFsOU
        <https://groups.google.com/forum/#%21topic/php-fig/L8rDUwRFsOU>
        for the
        >>>> beginning of the discussion.
        >>>>
        >>>> <TL;DR;> I believe that we should keep the SHOULD word in
        place because
        >>>> it does not hurt in practice and because we can require a
        container to
        >>>> always return the same instance in the yet to come PSR
        that will define how
        >>>> we put things into a container (see
        container-interop/service-provider for a
        >>>> proposal). On the other hand, requiring the container to
        return always the
        >>>> same value would make PSR-11 de-facto incompatible with
        most containers out
        >>>> there (so should be avoided). However, I think we
        definitely need to make
        >>>> improvements on the wording that is clumsy (see end of
        the message).
        >>>> </TL;DR;>
        >>>>
        >>>> Currently, the spec says:
        >>>>
        >>>> 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.
        >>>>
        >>>> Hari, Larry, Pedro are asking if we could strengthen the
        SHOULD to a
        >>>> MUST (as in: Two successive calls to get with the same
        identifier MUST
        >>>> return the same value).
        >>>>
        >>>> For instance, Larry says:
        >>>>
        >>>> > I would prefer to have get() defined to always return
        the same object
        >>>> > (lazy loaded or otherwise, not relevant here), as that
        is the typical case,
        >>>> > and another method added (either on the same interface
        or a separate one, I
        >>>> > am flexible) to handle the factory use case.  That way
        I know the behavior
        >>>> > of each object I get back, specifically in relation to
        whether I have a
        >>>> > private copy or not.
        >>>>
        >>>> First of all, let me say if I was starting from a blank
        slate, I would
        >>>> definitely agree with you and put a MUST here. But we are
        not starting from
        >>>> a blank slate and many containers out there offer the
        possibility to create
        >>>> new services each time you call "get". For instance
        Pimple (with the factory
        >>>> method), or Symfony (with the "shared" option:
        >>>>
        http://symfony.com/doc/current/service_container/shared.html
        <http://symfony.com/doc/current/service_container/shared.html>)
        >>>> I definitely want Pimple and Symfony in PSR-11 (or at
        least I want to be
        >>>> able to create an adapter for those) and putting a "MUST"
        in the
        >>>> requirements makes this impossible. So if we decide to
        force containers to
        >>>> always return the same value, we'd better have a very
        good reason to do so.
        >>>>
        >>>> Now, what is the real impact of this?
        >>>>
        >>>> You all seem to imply that if we don't put a "MUST" here,
        then we cannot
        >>>> "trust" the container to give us a usable instance. The
        container might be
        >>>> "vicious" and decide to fool its user by giving him a new
        instance while the
        >>>> user was expecting the same instance. But this is not how
        it works. The
        >>>> typical use case for ContainerInterface is to allow a
        consumer (like a
        >>>> router) to be "container agnostic". As a user, I can
        decide to plug any
        >>>> container to a given router. The router will then be able
        to fetch the
        >>>> correct controller (or the correct action if you do ADR)
        based on the
        >>>> controller name. In this scenario, the user is the one in
        charge of
        >>>> configuring the container (and putting the controller in
        the container). If
        >>>> the user decides that he wants to use a container that
        can act as a factory
        >>>> and that he wants to configure some services that are
        created on each
        >>>> request, it is his responsibility. Keep in mind that
        since we do not
        >>>> standardize how to put things in the container, it has to
        be the user
        >>>> responsibility to configure his container.
        >>>>
        >>>> So saying that a container MUST return always the same
        instance is
        >>>> premature. When we will start to standardize how to put
        things in a
        >>>> container, then we can define that configured instances
        MUST be the same.
        >>>> But we can do this in the next PSR (the one that defines
        how to put things
        >>>> into a container) and not in this one.
        >>>>
        >>>>
        >>>> Now, even if I'm really convinced that we should keep the
        "SHOULD" in
        >>>> place, I understand from the numerous comments that the
        way the spec is
        >>>> written is clumsy (or rather worrisome).
        >>>>
        >>>> Why did I propose this sentence in the first place? I was
        thinking that
        >>>> a "CompositeContainer" could try to cache in an array the
        list of services
        >>>> already fetched from its children. This is something you
        can do if you know
        >>>> the children containers will always return the same
        instance but that you
        >>>> cannot do if you don't know if the container will return
        the same or a new
        >>>> instance. So really, the "SHOULD" prevents us to write
        composite containers
        >>>> that "cache" the result from children. This is actually
        not a very big deal,
        >>>> as composite containers are very seldomly used. What I
        was trying to say
        >>>> here was: "Hey, don't cache the services returned by a
        container, it might
        >>>> return a new instance on the next call". I proposed this
        wording 2 years ago
        >>>> in container-interop. In hindsight, this is certainly too
        specific to be
        >>>> part of a PSR.
        >>>>
        >>>> I see 3 possible solutions to replace this:
        >>>>
        >>>> Solution 1:
        >>>> Let's completely drop the 2 sentences => PSR-11 does not
        address whether
        >>>> a container should return the same instance or not (this
        is something we can
        >>>> deal with when we define how to put things into a container)
        >>>>
        >>>> Solution 2:
        >>>> Let's only keep the first sentence: Two successive calls
        to get with the
        >>>> same identifier SHOULD return the same value.
        >>>>
        >>>> Solution 3:
        >>>> Let's rewrite the second sentence: Two successive calls
        to get with the
        >>>> same identifier SHOULD return the same value. A container
        MAY offer the
        >>>> possibility to return a new value for some identifiers if
        the user
        >>>> configured it accordingly.
        >>>> (The wording of solution 3 should be improved but you get
        the idea)
        >>>>
        >>>> @larry, @hari, @pedro: do you favor one of those
        solutions or do you
        >>>> still think we should use a "MUST" in the spec?
        >>>> @matthieu, @mathew: any preference regarding the
        solutions proposed
        >>>> above?
        >>>>
        >>>> We should also try to ping more container authors to get
        their advice on
        >>>> this issue. I'd be interested in knowing what Fabien
        Potencier or Taylor
        >>>> Otwell think about this.
        >>>>
        >>>> ++
        >>>> David
        >
        > --


--
You received this message because you are subscribed to the Google Groups "PHP 
Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/php-fig/94718104-a874-b33f-4716-a843a7cba324%40garfieldtech.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to