HTTP Factory Proposal

417 views
Skip to first unread message

Woody Gilk

unread,
May 31, 2016, 8:22:13 AM5/31/16
to PHP Framework Interoperability Group
Since this topic has gotten lost in the discussion regarding HTTP Middleware, I am opening a new thread to summarize where the current state of the HTTP Factory [1] proposal.

[1]: https://github.com/php-fig/fig-standards/pull/759

There have been a number of discussions as to how best organize the interfaces and what arguments each factory method should have. As per the proposed meta document, there are separate methods for each type of PSR-7 object, each in a separate interface. This currently looks like:

UploadedFileFactory: createUploadedFile
UriFactory: createUri
RequestFactory: createRequest
ResponseFactory: createResponse
ServerRequestFactory: createServerRequest
StreamFactory: createStream


Originally the request and response interfaces were totally independent. The point was made that users of the interfaces would have to inject as many as 4 separate interfaces to create a complete ServerRequest and that it would be better if the request/response interfaces extended the interfaces for creating their child objects. This made sense to me, so the current dependency tree for request and response interfaces is:

ResponseFactory
^ StreamFactory

RequestFactory
^ UriFactory
^ StreamFactory

ServerRequestFactory
^ UploadedFileFactory
^ UriFactory
^ StreamFactory


Implementation of such an interface will probably best be served by traits or injection with proxying. The current proposal does not include language that makes either suggestion.

I believe the current proposal defines clear separation of concerns and allows for partial implementations such as league/uri to provide a UriFactory without being dependent on any other implementation details for PSR-7, while more complete implementation such as guzzlehttp/guzzle to provide a complete set of factories.

Last, there has been a fair amount of discussion regarding arguments for the different methods. One existing HTTP factory package [2] defines a complete set of arguments for each type of object.

[2]: https://github.com/php-http/message-factory

I deemed this to be more work for anyone implementing the interfaces, and also completely unnecessary, since most HTTP message objects act as builders. To wit, this:

$request = $requestFactory->createRequest()
    ->withMethod('POST')
    ->withHeader('Content-Type', 'application/json');


Is easier to work with than:

$request = $requestFactory->createRequest(
    'POST',
    '',
    [
        'Content-Type' => 'application/json',
    ]
);


The former gives clear indication of what is being set, whereas the latter requires you to go inspect the method arguments to know the order, and also requires providing defaults when you need to skip parameters. Thus, having as few arguments as possible is ideal.

The only time that arguments must be defined is when creating UploadedFile and Stream, as the underlying streams cannot be replaced once set. In the case of UploadedFile, once the object is created, there are no methods to modify it. To address this, both factory methods have arguments to ensure that a complete object is created.

The only other argument that is defined is for createUri(), since every implementation of the URI object I could find included a method that took a string and created a complete URI object. This is purely for convenience, but I feel that:

$uri = $uriFactory->createUri('http://example.com/home.html');

Is much more usable than:

$uri = $uriFactory->createUri()
    ->withScheme('http')
    ->withHost('example.com')
    ->withPath('/home.html');


Hopefully that is an accurate summary of the discussions that have already been had on the PR in question. If anyone would like to correct my record, please do. 

I believe an entrance vote will be opened shortly and additional refinement of the proposal can begin.

Regards,
--
Woody Gilk
http://about.me/shadowhand

Tobion

unread,
Jun 6, 2016, 1:10:32 PM6/6/16
to PHP Framework Interoperability Group
About factories without argument:
IMO this is a really bad thing because it's not clear anymore what the default values for all request/response properties is.

So if you just use `$request = $requestFactory->createRequest()` is it not clear what the request method and request URI etc actually is at this point.
Is it a GET or a POST request and to which URI? Also all HTTP message objects are value objects which is quite the opposite of a builder that you claimed how they are used.

So IMO factories definitely need arguments. The question is only which are necessary (and the order), which are not needed and can be set later and which have default values.
To me guzzle/psr7 has chosen the best answers to those questions.

Woody Gilk

