engines.inc

  1. 8.0.x includes/engines.inc
  2. 6.x includes/engines.inc
  3. 7.x includes/engines.inc
  4. master includes/engines.inc

The drush engines API implementation and helpers.

Functions

Namesort descending Description
drush_engine_add_help_topics Add engine topics to the command topics, if any.
drush_engine_topic_command Implementation of command hook for docs-output-formats
drush_find_engine_to_use Returns a valid engine to use.
drush_get_command_engine_config Returns engine config supplied in the command definition.
drush_get_engine Return the engine of the specified type that was loaded by the Drush command.
drush_get_engines Return a structured array of engines of a specific type.
drush_get_engine_topics Take a look at all of the available engines, and create topic commands for each one that declares a topic.
drush_get_engine_types_info Obtain all engine types info and normalize with defaults.
drush_get_user_selected_engine Obtains the engine to use.
drush_include_engine Include the engine code for a specific named engine of a certain type.
drush_load_command_engine Selects and loads an engine implementing the given type.
drush_load_command_engines Include, instantiate and validate command engines.
drush_load_engine Loads and validate an engine of the given type.
drush_merge_engine_data Add command structure info from each engine type back into the command.
drush_set_engine Called by the Drush command (to cache the active engine instance.

File

includes/engines.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * The drush engines API implementation and helpers.
  5. */
  6. /**
  7. * Obtain all engine types info and normalize with defaults.
  8. *
  9. * @see hook_drush_engine_type_info().
  10. */
  11. function drush_get_engine_types_info() {
  12. $info = drush_command_invoke_all('drush_engine_type_info');
  13. foreach ($info as $type => $data) {
  14. $info[$type] += array(
  15. 'description' => '',
  16. 'option' => FALSE,
  17. 'default' => NULL,
  18. 'options' => array(),
  19. 'sub-options' => array(),
  20. 'config-aliases' => array(),
  21. 'add-options-to-command' => FALSE,
  22. 'combine-help' => FALSE,
  23. );
  24. }
  25. return $info;
  26. }
  27. /**
  28. * Return a structured array of engines of a specific type.
  29. *
  30. * Engines are pluggable subsystems. Each engine of a specific type will
  31. * implement the same set of API functions and perform the same high-level
  32. * task using a different backend or approach.
  33. *
  34. * This function/hook is useful when you have a selection of several mutually
  35. * exclusive options to present to a user to select from.
  36. *
  37. * Other commands are able to extend this list and provide their own engines.
  38. * The hook can return useful information to help users decide which engine
  39. * they need, such as description or list of available engine options.
  40. *
  41. * The engine path element will automatically default to a subdirectory (within
  42. * the directory of the commandfile that implemented the hook) with the name of
  43. * the type of engine - e.g. an engine "wget" of type "handler" provided by
  44. * the "pm" commandfile would automatically be found if the file
  45. * "pm/handler/wget.inc" exists and a specific path is not provided.
  46. *
  47. * @param $engine_type
  48. * The type of engine.
  49. *
  50. * @return
  51. * A structured array of engines.
  52. */
  53. function drush_get_engines($engine_type) {
  54. $info = drush_get_engine_types_info();
  55. if (!isset($info[$engine_type])) {
  56. return drush_set_error('DRUSH_UNKNOWN_ENGINE_TYPE', dt('Unknown engine type !engine_type', array('!engine_type' => $engine_type)));
  57. }
  58. $engines = array(
  59. 'info' => $info[$engine_type],
  60. 'engines' => array(),
  61. );
  62. $list = drush_commandfile_list();
  63. $hook = 'drush_engine_' . str_replace('-', '_', $engine_type);
  64. foreach ($list as $commandfile => $path) {
  65. if (drush_command_hook($commandfile, $hook)) {
  66. $function = $commandfile . '_' . $hook;
  67. $result = $function();
  68. foreach ($result as $key => $engine) {
  69. // Add some defaults
  70. $engine += array(
  71. 'commandfile' => $commandfile,
  72. // Engines by default live in a subdirectory of the commandfile that
  73. // declared them, named as per the type of engine they are.
  74. 'path' => sprintf("%s/%s", dirname($path), $engine_type),
  75. 'options' => array(),
  76. 'sub-options' => array(),
  77. );
  78. $engines['engines'][$key] = $engine;
  79. }
  80. }
  81. }
  82. return $engines;
  83. }
  84. /**
  85. * Take a look at all of the available engines,
  86. * and create topic commands for each one that
  87. * declares a topic.
  88. */
  89. function drush_get_engine_topics() {
  90. $items = array();
  91. $info = drush_get_engine_types_info();
  92. foreach ($info as $engine => $data) {
  93. if (array_key_exists('topic', $data)) {
  94. $items[$data['topic']] = array(
  95. 'description' => $data['description'],
  96. 'hidden' => TRUE,
  97. 'topic' => TRUE,
  98. 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
  99. 'callback' => 'drush_engine_topic_command',
  100. 'callback arguments' => array($engine),
  101. );
  102. }
  103. }
  104. return $items;
  105. }
  106. /**
  107. * Include, instantiate and validate command engines.
  108. *
  109. * @return FALSE if a engine doesn't validate.
  110. */
  111. function drush_load_command_engines($command) {
  112. $result = TRUE;
  113. foreach ($command['engines'] as $engine_type => $config) {
  114. $result = drush_load_command_engine($command, $engine_type);
  115. // Stop loading engines if any of them fails.
  116. if ($result === FALSE) {
  117. break;
  118. }
  119. }
  120. return $result;
  121. }
  122. /**
  123. * Returns engine config supplied in the command definition.
  124. */
  125. function drush_get_command_engine_config($command, $engine_type, $metadata = array()) {
  126. if (isset($command['engines'][$engine_type])) {
  127. $metadata = array_merge($metadata, $command['engines'][$engine_type]);
  128. }
  129. return $metadata;
  130. }
  131. /**
  132. * Selects and loads an engine implementing the given type.
  133. *
  134. * Loaded engines are stored as a context.
  135. */
  136. function drush_load_command_engine($command, $engine_type, $metadata = array()) {
  137. drush_log(dt("Loading !engine engine.", array('!engine' => $engine_type), 'bootstrap'));
  138. $config = drush_get_command_engine_config($command, $engine_type, $metadata);
  139. $engine_info = drush_get_engines($engine_type);
  140. $selected_engine = drush_get_user_selected_engine($config, $engine_info);
  141. $version = drush_drupal_major_version();
  142. $context = $engine_type . '_engine_' . $selected_engine . '_' . $version;
  143. $instance = drush_get_context($context, FALSE);
  144. if ($instance != FALSE) {
  145. drush_set_engine($engine_type, $instance);
  146. }
  147. else {
  148. $instance = drush_load_engine($engine_type, $selected_engine, NULL, NULL, $config);
  149. $command['bootstrap_errors']["FOO"] = "BAR";
  150. if ($instance == FALSE) {
  151. return FALSE;
  152. }
  153. drush_set_context($context, $instance);
  154. }
  155. return $instance;
  156. }
  157. /**
  158. * Add command structure info from each engine type back into the command.
  159. */
  160. function drush_merge_engine_data(&$command) {
  161. // First remap engine data from the shortcut location
  162. // ($command['engine_type']) to the standard location
  163. // ($command['engines']['engine_type'])
  164. $info = drush_get_engine_types_info();
  165. foreach ($info as $engine_type => $info) {
  166. if (isset($command[$engine_type])) {
  167. $config = $command[$engine_type];
  168. foreach ($info['config-aliases'] as $engine_option_alias_name => $engine_option_standard_name) {
  169. if (array_key_exists($engine_option_alias_name, $config)) {
  170. $config[$engine_option_standard_name] = $config[$engine_option_alias_name];
  171. unset($config[$engine_option_alias_name]);
  172. }
  173. }
  174. // Convert single string values of 'require-engine-capability' to an array.
  175. if (isset($config['require-engine-capability']) && is_string($config['require-engine-capability'])) {
  176. $config['require-engine-capability'] = array($config['require-engine-capability']);
  177. }
  178. $command['engines'][$engine_type] = $config;
  179. }
  180. }
  181. foreach ($command['engines'] as $engine_type => $config) {
  182. // Normalize engines structure.
  183. if (!is_array($config)) {
  184. unset($command['engines'][$engine_type]);
  185. $command['engines'][$config] = array();
  186. $engine_type = $config;
  187. }
  188. // Get all implementations for this engine type.
  189. $engine_info = drush_get_engines($engine_type);
  190. if ($engine_info === FALSE) {
  191. return FALSE;
  192. }
  193. // Complete command-declared engine type with default info.
  194. $command['engines'][$engine_type] += $engine_info['info'];
  195. $config = $command['engines'][$engine_type];
  196. $engine_data = array();
  197. // If there's a single implementation for this engine type, it will be
  198. // loaded by default, and makes no sense to provide a command line option
  199. // to select the only flavor (ie. --release_info=updatexml), so we won't
  200. // add an option in this case.
  201. // Additionally, depending on the command, it may be convenient to extend
  202. // the command with the engine options.
  203. if (count($engine_info['engines']) == 1) {
  204. if ($config['add-options-to-command'] !== FALSE) {
  205. // Add options and suboptions of the engine type and
  206. // the sole implementation.
  207. $engine = key($engine_info['engines']);
  208. $data = $engine_info['engines'][$engine];
  209. $engine_data += array(
  210. 'options' => $config['options'] + $data['options'],
  211. 'sub-options' => $config['sub-options'] + $data['sub-options'],
  212. );
  213. }
  214. }
  215. // Otherwise, provide a command option to choose between engines and add
  216. // the engine options and sub-options.
  217. else {
  218. // Add engine type global options and suboptions.
  219. $engine_data += array(
  220. 'options' => $config['options'],
  221. 'sub-options' => $config['sub-options'],
  222. );
  223. // If the 'combine-help' flag is set in the engine config,
  224. // then we will combine all of the help items into the help
  225. // text for $config['option'].
  226. $combine_help = $config['combine-help'];
  227. $combine_help_data = array();
  228. // Process engines in order. First the default engine, the rest alphabetically.
  229. $default = drush_find_engine_to_use($config, $engine_info);
  230. $engines = array_keys($engine_info['engines']);
  231. asort($engines);
  232. array_unshift($engines, $default);
  233. $engines = array_unique($engines);
  234. foreach ($engines as $engine) {
  235. $data = $engine_info['engines'][$engine];
  236. // Check to see if the command requires any particular
  237. // capabilities. If no capabilities are required, then
  238. // all engines are acceptable.
  239. $engine_is_usable = TRUE;
  240. if (array_key_exists('require-engine-capability', $config)) {
  241. // See if the engine declares that it provides any
  242. // capabilities. If no capabilities are listed, then
  243. // it is assumed that the engine can satisfy all requirements.
  244. if (array_key_exists('engine-capabilities', $data)) {
  245. $engine_is_usable = FALSE;
  246. // If 'require-engine-capability' is TRUE instead of an array,
  247. // then only engines that are universal (do not declare any
  248. // particular capabilities) are usable.
  249. if (is_array($config['require-engine-capability'])) {
  250. foreach ($config['require-engine-capability'] as $required) {
  251. // We need an engine that provides any one of the requirements.
  252. if (in_array($required, $data['engine-capabilities'])) {
  253. $engine_is_usable = TRUE;
  254. }
  255. }
  256. }
  257. }
  258. }
  259. if ($engine_is_usable) {
  260. $command['engines'][$engine_type]['usable'][] = $engine;
  261. if (!isset($data['hidden'])) {
  262. $option = $config['option'] . '=' . $engine;
  263. $engine_data['options'][$option]['description'] = array_key_exists('description', $data) ? $data['description'] : NULL;
  264. if ($combine_help) {
  265. $engine_data['options'][$option]['hidden'] = TRUE;
  266. if (drush_get_context('DRUSH_VERBOSE') || ($default == $engine) || !isset($data['verbose-only'])) {
  267. $combine_help_data[$engine] = $engine . ': ' . $data['description'];
  268. }
  269. }
  270. if (isset($data['options'])) {
  271. $engine_data['sub-options'][$option] = $data['options'];
  272. }
  273. if (isset($data['sub-options'])) {
  274. $engine_data['sub-options'] += $data['sub-options'];
  275. }
  276. }
  277. }
  278. }
  279. if (!empty($combine_help_data)) {
  280. $engine_selection_option = $config['option'];
  281. if (!is_array($engine_data['options'][$engine_selection_option])) {
  282. $engine_data['options'][$engine_selection_option] = array('description' => $config['options'][$engine_selection_option]);
  283. }
  284. if (drush_get_context('DRUSH_VERBOSE')) {
  285. $engine_data['options'][$engine_selection_option]['description'] .= "\n" . dt("All available values are:") . "\n - " . implode("\n - ", $combine_help_data) . "\n";
  286. }
  287. else {
  288. $engine_data['options'][$engine_selection_option]['description'] .= " " . dt("Available: ") . implode(', ', array_keys($combine_help_data)) . ". ";
  289. }
  290. $engine_data['options'][$engine_selection_option]['description'] .= dt("Default is !default.", array('!default' => $default));
  291. }
  292. else {
  293. // If the help options are not combined, then extend the
  294. // default engine description.
  295. $desc = $engine_info['engines'][$default]['description'];
  296. $engine_info['engines'][$default]['description'] = dt('Default !type engine.', array('!type' => $engine_type)) . ' ' . $desc;
  297. }
  298. }
  299. $command = array_merge_recursive($command, $engine_data);
  300. }
  301. }
  302. /**
  303. * Implementation of command hook for docs-output-formats
  304. */
  305. function drush_engine_topic_command($engine) {
  306. $engine_instances = drush_get_engines($engine);
  307. $option = $engine_instances['info']['option'];
  308. if (isset($engine_instances['info']['topic-file'])) {
  309. // To do: put this file next to the commandfile that defines the
  310. // engine type, not in the core docs directory.
  311. $docs_dir = drush_get_context('DOC_PREFIX', DRUSH_BASE_PATH);
  312. $path = $engine_instances['info']['topic-file'];
  313. $docs_file = (drush_is_absolute_path($path) ? '' : $docs_dir . '/') . $path;
  314. $doc_text = drush_html_to_text(file_get_contents($docs_file));
  315. }
  316. elseif (isset($engine_instances['info']['topic-text'])) {
  317. $doc_text = $engine_instances['info']['topic-text'];
  318. }
  319. else {
  320. return drush_set_error('DRUSH_BAD_ENGINE_TOPIC', dt("The engine !engine did not define its topic command correctly.", array('!engine' => $engine)));
  321. }
  322. // Look at each instance of the engine; if it has an html
  323. // file in the the 'topics' folder named after itself, then
  324. // include the file contents in the engine topic text.
  325. $instances = $engine_instances['engines'];
  326. ksort($instances);
  327. foreach ($instances as $instance => $config) {
  328. if (isset($config['description'])) {
  329. $doc_text .= "\n\n::: --$option=$instance :::\n" . $config['description'];
  330. $path = $config['path'] . '/topics/' . $instance . '.html';
  331. if (file_exists($path)) {
  332. $doc_text .= "\n" . drush_html_to_text(file_get_contents($path));
  333. }
  334. $additional_topic_text = drush_command_invoke_all('drush_engine_topic_additional_text', $engine, $instance, $config);
  335. if (!empty($additional_topic_text)) {
  336. $doc_text .= "\n\n" . implode("\n\n", $additional_topic_text);
  337. }
  338. }
  339. }
  340. // Write the topic text to a file so that is can be paged
  341. $file = drush_save_data_to_temp_file($doc_text);
  342. drush_print_file($file);
  343. }
  344. /**
  345. * Obtains the engine to use.
  346. *
  347. * Precedence:
  348. *
  349. * - user supplied engine via cli.
  350. * - default engine configured for the command.
  351. * - the first engine of all available.
  352. *
  353. * @see drush_find_engine_to_use().
  354. *
  355. * #TODO# clarify naming of functions and entrance point.
  356. */
  357. function drush_get_user_selected_engine(&$config, $engine_info) {
  358. $engines = array_keys($engine_info['engines']);
  359. $default = isset($config['default']) ? $config['default'] : current($engines);
  360. if (!empty($config['option'])) {
  361. $selected_engine = drush_get_option($config['option'], $default);
  362. }
  363. // Otherwise the default engine is the only option.
  364. else {
  365. $selected_engine = $default;
  366. }
  367. return $selected_engine;
  368. }
  369. /**
  370. * Returns a valid engine to use.
  371. *
  372. * If no preference is passed in $selected_engine, it will pick the
  373. * 'default' engine provided within the command config.
  374. *
  375. * $config is altered to add the engine info to $config['engine-info'].
  376. *
  377. * @see drush_get_user_selected_engine().
  378. *
  379. * #TODO# clarify naming of functions and entrance point.
  380. */
  381. function drush_find_engine_to_use(&$config, $engine_info, $selected_engine = NULL) {
  382. if (!isset($selected_engine)) {
  383. $selected_engine = $config['default'];
  384. }
  385. if (array_key_exists($selected_engine, $engine_info['engines'])) {
  386. $config['engine-info'] = $engine_info['engines'][$selected_engine];
  387. }
  388. return $selected_engine;
  389. }
  390. /**
  391. * Loads and validate an engine of the given type.
  392. */
  393. function drush_load_engine($type, $engine, $version = NULL, $path = NULL, $engine_config = NULL) {
  394. $engine_info = drush_get_engines($type);
  395. $selected_engine = drush_find_engine_to_use($engine_config, $engine_info, $engine);
  396. if (!array_key_exists($selected_engine, $engine_info['engines'])) {
  397. return drush_set_error('DRUSH_UNKNOWN_ENGINE_TYPE', dt('Unknown !engine_type engine !engine', array('!engine' => $selected_engine, '!engine_type' => $type)));
  398. }
  399. $result = drush_include_engine($type, $selected_engine, $version, $path, $engine_config);
  400. if (is_object($result)) {
  401. $valid = method_exists($result, 'validate') ? $result->validate() : TRUE;
  402. if ($valid) {
  403. drush_set_engine($type, $result);
  404. }
  405. }
  406. else {
  407. $function = strtr($type, '-', '_') . '_validate';
  408. $valid = function_exists($function) ? call_user_func($function) : TRUE;
  409. }
  410. if (!$valid) {
  411. return FALSE;
  412. }
  413. return $result;
  414. }
  415. /**
  416. * Include the engine code for a specific named engine of a certain type.
  417. *
  418. * If the engine type has implemented hook_drush_engine_$type the path to the
  419. * engine specified in the array will be used.
  420. *
  421. * If a class named in the form drush_$type_$engine exists, it will be an
  422. * object of that class will be created and returned.
  423. *
  424. * If you don't need to present any user options for selecting the engine
  425. * (which is common if the selection is implied by the running environment)
  426. * and you don't need to allow other modules to define their own engines you can
  427. * simply pass the $path to the directory where the engines are, and the
  428. * appropriate one will be included.
  429. *
  430. * Unlike drush_include this function will set errors if the requested engine
  431. * cannot be found.
  432. *
  433. * @param $type
  434. * The type of engine.
  435. * @param $engine
  436. * The key for the engine to be included.
  437. * @param $version
  438. * The version of the engine to be included - defaults to the current Drupal core
  439. * major version.
  440. * @param $path
  441. * A path to include from, if the engine has no corresponding
  442. * hook_drush_engine_$type item path.
  443. * @return TRUE or instanced object of available class on success. FALSE on fail.
  444. */
  445. function drush_include_engine($type, $selected_engine, $version = NULL, $path = NULL, $engine_config = NULL) {
  446. $engine_info = drush_get_engines($type);
  447. $engine = isset($engine_info['engines'][$selected_engine]['engine-class']) ? $engine_info['engines'][$selected_engine]['engine-class'] : $selected_engine;
  448. if (!$path && isset($engine_info['engines'][$engine])) {
  449. $path = $engine_info['engines'][$engine]['path'];
  450. }
  451. if (!$path) {
  452. return drush_set_error('DRUSH_ENGINE_INCLUDE_NO_PATH', dt('No path was set for including the !type engine !engine.', array('!type' => $type, '!engine' => $engine)));
  453. }
  454. if (drush_include($path, $engine, $version)) {
  455. $class = 'drush_' . $type . '_' . str_replace('-', '_', $engine);
  456. if (class_exists($class)) {
  457. $instance = new $class($engine_config);
  458. $instance->engine_type = $type;
  459. $instance->engine = $engine;
  460. $instance->selected_engine = $selected_engine;
  461. return $instance;
  462. }
  463. return TRUE;
  464. }
  465. return drush_set_error('DRUSH_ENGINE_INCLUDE_FAILED', dt('Unable to include the !type engine !engine from !path.' , array('!path' => $path, '!type' => $type, '!engine' => $engine)));
  466. }
  467. /**
  468. * Return the engine of the specified type that was loaded
  469. * by the Drush command.
  470. */
  471. function drush_get_engine($type) {
  472. return drush_get_context($type . '_engine', FALSE);
  473. }
  474. /**
  475. * Called by the Drush command (@see _drush_load_command_engines())
  476. * to cache the active engine instance.
  477. */
  478. function drush_set_engine($type, $instance) {
  479. drush_set_context($type . '_engine', $instance);
  480. }
  481. /**
  482. * Add engine topics to the command topics, if any.
  483. */
  484. function drush_engine_add_help_topics(&$command) {
  485. $engine_types = drush_get_engine_types_info();
  486. foreach ($command['engines'] as $engine_type => $config) {
  487. $info = $engine_types[$engine_type];
  488. if (isset($info['topics'])) {
  489. $command['topics'] = array_merge($command['topics'], $info['topics']);
  490. }
  491. if (isset($info['topic'])) {
  492. $command['topics'][] = $info['topic'];
  493. }
  494. }
  495. }