Needs work
Project:
Drupal core
Version:
main
Component:
routing system
Priority:
Major
Category:
Task
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
30 Jan 2015 at 18:57 UTC
Updated:
30 Jan 2023 at 21:20 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #1
wim leersI like it!
Also: I read string-parsey-magic as string-parsley-magic :D
Comment #2
dawehnerThe question is, where would this constant live: Is it
Drupal\Core\Url?Comment #3
kgoel commentedI am working on this.
Comment #4
kgoel commentedStill lots of places where schemes need to be replaced with constant.
Comment #5
webchickMarking as needs review, since there's a patch. Although...
Hm. :\ Is this actually an improvement? In addition to being way more characters, the concatenation makes it a lot harder to type without errors now, and it's inconsistently using Url::BASE vs. static::BASE in places. :\
Comment #7
webchickAlso, this further shows inconsistency. We don't have one of these named prefixes for public:// and private:// (and definitely won't for http or https), making this now look strange...
Comment #8
kgoel commentedI have added constants in Url.php so its using static::BASE or static::ENTITY in Url.php class but Url::BASE in other classes.
Comment #9
effulgentsia commentedI like that it gives a place to document the schemes. Those docs aren't in the patch, but once they are, it's kind of nice to be able to use an IDE to click through the constant usage and see docs about what the semantics of that constant is.
In an IDE, constants can be autocompleted, so that reduces errors. I can see how concatenating the constant with a ':' can be annoying though and lead to errors. In any cases, there should not be many usages of these schemes. There are a lot currently, but there are issues like #2417827: Evaluate and document each use of base: in core and #2417645: Change destination query string handling to break dependence on system path open to reduce them.
Per #8, that's a common idiom in PHP now and used elsewhere in Drupal including all class-defined constants. Inside the class that defines the constant or one of its subclasses, you can (and generally should) use
static, while in all consuming code you refer to the class by name.Agreed we don't need it for http and https, since those are defined by non-Drupal-only internet standards. I wouldn't be opposed to creating constants for public and private, since those are Drupalisms, so could use the docs. However, those are also fairly unambiguous. But, as we've seen,
baseanduser-patharen't self-evident, so can benefit the most from the automatic connection to docs that constants provide.Comment #10
wim leersSo the current patch adds
Url::BASE,Url::ENTITYandUrl::USER_PATH.I wonder if
Url::SCHEME_BASE,Url::SCHEME_ENTITYandUrl::SCHEME_USER_PATHwould be clearer?That being said, I'm also a bit hesitant now that I see how it affects code legibility.
In the mean time, here's a patch review:
This one forgets to add the colon.
I'm pretty certain all the changes in these files are unintentional.
Comment #11
berdirWhat surprised me a bit was that we're not using methods. The Url object actually has fromUserPathUri()/fromEntityUri(), which seemed like a great idea to me, but they are protected helper methods.
I don't quite get why we make it hard to use those, especially now that we came around and made user-path: and entity: official API's as far as I understand, unlike the way we treated base:// before.
I originally thought that those constants are mostly about internal checks when looking for a specific schema, but looking at the patch, that is doing a lot of replacements for fromUri(), which, honestly, looks worse than it did before?
And even for checks, why not add something like Url::isUserPathScheme($uri) ?
This is a perfect example, instead of having to know about the constant, and that you still need to append :, why not write Url::fromBaseUri('core/install.php')?
Comment #12
xjm@Berdir, I had the same thought last night. I think we'd have a lot of the DX and stability gain by using methods, without the loss of readability. We'd still potentially use constants internally within the classes, also.
Comment #13
xjmComment #14
effulgentsia commented+1 to methods. And if we have methods, then I don't know that constants in addition to the methods would have much value. In #2417567-19: Rename user-path: scheme to internal:, I'm suggesting to rename
user-pathtosite. If we do that, then I suggest adding the following static methods toUrl:- fromBaseRelativePath()
- fromSiteRelativePath()
I don't think we need a public one for
entity, because wherever we need an entity URL from code, we should be calling$entity->url()or$entity->urlInfo(). (I wouldn't mind renaming that latter one to$entity->getUrlObject(), but that's out of scope for this issue.)Comment #15
xjmI still think we should have constants, because the magic strings will still need to be checked in multiple places. (We have to both compose and decompose the URIs.)
fromBaseRelativePath()seems good. #2417827: Evaluate and document each use of base: in core should help with the risk of misuse, and having a method gives us an obvious place to put stern words about the correct use of the method. :)fromSiteRelativePath()doesn't work for me at all, as I would have no clue what the difference was between a site-relative path and base-relative path. However this depends partly on the name we pick in #2417567: Rename user-path: scheme to internal:. So I'll rework this patch with just the base scheme (and maybe the entity constant for internal use) and we can go from there.Comment #16
xjmHere's a start. Attached:
user-path:.Url::SCHEME_BASE,Url::SCHEME_ENTITY, andUrl::SCHEME_ROUTE.Url::fromBaseRelativePath().Url::$routeName, because that's almost always a real route name or null, except for a brief moment when constructing a URL object from an unrouted URI. (A followup to make this less confusing might be nice, but out of scope.)Url::fromUri('base:whatever')toUrl:fromBaseRelativePath('whatever'), except for one example ininstall.core.inc. I (or someone else) can do that once we have some more review of the direction here.Comment #17
xjmForgot that it's technically
core/update.php.Comment #19
David_Rothstein commentedThe approach looks nice to me.
I think for the other method (fromSiteRelativePath() or whatever it might be called) let's just reopen #2405551: Add a method to support UIs where users enter paths instead of route names and other valid use cases. That's exactly what that issue is about, and there are already in-progress patches there to do it. Also, I think it's potentially a more complicated situation than the one in the patch here, because 'user-path' has leaked into the public API on its own (not just in the context of
Url::fromUri('user-path:'...)and therefore it might not be possible to have a single method that replaces all occurrences.Comment #20
xjmSee #2417567-9: Rename user-path: scheme to internal:. Most of the uses of
user-path:are in test code. The ones instandard.installare an interim until Shortcut is finished. The rest are in Shortcut itself, the link widget, the form destination handling, and Views UI. All of these can be converted to the proper API, as far as I can see. And I'll do so in this patch once we finalize the scheme name etc.; I just wanted to make progress without blocking on that issue. Can we please stop splitting this discussion across several issues?Comment #21
wim leers::fromEntityUri(),::fromUserPathUri()and::fromRouteUri().Given that, strong -1 to
::fromBaseRelativePath(). It should be::fromBaseUri(). It needs to be consistent.\Drupal\Core\Urlbeing a simple API with few entry points (::fromRoute(),::fromRouteMatch()and::fromUri()), it then becomes one with MANY entry points.Comment #22
berdir2. It has just as many entry points now, in the form of magic string prefixes. Now it has many entry points in the form of methods that you get autocompleted/suggested.
3. I don't get this, as visible in the patch, "fromUri('base:install.php') becomes "from*Something*('install.php')" so you do not need to repeat the scheme?
4. I disagree. Those methods are not for documentation, they're for developers.
Comment #23
xjm@Wim Leers:
My undetstanding of @effulgentsia's suggestion was that it doesn't take a URI -- it takes a path. But I'm not fussed either way.
But we don't have to pass in the URI schemes. See the first hunk of the patch.
fromEntityUri()is (and should be) protected, and is only used internally, per effulgentsia's suggestion to useEntity::url().I agree that we should streamline the number of entry points a bit, maybe refactor the different code paths in
fromUri()a bit. InfromBaseRelativePath(), the method callsfromUri(), but the other methods are being called byfromUri().Comment #24
David_Rothstein commentedEven if the standard.install example is removed there would still be plenty in contrib, any time code calls
entity_create('menu_link_content', ...)orentity_create('shortcut', ...)or similar, right? (Basically any time you programmatically create a link, you pass in something like'link' => array('uri' => 'user-path:node/add')).) I don't think the same API could be used to replace that because Url::fromWhatever() would return something like '/path/to/my/drupal/site/node/add' which would not work as input to the above.Comment #25
xjmRe: #24, alright, let's explore what those cases might be, or whether we need to expand the API, in the postponed issue. For now, I'll continue with the approach here for the usecases that are specifically user input. @WimLeers, @effulgentsia, and I had a long discussion trying to disentangle user and developer expectations as well as the correct semantics (based on #2417647: Add leading slash to paths within 'user-path:' URIs, to allow 'user-path:' URIs to point to the <none> route, this issue, etc.). I'll update my patch to incorporate that consensus (which @effulgentsia will write up and post for others' review/feedback in a bit).
Comment #26
wim leers#24: programmatically created links would probably not use content entities, but use either
<module>.links.menu.ymlfiles (plenty of those in HEAD) or https://www.drupal.org/project/menu_link_config.Comment #27
xjmMissing
:(thank you unit tests!).Comment #28
effulgentsia commentedPart 1 is in #2417647-28: Add leading slash to paths within 'user-path:' URIs, to allow 'user-path:' URIs to point to the <none> route. Will write up part 2 as a comment in this issue tomorrow.
Comment #29
xjmAdded docs summarizing the public API, the additional "thin wrapper" methods for user input and host-relative paths, and the
SCHEME_USER_PATHconstant, with @todo where appropriate if the names are not necessarily finalized. Also fixing the typo.Comment #30
xjmBlaming my cat for that one. Sorry @Berdir.
Comment #31
xjmHilariously, I failed to actually add
fromUserInput(). Long day.Also, this needs moar caveats.
Comment #33
xjmFail is because of #2420421: HEAD BROKEN: Fatal error: Cannot use Drupal\Component\Utility\Xss as Xss because the name is already in use in /var/lib/drupaltestbot/sites/default/files/checkout/core/modules/views/src/Plugin/views/field/Field.php on line 11.
Comment #35
webchickCommitted that; re-testing.
Comment #36
webchickAhem.
Comment #37
wim leersNow with less purring :P
Comment #38
wim leersAwesome work, xjm! I think this patch is now looking great.
I basically only have a bunch of nitpicks: either nitpicky nitpicks, or wording/formatting that I think could be clearer. Leaving at NR, to get more feedback.
IMO it's correct to use the term "URLs" here. The
\Drupal\Core\Urlclass is about generating URLs. You feed it either routes, route matches, or URIs.So, while this class accepts entity URIs as input, it turns them into entity URLs as output.
This also makes the class-level documentation friendlier, since it doesn't need to make the distinction between URIs and URLs.
Great! :)
s/handling/storing/
or
s/handling/serializing/
This change is *extremely* confusing. The constructor is not designed to accept URIs at all. Passing a URI here would just break.
s/URL object/Url object/, as we consistently do in almost all places in this class.
I think we should also mention here that it can also be used to generate URLs for non-Drupal web apps that are installed inside of Drupal's root directory.
s/most/all/, right?
I think we want single quotes around the sample parameter value, to remove all ambiguity.
"within the site directory" makes me think there's a typo there and you meant "within the sites directory".
I think this should say "but within the directory in which Drupal is installed".
AFAICT this is the exact same paragraph as the one for
::fromBaseRelativePath(). That's wrong. This should be adapted because it's for generating host-relative URLs.Single quotes around the sample parameter value, great!
80 cols.
sounds almost unprofessional (i.e. not the writing itself, but "yo, we invented some URI schemes just for funs and giggles") to my ears — what about:
or even
I'd list the scheme for routes last.
Comment #39
hussainwebChanged as per some of the suggestions in #38 (whichever I could frame better). The only change in code was for point 3.
Comment #40
hussainwebI am not sure what to do with this. The issue in question is already fixed: #2418139: Add a toUriString method to Url class and add a route: scheme
Comment #41
dawehnerShould we take over that you should not use user-path, if possible?
Comment #42
xjmWell, the thing is, we actually do pass it a URI, every time one is used. See in
setUnrouted()and see #2346787: Update Url to reflect that it handles both routes and non-routed URIs which I was addressing on the fly since I was changing all the other references. I can revert the change and reopen #2346787: Update Url to reflect that it handles both routes and non-routed URIs, but it's still kind of a lie, and confusing when you try to read the class. (I wanted to protect the constructor, but then everyone gave me terrified expressions.) ;)Working on some other fixes. Thanks for the review!
Comment #43
xjmReopened #2346787-10: Update Url to reflect that it handles both routes and non-routed URIs since that discussion is out of scope here.
Comment #44
xjmMaybe we should just list them in alphabetical order?
Comment #45
wim leers#42: HAH! That explains some weird things that I saw when debugging somewhere in the last week! +1 to fixing that weirdness.
#44: that works too, but I figured we'd want to least stress the
route:URI scheme.Comment #46
effulgentsia commentedReworked the documentation for
base.Comment #47
xjmNice @effulgentsia!
This paragraph is very long. Maybe we could make it more readable by making a list of all the things you're throwing out by using
base:for a routed path?Minor: I'd suggest Url::access() for better consistency and readability.
Why this change? You might want to link to update.php. I can't think of why you'd ever want to link to install.php.
Meanwhile, working further on this now.
Comment #48
xjmNote to self: does static::foo() work on api.d.o? Otherwise I think these actually need to be \Drupal\fully\namespaced\thing.
Comment #49
xjmThe long detail for the base constant is definitely helpful, but it also seems like too much reading to force someone to do just to create a link to
http:;//example.com/drupal/README.txt. I'm going to try adding a TLDR at the top.Comment #50
xjmAddressing #38, #47, and #48 (api.d.o does not support
static::in docs; see https://www.drupal.org/node/1354#classes).I'm going to work further on this on a flight that may or may not have internet but posting what I have for now.
Comment #51
jhodgdonstatic:: should actually work in docs, I think, on api.drupal.org. It definitely does in classes, and should also in docs... But I don't think we advise using it according to our docs standards. Besides which, users of these methods/constants would need to use them as Url::whatever so that is I think better. So, a good change. :)
I took a quick look through the rest of the docs. Looks mostly good! A few nitpicks:
a)
Any HTML example code needs to be put into @code ... @endcode to render right on api.d.o I think.
b) I had a question about this section of the docs:
This didn't really make it clearer to me... Can it be combined with the previous section, which told what the output of these was, so I don't have to scroll back and forth to figure it out?
c) And here's a typo just below:
mo -> no (I think?)
d)
List standards - list items end in .
e) How come some of the methods say @return this and some say something else? One is probably incorrect. If they're factory methods, @return static is probably more correct than @return this or than spelling out the class returned.
https://www.drupal.org/node/1354#types
e)
List standards: The line before the list should end in : and there should not be a blank line in between.
f) Generally... This documentation seems very repetitive. Maybe it's not but it really seems like every method explains all the other methods. I think maybe we should just have an @see in each one to the others and leave it at that rather than repeating "On the other hand, use this other method..." in every one. I am all for good docs but this seems like overkill. Or put the explanation of which method to use on what on the main class doc block (which may have it anyway), and in the others say "See the main class docs for an explanation of which method to use for what" or something to that effect.
I also think that putting explanations on the scheme constants is less desirable than on the methods, especially if you want to encourage people to use the methods instead of the scheme constants?
g)
Needs blank line before @param.
Comment #52
xjmThanks @jhodgdon! Great feedback.
Unfortunately it doesn't -- see for example in Url::fomRoute()'s documentation. Would it be worth an API module issue for this?
Actually, per our content guidelines, list items that are not full sentences do not end in a period:
From https://www.drupal.org/style-guide/content under "punctuating lists".
Agreed about the repetitiveness, and I'd prefer to put the documentation on the methods as well. So for
base:, we could include the explanation of the semantics of the scheme there, but move the information about usage tofromBaseRelativePath(), and havefromHostRelativePath()reference the former with a note about the differences.I'll work on that next, plus adding some unit tests. Meanwhile, here is my work from the plane...
Comment #53
xjmCopypaste fail; obviously the second and third strpos should be # and ?.
Comment #54
jhodgdonYou are correct about the lists, sorry!
API issue, sure. I don't think it's a big deal though since our docs standards definitely say that after any @tag you need to put the fully namespaced class name. https://www.drupal.org/node/1354#classes
Comment #55
dawehnerNote: we should also @throw the exception.
There is either some serious magic going on here, or someone simply forgot to use "?" and "#" Maybe also use in_array() here.
So we need a little bit more test coverage :)
Comment #56
wim leers#46:
s/in/using/ — maybe? "in" just seems strange, but perhaps that's because I'm not a native speakers.
"incompatible with SEO"… I'm not sure what that means? :)
#52:
"mo" :P
Why not
ltrim($path, '/')?Maybe mention jumplinks here?
#55: see #53 :)
Comment #57
xjmI moved the bits related to
user-path:/internal:/whatever:to #2417567: Rename user-path: scheme to internal:.Comment #58
xjmThis is just moved LOC afaik. At least, I remember rewriting it once and then deciding it was out of scope. ;) Edit: Or it was in @effulgentsia's patch and I already changed it. One of the two.
lol. because I was on an airplane and couldn't reach php.net.
I think because of the not supporting path aliases.
Comment #59
xjmPoints (a), (b), and (f) from #51 still need to be addressed. And tests. And the @todo. And we should clarify something for #56 point (2). And then there's #2422995: Remove the base: scheme in favour of user-path:/internal:.
Comment #60
almaudoh commented#2417567: Rename user-path: scheme to internal: is in now.
Comment #61
xjmProbably won't work on this in the next two days, if someone wants to look into rerolling on what's already gone in now.
Comment #62
mpdonadioJust the reroll. This wasn't straightforward pick HEAD vs patch, so someone should double check that I didn't botch it.
Comment #63
mpdonadioI should be able to address the remaining feedback in the next day or three, and also add some unit test coverage for Url::fromBaseRelativePath() and Url:: fromHostRelativePath().
Since #2422995: Remove the base: scheme in favour of user-path:/internal: got closed as a dup of #2417567: Rename user-path: scheme to internal:, is there really anything to do in relation to it?
Comment #64
mpdonadioTook care of @todo
Thought about using regex or a str_replace, but though this would be the most performant (but I didn't actually profile it).
First pass at tests. Not sure if we need additional ones?
Still going to work on this to address the undone points from #59
Comment #65
mpdonadioGrrr. Pretty sure this will fail because I just noticed strlen(Url::SCHEME_BASE) != 5. Sigh; will fix in next patch.
Comment #67
mpdonadioOK, this should come up green. Everything but #51-f should be addressed. I wanted to post a clean patch before major rewrites on those docs.
Comment #68
pwolanin commentedIs Url::SCHEME_USER_PATH already defined? I don't see it in the patch?
Comment #69
mpdonadioJust unassigning myself because I forgot to work on it :/
Comment #71
mgiffordComment #73
rpayanmRerolled.
Comment #74
pwolanin commentedthe patch seems to be doing a lot more than the issue summary suggests. It's not clear to me these changes are still in scope for beta/RC
Comment #75
mile23#2346787: Update Url to reflect that it handles both routes and non-routed URIs is postponed on this.
Comment #76
mile23Reroll.
Comment #78
hussainwebI didn't review the patch in #76 in detail but this looks like the cause for all the errors.
Comment #79
xjmThis could go into 8.1.x as a BC-compatible API addition.
Comment #80
wim leersWe're working on 8.1 now.
Comment #81
dawehnerMh, I don't like the patch as it is, this is is for sure. We should simply get rid of all URL parsing stuff for actual URLs and better use one of the
\Psr\Http\Message\UriInterfaceimplementations.Comment #82
mile23Reroll of #78.
It seems as though:
a) If we're changing track, these special cases of Url should be subclasses.
b) If we're not changing track, the documentation for the static methods should be the canonical source for how the different schemes work, not the constants. So move all the substantial documentation out of the constants and into the methods, so it's in one place.
c) And in fact we don't really need the constants, because they're implementation details of the methods, and people should just use the methods as factories and not try to build their own using the constants.
Comment #91
moshe weitzman commentedThis seems worth doing still.
Comment #94
neclimdulThis is an old major... This is a kinda niave reroll that seems to apply on the 8.9 and 9.x branches. Rerolling over 5 years is special but not _too_ much has changed in Url in that period other then the finalization of the 'internal:' patch watch back before 8.0. Yowza. I kinda incorporated very quickly into the patch. Most of the other conflicts where documentation conflicts which I tried to review but enough has changed in the last 5 years the doc changes could probably all use a once over.
I'm mostly using it to do a deeper review to see if it addresses the problem I ran into with inconsistent parsing between schemas. :fingerscrossed:
Comment #95
neclimdulSafeMarkup? Lol not much has changed indeed. Looks like this needs more testing.
Comment #100
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.