When creating a Drupal 8 module, I attempted to use the views hook hook_views_pre_build(), which has the following function signature according to views.api.php:
function hook_views_pre_build(ViewExecutable &$view) {)
I implemented the hook in the following way:
function mymodule_views_pre_build(ViewExecutable &$view) {
// Do nothing.
}
Simply implementing the hook generates the following error:
Warning: Parameter 1 to mymodule_views_pre_build() expected to be a reference, value given in Drupal\Core\Extension\ModuleHandler->invokeAll() (line 285 of core/lib/Drupal/Core/Extension/ModuleHandler.php).
This indicates that when Views runs ModuleHandler->invokeAll(), it doesn't pass the $view argument by reference. Modifying the call to invokeAll(), which is at line 1029 of /core/modules/views/lib/Drupal/views/ViewExecutable.php fixes the error.
Original:
// Let modules modify the view just prior to building it.
$module_handler = \Drupal::moduleHandler();
$module_handler->invokeAll('views_pre_build', array($this));
Modification:
// Let modules modify the view just prior to building it.
$module_handler = \Drupal::moduleHandler();
$module_handler->invokeAll('views_pre_build', array(&$this));
I'd be happy to submit a patch for this, but I'd first like to get some feedback about this approach—I have a hazy recollection of passing arguments by reference in this manner as being deprecated. Additionally, if this change is made for hook_views_pre_build(), it will need to be made for other hooks as well.
Comment | File | Size | Author |
---|---|---|---|
#18 | views-hooks-2073507-18.patch | 6.06 KB | grasmash |
#14 | views-hooks-2073507-14.patch | 6.1 KB | grasmash |
#6 | views-hooks-2073507-6.patch | 5.68 KB | grasmash |
#2 | views-hooks-2073507-2.patch | 3.5 KB | grasmash |
Comments
Comment #1
grasmash CreditAttribution: grasmash commentedMore thought on the issue of passing by reference at call time. @see http://php.net/manual/en/language.references.pass.php:
Nevertheless, I believe that calling $module_handler->invokeAll(array(&$view)) is valid because the array is not being passed by reference, just one of the array items. I can confirm that this does not cause a fatal error in PHP 5.4.
The odd thing here is that I thought objects were always passed by reference in PHP 5.3. Is that not right?
Comment #2
grasmash CreditAttribution: grasmash commentedAttaching patch.
Comment #3
grasmash CreditAttribution: grasmash commentedAfter some more research, I found this:
http://stackoverflow.com/questions/9331519/php-object-by-reference-in-ph...
Which explains that by default, objects in PHP 5 are not really passed by reference or by value—they're passed by identifier (a reference to a reference).
It follows that we do not actually need to pass objects by reference when calling invokeAll() in Views. Passing the variables as-is will allow hooks to modify the passed $view object with accessors.
There are two paths to take:
The problem with leaving Views as-is will be that we will still have this problem with non-object parameters, of which there are a few.
Comment #4
larowlanSince php 5 objects get passed by reference, remove the & from both
Comment #5
grasmash CreditAttribution: grasmash commentedThat isn't technically true. If they were, the PHP warning would not appear. @see http://php.net/manual/en/language.oop5.references.php:
@larowlan Regardless of the technicalities, removing the "&" from the function signature may be a good solution for objects, but it doesn't address views hooks that are intended pass strings and arrays by reference. Notably hook_views_pre_view().
Comment #6
grasmash CreditAttribution: grasmash commentedRerolling. Fixing Views API documentation, passing strings and arrays by reference. Adding tag at @timplunkett's request.
Comment #8
grasmash CreditAttribution: grasmash commented#6: views-hooks-2073507-6.patch queued for re-testing.
Comment #9
grasmash CreditAttribution: grasmash commentedChanging title.
Comment #10
dawehnerThe current patch looks good. Objects don't need references at all, while strings/arrays do need if they are intended to be changed. I don't really get why you would like to change a display ID?
Comment #11
grasmash CreditAttribution: grasmash commentedI changed the display ID to be passed by reference because the function signature in views.api.php indicates that the display_id is expected to be passed by reference. It is documented in the same way in D7 Views.
Comment #12
grasmash CreditAttribution: grasmash commented@dawehner would you say this issue is RTBC?
Comment #13
dawehnerCould we fix that here, as there is no value in there at all. It would just break everything if it is changed.
Comment #14
grasmash CreditAttribution: grasmash commented@dawehner Re-rolled patch. $display_id is no longer passed by reference. This required changing the example for hook_views_preview() in views.api.php. At @merlinofchaos' suggestion, I added an example that modifies the $args array.
Comment #15
dawehnerIs there a reason for this change? We can still call the module handler directly ...
Comment #16
grasmash CreditAttribution: grasmash commentedJust being consistent with the other calls to the invokeAll() method in the same file. Every other method in that file sets $method_handler first.
Comment #17
dawehnerWell, the reason for the other places are that the variable is reusable ...
Comment #18
grasmash CreditAttribution: grasmash commentedComment #19
dawehnerPerfect!
Comment #20
webchickGreat catches.
Committed and pushed to 8.x. Thanks!