Calling all Drupal developers!
Help us get this on the first page of Digg. DIGG NOW!
Help us get this on the first page of Digg. DIGG NOW!
<?php
// $Id: coder.module,v 1.88.2.32 2008/03/29 23:55:19 douggreen Exp $
if (module_exists('drush')) {
include_once(drupal_get_path('module', 'coder') .'/coder.drush.inc');
}
/**
* @file
* Developer Module that assists with code review and version upgrade that
* supports a plug-in extensible hook system so contributed modules can
* define additional review standards.
*
* Built-in support for:
* - Drupal Coding Standards - http://drupal.org/node/318
* - Handle text in a secure fashion - http://drupal.org/node/28984
* - Converting 4.6.x modules to 4.7.x - http://drupal.org/node/22218
* - Converting 4.7.x modules to 5.x - http://drupal.org/node/64279
* - Converting 5.x modules to 6.x - http://drupal.org/node/114774
* - Comment Standards - http://drupal.org/node/1354
* - SQL coding conventions - http://drupal.org/node/2497
*
* Credit also to dries:
* - http://cvs.drupal.org/viewcvs/drupal/drupal/scripts/code-style.pl
*/
define('SEVERITY_MINOR', 1);
define('SEVERITY_NORMAL', 5);
define('SEVERITY_CRITICAL', 9);
/**
* Implementation of hook_help().
*/
function coder_help($page, $arg) {
switch ($page) {
case 'coder#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':
return t('coder [summary] [<styles>] [minor|major|critical] [active|core|all|default|<modules>] [no-<module>]');
}
}
/**
* Get all of the code review modules, including contributions.
*/
function _coder_reviews() {
return module_invoke_all('reviews');
}
/**
* Implementation of hook_reviews().
*/
function coder_reviews() {
global $_coder_reviews;
if (!isset($_coder_reviews)) {
$_coder_reviews = array();
$path = drupal_get_path('module', 'coder') .'/includes';
$files = drupal_system_listing('coder_.*\.inc$', $path, 'filename', 0);
foreach ($files as $file) {
require_once('./'. $file->filename);
$function = $file->name .'_reviews';
if (function_exists($function)) {
if ($review = call_user_func($function)) {
$_coder_reviews = array_merge($_coder_reviews, $review);
}
}
}
}
return $_coder_reviews;
}
/**
* Implementation of hook_cron().
*/
function coder_cron() {
if ($use_cache = variable_get('coder_cache', 1)) {
// TODO: move some of the work here... is this really worth it?
}
}
/**
* Implementation of hook_perm().
*/
function coder_perm() {
return array('view code review', 'view code review all');
}
/**
* Implementation of hook_menu().
*/
function coder_menu() {
$items = array();
$items['coder'] = array(
'title' => t('Code review'),
'page callback' => 'coder_page',
'access arguments' => array('view code review'),
'type' => MENU_NORMAL_ITEM,
);
$items['coder/settings'] = array(
'title' => t('Selection Form'),
'page callback' => 'coder_page',
'access arguments' => array('view code review'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -2,
);
$items['coder/default'] = array(
'title' => t('Default'),
'page callback' => 'coder_page',
'access arguments' => array('view code review'),
'type' => MENU_LOCAL_TASK,
'weight' => -1,
);
$items['coder/core'] = array(
'title' => t('Core'),
'page callback' => 'coder_page',
'access arguments' => array('view code review'),
'type' => MENU_LOCAL_TASK,
);
$items['coder/active'] = array(
'title' => t('Active'),
'page callback' => 'coder_page',
'access arguments' => array('view code review'),
'type' => MENU_LOCAL_TASK,
);
$items['coder/all'] = array(
'title' => t('All'),
'page callback' => 'coder_page',
'access arguments' => array('view code review all'),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
);
$items['admin/settings/coder'] = array(
'title' => t('Code review'),
'description' => t('Select code review plugins and modules'),
'page callback' => 'drupal_get_form',
'page arguments' => array('coder_admin_settings'),
'access arguments' => array('administer site configuration'),
);
return $items;
}
/**
* Implementation of hook_form_alter().
*
* Modify the module display view by adding a Coder Review link to every
* module description.
*/
function coder_form_alter(&$form, $form_state, $form_id) {
if ($form_id == 'system_modules') {
if (user_access('view code review')) {
$path = drupal_get_path('module', 'coder');
drupal_add_css($path .'/coder.css', 'module');
foreach ($form['name'] as $name => $data) {
$description = isset($form['description'][$name]['#value']) ? $form['description'][$name]['#value'] : $data['#value'];
$form['description'][$name]['#value'] = $description .' ('. l(t('Code Review'), "coder/$name") .')';
}
}
}
}
/**
* Helper functions for settings form.
*/
function _coder_default_reviews() {
return drupal_map_assoc(array('style', 'sql', 'comment', 'security'));
}
/**
* Build settings form API array for coder.
*
* Generates a form with the default reviews and default modules/themes to
* run Coder on.
*
* @note
* Actual forms may have additional sections added to them, this
* is simply a base.
*
* @param $settings
* Settings array for coder in the format of _coder_get_default_settings().
* @param $system
* Array of module and theme information, in form string theme/module
* name => boolean TRUE if checked by coder already.
* @param $files
* Associative array of files, in form string theme/module name => string
* filename to check.
* @return
* Array for form API for the settings box.
*/
function _coder_settings_form($settings, &$system, &$files) {
// Add the javascript.
$path = drupal_get_path('module', 'coder');
drupal_add_js($path .'/coder.js');
// Create the list of review options from the coder review plug-ins.
// Maintain a secondary list based on #title only, to make sorting possible.
$reviews = _coder_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'];
}
// Sort the reviews by #title.
asort($review_sort);
foreach ($review_sort as $name => $review) {
$review_sort[$name] = $review_options[$name];
}
// What reviews should be used?
$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'],
);
// What severities should be used?
$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'],
);
// Get the modules and theme.
$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();
while ($system = db_fetch_object($result)) {
$display_name = $system->name;
if ($system->status) {
$display_name .= t(' (active)');
$system_active[$system->name] = $system->name;
}
if (_coder_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, "coder/$system->name");
$files[$system->name] = $system->filename;
}
asort($system_links);
// Display what to review options.
$form['coder_what'] = array(
'#type' => 'fieldset',
'#title' => t('What to review'),
'#collapsible' => true,
'#collapsed' => false,
);
// NOTE: Should rename var.
$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)'),
);
$form['coder_what']['coder_includes'] = array(
'#type' => 'checkbox',
'#default_value' => isset($settings['coder_includes']) ? $settings['coder_includes'] : 0,
'#title' => t('include files (.inc and .php files)'),
);
if (arg(0) == 'admin') {
$form['coder_what']['coder_cache'] = array(
'#type' => 'checkbox',
'#default_value' => isset($settings['coder_cache']) ? $settings['coder_cache'] : 0,
'#title' => t('use the coder cache'),
);
}
// Display the modules in a fieldset.
$form['coder_what']['coder_modules'] = array(
'#type' => 'fieldset',
'#title' => t('Select Specific Modules'),
'#collapsible' => true,
'#collapsed' => true,
'checkboxes' => array(
'#theme' => '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);
}
else {
$modules = isset($settings['coder_modules']) && is_array($settings['coder_modules']) ? $settings['coder_modules'] : array();
}
// Display the themes in a fieldset.
$form['coder_what']['coder_themes'] = array(
'#type' => 'fieldset',
'#title' => t('Select Specific Themes'),
'#collapsible' => true,
'#collapsed' => true,
'checkboxes' => array(
'#theme' => '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);
}
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' => implode(' ', $classes)),
);
}
$system = array_merge($modules, $themes);
return $form;
}
if (!function_exists('theme_cols')) {
/**
* Implement theme_cols to theme the radiobuttons and checkboxes form
* elements in a table column.
*/
function theme_cols($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);
}
}
return theme('table', array(), $rows);
}
}
/**
* Implementation of settings page for Drupal 5.
*/
function coder_admin_settings() {
$settings = _coder_get_default_settings();
$form = _coder_settings_form($settings, $system, $files);
$form['help'] = array(
'#type' => 'markup',
'#value' => t('After setting these defaults, use <a href="@url">coder</a> to perform code reviews.', array('@url' => url('coder'))),
'#weight' => -1,
);
$form['#submit'][] = 'coder_settings_form_submit';
return system_settings_form($form);
}
/**
* Callback function for settings page in Drupal 5.
*/
function coder_settings_form_submit($form, &$form_state) {
$form_state['storage'] = $form_state['values'];
variable_set('coder_modules', _coder_settings_array($form_state, 'module'));
variable_set('coder_themes', _coder_settings_array($form_state, 'theme'));
}
/**
* Generate settings array for either modules or themes.
*
* @param $form_state
* Form array passed to submit function (note: entries that are processed.
* are removed for efficiency's sake).
* @param $type
* String type to generate settings for, either 'module' or 'theme'.
* @return
* Settings lookup array in form module/theme name => 1
*/
function _coder_settings_array(&$form_state, $type) {
$typekey = "coder_{$type}s-";
$typelen = strlen($typekey);
$systems = array();
foreach ($form_state['storage'] as $key => $value) {
if (substr($key, 0, $typelen) == $typekey) {
if ($value == 1) {
$system = substr($key, $typelen);
$systems[$system] = 1;
}
unset($form_state['storage'][$key]);
}
}
return $systems;
}
/**
* Implementation of code review page.
*/
function coder_page() {
$output = '<div class="coder-disclaimer">'. coder_help('coder#disclaimer', array()) .'</div>';
$output .= drupal_get_form('coder_page_form');
return $output;
}
/**
* Returns a active settings array for coder.
*
* @note
* The name is a misnomer, but is a largely correct characterization
* for most of Coder's settings as the variables usually do not exist.
*
* @param $args
* String settings argument, can be 'settings', 'active', 'core', 'all'
* and 'default'.
* @return
* Associative array of settings in form setting name => setting value.
*/
function _coder_get_default_settings($args = 'default') {
$settings['coder_reviews'] = variable_get('coder_reviews', _coder_default_reviews());
$settings['coder_severity'] = variable_get('coder_severity', SEVERITY_NORMAL);
$settings['coder_cache'] = variable_get('coder_cache', 1);
// Determine any options based on the passed in URL.
switch ($args) {
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 '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_modules'] = variable_get('coder_modules', array());
$settings['coder_themes'] = variable_get('coder_themes', array());
break;
default:
$settings['coder_includes'] = 1;
// TODO: Does this need to go into coder_themes sometimes?
$settings['coder_modules'] = array($args => $args);
break;
}
return $settings;
}
/**
* Implementation of hook_submit().
*/
function coder_page_form_submit($form, &$form_state) {
$form_state['storage'] = $form_state['values'];
}
/**
* Implementation of hook_form().
*
* Implements coder's main form, in which a user can select reviews and
* modules/themes to run them on.
*/
function coder_page_form($form_state) {
if (isset($form_state['storage'])) {
$settings = $form_state['storage'];
$settings['coder_modules'] = _coder_settings_array($form_state, 'module');
$settings['coder_themes'] = _coder_settings_array($form_state, 'theme');
drupal_set_title(t('Code review (submitted options)'));
}
else {
$options = arg(1);
$settings = _coder_get_default_settings($options);
if ($options) {
drupal_set_title(t('Code review (@options)', array('@options' => isset($options) ? $options : 'default options')));
}
}
// Get this once: list of the reviews to perform.
$reviews = array();
$avail_reviews = _coder_reviews();
$selected_reviews = $settings['coder_reviews'];
foreach ($selected_reviews as $name => $checked) {
if ($checked) {
$reviews[$name] = $avail_reviews[$name];
}
}
if ($coder_form = _coder_settings_form($settings, $system, $files)) {
// Add style sheet.
$path = drupal_get_path('module', 'coder');
drupal_add_css($path .'/coder.css', 'module');
// Code review non-module core files.
$module_weight = 0;
if (isset($settings['coder_core']) && $settings['coder_core']) {
$coder_args = array(
'#reviews' => $reviews,
'#severity' => $settings['coder_severity'],
// '#filename' => $filename,
);
$form['core_php'] = array(
'#type' => 'fieldset',
'#title' => 'core (php)',
'#collapsible' => true,
'#collapsed' => true,
'#weight' => ++ $module_weight,
);
$phpfiles = file_scan_directory('.', '.*\.php', array('.', '..', 'CVS'), 0, false, 'name', 0);
_coder_page_form_includes($form, $coder_args, 'core_php', $phpfiles, 2);
$form['core_includes'] = array(
'#type' => 'fieldset',
'#title' => 'core (includes)',
'#collapsible' => true,
'#collapsed' => true,
'#weight' => ++ $module_weight,
);
$includefiles = drupal_system_listing('.*\.inc$', 'includes', 'filename', 0);
_coder_page_form_includes($form, $coder_args, 'core_includes', $includefiles, 0);
}
// Loop through the selected modules and themes.
if (isset($system)) {
// Used to avoid duplicate includes.
$dups = array();
$stats = array();
foreach ($system as $name => $checked) {
if ($checked) {
// Process this one file.
$filename = $files[$name];
if (!$filename) {
drupal_set_message(t('Code Review file for %module not found', array('%module' => $name)));
continue;
}
$coder_args = array(
'#reviews' => $reviews,
'#severity' => $settings['coder_severity'],
'#filename' => $filename,
);
$results = do_coder_reviews($coder_args);
$stats[$filename] = $results['#stats'];
unset($results['#stats']);
// Output the results in a collapsible fieldset.
$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'] = array(
'#value' => theme('coder', $name, $filename, $results),
'#weight' => -1,
);
// Process the same directory include files.
if (!empty($settings['coder_includes'])) {
// NOTE: Convert to the realpath here so drupal_system_listing
// doesn't return additional paths (i.e., try "module").
if ($path = str_replace('\\', '/', dirname(realpath($filename)))) {
$offset = strpos($path, dirname($filename));
if (!isset($dups[$path])) {
if (substr($filename, -7) == '.module') {
$coder_args['#php_minor'] = 1;
}
$dups[$path] = 1;
$includefiles = drupal_system_listing('.*\.(inc|php|install|test)$', $path, 'filename', 0);
$stats[$filename]['#includes'] = _coder_page_form_includes($form, $coder_args, $name, $includefiles, $offset);
}
}
}
}
}
if (count($stats)) {
$summary = array('files' => 0, 'minor' => 0, 'normal' => 0, 'critical' => 0);
foreach ($stats as $stat) {
if (isset($stat['#includes'])) {
foreach ($stat['#includes'] as $includestat) {
$summary['files'] ++;
$summary['minor'] += $includestat['minor'];
$summary['normal'] += $includestat['normal'];
$summary['critical'] += $includestat['critical'];
}
}
$summary['files'] ++;
}
$display = array();
$display[] = t('Coder found @count projects', array('@count' => count($stats)));
$display[] = t('@count files', array('@count' => $summary['files']));
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));
}
}
drupal_set_message(implode(', ', $display));
if (function_exists('_coder_drush_is_option') && _coder_drush_is_option('drush')) {
print coder_print_drush_messages();
}
}
}
// Prepend the settings form.
$form['settings'] = array(
'#type' => 'fieldset',
'#title' => t('Selection Form'),
'#collapsible' => true,
'#collapsed' => isset($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/settings/coder'), '@default' => url('coder/default')));
}
$form['settings'][] = $coder_form;
$form['settings']['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
}
return $form;
}
/**
* Add results to form array for display on form page.
*
* @param $form
* Form array variable to be modified.
* @param $coder_args
* Coder settings, see do_coder_reviews() for details.
* @param $name
* Name of form element.
* @param $files
* Array of file objects to check and display the results of, see
* file_scan_directory().
* @param $offset
* Integer offset to munge filenames with.
* @return
* Statistics array in form: string filename => array value of
* '#stats' from do_coder_reviews().
*/
function _coder_page_form_includes(&$form, $coder_args, $name, $files, $offset) {
$stats = array();
$coder_args['#name'] = $name;
$weight = 0;
foreach ($files as $file) {
$filename = drupal_substr($file->filename, $offset);
$coder_args['#filename'] = $filename;
$results = do_coder_reviews($coder_args);
$stats[$filename] = $results['#stats'];
unset($results['#stats']);
// Output the results in a collapsible fieldset.
$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;
}
$form[$name][$filename]['output'] = array(
'#value' => theme('coder', $name, $filename, $results),
);
}
return $stats;
}
/**
* Return last modification timestamp of coder and all of its dependencies.
*/
function _coder_modified() {
static $_coder_mtime;
if (!isset($_coder_mtime)) {
$path = drupal_get_path('module', 'coder');
$includefiles = drupal_system_listing('.*\.(inc|module)$', $path .'/includes', 'filename', 0);
$_coder_mtime = filemtime(realpath($path .'/coder.module'));
foreach ($includefiles as $file) {
$mtime = filemtime(realpath($file->filename));
if ($mtime > $_coder_mtime) {
$_coder_mtime = $mtime;
}
}
}
return $_coder_mtime;
}
/**
* Perform batch coder reviews for multiple files.
*
* @param $coder_args
* Array of coder arguments, valid arguments are:
* - '#reviews' => array list of reviews to perform, see _coder_reviews();
* - '#severity' => integer magic number, see constants SEVERITY_*;
* - '#filename' => string filename to check,
* @return
* Array of results, in form:
* - '#stats' => Array with error counts for all severities, in form
* 'minor' => integer count, 'normal' => integer count;
* 'critical' => integer count;
* - integer ID => HTML error for display.
*/
function do_coder_reviews($coder_args) {
if ($use_cache = variable_get('coder_cache', 1)) {
// Load the cached results if they exist.
$cache_key = 'coder:'. implode(':', array_keys($coder_args['#reviews'])) . $coder_args['#severity'] .':'. $coder_args['#filename'];
$cache_mtime = filemtime(realpath($coder_args['#filename']));
if ($cache_results = cache_get($cache_key)) {
if ($cache_results->data['mtime'] == $cache_mtime && _coder_modified() < $cache_results->created) {
return $cache_results->data['results'];
}
}
}
$results = array('#stats' => array('minor' => 0, 'normal' => 0, 'critical' => 0));
// Skip php include files when the user requested severity is above minor.
if (isset($coder_args['#php_minor']) && drupal_substr($coder_args['#filename'], -4) == '.php') {
if ($coder_args['#severity'] > 1) {
return $results;
}
}
// Read the file.
if (_coder_read_and_parse_file($coder_args)) {
// Do all of the code reviews.
foreach ($coder_args['#reviews'] as $review) {
if ($result = do_coder_review($coder_args, $review)) {
foreach (array('critical', 'normal', 'minor') as $severity_level) {
if (isset($result['#stats'][$severity_level])) {
$results['#stats'][$severity_level] += $result['#stats'][$severity_level];
}
}
$results += $result;
}
}
// Sort the results.
ksort($results, SORT_NUMERIC);
}
else {
_coder_error_msg($results, t('Could not read the file'), 'critical');
}
// Save the results in the cache.
if ($use_cache) {
$cache_results = array(
'mtime' => $cache_mtime,
'results' => $results,
);
cache_set($cache_key, $cache_results);
}
return $results;
}
/**
* Parse and read a file into a format easy to validate.
*
* @param $coder_args
* Coder arguments array variable to add file lines of code (with
* trailing newlines. The following array indices are added: '#all_lines',
* '#php_lines', '#allphp_lines', '#html_lines', '#quote_lines',
* '#doublequote_lines', '#comment_lines'. Their names should be
* self explanatory.
* @return
* Integer 1 if success.
*/
function _coder_read_and_parse_file(&$coder_args) {
// Get the path to the module file.
if ($filepath = realpath(