unread,
Jun 6, 2016, 1:34:32 PM6/6/16
to PHP Framework Interoperability Group
On Mon, Jun 6, 2016 at 12:10 PM, Tobion <tob...@gmail.com> wrote:
>
> IMO this is a really bad thing because it's not clear anymore what the default values for all request/response properties is.


The default values are empty, why would they be anything else?

> So IMO factories definitely need arguments. The question is only which are necessary (and the order), which are not needed and can be set later and which have default values.

And this is exactly why they shouldn't have arguments. What if I want
to create POST request, but defer the URI setting until later? Now I
have to figure out what the defaults are and pass those until I get
the $method argument. From a developer standpoint, having an empty
object created is better than any other option.

Tobion

unread,
Jun 6, 2016, 3:14:07 PM6/6/16
to PHP Framework Interoperability Group
> The default values are empty, why would they be anything else? 

This makes no sense. For example the request method cannot be empty. See RFC 7230.

method = token
token  = 1*tchar

So you would be creating an invalid request instance which many PSR-7 implementations would probably not even allow.
Again HTTP messages are value object which are supposed to be valid at creation. What you are trying to do is
to create a factory for a builder of HTTP messages which is something completely different.

Rasmus Schultz

unread,
Jun 8, 2016, 3:27:37 PM6/8/16
to PHP Framework Interoperability Group
Yeah, this is why I'm here as well.

I think we can't have factories that create invalid PSR-7 objects - factories must create objects with valid state.

For every property of every object, that means one of two things:

1. the property must be optional (can initialize as an empty array or null), or
2. the property must initialize to a meaningful default, which must be specified in the PSR

If a property is non-optional, and cannot be initialized with a defined default value, it has to be part of the create-method signature.

Defaults must be specified in the PSR, otherwise factory implementations will differ on this point, which means factory implementations will be incompatible.

I think that means, minimally:

- request (and server-request) must be created with a URI, method and body
- response must be created with a status and body

In the argument lists, default for body arguments should probably be an empty string, "GET" for method, 200 for status - by specifying defaults in these method signatures, we're making these values non-optional, and at the same time enforcing common defaults for all implementations; for properties that can't default to an empty array or some other meaningful empty default, this simplifies enforcing common defaults for implementations.

Footnote: this thread wasn't linked to in the "relevant links" section of the PSR and was hard to find.

Rasmus Schultz

unread,
Jun 8, 2016, 3:46:46 PM6/8/16
to PHP Framework Interoperability Group
Note that the PSR-7 interfaces do *imply* certain things about the model that aren't necessarily specified.

For example, getProtocolVersion() is annotated with @return string, rather than e.g. @return string|null, which implies that null is not an expected internal state of this property.

Similarly, getBody() is annotated with @return StreamInterface, rather than @return StreamInterface|null, which implies that null is not a valid state - an empty stream may be a meaningful default in this case.

An so on...

Woody Gilk

unread,
Jun 10, 2016, 9:48:15 PM6/10/16
to PHP Framework Interoperability Group
On Wed, Jun 8, 2016 at 2:27 PM, Rasmus Schultz <ras...@mindplay.dk> wrote:
> I think that means, minimally:
>
> - request (and server-request) must be created with a URI, method and body
> - response must be created with a status and body

I have updated the proposal so that request/response methods require
the minimum viable parameters, you can see the changes here:

https://github.com/php-fig/fig-standards/pull/759/commits/16b5e12337272d006336962c62c6522282eae31f

Rasmus Schultz

unread,
Jun 11, 2016, 5:47:04 AM6/11/16
to PHP Framework Interoperability Group
I think that creation of server-request from values and from globals are two different concerns - I propose we use two different interfaces for those:


Now I'm wondering about streams... it seems that we only cover the most basic cases of stream and string resources - and we cover those with a single interface/method, which are essentially two different stream factories for two different implementations, likely with little in common. Is that clean?

And what about other stream types? I know we can't anticipate all possible stream types, but at least a callback-based stream seems a common use-case widely supported by most libraries with PSR-7 Stream implementations?

