Problem/Motivation
See #2229865: [meta] Modernize File/StreamWrapper API, we need to move file scheme functions out of file.inc. StreamWrapperManager is a good place for them, but some were already moved to FileSystem earlier.
Proposed resolution
Deprecate both the remaining undeprecated functions in file.inc and those that were already moved to FileSystem in favor of methods on StreamWrapperManager:
StreamWrapperManager is be a good place for them, as it is already injected into FileSystem and closely related to that stuff.
See #126 there for example:
Hm, on closer investigation, I don't think any of these additional methods belong into this new service.
* file_uri_target() is the counter-part to file_uri_scheme(), which is already on file_system
* file_stream_wrapper_uri_normalize() is also very related to these, they're all about (stream wrapper) URI's, not file URLs.
* file_valid_uri() is again about stream wrapper URI's and as a small wrapper around two functions/methods that were already moved to file_system.
* file_build_uri() also is about creating stream wrapper URIs and is tightly connected to these other functions.FileSystem is getting quite big with that other issue, so maybe these methods could be on the stream wrapper manager, but then we'd also need to move some existing methods from FileSystem to that to avoid a cross-dependency.
Remaining tasks
Make method names more consistent.
The following change records need updating on commit:
User interface changes
None
API changes
The following functions have been deprecated:
| Old function/method | New method |
|---|---|
| FileSystem::uriScheme() file_uri_scheme() |
StreamWrapperManager::getScheme() |
| file_uri_target() | StreamWrapperManager::getTarget() |
| FileSystem::validScheme() file_stream_wrapper_valid_scheme() |
\Drupal::service('stream_wrapper_manager')->isValidScheme() |
| file_stream_wrapper_uri_normalize() | \Drupal::service('stream_wrapper_manager')->normalizeUri() |
| file_valid_uri() | \Drupal::service('stream_wrapper_manager')->isValidUri() |
The above table indicates which methods should now be accessed through the service and which are now static methods on the StreamWrapperManager class.
Data model changes
None
Release notes snippet
Several stream wrapper methods have been deprecated in file.inc and the FileSystem service, and are now methods on StreamWrapperManager.
| Comment | File | Size | Author |
|---|---|---|---|
| #84 | 3034072-84-interdiff.txt | 17.99 KB | kim.pepper |
| #84 | 3034072-84.patch | 86.27 KB | kim.pepper |
| #80 | 3034072-80-interdiff.txt | 16.64 KB | kim.pepper |
| #80 | 3034072-80.patch | 86.37 KB | kim.pepper |
| #78 | 3034072-78-interdiff.txt | 1.01 KB | kim.pepper |
Comments
Comment #2
berdirComment #3
berdirSome DI injections left and some unit tests are doing fun things but this deals with most things. I think this makes a lot of sense as an API, multiple places that used these functions already had the stream wrapper manager injected, so that shows IMHO that it makes sense to have them there.
Two are left that I'm not sure: file_default_scheme(), which is a simple wrapper around \Drupal::config() and file_build_uri(), which uses that. StreamWrapperManager is too deep for config, so we can't move that there. But FileSystem uses them in prepareDestination, which only works because nothing that's low-level enough calls that.
Comment #5
kim.pepperRe-roll of #3 plus some code sniff fixes.
Comment #7
kim.pepperFixes some more usages of deprecated methods. Created \Drupal\KernelTests\Core\StreamWrapper\StreamWrapperManagerTest and moved the FileSystemTest method there.
Comment #8
kim.pepperOops. Forgot to update the service definition.
Comment #11
kim.pepperNeed to handle the child classes constructor too.
Comment #13
berdirstill not 100% official, but I prefer using \Drupal::service(), I just don't see a benefit of using $this->container :)
in this case it's also a bit overkill to have that property and setup method for one method, but I guess the goal is to have more test methods here.
Comment #14
kim.pepperFixes ConfigController calling new FileDownloadController() . :-\
Fixes #13.1 Changed to \Drupal::service().
That was the idea.
Comment #16
kim.pepperFix a few more fails. Struggling to decipher the large number of remaining fails.
I manually tried to upload and got an error:
Any ideas?
Comment #19
berdirThis should fix most of these fails. Was pondering about using isValidUri($destination) there but we need the scheme later on, so I ended up doing a mix of both :)
Comment #21
berdirSome more fixes and test updates.
Comment #22
jibranWe have to update https://www.drupal.org/node/2418133 after the commit.
Comment #23
kim.pepperNit: extra *
RTBC for committer review.
Comment #24
berdirThanks for the RTBC, I think it's a bit early for that though because the CR has not been actually written yet, and there is still the second paragraph of #3 to think about.
We could deal with those two functions elsewhere, but might make sense to have a plan at least.
Comment #25
kim.pepperAh missed #3!
file_default_scheme() seems like overkill for an api method. Why not just call \Drupal::config('system.file')->get('default_scheme') directly? We don't have API methods for other 'system.file' config do we?
file_build_uri() looks like it should be in FileSystem service, but we'd be introducing a dependency on config there. 🤔
Comment #26
berdirYes, I'd be happy to drop file_default_scheme() but it doesn't actually solve anything as file_build_uri() uses it.
Comment #27
kim.pepperfile_system service is the 'least worse' option as it is already somewhat high-level with dependencies on StreamWrapperManager, Logger, and Settings.
We could possibly move lower-level file system methods to the FileSystem component later?
Comment #28
berdirI'd really prefer that the file_system service doesn't depend on configuration, that just seems like a bad idea. Config\DatabaseStorage is full of fileSystem calls, and then that again depending on config and even writing config seems too dangerous, even i it's not the actually the same methods.
There's also file_directory_temp(), see #931794: 'file_temporary_path' variable and file_directory_temp() function should match, maybe a new FileSystemConfiguration service for those, but file_build_uri() is still weird, especially since FileSystem actually calls it already. The thing is that it is only called if $destination is *empty* for copy() or move() which, to be honest, seems extremely weird. I'm considering that we should just deprecate support for that entirely, copying/moving implicitly into the default scheme seems like a *suuper* rare case. Question is whether we could push that still in 8.7, then we wouldn't need a dedicated deprecation for that, we'd just make the argument required from the start.
If we drop that, we can remove the hidden config dependency in FileSystem, same for system_retrieve_file(), and then the only remaining non-test use case of that function is \Drupal\image\Entity\ImageStyle::buildUrl(), meaning, we can possibly deprecate that without a replacement too?
Comment #29
berdirI actually really like that idea, so created #3038988: Remove support for empty destination in FileSystem::copy(), FileSystem::move() and FileSystem::saveData()
Comment #30
kim.pepperSounds like a good plan!
Comment #31
kim.pepperI think you meant \Drupal\Core\Config\FileStorage?
We can do both. Makes sense to have a FileSystemConfiguration service and deprecate those functions.
Created #3039026: Deprecate file_directory_temp() and move to FileSystem service
Comment #32
larowlanFeels like we still need discussion here? What became of #3? Perhaps issue summary update to clarify what is done and what still needs to be done?
Looks like might need a re-roll too.
Comment #33
berdirThe answer to #3 is #3039026: Deprecate file_directory_temp() and move to FileSystem service and #3038988: Remove support for empty destination in FileSystem::copy(), FileSystem::move() and FileSystem::saveData(), we move those two into a separate service and remove the dependency on file_build_uri() in FileSystem.
Did a reroll, which git apply -3 resolved automatically, lets see if that worked.
Also started working on an issue summary and change record update. Shows that we still have some work to do in regards to making the new methods more consistent. I kept the names from FileSystem, but I think it would make more sense to prefix them with is/get or another verb (isValidScheme()? validateScheme()? getUriScheme()? getScheme()?)
Comment #34
kim.pepperYeah, I agree with that.
Comment #35
kim.pepperTo be clearer, I like isValidScheme() and getScheme(). :-)
Comment #36
kim.pepperRe-rolled #33.
Discussed the naming with @Berdir in slack and replaced validScheme() with isValidScheme() and uriScheme() with getScheme() as per #34.
Comment #37
kim.pepperFix incorrect case of isVAlidUri() in file.inc
Comment #38
berdirThe deprecation message here is a bit challenging. file_uri_scheme() *is* deprecated since 8.0.0, but the replacement is changing in 8.8, so we should probably say that? Easiest "fix" might be to just update it to say deprecated in 8.8, so that people know they can't use the stream wrapper manager replacement before that?
Either way, most messages will need to be updated to 8.8 and I think the message needs some updates for the new standard, e.g. it should AFAIK be "drupal:8.8.0" now.
Comment #39
kim.pepperI have updated all deprecation messages to drupal:8.8.0 format.
I also added a number of deprecation tests which were missing.
Comment #40
berdirWe might need to be a bit more specific with those kernel tests.
The problem is that this would still pass if the function would return an incorrect but non-null result. A legacy test has two goals:
a) Assert that the expected deprecation message is thrown.
b) Assert that it still works correctly.
b is only partially tested right now. See for example the copy/move test methods that I updated in that test.
Comment #41
kim.pepperRe: #40 I added proper tests for the deprecated methods.
I also found another usage of file_valid_uri() which I replaced.
Comment #43
kim.pepperOops. Typo with ::isValidUrl() instead of ::isValidUri()
Comment #44
berdirwhy the case change in those messages?
It is "drupal", according to the examples in https://www.drupal.org/project/coding_standards/issues/3024461, also for @deprecated.
Comment #45
kim.pepperFixes for #44
Comment #47
kim.pepperDoh. Update the tests with the correct deprecation messages.
Comment #48
claudiu.cristeaHere's a 1st round of review.
The CR branch should be incremented too to 8.8.0.
I see you have used the pattern from #3024461: Adopt consistent deprecation format for core and contrib deprecation messages in
trigger_error()but not in@deprecated. Let's be consistent and use the new standard in both.Exceeds 80 chars. The "Use" word can be moved one line up.
Should we test these too?
Comment #49
berdir4. We usually don't test constructor deprecations unless it's something special, I did dozens of these part of my entity manager issues.
Comment #50
kim.pepperI don't think we should be too pedantic about the deprecation message formatting until that issue is resolved. It's changed multiple times, and I think we're chasing our tail if we keep trying to match the latest format. None the less, I've updated the formatting as per #48.
Comment #51
claudiu.cristeaComment #52
berdirConflicted with #2802803: Temporary files whose files are missing on the disk result in never-ending error log messages
Comment #53
kim.pepperGiven the number of reviewers, contributors and all feedback being addressed, we can safely RTBC.
Comment #54
kim.pepperComment #55
kim.pepperRe-roll of #52
Comment #56
kim.pepperComment #57
larowlanApologies if these questions have been asked before
i'm not really sure why this is a service at all - there is no state? Would it fit better on a utility class as a static method. Does that untie some of our circular dependency issues?
same here
and here
and here
Comment #58
berdirGood question, I'm not sure. Happy to make some of these static, I assume they would still live on StreamWrapperManager? Note that it doens't really change that much. most calls that we have to one of these is then followed by another method call that is not static, typically something like this:
And leaving uriScheme() on FileSystem also doesn't make sense to me when all the other related functions are on StreamWrapperManager, including its companion getUriTarget().
1. and 3. is the same function I think, just old and new location.
2. getUriTarget(), yup, that could be static.
4. This one actually calls $this->getClass(), so no candidate.
So that means we would have 2 methods that could be static, getScheme() and getUriTarget().
Comment #59
alexpottThere are benefits to making things static in that we'll not be tempted to make these things stateful in the future. I think that means we wouldn't have to inject the stream wrapper manager into
ThemeSettingsForm. Another thought is looking at the code makes me wonder if we shouldn't be adding a value object like FileUri that has these methods.Comment #60
berdirI'm not sure how a FileUri value object would work. These are URI's that we're passing around to file_system and php functions, they need a string. And several things, like isValidScheme() need access to the registered stream wrappers and with that the manager.
Maybe a helper class FileUrl (..Validator|Parser|...) with static methods but the only thing we can IMHO move are getScheme() and getUriTarget() and not sure that's worth it.
Comment #61
kim.pepperI'm in favour of making dependencies explicit by passing them in. If we split this into two, one the service and another a helper class with static methods, only on of them will be explicit. However, both are still dependencies, and instead of one dependency, we now have two.
+1 for keeping them on the service.
Comment #62
berdirSetting back to RTBC to get feedback from @alexpott/@larowlan.
Comment #63
jibranComment #64
jibranIS has been updated since the tag was added.
Comment #65
kim.pepperRe-roll after #3049021: Deprecate file_default_scheme() and just use config
Comment #67
kim.pepperFixed leftover line from merge. Also switched to using assert for typehinting in tests, because I know @dawehner prefers that. ;-)
Comment #68
kim.pepperSetting back to RTBC as per #62
Comment #69
alexpottSo @Berdir convinced me that my value objects idea was a bad one BUT I agree with @larowlan that we should make getScheme() and getUriTarget() static. Declaring something has no need of state and keeping it that way is valuable.
Comment #70
berdirAs discussed with @alexpott in slack, we'll heave the methods on the interface/class and just make it static.
Comment #71
kim.pepperConverted getScheme() and getUriTarget() to static methods.
Comment #72
berdirhm, that's an interesting pattern. Not sure, but I'd either keep it as -> or with the classname.Rest of the patch is a mix of both. I'd wait for some more feedback on that.Discussed a bit on slack and thought about it a bit more. There's a good amount of existing examples in core and it helps to avoid an extra use statement in most cases, so $stream_wrapper_manager:: if it's needed as an object, the classname otherwise seems fine.
Looks like this was the only usage of this in CssOptimizer, so we can drop the constructor again, yay.
Comment #73
kim.pepperComment #74
berdirGreat, back to RTBC as #69 has been addressed.
Comment #75
alexpottUnnecessary use of a service.
unnecessary injection
As we're adding a constructor here we need to cope with a things that don't expect it to be required so we need to enable this to be constructed with out a stream wrapper manager. I.e as if we've added an arg to an existing constructor.
This could be converted into a legacy test with an @expected deprecation rather than removed. Otherwise I don;t think we have test coverage of $this->fileSystem->uriScheme()
Comment #76
kim.pepperComment #77
alexpottNeed a
= NULLhere.Comment #78
kim.pepperOops! Thanks!
Comment #79
berdirthis could be a static::getScheme(), not sure if it makes a difference.
nitpick: missing type and description of return seems off, "allowed" for what? should be valid I'd say
another one where the static call is the only call, so no service needed?
needs to be = NULL here too, so that subclasses of ImageStyleDownloadController still work as well.
we are injecting the stream wrapper manager here, so can use $this->streamWrapperManager
now that these two methods are static, can we just call them directly in the unit test? looks like that duplicates the real implementation? Then we can add a @trigger_error() to these too.
another static call?
doesn't seem to use the service elsewhere?
same.
same.
seeing them next to each other made me realize that one has Uri and the other doesn't, seems a bit inconsistent but not sure if that's an issue and which way we should make them consistent.
more static only calls.
Comment #80
kim.pepperThanks for the thorough review @Berdir!
Comment #81
berdirLooks good to me now, nice cleanup in that interdiff.
@alexpottt might have some thoughts on that method name in #79.11, I think otherwise it is ready.
Comment #82
alexpottRe the names getScheme() and getTargetUri() - looking at the PHP doc for inspiration - https://www.php.net/manual/en/intro.stream.php it says:
So I think the methods should be getScheme() and getTarget() because together they represent a URI but individually neither are a URI.
Comment #83
berdirWorks for me, thanks.
Comment #84
kim.pepperre: #82 renamed getUriTarget() to getTarget().
Comment #85
kim.pepperI think it's safe to put this back to rtbc
Comment #86
jibranYeah, #82 is addressed so RTBC +1
Comment #87
alexpottThe CR needs updating to match the final state of the patch.
The API changes section of the issue summary needs filling out
The release notes section needs completing.
Also we should list the CRs that need updating in the issue summary - atm from the comments I can find https://www.drupal.org/node/2418133
Comment #88
kim.pepperComment #89
kim.pepperI've updated the IS and CR. I couldn't find any other CRs that needs updating other than the one you listed. I put that in the IS.
Comment #90
alexpottI rerolled the patch to fix the conflict in core/tests/Drupal/Tests/Core/File/FileSystemTest.php and ran the test locally. All good - it was a conflict with the getMock() patch.
Committed e08ec13 and pushed to 8.8.x. Thanks!
Time to update that CR.
Comment #92
alexpottComment #93
jibranhttps://www.drupal.org/node/2418133 still needs the update.
Comment #94
kim.pepperWoop! \m/
Updated the related CR.
Comment #95
mondrakeFTR, the change of
Drupal\system\FileDownloadControllerconstructor's signature can lead to an error likeCall to a member function isValidScheme() on nullin modules that extend the base class without calling the parent constructor.
See for example #3059071: Call to a member function isValidScheme() on null for Textimage.
Comment #96
berdirFun :-/ I'd suggest you open a follow-up, we can do the same thing we did before in other places, add a private getFileSystem() method that does another @trigger_error() and falls back to Drupal::service() if it's not set.
Comment #98
xjmBased on discussion with @larowlan and @pameeela, this issue isn't more disruptive than any typical deprecation, so it probably doesn't need to be listed in the release notes (unless there's something more disruptive for some edgecase than what's called out in the CR).