View source
<?php
include_once dirname(__FILE__) . '/../includes/common.inc';
define('MODULE_BUILDER_ENV', 'drush');
function module_builder_drush_command() {
$items = array();
$items['mb-build'] = array(
'callback' => 'module_builder_callback_build',
'description' => "Generate the code for a new Drupal module, including file headers and hook implementations.",
'arguments' => array(
'module name' => 'The machine name of the module.',
'hooks' => 'Short names of hooks, separated by spaces.',
),
'aliases' => array(
'mb',
),
'options' => array(
'--data' => "Location to read hook data. May be absolute, or relative to Drupal files dir. Defaults to 'files/hooks'.",
'--build' => "Which file type to generate: 'all', 'code', 'info', 'FILE'. " . "'all' generates everything: info and any code files needed by the requested hooks. " . "'code' generates code files as needed. " . "'info' makes just the info file. " . "'module', 'install' make just the foo.module or foo.install files. " . "'If custom modules define other files to output, you can request those too, omitting the module root name part and any .inc extension, eg 'module_builder' for 'foo.module_builder.inc. " . "Default is 'all' if writing new files, 'code' if appending to file or outputting only to terminal.",
'--write' => 'Write files to sites/all/modules. Will prompt to overwrite existing files; use --yes to force. Use --quiet to suppress output to the terminal.',
'--go' => 'Write all module files and enable the new module. Take two commands into the shower? Not me.',
'--add' => "Append hooks to module file. Implies '--write --build=code'. Warning: will not check hooks already exist.",
'--name' => 'Readable name of the module.',
'--desc' => 'Description (for the admin module list).',
'--help' => 'Module help text (for the system help).',
'--dep' => 'Dependencies, separated by spaces, eg "forum views".',
'--package' => 'Module package.',
'--parent' => "Name of a module folder to place this new module into; use if this module is to be added to an existing package. Use '.' for the current working directory.",
),
'examples' => array(
'drush mb my_module menu cron nodeapi' => 'Generate module code with hook_menu, hook_cron, hook_nodeapi.',
'drush mb my_module --build=info --name="My module" --dep="forum views"' => 'Generate module info with readable name and dependencies.',
'drush mb my_module menu cron --write --name="My module" --dep="forum views"' => 'Generate both module files, write files and also output to terminal.',
'drush mb my_module menu cron --write ' => 'Generate module code, write files and also output to terminal.',
'drush mb my_module menu cron --write --quiet --name="My module" --dep="forum views"' => 'Generate both module files, write files and output nothing to terminal.',
'drush mb my_module menu cron --add' => 'Generate code for hook_cron and add it to the existing my_module.module file.',
'drush mb my_module menu cron --write --parent=cck' => 'Generate both module files, write files to a folder my_module inside the cck folder.',
'drush mb my_module menu cron --write --parent=.' => 'Generate both module files, write files to a folder my_module in the current working directory.',
),
);
$items['mb-download'] = array(
'callback' => 'module_builder_callback_hook_download',
'description' => "Update module_builder hook data.",
'options' => array(
'--data' => "Location to save downloaded files. May be absolute, or relative to Drupal files dir. Defaults to 'files/hooks'.",
),
'aliases' => array(
'mbdl',
),
);
$items['mb-list'] = array(
'callback' => 'module_builder_callback_hook_list',
'description' => "List the hooks module_builder knows about.",
);
$items['mb-dochooks'] = array(
'callback' => 'module_builder_callback_doc_hooks',
'description' => "Adds comment headers to hooks that need them in the given module.",
);
$items['mb-docparams'] = array(
'callback' => 'module_builder_callback_doc_params',
'description' => "Adds params... WIP!",
);
$items['mb-debug'] = array(
'callback' => 'module_builder_callback_debug',
'description' => "Debug module builder. Does whatever was needed at the time.",
);
return $items;
}
function module_builder_drush_help($section) {
switch ($section) {
case 'drush:mb-build':
return dt("Generates module code with the specified hooks.");
}
}
function module_builder_callback_build() {
$commands = func_get_args();
$module_data = module_builder_build_data($commands);
$build = drush_get_option('build');
if (!$build) {
if (drush_get_option('add')) {
$build = 'code';
}
elseif (drush_get_option(array(
'write',
'go',
))) {
$build = 'all';
}
else {
$build = 'code';
}
}
$build_list = explode(' ', $build);
if (count($build_list) > 1) {
$build = 'code';
}
module_builder_include('generate');
if ($build != 'info') {
if (!_module_builder_check_hook_data()) {
return drush_set_error("DRUSH_NOT_COMPLETED', 'No hook definitions found. You need to download hook definitions before using this module: see the command 'mbdl'.");
}
module_builder_callback_module($commands, $module_data, $build_list);
}
if ($build == 'info' or $build == 'all') {
module_builder_callback_info($commands, $module_data);
}
if (drush_get_option('go')) {
pm_module_manage(array(
array_shift($commands),
), TRUE);
}
}
function module_builder_build_data($commands) {
$module_data['module_root_name'] = array_shift($commands);
$module_data['module_readable_name'] = drush_get_option('name');
$module_data['module_short_description'] = drush_get_option('desc');
$module_data['module_help_text'] = drush_get_option('help');
$module_data['module_dependencies'] = drush_get_option('dep');
$module_data['module_package'] = drush_get_option('package');
return $module_data;
}
function module_builder_callback_module($commands, &$module_data, $build_list) {
array_shift($commands);
foreach ($commands as $hook_name) {
$module_data['hooks']["hook_{$hook_name}"] = TRUE;
}
$module_code = module_builder_generate_module($module_data, drush_get_option('add'));
if (is_null($module_code)) {
return drush_set_error('DRUSH_NOT_COMPLETED', 'No module code has been generated: perhaps you have specified invalid hook names or hooks this module does not know about.');
}
if (!in_array($build_list[0], array(
'code',
'all',
))) {
$requested_files = module_builder_requested_filenames($module_data['module_root_name'], array_keys($module_code), $build_list);
}
else {
$requested_files = array_keys($module_code);
}
foreach ($requested_files as $filename) {
$code = $module_code[$filename];
module_builder_drush_output_code($module_data['module_root_name'], $filename, $code);
}
return;
}
function module_builder_requested_filenames($module_root_name, $real, $abbrev) {
foreach ($real as $r) {
$p = preg_replace(array(
"[^{$module_root_name}\\.]",
'[\\.inc$]',
), array(
'',
'',
), $r);
$processed[$r] = $p;
}
$result = array_intersect($processed, $abbrev);
return array_keys($result);
}
function module_builder_callback_info($commands, $module_data) {
module_builder_include('generate_info');
$info_code = module_builder_generate_info($module_data);
module_builder_drush_output_code($module_data['module_root_name'], $module_data['module_root_name'] . '.info', $info_code);
}
function module_builder_drush_output_code($module_root_name, $filename, $code) {
if (!drush_get_option('quiet')) {
drush_print("Proposed {$filename}:");
drush_print_r($code);
}
$write = drush_get_option('write');
if (drush_get_option(array(
'write',
'add',
'go',
))) {
$module_dir = pm_dl_destination('module');
if (drush_get_option('parent')) {
$parent_dir = drush_get_option('parent');
if (substr($parent_dir, 0, 1) == '.') {
$parent_dir = substr($parent_dir, 1);
$module_dir = drush_get_context('DRUSH_OLDCWD') . '/' . $parent_dir . '/';
}
else {
$module_dir .= $parent_dir;
}
}
$module_dir .= $module_root_name;
if (!is_dir($module_dir)) {
@drush_op('mkdir', $module_dir, 0777);
}
$filepath = $module_dir . '/' . $filename;
if (drush_get_option('add') && file_exists($filepath)) {
$fh = fopen($filepath, 'a');
fwrite($fh, $code);
fclose($fh);
return;
}
if (file_exists($filepath)) {
if (!drush_confirm(dt('File ' . $filename . ' exists. Do you really want to overwrite?'))) {
return;
}
}
file_put_contents($filepath, $code);
}
}
function drush_input($msg, $required = FALSE, $indent = 0) {
print str_repeat(' ', $indent) . (string) $msg . ": ";
while ($line = trim(fgets(STDIN))) {
if (!$required or strlen($line) > 0) {
return $line;
}
print 'we never get here wtf?';
print str_repeat(' ', $indent) . (string) $msg . ": ";
}
}
function module_builder_callback_hook_download() {
$directory = _module_builder_get_hooks_directory();
$return = module_builder_update_data();
if (!$return) {
return drush_set_error('Problem downloading hooks.');
}
else {
drush_print("Hook files have been downloaded to {$directory} and processed.");
}
}
function module_builder_callback_hook_list() {
module_builder_include('process');
$data = module_builder_get_hook_data();
$time = module_builder_get_hook_data_last_updated();
foreach ($data as $file => $hooks) {
drush_print("Group {$file}:", 2);
foreach ($hooks as $key => $hook) {
drush_print($hook['name'] . ': ' . $hook['description'], 4);
}
}
drush_print(t("Hook data retrieved from @dir.", array(
'@dir' => _module_builder_get_hooks_directory(),
)));
drush_print(t("Hook data was processed on @time.", array(
'@time' => $time,
)));
}
function module_builder_callback_doc_hooks() {
$commands = func_get_args();
$module_root_name = array_shift($commands);
$filepath = drupal_get_path('module', $module_root_name);
$files = scandir($filepath);
foreach ($files as $filename) {
$ext = substr(strrchr($filename, '.'), 1);
if (in_array($ext, array(
'module',
'install',
'inc',
))) {
$module_files[] = $filename;
}
}
module_builder_include('process');
module_builder_include('generate');
$hook_names = module_builder_get_hook_names('short');
$pattern = '[(?<! \\* / \\n )' . "function \\ image_gallery _ ( \\w * ) # function declaration: capture hook name\n ]mx";
foreach ($module_files as $filename) {
$code = file_get_contents($filepath . '/' . $filename);
preg_match_all($pattern, $code, $function_names);
$bad_hooks = array_intersect($function_names[1], $hook_names);
foreach ($bad_hooks as $hook_name) {
$doc = module_builder_generate_hook_doxy("hook_{$hook_name}");
$pattern2 = "[(?= function \\ image_gallery _ {$hook_name} )]x";
$code = preg_replace($pattern2, $doc, $code);
}
if (!drush_get_option('quiet')) {
print $code;
}
print 'Added hook documentation headers for: ' . implode(', ', $bad_hooks) . "\n";
if (!drush_confirm(dt('Are you sure you want to overwrite ' . $filename . '?'))) {
continue;
}
file_put_contents($filepath . '/' . $filename, $code);
}
}
function module_builder_callback_doc_params() {
$commands = func_get_args();
print 'wip!!!';
return;
$module_root_name = array_shift($commands);
$filepath = drupal_get_path('module', $module_root_name);
$files = scandir($filepath);
foreach ($files as $filename) {
$ext = substr(strrchr($filename, '.'), 1);
if (in_array($ext, array(
'module',
'install',
'inc',
))) {
$module_files[] = $filename;
}
}
module_builder_include('process');
module_builder_include('generate');
$hook_names = module_builder_get_hook_names('short');
$pattern = '[
/ \\* \\* \\n # start phpdoc
\\ \\* \\ ( .* ) \\n # first line of phpdoc: capture the text
(?: \\ \\* .* \\n )* # lines of phpdoc
\\ \\* / \\n # end phpdoc
function \\ ( \\w* ) \\( ( .* ) \\) \\ { # function declaration: capture both entire declaration and name
]mx';
foreach ($module_files as $filename) {
$code = file_get_contents($filepath . '/' . $filename);
preg_match_all($pattern, $code, $function_names);
foreach ($bad_hooks as $hook_name) {
$doc = module_builder_generate_hook_doxy("hook_{$hook_name}");
$pattern2 = "[(?= function \\ image_gallery _ {$hook_name} )]x";
$code = preg_replace($pattern2, $doc, $code);
}
if (!drush_get_option('quiet')) {
}
print 'Added hook documentation headers for: ' . implode(', ', $bad_hooks) . "\n";
if (!drush_confirm(dt('Are you sure you want to overwrite ' . $filename . '?'))) {
continue;
}
}
}
function module_builder_callback_debug() {
return;
}