I think I'd suggest using three interfaces, e.g. with separate methods like createStreamFromString(), createStreamFromResource() and createStreamFromCallable() since these are different stream and factory implementations with different (rarely overlapping) use-cases.

Thoughts?

Rasmus Schultz

unread,
Jun 11, 2016, 6:06:07 AM6/11/16
to PHP Framework Interoperability Group
On another note, are we sure it's a good idea to aggregate related interfaces into the request/response factory interfaces? It effectively turns them into multi-method interfaces, e.g. factories with multiple responsibilities. And while I recognize the convenience of that, I'm also concerned about what this does to testability - having to mock four different factory-methods when only two of them are necessary.

Practical example: ResponseFactoryInterface extends StreamFactoryInterface, but it's not a given that I need to create a stream because I'm creating a response; it's common for a response to have no body (e.g. a default, empty stream) such as when creating a redirect. I think this clearly demonstrates mingling concerns?

It likely means that factory implementations will need to use traits, or aggregate and delegate to factory components, rather than implementing the individual factory components as stand-alone components.

Also, per what I said below about Streams - we can't really predict the kind of stream somebody wants to create, so, again, we're forcing factory implementations to support creation of multiple different unrelated types of Streams, which seems wrong, SRP and all?

In my opinion, keeping interfaces strictly separate leads to, yes, middleware constructors with a few more constructor arguments, but clearer separation of concerns - not just on the consumer side, but on the factory implementation side.

While it's likely you will need X when you need Y, I'm not sure it's wise to make those assumptions - it would seem to favor convenience over correctness, which in my experience always begets unnecessary complexity. Creation of one type of object is after all a single concern - requiring a consumer to explicitly state their required factory types is more verbose, but it's also simpler and more precise, and should lead to simpler factory implementations with cleaner separation of concerns.

Woody Gilk

unread,
Jun 13, 2016, 8:27:23 AM6/13/16
to PHP Framework Interoperability Group
On Sat, Jun 11, 2016 at 5:06 AM, Rasmus Schultz <ras...@mindplay.dk> wrote:
> In my opinion, keeping interfaces strictly separate leads to, yes,
> middleware constructors with a few more constructor arguments, but clearer
> separation of concerns - not just on the consumer side, but on the factory
> implementation side.

The argument was made that if you needed to construct a complete
request with a URI and a body, you would need to inject 3 separate
interfaces, which seems a bit burdensome for end users. I can really
see it both ways. Implementing the factories will require a bit of
complexity due to having to use traits, but we see this all the time
in PSR-7, where request/response share so much that they almost always
have a common MessageTrait.

From a user standpoint, is it really beneficial to require injecting 3
separate factories (or 4 when creating server request) or would it be
more effective to inject a single factory that is capable of
everything required? It could be argued that a method like
createServerRequestFromGlobals() implies that the interface is capable
of creating the entire request, and therefore the interface should be
able to create the same request without using globals.

All of that said, I think that separate interfaces is the most correct
approach but perhaps not the most user friendly. Both have to be
considered.

Woody Gilk

unread,
Jun 13, 2016, 8:38:49 AM6/13/16
to PHP Framework Interoperability Group
On Sat, Jun 11, 2016 at 4:47 AM, Rasmus Schultz <ras...@mindplay.dk> wrote:
> I think that creation of server-request from values and from globals are two
> different concerns - I propose we use two different interfaces for those:
>
> https://github.com/mindplay-dk/fig-standards/pull/1

They might be different concerns but the end result is the same: the
creation of a server request interface. Since both empty and globals
will probably share a significant amount of functionality, I would
much rather it be represented as two methods in a single interface. As
you stated in your PR, there is very little reason to create server
requests outside of the front controller. Regardless of whether the
user chooses to create from globals or manually, the location of usage
will be the same.

To me, splitting the interface is just making the implementation more
confusing and less user friendly.

Woody Gilk

unread,
Jun 13, 2016, 8:49:08 AM6/13/16
to PHP Framework Interoperability Group
Apologies for not combining these into a single email, I missed this before.

