You are here

function coder_format_string in Coder 5

Same name and namespace in other branches
  1. 5.2 scripts/coder_format/coder_format.inc \coder_format_string()
  2. 6.2 scripts/coder_format/coder_format.inc \coder_format_string()
  3. 6 scripts/coder_format/coder_format.inc \coder_format_string()
  4. 7.2 scripts/coder_format/coder_format.inc \coder_format_string()
  5. 7 scripts/coder_format/coder_format.inc \coder_format_string()

Format the source code according to Drupal coding style guidelines.

This function uses PHP's tokenizer functions.

To achieve the desired coding style, we have to take some special cases into account. These are:

Indent-related: $_coder_indent int Indent level The number of indents for the next line. This is

  • increased after {, : (after case and default).
  • decreased after }, break, case and default (after a previous case).

$in_case bool Is true after case and default. Is false after break and return, if $braces_in_case is not greater than 0. $braces_in_case int Count of braces The number of currently opened curly braces in a case. This is needed to support arbitrary function exits inside of a switch control strucure. $parenthesis int Parenthesis level The number of currently opened parenthesis. This

  • prevents line feeds in brackets (f.e. in arguments of for()).
  • is the base for formatting of multiline arrays. Note: If the last ');' is not formatted to the correct indent level then there is no ',' (comma) behind the last array value.

$in_brace bool Is true after left curly braces if they are in quotes, an object or after a dollar sign. Prevents line breaks around such variable statements. $in_heredoc bool Is true after heredoc output method and false after heredoc delimiter. Prevents line breaks in heredocs. $first_php_tag bool Is false after the first PHP tag. Allows inserting a line break after the first one. $in_do_while bool Is true after a do {} statement and set to false in the next while statement. Prevents a line break in the do {...} while() construct.

Whitespace-related: $in_object bool Prevents whitespace after ->. Is true after ->. Is reset to false after the next string or variable. $in_at bool Prevents whitespace after @. Is true after @. Is reset to false after the next string or variable. $in_quote bool Prevents

  • removal of whitespace in double quotes.
  • injection of new line feeds after brackets in double quotes.

$inline_if bool Controls formatting of ? and : for inline ifs until a ; (semicolon) is processed.

Parameters

string $code: The source code to format.

Return value

mixed $result Returns the formatted code or false if it fails.

See also

<http://www.php.net/manual/en/ref.tokenizer.php>

1 call to coder_format_string()
coder_format_file in scripts/coder_format/coder_format.inc
Reads, processes and writes the source code from and to a file.

File

scripts/coder_format/coder_format.inc, line 163

Code

