Problem/Motivation
Currently drupal_get_query_array does't support a syntax like that 'http://domain.org?flag' because it sets the value
of flag to "". If drupal_http_build_query is called on this it is converted to 'http://domain.org?flag=' which is wrong.
Proposed resolution
Convert empty string values from keys not containing an equal sign to NULL, so the URL is kept as expected.
Remaining tasks
Convert #64 patch to a MR.
Review
Original report by [robit8deb]
// Text of original report here.
(for legacy issues whose initial post was not the issue summary)
I have a scenario where a customer is using an external website in correlation with his drupal site. The external vendor is using poorly formatted urls that are getting (correctly) rewritten with and = sign at the end because it is expecting a value. For example, http:///foo.cgi?foo gets rewritten to http:///foo.cgi?foo=. I need to turn off this validation because the resulting url with the = sign at the end takes the user to page cannot be displayed.
I fixed this behavior on link cck fields using this patch:
http://drupal.org/node/1306352
However, the field that i am rewriting now is a Boolean field where, when on, gets rewritten to a link.
Please advise.
| Comment | File | Size | Author |
|---|
Issue fork drupal-1464244
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #1
dawehnerThe problem is that drupal_get_query_array('foo') doesn't return array('foo' => NULL) but array('foo' => '');
Regarding the issue in views: this first has to be fixed 8.x and then get's backported to 7.x and then should work in views.
Comment #2
dawehnerHere is a test, lets see whether it fails.
Comment #4
dawehnerSo here is a path with an actual fix.
Comment #5
robit8deb commentedExcuse my ignorance, but what directory should i be in to run this patch?
Thanks!
Comment #6
bleen commented#4 looks good:
RTBC
@robit8deb - for core patches always patch from your webroot. For contrib module patches always patch from the top-level of the module folder.
Comment #7
xjmComment #8
dries commentedThis needs a quick re-roll because the tests moved from
./core/modules/simpletest/tests/common.testto./core/modules/system/tests/common.test. Otherwise this patch looks good to me.Comment #9
tim.plunkettBecause it was just a file switch and no other conflicts, setting back to RTBC.
Comment #10
catchHmm the test added is a proper unit test, but CommonUrlUnitTestCase extends DrupalWebTestCase (which is right in the sense the existing tests aren't proper unit tests, but the name is very wrong). Could we put this into a new class that extends DrupalUnitTestCase? A bit scope creepy to rename CommonUrlUnitTestCase here but just removing the 'Unit' would probably do fine, follow works for me too though.
Comment #11
xjmTagging novice for the test cleanup catch describes in #10.
Comment #12
tim.plunketts/DrupalUnitTestCase/UnitTestBase, but yeah.
Comment #13
ryan.ryan commentedComment #14
catchThanks! Committed/pushed to 8.x.
Comment #15
sunDid anyone care to verify whether this change is actually correct?
The equal sign is optional for query parameters.
For a query string
?foo, PHP returns an empty string in$_GET['foo'].Normally, all query parameter values are expected to be strings.
Comment #16
bleen commented@sun I'm confused. What the use-case is for ?foo= with nothing after the equal sign. This is equivalent to ?foo is it not? In both cases $_GET['foo'] will have a value of empty string.
Comment #17
sunThat's correct. But this patch changed our own user space parser to not comply with that:
PHP's value of $_GET['foo'] is an empty string in both cases.
Comment #18
catchOK reverted in 8.x for now so we can discuss some more.
Comment #19
Niklas Fiekas commentedI merged this into #1598564: Convert common.test to PSR-0 (before I noticed it had been reverted). Here's the class converted to PSR-0. In case this is to be committed again, use the PSR-0 variant straight away (for the added class)?
Comment #19.0
Niklas Fiekas commentedAdd issue summary
Comment #20
lokapujyaComment #21
dimaro commented12: drupal-1464244-12.patch queued for re-testing.
Comment #30
nikunjkotechaI think this is good to be closed now (outdated), function in reference (drupal_get_query_array) is removed - check https://www.drupal.org/node/1597784
Comment #33
rwam commentedHm, but why I have this problem with current version 8.5.4 again? Or 8.5.x? I don't know how long this error exists because it was only reported today. I render a link field with external urls with
{{ content.field_external_url.0.['#url'] }}This worked in the past, but now I get a trailing equal sign for a link of type http://domain.org/?flag. And this leads to an 404 on the external page.
I had to change to
{{ content.field_external_url.0.['#url'].uri }}This works for me because I'm using external links only. But I don't know how if it works for internal links too.
Comment #34
rwam commentedHi @all,
please have a look at this. There are more issues reporting by users now with the same wrong behavior. And I cannot fix it because of using default link formatter.
You can reproduce it with
http://www.domain.org/?flaghttp://www.domain.org/?flag=Argh – how can I fix this?
Ciao
Ralf
Comment #35
rwam commentedComment #36
rwam commentedI can fix the problem mentioned above by applying attached patch. But I think it could be a problem in future versions too. The @todo at UrlHelper::buildQuery() prefers the use of php build in http_build_query() but if I use this the same issue exists again.
The patch are usable for the 8.6.x and 8.7x branches too. What do you think?
Comment #38
madsciencepro commentedI'm running into the same issue with 8.5.5. It looks like the code from #36 is in Core now, but I'm still getting the added "=".
Comment #39
mpdonadioWe should have a test to demonstrate the problem and to prevent regressions in the future.
Comment #41
herzbube commentedI strongly believe that the patch in #36 is not correct!
First of all, it must be possible to distinguish between 1) valueless query parameters (
?foo), and 2) query parameters whose value is an empty string (?foo=). The original code, which usedisset(), made that distinction possible, but the patched code, which usesempty(), makes the distinction impossible. The patch therefore is no solution at all, it simply shifts the problem. Before the patch, all occurrences of 1) were wrong, but 2) were correct. After the patch, all occurrences of 1) are correct, but 2) are now incorrect. Example: After the patch,http://www.example.com/cgi-bin/foo.cgi?bar=will be processed intohttp://www.example.com/cgi-bin/foo.cgi?bar, i.e. the "=" character is omitted.Second, and this is even worse,
empty()treats all kinds of values as "empty". According to the PHP docs (does anybody read the docs?) a variable is considered empty "[...] if its value equals FALSE." This means that the patch causes some query parameter values, which happen to be equal to PHP's definition of FALSE, to be omitted. Example:http://www.example.com/cgi-bin/foo.cgi?bar=0would be processed intohttp://www.example.com/cgi-bin/foo.cgi?bar=because the query parameter value 0 is, of course, equal to FALSE.The root of the problem, in my opinion, is not
UrlHelper::buildQuery(), it is the use of the PHP functionparse_str()inUrlHelper::parse(). I believeparse_str()is the wrong function to use, because it does not distinguish between valueless query parameters (?foo) and query parameters whose value is an empty string (?foo=) - it produces the same result for both. As long asparse_str()is used to break down a query string into its parts, it is therefore simply impossible to correctly reassemble the query string later on.Also check out the discussion in issue 2987114, maybe the issue helps to understand where
parse_str()was introduced.Comment #42
robindh commentedAfter looking for the root cause of this bug (before I stumbled onto this issue), I came to roughly the same conclusion as herzbube;
parse_str()loses too much information about the query string. There is no way to distinguish a valueless query string and an empty-valued query string after this function has been executed.Ideally
NULLshould be returned as value, when dealing with valueless query string parameters.As a workaround, I've introduced https://github.com/thephpleague/uri-query-parser in my project and I've replaced the
parse_strcode inUrlHelper::parse()with something like this:Comment #45
ahebrank commentedFor my purposes (where the extra `=` causes a 404!), replacing parse_str with GuzzleHttp\Psr7\parse_query corrects this problem.
Adding a related issue since parse_str is messed up in other ways.
Comment #47
drasgardian commentedAttached patch adds test coverage for this issue.
But there seems to be other areas where the test coverage is checking for the opposite behaviour, as indicated in the failed tests on #45.
e.g. in UrlRewritingTest.php there is a test for this:
Which originates here: https://www.drupal.org/project/drupal/issues/2276203#comment-9289445
Why would that code be expecting the = to be added?
Comment #48
drasgardian commentedUpdated patch which is a lighter approach to a fix, based on #45.
Only using GuzzleHttp\Psr7\parse_query for external urls as that is where the added equals sign can cause 404s. PHP should treat ?foo and ?foo= the same, so not changing the processing of the internal urls, which avoids the problems with the failing tests in #45.
Comment #49
drasgardian commentedComment #50
porchlight commented#48 worked for me.
Comment #51
CalamityJen#48 worked for me as well -thank you!
Comment #52
drasgardian commented@porchlight, @calamityjen perhaps you could mark as RTBC to help this progress towards being committed?
https://www.drupal.org/docs/develop/issues/fields-and-other-parts-of-an-...
Comment #53
porchlight commentedComment #55
robpowellLet's see if these pass with a rerun of the tests.
Comment #56
spokjeSeems to have tests, so removed needs test tag
Comment #57
drasgardian commentedTests passed on a re-run so setting back to RTBC.
Comment #58
quietone commentedI notice that the proposed resolution in the IS does not match what is in the patch. Can that be updated?
Comment #59
catchComment #60
dawehnerTwo thoughts/questions:
core/lib/Drupal/Component/Utility/composer.json:17Comment #61
alexpottWe definitely need to add the dependency as per #60
Also the docs to the Guzzle function say:
I wonder if this will have any unintended impacts. Making a change like this is really hard because of all of the unknowns.
Comment #62
alexpottMore on #61...
That's quite a big difference in output for the same input. Does this matter... not sure.
For me we should do the solution in #3038774-31: Url only outputs the last value of a query parameter and not parse and alter the external uri at all. That would mean we could also fix #2984272: Dots in query parameter names converted to underscores too.
Comment #63
mennega commentedComment #64
nikitagupta commentedComment #66
quietone commentedThe patch in #64 still applies to Drupal 9.3.x, Changing version.
Needs work for #60 and #61 and still needs an Issue Summary Update.
Comment #71
dysproseum commentedPatch #64 updated to apply on Drupal 10.2.4.
Comment #72
kimlop commentedI tested the last two patches (#71 #64) with core 10.3.5 and both don't work.
Comment #73
duaelfrPatch #64 applies on Drupal 10.3.x and fixes the issue.
I updated the IS and I believe that comments from #60 and #61 don't apply anymore with the approach followed in the patch.
For novices: please convert #64 patch to a MR to ease maintainers job.
Comment #74
smustgrave commentedFixes should be in MRs
Have not reviewed yet.
Comment #75
sayan_k_dutta commentedWorking on it.
Comment #77
sayan_k_dutta commentedCreated MR for patch #64. Please review it.
Comment #78
duaelfrComment #79
rsbarbano commentedPatch #64 is working for me in a Drupal version 11.0.4.
Thank you!.
Comment #80
smustgrave commentedHave re-ran tests twice and they still fail.
Comment #81
sayan_k_dutta commentedTests are now passing, moving to Needs Review.
Comment #82
igorgoncalves commentedHi Sayan, thanks for the effort.
Despite the fact that tests have "Pass" status, there's one warning left, and checking the detail says:
which sounds like pretty much what we are testing here, isnt?
Comment #83
sayan_k_dutta commented@igorgoncalves yes, indeed the two strings of urls are different, that is what we need here. Do I need to make changes to test files, to remove the warnings. I don't have much knowledge of phpunit tests, so if someone can look into it, it would be better.
Comment #84
sayan_k_dutta commentedAll tests are now passing, warnings are resolved. Please review.
Comment #85
koustav_mondal commentedChecked in my local setup before and after changes. Attaching screenshots. LGTM+
Comment #86
alexpottThe fix has issues - added a comment to MR.
Comment #88
joegraduateUpdated the MR with @alexpott's recommended changes and also replaced
strpos()withpreg_match()using a regex that should prevent the false positives due to substring overlap that @alexpott noted by ensuring that the matches are either at the beginning of the query string or after a&.Comment #89
smustgrave commentedRebased as this MR was 800 commits back
Feedback appears to be addressed on this one.
Comment #90
xjmNR for a small commment improvement suggestion I made on the MR. It's okay to re-RTBC the issue if the suggestion is accepted (no need for an extra review cycle over something small).
Comment #92
smustgrave commentedFeedback from xym appears to be addressed, saving credit for xjm review in the MR.
Comment #93
oily commentedLine 227 of UrlHelper.php, removed comment that could confuse the meaning of the comment addition in this MR. Cannot see any usefulness in removed comment at all except to confuse people.
Comment #94
oily commentedComment #95
joegraduateRestored comment removed from else block in UrlHelper::parse(). Not sure why that was removed (was removed in one of the earlier patches that preceded the MR).
Comment #96
joegraduateReverted my most recent commit to restore removed comment (see #95). Failed to realize that @oily was describing a change that had already been made and not requesting a change. Apologies for the confusion.
I agree that the comment that has been removed was confusing. @oily's change seems like a good one to me.
Comment #97
oily commentedComment #98
xjmThanks for the update!
Despite my earlier comment: This is pretty low-level, and the approach has changed several times over the issue's 13-year (!) history. So, I'd like subystem and/or framework signoff for the fix. Leaving RTBC for that.
Comment #99
catchI discussed this a bit with @longwave in slack and I have minor concerns about whether there's a way to do this without a regexp and/or whether the behaviour is definitely a bug or not, but these are minor and not anything to hold it up - I was not able to come up with a better suggestion and @longwave convinced me we should actually fix it. So removing the FM review tag.
Comment #100
alexpottI think this needs pointing out...
So we are back at the solution that was rejected all the way back in #15.
FWIW I think #15 was wrong because URLs are not only processed by PHP based webservers and also if you use something like https://uri.thephpleague.com/ it was behave similarly.
I still think we should do something like #3038774-31: Url only outputs the last value of a query parameter and not futz at all with external URLs unless we have to and that would solve the majority of issues with using PHP to process URLs supported by systems not running PHP.
Comment #101
alexpottWe definitely need a CR to cover this change of behaviour as outlined in #100.
I've opened #3554522: Only parse and mess with the URI in \Drupal\Core\Utility\UnroutedUrlAssembler::buildExternalUrl() if we have to to do #3038774-31: Url only outputs the last value of a query parameter which I believe would actually solve most people's issue here and on the other issue.
Comment #102
alexpottI would encourage people to review #3554522: Only parse and mess with the URI in \Drupal\Core\Utility\UnroutedUrlAssembler::buildExternalUrl() if we have to as I expected that that minimal change solves quite a lot of the reasons why people end up on this issue.
Comment #103
catchMoving this to postponed, then we can refocus this issue on anything that's left.