On Sat, Jun 11, 2016 at 4:47 AM, Rasmus Schultz <ras...@mindplay.dk> wrote:
> I think I'd suggest using three interfaces, e.g. with separate methods like
> createStreamFromString(), createStreamFromResource() and
> createStreamFromCallable() since these are different stream and factory
> implementations with different (rarely overlapping) use-cases.

Again, this level of splitting seems like a burden from a user
standpoint. I don't want to have to configure 3-4 separate interfaces
in my dependency injector just for creating streams. As it stands
right now, there are already 6 interfaces that I need to configure to
use HTTP factories. If we were to split stream factory by method, it
would be 8 or 9 interfaces.

I think the only right way to balance correctness of definition and
user expectations is to group the interfaces by return type, eg any
method that creates a ResponseInterface will be contained in the
ResponseFactoryInterface and anything that creates a StreamInterface
will be contained in the StreamFactoryInterface.

Woody Gilk

unread,
Jun 13, 2016, 9:06:31 AM6/13/16
to PHP Framework Interoperability Group
I've just pushed a couple of commits to the PR [1] that separate the
stream methods and remove the interface extension. Required arguments
have been added a couple of days ago.

I know that Dracony was very much in favor of the interface extension.
He may want to add his arguments here, as we go into Draft phase of
the PSR.

[1]: https://github.com/php-fig/fig-standards/pull/759

Larry Garfield

unread,
Jun 13, 2016, 10:42:20 AM6/13/16
to php...@googlegroups.com
This is the sort of area where union types would be useful. (Cf, recent
discussions on the Internals list.) If I have a class that needs to
generate, say, a Response and a Request and a Stream, that would allow:

class Me {
public function __construct(RequestFactory & ResponseFactory &
StreamFactory $factory) { }
}

Of course, we don't have that. Which means the next-best thing would be
to pass the same object to multiple parameters (which is weird, but
completely legal):

class Me {
public function __construct(RequestFactory $req, ResponseFactory
$res, StreamFactory $stream) { }
}

$factory = new CombinedFactory();
$m = new Me($factory, $factory, $factory);

Or we could collapse the interfaces together. The implicit use of
traits to build a full factory suite doesn't bother me at all, frankly.

This is a good case for trial implementations. Let's see someone try
and build a Diactoros factory suite (or some other PSR-7 lib, take your
pick) and see what happens, and what is fugly. That will give us much
better feedback on how we should break down the interfaces.

On an academic level, I agree with lots of micro-interfaces. In
practice, I am fairly confident that would be unusable. Let's let some
actual building drive our decision on what is most usable.

--Larry Garfield

Rasmus Schultz

unread,
Jun 13, 2016, 12:38:32 PM6/13/16
to php...@googlegroups.com
Again, this level of splitting seems like a burden from a user standpoint

You're arguing convenience over correctness.

IMO, if you're going to argue convenience, the logical course is to bundle all factory methods into a single interface - then you will never need to inject more than one factory instance.

That's probably easier for most users to understand anyhow.

But I would say, go one way, or go the other way - either segregate factory concerns, or don't.

Trying to hit a middle ground really doesn't make sense to me - if the argument is convenience, make it as convenient as possible; if the argument is correctness, make it as correct as possible.

Having it somewhat correct and somewhat convenient is confusing, and as demonstrated, you can't really anticipate which factory concerns belong together - there are too many variations.

I wouldn't be against a single interface, though personally I would favor correctness over convenience.


--
You received this message because you are subscribed to a topic in the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/php-fig/piRtB2Z-AZs/unsubscribe.
To unsubscribe from this group and all its topics, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/CAGOJM6K6g2jfT4aabZj4-qZ%3DtXgs4tR2J1_Zh0Ec%3DQkAStevjw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Matthew Weier O'Phinney