function coder_format_string($code = '') {
  global $_coder_indent;

  // indent controls
  $_coder_indent = 0;
  $in_case = false;
  $parenthesis = 0;
  $braces_in_case = 0;
  $in_brace = false;
  $in_heredoc = false;
  $first_php_tag = true;
  $in_do_while = false;

  // whitespace controls
  $in_object = false;
  $in_at = false;
  $in_php = false;
  $in_quote = false;
  $inline_if = false;
  $result = '';
  $lasttoken = array(
    0,
  );
  $tokens = token_get_all($code);

  // Mask T_ML_COMMENT (PHP4) as T_COMMENT (PHP5).
  // Mask T_DOC_COMMENT (PHP5) as T_ML_COMMENT (PHP4).
  if (!defined('T_ML_COMMENT')) {
    define('T_ML_COMMENT', T_COMMENT);
  }
  else {
    if (!defined('T_DOC_COMMENT')) {
      define('T_DOC_COMMENT', T_ML_COMMENT);
    }
  }
  foreach ($tokens as $token) {
    if (is_string($token)) {

      // simple 1-character token
      $text = trim($token);
      switch ($text) {
        case '{':

          // Write curly braces at the end of lines followed by a line break if
          // not in quotes (""), object ($foo->{$bar}) or in variables (${foo}).
          // [T_DOLLAR_OPEN_CURLY_BRACES exists but is never assigned.]
          if (!$in_quote && !$in_object && substr(rtrim($result), -1) != '$') {
            if ($in_case) {
              ++$braces_in_case;
            }
            ++$_coder_indent;
            $result = rtrim($result) . ' ' . $text . coder_br();
          }
          else {
            $in_brace = true;
            $result .= $text;
          }
          break;
        case '}':
          if (!$in_quote && !$in_brace && !$in_heredoc) {
            if ($in_case) {
              --$braces_in_case;
            }
            --$_coder_indent;
            if ($braces_in_case < 0) {

              // Decrease indent if last case in a switch is not terminated
              --$_coder_indent;
              $in_case = false;
              $braces_in_case = 0;
            }
            $result = rtrim($result);
            if (substr($result, -1) != '{') {

              // Avoid line break in empty curly braces
              $result .= coder_br();
            }
            $result .= $text . coder_br();
          }
          else {
            $in_brace = false;
            $result .= $text;
          }
          break;
        case ';':
          $result = rtrim($result) . $text;
          if (!$parenthesis && !$in_heredoc) {
            $result .= coder_br();
          }
          else {
            $result .= ' ';
          }
          if ($inline_if) {
            $inline_if = false;
          }
          break;
        case '?':
          $inline_if = true;
          $result .= ' ' . $text . ' ';
          break;
        case ':':
          if ($inline_if) {
            $result .= ' ' . $text . ' ';
          }
          else {
            if ($in_case) {
              ++$_coder_indent;
            }
            $result = rtrim($result) . $text . coder_br();
          }
          break;
        case '(':
          $result .= $text;
          ++$parenthesis;
          break;
        case ')':
          if (!$in_quote && !$in_heredoc && substr(rtrim($result), -1) == ',') {

            // Fix indent of right parenthesis in multiline arrays by
            // increasing indent for each parenthesis and decreasing one level.
            $_coder_indent = $_coder_indent + $parenthesis - 1;
            $result = rtrim($result) . coder_br() . $text;
            $_coder_indent = $_coder_indent - $parenthesis + 1;
          }
          else {
            $result .= $text;
          }
          if ($parenthesis) {
            --$parenthesis;
          }
          break;
        case '@':
          $in_at = true;
          $result .= $text;
          break;
        case ',':
          $result .= $text . ' ';
          break;
        case '.':
          if (substr(rtrim($result), -1) == "'" || substr(rtrim($result), -1) == '"') {

            // Write string concatenation character directly after strings
            $result = rtrim($result) . $text . ' ';
          }
          else {
            $result = rtrim($result) . ' ' . $text . ' ';
          }
          break;
        case '=':
        case '<':
        case '>':
        case '+':
        case '*':
        case '/':
        case '|':
        case '^':
        case '%':
          $result = rtrim($result) . ' ' . $text . ' ';
          break;
        case '&':
          if (substr(rtrim($result), -1) == '=' || substr(rtrim($result), -1) == '(' || substr(rtrim($result), -1) == ',') {
            $result .= $text;
          }
          else {
            $result = rtrim($result) . ' ' . $text . ' ';
          }
          break;
        case '-':
          $result = rtrim($result);

          // Do not add a space before negative numbers or variables
          if (substr($result, -1) == '>' || substr($result, -1) == '=' || substr($result, -1) == ',' || substr($result, -1) == ':') {
            $result .= ' ' . $text;
          }
          else {
            $result .= ' ' . $text . ' ';
          }
          break;
        case '"':

          // toggle quote if the char is not escaped
          if (rtrim($result) != "\\") {
            $in_quote = $in_quote ? false : true;
          }
          if (substr($result, -3) == ' . ') {

            // Write string concatenation character directly before strings
            $result = rtrim($result);
          }
          $result .= $text;
          break;
        default:
          $result .= $text;
          break;
      }
    }
    else {

      // If we get here, then we have found not a single char, but a token.
      // See <http://www.php.net/manual/en/tokens.php> for a reference.
      // Fetch token array
      list($id, $text) = $token;

      // Debugging

      /*
      if ($lasttoken[0] == T_WHITESPACE) {
        $result .= token_name($id);
      }
      */
      switch ($id) {
        case T_ARRAY:

          // Write array in lowercase
          $result .= strtolower(trim($text));
          break;
        case T_OPEN_TAG:
        case T_OPEN_TAG_WITH_ECHO:
          $in_php = true;

          // Add a line break between two PHP tags
          if (substr(rtrim($result), -2) == '?>') {
            $result .= coder_br();
          }
          $result .= trim($text);
          if ($first_php_tag) {
            $result .= coder_br();
            $first_php_tag = false;
          }
          else {
            $result .= ' ';
          }
          break;
        case T_CLOSE_TAG:
          $in_php = false;

          // Remove preceding line break for inline PHP output in HTML
          if (substr(rtrim($result), -1) == ';' || substr(rtrim($result), -1) == ':') {
            $result = rtrim($result) . ' ';
          }
          $result .= trim($text);
          break;
        case T_OBJECT_OPERATOR:
          $in_object = true;
          $result .= trim($text);
          break;
        case T_CONSTANT_ENCAPSED_STRING:
          if (substr($result, -2) == '. ') {

            // Write string concatenation character directly before strings
            $result = rtrim($result);
          }

        // Move on to T_STRING / T_VARIABLE
        case T_STRING:
        case T_VARIABLE:
          if ($in_object || $in_at) {

            // No space after object operator ($foo->bar) and error suppression (@function())
            $result = rtrim($result) . trim($text);
            $in_object = false;
            $in_at = false;
          }
          else {
            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,
            ))) {

              // Insert a space after right parenthesis, but not after type casts
              coder_add_space($result);
            }
            $result .= trim($text);
          }
          break;
        case T_ENCAPSED_AND_WHITESPACE:
          $result .= $text;
          break;
        case T_WHITESPACE:

          // Avoid duplicate line feeds outside arrays
          $c = $parenthesis ? 0 : 1;
          for ($c, $cc = substr_count($text, chr(10)); $c < $cc; ++$c) {
            if ($parenthesis) {

              // Add extra indent for each parenthesis in multiline definitions (f.e. arrays)
              $_coder_indent = $_coder_indent + $parenthesis;
              $result = rtrim($result) . coder_br();
              $_coder_indent = $_coder_indent - $parenthesis;
            }
            else {

              // Discard any whitespace, just insert a line break
              $result .= coder_br();
            }
          }
          break;
        case T_IF:
        case T_FOR:
        case T_FOREACH:
        case T_SWITCH:
        case T_GLOBAL:
        case T_STATIC:
        case T_ECHO:
        case T_PRINT:
        case T_NEW:
        case T_REQUIRE:
        case T_REQUIRE_ONCE:
        case T_INCLUDE:
        case T_INCLUDE_ONCE:
          coder_add_space($result);

          // Append a space
          $result .= trim($text) . ' ';
          break;
        case T_DO:
          $result .= trim($text);
          $in_do_while = true;
          break;
        case T_WHILE:
          if ($in_do_while) {

            // Write while after right parenthesis for do {...} while()
            $result = rtrim($result) . ' ';
            $in_do_while = false;
          }

          // Append a space
          $result .= trim($text) . ' ';
          break;
        case T_ELSE:
        case T_ELSEIF:

          // Write else and else if to a new line
          $result = rtrim($result) . coder_br() . trim($text) . ' ';
          break;
        case T_CASE:
        case T_DEFAULT:
          $braces_in_case = 0;
          $result = rtrim($result);
          if (!$in_case) {
            $in_case = true;

            // Add a line break between cases
            if (substr($result, -1) != '{') {
              $result .= coder_br();
            }
          }
          else {

            // Decrease current indent to align multiple cases
            --$_coder_indent;
          }
          $result .= coder_br() . trim($text) . ' ';
          break;
        case T_BREAK:

          // Write break to a new line
          $result = rtrim($result) . coder_br() . trim($text);
          if ($in_case && !$braces_in_case) {
            --$_coder_indent;
            $in_case = false;
          }
          break;
        case T_RETURN:
        case T_CONTINUE:
          coder_add_space($result);
          $result .= trim($text) . ' ';

          // Decrease indent only if we're not in a control structure inside a case
          if ($in_case && !$braces_in_case) {
            --$_coder_indent;
            $in_case = false;
          }
          break;
        case T_FUNCTION:
        case T_CLASS:

          // Write function and class to new lines
          $result = rtrim($result);
          if (substr($result, -1) == '}') {
            $result .= coder_br();
          }
          $result .= coder_br() . trim($text) . ' ';
          break;
        case T_AND_EQUAL:
        case T_AS:
        case T_BOOLEAN_AND:
        case T_BOOLEAN_OR:
        case T_CONCAT_EQUAL:
        case T_DIV_EQUAL:
        case T_DOUBLE_ARROW:
        case T_IS_EQUAL:
        case T_IS_NOT_EQUAL:
        case T_IS_IDENTICAL:
        case T_IS_NOT_IDENTICAL:
        case T_IS_GREATER_OR_EQUAL:
        case T_IS_SMALLER_OR_EQUAL:
        case T_LOGICAL_AND:
        case T_LOGICAL_OR:
        case T_LOGICAL_XOR:
        case T_MINUS_EQUAL:
        case T_MOD_EQUAL:
        case T_MUL_EQUAL:
        case T_OR_EQUAL:
        case T_PLUS_EQUAL:
        case T_SL:
        case T_SL_EQUAL:
        case T_SR:
        case T_SR_EQUAL:
        case T_XOR_EQUAL:

          // Surround operators with spaces
          if (substr($result, -1) != ' ') {

            // $result must not be trimmed to allow multi-line if-clauses
            $result .= ' ';
          }
          $result .= trim($text) . ' ';
          break;
        case T_COMMENT:
        case T_ML_COMMENT:
        case T_DOC_COMMENT:
          if (substr($text, 0, 3) == '/**') {

            // Prepend a new line
            $result = rtrim($result) . coder_br() . coder_br();

            // Remove carriage returns
            $text = str_replace("\r", '', $text);
            $lines = explode("\n", $text);
            $params_fixed = false;
            for ($l = 0; $l < count($lines); ++$l) {
              $lines[$l] = trim($lines[$l]);

              // Add a new line between function description and first parameter description.
              if (!$params_fixed && substr($lines[$l], 0, 8) == '* @param' && $lines[$l - 1] != '*') {
                $result .= ' *' . coder_br();
                $params_fixed = true;
              }
              else {
                if (!$params_fixed && substr($lines[$l], 0, 8) == '* @param') {

                  // Do nothing if parameter description is properly formatted.
                  $params_fixed = true;
                }
              }

              // Add a new line between function params and return.
              if (substr($lines[$l], 0, 9) == '* @return' && $lines[$l - 1] != '*') {
                $result .= ' *' . coder_br();
              }

              // Add one space indent to get ' *[...]'
              if ($l > 0) {
                $result .= ' ';
              }
              $result .= $lines[$l];
              if ($l < count($lines)) {
                $result .= coder_br();
              }
            }
          }
          else {
            $result .= trim($text);
            if ($parenthesis) {

              // Add extra indent for each parenthesis in multiline definitions (f.e. arrays)
              $_coder_indent = $_coder_indent + $parenthesis;
              $result = rtrim($result) . coder_br();
              $_coder_indent = $_coder_indent - $parenthesis;
            }
            else {

              // Discard any whitespace, just insert a line break
              $result .= coder_br();
            }
          }
          break;
        case T_INLINE_HTML:
          $result .= $text;
          break;
        case T_START_HEREDOC:
          $result .= trim($text) . coder_br(false);
          $in_heredoc = true;
          break;
        case T_END_HEREDOC:
          $result .= trim($text) . coder_br(false);
          $in_heredoc = false;
          break;
        default:
          $result .= trim($text);
          break;
      }

      // Store last token
      $lasttoken = $token;
    }
  }
  return $result;
}