Problem/Motivation
file_create_url()
got no love at all during the D8 cycle.
Consequently, it's:
- still procedural
- calls
\Drupal::service()
four times, and uses a global - is not unit testable
Additionally, in current D8, two additional problems arose:
- 90% of the calls need to be wrapped like this
file_url_transform_relative(file_create_url($css_asset['data']));
because it generates absolute URL's and we want to avoid the url.site cache context whenever we can. - Some of the calls that would like to use relative URLs currently can't because they need to pass the result as a Url object, and the problem is that non-absolute file_create_url() URL's are root-relative, so they contain the path that drupal is installed in. The Url class however doesn't accept that and can't deal with it. That is what #2646744: \Drupal\Core\Url does not accept root-relative (file) URLs, making it impossible to let LinkGenerator create root-relative file URL links is about. However, that is in my (berdir) opinion almost impossible to solve, because if you have /admin/foo without any further information, it's not possible to tell if /admin is now the base path or not.. Even if the base path really is /admin, it might still be an internal /-prefixed URL.
Proposed resolution
We have two possible options, but both involve adding a FileUrlGenerator service with methods to deal with this. The best way to fix the above problem is to return Url objects already from that service, at least when we need it. Initial versions had a method with an argument to get a relative/absolute URL, but that has been abandoned.
Option 1)
Add 3 dedicated methods for each use case: relative string: generate(), absolute string: generateAbsolute(), Url object: generateUrl(). Advantage is that *many* cases right now really do just need a string, but it leads to considerable duplication within the methods as the current implementation is designed to make it as efficient as possible. E.g. if we get a relative file URL, and want a relative result, we just generate that. And not a Url object or absolute URL just to make it then relative again like we do now.
Option 2)
Only add generateUrl(), if you want a string, use toString(), if you want it to be absolute, use setAbsolute()->toString(). That comes with a certain overhead when you don't need a Url object both in terms of code execution and also characters you have to type. But I more and more places deal with Url objects and the ->toString() thing is everywhere now.. entity toUrl/toLink, and we deprecated several other API's in favor of Url/Link .. toString(). It also allows us to improve cacheability metadata handling, as it can be added automatically.
The patch up until comment #130 implements Option 1, and I (again, berdir) initially believed that to be better, but I'm starting to reconsider. As far as the amount of calls on each of those calls go, these are the current numbers, identified with file.+->generate\(
:
generate(): 76 calls in 32 files
generateAbsolute(): 35 calls in 13 files
generateUrl(): 6 calls in 5 files.
That does look it overwhelmingly points out that the separate methods are very useful. *But* a huge amount of those calls are actually in tests and are neither performance-relevant nor the main use case for DX. If I exclude those, it changes quite a bit:
generate(): 21 calls in 14 files
generateAbsolute(): 3 calls in 3 files
generateUrl(): 4 calls in 4 files.
Still considerably more generate() calls than generateUrl() but it does paint a more realistic picture.
Update
The decision was initially made to go with Option 2. However, considerable drawbacks were discovered while implementing that, for example because several related and underlying API's can only return strings and multiple conversions between Url object and back were necessary to display an image style for example. See #137for the details. Changing these API's is not feasible in the already large scope of this issue.
Because of that, this decision was reverted and starting with #151, the patch when back to an earlier version implementing Option 1.
Remaining tasks
Create a follow-up to deprecate the non-Url
methods in Drupal 9, likely as a meta issue that first needs to update the related API's.#3087434: [meta] Use FileUrlGenerator::generateUrl() everywhere, then deprecate generate() and generateAbsolute()
Update #2646744: \Drupal\Core\Url does not accept root-relative (file) URLs, making it impossible to let LinkGenerator create root-relative file URL links after this is resolved. While the bug is not technically fixed, it becomes much less important as we can create a Url object directly instead of having to convert it.
User interface changes
None.
API changes
None.
Data model changes
None.