unread,
Jun 13, 2016, 3:48:12 PM6/13/16
to php...@googlegroups.com
On Mon, Jun 13, 2016 at 11:38 AM, Rasmus Schultz <ras...@mindplay.dk> wrote:
>> Again, this level of splitting seems like a burden from a user standpoint
>
> You're arguing convenience over correctness.
>
> IMO, if you're going to argue convenience, the logical course is to bundle
> all factory methods into a single interface - then you will never need to
> inject more than one factory instance.
>
> That's probably easier for most users to understand anyhow.
>
> But I would say, go one way, or go the other way - either segregate factory
> concerns, or don't.
>
> Trying to hit a middle ground really doesn't make sense to me - if the
> argument is convenience, make it as convenient as possible; if the argument
> is correctness, make it as correct as possible.

Why do you feel it must be an either/or situation?

Developer eXperience initiatives in a number of frameworks have shown
that you can often get reasonable amounts of both, but that "correct"
often leads to kludgy, difficult to use and understand code, while
going too far towards "convenience" often ends up in stuff like global
singletons. But there *is* often a middle ground that poses a minimal
learning curve while being correct from a SOLID view point as well. I
don't see why we wouldn't strive for this.

I tend to agree that injecting 3-5 factories in order to be able to
create a single viable instance that I'll actually consume feels like
overkill. It may be correct, but it also will be a hard-sell for
users, and hard to teach. I think union types would be awesome, but
since we're still targeting 5.6 and/or 7.0 at this time, I tend to
agree that using interface inheritance makes sense here, and nicely
accommodates both usability and SOLID.

> Having it somewhat correct and somewhat convenient is confusing,

You've asserted that it's confusing, but not *explained* in what way
it is confusing. Could you please elaborate?
> 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 php-fig+u...@googlegroups.com.
> To post to this group, send email to php...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/php-fig/CADqTB_hYxR2x91KjvvOmGnY8uqL06LX_AHSDapXQ5UE2STTvfg%40mail.gmail.com.
>
> For more options, visit https://groups.google.com/d/optout.



--
Matthew Weier O'Phinney
mweiero...@gmail.com
https://mwop.net/

Rasmus Schultz

unread,
Jun 13, 2016, 4:26:52 PM6/13/16
to php...@googlegroups.com
It may be correct, but it also will be a hard-sell for users, and hard to teach

Well, this is where I think you're wrong, and why I think we need to choose one approach or the other.

Consider these possible explanations for each of three scenarios, e.g. method-per-interface, single factory, and some-methods-per-interface, in order:

1. "to create an HTTP entity, inject the appropriate factory for that entity"

2. "to create any HTTP entity, inject the HTTP factory"

3. "to create an HTTP request or response entity, inject the appropriate factory - to create A and B you can also use these factories, however, to create C and D, also inject this or that factory"

The two first options and simple and easily explained - the third option is not.

We're not talking about global singletons here - there is no harm and nothing wrong with having one factory per entity; it's arguably the most correct in terms of abstraction, since being a factory for a distinct entity is arguably a single responsibility. On the other hand, there is also nothing wrong with having a single factory for an entire domain of related entities.

Having some factories in some interfaces, on the other hand, requires a good explanation and understanding of which interfaces provide what factories - the names of those interfaces, for one, would not indicate which capabilities they have; as a developer, you have to simply learn and memorize which interfaces have which capabilities.

I don't get why you would favor convenience for something like this, since, really, only a few middleware components will need these factories in the first place - the majority of middleware components modify responses, they don't create responses. Add to that the fact that you're probably not writing several middleware components per day, likely only a few in the lifetime of a project. If there are other components that need these factories, they're going to be even fewer.

Also, suppose I wanted a special implementation of a factory for one particular entity? Like a custom stream implementation. Now I need to extend some other class to inherit some other unrelated factory method implementations, or inject one via my factory's constructor and delegate to it at run-time. If they were properly separated, those kind of issues would not arise.

In my opinion, favoring convenience is, in this case, an impulse that will come back to punish you later.


Larry Garfield

unread,
Jun 13, 2016, 5:19:20 PM6/13/16
to php...@googlegroups.com
On 06/13/2016 03:26 PM, Rasmus Schultz wrote:
It may be correct, but it also will be a hard-sell for users, and hard to teach

