Problem/Motivation
We have DRUPAL_ROOT, a global constant. Global constants are global variables. That is, they complect anything they touch with the environment. It's an implicit dependency that you cannot override, inject, or otherwise reason about.
Proposed resolution
We now have the app.root container property, which has the same value. Use it!
A passed parameter is better than a global constant for all the same reasons that an injected service is better than a global function (or static method). It's an explicit, mockable, controllable dependency we can reason about rather than a stealth dependency and ticking time bomb.
Remaining tasks
Commit the current patch.
User interface changes
None.
API changes
Implicitly DRUPAL_ROOT is deprecated, but that wasn't introduced by this issue.
Comment | File | Size | Author |
---|---|---|---|
#100 | interdiff.txt | 746 bytes | dawehner |
#100 | 2328111-100.patch | 137.19 KB | dawehner |
#97 | 2328111-97.patch | 137.08 KB | dawehner |
#94 | interdiff.txt | 10.65 KB | dawehner |
#94 | 2328111-94.patch | 137.17 KB | dawehner |
Comments
Comment #1
dawehnerFirst version...
Comment #3
dawehnerSome work.
Comment #6
sun@msonnabaum, @neclimdul, @Crell, and I already had some discussions on the topic of how to handle DRUPAL_ROOT, in particular for unit tests. One affected issue was #2246611: Document ModuleHandler pre-conditions
We discussed a constructor injection approach (like here), but further discussion made us prefer a
getAppRoot()
method that defaults to DRUPAL_ROOT but can be cleanly overridden in test stubs. Unfortunately, I don't remember what made us arrive at that conclusion, but IIRC, @msonnabaum raised some legit concerns against constructor injection (or perhaps someone else, dunno).I think that could use some more discussion (→ plan) before investing a lot of work here, so as to ensure that the proposed solution is the desired + sustainable solution.
In any case, I don't really see the point of
\Drupal::root()
and would rather prefer to omit that here. I don't think we're able to remove the DRUPAL_ROOT constant anyway, since a lot of other code depends on it.Comment #7
dawehnerComment #8
Crell CreditAttribution: Crell commentedTo the extent possible I fully support this. I don't think that the extent possible will be 100%, but that doesn't mean we shouldn't do the parts we can. Constructor injection for this is the ideal if we can make it work in a given context.
That said, there's probably a lot of places we're using DRUPAL_ROOT where we arguably don't even need it. That would be even better. :-)
Comment #9
neclimdulWhat Crell said.
Comment #10
dawehnerThe places where we cannot use it is mostly the installer and the kernel and testing, but for sure with this patch, these spaces are actually exposed properly.
Note: For example Settings::initialize() could be free from the root, if we decide to pass in an absolute path.
Comment #12
dawehnerFixed a couple of instances.
Comment #14
dawehnerBack to green.
Comment #15
dawehnerAdded a change record, even this is not actually caused by this issue.
Comment #16
Crell CreditAttribution: Crell commentedThis doesn't get every instance of DRUPAL_ROOT, but it looks like it gets most of 'em. It's also still a digestable patch size. I therefore think we should go ahead with it. If we are able to convert anything else later we can do so.
Comment #18
dawehnerReroll.
Comment #20
dawehnerOkay it is getting late.
Comment #21
Crell CreditAttribution: Crell commentedI've so been there... :-)
Comment #22
tstoecklerHmm... I actually like #18 better. I think every app should have a roo!
(Sorry, couldn't resist.)
Comment #23
cosmicdreams CreditAttribution: cosmicdreams commentedTried to apply patch and it failed in the following files:
core/includes/bootstrap.inc
core/includes/install.core.inc
core/lib/Drupal/Core/DrupalKernel.php
core/lib/Drupal/Core/Site/Settings.php
core/modules/simpletest/src/InstallerTestBase.php
core/modules/simpletest/src/WebTestBase.php
core/rebuild.php
Patch likely was impacted by #2325197: Remove drupal_classloader()
Comment #24
cosmicdreams CreditAttribution: cosmicdreams commentedComment #25
neclimdulNot an "easy" reroll but not t0o bad. Lots off conflicts because of the addition of a classloader to the settings init which had to manually be resolved. Details in the interdiff. Not really comfortable with that change but I missed the issue so to the follow up I'll go.
Comment #26
Crell CreditAttribution: Crell commentedSilly fast-moving core...
Comment #28
dawehnerRerolled.
Comment #30
dawehnerThe url test was indeed a random failure, see #2330751: Random test failures everywhere due to ->with(Request::create())
Comment #31
alexpottWe've gone from 248 to 201 instances of DRUPAL_ROOT so
And the issue title is a bit disingenuous.
I'm happy to do this replacement in several steps but let's have an honest title and followups to replace the other 200 DRUPAL_ROOT instances. I have no idea why we've converted what we've converted in this patch and why we've added additional usages of DRUPAL_ROOT
For example
Comment #32
dawehnerLet's see how much fails. After that there are just 104 left.
Comment #34
dawehnerOkay, let's get back to fix the failures.
Comment #35
Crell CreditAttribution: Crell commentedRetitling to be more accurate. (104 left means more than half are replaced here, which qualifies for "most".)
Comment #36
alexpottComment #37
dawehnerThere we go.
Comment #38
moshe weitzman CreditAttribution: moshe weitzman commentedIMO the motivation section of the IS needs work. I don't know whats ugly about DRUPAL_ROOT. Seems pretty straightforward to me. I see it replaced with various things like $this->root, \Drupal::root(), app.root. Those aren't too bad either - just want to know whats wrong here.
Comment #39
Crell CreditAttribution: Crell commentedmoshe: Global constants are global variables. Aka, complecting with the environment. Aka the class will fatal if that's not defined, as opposed to will not instantiate without an obvious constructor parameter. That is what "ugly" means in this context.
Comment #40
dawehnerThis is about making it clear about the dependencies of these classes.
Comment #41
alexpottSo lets update the summary and be explicit about what we mean by ugliness then.
Comment #42
Crell CreditAttribution: Crell commentedDefining ugly.
(Also this isn't really WSCCI, just general good architecture.)
Comment #43
catchComment #44
dawehnerGood review and embarrassing! I was probably in the middle of debugging something unrelated.
Comment #46
dawehnerForgot one merge conflict.
Comment #47
Crell CreditAttribution: Crell commentedComment #48
catchapp.root is stored on the container.
The container is stored in phpstorage. This massively changes the semantics compared to DRUPAL_ROOT which is guaranteed to be correct each request, now it can get stale.
If I copy or move my Drupal install to a different directory, what happens?
Moving - won't it fatal and require rebuild.php? (not too bad)
Copying - won't it start including files from the wrong directory? (this seems like the potential for all kinds of fun debugging situations).
Comment #49
ParisLiakos CreditAttribution: ParisLiakos commentedthats true for d7 too..move files..you get all kind of weird stuff until you run
drush rr
. i dont see the problemComment #50
dawehnerAn alternative approach would be to construct this on runtime.
Comment #52
dawehnerRerolled.
Comment #53
Crell CreditAttribution: Crell commentedInterdiff please?
Comment #54
dawehnerHere it is. This is btw. a usecase you could use expression language for.
Comment #55
Crell CreditAttribution: Crell commentedI was referring more to an interdiff for #20, with the runtime approach.
Comment #56
dawehnerOH sure, this is a completely different file, maybe this one?
Comment #59
dawehnerReroll
Comment #60
dawehnerReroll.
Comment #62
martin107 CreditAttribution: martin107 commentedWhen I rewound head to October 10, 2014 at 8:11pm and applied the patch from #60, it failed .. I am assuming other commits about that time invalidated the reroll...
So I rerolled from #59
Comment #63
dawehnerAwesome martin!
We sadly need another proper review, given that bits of the actual architecture changed in the meantime.
Comment #66
dawehnerAnother reroll.
Comment #67
neclimdulNot a real review but
Oops
Comment #68
dawehner/me hides
Comment #69
penyaskitoI don't have a concrete proposal but... is there nothing more readable?
If this is absolutely necessary, can it be moved to the parent class avoiding repetition?
Comment #70
dawehnerJust to be clear, this is a unit test ... so nothing you would ever need in production.
Comment #71
penyaskitoI know... but we want people to write unit tests :-)
I am sure people will start by copypasting core ones, and it can be difficult to realize that 99% cases need to replace
with
if they put their modules on
DRUPAL_ROOT/modules
Comment #72
dawehnerSure, let's do it.
Comment #73
penyaskitoThis could use $this->root now I think.
I think here too.
and here.
Comment #74
dawehnerGood catches!
Comment #75
penyaskitoThanks! Looks good to me. However I don't feel confident for RTBCing myself.
Comment #76
jibranI am confused why there are three different approaches?
Comment #77
dawehner... we need DRUPAL_ROOT for really early installer requests ... once we have the container, we can use $container->getParameter('app.root')
Everywhere else we can use the container parameter injected or, in case we don't have it injectable, \Drupal::root().
Comment #78
dawehnerRerolled ...
Comment #80
martin107 CreditAttribution: martin107 commentedFixed up incomplete reroll.
Comment #81
EclipseGc CreditAttribution: EclipseGc commentederrr... weird that it's lowercase and also this is a non-test class, shouldn't we be able to inject the dependency?
Again, we should be able to inject this.
My only other criticism here is that ExtensionDiscovery looks like it could probably be a service in its own right. That's probably outside the scope of this patch, but it's worth noting. Overall, I really like the move away from direct dependencies on Drupal specific code, so this is generally ++ to me. Looks like just a few little issues left with it.
Eclipse
Comment #82
martin107 CreditAttribution: martin107 commentedA Simple fix.
Comment #84
martin107 CreditAttribution: martin107 commentedOpps .. UpdateUploadTest is now green.
Comment #85
dawehnerThe feedback of cris got adressed
Comment #86
alexpottI ponder if we can remove
define('DRUPAL_ROOT', realpath(__DIR__ . '/../../'));
from the PHPUnit bootstrap. The only test fails we have are in UnroutedUrlAssemblerTest and UrlGeneratorTest because the tests are using the constant. If we could remove the define from PHPUnit we would be even closer to removing the dependency and certainly less likely to re-introduce it anywhere unnecessarily.Comment #87
EclipseGc CreditAttribution: EclipseGc commentedThat seems super sensible.
Eclipse
Comment #88
dawehnerFixed #86 and added a couple of more replacements.
Comment #90
dawehnerMh, seems to be some reroll conflict.
Comment #91
dawehnerReroll
Comment #92
penyaskitoBack to RTBC.
Comment #93
tstoecklerLooked through this as well, and it's quite awesome.
Noticed some things, none of them should block commit, though:
protected $root;
toUnitTestCase
(which is good), but a lot of subclasses a duplicating the variable documentation. That could be omitted. (I will open a tiny, unimportant follow-up for that.)As far as I can tell, this is never actually used, but instead
$container->getParameter('app.root')
is being used (vs.$container->get('app.root')
. Am I missing something?Comment #94
dawehnerGood points.
1) Fixed them
2) actually, the 'app.root' service should be used everywhere possible, see
core.services.yml
.I had to get rid of the remaining get/setParameter calls
Comment #95
tstoecklerAwesome, thanks!
Comment #97
dawehnerComment #98
tstoecklerlooks good.
Comment #100
dawehnerArg!
Comment #101
tstoecklerComment #102
alexpottThis issue addresses a major bug and is allowed per https://www.drupal.org/core/beta-changes. Whilst this change is quite disruptive - we gain the benefit of code that is less coupled to global constants and the disruption is only due to patch size and not complexity so I think the benefits outweigh the risk. This patch brings us down to 107 instances of DRUPAL_ROOT (from 288). Also having to rebuild the container after moving a drupal install location seems totally reasonable to me. Committed 91c38c8 and pushed to 8.0.x. Thanks!
Can the CR be updated to include
\Drupal::root()M
too.Comment #104
dawehnerThank you alex!
Adapted the CR.