#!/local/php5/bin/php
<?php
define('PHPCF_KEY_ALL', 1);
define('PHPCF_KEY_LEFT', 2);
define('PHPCF_KEY_RIGHT', 3);
define('PHPCF_KEY_TYPE', 4);
define('PHPCF_KEY_CODE', 5);
define('PHPCF_KEY_TEXT', 6);
define('PHPCF_KEY_LINE', 7);
define('PHPCF_KEY_SEQUENCE', 8);
define('PHPCF_KEY_TOKEN_LENGTH', 9);
define('PHPCF_KEY_DESCR_LEFT', 10);
define('PHPCF_KEY_DESCR_RIGHT', 11);
/*************************************************************************************************/
/* PHPCF_EX-constants - special PHPCF_EXecutors processed at final output stage
/* Note, that order is important -- first executor has higher priority than the last
/* - DELETE: just delete, nuff said
/* - SHRINK: shrink to a single token
/* - CHECK: just check, if not exists - add, but don't shrink
/* - INCREASE/DECREASE: indent operations
/*************************************************************************************************/
$cnt = 100;
define('PHPCF_EX_DO_NOT_TOUCH_ANYTHING', $cnt++);
define('PHPCF_EX_CHECK_NL_STRONG', $cnt++);
define('PHPCF_EX_DELETE_SPACES_STRONG', $cnt++);
define('PHPCF_EX_SHRINK_SPACES_STRONG', $cnt++);
define('PHPCF_EX_SHRINK_NLS_STRONG', $cnt++);
define('PHPCF_EX_SHRINK_NLS_2', $cnt++); // shrink to two lines (if any)
define('PHPCF_EX_SHRINK_NLS', $cnt++);
define('PHPCF_EX_CHECK_NL', $cnt++);
define('PHPCF_EX_DELETE_SPACES', $cnt++);
define('PHPCF_EX_SHRINK_SPACES', $cnt++);
define('PHPCF_EX_NL_OR_SPACE', $cnt++); // accept either new line or space as whitespace
unset($cnt);
define('PHPCF_EX_INCREASE_INDENT', 200);
define('PHPCF_EX_DECREASE_INDENT', 201);
define('PHPCF_CTX_NOW', 'CTX_NOW');
define('PHPCF_CTX_NEXT', 'CTX_NEXT');
// constant is used to determine whether or not need to split expression to several lines
define('PHPCF_LONG_EXPRESSION_LENGTH', 120);
if (!ini_get('short_open_tag')) {
// otherwise short tags will not be tokenized
trigger_error('short_open_tag must be enabled for tokenization to work');
}
mb_internal_encoding('UTF-8');
if (is_callable('dl')) {
if (!function_exists('posix_isatty')) @dl('posix.so');
if (!function_exists('pcntl_fork')) @dl('pcntl.so');
}
if (!function_exists('posix_isatty')) {
function posix_isatty()
{
return false;
}
}
class PHPCodeFSM
{
public $rules = array();
public $state = null;
public $state_stack = array();
public $old_state = null;
public $prev_code = null;
public $current_rules = array(/* state, code_key, rules */);
private $delayed_rule = null;
public function __construct($rules)
{
if (!empty($rules)) {
foreach ($rules as $key => $data) {
$parts = explode(' ', $key);
if (empty($parts)) $parts = array($key);
foreach ($parts as $key_sub) {
$this->rules[$key_sub] = $data;
}
}
if (isset($rules[0])) {
$this->state = $rules[0];
}
}
}
public function getState()
{
return $this->state;
}
public function getStackPath()
{
$result = $this->state_stack;
$result[] = $this->state;
return implode(' / ', $result);
}
public function transit($code)
{
if ($code == 'T_WHITESPACE') {
return;
}
$i_rules = false;
$code_key = $code;
if (isset($this->delayed_rule)) {
if (PHPCF_DEBUG) {
echo "Found delayed rule: " . print_r($this->delayed_rule, true);
echo " in current state: {$this->state}\n";
}
$this->state = $this->delayed_rule;
$this->executeTransition();
$this->delayed_rule = null;
}
if (isset($this->rules[$this->state])) {
$i_rules = $this->rules[$this->state];
if (!isset($i_rules[$code_key])) {
$code_key = PHPCF_KEY_ALL;
}
if (!empty($i_rules[$code_key])) {
$this->current_rules = array($this->state, $code);
$this->old_state = $this->state;
$this->state = $i_rules[$code_key];
// you can specify delayed context switch as
// array(PHPCF_CTX_NOW => ..., PHPCF_CTX_NEXT => ...)
if (is_array($this->state) && isset($this->state[PHPCF_CTX_NOW])) {
if (isset($this->state[PHPCF_CTX_NEXT])) {
$this->delayed_rule = $this->state[PHPCF_CTX_NEXT];
}
$this->state = $this->state[PHPCF_CTX_NOW];
}
$this->executeTransition();
}
}
$this->prev_code = $code;
}
private function executeTransition()
{
if (is_array($this->state)) {
// context inside context
$this->state = $this->state[0];
$this->state_stack[] = $this->old_state;
} elseif ($this->state < 0) {
$i = -$this->state;
while ($i > 0) {
$this->state = array_pop($this->state_stack);
$i--;
}
}
}
}
class PHPCodeFormatter
{
private $tokens = array();
public $FSM = null;
public $sniff_exit_code = 0;
private $exec = array();
private $exec_ctx = array();
private $color = false;
private $indent_char = ' ';
private $newline_char = "\n";
private $indent_level = 0;
private $indent_width = 4;
private $current_pos = 0;
private $controls = array();
private $context_map = array();
private $context = false;
private $new_line = "\n";
// private $file_lines = array(); // can be useful for debugging of problem with line numbers
private $sniff_errors = array(/* error text => array(lines) */);
private $white_space_map = array(
"\r" => "",
"\0" => "",
);
private $constants = array(
'true' => 1,
'false' => 1,
'null' => 1,
);
function __construct($filename, $fsm_rules, $controls)
{
$this->init();
$this->initFile($filename);
$this->initControls($controls);
$this->FSM = new PHPCodeFSM($fsm_rules);
}
private function initFile($filename)
{
if (!is_readable($filename)) {
fwrite(STDERR, "ERROR: can't read file '" . $filename . "', will exit.\n");
exit(1);
}
$body = file_get_contents($filename);
// $this->file_lines = file($filename); // can be useful for debugging of problem with line numbers
$this->prepareTokens($body);
}
private function init()
{
$this->white_space_map += array(
" " => $this->indent_char,
"\t" => $this->indent_char,
"\n" => $this->newline_char,
);
$this->color = posix_isatty(1);
}
private function isDebug()
{
return PHPCF_DEBUG;
}
private function debug($m, $data = null)
{
if ($this->isDebug()) {
echo $m, "\n";
if ($data !== null) {
var_dump($data);
}
}
}
/* text inside HEREDOCs is tokenized in PHP by default, which is not what we need at all */
private function tokenHookHeredoc(&$tokens, $idx_tokens, $i_value)
{
$processed_tokens = array();
$heredoc_value = '';
$this->current_line = $i_value[2];
$processed_tokens[] = array(
PHPCF_KEY_CODE => $idx_tokens[$i_value[0]],
PHPCF_KEY_TEXT => $i_value[1],
PHPCF_KEY_LINE => $this->current_line,
);
while (list(, $i_value) = each($tokens)) {
if ($i_value[0] === T_END_HEREDOC) {
$this->current_line = $i_value[2];
$processed_tokens[] = array(
PHPCF_KEY_CODE => 'T_HEREDOC_CONTENTS',
PHPCF_KEY_TEXT => $heredoc_value,
PHPCF_KEY_LINE => $this->current_line,
);
break;
}
if (is_array($i_value)) {
$heredoc_value .= $i_value[1];
$this->current_line = $i_value[2];
} else {
$heredoc_value .= $i_value;
}
}
$processed_tokens[] = array(
PHPCF_KEY_CODE => $idx_tokens[$i_value[0]],
PHPCF_KEY_TEXT => $i_value[1],
PHPCF_KEY_LINE => $this->current_line,
);
return $processed_tokens;
}
/* text inside "strings" is also tokenized in PHP by default: we do not care about these */
private function tokenHookDblStr(&$tokens)
{
$quote_token = array(
PHPCF_KEY_CODE => '"',
PHPCF_KEY_TEXT => '"',
PHPCF_KEY_LINE => $this->current_line,
);
$string_value = '';
$processed_tokens = array();
$processed_tokens[] = $quote_token;
while (list(, $i_value) = each($tokens)) {
if ($i_value === '"') {
$processed_tokens[] = array(
PHPCF_KEY_CODE => 'T_STRING_CONTENTS',
PHPCF_KEY_TEXT => $string_value,
PHPCF_KEY_LINE => $this->current_line,
);
break;
}
if (is_array($i_value)) {
$string_value .= $i_value[1];
$this->current_line = $i_value[2];
} else {
$string_value .= $i_value;
}
}
$quote_token[PHPCF_KEY_LINE] = $this->current_line;
$processed_tokens[] = $quote_token;
return $processed_tokens;
}
private function countExprLength(&$tokens, $found_first_bracket = false, $max_length)
{
$length = 0;
$depth = $found_first_bracket ? 1 : 0;
$current_pos = key($tokens);
// not using next() as do not want to move array pointer
for ($i = $current_pos; $i < count($tokens); $i++) {
$token = $tokens[$i];
if (!$found_first_bracket) {
if ($token == '(') $found_first_bracket = true;
else continue;
}
// allow having long array definitions if user has decided that there should be an array in long form
// also, functions are always required to be on separate lines, so we will consider having opening brace
// as a sign that this expression is "long"
if ($token === '{' || $token[0] === T_WHITESPACE && strpos($token[1], "\n") !== false) {
return $max_length + 1;
}
if (is_array($token)) $length += strlen($token[1]);
else $length += strlen($token);
if ($length > $max_length) break;
if ($token === '(') $depth++;
if ($token === ')') $depth--;
if ($depth <= 0) break;
}
return $length;
}
// T_ARRAY => T_ARRAY_LONG in case of array with a lot of contents
private function tokenHookOpenBrace(&$tokens, $idx_tokens, $i_value)
{
$length = $this->countExprLength($tokens, true, PHPCF_LONG_EXPRESSION_LENGTH);
$token = '(';
if ($length >= PHPCF_LONG_EXPRESSION_LENGTH) $token = '(_LONG';
return array(
array(
PHPCF_KEY_CODE => $token,
PHPCF_KEY_TEXT => '(',
PHPCF_KEY_LINE => $this->current_line,
)
);
}
private function shouldIgnoreToken($token)
{
return $token == T_WHITESPACE || $token == T_COMMENT;
}
private function tokenHookCheckUnary(&$tokens, $idx_tokens, $i_value)
{
static $left_expression_symbols = array(']', ')', T_LNUMBER, T_DNUMBER, T_VARIABLE, T_STRING,);
$is_unary = false;
$current_pos = key($tokens) - 1; // token position is already pointing to next token
// token is unary if there is no expression on the left
for ($i = $current_pos - 1; $i > 0; $i--) {
$tok = $tokens[$i];
if (is_array($tok) && $this->shouldIgnoreToken($tok[0])) continue;
if (is_array($tok)) $tok = $tok[0];
if (!in_array($tok, $left_expression_symbols, true)) {
$is_unary = true;
}
break;
}
$token = $i_value . ($is_unary ? '_UNARY' : '');
return array(
array(
PHPCF_KEY_CODE => $token,
PHPCF_KEY_TEXT => $i_value,
PHPCF_KEY_LINE => $this->current_line,
)
);
}
// interpret "static" in "static::HELLO" as normal text (T_STRING) instead of keyword (T_STATIC)
private function tokenHookStatic(&$tokens, $idx_tokens, $i_value)
{
static $right_normal_context_symbols = array(
T_VARIABLE, // static $var; protected static $var;
T_PROTECTED, T_PUBLIC, T_PRIVATE, T_FINAL, T_VAR, T_ABSTRACT, // static protected $var;
T_FUNCTION, // static function() { ... }
);
$is_normal_context = false;
$next_pos = key($tokens);
$this->current_line = $i_value[2];
for ($i = $next_pos; $i < count($tokens); $i++) {
$tok = $tokens[$i];
if (is_array($tok) && $this->shouldIgnoreToken($tok[0])) continue;
if (in_array($tok[0], $right_normal_context_symbols, true)) $is_normal_context = true;
break;
}
if ($is_normal_context) return $this->tokenHookClassdef($tokens, $idx_tokens, $i_value);
return array(
array(
PHPCF_KEY_CODE => 'T_STRING',
PHPCF_KEY_TEXT => 'static',
PHPCF_KEY_LINE => $this->current_line,
)
);
}
// check for newline after "const", "private", etc and check if it is something like this:
// const
// CONST1 = "Something",
// CONST2 = "Another thing";
private function tokenHookClassdef(&$tokens, $idx_tokens, $i_value)
{
$token = $idx_tokens[$i_value[0]];
$this->current_line = $i_value[2];
$next_pos = key($tokens);
$is_property_def = false;
$is_newline = false; // whether or not definition is on a new line
for ($i = $next_pos; $i < count($tokens); $i++) {
$tok = $tokens[$i];
// look for whitespace, comment, variable or constant name after, e.g. "private"
// if nothing was found, then it is end of property def (or real property def did not begin)
if ($tok[0] === T_VARIABLE || $tok[0] === T_STRING) $is_property_def = true;
else if ($tok[0] !== T_COMMENT && $tok[0] != T_WHITESPACE) break;
if (strpos($tok[1], "\n") !== false) $is_newline = true;
}
if ($is_property_def && $is_newline) $token .= '_NL';
return array(
array(
PHPCF_KEY_CODE => $token,
PHPCF_KEY_TEXT => $i_value[1],
PHPCF_KEY_LINE => $this->current_line,
)
);
}
private function appendWhiteSpace(&$tokens, &$return_tokens, $text = "\n")
{
$next_token = current($tokens);
if ($next_token[0] !== T_WHITESPACE) {
$return_tokens[] = array(
PHPCF_KEY_CODE => 'T_WHITESPACE',
PHPCF_KEY_TEXT => $text,
PHPCF_KEY_LINE => $this->current_line,
);
} else {
$tokens[key($tokens)][1] = "\n" . $tokens[key($tokens)][1];
}
}
// only long "<?php" open tags are allowed by our rules
private function tokenHookOpenTag(&$tokens, $idx_tokens, $i_value)
{
$open_tag = "<?php";
$this->current_line = $i_value[2];
if (rtrim($i_value[1]) !== $open_tag) $this->sniffMessage('Only "<?php" allowed as open tag');
$ret = array(
array(
PHPCF_KEY_CODE => 'T_OPEN_TAG',
PHPCF_KEY_TEXT => $open_tag,
PHPCF_KEY_LINE => $this->current_line,
)
);
$this->appendWhiteSpace($tokens, $ret);
return $ret;
}
// T_INC => T_INC_LEFT || T_INC_RIGHT ( T_INC_LEFT in case "++$a", T_INC_RIGHT in case "$a++")
private function tokenHookIncrement(&$tokens, $idx_tokens, $i_value)
{
$next_pos = key($tokens);
$is_left = false;
if ($next_pos) {
for ($i = $next_pos; $i < count($tokens); $i++) {
$tok = $tokens[$i];
if (is_array($tok) && $this->shouldIgnoreToken($tok[0])) continue;
// in situations like '++ $a' there would be variable on the right, so the token is positioned left
if ($tok[0] === T_VARIABLE) $is_left = true;
break;
}
}
$token = $idx_tokens[$i_value[0]] . ($is_left ? '_LEFT' : '_RIGHT');
$this->current_line = $i_value[2];
return array(
array(
PHPCF_KEY_CODE => $token,
PHPCF_KEY_TEXT => $i_value[1],
PHPCF_KEY_LINE => $this->current_line,
)
);
}
private function isLineAligned($line_pos, $line_length, $whitespace_length, &$tokens)
{
$other_line_length = 0;
for ($i = $line_pos; $i < count($tokens); $i++) {
$tok = $tokens[$i];
if (!is_array($tok)) {
$other_line_length += strlen($tok);
} else {
$is_str = $tok[0] === T_CONSTANT_ENCAPSED_STRING || $tok[0] === T_ENCAPSED_AND_WHITESPACE;
if (!$is_str && strpos($tok[1], "\n") !== false) break;
$other_line_length += strlen($tok[1]);
}
if ($other_line_length == $whitespace_length && $tok[0] === T_WHITESPACE) {
// aligned by whitespace
return true;
} else if ($other_line_length == $line_length && $tokens[$i - 1][0] === T_WHITESPACE) {
// aligned by tokens!
return true;
} else if ($other_line_length > $line_length) {
// no chance
break;
}
}
return false;
}
private function isPrevLineAligned($prev_line_pos, $line_length, $whitespace_length, &$tokens)
{
for ($i = $prev_line_pos; $i >= 0; $i--) { // find beginning of line
$tok = $tokens[$i];
if (!is_array($tok)) continue;
$is_str = $tok[0] === T_CONSTANT_ENCAPSED_STRING || $tok[0] === T_ENCAPSED_AND_WHITESPACE;
if (!$is_str && strpos($tok[1], "\n") !== false) break;
}
if ($i < $prev_line_pos) {
return $this->isLineAligned($i + 1, $line_length, $whitespace_length, $tokens);
}
return false;
}
private function isNextLineAligned($next_line_pos, $line_length, $whitespace_length, &$tokens)
{
for ($i = $next_line_pos; $i < count($tokens); $i++) { // find beginning of line
$tok = $tokens[$i];
if (!is_array($tok)) continue;
$is_str = $tok[0] === T_CONSTANT_ENCAPSED_STRING || $tok[0] === T_ENCAPSED_AND_WHITESPACE;
if (!$is_str && strpos($tok[1], "\n") !== false) break;
}
if ($i > $next_line_pos) {
return $this->isLineAligned($i + 1, $line_length, $whitespace_length, $tokens);
}
return false;
}
// find aligned expressions
private function tokensIsWhiteSpaceAligned(&$tokens, $i_value)
{
$next_pos = key($tokens);
// either it's EOF or whitespace has tabs/newlines or it has length less than 2
if (!$next_pos || !preg_match('/^ +$/s', $i_value[1])) {
return false;
}
// search for beginning of line and count length of line before this whitespace
$whitespace_length = $line_length = 0;
for ($i = $next_pos; $i >= 0; $i--) {
$tok = $tokens[$i];
if (!is_array($tok)) {
$line_length += strlen($tok);
if ($i != $next_pos) $whitespace_length += strlen($tok);
} else {
$is_str = $tok[0] === T_CONSTANT_ENCAPSED_STRING || $tok[0] === T_ENCAPSED_AND_WHITESPACE;
if (!$is_str && strpos($tok[1], "\n") !== false) break;
// this will skip the indent as well, we do not care
$line_length += strlen($tok[1]);
if ($i != $next_pos) $whitespace_length += strlen($tok[1]);
}
}
$is_aligned = false;
if ($i > 0) $is_aligned = $this->isPrevLineAligned($i - 1, $line_length, $whitespace_length, $tokens);
if (!$is_aligned) $is_aligned = $this->isNextLineAligned($next_pos, $line_length, $whitespace_length, $tokens);
return $is_aligned;
}
private function tokenHookWhiteSpace(&$tokens, $idx_tokens, $i_value)
{
// fix for bug with wrong line numbers of tokens like "{" which do not have line number assotiated with them
$this->current_line = $i_value[2] + substr_count($i_value[1], "\n");
if ($this->tokensIsWhiteSpaceAligned($tokens, $i_value)) $token = 'T_WHITESPACE_ALIGNED';
else $token = 'T_WHITESPACE';
return array(
array(
PHPCF_KEY_CODE => $token,
PHPCF_KEY_TEXT => $i_value[1],
PHPCF_KEY_LINE => $this->current_line,
)
);
}
private function tokenHookElse(&$tokens, $idx_tokens, $i_value)
{
$token = $idx_tokens[$i_value[0]];
$this->current_line = $i_value[2];
$next_pos = key($tokens);
$has_block_before = false;
for ($i = $next_pos - 2; $i > 0; $i--) {
$tok = $tokens[$i];
if (is_array($tok) && $this->shouldIgnoreToken($tok[0])) continue;
if ($tok === '}') $has_block_before = true;
break;
}
if (!$has_block_before) $token .= '_INLINE';
return array(
array(
PHPCF_KEY_CODE => $token,
PHPCF_KEY_TEXT => $i_value[1],
PHPCF_KEY_LINE => $this->current_line,
)
);
}
// "// comment\n" is split into 2 tokens "// comment" and "\n", and "\n" is prepended to previous whitespace token
// if present. This split allows not to take easy cases with single-line comments into account
private function tokenHookComment(&$tokens, $idx_tokens, $i_value)
{
$token = $idx_tokens[$i_value[0]];
$this->current_line = $i_value[2];
if (substr($i_value[1], 0, 2) == '//' || $i_value[1][0] == '#') {
// if previous token is whitespace with line feed, then it means that this comment takes whole line
$prev_pos = key($tokens) - 2; // otherwise it is appended to some expression like this one
if ($prev_pos > 1 && $tokens[$prev_pos][0] === T_WHITESPACE && strpos($tokens[$prev_pos][1], "\n") !== false) {
$token = 'T_SINGLE_LINE_COMMENT_ALONE';
} else {
$token = 'T_SINGLE_LINE_COMMENT';
}
$i_value[1] = rtrim($i_value[1]);
}
$ret = array(
array(
PHPCF_KEY_CODE => $token,
PHPCF_KEY_TEXT => $i_value[1],
PHPCF_KEY_LINE => $this->current_line,
),
);
if ($token == 'T_COMMENT') return $ret;
$this->appendWhiteSpace($tokens, $ret);
return $ret;
}
private function tokenHookConcat(&$tokens, $idx_tokens, $i_value)
{
$next_pos = key($tokens);
$prev_pos = key($tokens) - 2;
$token = '.';
if ($tokens[$next_pos][0] === T_WHITESPACE && strpos($tokens[$next_pos][1], "\n") !== false) {
$token = '._NL';
} else if ($tokens[$prev_pos][0] === T_WHITESPACE && strpos($tokens[$prev_pos][1], "\n") !== false) {
$token = '._NL';
}
return array(
array(
PHPCF_KEY_CODE => $token,
PHPCF_KEY_TEXT => '.',
PHPCF_KEY_LINE => $this->current_line,
),
);
}
private function tokenHookConstant(&$tokens, $idx_tokens, $i_value)
{
$this->current_line = $i_value[2];
$value = strtolower($i_value[1]);
if (!isset($this->constants[$value])) {
$value = $i_value[1];
} else if ($value != $i_value[1]) {
$this->sniffMessage('Reserved word "' . $value . '" must be written in lowercase');
}
return array(
array(
PHPCF_KEY_CODE => $idx_tokens[$i_value[0]],
PHPCF_KEY_TEXT => $value,
PHPCF_KEY_LINE => $this->current_line,
)
);
}
private function isLongComma(&$tokens, $max_length, $remember_positions = false)
{
static $last_long_position = -1;
$curr_pos = key($tokens) - 1;
$len = 0;
$depth = 0;
// getting first right non-whitespace token length (only if it is not )
for ($i = $curr_pos + 1; $i < count($tokens); $i++) {
$tok = $tokens[$i];
if (is_array($tok)) {
if ($tok[0] === T_WHITESPACE) {
if (strpos($tok[0], "\n") !== false) break;
continue;
}
$len += strlen($tok[1]);
} else {
if ($tok == ')') break;
$len += strlen($tok);
}
break;
}
// going backwards to see if there already is more than 120 symbols in line
for ($i = $curr_pos; $i > 0; $i--) {
// we can remember that which comma was long and
// count the next long comma only starting from the previous long one
if ($i == $last_long_position) break;
$tok = $tokens[$i];
// we should stop when line beginning is reached or in another special case described below
if (is_array($tok)) {
if (strpos($tok[0], "\n") !== false) break;
if ($tok[0] === T_WHITESPACE) continue;
$len += strlen($tok[1]);
} else {
if ($tok == ',') $len += 2;
else if ($tok == '(') $depth--;
else if ($tok == ')') $depth++;
else $len += strlen($tok);
}
// found opening "(" before reaching max length, which means it is something like this:
// array( 'something', some_func($argument100, $argument200), 'something else' )
// ^ our brace ^ our comma
// such comma must not be long even though the length before newline can be higher than max.
// comma must be directly related to the array (or argument list), but in this case it does not
if ($depth < 0) return false;
if ($depth == 0 && $len >= $max_length) break;
}
$is_long = $len >= $max_length;
if ($remember_positions && $is_long) $last_long_position = $curr_pos;
return $is_long;
}
private function tokenHookComma(&$tokens, $idx_tokens, $i_value)
{
$token = ',';
if ($this->isLongComma($tokens, PHPCF_LONG_EXPRESSION_LENGTH, true)) $token .= '_LONG';
return array(
array(
PHPCF_KEY_CODE => $token,
PHPCF_KEY_TEXT => ',',
PHPCF_KEY_LINE => $this->current_line,
)
);
}
private $token_hook_names = array(
// hook should return all parsed tokens
T_START_HEREDOC => 'tokenHookHeredoc',
'.' => 'tokenHookConcat',
'"' => 'tokenHookDblStr',
'(' => 'tokenHookOpenBrace',
'+' => 'tokenHookCheckUnary',
'-' => 'tokenHookCheckUnary',
'&' => 'tokenHookCheckUnary',
',' => 'tokenHookComma',
T_STATIC => 'tokenHookStatic',
T_OPEN_TAG => 'tokenHookOpenTag',
T_INC => 'tokenHookIncrement',
T_DEC => 'tokenHookIncrement',
T_WHITESPACE => 'tokenHookWhiteSpace',
T_ELSE => 'tokenHookElse',
T_ELSEIF => 'tokenHookElse',
T_COMMENT => 'tokenHookComment',
T_STRING => 'tokenHookConstant',
T_VAR => 'tokenHookClassdef',
T_PUBLIC => 'tokenHookClassdef',
T_PROTECTED => 'tokenHookClassdef',
T_PRIVATE => 'tokenHookClassdef',
T_CONST => 'tokenHookClassdef',
T_FINAL => 'tokenHookClassdef',
);
private function prepareTokens($body)
{
$tokens = token_get_all($body);
$list_tokens = preg_grep('/^T_/', array_keys(get_defined_constants()));
$idx_tokens = array();
foreach ($list_tokens as $i_token) {
$idx_tokens[constant($i_token)] = $i_token;
}
$this->tokens = array();
$in_nowdoc = $in_heredoc = $in_string = false;
$heredoc_value = $string_value = '';
// iterate array manually so that we can read several tokens in hooks and auto-advance position
reset($tokens);
while (list(, $i_value) = each($tokens)) {
$tok = is_array($i_value) ? $i_value[0] : $i_value;
if (isset($this->token_hook_names[$tok])) {
$method_name = $this->token_hook_names[$tok];
$result = $this->$method_name($tokens, $idx_tokens, $i_value);
foreach ($result as $parsed_token) {
$this->tokens[] = $parsed_token;
}
continue;
}
if (is_array($i_value)) {
$this->current_line = $i_value[2];
$this->tokens[] = array(
PHPCF_KEY_CODE => $idx_tokens[$i_value[0]],
PHPCF_KEY_TEXT => $i_value[1],
PHPCF_KEY_LINE => $this->current_line,
);
$this->current_line += substr_count($i_value[1], "\n");
} else {
$this->tokens[] = array(
PHPCF_KEY_CODE => $i_value,
PHPCF_KEY_TEXT => $i_value,
PHPCF_KEY_LINE => $this->current_line,
);
}
}
}
public function dumpStruct()
{
echo str_repeat("=", 100) . "\n";
$i = 0;
ob_start();
foreach ($this->tokens as $i_data) {
printf("%5d: ", $i);
echo $i_data[PHPCF_KEY_CODE] . " " . $this->humanWhiteSpace($i_data[PHPCF_KEY_TEXT], true);
if (isset($i_data[PHPCF_KEY_TOKEN_LENGTH])) {
echo " " . $i_data[PHPCF_KEY_TOKEN_LENGTH];
}
echo "\n";
$i++;
}
ob_end_flush();
}
public function dump()
{
echo str_repeat("=", 100) . "\n";
$i = 0;
$n_exec = count($this->exec);
while ($i < $n_exec) {
$i_exec = $this->exec[$i];
if (is_string($i_exec)) {
echo sprintf("%5d", $i) . ': ' . $this->humanWhiteSpace($i_exec, true) . "\n";
$i++;
} else {
$list_exec = array();
while (!is_string($i_exec)) {
if (is_array($i_exec)) {
foreach ($i_exec as $i_byte) {
$list_exec[] = $this->getHumanReadableExecByte($i_byte);
}
} else {
$list_exec[] = $this->getHumanReadableExecByte($i_exec);
}
$i++;
$i_exec = $this->exec[$i];
}
echo sprintf("%5d", $i) . ': PHPCF_EXEC = {' . implode(', ', $list_exec) . "}\n";
}
}
}
private function getHumanReadableExecByte($byte)
{
static $map = array(
PHPCF_EX_DELETE_SPACES => 'PHPCF_EX_DELETE_SPACES',
PHPCF_EX_DELETE_SPACES_STRONG => 'PHPCF_EX_DELETE_SPACES_STRONG',
PHPCF_EX_SHRINK_SPACES => 'PHPCF_EX_SHRINK_SPACES',
PHPCF_EX_SHRINK_SPACES_STRONG => 'PHPCF_EX_SHRINK_SPACES_STRONG',
PHPCF_EX_SHRINK_NLS => 'PHPCF_EX_SHRINK_NLS',
PHPCF_EX_SHRINK_NLS_STRONG => 'PHPCF_EX_SHRINK_NLS_STRONG',
PHPCF_EX_SHRINK_NLS_2 => 'PHPCF_EX_SHRINK_NLS_2',
PHPCF_EX_CHECK_NL => 'PHPCF_EX_CHECK_NL',
PHPCF_EX_CHECK_NL_STRONG => 'PHPCF_EX_CHECK_NL_STRONG',
PHPCF_EX_INCREASE_INDENT => 'PHPCF_EX_INCREASE_INDENT',
PHPCF_EX_DECREASE_INDENT => 'PHPCF_EX_DECREASE_INDENT',
PHPCF_EX_NL_OR_SPACE => 'PHPCF_EX_NL_OR_SPACE',
PHPCF_EX_DO_NOT_TOUCH_ANYTHING => 'PHPCF_EX_DO_NOT_TOUCH_ANYTHING'
);
if (empty($map[$byte])) return 'UNKNOWN - "' . $byte . '"';
return $map[$byte];
}
private function getHumanReadableExecSequence($sequence)
{
$parts = array();
foreach ($sequence as $v) {
if (is_int($v)) $parts[] = $this->getHumanReadableExecByte($v);
else $parts[] = $this->humanWhiteSpace($v);
}
return implode(', ', $parts);
}
public function sanitizeWhiteSpace($s)
{
return strtr($s, $this->white_space_map);
}
public function exec($lines = null)
{
if ($this->isDebug()) {
$this->dumpStruct();
$this->dump();
echo str_repeat("=", 100) . "\n";
}
if (!count($this->exec)) return ''; // empty file
$exec_ctx = array();
$exec_sequence = array();
$out = '';
foreach ($this->exec as $i => $i_exec) {
$ctx = $this->exec_ctx[$i];
$line = $this->tokens[$ctx['current_pos']][PHPCF_KEY_LINE];
if (is_string($i_exec)) {
if ($ctx['whitespace']) {
if (!isset($lines) || isset($lines[$line])) {
$exec_sequence[] = $this->sanitizeWhiteSpace($i_exec);
} else {
$exec_sequence[] = $i_exec;
}
$exec_ctx[] = $ctx;
} else {
if (!empty($exec_sequence)) {
$out .= $this->execSequence($exec_sequence, $exec_ctx, $line, $lines);
$exec_sequence = array();
$exec_ctx = array();
}
$out .= $i_exec;
}
} elseif (is_array($i_exec)) {
foreach ($i_exec as $i) {
$exec_sequence[] = $i;
$exec_ctx[] = $ctx;
}
} else {
$exec_sequence[] = $i_exec;
$exec_ctx[] = $ctx;
}
}
if (!empty($exec_sequence)) {
$out .= $this->execSequence($exec_sequence, $exec_ctx, $line, $lines);
}
return $out;
}
static $exec_methods = array(
PHPCF_EX_CHECK_NL_STRONG => 'execCheckNewline',
PHPCF_EX_CHECK_NL => 'execCheckNewline',
PHPCF_EX_DELETE_SPACES_STRONG => 'execDeleteSpaces',
PHPCF_EX_DELETE_SPACES => 'execDeleteSpaces',
PHPCF_EX_SHRINK_SPACES => 'execShrinkSpaces',
PHPCF_EX_SHRINK_SPACES_STRONG => 'execShrinkSpaces',
PHPCF_EX_SHRINK_NLS_2 => 'execShrinkNewlines2',
PHPCF_EX_SHRINK_NLS => 'execShrinkNewlines',
PHPCF_EX_SHRINK_NLS_STRONG => 'execShrinkNewlines',
PHPCF_EX_NL_OR_SPACE => 'execNewlineOrSpace',
PHPCF_EX_DO_NOT_TOUCH_ANYTHING => 'execNothing',
);
private function execCheckNewline($in)
{
if (substr_count($in, "\n") >= 2) return "\n\n"; // allow 2 new lines
return "\n";
}
private function execDeleteSpaces($in)
{
return '';
}
private function execShrinkSpaces($in)
{
return ' ';
}
private function execShrinkNewlines($in)
{
return "\n";
}
private function execShrinkNewlines2($in)
{
return "\n\n";
}
private function execNewlineOrSpace($in)
{
if (strpos($in, "\n") !== false) return "\n";
return ' ';
}
private function execNothing($in)
{
return $in;
}
private function execSequence($sequence, $exec_ctx, $line, $lines)
{
$c = array();
$in = '';
$out = '';
$context = array(
'descr' => 'correct indentation level',
'current_pos' => $exec_ctx[0] ? $exec_ctx[0]['current_pos'] : null,
);
for ($i = 0; $i < count($sequence); $i++) {
if (is_int($sequence[$i])) {
if (PHPCF_EX_INCREASE_INDENT == $sequence[$i]) {
$this->indent_level++;
} elseif (PHPCF_EX_DECREASE_INDENT == $sequence[$i]) {
if ($this->indent_level > 0) {
$this->indent_level--;
}
} else {
$c[$sequence[$i]] = $exec_ctx[$i] ? $exec_ctx[$i] : 1;
}
} else {
$in .= $sequence[$i];
}
}
// account for ignoring lines
if (isset($lines) && !isset($lines[$line])) return $in;
if (count($c)) {
// the executors with less value have higher precedence
$min_key = min(array_keys($c));
$action = self::$exec_methods[$min_key];
$out = $this->$action($in);
$context = $c[$min_key];
} else {
$out = $in;
}
if (false !== strpos($out, "\n")) {
$out = rtrim($out, ' ');
$out .= $this->getIndentString();
}
if ($in !== $out) {
$this->sniff($in, $out, $context, $sequence);
}
return $out;
}
function humanWhiteSpace($str, $color = false)
{
static $whitespace_map, $colored_whitespace_map;
if (!isset($whitespace_map)) {
$whitespace_map = array("\n" => '\n', "\r" => '\r', "\t" => '\t', " " => ' ');
$colored_whitespace_map = array("\n" => '\n', "\r" => '\r', "\t" => '\t', " " => 'ยท');
foreach ($colored_whitespace_map as &$v) {
$v = "\033[38;5;246m" . $v . "\033[0m";
}
unset($v);
}
return strtr($str, $this->color && $color ? $colored_whitespace_map : $whitespace_map);
}
private function msg($message)
{
fwrite(STDERR, $message . "\n");
}
private function sniffMessage($message)
{
if (!PHPCF_SNIFF) return;
$this->sniff_exit_code = 1;
$this->sniff_errors[$message][] = $this->current_line;
// $this->msg(sprintf("line%6d) %s\n\n", $this->current_line, $message));
}
private function sniff($in, $out, $context, $sequence)
{
if (!PHPCF_SNIFF) return;
$this->sniff_exit_code = 1;
$current_pos = $context['current_pos'];
$this->sniff_errors[$context['descr']][] = $this->tokens[$current_pos][PHPCF_KEY_LINE];
}
function printSnifferMessages($lines_opt)
{
if (!PHPCF_SNIFF || !count($this->sniff_errors)) return;
if (PHPCF_SUMMARY) {
if ($this->sniff_errors) {
$this->msg('Total errors: ' . array_sum(array_map('count', $this->sniff_errors)));
}
} else {
if (count($this->sniff_errors) && isset($lines_opt)) {
$this->msg(' Checking with --lines=' . $lines_opt . "\n");
}
foreach ($this->sniff_errors as $text => $lines) {
$lines = array_unique($lines);
$text = ' Expecting ' . lcfirst($text);
$wrapped_lines = wordwrap(implode(' ', $lines), 70, "\n ");
$msg = sprintf("%s:\n line%s %s\n", $text, count($lines) > 1 ? 's' : '', $wrapped_lines);
$this->msg($msg);
}
}
}
private function next()
{
if ($this->current_pos + 1 >= count($this->tokens)) {
return false;
}
$this->current_pos++;
$this->setContext();
return $this->tokens[$this->current_pos];
}
private function setContext()
{
// $this->debug("==== FSM->setContext called for " . $this->getCode());
$this->FSM->transit($this->tokens[$this->current_pos][PHPCF_KEY_CODE]);
$this->context = $this->FSM->getState();
// $this->debug("==== FSM->setContext sets new context to " . $this->context);
}
private function initControls($controls)
{
$this->controls = array();
foreach ($controls as $key => $data) {
$list_keys = explode(' ', $key);
if (empty($list_keys)) {
$list_keys = array($key);
}
foreach ($list_keys as $i_key) {
foreach ($data as $j_key => $j_data) {
$j_list_keys = explode(' ', $j_key);
if (empty($j_list_keys)) {
$j_list_keys = array($j_key);
}
foreach ($j_list_keys as $k) {
$this->controls[$i_key][$k] = $j_data;
}
}
}
}
}
private function getContext()
{
$rules = $this->FSM->current_rules;
return array(
'current_pos' => $this->current_pos,
'whitespace' => $this->tokens[$this->current_pos][PHPCF_KEY_CODE] === 'T_WHITESPACE',
);
}
public function process()
{
if (!$this->tokens) return; // empty file
$this->setContext();
do {
$cur_token = $this->tokens[$this->current_pos];
$i_code = $cur_token[PHPCF_KEY_CODE];
$i_text = $cur_token[PHPCF_KEY_TEXT];
if ($this->isDebug()) {
$debug_code = sprintf("%30s", $i_code);
$whitespaces = str_repeat(' ', max(0, 30 - strlen($i_text)));
$msg = $debug_code . " " . $this->humanWhiteSpace($i_text, true) . $whitespaces;
$msg .= " " . $this->FSM->getStackPath();
$this->debug($msg);
}
$rule_context = $this->getContext();
if (empty($this->controls[$i_code])) {
$this->exec[] = $i_text;
$this->exec_ctx[] = $rule_context;
continue;
}
$i_controls = $this->controls[$i_code];
$i_context_controls = false;
if (!empty($i_controls[$this->context])) {
$i_context_controls = $i_controls[$this->context];
} elseif (!empty($i_controls[PHPCF_KEY_ALL])) {
$i_context_controls = $i_controls[PHPCF_KEY_ALL];
}
if (!empty($i_context_controls)) {
$this->processControls($i_context_controls, $rule_context);
} else {
$this->exec[] = $i_text;
$this->exec_ctx[] = $rule_context;
}
} while ($this->next());
}
private function isWhiteSpaceAligned($pos)
{
if (!isset($this->tokens[$pos][PHPCF_KEY_CODE])) return false;
return $this->tokens[$pos][PHPCF_KEY_CODE] == 'T_WHITESPACE_ALIGNED';
}
function processControls($controls, $context)
{
$i = count($this->exec);
if (!empty($controls[PHPCF_KEY_LEFT]) && !$this->isWhiteSpaceAligned($this->current_pos - 1)) {
$c = $controls[PHPCF_KEY_LEFT];
$this->exec_ctx[$i] = $context + array('descr' => $controls[PHPCF_KEY_DESCR_LEFT], 'from' => PHPCF_KEY_LEFT);
if (is_array($c)) {
$this->exec[$i] = array();
foreach ($c as $i_c) $this->exec[$i][] = $i_c;
} else {
$this->exec[$i] = array($c);
}
$i++;
}
$this->exec_ctx[$i] = $context;
$this->exec[$i] = $this->tokens[$this->current_pos][PHPCF_KEY_TEXT];
if (!empty($controls[PHPCF_KEY_RIGHT]) && !$this->isWhiteSpaceAligned($this->current_pos + 1)) {
$i++;
$c = $controls[PHPCF_KEY_RIGHT];
$this->exec_ctx[$i] = $context + array('descr' => $controls[PHPCF_KEY_DESCR_RIGHT], 'from' => PHPCF_KEY_RIGHT);
if (is_array($c)) {
$this->exec[$i] = array();
foreach ($c as $i_c) $this->exec[$i][] = $i_c;
} else {
$this->exec[$i] = $c;
}
}
}
function getIndentString()
{
return str_repeat($this->indent_char, $this->indent_level * $this->indent_width);
}
}
$casts = 'T_INT_CAST T_DOUBLE_CAST T_BOOL_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_UNSET_CAST';
$controls = array(
'{' => array(
'CTX_INLINE_BRACE' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before "{" in "->{...}" expression',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES,
PHPCF_KEY_DESCR_RIGHT => 'No whitespace after "{" in "->{...}" expression',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES,
),
'CTX_FUNCTION_D CTX_CLASS_D CTX_CLASS_METHOD_D' => array(
PHPCF_KEY_DESCR_LEFT => 'New line before "{" in function/method/class declaration',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_NLS,
PHPCF_KEY_DESCR_RIGHT => 'Indent and 1 or 2 newlines with proper indent after "{"',
PHPCF_KEY_RIGHT => array(PHPCF_EX_INCREASE_INDENT, PHPCF_EX_SHRINK_NLS),
),
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'One space before "{"',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'Indent and 1 or 2 newlines with proper indent after "{"',
PHPCF_KEY_RIGHT => array(PHPCF_EX_INCREASE_INDENT, PHPCF_EX_SHRINK_NLS),
),
),
'}' => array(
'CTX_INLINE_BRACE' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before "}" in "->{...}" expression',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES,
PHPCF_KEY_DESCR_RIGHT => 'No whitespace after "}" in "->{...}" expression',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES,
),
'CTX_CASE_END_OF_BLOCK' => array(
PHPCF_KEY_DESCR_LEFT => 'Unindent and 1 or 2 newlines with proper indent after "}"',
PHPCF_KEY_LEFT => array(PHPCF_EX_DECREASE_INDENT, PHPCF_EX_DECREASE_INDENT, PHPCF_EX_SHRINK_NLS),
PHPCF_KEY_DESCR_RIGHT => '1 or 2 newlines with proper indent after "}"',
PHPCF_KEY_RIGHT => PHPCF_EX_CHECK_NL,
),
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'Unindent and 1 or 2 newlines with proper indent after "}"',
PHPCF_KEY_LEFT => array(PHPCF_EX_DECREASE_INDENT, PHPCF_EX_SHRINK_NLS),
PHPCF_KEY_DESCR_RIGHT => '1 or 2 newlines with proper indent after "}"',
PHPCF_KEY_RIGHT => PHPCF_EX_CHECK_NL,
),
),
'T_IF T_FOR T_FOREACH T_DO T_ELSE_INLINE T_ELSEIF_INLINE T_TRY' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => '1 or 2 newlines with proper indent before control statements',
PHPCF_KEY_LEFT => PHPCF_EX_CHECK_NL,
PHPCF_KEY_DESCR_RIGHT => 'One space after control statements',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES_STRONG,
),
),
'T_AS T_ELSEIF T_ELSE T_CATCH' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'One space before as, elseif, else, catch',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'One space after as, elseif, else, catch',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES_STRONG,
)
),
'T_WHILE' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => '1 or 2 newlines with proper indent before "while"',
PHPCF_KEY_LEFT => PHPCF_EX_CHECK_NL,
PHPCF_KEY_DESCR_RIGHT => 'One space after "while"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES_STRONG,
),
'CTX_WHILE_AFTER_DO' => array(
PHPCF_KEY_DESCR_LEFT => '1 or 2 newlines with proper indent before "while" in "do/while"',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'One space after "while" in "do/while"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES_STRONG,
),
),
'T_CLASS T_INTERFACE' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => '2 newlines with proper indent before "class/interface"',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_NLS_2,
PHPCF_KEY_DESCR_RIGHT => 'One space after class/interface name',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES,
),
),
'T_EXTENDS T_IMPLEMENTS' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'One space before "extends/implements"',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_SPACES,
PHPCF_KEY_DESCR_RIGHT => 'One space after "extends/implements"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES,
)
),
'T_STATIC T_VAR T_PUBLIC T_PRIVATE T_PROTECTED T_CONST T_FINAL T_ABSTRACT' => array(
'CTX_CLASS' => array(
PHPCF_KEY_DESCR_LEFT => '1 or 2 newlines with proper indent before class properties/methods declarations',
PHPCF_KEY_LEFT => PHPCF_EX_CHECK_NL,
PHPCF_KEY_DESCR_RIGHT => 'One space after "final", "abstract", "public" etc.',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES_STRONG,
),
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_RIGHT => 'One space after "final", "abstract", "public" etc.',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES_STRONG,
),
),
'T_STATIC_NL T_VAR_NL T_PUBLIC_NL T_PRIVATE_NL T_PROTECTED_NL T_CONST_NL T_FINAL_NL T_ABSTRACT_NL' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_RIGHT => 'New line and indent after end of property declaration',
PHPCF_KEY_RIGHT => array(PHPCF_EX_INCREASE_INDENT, PHPCF_EX_SHRINK_NLS),
),
),
'T_OBJECT_OPERATOR T_DOUBLE_COLON' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before "::" and "->"',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES,
PHPCF_KEY_DESCR_RIGHT => 'No whitespace after "::" and "->"',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES,
),
),
'( (_LONG' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before "("',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES,
PHPCF_KEY_DESCR_RIGHT => 'No whitespace after "("',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES,
),
'CTX_GENERIC_PARENTHESIS' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before "("',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES,
PHPCF_KEY_DESCR_RIGHT => 'No whitespace after "("',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES,
),
'CTX_LONG_PARENTHESIS CTX_ARRAY_LONG_PARENTHESIS' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before "("',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES,
PHPCF_KEY_DESCR_RIGHT => 'Newline after "(" in long expression',
PHPCF_KEY_RIGHT => array(PHPCF_EX_INCREASE_INDENT, PHPCF_EX_CHECK_NL)
),
),
')' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ")"',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'One space after ")"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES,
),
'CTX_GENERIC_PARENTHESIS' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ")"',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'One space after ")"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES,
),
'CTX_LONG_PAR_END' => array(
PHPCF_KEY_DESCR_LEFT => 'Unindent and newline before ")" in long expression',
PHPCF_KEY_LEFT => array(PHPCF_EX_DECREASE_INDENT, PHPCF_EX_CHECK_NL),
PHPCF_KEY_DESCR_RIGHT => 'No whitespace after ")" in long expression',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES,
),
'CTX_INLINE_EXPR_NL_END' => array(
PHPCF_KEY_DESCR_LEFT => 'Unindent and no whitespace before ")" in end of long expression',
PHPCF_KEY_LEFT => array(PHPCF_EX_DECREASE_INDENT, PHPCF_EX_DELETE_SPACES_STRONG),
PHPCF_KEY_DESCR_RIGHT => 'No whitespace after ")" in long expression',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES,
),
'CTX_LONG_EXPR_NL_END' => array(
PHPCF_KEY_DESCR_LEFT => 'Unindent and newline before ")" in long expression',
PHPCF_KEY_LEFT => array(PHPCF_EX_DECREASE_INDENT, PHPCF_EX_DECREASE_INDENT, PHPCF_EX_CHECK_NL),
PHPCF_KEY_DESCR_RIGHT => 'No whitespace after ")" in long expression',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES,
)
),
';' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ";"',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => '1 or 2 newlines with proper indent after ";"',
PHPCF_KEY_RIGHT => PHPCF_EX_CHECK_NL,
),
'CTX_FOR_PARENTHESIS' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ";"',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'One space after ";" in for()',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES,
),
'CTX_INLINE_EXPR_NL_END' => array(
PHPCF_KEY_DESCR_LEFT => 'Unindent and no whitespace before ")" in end of long expression',
PHPCF_KEY_LEFT => array(PHPCF_EX_DECREASE_INDENT, PHPCF_EX_DELETE_SPACES_STRONG),
PHPCF_KEY_DESCR_RIGHT => '1 or 2 newlines with proper indent after ";"',
PHPCF_KEY_RIGHT => PHPCF_EX_CHECK_NL,
),
'CTX_CLASS_CONST_NL_END CTX_CLASS_VARIABLE_D_NL_END' => array(
PHPCF_KEY_DESCR_LEFT => 'Unindent and no whitespace before ";" in end of long property definition',
PHPCF_KEY_LEFT => array(PHPCF_EX_DECREASE_INDENT, PHPCF_EX_DELETE_SPACES_STRONG),
PHPCF_KEY_DESCR_RIGHT => '1 or 2 newlines with proper indent after ";"',
PHPCF_KEY_RIGHT => PHPCF_EX_CHECK_NL,
),
),
'T_AND_EQUAL T_CONCAT_EQUAL T_DIV_EQUAL T_IS_EQUAL T_IS_GREATER_OR_EQUAL ' .
'T_IS_NOT_EQUAL T_IS_SMALLER_OR_EQUAL T_MINUS_EQUAL T_MOD_EQUAL T_MUL_EQUAL ' .
'T_OR_EQUAL T_PLUS_EQUAL T_SL_EQUAL T_SR_EQUAL T_XOR_EQUAL ' .
'= + & - * ^ % / ? | < > . T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_IS_EQUAL T_IS_NOT_EQUAL ' .
'T_LOGICAL_AND T_BOOLEAN_AND T_LOGICAL_OR T_BOOLEAN_OR T_LOGICAL_XOR' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'One space before binary operators (= < > * . etc) ',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'One space after binary operators (= < > * . etc) ',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES_STRONG,
)
),
'T_DOUBLE_ARROW' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'One space before "=>"',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'One space after "=>"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES_STRONG,
),
'CTX_INLINE_EXPR_NL_END CTX_LONG_EXPR_NL_END' => array(
PHPCF_KEY_DESCR_LEFT => 'One space before "=>"',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'One space after "=>"',
PHPCF_KEY_RIGHT => array(PHPCF_EX_DECREASE_INDENT, PHPCF_EX_SHRINK_SPACES_STRONG),
)
),
'+_UNARY -_UNARY &_UNARY ! @ $ ~' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_RIGHT => 'No whitespace before unary: + - & ! @ $',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES_STRONG,
),
),
'._NL' => array(
'CTX_INLINE_FIRST_NL CTX_LONG_FIRST_NL' => array(
PHPCF_KEY_DESCR_LEFT => 'Single space before "." in multiline concatenation',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'Indent and newline after "." in long expression',
PHPCF_KEY_RIGHT => array(PHPCF_EX_INCREASE_INDENT, PHPCF_EX_CHECK_NL)
),
'CTX_INLINE_EXPR_NL CTX_LONG_EXPR_NL' => array(
PHPCF_KEY_DESCR_LEFT => 'Single space before "." in multiline concatenation',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'Same indent and newline after "." in long expression',
PHPCF_KEY_RIGHT => PHPCF_EX_CHECK_NL,
),
),
',' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ","',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'One space after ","',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES,
),
'CTX_CLASS_CONST_D_NL CTX_CLASS_VARIABLE_D_NL' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ","',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => '1 newline with proper indent after "," in property definition',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_NLS,
),
'CTX_LONG_PARENTHESIS' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ","',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => '1 newline with proper indent after "," in long expression',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_NLS,
),
'CTX_ARRAY_LONG_PARENTHESIS' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ","',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'Either newline or space with proper indent after "," in long array',
PHPCF_KEY_RIGHT => PHPCF_EX_NL_OR_SPACE,
),
),
',_LONG' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ","',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'One space after ","',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES,
),
'CTX_CLASS_CONST_D_NL CTX_CLASS_VARIABLE_D_NL' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ","',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => '1 newline with proper indent after "," in property definition',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_NLS_STRONG,
),
'CTX_LONG_PARENTHESIS' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ","',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => '1 newline with proper indent after "," in long expression',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_NLS,
),
'CTX_ARRAY_LONG_PARENTHESIS' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ","',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => '1 newline with proper indent after "," in item list in array',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_NLS,
),
),
'[' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before "["',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'No whitespace after "["',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES_STRONG,
),
),
']' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before "]"',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES_STRONG,
),
),
':' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ":"',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES,
PHPCF_KEY_DESCR_RIGHT => 'No whitespace after ":"',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES,
),
'CTX_CASE_COLON' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ":" in "case:"',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES,
PHPCF_KEY_DESCR_RIGHT => 'Indent and 1 or 2 newlines with proper indent after "case:"',
PHPCF_KEY_RIGHT => array(PHPCF_EX_INCREASE_INDENT, PHPCF_EX_SHRINK_NLS),
),
'CTX_CASE_MULTI_COLON' => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before ":" in "case:" (multi colon)',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES,
PHPCF_KEY_DESCR_RIGHT => 'Indent and 1 or 2 newlines with proper indent after "case:" (multi colon)',
PHPCF_KEY_RIGHT => array(PHPCF_EX_INCREASE_INDENT, PHPCF_EX_SHRINK_NLS),
),
'CTX_TERNARY_OPERATOR' => array(
PHPCF_KEY_DESCR_LEFT => 'One space before ":" in ternary operator ( ... ? ... : ... )',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_SPACES_STRONG,
PHPCF_KEY_DESCR_RIGHT => 'One space after ":" in ternary operator ( ... ? ... : ... )',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES_STRONG,
),
),
'T_SWITCH' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => '1 or 2 newlines with proper indent before "switch"',
PHPCF_KEY_LEFT => PHPCF_EX_CHECK_NL,
PHPCF_KEY_DESCR_RIGHT => 'One space after "switch"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES_STRONG,
),
),
'T_CASE' => array(
'CTX_CASE_FIRST_D' => array(
PHPCF_KEY_DESCR_LEFT => '1 newline with proper indent before first "case/default"',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_NLS,
PHPCF_KEY_DESCR_RIGHT => 'One space after "case"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES
),
'CTX_CASE_D' => array(
PHPCF_KEY_DESCR_LEFT => '2 newlines with proper indent before "case"',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_NLS_2,
PHPCF_KEY_DESCR_RIGHT => 'One space after "case"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES
),
'CTX_CASE_MULTI_D' => array(
PHPCF_KEY_DESCR_LEFT => '1 newline with proper indent before repeated "case"',
PHPCF_KEY_LEFT => array(PHPCF_EX_DECREASE_INDENT, PHPCF_EX_SHRINK_NLS),
PHPCF_KEY_DESCR_RIGHT => 'One space after "case"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES
),
'CTX_NOBREAK_CASE_D' => array(
PHPCF_KEY_DESCR_LEFT => '2 newlines with proper indent before "case" without break',
PHPCF_KEY_LEFT => array(PHPCF_EX_DECREASE_INDENT, PHPCF_EX_SHRINK_NLS_2),
PHPCF_KEY_DESCR_RIGHT => 'One space after "case"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES,
),
),
'T_DEFAULT' => array(
'CTX_CASE_FIRST_D' => array(
PHPCF_KEY_DESCR_LEFT => '1 newline with proper indent before first "case/default"',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_NLS,
PHPCF_KEY_DESCR_RIGHT => 'No spaces after "default"',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES
),
'CTX_CASE_D' => array(
PHPCF_KEY_DESCR_LEFT => '2 newlines with proper indent before "default"',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_NLS_2,
PHPCF_KEY_DESCR_RIGHT => 'No spaces after "default"',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES
),
'CTX_CASE_MULTI_D' => array(
PHPCF_KEY_DESCR_LEFT => '1 newline with proper indent before repeated "default"',
PHPCF_KEY_LEFT => array(PHPCF_EX_DECREASE_INDENT, PHPCF_EX_SHRINK_NLS),
PHPCF_KEY_DESCR_RIGHT => 'No spaces after "default"',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES
),
'CTX_NOBREAK_CASE_D' => array(
PHPCF_KEY_DESCR_LEFT => '2 newlines with proper indent before "default" without break',
PHPCF_KEY_LEFT => array(PHPCF_EX_DECREASE_INDENT, PHPCF_EX_SHRINK_NLS_2),
PHPCF_KEY_DESCR_RIGHT => 'No spaces after "default"',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES
),
),
'T_BREAK' => array(
// PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES,
// breaks the following construct: "break 3;"
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespace before "break"',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES,
),
'CTX_IF CTX_ELSEIF CTX_ELSE' => array(
PHPCF_KEY_DESCR_LEFT => '1 space before "break" in oneline "if/else/elseif"',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_SPACES_STRONG,
),
'CTX_CASE_BREAK' => array(
PHPCF_KEY_DESCR_LEFT => '1 or 2 newlines with proper indent before "break"',
PHPCF_KEY_LEFT => PHPCF_EX_SHRINK_NLS,
PHPCF_KEY_DESCR_RIGHT => 'Indent and no whitespace before break',
PHPCF_KEY_RIGHT => array(PHPCF_EX_DECREASE_INDENT),
),
),
'T_ECHO T_RETURN' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_RIGHT => 'One space after "echo" and "return"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES_STRONG,
),
),
'T_FUNCTION' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_RIGHT => 'One space after "function"',
PHPCF_KEY_RIGHT => PHPCF_EX_SHRINK_SPACES,
),
),
// $a--, $b++
'T_INC_RIGHT T_DEC_RIGHT' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'No whitespaces in "$c++" and type casts',
PHPCF_KEY_LEFT => PHPCF_EX_DELETE_SPACES,
),
),
// --$a, --$b
'T_INC_LEFT T_DEC_LEFT ' . $casts => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_RIGHT => 'No whitespaces in "++$c" and type casts',
PHPCF_KEY_RIGHT => PHPCF_EX_DELETE_SPACES,
),
),
/* DO NOT CHANGE rules for comments unless you really know what you are doing */
'T_SINGLE_LINE_COMMENT' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_LEFT => 'One space in comment after expression',
PHPCF_KEY_LEFT => PHPCF_EX_DO_NOT_TOUCH_ANYTHING,
PHPCF_KEY_DESCR_RIGHT => 'New line after single-line comment',
PHPCF_KEY_RIGHT => PHPCF_EX_CHECK_NL_STRONG,
)
),
'T_SINGLE_LINE_COMMENT_ALONE' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_RIGHT => 'New line after single-line comment',
PHPCF_KEY_RIGHT => PHPCF_EX_CHECK_NL_STRONG,
),
),
/* / DO NOT CHANGE */
'T_OPEN_TAG' => array(
PHPCF_KEY_ALL => array(
PHPCF_KEY_DESCR_RIGHT => 'New line after opening tag',
PHPCF_KEY_RIGHT => PHPCF_EX_CHECK_NL_STRONG,
)
)
);
$fsm_parenthesis_rules = array(
'(_LONG' => array('CTX_LONG_PARENTHESIS'),
'(' => array('CTX_GENERIC_PARENTHESIS'),
);
$fsm_inline_rules = $fsm_parenthesis_rules + array(
'?' => array('CTX_TERNARY_BEGIN'),
'T_OBJECT_OPERATOR' => array('CTX_INLINE_BRACE_BEGIN'),
'$' => array('CTX_INLINE_BRACE_BEGIN'),
'._NL' => array('CTX_INLINE_FIRST_NL'),
'T_ARRAY' => array('CTX_ARRAY'),
);
$fsm_generic_code_rules = array(
'T_CLOSE_TAG' => 'CTX_DEFAULT',
'T_FINAL' => array('CTX_CLASS_D'),
'T_ABSTRACT' => array('CTX_CLASS_D'),
'T_CLASS' => array('CTX_CLASS_D'),
'T_INTERFACE' => array('CTX_CLASS_D'),
'T_FUNCTION' => array('CTX_FUNCTION_D'),
'T_SWITCH' => array('CTX_SWITCH'),
'T_WHILE' => array('CTX_WHILE'),
'T_FOREACH' => array('CTX_FOREACH'),
'T_FOR' => array('CTX_FOR'),
'T_DO' => array('CTX_DO'),
'T_IF' => array('CTX_IF'),
'T_ELSEIF' => array('CTX_ELSEIF'),
'T_ELSE' => array('CTX_ELSE'),
'T_CASE' => array('CTX_CASE_FIRST_D'),
'T_DEFAULT' => array('CTX_CASE_FIRST_D'),
) + $fsm_inline_rules;
$fsm_generic_code_block_rules = $fsm_generic_code_rules + array('}' => -1, '{' => array('CTX_GENERIC_BLOCK'));
$fsm_context_rules_switch = array(
'CTX_SWITCH' => array(
'{' => 'CTX_GENERIC_BLOCK',
),
'CTX_SWITCH_BLOCK' => array(
'T_CASE' => array('CTX_CASE_D'),
'T_DEFAULT' => array('CTX_CASE_D'),
) + $fsm_generic_code_block_rules,
'CTX_CASE_D CTX_CASE_FIRST_D CTX_NOBREAK_CASE_D' => array(
':' => 'CTX_CASE_MULTI_COLON',
),
'CTX_CASE_MULTI_D' => array(
':' => 'CTX_CASE_MULTI_COLON',
),
'CTX_CASE_MULTI_COLON' => array(
'T_CASE' => 'CTX_CASE_MULTI_D',
'T_DEFAULT' => 'CTX_CASE_MULTI_D',
'T_BREAK' => 'CTX_CASE_BREAK',
PHPCF_KEY_ALL => 'CTX_CASE',
),
'CTX_CASE' => array(
'T_CASE' => 'CTX_NOBREAK_CASE_D',
'T_DEFAULT' => 'CTX_NOBREAK_CASE_D',
'T_BREAK' => 'CTX_CASE_BREAK',
'}' => array(PHPCF_CTX_NOW => 'CTX_CASE_END_OF_BLOCK', PHPCF_CTX_NEXT => -2),
) + $fsm_generic_code_block_rules,
'CTX_CASE_BREAK' => array(
';' => array(PHPCF_CTX_NOW => -1, PHPCF_CTX_NEXT => 'CTX_SWITCH_BLOCK',),
),
);
$fsm_context_rules_loops = array(
'CTX_WHILE' => array(
'{' => 'CTX_GENERIC_BLOCK',
';' => -1
) + $fsm_inline_rules,
'CTX_FOREACH' => array(
'{' => 'CTX_GENERIC_BLOCK',
';' => -1
) + $fsm_inline_rules,
'CTX_FOR' => array(
'(' => 'CTX_FOR_PARENTHESIS',
'(_LONG' => 'CTX_FOR_PARENTHESIS',
),
'CTX_DO' => array(
'{' => array('CTX_GENERIC_BLOCK'),
'T_WHILE' => 'CTX_WHILE_AFTER_DO',
PHPCF_KEY_ALL => -1
),
'CTX_WHILE_AFTER_DO' => array(
';' => -1,
),
);
$fsm_context_rules_parenthesis = array(
'CTX_FOR_PARENTHESIS' => array(
')' => -1,
) + $fsm_inline_rules,
'CTX_GENERIC_PARENTHESIS' => array(
')' => -1,
) + $fsm_inline_rules,
'CTX_LONG_PARENTHESIS CTX_ARRAY_LONG_PARENTHESIS' => array(
')' => array(PHPCF_CTX_NOW => 'CTX_LONG_PAR_END', PHPCF_CTX_NEXT => -1),
'._NL' => array('CTX_LONG_FIRST_NL'),
) + $fsm_inline_rules,
);
$fsm_context_rules_conditions = array(
'CTX_IF CTX_ELSEIF' => array(
'(_LONG' => array('CTX_GENERIC_PARENTHESIS'),
'{' => 'CTX_GENERIC_BLOCK',
';' => -1,
) + $fsm_inline_rules,
'CTX_ELSE' => array(
'{' => 'CTX_GENERIC_BLOCK',
';' => -1,
),
'CTX_TERNARY_BEGIN' => array(
':' => array(PHPCF_CTX_NOW => 'CTX_TERNARY_OPERATOR', PHPCF_CTX_NEXT => -1),
) + $fsm_inline_rules,
);
$fsm_context_rules_array = array(
'CTX_ARRAY' => array(
'(_LONG' => 'CTX_ARRAY_LONG_PARENTHESIS',
'(' => 'CTX_GENERIC_PARENTHESIS',
),
);
$fsm_context_rules_class = array(
'CTX_CLASS_D' => array(
'{' => array(PHPCF_CTX_NOW => 'CTX_CLASS_D', PHPCF_CTX_NEXT => 'CTX_CLASS'),
),
'CTX_CLASS' => array(
'T_PUBLIC' => array('CTX_CLASS_DEF'),
'T_CONST' => array('CTX_CLASS_DEF'),
'T_PRIVATE' => array('CTX_CLASS_DEF'),
'T_PROTECTED' => array('CTX_CLASS_DEF'),
'T_STATIC' => array('CTX_CLASS_DEF'),
'T_CONST' => array('CTX_CLASS_DEF'),
'T_FINAL' => array('CTX_CLASS_DEF'),
'T_ABSTRACT' => array('CTX_CLASS_DEF'),
'T_PUBLIC_NL' => array('CTX_CLASS_DEF_NL'),
'T_CONST_NL' => array('CTX_CLASS_DEF_NL'),
'T_PRIVATE_NL' => array('CTX_CLASS_DEF_NL'),
'T_PROTECTED_NL' => array('CTX_CLASS_DEF_NL'),
'T_STATIC_NL' => array('CTX_CLASS_DEF_NL'),
'T_CONST_NL' => array('CTX_CLASS_DEF_NL'),
'T_FINAL' => array('CTX_CLASS_DEF_NL'),
'T_VAR' => array('CTX_CLASS_VARIABLE_D'),
'T_FUNCTION' => array('CTX_CLASS_METHOD_D'),
'}' => -1,
),
'CTX_CLASS_DEF' => array(
'T_FUNCTION' => 'CTX_CLASS_METHOD_D',
'T_CONST' => 'CTX_CLASS_CONST_D',
'T_VARIABLE' => 'CTX_CLASS_VARIABLE_D',
'T_PUBLIC_NL' => 'CTX_CLASS_DEF_NL',
'T_CONST_NL' => 'CTX_CLASS_DEF_NL',
'T_PRIVATE_NL' => 'CTX_CLASS_DEF_NL',
'T_PROTECTED_NL' => 'CTX_CLASS_DEF_NL',
'T_STATIC_NL' => 'CTX_CLASS_DEF_NL',
'T_CONST_NL' => 'CTX_CLASS_DEF_NL',
'T_FINAL' => 'CTX_CLASS_DEF_NL',
),
'CTX_CLASS_DEF_NL' => array(
'T_FUNCTION' => 'CTX_CLASS_METHOD_D_NL',
'T_STRING' => 'CTX_CLASS_CONST_D_NL',
'T_VARIABLE' => 'CTX_CLASS_VARIABLE_D_NL',
),
'CTX_CLASS_CONST_D' => array(
'T_STRING' => 'CTX_CLASS_CONST',
),
'CTX_CLASS_CONST_D_NL' => array(
'T_STRING' => 'CTX_CLASS_CONST_NL',
),
'CTX_CLASS_CONST' => array(
';' => -1,
),
'CTX_CLASS_CONST_NL' => array(
';' => array(PHPCF_CTX_NOW => 'CTX_CLASS_CONST_NL_END', PHPCF_CTX_NEXT => -1)
),
'CTX_CLASS_VARIABLE_D' => array(
';' => -1,
) + $fsm_inline_rules,
'CTX_CLASS_VARIABLE_D_NL' => array(
';' => array(PHPCF_CTX_NOW => 'CTX_CLASS_VARIABLE_D_NL_END', PHPCF_CTX_NEXT => -1)
) + $fsm_inline_rules,
'CTX_CLASS_METHOD_D CTX_CLASS_METHOD_D_NL' => array(
'{' => array(PHPCF_CTX_NOW => 'CTX_CLASS_METHOD_D', PHPCF_CTX_NEXT => 'CTX_CLASS_METHOD'),
),
'CTX_FUNCTION_D' => array(
'{' => array(PHPCF_CTX_NOW => 'CTX_FUNCTION_D', PHPCF_CTX_NEXT => 'CTX_FUNCTION'),
),
'CTX_CLASS_METHOD' => array('}' => -1) + $fsm_generic_code_block_rules,
'CTX_FUNCTION' => $fsm_generic_code_block_rules,
);
$fsm_context_rules = array(
0 => 'CTX_DEFAULT',
'CTX_DEFAULT' => array(
'T_OPEN_TAG' => 'CTX_PHP',
'T_OPEN_TAG_WITH_ECHO' => 'CTX_PHP',
),
'CTX_PHP' => $fsm_generic_code_rules,
'CTX_INLINE_BRACE_BEGIN' => array(
'{' => 'CTX_INLINE_BRACE',
PHPCF_KEY_ALL => -1,
),
'CTX_INLINE_BRACE' => array(
'}' => array(PHPCF_CTX_NOW => 'CTX_INLINE_BRACE', PHPCF_CTX_NEXT => -1),
),
'CTX_GENERIC_BLOCK' => $fsm_generic_code_block_rules,
'CTX_LONG_FIRST_NL CTX_LONG_EXPR_NL' => array(
'._NL' => 'CTX_LONG_EXPR_NL',
'T_DOUBLE_ARROW' => array(PHPCF_CTX_NOW => 'CTX_INLINE_EXPR_NL_END', PHPCF_CTX_NEXT => -1),
')' => array(PHPCF_CTX_NOW => 'CTX_LONG_EXPR_NL_END', PHPCF_CTX_NEXT => -2),
) + $fsm_inline_rules,
'CTX_INLINE_FIRST_NL CTX_INLINE_EXPR_NL' => array(
'._NL' => 'CTX_INLINE_EXPR_NL',
')' => array(PHPCF_CTX_NOW => 'CTX_INLINE_EXPR_NL_END', PHPCF_CTX_NEXT => -1),
';' => array(PHPCF_CTX_NOW => 'CTX_INLINE_EXPR_NL_END', PHPCF_CTX_NEXT => -1),
'T_DOUBLE_ARROW' => array(PHPCF_CTX_NOW => 'CTX_INLINE_EXPR_NL_END', PHPCF_CTX_NEXT => -1),
) + $fsm_inline_rules,
);
$fsm_context_rules += $fsm_context_rules_parenthesis;
$fsm_context_rules += $fsm_context_rules_conditions;
$fsm_context_rules += $fsm_context_rules_loops;
$fsm_context_rules += $fsm_context_rules_switch;
$fsm_context_rules += $fsm_context_rules_array;
$fsm_context_rules += $fsm_context_rules_class;
// print_r($fsm_context_rules);
$options = array(
'debug' => 0,
'quiet' => 0,
'summary' => 0,
'check' => 0,
'preview' => 0,
'apply' => 0,
'lines' => null,
);
foreach ($argv as $k => $v) {
if ($v === '--debug' || $v === '-d') {
unset($argv[$k]);
$options['debug'] = 1;
}
if ($v === '--quiet' || $v === '-q') {
unset($argv[$k]);
$options['quiet'] = 1;
}
if ($v === '--summary' || $v === '-s') {
unset($argv[$k]);
$options['summary'] = 1;
}
$arg = '--lines=';
if (substr($v, 0, strlen($arg)) == $arg) {
$lines_str = substr($argv[$k], strlen($arg));
$options['lines'] = $lines_str;
unset($argv[$k]);
}
}
$argv = array_values($argv);
$argc = count($argv);
if ($argc < 3 || !isset($options[$argv[1]])) {
if ($argc > 1 && !isset($options[$argv[1]])) {
fwrite(STDERR, "ERROR: Unknown command: " . $argv[1] . "\n\n");
}
fwrite(STDERR, "Usage: $argv[0] [<flags>] <command> <filename> [ ... <filename>]\n\n");
fwrite(STDERR, "Flags:\n");
fwrite(STDERR, " --debug turn on debug mode\n");
fwrite(STDERR, " --quiet do not print status messages\n");
fwrite(STDERR, " --summary show only number of formatting error messages (if any)\n");
fwrite(STDERR, " --lines=... comma-separated list of line numbers to format instead of all file\n\n");
fwrite(STDERR, "Commands:\n");
fwrite(STDERR, " check just check a file and report about problems with non-zero exit code\n");
fwrite(STDERR, " apply format file, overwrite it and print report\n");
fwrite(STDERR, " preview show diff between original and suggested format and print report\n");
exit(1);
}
$options[$argv[1]] = 1;
define('PHPCF_DEBUG', $options['debug']);
define('PHPCF_SNIFF', true);
define('PHPCF_APPLY', $options['apply']);
define('PHPCF_PREVIEW', $options['preview']);
define('PHPCF_SUMMARY', $options['summary']);
function phpcf_format_file($filename, $fsm_context_rules, $controls, $options, $lock = false)
{
if ($lock) $fp = fopen($lock, 'w+');
$F = new PHPCodeFormatter($filename, $fsm_context_rules, $controls);
$F->process();
$lines = null;
if (isset($options['lines'])) {
$lines = strlen($options['lines']) ? array_flip(explode(',', $options['lines'])) : array();
}
$formatted = $F->exec($lines);
if ($lock) flock($fp, LOCK_EX);
if ($F->sniff_exit_code) fwrite(STDERR, "Errors for $filename:\n");
$F->printSnifferMessages($options['lines']);
if (PHPCF_PREVIEW) {
$tmpnam = tempnam('/tmp', 'phpcf');
if (!$tmpnam) return false;
if (!file_put_contents($tmpnam, $formatted)) return false;
system('diff -u ' . escapeshellarg($filename) . ' ' . $tmpnam . ' | cdiff');
if (file_exists($tmpnam)) unlink($tmpnam);
}
if (PHPCF_APPLY) {
$success = file_put_contents($filename, $formatted);
if (!$options['quiet'] && $success !== false) {
echo "$filename formatted successfully\n";
}
if ($success === false) {
fwrite(STDERR, "Could not format $filename\n");
return false;
}
}
if (PHPCF_SNIFF) {
if (!$F->sniff_exit_code && !$options['quiet'] && !PHPCF_APPLY) {
echo "$filename is OK\n";
}
}
if ($options['check'] && $F->sniff_exit_code) return false;
return true;
}
if (function_exists('pcntl_fork')) {
$lock = tempnam('/tmp', 'phpcf-lock');
$children = 0;
for ($i = 2; $i < $argc; $i++) {
$filename = $argv[$i];
$pid = pcntl_fork();
if ($pid > 0) $children++;
if ($pid == 0) exit(phpcf_format_file($filename, $fsm_context_rules, $controls, $options, $lock) ? 0 : 1);
}
$fp = fopen($lock, 'w+');
$children_status = 0;
$success = true;
for ($i = 0; $i < $children; $i++) {
pcntl_wait($children_status);
if (pcntl_wexitstatus($children_status)) $success = false;
}
fclose($fp);
unlink($lock);
} else {
$success = true;
for ($i = 2; $i < $argc; $i++) {
$filename = $argv[$i];
if (!phpcf_format_file($filename, $fsm_context_rules, $controls, $options)) $success = false;
}
}
exit($success ? 0 : 1);
|