View source
<?php
if (module_exists('drush')) {
include drupal_get_path('module', 'coder_review') . '/coder_review.drush.inc';
}
define('SEVERITY_MINOR', 1);
define('SEVERITY_NORMAL', 5);
define('SEVERITY_CRITICAL', 9);
function coder_review_flush_caches() {
return array(
'cache_coder',
);
}
function coder_review_help($page, $arg) {
switch ($page) {
case 'coder_review#disclaimer':
return t('Coder provides helpful hints without false positives, but offers no guarantee for creating good code. You are the final arbitrar. If in doubt, read the Drupal documentation (see review links below and <a href="@api">api.drupal.org</a>).', array(
'@api' => 'http://api.drupal.org',
));
case 'drush:coder_review':
return t('coder_review [summary] [@reviews] [minor|major|critical] [active|core|all|default|<modules>] [no-<module>]', array(
'@reviews' => implode('|', array_keys(_coder_review_reviews())),
));
}
}
function _coder_review_reviews() {
return module_invoke_all('reviews');
}
function coder_review_reviews() {
global $_coder_reviews;
if (!isset($_coder_reviews)) {
$_coder_reviews = array();
$path = drupal_get_path('module', 'coder_review') . '/includes';
$files = drupal_system_listing('/coder_review_.*\\.inc$/', $path, 'filepath', 0);
foreach ($files as $file) {
require_once DRUPAL_ROOT . '/' . $file->uri;
$function = $file->name . '_reviews';
if (function_exists($function)) {
if ($review = call_user_func($function)) {
$_coder_reviews = array_merge($_coder_reviews, $review);
}
}
}
}
return $_coder_reviews;
}
function coder_review_permission() {
return array(
'view code review' => array(
'title' => t('View code review'),
'description' => t('Perform code reviews on modules and themes.'),
),
'view code review all' => array(
'title' => t('View all code reviews'),
'description' => t('Perform code reviews on modules and themes, using the "all" modules feature.'),
),
);
}
function coder_review_menu() {
$items['admin/config/development/coder/review'] = array(
'title' => 'Review',
'page callback' => 'coder_review_page',
'access arguments' => array(
'view code review',
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -2,
);
$items['admin/config/development/coder/review/select'] = array(
'title' => 'Select',
'page callback' => 'coder_review_page',
'access arguments' => array(
'view code review',
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -2,
);
$items['admin/config/development/coder/review/default'] = array(
'title' => 'Default',
'page callback' => 'coder_review_page',
'page arguments' => array(
'default',
),
'access arguments' => array(
'view code review',
),
'type' => MENU_LOCAL_TASK,
'weight' => -1,
);
$items['admin/config/development/coder/review/core'] = array(
'title' => 'Core',
'page callback' => 'coder_review_page',
'page arguments' => array(
'core',
),
'access arguments' => array(
'view code review',
),
'type' => MENU_LOCAL_TASK,
);
$items['admin/config/development/coder/review/active'] = array(
'title' => 'Active',
'page callback' => 'coder_review_page',
'page arguments' => array(
'active',
),
'access arguments' => array(
'view code review',
),
'type' => MENU_LOCAL_TASK,
);
$items['admin/config/development/coder/review/all'] = array(
'title' => 'All',
'page callback' => 'coder_review_page',
'page arguments' => array(
'all',
),
'access arguments' => array(
'view code review all',
),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
);
$items['admin/config/development/coder/review/files'] = array(
'title' => 'Files',
'page callback' => 'coder_review_page',
'page arguments' => array(
'files',
),
'access arguments' => array(
'view code review',
),
'type' => MENU_LOCAL_TASK,
'weight' => 2,
);
$items['admin/config/development/coder/review/patches'] = array(
'title' => 'Patches',
'page callback' => 'coder_review_page',
'page arguments' => array(
'patches',
),
'access arguments' => array(
'view code review',
),
'type' => MENU_LOCAL_TASK,
'weight' => 3,
);
$items['admin/config/development/coder/review/settings'] = array(
'title' => 'Settings',
'description' => 'Select code review plugins and modules',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'coder_review_admin_settings',
),
'access arguments' => array(
'administer site configuration',
),
'type' => MENU_LOCAL_TASK,
'weight' => 3,
);
return $items;
}
function coder_review_menu_alter(&$items) {
$items['admin/config/development/coder']['page callback'] = 'coder_review_page';
}
function coder_review_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'system_modules' && (!isset($form['#theme']) || $form['#theme'] != 'confirm_form')) {
if (user_access('view code review')) {
$form['#attached']['css'] = array(
drupal_get_path('module', 'coder_review') . '/coder_review.css',
);
foreach ($form['modules'] as $package => $modules) {
if (!preg_match('/^#/', $package)) {
foreach ($modules as $name => $data) {
if (!preg_match('/^#/', $name)) {
$description = $data['description']['#markup'];
$form['modules'][$package][$name]['description']['#markup'] = $description . ' (' . l(t('Code review'), "admin/config/development/coder/review/{$name}") . ')';
}
}
}
}
}
}
}
function _coder_review_default_reviews() {
return drupal_map_assoc(array(
'style',
'sql',
'comment',
'security',
'i18n',
));
}
function _coder_review_settings_form($settings, &$system, &$files) {
$form['#attached']['js'] = array(
drupal_get_path('module', 'coder_review') . '/coder_review.js',
);
$reviews = _coder_review_reviews();
foreach ($reviews as $name => $review) {
$review_options[$name] = isset($review['#link']) ? l($review['#title'], $review['#link']) : $review['#title'];
if (isset($review['#description'])) {
$review_options[$name] .= ' (' . $review['#description'] . ')';
}
$review_sort[$name] = $review['#title'];
}
asort($review_sort);
foreach ($review_sort as $name => $review) {
$review_sort[$name] = $review_options[$name];
}
$form['coder_reviews_group'] = array(
'#type' => 'fieldset',
'#title' => t('Reviews'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['coder_reviews_group']['coder_reviews'] = array(
'#type' => 'checkboxes',
'#options' => $review_sort,
'#description' => t('apply the checked coding reviews'),
'#default_value' => $settings['coder_reviews'],
);
$form['coder_reviews_group']['coder_severity'] = array(
'#type' => 'radios',
'#options' => array(
SEVERITY_MINOR => 'minor (most)',
SEVERITY_NORMAL => 'normal',
SEVERITY_CRITICAL => 'critical (fewest)',
),
'#description' => t('show warnings at or above the severity warning level'),
'#default_value' => $settings['coder_severity'],
);
if (!empty($settings['coder_files'])) {
$form['coder_what'] = array(
'#type' => 'fieldset',
'#title' => t('What to review'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['coder_what']['coder_file_list'] = array(
'#type' => 'textarea',
'#title' => t('List of files'),
'#description' => t('List each relative file path on a separate line.'),
'#default_value' => isset($settings['coder_file_list']) ? $settings['coder_file_list'] : '',
);
$form['coder_files'] = array(
'#type' => 'value',
'#value' => 1,
);
if (!empty($settings['coder_file_list'])) {
$lines = explode("\n", $settings['coder_file_list']);
foreach ($lines as $line) {
$line = trim($line);
$system[$line] = $line;
$files[$line] = $line;
}
}
}
elseif (!empty($settings['coder_patches'])) {
$form['coder_what'] = array(
'#type' => 'fieldset',
'#title' => t('What to review'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['coder_what']['coder_patch_help'] = array(
'#markup' => '<p>' . t('All patches must be in unified format. It\'s also preferable for the patch to be created with the "-p" option, which shows the function closest to the code change. Without this, some function dependent tests may not be triggered. Coder offers no guarantee that the patch will apply cleanly or will function correctly.') . '</p>',
'#weight' => -4,
);
$form['coder_what']['coder_patch_link'] = array(
'#type' => 'textfield',
'#title' => t('Link to patch'),
'#default_value' => isset($settings['coder_patch_link']) ? $settings['coder_patch_link'] : '',
'#weight' => -3,
);
$form['coder_what']['coder_patch_help'] = array(
'#markup' => '<p>' . t('Or') . '</p>',
'#weight' => -2,
);
$form['coder_what']['coder_patch_text'] = array(
'#type' => 'textarea',
'#title' => t('Patch text'),
'#rows' => 20,
'#weight' => -1,
'#default_value' => isset($settings['coder_patch_text']) ? $settings['coder_patch_text'] : '',
'#attributes' => array(
'wrap' => 'OFF',
),
);
$form['coder_patches'] = array(
'#type' => 'value',
'#value' => 1,
);
$in_patch = 0;
$patch = $link_contents = $textarea_contents = '';
$patches = array();
if (!empty($settings['coder_patch_link'])) {
$link_contents = file_get_contents($settings['coder_patch_link']);
}
if (!empty($settings['coder_patch_text'])) {
$textarea_contents = $settings['coder_patch_text'];
}
$patch_contents = $link_contents . "\n" . $textarea_contents;
$lines = preg_split("/(\r\n|\n)/", $patch_contents);
foreach ($lines as $line) {
if ($line == '') {
continue;
}
if (preg_match("/^\\+\\+\\+\\s+([\\w\\.\\-\\/]+)\\s*.*?\$/", $line, $matches)) {
if (!empty($patch)) {
$patches[$filename . ': ' . $patch_line_numbers] = $patch;
$system[$filename . ': ' . $patch_line_numbers] = $filename;
$patch = '';
}
$filename = $matches[1];
}
elseif (preg_match("/^(@@\\s+\\-\\d+,\\d+\\s+\\+\\d+,\\d+\\s+@@)\\s*((function|(protected|private|public)*\\s*class)\\s([\\w]+).*?)*\$/", $line, $matches)) {
if (!empty($patch)) {
$patches[$filename . ': ' . $patch_line_numbers] = $patch;
$system[$filename . ': ' . $patch_line_numbers] = $filename;
$patch = '';
}
if (isset($matches[3]) && isset($matches[4])) {
if ($matches[3] == 'function') {
$function_current = $matches[4];
$patch = 'function ' . $function_current . "() {\n";
}
else {
$class_current = $matches[4];
$patch = $matches[2] . "\n";
}
}
$patch_line_numbers = $matches[1];
}
elseif (preg_match("/^\\+(?!\\+)/", $line)) {
$patch .= ltrim($line, '+') . "\n";
$in_patch = 1;
}
elseif (preg_match("/^\\s/", $line)) {
$patch .= drupal_substr($line, 1) . "\n";
$in_patch = 1;
}
else {
$in_patch = 0;
}
}
if (!empty($patch)) {
$patches[$filename . ': ' . $patch_line_numbers] = $patch;
$system[$filename . ': ' . $patch_line_numbers] = $filename;
$patch = '';
}
$files = $patches;
}
else {
$sql = 'SELECT name, filename, type, status FROM {system} WHERE type=\'module\' OR type=\'theme\' ORDER BY weight ASC, filename ASC';
$result = db_query($sql);
$system_modules = array();
$system_themes = array();
foreach ($result as $system) {
$display_name = $system->name;
if ($system->status) {
$display_name .= ' (' . t('active') . ')';
$system_active[$system->name] = $system->name;
}
if (_coder_review_is_drupal_core($system)) {
$display_name .= ' (' . t('core') . ')';
$system_core[$system->name] = $system->name;
}
if ($system->type == 'module') {
$system_modules[$system->name] = $system->name;
}
else {
$system_themes[$system->name] = $system->name;
}
$system_links[$system->name] = l($display_name, "admin/config/development/coder/review/{$system->name}");
$files[$system->name] = $system->filename;
}
asort($system_links);
$form['coder_what'] = array(
'#type' => 'fieldset',
'#title' => t('What to review'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['coder_what']['coder_active_modules'] = array(
'#type' => 'checkbox',
'#default_value' => isset($settings['coder_active_modules']) ? $settings['coder_active_modules'] : 0,
'#title' => t('active modules and themes'),
);
$form['coder_what']['coder_core'] = array(
'#type' => 'checkbox',
'#default_value' => isset($settings['coder_core']) ? $settings['coder_core'] : 0,
'#title' => t('core files (php, modules, and includes)'),
);
$ext = variable_get('coder_review_php_ext', array(
'inc',
'php',
'install',
'test',
));
$form['coder_what']['coder_includes'] = array(
'#type' => 'checkbox',
'#default_value' => isset($settings['coder_includes']) ? $settings['coder_includes'] : 0,
'#title' => t('include files (@ext)', array(
'@ext' => implode(' | ', $ext),
)),
);
$form['coder_what']['coder_includes_exclusion_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t('Include file exclusions'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['coder_what']['coder_includes_exclusion_fieldset']['coder_includes_exclusions'] = array(
'#rows' => 3,
'#type' => 'textarea',
'#default_value' => isset($settings['coder_includes_exclusions']) ? $settings['coder_includes_exclusions'] : '',
'#description' => t('List file names or paths, one per line, which should be excluded (only valid if "include files" is checked above). For example, modules/system/*.php will exclude all php files in the modules/system directory.'),
);
$form['coder_what']['coder_modules'] = array(
'#type' => 'fieldset',
'#title' => t('Select specific modules'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'checkboxes' => array(
'#theme' => 'coder_review_table_cols',
'#cols' => 2,
),
);
if (isset($settings['coder_all'])) {
$modules = $system_modules;
}
elseif (isset($settings['coder_active_modules']) && $settings['coder_active_modules']) {
if (isset($settings['coder_core']) && $settings['coder_core']) {
$modules = array_intersect($system_active, $system_core);
$modules = array_intersect($modules, $system_modules);
}
else {
$modules = array_intersect($system_active, $system_modules);
}
}
elseif (isset($settings['coder_core']) && $settings['coder_core']) {
$modules = array_intersect($system_core, $system_modules);
}
elseif (isset($settings['coder_active_modules']) && $settings['coder_active_modules']) {
$modules = array_intersect($system_active, $system_modules);
}
elseif (isset($settings['coder_contrib']) && $settings['coder_contrib']) {
$modules = array_diff($system_modules, array_intersect($system_core, $system_modules));
}
else {
$modules = isset($settings['coder_modules']) && is_array($settings['coder_modules']) ? $settings['coder_modules'] : array();
}
$form['coder_what']['coder_themes'] = array(
'#type' => 'fieldset',
'#title' => t('Select specific themes'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'checkboxes' => array(
'#theme' => 'coder_review_table_cols',
),
);
if (isset($settings['coder_all'])) {
$themes = $system_themes;
}
elseif (isset($settings['coder_active_modules']) && $settings['coder_active_modules']) {
if (isset($settings['coder_core']) && $settings['coder_core']) {
$themes = array_intersect($system_active, $system_core);
$themes = array_intersect($themes, $system_themes);
}
else {
$themes = array_intersect($system_active, $system_themes);
}
}
elseif (isset($settings['coder_core']) && $settings['coder_core']) {
$themes = array_intersect($system_core, $system_themes);
}
elseif (isset($settings['coder_active_modules']) && $settings['coder_active_modules']) {
$themes = array_intersect($system_active, $system_themes);
}
elseif (isset($settings['coder_contrib']) && $settings['coder_contrib']) {
$themes = array_diff($system_themes, array_intersect($system_core, $system_themes));
}
else {
$themes = isset($settings['coder_themes']) && is_array($settings['coder_themes']) ? $settings['coder_themes'] : array();
}
foreach ($system_links as $name => $link) {
$classes = array();
if (in_array($name, $system_active)) {
$classes[] = 'coder-active';
}
if (in_array($name, $system_core)) {
$classes[] = 'coder-core';
}
if (in_array($name, $system_themes)) {
$type = 'theme';
$default_value = isset($themes[$name]);
}
else {
$type = 'module';
$default_value = isset($modules[$name]);
}
$form['coder_what']["coder_{$type}s"]['checkboxes']["coder_{$type}s-{$name}"] = array(
'#type' => 'checkbox',
'#title' => $link,
'#default_value' => $default_value,
'#attributes' => array(
'class' => $classes,
),
);
}
$system = array_merge($modules, $themes);
}
return $form;
}
function theme_coder_review_table_cols($variables) {
if (!isset($variables['form']) || !is_array($variables['form'])) {
return '';
}
$form = $variables['form'];
$total = 0;
$cols = isset($form['#cols']) ? $form['#cols'] : 3;
foreach ($form as $element_id => $element) {
if ($element_id[0] != '#') {
$total++;
}
}
$total = (int) ($total % $cols ? ($total + $cols - 1) / $cols : $total / $cols);
$pos = 0;
$rows = array();
foreach ($form as $element_id => $element) {
if ($element_id[0] != '#') {
$pos++;
$row = $pos % $total;
$col = $pos / $total;
if (!isset($rows[$row])) {
$rows[$row] = array();
}
$rows[$row][$col] = drupal_render($element);
}
}
$output = theme('table', array(
'rows' => $rows,
'attributes' => array(
'id' => 'filter-order',
),
));
return $output;
}
function coder_review_admin_settings($form, &$form_state) {
$settings = _coder_review_get_default_settings();
$form = _coder_review_settings_form($settings, $system, $files);
$form['help'] = array(
'#type' => 'markup',
'#markup' => t('After setting these defaults, use <a href="@url">coder review</a> to perform code reviews.', array(
'@url' => url('admin/config/development/coder/review'),
)),
'#weight' => -1,
);
$form['#submit'][] = 'coder_review_settings_form_submit';
return system_settings_form($form);
}
function coder_review_settings_form_submit($form, &$form_state) {
$form_state['storage'] = $form_state['values'];
variable_set('coder_modules', _coder_review_settings_array($form_state, 'module'));
variable_set('coder_themes', _coder_review_settings_array($form_state, 'theme'));
}
function _coder_review_settings_array(&$form_state, $type) {
$typekey = "coder_{$type}s-";
$typelen = drupal_strlen($typekey);
$systems = array();
foreach ($form_state['storage'] as $key => $value) {
if (drupal_substr($key, 0, $typelen) == $typekey) {
if ($value == 1) {
$system = drupal_substr($key, $typelen);
$systems[$system] = 1;
}
unset($form_state['storage'][$key]);
unset($form_state['values'][$key]);
}
}
return $systems;
}
function coder_review_page($type = '') {
$form = drupal_get_form('coder_review_page_form', $type);
$output = '<div class="coder-disclaimer">' . coder_review_help('coder#disclaimer', array()) . '</div>';
$output .= drupal_render($form);
return $output;
}
function _coder_review_get_default_settings($arg = 'default') {
$settings['coder_reviews'] = variable_get('coder_reviews', _coder_review_default_reviews());
$settings['coder_severity'] = variable_get('coder_severity', SEVERITY_NORMAL);
switch ($arg) {
case 'settings':
$settings['coder_includes'] = 1;
break;
case 'active':
$settings['coder_active_modules'] = 1;
break;
case 'core':
$settings['coder_core'] = 1;
$settings['coder_includes'] = 1;
break;
case 'all':
$settings['coder_core'] = 1;
$settings['coder_includes'] = 1;
$settings['coder_all'] = 1;
break;
case 'contrib':
$settings['coder_includes'] = 1;
$settings['coder_contrib'] = 1;
break;
case 'files':
$settings['coder_files'] = 1;
break;
case 'patches':
$settings['coder_patches'] = 1;
break;
case 'default':
$settings['coder_active_modules'] = variable_get('coder_active_modules', 1);
$settings['coder_core'] = variable_get('coder_core', 0);
$settings['coder_includes'] = variable_get('coder_includes', 0);
$settings['coder_includes_exclusions'] = variable_get('coder_includes_exclusions', '');
$settings['coder_modules'] = variable_get('coder_modules', array());
$settings['coder_themes'] = variable_get('coder_themes', array());
break;
default:
$settings['coder_includes'] = 1;
$settings['coder_includes_exclusions'] = variable_get('coder_includes_exclusions', '');
$settings['coder_modules'] = array(
$arg => $arg,
);
break;
}
return $settings;
}
function coder_review_page_form_submit($form, &$form_state) {
$form_state['storage'] = $form_state['values'];
$form_state['rebuild'] = TRUE;
}
function _coder_review_get_reviews_extensions($defaults, $reviews) {
$extensions = array();
foreach ($reviews as $review) {
foreach ($review['#rules'] as $rule) {
if (isset($rule['#filename'])) {
foreach ($rule['#filename'] as $ext) {
if (!in_array($ext, $defaults) && !in_array($ext, $extensions)) {
$extensions[] = $ext;
}
}
}
}
}
return $extensions;
}
function coder_review_page_form($form, &$form_state, $arg = '') {
if (isset($form_state['storage'])) {
$settings = $form_state['storage'];
$settings['coder_modules'] = _coder_review_settings_array($form_state, 'module');
$settings['coder_themes'] = _coder_review_settings_array($form_state, 'theme');
drupal_set_title(t('Code review (submitted options)'), PASS_THROUGH);
}
else {
$settings = _coder_review_get_default_settings($arg);
if ($arg) {
drupal_set_title(t('Code review (@options)', array(
'@options' => isset($arg) ? $arg : 'default options',
)), PASS_THROUGH);
}
}
$reviews = array();
$avail_reviews = _coder_review_reviews();
$selected_reviews = $settings['coder_reviews'];
foreach ($selected_reviews as $name => $checked) {
if ($checked) {
$reviews[$name] = $avail_reviews[$name];
}
}
if ($coder_form = _coder_review_settings_form($settings, $system, $files)) {
$coder_includes_exclusions = isset($settings['coder_includes_exclusions']) ? $settings['coder_includes_exclusions'] : '';
$ignores = coder_review_get_ignores();
$module_weight = 0;
if (isset($settings['coder_core']) && $settings['coder_core']) {
$php_extensions = variable_get('coder_review_php_ext', array(
'inc',
'php',
'install',
'test',
));
$coder_args = array(
'#reviews' => $reviews,
'#severity' => $settings['coder_severity'],
'#php_extensions' => $php_extensions,
'#include_extensions' => array(),
'#cache' => TRUE,
);
drupal_alter('coder_review_args', $coder_args);
$form['core_php'] = array(
'#type' => 'fieldset',
'#title' => 'core (php)',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => ++$module_weight,
);
$phpfiles = file_scan_directory('.', '/.*\\.php/', array(
'recurse' => FALSE,
'key' => 'name',
));
_coder_review_page_form_includes($form, $coder_args, 'core_php', $phpfiles, 2, 0, $coder_includes_exclusions, $ignores);
$form['core_includes'] = array(
'#type' => 'fieldset',
'#title' => 'core (includes)',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => ++$module_weight,
);
$includefiles = drupal_system_listing('/.*\\.inc$/', 'includes', 'filepath', 0);
_coder_review_page_form_includes($form, $coder_args, 'core_includes', $includefiles, 0, $coder_includes_exclusions, $ignores);
}
if (isset($system)) {
$php_extensions = variable_get('coder_review_php_ext', array(
'inc',
'php',
'install',
'test',
));
$include_extensions = _coder_review_get_reviews_extensions($php_extensions, $reviews);
$includes = array_merge($php_extensions, $include_extensions);
$dups = array();
$stats = array();
$patch = '';
foreach ($system as $name => $checked) {
if ($checked) {
$filename = isset($files[$name]) ? $files[$name] : FALSE;
if (!$filename) {
drupal_set_message(t('Code review file for %module not found', array(
'%module' => $name,
)), 'error');
continue;
}
if (!empty($settings['coder_patches'])) {
$patch = $filename;
$filename = $name;
}
$coder_args = array(
'#reviews' => $reviews,
'#severity' => $settings['coder_severity'],
'#filename' => $filename,
'#patch' => $patch,
'#php_extensions' => $php_extensions,
'#include_extensions' => $include_extensions,
'#ignore_lines' => isset($ignores[$filename]) && is_array($ignores[$filename]) ? $ignores[$filename] : array(),
'#cache' => TRUE,
);
drupal_alter('coder_review_args', $coder_args);
$results = do_coder_reviews($coder_args);
$stats[$filename] = $results['#stats'];
unset($results['#stats']);
module_invoke_all('coder_review_results_view', $results);
$form[$name] = array(
'#type' => 'fieldset',
'#title' => $filename,
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => ++$module_weight,
);
if (empty($results)) {
$results[] = t('No Problems Found');
}
else {
$form[$name]['#collapsed'] = FALSE;
}
$form[$name]['output'] = _coder_review_form_output($name, $filename, $results);
$form[$name]['output']['#weight'] = -1;
if (!empty($settings['coder_includes'])) {
if ($path = str_replace('\\', '/', dirname(realpath($filename)))) {
if (!isset($dups[$path])) {
if (drupal_substr($filename, -7) == '.module') {
$coder_args['#php_minor'] = 1;
}
$dups[$path] = 1;
$regex = '/\\.(' . implode('|', $includes) . ')$/';
$includefiles = drupal_system_listing($regex, dirname($filename), 'filepath', 0);
$offset = strpos($filename, dirname($filename));
$stats[$filename]['#includes'] = _coder_review_page_form_includes($form, $coder_args, $name, $includefiles, $offset, $coder_includes_exclusions, $ignores);
}
}
}
}
}
if (count($stats)) {
$summary = array(
'files' => 0,
'minor' => 0,
'normal' => 0,
'critical' => 0,
'ignored' => 0,
);
foreach ($stats as $stat) {
$summary['minor'] += $stat['minor'];
$summary['normal'] += $stat['normal'];
$summary['critical'] += $stat['critical'];
$summary['ignored'] += $stat['ignored'];
if (isset($stat['#includes'])) {
foreach ($stat['#includes'] as $includestat) {
$summary['files']++;
$summary['minor'] += $includestat['minor'];
$summary['normal'] += $includestat['normal'];
$summary['critical'] += $includestat['critical'];
$summary['ignored'] += $includestat['ignored'];
}
}
$summary['files']++;
}
$display = array();
if (empty($settings['coder_patches'])) {
$display[] = t('Coder found @count projects', array(
'@count' => count($stats),
));
$display[] = t('@count files', array(
'@count' => $summary['files'],
));
}
else {
$display[] = t('Coder found @count patch hunks', array(
'@count' => count($stats),
));
}
foreach (array(
'critical',
'normal',
'minor',
) as $severity_name) {
if ($summary[$severity_name] > 0) {
$display[] = t('@count %severity_name warnings', array(
'@count' => $summary[$severity_name],
'%severity_name' => $severity_name,
));
}
}
$display[] = t('@count warnings were flagged to be ignored', array(
'@count' => $summary['ignored'],
));
drupal_set_message(implode(', ', $display));
if (function_exists('_coder_review_drush_is_option') && _coder_review_drush_is_option('drush')) {
print coder_review_print_drush_messages();
}
}
}
$form['settings'] = array(
'#type' => 'fieldset',
'#title' => t('Selection form'),
'#collapsible' => TRUE,
'#collapsed' => !empty($form),
'#weight' => -1,
);
if ($form['settings']['#collapsed']) {
$form['settings']['#prefix'] = t('<div class="coder-settings">Use the Selection form to select options for this code review, or change the <a href="@settings">Default Settings</a> and use the <a href="@default">Default</a> tab above.</div>', array(
'@settings' => url('admin/config/development/coder/review/settings'),
'@default' => url('admin/config/development/coder/review/default'),
));
}
$form['settings'][] = $coder_form;
$form['settings']['submit'] = array(
'#type' => 'submit',
'#value' => t('Run reviews'),
);
$form['#attached']['css'] = array(
drupal_get_path('module', 'coder_review') . '/coder_review.css',
);
}
return $form;
}
function _coder_review_page_form_includes(&$form, $coder_args, $name, $files, $offset, $exclusions, $ignores) {
$stats = array();
$coder_args['#name'] = $name;
$weight = 0;
$exclusions = str_replace(array(
"\r\n",
"\r",
"\n",
'/',
'*',
'.',
), array(
'|',
'|',
'|',
'\\/',
'.*',
'\\.',
), $exclusions);
foreach ($files as $file) {
if (!empty($exclusions) && preg_match("/{$exclusions}/", $file->uri)) {
continue;
}
$filename = drupal_substr($file->uri, $offset);
$php_extensions = variable_get('coder_review_php_ext', array(
'inc',
'php',
'install',
'test',
));
$coder_args['#filename'] = $filename;
$coder_args['#php_extensions'] = $php_extensions;
$coder_args['#ignore_lines'] = isset($ignores[$filename]) && is_array($ignores[$filename]) ? $ignores[$filename] : array();
$results = do_coder_reviews($coder_args);
$stats[$filename] = $results['#stats'];
unset($results['#stats']);
module_invoke_all('coder_review_results_view', $results);
$form[$name][$filename] = array(
'#type' => 'fieldset',
'#title' => $filename,
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => ++$weight,
);
if (empty($results)) {
$results[] = t('No Problems Found');
}
else {
$form[$name][$filename]['#collapsed'] = FALSE;
$form[$name]['#collapsed'] = FALSE;
}
if (function_exists('_coder_review_drush_is_option') && _coder_review_drush_is_option('drush')) {
theme('coder_review', array(
'name' => $name,
'filename' => $filename,
'results' => $results,
));
}
else {
$form[$name][$filename]['output'] = array(
'#theme' => 'coder_review',
'#name' => $name,
'#filename' => $filename,
'#results' => $results,
);
}
}
return $stats;
}
function _coder_review_form_output($name, $filename, $results) {
$output = array(
'#theme' => 'coder_review',
'#name' => $name,
'#filename' => $filename,
'#results' => $results,
);
if (function_exists('_coder_review_drush_is_option') && _coder_review_drush_is_option('drush')) {
render($output);
}
return $output;
}
function _coder_review_modified() {
static $_coder_mtime;
if (!isset($_coder_mtime)) {
$path = drupal_get_path('module', 'coder_review');
$includefiles = drupal_system_listing('/.*\\.(inc|module|coder_review_ignores.txt)$/', $path . '/includes', 'filepath', 0);
$_coder_mtime = filemtime(realpath($path . '/coder_review.module'));
foreach ($includefiles as $file) {
$mtime = filemtime(realpath($path . '/includes/' . $file->filename));
if ($mtime > $_coder_mtime) {
$_coder_mtime = $mtime;
}
}
}
return $_coder_mtime;
}
function do_coder_reviews($coder_args) {
if (empty($coder_args['#patch']) && empty($coder_args['#test']) && $coder_args['#cache']) {
$cache_key = 'coder:' . md5(implode(':', array_keys($coder_args['#reviews']))) . $coder_args['#severity'] . ':' . $coder_args['#filename'];
if (function_exists('_coder_review_drush_is_option') && _coder_review_drush_is_option('drush')) {
if (_coder_review_drush_is_option('checkstyle')) {
$cache_key .= ':drushcheckstyle';
}
elseif (_coder_review_drush_is_option('xml')) {
$cache_key .= ':drushxml';
}
}
if (file_exists($filepath = realpath($coder_args['#filename']))) {
$cache_mtime = filemtime($filepath);
if ($cache_results = cache_get($cache_key, 'cache_coder')) {
if ($cache_results->data['mtime'] == $cache_mtime && _coder_review_modified() < $cache_results->created) {
return $cache_results->data['results'];
}
}
}
}
$results = array(
'#stats' => array(
'minor' => 0,
'normal' => 0,
'critical' => 0,
'ignored' => 0,
),
);
if (isset($coder_args['#php_minor']) && drupal_substr($coder_args['#filename'], -4) == '.php') {
if ($coder_args['#severity'] > 1) {
return $results;
}
}
if (_coder_review_read_and_parse_file($coder_args)) {
$errors = array();
foreach ($coder_args['#reviews'] as $review_name => $review) {
$review['#review_name'] = $review_name;
if ($result = do_coder_review($coder_args, $review)) {
foreach (array(
'critical',
'normal',
'minor',
'ignored',
) as $severity_level) {
if (isset($result['#stats'][$severity_level])) {
$results['#stats'][$severity_level] += $result['#stats'][$severity_level];
}
}
$errors += $result;
}
}
foreach ($errors as $key => $error) {
if (is_numeric($key)) {
$error['warning'] = _coder_review_warning($error['rule']);
$results[$key] = theme('coder_review_warning_msg', array(
'error' => $error,
));
}
}
ksort($results, SORT_NUMERIC);
}
else {
$results[] = theme('coder_review_warning', array(
'warning' => t('Could not read the file'),
'severity_name' => 'critical',
));
}
if (empty($coder_args['#patch']) && empty($coder_args['#test']) && $coder_args['#cache'] && isset($cache_mtime)) {
$cache_results = array(
'mtime' => $cache_mtime,
'results' => $results,
);
cache_set($cache_key, $cache_results, 'cache_coder');
}
return $results;
}
function _coder_review_read_and_parse_file(&$coder_args) {
$is_php_file = 1;
$regex = '/(' . implode('|', array_merge(array(
'module',
), $coder_args['#php_extensions'])) . ')$/';
if (!preg_match($regex, $coder_args['#filename'])) {
$is_php_file = 0;
}
$filepath = realpath($coder_args['#filename']);
if (!empty($coder_args['#patch']) || !empty($coder_args['#test']) || ($filepath = realpath($coder_args['#filename'])) && file_exists($filepath)) {
$in_php = $is_php_file ? 0 : 1;
$in_allphp = $is_php_file ? 0 : 1;
$in_comment = 0;
if (!empty($coder_args['#patch'])) {
$content = $coder_args['#patch'];
if (preg_match('/^\\s*\\*/', $content)) {
$in_comment = '*';
}
else {
$content = preg_replace('/^(function\\s.*?(\\r\\n|\\n)+)(\\s*\\*)/', '${1}/*', $content);
$in_php = 1;
$in_allphp = 1;
}
}
elseif (!empty($coder_args['#test'])) {
$content = $coder_args['#test'];
$in_php = 1;
$in_allphp = 1;
}
else {
$content = file_get_contents($filepath) . "\n";
}
$content_length = drupal_strlen($content);
$in_comment = 0;
$beginning_of_line = 0;
$in_quote_html = 0;
$in_backslash = 0;
$in_quote = 0;
$in_heredoc = 0;
$in_heredoc_html = '';
$heredoc = '';
$all_lines = array();
$full_lines = array();
$php_lines = array();
$allphp_lines = array();
$html_lines = array();
$quote_lines = array();
$doublequote_lines = array();
$comment_lines = array();
$this_all_lines = '';
$this_php_lines = '';
$this_allphp_lines = '';
$this_html_lines = '';
$this_quote_lines = array(
'',
);
$this_quote_index = -1;
$this_quote_sep = FALSE;
$this_doublequote_lines = array(
'',
);
$this_doublequote_index = -1;
$this_comment_lines = '';
$lineno = 1;
for ($pos = 0; $pos < $content_length; $pos++) {
$char = $content[$pos];
if ($char == "\n") {
if ($in_comment === '/') {
$in_comment = 0;
}
$in_quote_html = 0;
if (preg_match('/assertCoderReview(Fail|Pass)/', $this_all_lines)) {
$lineno++;
$this_all_lines = '';
$this_php_lines = '';
$this_allphp_lines = '';
$this_html_lines = '';
$this_comment_lines = '';
$this_quote_lines = array(
'',
);
continue;
}
if ($this_all_lines != '') {
if (trim($this_all_lines, "\r\n") != '') {
$all_lines[$lineno] = array(
$this_all_lines,
);
$full_lines[$lineno] = $this_all_lines;
}
if (trim($this_php_lines, "\r\n") != '') {
$php_lines[$lineno] = array(
$this_php_lines,
);
}
if (trim($this_allphp_lines, "\r\n") != '') {
$allphp_lines[$lineno] = array(
$this_allphp_lines,
);
}
if (trim($this_html_lines, "\r\n") != '') {
$html_lines[$lineno] = array(
$this_html_lines,
);
}
$quotes = array();
foreach ($this_quote_lines as $quote_line) {
if (trim($quote_line, "\r\n") != '') {
$quotes[] = $quote_line;
}
}
if (count($quotes)) {
$quote_lines[$lineno] = $quotes;
}
$quotes = array();
foreach ($this_doublequote_lines as $quote_line) {
if (trim($quote_line, "\r\n") != '') {
$quotes[] = $quote_line;
}
}
if (count($quotes)) {
$doublequote_lines[$lineno] = $quotes;
}
if (trim($this_comment_lines, "\r\n") != '') {
$comment_lines[$lineno] = array(
$this_comment_lines,
);
}
}
$lineno++;
$this_all_lines = '';
$this_php_lines = '';
$this_allphp_lines = '';
$this_html_lines = '';
$this_quote_lines = array(
'',
);
$this_doublequote_lines = array(
'',
);
$this_quote_index = -1;
$this_quote_sep = FALSE;
$this_doublequote_index = -1;
$this_comment_lines = '';
$beginning_of_line = 1;
continue;
}
if ($this_all_lines != '') {
$beginning_of_line = 0;
}
$this_all_lines .= $char;
if ($in_php || $in_allphp) {
if ($in_quote) {
if ($in_backslash) {
$in_backslash = 0;
}
elseif ($char == '\\') {
$in_backslash = 1;
}
elseif ($char == $in_quote && !$in_backslash) {
$in_quote = 0;
}
elseif ($char == '<') {
$in_quote_html = '>';
}
if ($in_quote) {
if ($this_quote_index == -1) {
$this_quote_index = 0;
}
$this_quote_lines[$this_quote_index] .= $char;
if ($in_quote == '"') {
if ($this_doublequote_index == -1) {
$this_doublequote_index = 0;
}
$this_doublequote_lines[$this_doublequote_index] .= $char;
}
if ($in_quote_html) {
$this_html_lines .= $char;
}
}
if ($char == $in_quote_html) {
$in_quote_html = 0;
}
$this_allphp_lines .= $char;
unset($char);
}
elseif ($in_heredoc) {
if ($beginning_of_line && $char == $in_heredoc[0] && drupal_substr($content, $pos, $in_heredoc_length) == $in_heredoc) {
$this_all_lines .= drupal_substr($content, $pos + 1, $in_heredoc_length - 1);
$in_heredoc = 0;
$pos += $in_heredoc_length;
}
elseif ($char == '<') {
$in_heredoc_html = '>';
}
if ($in_heredoc && $in_heredoc_html) {
$this_html_lines .= $char;
}
if ($in_heredoc_html && $char == $in_heredoc_html) {
$in_heredoc_html = '';
}
unset($char);
}
elseif ($char == '?' && $content[$pos + 1] == '>' && $in_comment !== '*') {
unset($char);
$in_php = 0;
$in_allphp = 0;
$this_all_lines .= '>';
$pos++;
}
elseif ($in_comment) {
$this_comment_lines .= $char;
if ($in_comment == '*' && $char == '*' && $content[$pos + 1] == '/') {
$in_comment = 0;
$this_all_lines .= '/';
$this_comment_lines .= '/';
$pos++;
}
unset($char);
}
else {
switch ($char) {
case ',':
case ')':
case '(':
case '>':
case ':':
if ($this_quote_index < 0 || !empty($this_quote_lines[$this_quote_index])) {
$this_quote_sep = TRUE;
}
break;
case '\'':
case '"':
if ($content[$pos - 1] != '\\') {
$this_php_lines .= $char;
$in_quote = $char;
if ($this_quote_sep) {
$this_quote_lines[++$this_quote_index] = '';
if ($char == '"') {
$this_doublequote_lines[++$this_doublequote_index] = '';
}
}
$this_quote_sep = FALSE;
}
break;
case '/':
$next_char = $content[$pos + 1];
if ($next_char == '/' || $next_char == '*') {
unset($char);
$in_comment = $next_char;
$this_all_lines .= $next_char;
$this_comment_lines .= '/' . $next_char;
$pos++;
}
break;
case '<':
if ($content[$pos + 1] == '<' && $content[$pos + 2] == '<') {
unset($char);
$this_all_lines .= '<<';
for ($pos += 3; $pos < $content_length; $pos++) {
$char = $content[$pos];
if ($char == "\n") {
$pos--;
if (preg_match('/^\\s*(\\w+)/', $heredoc, $match)) {
$in_heredoc = $match[1];
$in_heredoc_length = drupal_strlen($in_heredoc);
}
break;
}
$this_all_lines .= $char;
$heredoc .= $char;
}
$heredoc = '';
$this_php_lines .= '\'\'';
$this_allphp_lines .= '\'\'';
unset($char);
}
break;
}
}
if (isset($char)) {
$this_php_lines .= $char;
$this_allphp_lines .= $char;
}
}
else {
switch ($char) {
case '<':
if ($content[$pos + 1] == '?') {
if ($content[$pos + 2] == ' ') {
$in_php = 1;
$in_allphp = 1;
$this_all_lines .= '? ';
$pos += 2;
}
elseif (drupal_substr($content, $pos + 2, 3) == 'php') {
$in_php = 1;
$in_allphp = 1;
$this_all_lines .= '?php';
$pos += 4;
}
break;
}
default:
$this_html_lines .= $char;
break;
}
}
}
if (trim($this_all_lines) != '') {
$all_lines[$lineno] = array(
$this_all_lines,
);
$full_lines[$lineno] = $this_all_lines;
}
if (trim($this_php_lines) != '') {
$php_lines[$lineno] = array(
$this_php_lines,
);
}
if (trim($this_allphp_lines) != '') {
$allphp_lines[$lineno] = array(
$this_allphp_lines,
);
}
if (trim($this_html_lines) != '') {
$html_lines[$lineno] = array(
$this_html_lines,
);
}
$quotes = array();
foreach ($this_quote_lines as $quote_line) {
if (trim($quote_line, "\r\n") != '') {
$quotes[] = $quote_line;
}
}
if (count($quotes)) {
$quote_lines[$lineno] = $quotes;
}
$quotes = array();
foreach ($this_doublequote_lines as $quote_line) {
if (trim($quote_line, "\r\n") != '') {
$quotes[] = $quote_line;
}
}
if (count($quotes)) {
$doublequote_lines[$lineno] = $quotes;
}
if (trim($this_comment_lines) != '') {
$comment_lines[$lineno] = array(
$this_comment_lines,
);
}
$coder_args['#all_array_lines'] = $all_lines;
$coder_args['#php_array_lines'] = $php_lines;
$coder_args['#allphp_array_lines'] = $allphp_lines;
$coder_args['#html_array_lines'] = $html_lines;
$coder_args['#quote_array_lines'] = $quote_lines;
$coder_args['#doublequote_array_lines'] = $doublequote_lines;
$coder_args['#comment_array_lines'] = $comment_lines;
$coder_args['#all_lines'] = $full_lines;
return 1;
}
}
function _coder_review_severity($severity_name, $default_value = SEVERITY_NORMAL) {
if (!isset($severity_names)) {
$severity_names = array(
'minor' => SEVERITY_MINOR,
'normal' => SEVERITY_NORMAL,
'critical' => SEVERITY_CRITICAL,
);
}
if (isset($severity_names[$severity_name])) {
return $severity_names[$severity_name];
}
return $default_value;
}
function _coder_review_severity_name($coder_args, $review, $rule) {
if (isset($coder_args['#php_minor']) && drupal_substr($coder_args['#filename'], -4) == '.php') {
return 'minor';
}
if (isset($rule['#severity'])) {
return $rule['#severity'];
}
if (isset($review['#severity'])) {
return $review['#severity'];
}
return 'normal';
}
function do_coder_review($coder_args, $review) {
$results = array(
'#stats' => array(
'minor' => 0,
'normal' => 0,
'critical' => 0,
'ignored' => 0,
),
);
$allowed_extensions = array_merge($coder_args['#php_extensions'], $coder_args['#include_extensions'], array(
'module',
));
if ($review['#rules']) {
$default_severity = isset($review['#severity']) ? _coder_review_severity($review['#severity']) : SEVERITY_NORMAL;
$filename = empty($coder_args['#patch']) ? $coder_args['#filename'] : 'coder_review.patch';
$is_patch = 0;
if (!empty($coder_args['#patch']) && preg_match('/(.+?):/', $coder_args['#filename'], $matches)) {
$filename = empty($matches) ? 'coder_review.patch' : $matches[1];
$is_patch = 1;
}
foreach ($review['#rules'] as $rule) {
if ($filename_includes = isset($rule['#filename']) ? $rule['#filename'] : (isset($rule['#filename-not']) ? $coder_args['#php_extensions'] : NULL)) {
$regex = '/(' . implode('|', $filename_includes) . ')$/i';
if (!preg_match($regex, $filename, $matches)) {
continue;
}
}
$not = isset($rule['#filename-not']) ? $rule['#filename-not'] : (isset($rule['#filename']) ? NULL : $coder_args['#include_extensions']);
if ($not) {
$regex = '/(' . implode('|', $not) . ')$/i';
if (preg_match($regex, $filename, $matches) || $is_patch && preg_match($regex, 'coder_review.patch', $matches)) {
continue;
}
}
if ($is_patch) {
$regex = '/(' . implode('|', $allowed_extensions) . ')$/i';
if (!preg_match($regex, $filename, $matches)) {
continue;
}
}
$severity = _coder_review_severity(isset($rule['#severity']) ? $rule['#severity'] : '', $default_severity);
if ($severity >= $coder_args['#severity']) {
if (isset($rule['#source'])) {
$source = '#' . $rule['#source'] . '_array_lines';
$lines = isset($coder_args[$source]) ? $coder_args[$source] : array();
}
else {
$lines = isset($coder_args['#php_array_lines']) ? $coder_args['#php_array_lines'] : array();
}
switch ($rule['#type']) {
case 'regex':
do_coder_review_regex($coder_args, $review, $rule, $lines, $results);
break;
case 'grep':
do_coder_review_grep($coder_args, $review, $rule, $lines, $results);
break;
case 'grep_invert':
do_coder_review_grep_invert($coder_args, $review, $rule, $lines, $results);
break;
case 'callback':
do_coder_review_callback($coder_args, $review, $rule, $lines, $results);
break;
}
}
}
}
return $results;
}
function do_coder_review_regex(&$coder_args, $review, $rule, $lines, &$results) {
if (isset($rule['#value']) && !empty($lines)) {
$regexflags = isset($rule['#case-sensitive']) ? '' : 'i';
$regex = '/' . $rule['#value'] . '/' . $regexflags;
$class_regex = isset($rule['#class']) ? '/' . $rule['#class'] . '/' : '';
$class_not_regex = isset($rule['#class-not']) ? '/' . $rule['#class-not'] . '/' : '';
$class_current = '';
$class_paren = 0;
$function_regex = isset($rule['#function']) ? '/' . $rule['#function'] . '/' : '';
$function_not_regex = isset($rule['#function-not']) ? '/' . $rule['#function-not'] . '/' : '';
$function_current = '';
$function_paren = 0;
$not_regex = isset($rule['#not']) ? '/' . $rule['#not'] . '/' . $regexflags : '';
$never_regex = isset($rule['#never']) ? '/' . $rule['#never'] . '/' . $regexflags : '';
foreach ($lines as $lineno => $line_array) {
foreach ($line_array as $line) {
if ($function_regex || $function_not_regex) {
if (preg_match('/function (\\w+)\\s*\\(/', $line, $match)) {
$function_current = $match[1];
}
if (preg_match('/([{}])/', $line, $match)) {
$function_paren += $match[0] == '{' ? 1 : -1;
}
if ($function_paren < 0 || $function_current == '' || $function_regex && !preg_match($function_regex, $function_current) || $function_not_regex && preg_match($function_not_regex, $function_current)) {
continue;
}
}
if ($class_regex || $class_not_regex) {
if (preg_match('/class (\\w+)/', $line, $match) || preg_match('/interface (\\w+)/', $line, $match)) {
$class_current = $match[1];
}
if (preg_match('/([{}])/', $line, $match)) {
$class_paren += $match[0] == '{' ? 1 : -1;
}
if ($class_paren < 0 || $class_regex && $class_current == '' || $class_regex && !preg_match($class_regex, $class_current) || $class_not_regex && preg_match($class_not_regex, $class_current)) {
continue;
}
}
if (preg_match($regex, $line, $matches)) {
if ($not_regex) {
foreach ($matches as $match) {
if (preg_match($not_regex, $match)) {
continue 2;
}
}
}
if ($never_regex) {
if (preg_match($never_regex, $coder_args['#all_lines'][$lineno])) {
continue;
}
}
$line = $coder_args['#all_lines'][$lineno];
$severity_name = _coder_review_severity_name($coder_args, $review, $rule);
if (!isset($coder_args['#ignore_lines'][$review['#review_name']])) {
$coder_args['#ignore_lines'][$review['#review_name']] = array();
}
_coder_review_error($results, $rule, $severity_name, $lineno, $line, $coder_args['#ignore_lines'][$review['#review_name']]);
}
}
}
}
}
function _coder_review_error(&$results, $rule, $severity_name, $lineno = -1, $line = '', $ignores = array()) {
global $_coder_errno;
if (is_array($ignores) && in_array($lineno, $ignores)) {
$results['#stats']['ignored']++;
}
else {
$key = ($lineno + 1) * 10000 + $_coder_errno++;
$results[$key] = array(
'rule' => $rule,
'severity_name' => $severity_name,
'lineno' => $lineno,
'line' => $line,
);
$results['#stats'][$severity_name]++;
}
}
function do_coder_review_grep(&$coder_args, $review, $rule, $lines, &$results) {
if (isset($rule['#value'])) {
foreach ($lines as $lineno => $line_array) {
foreach ($line_array as $line) {
if (_coder_review_search_string($line, $rule)) {
$line = $coder_args['#all_lines'][$lineno];
$severity_name = _coder_review_severity_name($coder_args, $review, $rule);
if (!isset($coder_args['#ignore_lines'][$review['#review_name']])) {
$coder_args['#ignore_lines'][$review['#review_name']] = array();
}
_coder_review_error($results, $rule, $severity_name, $lineno, $line, $coder_args['#ignore_lines'][$review['#review_name']]);
}
}
}
}
}
function do_coder_review_grep_invert(&$coder_args, $review, $rule, $lines, &$results) {
if (isset($rule['#value'])) {
foreach ($lines as $lineno => $line_array) {
foreach ($line_array as $line) {
if (_coder_review_search_string($line, $rule)) {
return;
}
}
}
$severity_name = _coder_review_severity_name($coder_args, $review, $rule);
_coder_review_error($results, $rule, $severity_name);
}
}
function do_coder_review_callback(&$coder_args, $review, $rule, $lines, &$results) {
if ($function = $rule['#value']) {
if (function_exists($function)) {
call_user_func_array($function, array(
&$coder_args,
$review,
$rule,
$lines,
&$results,
));
}
}
}
function _coder_review_search_string($line, $rule) {
static $php5;
if (!isset($php5)) {
if (function_exists('stripos')) {
$php5 = TRUE;
}
else {
$php5 = FALSE;
}
}
if (isset($rule['#case-sensitive'])) {
return strpos($line, $rule['#value']) !== FALSE;
}
if ($php5 && !isset($rule['#case-sensitive'])) {
return stripos($line, $rule['#value']) !== FALSE;
}
$regex = '/' . preg_quote($rule['#value']) . '/i';
return preg_match($regex, $line);
}
function _coder_review_is_drupal_core($system) {
$info_file = dirname(realpath($system->filename)) . '/' . $system->name . '.info';
$info = drupal_parse_info_file($info_file);
return !empty($info['package']) && strtolower($info['package']) == 'core';
}
function coder_review_get_ignores() {
$ignores = coder_review_parse_ignores(drupal_get_path('module', 'coder_review') . '/core.coder_review_ignores.txt');
foreach (module_implements('coder_review_ignore') as $module) {
$function = $module . '_coder_review_ignore';
$default_info = array(
'path' => '',
'line prefix' => '',
);
$info = array_merge($default_info, $function());
$ignores += coder_review_parse_ignores($info['path'] . '/' . $module . '.coder_review_ignores.txt', $info['line prefix']);
}
return $ignores;
}
function coder_review_parse_ignores($filepath, $line_prefix = '') {
$ignores = array();
if (!is_file($filepath) || !is_readable($filepath)) {
drupal_set_message(t("File %file doesn't exist or isn't readable.", array(
'%file' => $filepath,
)), 'error');
return array();
}
$lines = file($filepath);
foreach ($lines as $line) {
$line = trim($line);
if (empty($line) || preg_match('/^;/', $line, $matches)) {
continue;
}
$line = $line_prefix . $line;
$parts = explode(':', $line);
$ignores[$parts[0]][$parts[2]][] = $parts[1];
}
return $ignores;
}
function coder_review_simpletest() {
return array_keys(file_scan_directory(drupal_get_path('module', 'coder_review') . '/tests', '/\\.test/'));
}
function coder_review_theme() {
return array(
'coder_review' => array(
'variables' => array(
'name' => NULL,
'filename' => NULL,
'results' => NULL,
),
),
'coder_review_warning' => array(
'variables' => array(
'warning' => NULL,
'severity_name' => NULL,
'lineno' => NULL,
'line' => NULL,
),
),
'coder_review_warning_msg' => array(
'variables' => array(
'error' => NULL,
),
),
'coder_review_table_cols' => array(
'render element' => 'form',
),
'drupalapi' => array(
'variables' => array(
'function' => NULL,
'version' => drupal_substr(VERSION, 0, 1),
),
),
'phpapi' => array(
'variables' => array(
'function' => NULL,
),
),
);
}
function theme_coder_review($variables) {
if (function_exists('_coder_review_drush_is_option') && _coder_review_drush_is_option('drush')) {
return theme_drush_coder_review($variables);
}
$name = $variables['name'];
$filename = $variables['filename'];
$results = $variables['results'];
$output = '<div class="coder"><h2>' . basename($filename) . '</h2>';
if (!empty($results)) {
$output .= theme('item_list', array(
'items' => $results,
));
}
$output .= '</div>';
return $output;
}
function theme_coder_review_warning_msg($variables) {
$error = $variables['error'];
return theme('coder_review_warning', array(
'warning' => _coder_review_warning($error['rule']),
'severity_name' => $error['severity_name'],
'lineno' => $error['lineno'],
'line' => $error['line'],
));
}
function _coder_review_warning($rule) {
if (isset($rule['#warning_callback'])) {
if (function_exists($rule['#warning_callback'])) {
return $rule['#warning_callback']();
}
return t('please <a href="@report">report</a> this !warning', array(
'@report' => 'http://drupal.org/node/add/project_issue/coder/bug',
'!warning' => $rule['#warning_callback'],
));
}
return t($rule['#warning']);
}
function theme_coder_review_warning($variables) {
if (function_exists('_coder_review_drush_is_option') && _coder_review_drush_is_option('drush')) {
return theme_drush_coder_review_warning($variables);
}
$warning = $variables['warning'];
$severity_name = $variables['severity_name'];
$lineno = $variables['lineno'];
$line = $variables['line'];
if (is_array($warning)) {
$description = isset($warning['#description']) ? $warning['#description'] : '';
$warning_msg = $warning['#warning'];
if (isset($warning['#link'])) {
$warning_msg .= ' (' . l(t('Drupal Docs'), $warning['#link']) . ')';
}
$warning = $warning_msg;
}
if ($lineno) {
$warning = t('Line @number: !warning', array(
'@number' => $lineno,
'!warning' => $warning,
));
if ($line) {
$warning .= '<pre>' . check_plain($line) . '</pre>';
}
}
$class = 'coder-warning';
if ($severity_name) {
$class .= " coder-{$severity_name}";
}
$path = drupal_get_path('module', 'coder');
$title = t('severity: @severity', array(
'@severity' => $severity_name,
));
$img = theme('image', array(
'path' => $path . "/images/{$severity_name}.png",
'alt' => $title,
'title' => $title,
'attributes' => array(
'align' => 'right',
'class' => 'coder',
),
'getsize' => FALSE,
));
if (!empty($description)) {
$img .= theme('image', array(
'path' => $path . '/images/more.png',
'alt' => t('click to read more'),
'atributes' => array(
'align' => 'right',
'class' => 'coder-more',
),
'getsize' => FALSE,
));
$warning .= '<div class="coder-description">Explanation: ' . $description . '</div>';
}
return '<div class="' . $class . '">' . $img . $warning . '</div>';
}
function theme_drupalapi($variables) {
$version = $variables['version'];
$function = $variables['function'];
return l($function, "http://api.drupal.org/api/function/{$function}/{$version}");
}
function theme_phpapi($variables) {
$function = $variables['function'];
return l($function, "http://php.net/{$function}");
}