drush
drush copied to clipboard
drush updatedb does not call hook_boot()
Describe the bug We have this hook_boot() in a module to autoload our composer dependencies in Drupal 7
/**
* Implements hook_boot().
*/
function recruiter_jobiqo_boot() {
// include the composer autoload file.
require_once DRUPAL_ROOT . '/profiles/recruiter/vendor/autoload.php';
}
If we now try to use a class from vendor in an update function like this
function recruiter_dev_update_7000() {
$pdf = new Smalot\PdfParser\Parser();
}
then we get a fatal error with drush upodatedb because the autoloader was never registered because our hook_boot() was never called.
If we run the update with Drupal core's update.php in the browser then the update function runs normally. hook_boot() is called correctly there.
Expected behavior
drush updatedb should invoke hook_boot() correctly.
Actual behavior
$ drush updatedb
WD php: Error: Class 'Smalot\PdfParser\Parser' not found in recruiter_dev_update_7000()
Workaround We could manually include our autoloader in every update function, but that is tedious.
System Configuration
| Q | A |
|---|---|
| Drush version? | 8.1.18 |
| Drupal version? | 7.67 |
| PHP version | 7.2 |
| OS? | Linux |
We implemented the following workaround:
/**
* Implements hook_batch_alter().
*
* "drush updatedb" runs the update in a separate process that doesn't invoke
* hook_boot. As a result classes that are loaded via composer are missing.
*
* Because drush offers no hooks to alter the (internal) updatedb-batch-process
* command we need to modify the batch set instead and inject
* composer_manager_boot as the first item.
*/
function mymodule_batch_alter(&$batch) {
$set = &$batch['sets'][$batch['current_set']];
if(!empty($set['finished']) && $set['finished'] === 'drush_update_finished') {
array_unshift($set['operations'], array('composer_manager_boot', array()));
$set['count']++;
}
}
Instead of including the autoloader from hook_boot, perhaps it would be more reliable to include it from settings.php.
We ended up having to switch to a broader approach since a batch might get split into multiple sets:
function mymodule_batch_alter(&$batch) {
$set = &$batch['sets'][$batch['current_set']];
if(!empty($set['finished']) && $set['finished'] === 'drush_update_finished') {
foreach($set['operations'] as &$op) {
if($op[0] === 'drush_update_do_one') {
$op = ['_mymodule_batch_ensure_autoloader', $op];
}
}
}
}
function _mymodule_batch_ensure_autoloader($function, $args, &$batch_context) {
composer_manager_register_autoloader();
return call_user_func_array($function, array_merge($args, array(&$batch_context)));
}
Btw, creating a watch for getmypid() proved useful to keep an eye out for new processes.
Instead of including the autoloader from hook_boot, perhaps it would be more reliable to include it from settings.php.
I was looking for an approach that would work out of the box, without requiring additional per-project configuration. We added the fix to our base profile.
I also noticed that direct inclusion via e.g. hook_drush_init wouldn't work. Didn't investigate further though.
Our workaround is to directly include our composer autoloader in a module drush command file:
example.drush.inc
<?php
/**
* @file
* Drush command file for recruiter_jobiqo.
*/
// hook_boot() is not called during drush updb - work around this by including
// the autoloader here whenever drush includes module command files.
require_once __DIR__ . '/../../../vendor/autoload.php';