Well, this is where I think you're wrong, and why I think we need to choose one approach or the other.

Consider these possible explanations for each of three scenarios, e.g. method-per-interface, single factory, and some-methods-per-interface, in order:

1. "to create an HTTP entity, inject the appropriate factory for that entity"

2. "to create any HTTP entity, inject the HTTP factory"

3. "to create an HTTP request or response entity, inject the appropriate factory - to create A and B you can also use these factories, however, to create C and D, also inject this or that factory"

The two first options and simple and easily explained - the third option is not.

We're not talking about global singletons here - there is no harm and nothing wrong with having one factory per entity; it's arguably the most correct in terms of abstraction, since being a factory for a distinct entity is arguably a single responsibility. On the other hand, there is also nothing wrong with having a single factory for an entire domain of related entities.

Having some factories in some interfaces, on the other hand, requires a good explanation and understanding of which interfaces provide what factories - the names of those interfaces, for one, would not indicate which capabilities they have; as a developer, you have to simply learn and memorize which interfaces have which capabilities.

I don't get why you would favor convenience for something like this, since, really, only a few middleware components will need these factories in the first place - the majority of middleware components modify responses, they don't create responses. Add to that the fact that you're probably not writing several middleware components per day, likely only a few in the lifetime of a project. If there are other components that need these factories, they're going to be even fewer.

Also, suppose I wanted a special implementation of a factory for one particular entity? Like a custom stream implementation. Now I need to extend some other class to inherit some other unrelated factory method implementations, or inject one via my factory's constructor and delegate to it at run-time. If they were properly separated, those kind of issues would not arise.

In my opinion, favoring convenience is, in this case, an impulse that will come back to punish you later.

I'm generally a proponent of Simple over Easy (c.f. https://www.infoq.com/presentations/Simple-Made-Easy, which is required viewing for anyone designing anything), but you're assuming that the "some methods" approach will be incomprehensible.  Grouping by return type, as Woody suggested, is pretty simple to explain.

It's also reasonable to say that, in many cases, one factory may compose another.  The Response factory, for instance, may want to compose a Stream factory.  That's entirely reasonable.

Also, it's not just middleware that would want a factory.  For instance, I'd absolutely take a factory over what I'm doing here:

https://github.com/Crell/ApiProblem/pull/13/files#diff-079a94b89dcd12f63d4af42053ec690aR41

And that's not a middleware, not would it be used in one.

As I said before, though, we should actually try implementing it in various ways and let that experience guide us.  Until we do, there's little value in debating theory vs practice until we have some practice to debate about. :-)

--Larry Garfield

Rasmus Schultz

unread,
Jun 13, 2016, 6:23:27 PM6/13/16
to php...@googlegroups.com
I'm generally a proponent of Simple over Easy

Rich Hickey is a great source of inspiration for me too :-)

you're assuming that the "some methods" approach will be incomprehensible

No, I'm demonstrating that it's *more* difficult to reason about than the other two options - the reason being, it's based on certain assumptions about use-cases. Assumptions you don't need to make if you would settle on something simpler.

Grouping by return type, as Woody suggested, is pretty simple to explain

Yet, still doesn't make sense in some cases, such as ServerRequestFactory - in what scenario would you need a custom server request as well as a server request based on globals? You would not. They are two different factories for different scenarios.

It's also reasonable to say that, in many cases, one factory may compose another

Yes, and it's also a good argument for separation of concerns, so you can more freely compose factories. When factories come as implementations of two or more factories in a single object, they don't compose as well - to reimplement part of it, you would need a work-around, such as delegating one method.

Look:

interface FooFactory { public function createFoo(); }
interface BarFactory { public function createBar(); }
interface LolFactory extends FooFactory, BarFactory {}

This scenario is problematic. You effectively end up with *two* abstractions for factories for *each* entity type, e.g. both FooFactory and LolFactory create Foo entities. You may be assuming the class implementations will compose exactly like the interfaces, but that's not a given - interfaces do not dictate a class hierarchy.

