Parent Directory
|
Revision Log
|
Revision Graph
by sun: Fixed boolean constants must be written uppercase.
| 1 | <?php |
| 2 | // $Id: coder_format.inc,v 1.5 2009/01/19 03:28:08 sun Exp $ |
| 3 | |
| 4 | |
| 5 | /** |
| 6 | * Recursively process .module and .inc files in directory with coder_format_file(). |
| 7 | * |
| 8 | * @param $directory |
| 9 | * Path to a directory to process recursively. |
| 10 | * @param $undo |
| 11 | * Boolean whether or not to undo batch replacements. |
| 12 | */ |
| 13 | function coder_format_recursive($directory, $undo = false) { |
| 14 | // Convert Windows paths (only cosmetical). |
| 15 | $directory = str_replace('\\', '/', $directory); |
| 16 | |
| 17 | // Check if directory exists. |
| 18 | if (!file_check_directory($directory)) { |
| 19 | drupal_set_message(t('%directory not found.', array('%directory' => $directory)), 'error'); |
| 20 | return FALSE; |
| 21 | } |
| 22 | |
| 23 | // Fetch files to process. |
| 24 | $mask = '\.php$|\.module$|\.inc$|\.install|\.profile$'; |
| 25 | $nomask = array('.', '..', 'CVS', '.svn'); |
| 26 | $files = file_scan_directory($directory, $mask, $nomask, 0, true); |
| 27 | foreach ($files as $file) { |
| 28 | coder_format_file($file->filename, $undo); |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | /** |
| 33 | * Reads, backups, processes and writes the source code from and to a file. |
| 34 | * |
| 35 | * @param $filename |
| 36 | * Path to a file to process or restore. Pass original filename to restore an |
| 37 | * already processed file. |
| 38 | * @param $undo |
| 39 | * Whether to restore a processed file. Always restores the last backup. |
| 40 | * |
| 41 | * @return |
| 42 | * TRUE on success. |
| 43 | */ |
| 44 | function coder_format_file($filename, $undo = FALSE) { |
| 45 | // Restore a processed file. |
| 46 | if ($undo) { |
| 47 | // Do nothing if no backup file exists at all. |
| 48 | if (!file_exists($filename .'.coder.orig')) { |
| 49 | return; |
| 50 | } |
| 51 | // Save original filename. |
| 52 | $original = $filename; |
| 53 | // Retrieve the file's directory. |
| 54 | $basename = file_check_path($filename); |
| 55 | // Find all backups. |
| 56 | $mask = '^'. preg_quote($basename) .'(\.coder\.orig)+$'; |
| 57 | $nomask = array('.', '..', 'CVS', '.svn'); |
| 58 | $backups = file_scan_directory($filename, $mask, $nomask, 0, FALSE); |
| 59 | // Find the latest backup to restore. |
| 60 | ksort($backups); |
| 61 | $latest = array_pop($backups); |
| 62 | // Restore latest backup. |
| 63 | if (file_move($latest->filename, $original, FILE_EXISTS_REPLACE)) { |
| 64 | drupal_set_message(t('%file restored.', array('%file' => $original))); |
| 65 | return TRUE; |
| 66 | } |
| 67 | else { |
| 68 | drupal_set_message(t('%file could not be restored.', array('%file' => $original)), 'error'); |
| 69 | return FALSE; |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | // Backup original file. |
| 74 | // file_copy() replaces source filepath with target filepath. |
| 75 | $sourcefile = $filename; |
| 76 | if (!file_copy($filename, $filename .'.coder.orig', FILE_EXISTS_RENAME)) { |
| 77 | drupal_set_message(t('%file could not be backup.', array('%file' => $filename)), 'error'); |
| 78 | return FALSE; |
| 79 | } |
| 80 | |
| 81 | // Read source code from source file. |
| 82 | $fd = fopen($sourcefile, 'r'); |
| 83 | $code = fread($fd, filesize($sourcefile)); |
| 84 | fclose($fd); |
| 85 | |
| 86 | if ($code !== false) { |
| 87 | $code = coder_format_string_all($code); |
| 88 | |
| 89 | if ($code !== false) { |
| 90 | // Write formatted source code to target file. |
| 91 | $fd = fopen($sourcefile, 'w'); |
| 92 | $status = fwrite($fd, $code); |
| 93 | fclose($fd); |
| 94 | |
| 95 | drupal_set_message(t('%file processed.', array('%file' => $sourcefile))); |
| 96 | return $status; |
| 97 | } |
| 98 | else { |
| 99 | drupal_set_message(t('An error occurred while processing %file.', array('%file' => $sourcefile)), 'error'); |
| 100 | return FALSE; |
| 101 | } |
| 102 | } |
| 103 | else { |
| 104 | drupal_set_message(t('%file could not be opened.', array('%file' => $sourcefile)), 'error'); |
| 105 | return FALSE; |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | /** |
| 110 | * Formats source code according to Drupal conventions, also using |
| 111 | * post and pre-processors. |
| 112 | * |
| 113 | * @param |
| 114 | * $code Code to process. |
| 115 | */ |
| 116 | function coder_format_string_all($code) { |
| 117 | // Preprocess source code. |
| 118 | $code = coder_exec_processors($code, 'coder_preprocessor'); |
| 119 | |
| 120 | // Process source code. |
| 121 | $code = coder_format_string($code); |
| 122 | |
| 123 | // Postprocess source code. |
| 124 | $code = coder_exec_processors($code, 'coder_postprocessor'); |
| 125 | |
| 126 | // Fix beginning and end of code. |
| 127 | $code = coder_trim_php($code); |
| 128 | |
| 129 | return $code; |
| 130 | } |
| 131 | |
| 132 | /** |
| 133 | * Format the source code according to Drupal coding style guidelines. |
| 134 | * |
| 135 | * This function uses PHP's tokenizer functions. |
| 136 | * @see <http://www.php.net/manual/en/ref.tokenizer.php> |
| 137 | * |
| 138 | * To achieve the desired coding style, we have to take some special cases |
| 139 | * into account. These are: |
| 140 | * |
| 141 | * Indent-related: |
| 142 | * $_coder_indent int Indent level |
| 143 | * The number of indents for the next line. This is |
| 144 | * - increased after {, : (after case and default). |
| 145 | * - decreased after }, break, case and default (after a previous case). |
| 146 | * $in_case bool |
| 147 | * Is true after case and default. Is false after break and return, if |
| 148 | * $braces_in_case is not greater than 0. |
| 149 | * $switches int Switch level |
| 150 | * Nested switches need to have extra indents added to them. |
| 151 | * $braces_in_case array Count of braces |
| 152 | * The number of currently opened curly braces in a case. This is needed |
| 153 | * to support arbitrary function exits inside of a switch control strucure. |
| 154 | * This is an array to allow for nested switches. |
| 155 | * $parenthesis int Parenthesis level |
| 156 | * The number of currently opened parenthesis. This |
| 157 | * - prevents line feeds in brackets (f.e. in arguments of for()). |
| 158 | * - is the base for formatting of multiline arrays. Note: If the last |
| 159 | * ');' is not formatted to the correct indent level then there is no |
| 160 | * ',' (comma) behind the last array value. |
| 161 | * $in_brace bool |
| 162 | * Is true after left curly braces if they are in quotes, an object or |
| 163 | * after a dollar sign. Prevents line breaks around such variable |
| 164 | * statements. |
| 165 | * $in_heredoc bool |
| 166 | * Is true after heredoc output method and false after heredoc delimiter. |
| 167 | * Prevents line breaks in heredocs. |
| 168 | * $first_php_tag bool |
| 169 | * Is false after the first PHP tag. Allows inserting a line break after |
| 170 | * the first one. |
| 171 | * $in_do_while bool |
| 172 | * Is true after a do {} statement and set to false in the next while |
| 173 | * statement. Prevents a line break in the do {...} while() construct. |
| 174 | * |
| 175 | * Whitespace-related: |
| 176 | * $in_object bool |
| 177 | * Prevents whitespace after ->. |
| 178 | * Is true after ->. Is reset to false after the next string or variable. |
| 179 | * $in_at bool |
| 180 | * Prevents whitespace after @. |
| 181 | * Is true after @. Is reset to false after the next string or variable. |
| 182 | * $in_quote bool |
| 183 | * Prevents |
| 184 | * - removal of whitespace in double quotes. |
| 185 | * - injection of new line feeds after brackets in double quotes. |
| 186 | * $inline_if bool |
| 187 | * Controls formatting of ? and : for inline ifs until a ; (semicolon) is |
| 188 | * processed. |
| 189 | * $in_function_declaration |
| 190 | * Prevents whitespace after & for function declarations, e.g. |
| 191 | * function &foo(). Is true after function token but before first |
| 192 | * parenthesis. |
| 193 | * $in_array |
| 194 | * Array of parenthesis level to whether or not the structure |
| 195 | * is for an array. |
| 196 | * $in_multiline |
| 197 | * Array of parenthesis level to whether or not the structure |
| 198 | * is multiline. |
| 199 | * |
| 200 | * Context flags: |
| 201 | * These variables give information about what tokens have just been |
| 202 | * processed so that operations can change their behavior depending on |
| 203 | * the preceding context without having to scan backwards on the fully |
| 204 | * formed result. Most of these are ad hoc and have a very specific |
| 205 | * purpose in the program. It would probably be a good idea to generalize |
| 206 | * this facility. |
| 207 | * |
| 208 | * $after_semicolon |
| 209 | * Is the token being processed on the same line as a semicolon? This |
| 210 | * allows for the semicolon processor to unconditionally add a newline |
| 211 | * while allowing things like inline comments on the same line to |
| 212 | * be bubbled up. |
| 213 | * $after_case |
| 214 | * Is the token being processed on the same line as a case? This |
| 215 | * is a specific override for comment movement behavior that places |
| 216 | * inline comments after a case before the case declaration. |
| 217 | * $after_comment |
| 218 | * Is the line being processed preceded by an inline comment? |
| 219 | * This is used to preserve newlines after comments. |
| 220 | * $after_initial_comment |
| 221 | * Is the line being processed preceded by the // $Id |
| 222 | * (ending dollar sign omitted) comment? This is a workaround to |
| 223 | * prevent the usual double-newline before docblocks for the very |
| 224 | * first docblock. |
| 225 | * $after_visibility_modifier |
| 226 | * Is the token being processed immediately preceded by a |
| 227 | * visibility modifier like public/protected/private? This prevents |
| 228 | * extra newlines added by T_FUNCTION. |
| 229 | * $after_return_in_case |
| 230 | * Whether or not the token is after a return statement in a case. |
| 231 | * This prevents the extra indent after case statements from being |
| 232 | * terminated prematurely for multiline return lines. |
| 233 | * |
| 234 | * @param $code |
| 235 | * The source code to format. |
| 236 | * |
| 237 | * @return |
| 238 | * The formatted code or false if it fails. |
| 239 | */ |
| 240 | function coder_format_string($code = '') { |
| 241 | global $_coder_indent; |
| 242 | |
| 243 | // Indent controls: |
| 244 | $_coder_indent = 0; |
| 245 | $in_case = false; |
| 246 | $switches = 0; |
| 247 | $parenthesis = 0; |
| 248 | $braces_in_case = array(); |
| 249 | $in_brace = false; |
| 250 | $in_heredoc = false; |
| 251 | $first_php_tag = true; |
| 252 | $in_do_while = false; |
| 253 | |
| 254 | // Whitespace controls: |
| 255 | $in_object = FALSE; |
| 256 | $in_at = FALSE; |
| 257 | $in_php = FALSE; |
| 258 | $in_quote = FALSE; |
| 259 | $inline_if = FALSE; |
| 260 | $in_array = array(); |
| 261 | $in_multiline = array(); |
| 262 | |
| 263 | // Context flags: |
| 264 | $after_semicolon = FALSE; |
| 265 | $after_case = FALSE; |
| 266 | $after_comment = FALSE; |
| 267 | $after_initial_comment = FALSE; |
| 268 | $after_visibility_modifier = FALSE; |
| 269 | $after_return_in_case = FALSE; |
| 270 | $after_php = FALSE; |
| 271 | |
| 272 | // Whether or not a function token was encountered: |
| 273 | $in_function_declaration = FALSE; |
| 274 | |
| 275 | // The position of the last character of the last non-whitespace |
| 276 | // non-comment token, e.g. it would be: |
| 277 | // function foo() { // bar |
| 278 | // ^ this character |
| 279 | $position_last_significant_token = 0; |
| 280 | |
| 281 | $result = ''; |
| 282 | $lasttoken = array(0); |
| 283 | $tokens = token_get_all($code); |
| 284 | |
| 285 | // Mask T_ML_COMMENT (PHP4) as T_COMMENT (PHP5). |
| 286 | if (!defined('T_ML_COMMENT')) { |
| 287 | define('T_ML_COMMENT', T_COMMENT); |
| 288 | } |
| 289 | // Mask T_DOC_COMMENT (PHP5) as T_ML_COMMENT (PHP4). |
| 290 | else if (!defined('T_DOC_COMMENT')) { |
| 291 | define('T_DOC_COMMENT', T_ML_COMMENT); |
| 292 | } |
| 293 | |
| 294 | foreach ($tokens as $token) { |
| 295 | if (is_string($token)) { |
| 296 | // Simple 1-character token. |
| 297 | $text = trim($token); |
| 298 | switch ($text) { |
| 299 | case '{': |
| 300 | // Add a space before and behind a curly brace, if we are in inline |
| 301 | // PHP, e.g. <?php if ($foo) { print $foo } |
| 302 | if ($after_php) { |
| 303 | $text = " $text "; |
| 304 | } |
| 305 | // Write curly braces at the end of lines followed by a line break if |
| 306 | // not in quotes (""), object ($foo->{$bar}) or in variables (${foo}). |
| 307 | // (T_DOLLAR_OPEN_CURLY_BRACES exists but is never assigned.) |
| 308 | $c = substr(rtrim($result), -1); |
| 309 | if (!$after_php && !$in_quote && (!$in_variable && !$in_object && $c != '$' || $c == ')')) { |
| 310 | if ($in_case) { |
| 311 | ++$braces_in_case[$switches]; |
| 312 | $_coder_indent += $switches - 1; |
| 313 | } |
| 314 | ++$_coder_indent; |
| 315 | $result = rtrim($result) .' '. $text; |
| 316 | coder_br($result); |
| 317 | } |
| 318 | else { |
| 319 | $in_brace = true; |
| 320 | $result .= $text; |
| 321 | } |
| 322 | break; |
| 323 | |
| 324 | case '}': |
| 325 | if (!$in_quote && !$in_brace && !$in_heredoc) { |
| 326 | if ($switches) { |
| 327 | --$braces_in_case[$switches]; |
| 328 | } |
| 329 | --$_coder_indent; |
| 330 | if ($braces_in_case[$switches] < 0 && $in_case) { |
| 331 | // Decrease indent if last case in a switch is not terminated. |
| 332 | --$_coder_indent; |
| 333 | $in_case = FALSE; |
| 334 | } |
| 335 | if ($braces_in_case[$switches] < 0) { |
| 336 | $braces_in_case[$switches] = 0; |
| 337 | $switches--; |
| 338 | } |
| 339 | if ($switches > 0) { |
| 340 | $in_case = TRUE; |
| 341 | } |
| 342 | |
| 343 | if (!$after_php) { |
| 344 | $result = rtrim($result); |
| 345 | if (substr($result, -1) != '{') { |
| 346 | // Avoid line break in empty curly braces. |
| 347 | coder_br($result); |
| 348 | } |
| 349 | $result .= $text; |
| 350 | coder_br($result); |
| 351 | } |
| 352 | else { |
| 353 | // Add a space before a curly brace, if we are in inline PHP, e.g. |
| 354 | // <?php if ($foo) { print $foo } |
| 355 | $result = rtrim($result, ' '); |
| 356 | if (substr($result, -1) !== "\n") { |
| 357 | $result .= ' '; |
| 358 | } |
| 359 | $result .= $text; |
| 360 | } |
| 361 | } |
| 362 | else { |
| 363 | $in_brace = false; |
| 364 | $result .= $text; |
| 365 | } |
| 366 | break; |
| 367 | |
| 368 | case ';': |
| 369 | $result = rtrim($result) . $text; |
| 370 | // Check if we had deferred reduction of indent because we were in |
| 371 | // a case statement. Now we can decrease the indent. |
| 372 | if ($after_return_in_case) { |
| 373 | --$_coder_indent; |
| 374 | $after_return_in_case = FALSE; |
| 375 | } |
| 376 | if (!$parenthesis && !$in_heredoc && !$after_php) { |
| 377 | coder_br($result); |
| 378 | $after_semicolon = TRUE; |
| 379 | } |
| 380 | else { |
| 381 | $result .= ' '; |
| 382 | } |
| 383 | if ($inline_if) { |
| 384 | $inline_if = false; |
| 385 | } |
| 386 | break; |
| 387 | |
| 388 | case '?': |
| 389 | $inline_if = true; |
| 390 | $result .= ' '. $text .' '; |
| 391 | break; |
| 392 | |
| 393 | case ':': |
| 394 | if ($inline_if) { |
| 395 | $result .= ' '. $text .' '; |
| 396 | } |
| 397 | elseif ($after_php) { |
| 398 | $result .= $text; |
| 399 | } |
| 400 | else { |
| 401 | if ($in_case) { |
| 402 | ++$_coder_indent; |
| 403 | } |
| 404 | $result = rtrim($result) . $text; |
| 405 | coder_br($result); |
| 406 | } |
| 407 | break; |
| 408 | |
| 409 | case '(': |
| 410 | $result .= $text; |
| 411 | ++$parenthesis; |
| 412 | // Not multiline until proven so by whitespace. |
| 413 | $in_multiline[$parenthesis] = FALSE; |
| 414 | // If the $in_array flag for this parenthesis level was not |
| 415 | // set previously, set it to FALSE. |
| 416 | if (!isset($in_array[$parenthesis])) { |
| 417 | $in_array[$parenthesis] = FALSE; |
| 418 | } |
| 419 | // Terminate function declaration, as a parenthesis indicates |
| 420 | // the beginning of the arguments. This will catch all other |
| 421 | // instances of parentheses, but in this case it's not a problem. |
| 422 | $in_function_declaration = FALSE; |
| 423 | break; |
| 424 | |
| 425 | case ')': |
| 426 | if ($in_array[$parenthesis] && $in_multiline[$parenthesis]) { |
| 427 | // Check if a comma insertion is necessary: |
| 428 | $c = $position_last_significant_token; |
| 429 | if ($result[$c] !== ',') { |
| 430 | // We need to add a comma at $c: |
| 431 | $result = substr($result, 0, $c + 1) .','. substr($result, $c + 1); |
| 432 | } |
| 433 | } |
| 434 | if (!$in_quote && !$in_heredoc && (substr(rtrim($result), -1) == ',' || $in_multiline[$parenthesis])) { |
| 435 | // Fix indent of right parenthesis in multiline structures by |
| 436 | // increasing indent for each parenthesis and decreasing one level. |
| 437 | $result = rtrim($result); |
| 438 | coder_br($result, $parenthesis - 1); |
| 439 | $result .= $text; |
| 440 | } |
| 441 | else { |
| 442 | $result .= $text; |
| 443 | } |
| 444 | if ($parenthesis) { |
| 445 | // Current parenthesis level is not an array anymore. |
| 446 | $in_array[$parenthesis] = FALSE; |
| 447 | --$parenthesis; |
| 448 | } |
| 449 | break; |
| 450 | |
| 451 | case '@': |
| 452 | $in_at = true; |
| 453 | $result .= $text; |
| 454 | break; |
| 455 | |
| 456 | case ',': |
| 457 | $result .= $text .' '; |
| 458 | break; |
| 459 | |
| 460 | case '.': |
| 461 | // Starting from 7.x, string concatenations follow PEAR's standard. |
| 462 | $result = rtrim($result) . ' ' . $text . ' '; |
| 463 | break; |
| 464 | |
| 465 | case '=': |
| 466 | case '<': |
| 467 | case '>': |
| 468 | case '+': |
| 469 | case '*': |
| 470 | case '/': |
| 471 | case '|': |
| 472 | case '^': |
| 473 | case '%': |
| 474 | $result = rtrim($result) .' '. $text .' '; |
| 475 | break; |
| 476 | |
| 477 | case '&': |
| 478 | if (substr(rtrim($result), -1) == '=' || substr(rtrim($result), -1) == '(' || substr(rtrim($result), -1) == ',') { |
| 479 | $result .= $text; |
| 480 | } |
| 481 | else { |
| 482 | $result = rtrim($result) .' '. $text; |
| 483 | // Ampersands used to declare reference return value for |
| 484 | // functions should not have trailing space. |
| 485 | if (!$in_function_declaration) { |
| 486 | $result .= ' '; |
| 487 | } |
| 488 | } |
| 489 | break; |
| 490 | |
| 491 | case '-': |
| 492 | $result = rtrim($result); |
| 493 | // Do not add a space before negative numbers or variables. |
| 494 | $c = substr($result, -1); |
| 495 | // Do not add a space between closing parenthesis and negative arithmetic operators. |
| 496 | if ($c == '(') { |
| 497 | $result .= ltrim($text); |
| 498 | } |
| 499 | // Add a space in front of the following chars, but not after them. |
| 500 | elseif ($c == '>' || $c == '=' || $c == ',' || $c == ':' || $c == '?') { |
| 501 | $result .= ' '. $text; |
| 502 | } |
| 503 | // Default arithmetic operator behavior. |
| 504 | else { |
| 505 | $result .= ' '. $text .' '; |
| 506 | } |
| 507 | break; |
| 508 | |
| 509 | case '"': |
| 510 | // Toggle quote if the char is not escaped. |
| 511 | if (rtrim($result) != "\\") { |
| 512 | $in_quote = $in_quote ? false : true; |
| 513 | } |
| 514 | $result .= $text; |
| 515 | break; |
| 516 | |
| 517 | default: |
| 518 | $result .= $text; |
| 519 | break; |
| 520 | } |
| 521 | |
| 522 | // All text possibilities are significant: |
| 523 | $position_last_significant_token = strlen(rtrim($result)) - 1; |
| 524 | |
| 525 | // Because they are all significant, we cannot possibly be after |
| 526 | // a comment now. |
| 527 | $after_comment = FALSE; |
| 528 | $after_initial_comment = FALSE; |
| 529 | |
| 530 | // TODO: Make resetting context flags easier to do. |
| 531 | } |
| 532 | else { |
| 533 | // If we get here, then we have found not a single char, but a token. |
| 534 | // See <http://www.php.net/manual/en/tokens.php> for a reference. |
| 535 | |
| 536 | // Fetch token array. |
| 537 | list($id, $text) = $token; |
| 538 | |
| 539 | // Debugging: |
| 540 | /* |
| 541 | if ($lasttoken[0] == T_WHITESPACE) { |
| 542 | $result .= token_name($id); |
| 543 | } |
| 544 | */ |
| 545 | |
| 546 | |
| 547 | switch ($id) { |
| 548 | case T_ARRAY: |
| 549 | // Write array in lowercase. |
| 550 | $result .= strtolower(trim($text)); |
| 551 | // Mark the next parenthesis level (we haven't consumed that token |
| 552 | // yet) as an array. |
| 553 | $in_array[$parenthesis + 1] = TRUE; |
| 554 | break; |
| 555 | |
| 556 | case T_OPEN_TAG: |
| 557 | case T_OPEN_TAG_WITH_ECHO: |
| 558 | $in_php = true; |
| 559 | // Add a line break between two PHP tags. |
| 560 | if (substr(rtrim($result), -2) == '?>' && !$after_php) { |
| 561 | coder_br($result); |
| 562 | } |
| 563 | $after_php = true; |
| 564 | $nl = substr_count($text, "\n"); |
| 565 | $result .= trim($text); |
| 566 | if ($first_php_tag) { |
| 567 | coder_br($result); |
| 568 | $first_php_tag = FALSE; |
| 569 | } |
| 570 | else { |
| 571 | if ($nl) { |
| 572 | coder_br($result, $parenthesis); |
| 573 | } |
| 574 | else { |
| 575 | $result .= ' '; |
| 576 | } |
| 577 | } |
| 578 | break; |
| 579 | |
| 580 | case T_CLOSE_TAG: |
| 581 | $in_php = false; |
| 582 | if ($after_php) { |
| 583 | $result = rtrim($result, ' ') .' '; |
| 584 | $text = ltrim($text, ' '); |
| 585 | } |
| 586 | // Do not alter a closing PHP tag ($text includes trailing white-space) |
| 587 | // at all. Should allow to apply coder_format on phptemplate files. |
| 588 | $result .= $text; |
| 589 | break; |
| 590 | |
| 591 | case T_OBJECT_OPERATOR: |
| 592 | $in_object = true; |
| 593 | $result .= trim($text); |
| 594 | break; |
| 595 | |
| 596 | case T_CONSTANT_ENCAPSED_STRING: |
| 597 | case T_STRING: |
| 598 | case T_VARIABLE: |
| 599 | // Boolean constants (TRUE, FALSE, NULL) are T_STRINGs, but must be |
| 600 | // written uppercase. |
| 601 | $text = trim($text); |
| 602 | if ($text == 'true' || $text == 'false' || $text == 'null') { |
| 603 | $text = strtoupper($text); |
| 604 | } |
| 605 | // No space after object operator ($foo->bar) and error suppression (@function()). |
| 606 | if ($in_object || $in_at) { |
| 607 | $result = rtrim($result) . $text; |
| 608 | $in_object = false; |
| 609 | $in_at = false; |
| 610 | } |
| 611 | else { |
| 612 | // Insert a space after right parenthesis, but not after type casts. |
| 613 | if (!in_array($lasttoken[0], array(T_ARRAY_CAST, T_BOOL_CAST, T_DOUBLE_CAST, T_INT_CAST, T_OBJECT_CAST, T_STRING_CAST, T_UNSET_CAST))) { |
| 614 | coder_add_space($result); |
| 615 | } |
| 616 | $result .= $text; |
| 617 | } |
| 618 | $in_variable = true; |
| 619 | break; |
| 620 | |
| 621 | case T_CONST: |
| 622 | // Constants are written uppercase. |
| 623 | $result = rtrim($result) . strtoupper(trim($text)); |
| 624 | break; |
| 625 | |
| 626 | case T_ENCAPSED_AND_WHITESPACE: |
| 627 | $result .= $text; |
| 628 | break; |
| 629 | |
| 630 | case T_WHITESPACE: |
| 631 | // Avoid duplicate line feeds outside arrays. |
| 632 | $c = ($parenthesis || $after_comment) ? 0 : 1; |
| 633 | |
| 634 | for ($c, $cc = substr_count($text, "\n"); $c < $cc; ++$c) { |
| 635 | // Newlines were added; not after semicolon anymore |
| 636 | coder_br($result, $parenthesis); |
| 637 | } |
| 638 | |
| 639 | // If there were newlines present inside a parenthesis, |
| 640 | // turn on multiline mode. |
| 641 | if ($cc && $parenthesis) { |
| 642 | $in_multiline[$parenthesis] = TRUE; |
| 643 | } |
| 644 | |
| 645 | // If there were newlines present, move inline comments above. |
| 646 | if ($cc) { |
| 647 | $after_semicolon = FALSE; |
| 648 | $after_case = FALSE; |
| 649 | $after_php = FALSE; |
| 650 | } |
| 651 | |
| 652 | $in_variable = FALSE; |
| 653 | break; |
| 654 | |
| 655 | case T_SWITCH: |
| 656 | ++$switches; |
| 657 | // Purposely fall through. |
| 658 | case T_IF: |
| 659 | case T_FOR: |
| 660 | case T_FOREACH: |
| 661 | case T_GLOBAL: |
| 662 | case T_STATIC: |
| 663 | case T_ECHO: |
| 664 | case T_PRINT: |
| 665 | case T_NEW: |
| 666 | case T_REQUIRE: |
| 667 | case T_REQUIRE_ONCE: |
| 668 | case T_INCLUDE: |
| 669 | case T_INCLUDE_ONCE: |
| 670 | case T_VAR: |
| 671 | coder_add_space($result); |
| 672 | // Append a space. |
| 673 | $result .= trim($text) .' '; |
| 674 | break; |
| 675 | |
| 676 | case T_DO: |
| 677 | $result .= trim($text); |
| 678 | $in_do_while = true; |
| 679 | break; |
| 680 | |
| 681 | case T_WHILE: |
| 682 | if ($in_do_while && substr(rtrim($result), -1) === '}') { |
| 683 | // Write while after right parenthesis for do {...} while(). |
| 684 | $result = rtrim($result) .' '; |
| 685 | $in_do_while = false; |
| 686 | } |
| 687 | // Append a space. |
| 688 | $result .= trim($text) .' '; |
| 689 | break; |
| 690 | |
| 691 | case T_ELSE: |
| 692 | case T_ELSEIF: |
| 693 | // Write else and else if to a new line. |
| 694 | $result = rtrim($result); |
| 695 | coder_br($result); |
| 696 | $result .= trim($text) .' '; |
| 697 | break; |
| 698 | |
| 699 | case T_CASE: |
| 700 | case T_DEFAULT: |
| 701 | $braces_in_case[$switches] = 0; |
| 702 | $result = rtrim($result); |
| 703 | $after_case = true; |
| 704 | if (!$in_case) { |
| 705 | $in_case = true; |
| 706 | // Add a line break between cases. |
| 707 | if (substr($result, -1) != '{') { |
| 708 | coder_br($result); |
| 709 | } |
| 710 | } |
| 711 | else { |
| 712 | // Decrease current indent to align multiple cases. |
| 713 | --$_coder_indent; |
| 714 | } |
| 715 | coder_br($result); |
| 716 | $result .= trim($text) .' '; |
| 717 | break; |
| 718 | |
| 719 | case T_BREAK: |
| 720 | // Write break to a new line. |
| 721 | $result = rtrim($result); |
| 722 | coder_br($result); |
| 723 | // Trailing space needed for 'break 3;'. |
| 724 | $result .= trim($text) . ' '; |
| 725 | if ($in_case && !$braces_in_case[$switches]) { |
| 726 | --$_coder_indent; |
| 727 | $in_case = FALSE; |
| 728 | } |
| 729 | break; |
| 730 | |
| 731 | case T_RETURN: |
| 732 | if ($in_case && !$braces_in_case[$switches]) { |
| 733 | // Defer reduction of indent for later. |
| 734 | ++$_coder_indent; |
| 735 | $after_return_in_case = true; |
| 736 | } |
| 737 | case T_CONTINUE: |
| 738 | coder_add_space($result); |
| 739 | $result .= trim($text) .' '; |
| 740 | // Decrease indent only if we're not in a control structure inside a case. |
| 741 | if ($in_case && !$braces_in_case[$switches]) { |
| 742 | --$_coder_indent; |
| 743 | $in_case = false; |
| 744 | } |
| 745 | break; |
| 746 | |
| 747 | case T_ABSTRACT: |
| 748 | case T_PRIVATE: |
| 749 | case T_PUBLIC: |
| 750 | case T_PROTECTED: |
| 751 | // Class member function properties must be treated similar to |
| 752 | // T_FUNCTION, but without line-break after the token. Because more |
| 753 | // than one of these tokens can appear in front of a function token, |
| 754 | // we need another white-space control variable. |
| 755 | $result .= trim($text) .' '; |
| 756 | $after_visibility_modifier = TRUE; |
| 757 | break; |
| 758 | |
| 759 | case T_FUNCTION: |
| 760 | $in_function_declaration = TRUE; |
| 761 | // Fall through. |
| 762 | case T_CLASS: |
| 763 | // Write function and class to new lines. |
| 764 | $result = rtrim($result); |
| 765 | if (substr($result, -1) == '}') { |
| 766 | coder_br($result); |
| 767 | } |
| 768 | if (!$after_visibility_modifier) { |
| 769 | coder_br($result); |
| 770 | } |
| 771 | else { |
| 772 | // This code only applies to T_FUNCTION; do not add a newline |
| 773 | // after public/protected/private/abstract. |
| 774 | $after_visibility_modifier = FALSE; |
| 775 | $result .= ' '; |
| 776 | } |
| 777 | $result .= trim($text) .' '; |
| 778 | break; |
| 779 | |
| 780 | case T_EXTENDS: |
| 781 | case T_INSTANCEOF: |
| 782 | // Add space before and after 'extends' and 'instanceof'. |
| 783 | $result = rtrim($result); |
| 784 | $result .= ' '. trim($text) .' '; |
| 785 | break; |
| 786 | |
| 787 | case T_AND_EQUAL: |
| 788 | case T_AS: |
| 789 | case T_BOOLEAN_AND: |
| 790 | case T_BOOLEAN_OR: |
| 791 | case T_CONCAT_EQUAL: |
| 792 | case T_DIV_EQUAL: |
| 793 | case T_DOUBLE_ARROW: |
| 794 | case T_IS_EQUAL: |
| 795 | case T_IS_NOT_EQUAL: |
| 796 | case T_IS_IDENTICAL: |
| 797 | case T_IS_NOT_IDENTICAL: |
| 798 | case T_IS_GREATER_OR_EQUAL: |
| 799 | case T_IS_SMALLER_OR_EQUAL: |
| 800 | case T_LOGICAL_AND: |
| 801 | case T_LOGICAL_OR: |
| 802 | case T_LOGICAL_XOR: |
| 803 | case T_MINUS_EQUAL: |
| 804 | case T_MOD_EQUAL: |
| 805 | case T_MUL_EQUAL: |
| 806 | case T_OR_EQUAL: |
| 807 | case T_PLUS_EQUAL: |
| 808 | case T_SL: |
| 809 | case T_SL_EQUAL: |
| 810 | case T_SR: |
| 811 | case T_SR_EQUAL: |
| 812 | case T_XOR_EQUAL: |
| 813 | // Surround operators with spaces. |
| 814 | if (substr($result, -1) != ' ') { |
| 815 | // $result must not be trimmed to allow multi-line if-clauses. |
| 816 | $result .= ' '; |
| 817 | } |
| 818 | $result .= trim($text) .' '; |
| 819 | break; |
| 820 | |
| 821 | case T_COMMENT: |
| 822 | case T_ML_COMMENT: |
| 823 | case T_DOC_COMMENT: |
| 824 | if (substr($text, 0, 3) == '/**') { |
| 825 | // Prepend a new line. |
| 826 | $result = rtrim($result); |
| 827 | if (!$after_initial_comment) { |
| 828 | coder_br($result); |
| 829 | } |
| 830 | else { |
| 831 | // This probably will get set below, but it's good to |
| 832 | // explicitly turn it off after the initial comment has |
| 833 | // influenced behavior and now is not necessary. |
| 834 | $after_initial_comment = FALSE; |
| 835 | } |
| 836 | coder_br($result); |
| 837 | |
| 838 | // Remove carriage returns. |
| 839 | $text = str_replace("\r", '', $text); |
| 840 | |
| 841 | $lines = explode("\n", $text); |
| 842 | $params_fixed = false; |
| 843 | for ($l = 0; $l < count($lines); ++$l) { |
| 844 | $lines[$l] = trim($lines[$l]); |
| 845 | |
| 846 | // Add a new line between function description and first parameter description. |
| 847 | if (!$params_fixed && substr($lines[$l], 0, 8) == '* @param' && $lines[$l - 1] != '*') { |
| 848 | $result .= ' *'; |
| 849 | coder_br($result); |
| 850 | $params_fixed = true; |
| 851 | } |
| 852 | else if (!$params_fixed && substr($lines[$l], 0, 8) == '* @param') { |
| 853 | // Do nothing if parameter description is properly formatted. |
| 854 | $params_fixed = true; |
| 855 | } |
| 856 | |
| 857 | // Add a new line between function params and return. |
| 858 | if (substr($lines[$l], 0, 9) == '* @return' && $lines[$l - 1] != '*') { |
| 859 | $result .= ' *'; |
| 860 | coder_br($result); |
| 861 | } |
| 862 | |
| 863 | // Add one space indent to get ' *[...]'. |
| 864 | if ($l > 0) { |
| 865 | $result .= ' '; |
| 866 | } |
| 867 | $result .= $lines[$l]; |
| 868 | if ($l < count($lines)) { |
| 869 | coder_br($result); |
| 870 | } |
| 871 | } |
| 872 | } |
| 873 | else { |
| 874 | // Move the comment above if it's embedded. |
| 875 | $statement = false; |
| 876 | // Some PHP versions throw a warning about wrong parameter count for |
| 877 | // substr_count(). |
| 878 | $cc = substr_count(substr($result, $position_last_significant_token), "\n"); |
| 879 | if ((!$cc || $after_semicolon) && !$after_case) { |
| 880 | $nl_position = strrpos(rtrim($result, " \n"), "\n"); |
| 881 | $statement = substr($result, $nl_position); |
| 882 | $result = substr($result, 0, $nl_position); |
| 883 | $after_semicolon = false; |
| 884 | coder_br($result, $parenthesis); |
| 885 | } |
| 886 | $result .= trim($text); |
| 887 | coder_br($result, $parenthesis); |
| 888 | if ($statement) { |
| 889 | // Newlines are automatically added, so remove these. |
| 890 | $result = rtrim($result, "\n "); |
| 891 | $result .= rtrim($statement, "\n "); |
| 892 | coder_br($result, $parenthesis); |
| 893 | // Need to update this, as our comment trickery has just |
| 894 | // reshuffled the index. |
| 895 | $position_last_significant_token = strlen(rtrim($result, " \n")) - 1; |
| 896 | } |
| 897 | else { |
| 898 | if (strpos($text, '$' . 'Id$') === FALSE) { |
| 899 | $after_comment = TRUE; |
| 900 | } |
| 901 | else { |
| 902 | // Is the number two so that our bottom code doesn't override |
| 903 | // our flag immediately. |
| 904 | $after_initial_comment = 2; |
| 905 | } |
| 906 | } |
| 907 | } |
| 908 | break; |
| 909 | |
| 910 | case T_INLINE_HTML: |
| 911 | $result .= $text; |
| 912 | break; |
| 913 | |
| 914 | case T_START_HEREDOC: |
| 915 | $result .= trim($text); |
| 916 | coder_br($result, FALSE, FALSE); |
| 917 | $in_heredoc = TRUE; |
| 918 | break; |
| 919 | |
| 920 | case T_END_HEREDOC: |
| 921 | $result .= trim($text); |
| 922 | coder_br($result, FALSE, FALSE); |
| 923 | $in_heredoc = FALSE; |
| 924 | break; |
| 925 | |
| 926 | default: |
| 927 | $result .= trim($text); |
| 928 | break; |
| 929 | } |
| 930 | |
| 931 | // Store last token. |
| 932 | $lasttoken = $token; |
| 933 | |
| 934 | // Excluding comments and whitespace, set the position of the |
| 935 | // last significant token's last character to the length of the |
| 936 | // string minus one. |
| 937 | switch ($id) { |
| 938 | case T_WHITESPACE: |
| 939 | case T_COMMENT: |
| 940 | case T_ML_COMMENT: |
| 941 | case T_DOC_COMMENT: |
| 942 | break; |
| 943 | |
| 944 | default: |
| 945 | $position_last_significant_token = strlen(rtrim($result, " \n")) - 1; |
| 946 | break; |
| 947 | } |
| 948 | |
| 949 | if ($id !== T_COMMENT && $id !== T_ML_COMMENT) { |
| 950 | $after_comment = FALSE; |
| 951 | } |
| 952 | if ($after_initial_comment && $id !== T_WHITESPACE) $after_initial_comment--; |
| 953 | } |
| 954 | } |
| 955 | return $result; |
| 956 | } |
| 957 | |
| 958 | /** |
| 959 | * Generate a line feed including current line indent. |
| 960 | * |
| 961 | * This function will also remove all line indentation from the |
| 962 | * previous line if no text was added. |
| 963 | * |
| 964 | * @param &$result |
| 965 | * Result variable to append break and indent to, passed by reference. |
| 966 | * @param $parenthesis |
| 967 | * Optional integer of parentheses level for extra indents. |
| 968 | * @param $add_indent |
| 969 | * Whether to add current line indent after line feed. |
| 970 | */ |
| 971 | function coder_br(&$result, $parenthesis = false, $add_indent = true) { |
| 972 | global $_coder_indent; |
| 973 | |
| 974 | // Scan result backwards for whitespace. |
| 975 | for ($i = strlen($result) - 1; $i >= 0; $i--) { |
| 976 | if ($result[$i] == ' ') { |
| 977 | continue; |
| 978 | } |
| 979 | if ($result[$i] == "\n") { |
| 980 | $result = rtrim($result, ' '); |
| 981 | break; |
| 982 | } |
| 983 | // Non-whitespace was encountered, no changes necessary. |
| 984 | break; |
| 985 | } |
| 986 | |
| 987 | if ($parenthesis) { |
| 988 | // Add extra indent for each parenthesis in multiline definitions (f.e. arrays). |
| 989 | $_coder_indent = $_coder_indent + $parenthesis; |
| 990 | $result = rtrim($result); |
| 991 | // This recursive call will only be done once, as $parenthesis is |
| 992 | // set to false. |
| 993 | coder_br($result, false, $add_indent); |
| 994 | $_coder_indent = $_coder_indent - $parenthesis; |
| 995 | } |
| 996 | else { |
| 997 | $output = "\n"; |
| 998 | if ($add_indent && $_coder_indent >= 0) { |
| 999 | $output .= str_repeat(' ', $_coder_indent); |
| 1000 | } |
| 1001 | $result .= $output; |
| 1002 | } |
| 1003 | } |
| 1004 | |
| 1005 | /** |
| 1006 | * Write a space in certain conditions. |
| 1007 | * |
| 1008 | * A conditional space is needed after a right parenthesis of an if statement |
| 1009 | * that is not followed by curly braces. |
| 1010 | * |
| 1011 | * @param $result |
| 1012 | * Current result string that will be checked. |
| 1013 | * |
| 1014 | * @return |
| 1015 | * Resulting string with or without an additional space. |
| 1016 | */ |
| 1017 | function coder_add_space(&$result) { |
| 1018 | if (substr($result, -1) == ')') { |
| 1019 | $result .= ' '; |
| 1020 | } |
| 1021 | } |
| 1022 | |
| 1023 | /** |
| 1024 | * Trim overall code. |
| 1025 | * |
| 1026 | * Strips whitespace at the beginning and end of code, |
| 1027 | * removes the closing PHP tag and appends two empty lines. |
| 1028 | */ |
| 1029 | function coder_trim_php($code) { |
| 1030 | // Remove surrounding whitespace. |
| 1031 | $code = trim($code); |
| 1032 | |
| 1033 | // Insert CVS keyword Id. |
| 1034 | // Search in the very first 1000 chars, insert only one instance. |
| 1035 | if (strpos(substr($code, 0, 1000), '$Id') === false) { |
| 1036 | $code = preg_replace('/<\?php\n/', "<?php\n// \$Id\$\n\n", $code, 1); |
| 1037 | } |
| 1038 | |
| 1039 | // Remove closing PHP tag. |
| 1040 | if (substr($code, -2) == '?>') { |
| 1041 | $code = rtrim($code, '?>'); |
| 1042 | } |
| 1043 | |
| 1044 | // Append two empty lines. |
| 1045 | $code .= str_repeat(chr(10), 2); |
| 1046 | |
| 1047 | return $code; |
| 1048 | } |
| 1049 | |
| 1050 | /** |
| 1051 | * Execute special tasks on source code. |
| 1052 | * |
| 1053 | * This function works similar to the Drupal hook and forms system. It searches |
| 1054 | * for all defined functions with the given prefix and performs a preg_replace |
| 1055 | * on the source code for each of these functions. |
| 1056 | * |
| 1057 | * Processor functions are defined with a associative array containing the |
| 1058 | * following keys with the corresponding values: |
| 1059 | * #title |
| 1060 | * A human readable text describing what the processor actually does. |
| 1061 | * #search |
| 1062 | * The regular expression to search for. |
| 1063 | * #replace |
| 1064 | * The replacement text for each match. |
| 1065 | * |
| 1066 | * Optional definitions: |
| 1067 | * #debug |
| 1068 | * Set this to true to directly output the results of preg_match_all and |
| 1069 | * exit script execution after this processor. |
| 1070 | * |
| 1071 | * @param string $code |
| 1072 | * The source code to process. |
| 1073 | * @param string $prefix |
| 1074 | * Prefix of the functions to execute. |
| 1075 | * |
| 1076 | * @return |
| 1077 | * The processed source code. |
| 1078 | */ |
| 1079 | function coder_exec_processors($code, $prefix = '') { |
| 1080 | if (empty($prefix)) { |
| 1081 | return; |
| 1082 | } |
| 1083 | $tasks = get_defined_functions(); |
| 1084 | $tasks = $tasks['user']; |
| 1085 | for ($c = 0, $cc = count($tasks); $c < $cc; ++$c) { |
| 1086 | if (strpos($tasks[$c], $prefix) === false) { |
| 1087 | unset($tasks[$c]); |
| 1088 | } |
| 1089 | else { |
| 1090 | $tasks[$tasks[$c]] = call_user_func($tasks[$c]); |
| 1091 | unset($tasks[$c]); |
| 1092 | } |
| 1093 | } |
| 1094 | uasort($tasks, 'coder_order_processors'); |
| 1095 | foreach ($tasks as $func => $task) { |
| 1096 | if (!isset($task['#search']) || (!isset($task['#replace']) && !isset($task['#replace_callback']))) { |
| 1097 | continue; |
| 1098 | } |
| 1099 | if (isset($task['#debug'])) { |
| 1100 | // Output regular expression results if debugging is enabled. |
| 1101 | preg_match_all($task['#search'], $code, $matches, PREG_SET_ORDER); |
| 1102 | echo "<pre>"; |
| 1103 | var_dump($matches); |
| 1104 | echo "</pre>\n"; |
| 1105 | // Exit immediately in debugging mode. |
| 1106 | exit; |
| 1107 | } |
| 1108 | if (isset($task['#replace_callback'])) { |
| 1109 | $code = preg_replace_callback($task['#search'], $task['#replace_callback'], $code); |
| 1110 | } |
| 1111 | else { |
| 1112 | $code = preg_replace($task['#search'], $task['#replace'], $code); |
| 1113 | } |
| 1114 | } |
| 1115 | |
| 1116 | return $code; |
| 1117 | } |
| 1118 | |
| 1119 | /** |
| 1120 | * Orders preprocessors by weight. |
| 1121 | * |
| 1122 | * @see coder_exec_processors() |
| 1123 | */ |
| 1124 | function coder_order_processors($a, $b) { |
| 1125 | if (isset($a['#weight']) && isset($b['#weight'])) { |
| 1126 | return $a['#weight'] - $b['#weight']; |
| 1127 | } |
| 1128 | else { |
| 1129 | return isset($a['#weight']) ? false : true; |
| 1130 | } |
| 1131 | } |
| 1132 | |
| 1133 | /** |
| 1134 | * @defgroup coder_preprocessor Preprocessors. |
| 1135 | * @{ |
| 1136 | */ |
| 1137 | function coder_preprocessor_line_breaks_win() { |
| 1138 | return array( |
| 1139 | '#title' => 'Convert Windows line breaks to Unix format.', |
| 1140 | '#weight' => 1, |
| 1141 | '#search' => "@\r\n@", |
| 1142 | '#replace' => "\n", |
| 1143 | ); |
| 1144 | } |
| 1145 | |
| 1146 | function coder_preprocessor_line_breaks_mac() { |
| 1147 | return array( |
| 1148 | '#title' => 'Convert Macintosh line breaks to Unix format.', |
| 1149 | '#weight' => 2, |
| 1150 | '#search' => "@\r@", |
| 1151 | '#replace' => "\n", |
| 1152 | ); |
| 1153 | } |
| 1154 | |
| 1155 | function coder_preprocessor_php() { |
| 1156 | return array( |
| 1157 | '#title' => 'Always use <?php ?> to delimit PHP code, not the <? ?> shorthands.', |
| 1158 | '#search' => '@<\?(\s)@', |
| 1159 | '#replace' => "<?php$1", |
| 1160 | ); |
| 1161 | } |
| 1162 | |
| 1163 | function coder_preprocessor_switch_duplicate_exit() { |
| 1164 | return array( |
| 1165 | '#title' => 'Either exit a switch case with return *or* break.', |
| 1166 | '#search' => '@ |
| 1167 | (return # match a return |
| 1168 | \s+ # - followed by some white-space |
| 1169 | .+ # - followed by any characters |
| 1170 | ; # - followed by a semicolon |
| 1171 | ) |
| 1172 | \s+ # match white-space (required) |
| 1173 | break; # match a directly following "break;" |
| 1174 | @mx', |
| 1175 | '#replace' => '$1', |
| 1176 | ); |
| 1177 | } |
| 1178 | |
| 1179 | function coder_preprocessor_inline_comment() { |
| 1180 | return array( |
| 1181 | '#title' => 'Move inline comments above remarked line.', |
| 1182 | '#weight' => 2, |
| 1183 | '#search' => '@ |
| 1184 | ^([\040\t]*) # match spaces or tabs only. |
| 1185 | (?!case) # do not match case statements. |
| 1186 | (\S.+? # do not match lines containing only a comment. |
| 1187 | [;,{] # match the TRICKY lines only. |
| 1188 | ) |
| 1189 | [\040\t]* # match spaces or tabs only. |
| 1190 | (?!:) # do not match URL protocols. |
| 1191 | //\s* # match inline comment token. |
| 1192 | ([^;\$]+?)$ # fetch comment, but do not match CVS keyword Id, nested comments, and comment tokens in quotes (f.e. "W3C//DTD"). |
| 1193 | @mx', |
| 1194 | '#replace' => "$1// $3\n$1$2", |
| 1195 | ); |
| 1196 | } |
| 1197 | |
| 1198 | /** |
| 1199 | * @} End of "defgroup coder_preprocessor". |
| 1200 | */ |
| 1201 | |
| 1202 | /** |
| 1203 | * @defgroup coder_postprocessor Postprocessors. |
| 1204 | * @{ |
| 1205 | */ |
| 1206 | function coder_postprocessor_cvs_id() { |
| 1207 | return array( |
| 1208 | '#title' => 'If the CVS keyword Id already exists, append a new line after it.', |
| 1209 | '#search' => '@ |
| 1210 | ^( # match start of a line |
| 1211 | //.* # match an inline comment followed by any characters |
| 1212 | \$Id.*\$ # match a CVS Id tag |
| 1213 | )$ # match end of a line |
| 1214 | @mx', |
| 1215 | '#replace' => "$1\n", |
| 1216 | ); |
| 1217 | } |
| 1218 | |
| 1219 | function coder_postprocessor_multiple_vars() { |
| 1220 | return array( |
| 1221 | '#title' => 'Align equal signs of multiple variable assignments in the same column.', |
| 1222 | '#search' => '@ |
| 1223 | ^( # match start of a line |
| 1224 | \n?\ * # match white-space, but only one new line |
| 1225 | \$.+? # match a variable name |
| 1226 | \ =\ # match a variable assignment |
| 1227 | .+?$ # match a variable value |
| 1228 | ){3,} # require the pattern to match at least 3 times |
| 1229 | @mx', |
| 1230 | '#replace_callback' => 'coder_replace_multiple_vars', |
| 1231 | ); |
| 1232 | } |
| 1233 | |
| 1234 | function coder_replace_multiple_vars($matches) { |
| 1235 | // Retrieve all variable name = variable value pairs. |
| 1236 | $regex = '@ |
| 1237 | ^ # match start of a line |
| 1238 | (\s*) # match a single optional white-space char |
| 1239 | (\$.+?) # match a variable name |
| 1240 | \ (.?)=\ # match a variable assignment |
| 1241 | (.+?$) # match a variable value including end of line |
| 1242 | @mx'; |
| 1243 | preg_match_all($regex, $matches[0], $vars, PREG_SET_ORDER); |
| 1244 | |
| 1245 | // Determine the longest variable name. |
| 1246 | $maxlength = 0; |
| 1247 | foreach ($vars as $var) { |
| 1248 | if (strlen($var[2]) > $maxlength) { |
| 1249 | $maxlength = strlen($var[2] . $var[3]); |
| 1250 | } |
| 1251 | } |
| 1252 | |
| 1253 | // Realign variable values at the longest variable names. |
| 1254 | $return = ''; |
| 1255 | $extra_spaces = 0; |
| 1256 | for ($c = 0, $cc = count($vars); $c < $cc; ++$c) { |
| 1257 | if ($maxlength <= 20) { |
| 1258 | $extra_spaces = $maxlength - strlen($vars[$c][2] . $vars[$c][3]); |
| 1259 | } |
| 1260 | $return .= $vars[$c][1] . $vars[$c][2]; |
| 1261 | $return .= str_repeat(' ', $extra_spaces) .' '. $vars[$c][3] .'= '; |
| 1262 | $return .= $vars[$c][4]; |
| 1263 | if ($c < $cc - 1) { |
| 1264 | // Append a line break, but not to the last variable assignment. |
| 1265 | $return .= "\n"; |
| 1266 | } |
| 1267 | } |
| 1268 | |
| 1269 | return $return; |
| 1270 | } |
| 1271 | |
| 1272 | function coder_postprocessor_indent_multiline_array() { |
| 1273 | // Still buggy, disabled for now. |
| 1274 | return array( |
| 1275 | '#title' => 'Align equal signs of multiline array assignments in the same column.', |
| 1276 | '#search' => '@ |
| 1277 | ^ # match start of a line |
| 1278 | (?:\s* # require initial white-space |
| 1279 | (?: |
| 1280 | (?: |
| 1281 | ([\'"]).+?\1 # capture a string key |
| 1282 | |.+? # or any other key without white-space |
| 1283 |