9

I'm working on a Drupal 8 site, and part of the requirement is giving the client a page where they can click a button to do a particular task. I am able to do this using a Drush command from the command line, but I want to do it from a module.

I had seen code using either php_exec() or drush_invoke_process(). How can I use this function?

5
  • I had seen that but there the questioner had added a comment. " drush_invoke_process are undefined. Do i need to include any class or file in my custom drupal 8 module?" I am also getting same error..How can i define drush_invoke_process function? Commented Sep 17, 2018 at 7:41
  • Yes, I got the same error, it is because Drush doesn't not bootstrap Commented Sep 17, 2018 at 8:24
  • when using drush_invoke_process() it is showing me an error that function undefined. If I am using php_exec() my drush commands is not working. Eg:- exec('whoami') is working but exec("/vendor/bin/drush/drush status"); is not working. Commented Sep 17, 2018 at 11:06
  • /vendor/bin/drush/drush seems to be a bit of an odd location for Drush. /vendor in your machine's root, really? I bet you just referenced the wrong location. Commented Sep 21, 2018 at 19:37
  • I think this is probably too opinionated because I wuoldn't try to run drush commands in the UI personally. Instead I would probably abstract the code and make an interface to run via batchable or non-batchable actions. Commented Sep 24, 2018 at 0:00

5 Answers 5

7

Just start it from the other side. Imagine that every Drush command executes some PHP code. Then check the code of the Drush command you want to run. And then refactor it into the submit function of your button.

Doing it like suggested in your question doesn't seem to be very sustainable, taking into account that not every host has Drush installed. So you better make that functionality work independently from its environment.


Found one possible way: How to run Drupal console command from code? using Symfony's Process and flagged question as possible duplicate. And regarding drush_invoke_process(), as how I understand it so far, it seems to be callable only from within other Drush commands.


Next thing that came to my attention: In the comments to your question you are saying that you are trying to execute /vendor/bin/drush/drush status which would mean you have the vendor folder at the root of your machine. Which very probably is wrong and you just need to fix that path.

3
  • 1
    when using drush_invoke_process() it is showing me an error that function undefined. If I am using php_exec() my drush commands is not working. Eg:- exec('whoami') is working but exec("/vendor/bin/drush/drush status"); is not working. Commented Sep 17, 2018 at 11:06
  • 2
    @RifasAli – Well, then just don't do it that way. Which command by the way? Commented Sep 17, 2018 at 11:08
  • 1
    Actually I am trying to do drush commands from custom module. Actually the use case is I want to create a file upload section with a button. And while clicking this button I want the migration import command to be done. Write now client is using drush command from CLI to do mi import. We want to provide a ui for the user to achieve this , So as a first step I am trying to run any drush commands using my custom module.Am I missing any includes/ use? Commented Sep 17, 2018 at 11:13
12

From Drush 9 and higher drush commands are symfony services, so you can load them as other services though the services container. example:

if (PHP_SAPI === 'cli') { $command = \Drupal::service('migrate_tools.commands'); $command->import('my_migration', ['update' => TRUE, 'sync' => TRUE]); } 
4
  • 3
    Be careful using this method because drush is assuming the context is CLI so depending on the command it could not work as expected when is run from UI, but you could use the approach for making drush commands calling another commands or other in other CLI contexts like cron execution. Commented Sep 28, 2020 at 9:51
  • 1
    I edited the snippet so the code won't fail if called from UI. It will be valid only if the implementation is aim to be called from CLI (i.e custom drush commands, cron jobs, etc) Commented Mar 2, 2021 at 11:01
  • I would like to add a note how to get result from command execution. Assume we are running next command: $command = \Drupal::service('migrate_tools.commands'); $result = $command->status('migrate_path_alias', ['field' => 'last_imported']);, then to get some field you need iterator_to_array($result)[0]['last_imported'] Commented Jan 28, 2022 at 18:43
  • you have to set $command->setOutput() and $command->setLogger() Commented Mar 20, 2023 at 20:12
2

On Drupal 9.5.11, I wanted to call the command from a contrib module (search_api) to get more control over it. This is how I did :

$self = Drush::aliasManager()->getSelf(); $finished = FALSE; // These are the options of the drush command $options = ['limit' => '100', 'batch-size' => '50']; while (!$finished) { // You can specify an index id in the 3rd argument, ex : ['index'] $process = $this->processManager()->drush($self, 'search-api:index', [], $options); // We capture the output to print it here and check if it finished. $process->run($process->showRealtime()->hideStdout()); if (!$process->isSuccessful() || empty($process->getOutput())) { $finished = TRUE; } else { // \Drupal::logger('YourModule')->notice($process->getOutput(), []); } } 

You can check the command drush search-api:index to identify the argument and the options of it, and how there are dealt with in this code.

1
  • I suggest this code is part of some another Drush command. As documentation says: Since DrushCommands already uses ProcessManagerAwareTrait, all Drush commands may use the process manager to call other Drush commands.Other classes will need to ensure that the process manager is injected Commented Aug 22, 2024 at 19:07
1

I had a similar problem and solved it like this:

if(!getenv('HOME')) { $drupal_home = getcwd().'/drupal/web'; putenv("HOME=$drupal_home"); } exec('drupal/vendor/bin/drush status', $output, $return_var); if(!$return_var) { var_dump($output); } 
1
  • Thank you man! This really saved our ass. Commented Mar 4 at 17:20
-2

run updb from other drush command class

 public function updateDB() { $updb = new \Drush\Commands\core\UpdateDBCommands(); $updb->setProcessManager($this->processManager()); $updb->setConfig($this->getConfig()); $updb->setLogger($this->logger()); $updb->setSiteAliasManager($this->siteAliasManager()); $updb->input()->setOption('yes', true); $updb->setOutput($this->output()); $updb->updatedb(['cache-clear' => true, 'entity-updates' => false, 'post-updates' => true]); } 
2
  • This doesn't really make much sense to me, perhaps you can elaborate how the OP can apply this to their scenario? Commented Jun 5, 2023 at 8:06
  • he can apply exaclty the same as on example above: take the class which which implements the command he desires, set the process manager, config, logger channel, i/o stream and data to avoid waiting for user input and run the command class method to do job. In example above $this is a custom drupal service. Commented Jun 7, 2023 at 12:44

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.