Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
The basic handler itself, this should offer access to HTTP information via the Symfony2 HttpFoundation library. This is high priority as soon as #1178246: Add Symfony2 HttpFoundation library to core goes in.
Comment | File | Size | Author |
---|---|---|---|
#36 | wscci-http-handler-review5.patch | 6.76 KB | ygerasimov |
#25 | wscci-http-handler-review4.patch | 6.39 KB | ygerasimov |
#23 | wscci-http-handler-review3.patch | 6.48 KB | ygerasimov |
#16 | wscci-http-handler-review2.patch | 6.36 KB | ygerasimov |
#11 | wscci-http-handler-review.patch | 4.21 KB | ygerasimov |
Comments
Comment #1
Crell CreditAttribution: Crell commentedWe now have the HttpFoundation library in WSCCI per #1290504: Merge in Symfony2 and PSR-0 autoloader, so this issue is now unblocked and critical.
Comment #2
Anonymous (not verified) CreditAttribution: Anonymous commentedsubscribe.
Comment #3
pounardsub
Comment #4
mr.moses CreditAttribution: mr.moses commented.
Comment #5
ygerasimov CreditAttribution: ygerasimov commentedAssigned to me according to todays IRC chat in #drupal-wscci
Comment #6
tobiassjosten CreditAttribution: tobiassjosten commentedSubscribing. I will help review this.
Comment #7
Crell CreditAttribution: Crell commentedAny progress here? I'd love to be able to review code, even if it's preliminary...
Comment #8
ygerasimov CreditAttribution: ygerasimov commentedPlease find attached patch with current code state.
What I am working on at the moment is writing tests to check how Symfony handles HTTP request. Specially what is interesting is how Symfony does parsing of the body.
Comment #9
ygerasimov CreditAttribution: ygerasimov commentedPlease disregard my patch #8.
I have recreated branch 1279942-http-handler and store all code there.
Attached patch has implemented HandlerHTTP plus very basic test.
Comment #10
Crell CreditAttribution: Crell commentedThe HandlerAbstract class should be imported with a use statement, and then referenced by its short name.
Also, minor quibble. Symfony uses Http, not HTTP, so we probably should as well for consistency.
Properties of an object should be in lowerCamel case. That is, $symfonyRequest. (Although just $request is probably fine here.)
See above. Also, this should be static protected, not just static, or else it becomes public. "properties" is also misspelled.
This is already done by the parent constructor, which should be called first. parent::__construct($context, $params).
Also, the context reference is going to move to the getValue() method shortly. Just an FYI.
This is guaranteed to be empty when the object is constructed, so the empty() check is unnecessary.
$property is misspelled.
Also, I'm not sure that the constructor is the appropriate place to pull this data out. Much of that information will likely never be requested, so there's no need to extract it from the request object at all. This switch statement (or whatever it turns into) should be in getValue().
This should actually just use $args[0]. If $args has extra elements at the end, because the caller did something silly, it will break as there will be no such key as "request_args:blargh", will there?
See #1262020: improve getting of butler params for further discussion of that problem.
We probably want some way to allow the handler to be populated with a self-crafted request object. HttpFoundation\Request supports that (there's another factory method for it), and we will want that for testing.
Since we cannot pass new constructor arguments directly to a constructor object at registration, perhaps we can make use of a flag in the $params array and pass in either alternate arrays to use to populate the request object, or a pre-created request object. The latter won't work in the root context object, though, since we want that to load straight from disk.
Hm. Pounard is probably going to laugh at me for this, but perhaps testing is a place where registering a pre-instantiated object is appropriate. :-) Not sure there, but perhaps.
Thanks, Yuriy!
Comment #11
ygerasimov CreditAttribution: ygerasimov commentedLarry thank you for the review! So many misspelling :) Looks like I love word property to be spelled differently :)
Rerolled patch with corrections.
Regarding mocking up. When we register handler we can also send argument $params with our mocked data. In this way we can mock the context.
There is method Request::create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null) to create Symfony Request from passed data. But do we really need to mock Request if we can mock context?
Comment #12
pounardNo comment.
Personally I'd get rid of the foreach() and switch() statements and either:1. Do all the assignments directly in constructor, OR:
2. Proceed to lazy assignments on demand in the getValue() method.
I'd go for 2 only if those assignments really are costy in term of performances, but I'm not sure they are.
EDIT: Oh you did 2. in the latest patch, nice!
By the way, doing all these array copies seems like wasting memory (but as we won't modify them that's not true thanks to PHP copy on write), but the correct algorithmic way would be to leave them in request object and interogate the request object in getValue() ?
Comment #13
Crell CreditAttribution: Crell commentedThis can also be simplified with a use statement.
This can also be simplified with a use statement.
This would return an array, would it not? I believe in most cases we would want back an individual elements based on $arg[1]. This is where I think it should be pushed to a separate internal method rather than a switch statement.
Should this perhaps also require an arg[1], and return info on that particular file? (Specified by, uh, not sure. Name of the field?)
Same here. I'd rather see $c['http:header:bah'] to get back the "bah" header, for instance.
This is the part where I think we do need to be able to alter the request object. If this unit test is run from drush, will it pass? I don't know if drush is simulating an http request, but if it's not then this test is still dependent on the global test environment.
Comment #14
pounardThe request object might be injectable into the handler? Or the handler should be replaceable?
Comment #15
Crell CreditAttribution: Crell commentedThat's where I was thinking we could pass in a fake GET array as a param to the handler, and it would use that to call Request::create() rather than Request::createfromGlobals(). If you pass in no params, it just calls createFromGlobals() (which would be the case pretty much everywhere except in unit tests).
Comment #16
ygerasimov CreditAttribution: ygerasimov commentedThank you for the review.
Here is updated patch.
Shall we allow constructions like $c['http:query_args:foo:bar'] to get property 'bar' from argument 'foo'? Attached patch does allow this.
Comment #17
Crell CreditAttribution: Crell commentedWe have no formal standard here yet, but I think my preference would be one use per line, and if you're not renaming the class the "as Foo" should be omitted.
I should probably open a coding standards ticket for this, but I'm hesitant to do so before the Symfony2 patch is in.
For consistency I think this should be HttpHandler. I don't know that we have a formal standard yet, though.
The extract() makes me scared, as it's a dangerous function with potentially oddball side-effects. What if instead of using all $params as HTTP overrides, we do something like $params['overrides'], as an array? If there's anything in it, we then just += an array of the default values and then use that. (The default values listed here look fine.)
We could probably use some documentation here as to why we're doing this, too. I can help with that if you'd like.
Property is missing an O. :-)
I know Symfony uses the term "request" to refer to the POST variables, but I don't think that's particularly intuitive or self-documenting. We should come up with something else here to refer to "stuff that you used to get from $_POST".
Completely personal preference: query instead of query_args? We're going to be typing this a lot, and I'd rather not have the _ in there. (Consensus can overrule me, though.)
Is an array of all headers useful, or would we want a specific header by name? http:header:someheadername?
What about http:headers is an array, and http:header:name is just the value of that header (or empty string if it's not set)?
The same would be true for cookie, file, and server, I think.
Hm. I'm not sure I like this part. It seems like it would make the return value inconsistent. I don't know that we'd want to allow indefinite nesting into the keys of an array parameter.
In fact, I'm inclined to say that we don't, since the array itself is the value of the context; that's the atomic value, so that's the level at which we should "cut" and return something.
Also, I'd like to avoid pulling random Drupal utility functions into handlers, especially low-level ones, as that's a higher-level Drupal-specific dependency. The fewer of those we have, the better. (Remember that function is only available after boostrap or common.inc are loaded, and we want to dismantle those.)
I think the main question is how "raw" the data we get back should be. I'd like to offer at least a little more than just wrapping calls to HttpFoundation. However, I could see an argument that something like http:header:foo should be its own separate handler, which just references back to http:headers. Thoughts?
Comment #18
Gábor Hojtsy@Crell asked me to look at the language aspect. Looked at getLanguages(), and that seems to do mangling of languages to replace hyphens with underscores and uppercases the second/third/etc part of the language codes. This is not good at all for Drupal. We use all lowercase and hyphens, see localize.drupal.org, etc. The hyphen thing is consistent with W3C language tags (see http://www.w3.org/International/articles/language-tags/), the lowercase thing is just how we do it historically. So if we are to just use this as-is, we'd need to do the back-transformation of strtolower(str_replace('_', '-', $langcode)) later, which does not sound too effective.
For Drupal I'd consider using this code to fill in a langauge array for us:
See #221712: locale_language_from_browser() doesn't parse language tags correctly, has a broken logic for where our codebase is heading in terms of language detection from browsers. (Edit: that is, we can plug in the data provided by Symphony there).
Comment #19
Crell CreditAttribution: Crell commentedHm, interesting. Is there a compelling reason we could not follow Symfony's example here? Or if Symfony is violating the W3 / ISO standard, should we push back on them about it?
Comment #20
Gábor Hojtsy@Crell: well, if you check the links posted, these are how the codes stack up:
W3C/IETF: en, en-GB, zh-Hant (examples from the page linked above at http://www.w3.org/International/articles/language-tags/)
Drupal: en, en-gb, zh-hant (check localize.drupal.org, standard.inc in D8)
Symfony: en, en_GB, zh_HANT (check the code inside getLanguages() which produce these at https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Htt...)
Note that Drupal is a lowercase of W3C/IETF, while Symfony uses a str_replace('-', '_', $value) and also uppercases everything but the first portion, which it forces lowercased. Currently all we do is we lowercase what we get from browsers (and lowercase user configured language codes, since they can be mixed case) for comparison. If we go with Symfony's getLanguages() we'll need to work against that with replacing the underscores back to hyphens too. Its not the end of the world, but it is evidently a useless roundtrip with the underscores.
Comment #21
pounardLinux systems uses "fr_FR.UTF-8" syntax [lowercase langcode][underscore][uppercase country][dot][uppercase encoding] in their system configuration files.
EDIT: I guess that Symfony uses this convention, widely use if not standard.
Comment #22
sunThat's a upstream issue to file then. Symfony shouldn't mangle language identifiers in any way, if it claims and wants to be a general purpose component.
Comment #23
ygerasimov CreditAttribution: ygerasimov commentedHere is patch taking into account review from #17
Still not changed names of 'request_args' and 'query_args' as we don't have agreed names yet.
Comment #24
pounardIf you are using the code only once, why did you create a function for the content type header array cleanup?
Personnally I'd go for the HttpHandler name, but this has to be discussed with everyone involve I guess.
Else it looks fine for a start.
Comment #25
ygerasimov CreditAttribution: ygerasimov commented@pounard. You are right about method arrayCleanup(). I suspected that something similar can popup in other arrays we get back from Symfony but that didn't happened. At least didn't happened yet.
There are several "naming" issues raised that should be discussed:
1. HttpHandler or HandlerHttp
2. Better key names for GET and POST variables (query_args and request_args at the moment).
Comment #26
lsmith77 CreditAttribution: lsmith77 commented@gabor: hey .. if we are clearly in violation of a standard we will surely fix things. i guess the purpose of the code was to ensure that things are normalized, but we might have taken the wrong approach there. definitely get in contact with the entire Symfony2 dev team via a ticket or better yet a PR (with tests) on the github repo.
Comment #27
sun@lsmith77: There is no single standard that can be applied here, so it would be wrong to say that Symfony violates the standard. Rather, there are multiple ways to classify and identify languages and locales (as [partially] detailed in #20). Which identifier syntax is chosen is an application-level decision. Drupal's decision varies from Symfony's - which is completely legit and not unusual. However, it looks like Symfony's code forces the application to follow its decision by only understanding and/or automatically converting language identifiers in the returned result. And that is a problem, in terms of a common, general-purpose library.
Comment #28
pounard@sun This is a standard only for XHTML/XML files, not for an overall usage. I don't see why Symfony actually violate the standard as long as they use it only for internals. Plus their naming convention matches a lot of other systems' convention, such as most UNIX, iOS, IBM stuff, etc.
Some interesting links
http://stackoverflow.com/questions/836983/is-there-a-naming-convention-f...
http://developer.apple.com/library/ios/#DOCUMENTATION/CoreFoundation/Con...
http://publib.boulder.ibm.com/infocenter/zos/v1r12/index.jsp?topic=%2Fco...
http://publib.boulder.ibm.com/infocenter/aix/v6r1/index.jsp?topic=%2Fcom...
http://en.wikipedia.org/wiki/Locale
Comment #29
Crell CreditAttribution: Crell commentedI suggest we carry forward in this thread without any language case folding in the HTTP context handler itself. Just return what Symfony gives us for now. We can open a separate discussion, probably with the Symfony project directly on GitHub, about what HttpFoundation should be returning and why.
(If we need to do any re-mapping of language strings, we can do that in a separate langauge:whatever handler.)
Any other reviews of #25?
Comment #30
pounard#25 seems fine except it voluntarely ignores anything deeper than 2 in the $args array, did I misread or does it really sounds like an odd behavior?
Comment #31
ygerasimov CreditAttribution: ygerasimov commented@pounard that is intended behavior. I can't think about usecase for more than 2 levels. We have arrays for arguments, files, cookies, headers. I would suggest to keep it as it is for now and then if we will need, we can add another level(s).
Comment #32
lsmith77 CreditAttribution: lsmith77 commentedbasically getLanguages() tries return a normalized array to make life easier for developers. if you want the more "raw" form .. simply do not use that method and instead use $request->splitHttpAcceptHeader($this->headers->get('Accept-Language'))
just FYI .. splitAcceptHeaders() currently has a bug with handling of parameters that I fixed here:
https://github.com/symfony/symfony/pull/2365
Comment #33
Crell CreditAttribution: Crell commentedI think this should be a sub-parameter of $params, in case we ever have need to add more things that are not overrides. So $params['values'] or something like that. Also, if we use isset() instead of !empty() I think that should allow through an empty array, which allows us to create an "empty" request object by passing in an empty array there. (I don't know when we'd want to do that, but I could see a use for testing.)
This maps to what was in $_POST, right? I'm a bit concerned that people will confuse request_args with $_REQUEST, instead, which is different in important ways. Is there some other name here that would make sense? (Lukas, your input here on DX would be helpful since you've worked with HttpFoundation more.)
My knee-jerk feeling here is that "query" is better than "query_args", since people will be typing it a lot.
It took me a while to understand the value here. This is actually really clever, but not obvious why it's so clever.
This allows a uniform way of accessing an individual query arg, individual header, individual file, etc., all with the same code. That's quite slick, and I didn't realize until now why you were pushing for this array checking stuff. Now that I get it (from looking at the unit test), I really really like it. But that means that it needs a better documentation paragraph to explain why we're doing it, so that other people don't get confused.
As for language, let's do what Lukas suggests in #32 for now until Symfony and Drupal can standardize the string format. If/when we change it later, it won't be an API break (aside from the string changing, but it won't break the context API at all).
Comment #34
Gábor Hojtsy@lsmith77: re #32, yes, I was saying this above as well (that we should just get header values via Symfony then without the normalization), if Symfony will not change the standards it normalizes language codes to. We are trying to stick closer to W3C / HTML5 as possible.
Comment #35
podarokClasLoader already in core
#1178246: Add Symfony2 HttpFoundation library to core
Comment #36
ygerasimov CreditAttribution: ygerasimov commentedHere is updated patch.
I will definitively need help on comments
Regarding key names of 'request_args' and 'query_args', can we simply change them to 'post' and 'get'. I believe it will be more intuitive.
Lets discuss it during our today's irc meeting.
Comment #37
Gábor HojtsyI think this looks good in terms of the http:languages value now, it fits with the suggestion from @lsmith77 above.
Comment #38
Gábor HojtsyBTW submitted https://github.com/symfony/symfony/issues/2468 to help Symfony people be aware of our issues with their understanding of language codes.
Comment #39
Crell CreditAttribution: Crell commented"not necessary" should be "unnecessary".
"nexting" should be "nesting".
I'm open to suggestions on query_args/get/request_args/post/etc.
Yuriy, if it's OK with you once that's sorted out I'll just go ahead and add OCD documentation myself, then commit this. :-)
Comment #40
Crell CreditAttribution: Crell commentedPer today's IRC meeting:
query_args should become "query".
request_args and request_body will remain as is for now.
I'll work on beefing up the docs shortly. frankcarey volunteered to review them once I'd done so. :-)
Comment #41
Crell CreditAttribution: Crell commentedI have cleaned up the documentation on this branch and wired it up in system.module. (yeah, yeah, I know, system.module.) Also, I discovered that one of the tests would not and never would work when tests are run from the command line, so I just set that one to get skipped from the CLI.
Thanks, ygerasimov!