Newer
Older
/**
Stella Power
committed
* @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
Stella Power
committed
* - 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
* Implementation of hook_help().
function coder_help($section) {
switch ($section) {
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'));
default :
return;
Stella Power
committed
* Get all of the code review modules, including contributions.
return module_invoke_all('reviews');
}
Stella Power
committed
/**
* 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() {
Doug Green
committed
return array('view code review', 'view code review all');
}
/**
* Implementation of hook_menu().
*/
function coder_menu($may_cache = true) {
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
if ($may_cache) {
$items[] = array(
'path' => 'coder',
'title' => t('Code review'),
'callback' => 'coder_page',
'access' => user_access('view code review'),
'type' => MENU_NORMAL_ITEM,
);
$items[] = array(
'path' => 'coder/settings',
'title' => t('Selection Form'),
'callback' => 'coder_page',
'access' => user_access('view code review'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -2,
);
$items[] = array(
'path' => 'coder/default',
'title' => t('Default'),
'callback' => 'coder_page',
'access' => user_access('view code review'),
'type' => MENU_LOCAL_TASK,
'weight' => -1,
);
$items[] = array(
'path' => 'coder/core',
'title' => t('Core'),
'callback' => 'coder_page',
'access' => user_access('view code review'),
'type' => MENU_LOCAL_TASK,
);
$items[] = array(
'path' => 'coder/active',
'title' => t('Active'),
'callback' => 'coder_page',
'access' => user_access('view code review'),
'type' => MENU_LOCAL_TASK,
);
$items[] = array(
'path' => 'coder/all',
'title' => t('All'),
'callback' => 'coder_page',
'access' => user_access('view code review all'),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
);
$items[] = array(
'path' => 'admin/settings/coder',
'title' => t('Code review'),
'description' => t('Select code review plugins and modules'),
'callback' => 'drupal_get_form',
'callback arguments' => 'coder_admin_settings',
'access' => user_access('administer site configuration'),
);
}
/**
* Implementation of hook_form_alter().
Stella Power
committed
*
* Modify the module display view by adding a Coder Review link to every
* module description.
*/
function coder_form_alter($form_id, &$form) {
if ($form_id == 'system_modules' && $form['#base'] != 'confirm_form') {
$path = drupal_get_path('module', 'coder');
drupal_add_css($path .'/coder.css', 'module');
foreach ($form['name'] as $name => $data) {
$form['description'][$name]['#value'] = $form['description'][$name]['#value'] .'('. l(t('Code Review'), "coder/$name") .')';
Stella Power
committed
* Helper functions for settings form.
function _coder_default_reviews() {
return drupal_map_assoc(array('style', 'sql', 'comment', 'security'));
Stella Power
committed
/**
* 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) {
Stella Power
committed
// Add the javascript.
$path = drupal_get_path('module', 'coder');
drupal_add_js($path .'/coder.js');
Stella Power
committed
// 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'];
Stella Power
committed
// Sort the reviews by #title.
asort($review_sort);
foreach ($review_sort as $name => $review) {
$review_sort[$name] = $review_options[$name];
}
Stella Power
committed
// 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'],
);
Stella Power
committed
// What severities should be used?
$form['coder_reviews_group']['coder_severity'] = array(
'#type' => 'radios',
'#options' => array(
1 => 'minor (most)',
5 => 'normal',
9 => 'critical (fewest)'
),
'#description' => t('show warnings at or above the severity warning level'),
'#default_value' => $settings['coder_severity'],
Stella Power
committed
// 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';
$system_modules = array();
$system_themes = array();
while ($system = db_fetch_object($result)) {
$display_name = $system->name;
if ($system->status) {
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);
Stella Power
committed
// Display what to review options.
'#collapsible' => true,
'#collapsed' => false,
Stella Power
committed
// NOTE: Should rename var.
$form['coder_what']['coder_active_modules'] = array(
'#default_value' => isset($settings['coder_active_modules']) ? $settings['coder_active_modules'] : 0,
'#default_value' => isset($settings['coder_core']) ? $settings['coder_core'] : 0,
'#type' => 'checkbox',
'#default_value' => $settings['coder_includes'],
'#title' => t('include files (.inc and .php files)'),
if (arg(0) == 'admin') {
'#type' => 'checkbox',
'#default_value' => $settings['coder_cache'],
Doug Green
committed
'#title' => t('use the coder cache'),
);
}
Stella Power
committed
// Display the modules in a fieldset.
'#type' => 'fieldset',
'#title' => t('Select Specific Modules'),
'#collapsible' => true,
'#collapsed' => true,
'checkboxes' => array(
'#theme' => 'coder_checkboxes',
),
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();
Stella Power
committed
// 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' => 'coder_checkboxes',
),
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);
$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);
$themes = isset($settings['coder_themes']) && is_array($settings['coder_themes']) ? $settings['coder_themes'] : array();
$classes[] = 'coder-active';
$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(
'#title' => $link,
'#default_value' => $default_value,
'#attributes' => array('class' => implode(' ', $classes)),
);
}
Stella Power
committed
/**
* Format checkbox field into columns.
*
* @param $form
* Form sub-array to render, usually supplied as callback.
*/
function theme_coder_checkboxes($form) {
$total = 0;
foreach ($form as $element_id => $element) {
if ($element_id[0] != '#') {
$total ++;
}
}
$total = (int) (($total % 3) ? (($total + 2) / 3) : ($total / 3));
$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);
}
Stella Power
committed
* Implementation of settings page for Drupal 5.
*/
function coder_admin_settings() {
$form = _coder_settings_form($settings, $system, $files);
$form['#submit']['coder_settings_form_submit'] = array();
$form['#submit']['system_settings_form_submit'] = array();
return system_settings_form($form);
}
Stella Power
committed
/**
* Callback function for settings page in Drupal 5.
*/
function coder_settings_form_submit($form_id, &$form_values) {
variable_set('coder_modules', _coder_settings_array($form_values, 'module'));
variable_set('coder_themes', _coder_settings_array($form_values, 'theme'));
Stella Power
committed
/**
* Generate settings array for either modules or themes.
*
* @param $form_values
* 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_values, $type) {
$typekey = "coder_{$type}s-";
$typelen = strlen($typekey);
$systems = array();
$system = substr($key, $typelen);
$systems[$system] = 1;
Stella Power
committed
* Implementation of code review page.
$output = '<div class="coder-disclaimer">'. coder_help('coder#disclaimer') .'</div>';
$output .= drupal_get_form('coder_page_form');
return $output;
Stella Power
committed
/**
* 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', 5);
$settings['coder_cache'] = variable_get('coder_cache', 1);
Stella Power
committed
// Determine any options based on the passed in URL.
break;
case 'active':
$settings['coder_active_modules'] = 1;
break;
case 'core':
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());
// TODO: does this need to go into coder_themes sometimes?
$settings['coder_modules'] = array($args => $args);
break;
}
return $settings;
}
Stella Power
committed
/**
* 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_values = null) {
if (isset($form_values['op'])) {
$settings = $form_values;
$settings['coder_modules'] = _coder_settings_array($form_values, 'module');
$settings['coder_themes'] = _coder_settings_array($form_values, 'theme');
drupal_set_title(t('Code review (submitted options)'));
$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')));
}
Stella Power
committed
// Get this once - list of the reviews to perform.
$reviews = array();
$avail_reviews = _coder_reviews();
foreach ($selected_reviews as $name => $checked) {
if ($checked) {
$reviews[$name] = $avail_reviews[$name];
}
}
if ($coder_form = _coder_settings_form($settings, $system, $files)) {
Stella Power
committed
// Add style sheet.
$path = drupal_get_path('module', 'coder');
drupal_add_css($path .'/coder.css', 'module');
Stella Power
committed
// 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);
}
Stella Power
committed
// Loop through the selected modules and themes.
Stella Power
committed
// Used to avoid duplicate includes.
$dups = array();
$stats = array();
Stella Power
committed
// Process this one file.
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']);
Stella Power
committed
// Output the results in a collapsible fieldset.
$form[$name] = array(
'#type' => 'fieldset',
'#title' => $filename,
'#collapsible' => true,
'#collapsed' => true,
if (empty($results)) {
$form[$name]['#collapsed'] = false;
}
$form[$name]['output'] = array(
'#value' => theme('coder', $name, $filename, $results),
'#weight' => -1,
);
Stella Power
committed
// Process the same directory include files.
Stella Power
committed
// NOTE: Convert to the realpath here so drupal_system_listing
// doesn't return additional paths (i.e., try "module").
$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;
}
$includefiles = drupal_system_listing('.*\.(inc|php|install|schema)$', $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));
}
Stella Power
committed
// Prepend the settings form.
$form['settings'] = array(
'#type' => 'fieldset',
'#collapsible' => true,
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'),
);
$form['#multistep'] = true;
$form['#redirect'] = false;
Stella Power
committed
/**
* 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();
$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']);
Stella Power
committed
// 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;
}
Stella Power
committed
/**
* Return last modification timestamp of coder and all of its dependencies.
*/
Doug Green
committed
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'));
Doug Green
committed
foreach ($includefiles as $file) {
$mtime = filemtime(realpath($file->filename));
if ($mtime > $_coder_mtime) {
$_coder_mtime = $mtime;
}
}
}
return $_coder_mtime;
}
Stella Power
committed
/**
* 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)) {
Stella Power
committed
// Load the cached results if they exists.
$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 && ($x = _coder_modified()) < $cache_results->created) {
return unserialize($cache_results->data['results']);
}
}
}
$results = array('#stats' => array('minor' => 0, 'normal' => 0, 'critical' => 0));
Stella Power
committed
// 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;
}
}
Stella Power
committed
// Read the file.
if (_coder_read_and_parse_file($coder_args)) {
Stella Power
committed
// Do all of the code reviews.
foreach ($coder_args['#reviews'] as $review) {
if ($result = do_coder_review($coder_args, $review)) {
$results += $result;
}
}
Stella Power
committed
// Sort the results.
}
else {
_coder_error_msg($results, t('Could not read the file'), 'critical');
}
Stella Power
committed
// Save the results in the cache.
if ($use_cache) {
$cache_results = array(
'mtime' => $cache_mtime,
'results' => $results,
);
cache_set($cache_key, 'cache', serialize($cache_results));
}
return $results;
}
Stella Power
committed
/**
* 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) {
Stella Power
committed
// Get the path to the module file.
if ($filepath = realpath($coder_args['#filename'])) {
Stella Power
committed
// Read the file.
$content = file_get_contents($filepath) ."\n";
$content_length = drupal_strlen($content);
$in_comment = 0;
$beginning_of_line = 0;
$in_php = 0;
$in_quote_html = 0;
$in_backslash = 0;
$in_quote = 0;
$in_heredoc = 0;
$in_heredoc_html = '';
$heredoc = '';
$all_lines = array();
$php_lines = array();
$html_lines = array();
$quote_lines = array();
$doublequote_lines = array();
$comment_lines = array();
$this_all_lines = '';
$this_php_lines = '';
$this_html_lines = '';
$this_quote_lines = '';
$this_doublequote_lines = '';
$this_comment_lines = '';
Stella Power
committed
// Parse the file:
// - Strip comments,
// - Strip quote content,
// - Strip stuff not in php,
// - Break into lines.
$lineno = 0;
for ($pos = 0; $pos < $content_length; $pos ++) {
Stella Power
committed
// Get the current character.
$char = $content[$pos];
if ($char == "\n") {
Stella Power
committed
if ($in_comment && $in_comment == '/') { // End C++ style comments on newline.
$in_comment = 0;
Doug Green
committed
Stella Power
committed
// Assume that html inside quotes doesn't span newlines.
$in_quote_html = 0;
Doug Green
committed
Stella Power
committed
// Remove blank lines now, so we avoid processing them over-and-over.
if ($this_all_lines != '') {
if (trim($this_all_lines) != '') {
$all_lines[$lineno] = $this_all_lines;
}
if (trim($this_php_lines) != '') {
$php_lines[$lineno] = $this_php_lines;
}
if (trim($this_allphp_lines) != '') {
$allphp_lines[$lineno] = $this_allphp_lines;
}
if (trim($this_html_lines) != '') {
$html_lines[$lineno] = $this_html_lines;
}
if (trim($this_quote_lines) != '') {
$quote_lines[$lineno] = $this_quote_lines;
}
if (trim($this_doublequote_lines) != '') {
$doublequote_lines[$lineno] = $this_doublequote_lines;
}
if (trim($this_comment_lines) != '') {
$comment_lines[$lineno] = $this_comment_lines;
}
Doug Green
committed
}
Stella Power
committed
// Save this line and start a new line.
$lineno ++;
$this_all_lines = '';
$this_php_lines = '';
$this_html_lines = '';
$this_quote_lines = '';
$this_doublequote_lines = '';
$this_comment_lines = '';
continue;
}
if ($this_all_lines != '') {
$beginning_of_line = 0;
}
$this_all_lines .= $char;
Stella Power
committed
// When in a quoted string, look for the trailing quote
// strip characters in the string, replacing with '' or "".
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) {
$this_quote_lines .= $char;
if ($in_quote == '"') {
$this_doublequote_lines .= $char;
}
if ($in_quote_html) {
$this_html_lines .= $char;
}
}
if ($char == $in_quote_html) {
$in_quote_html = 0;
Stella Power
committed
unset($char); // NOTE: Trailing char output with starting one.
elseif ($in_heredoc) {
if ($beginning_of_line && $char == $in_heredoc[0] && substr($content, $pos, $in_heredoc_length) == $in_heredoc) {
$this_all_lines .= 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 = '';
Stella Power
committed
// Look for the ending php tag.
elseif ($char == '?' && $content[$pos + 1] == '>') {
unset($char);
$in_php = 0;
$this_all_lines .= '>';
$pos ++;
}
Stella Power
committed
// When in a comment look for the trailing comment.
elseif ($in_comment) {
$this_comment_lines .= $char;
if ($in_comment == '*' && $char == '*' && $content[$pos + 1] == '/') {
$in_comment = 0;
$this_all_lines .= '/';
$this_comment_lines .= '/';