In practice, that means, if a consumer requires LolFactory, and I want to implement a custom factory for creation of a Foo or Bar entity, which forces me to implement a class with two methods, one of them unrelated to what I'm trying to do - it's a clear consequence of violating SRP... look - all this:

class MyLolFactory implements LolFactory
{
    private $bar_factory;

    public function __construct(BarFactory $other) {
        $this->bar_factory = $other;
    }

    public function createFoo() {
        return new Foo(...);
    }

    public function createBar() {
        return $this->bar_factory->createBar();
    }
}

As opposed to just this:

class MyFooFactory implements FooFactory {
    public function createFoo() {
        return new Foo(...);
    }
}

And on the consumer side:

class Something {
    public function __construct(LolFactory $lol) {
        // ... code you have to read to figure out what it's using LolFactory for ...
    }
    // ... more code you have to read ...
}

As opposed to:

class Something {
    public function __construct(FooFactory $foo_factory, BarFactory $bar_factory) {
        // ... code you DON'T need to read, as each factory has one well-defined use!
    }
}

I understand you want convenience, but this kind of convenience is extremely superficial - the convenience of typing out fewer type-hints comes at the cost of simplicity, and as you can clearly see from these examples, this is the worst kind of complexity: the kind that *spreads* to all your code.

Long-term, this will make things more complex and less transparent - if typing out your constructors and bootstrapping is a little more work, it's going to pay itself back in the form of much clearer separation, simpler composition, easier testing, mocking, etc...

Convenience isn't one of the SOLID principles, guys - but interface segregation is, e.g. "many client-specific interfaces are better than one general-purpose interface". Isn't that precisely what we're discussing here?

I really, really think that favoring convenience over tried-and-tested principles would be a mistake here.

And I think you're exaggerating the inconvenience of having to inject a few factories to begin with. It's not a big deal.

Why don't you try simple first, and see if that makes your head explode? I bet it won't, but if it does, and you feel you must, we can start talking about which principles we're willing to break to make the experience less painful. For the moment, I'm betting you will find that it's actually quite painless - most middleware components probably won't have more than 3 or at the most 4 dependencies...


--
You received this message because you are subscribed to a topic in the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/php-fig/piRtB2Z-AZs/unsubscribe.
To unsubscribe from this group and all its topics, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.

Larry Garfield

unread,
Jun 13, 2016, 6:35:32 PM6/13/16
to php...@googlegroups.com
1) Please don't top-post in a bottom-posting thread.

2) Dude, chill. :-)  Remember, I just spent 5 years teaching the Drupal community that classes and DI aren't scary and evil.  You're preaching to the choir here on separation of concerns and the dangers of short-term convenience.

3) That's not what I meant as far as composition.  Rather, I was thinking more of:

class RequestFactory implements RequestFactoryInterface {
  public function __construct(StreamFactory $sf) { ... }

  public function createResponse($code, $body) {
    return new Response($code, $this->sf->createStreamFromString($body));
  }
}

Which then obviates the need for the caller (middleware or otherwise) to know about the stream factory in the first place.  And would, actually, make it easier to use separated interfaces.

4) It sounds like you're agreeing with my key point though: Until we actually try to implement this stuff, nothing in this thread is meaningful. :-)  Let's do that first, then compare/contrast.

--Larry Garfield

Woody Gilk

unread,
Jun 15, 2016, 5:03:11 PM6/15/16
to PHP Framework Interoperability Group
On Mon, Jun 13, 2016 at 5:35 PM, Larry Garfield <la...@garfieldtech.com> wrote:
> 4) It sounds like you're agreeing with my key point though: Until we
> actually try to implement this stuff, nothing in this thread is meaningful.
> :-) Let's do that first, then compare/contrast.


I've created a repository that contains implementations of the current
interfaces at https://github.com/shadowhand/http-factory

And a separate repository that includes an implementation for
Diactoros at https://github.com/shadowhand/http-factory-diactoros
(this is not yet complete)
Reply all
Reply to author
Forward
0 new messages