public function process(XHPASTNode $root) { $logical_ands = $root->selectTokensOfType('T_LOGICAL_AND'); $logical_ors = $root->selectTokensOfType('T_LOGICAL_OR'); foreach ($logical_ands as $logical_and) { $this->raiseLintAtToken($logical_and, pht('Use `%s` instead of `%s`.', '&&', 'and'), '&&'); } foreach ($logical_ors as $logical_or) { $this->raiseLintAtToken($logical_or, pht('Use `%s` instead of `%s`.', '||', 'or'), '||'); } }
public function process(XHPASTNode $root) { $tokens = $root->selectTokensOfType('T_ELSEIF'); foreach ($tokens as $token) { $this->raiseLintAtToken($token, pht('Usage of `%s` is preferred over `%s`.', 'else if', 'elseif'), 'else if'); } }
public function process(XHPASTNode $root) { $inline_html = $root->selectDescendantsOfType('n_INLINE_HTML'); if ($inline_html) { return; } foreach ($root->selectTokensOfType('T_CLOSE_TAG') as $token) { $this->raiseLintAtToken($token, pht('Do not use the PHP closing tag, "%s".', '?>')); } }
public function process(XHPASTNode $root) { foreach ($root->selectTokensOfType('T_COMMENT') as $comment) { $value = $comment->getValue(); if ($value[0] !== '#') { continue; } $this->raiseLintAtOffset($comment->getOffset(), pht('Use `%s` single-line comments, not `%s`.', '//', '#'), '#', preg_match('/^#\\S/', $value) ? '// ' : '//'); } }
public function process(XHPASTNode $root) { $tokens = $root->selectTokensOfType(';'); foreach ($tokens as $token) { $prev = $token->getPrevToken(); if ($prev->isAnyWhitespace()) { $this->raiseLintAtToken($prev, pht('Space found before semicolon.'), ''); } } }
public function process(XHPASTNode $root) { foreach ($root->selectTokensOfType('T_COMMENT') as $comment) { $value = $comment->getValue(); if ($value[0] !== '#') { $match = null; if (preg_match('@^(/[/*]+)[^/*\\s]@', $value, $match)) { $this->raiseLintAtOffset($comment->getOffset(), pht('Put space after comment start.'), $match[1], $match[1] . ' '); } } } }
public function process(XHPASTNode $root) { $inline_html = $root->selectTokensOfType('T_INLINE_HTML'); foreach ($inline_html as $html) { if (substr($html->getValue(), 0, 2) == '#!') { // Ignore shebang lines. continue; } if (preg_match('/^\\s*$/', $html->getValue())) { continue; } $this->raiseLintAtToken($html, pht('PHP files must only contain PHP code.')); break; } }
public function process(XHPASTNode $root) { $double_colons = $root->selectTokensOfType('T_PAAMAYIM_NEKUDOTAYIM'); foreach ($double_colons as $double_colon) { $tokens = $double_colon->getNonsemanticTokensBefore() + $double_colon->getNonsemanticTokensAfter(); foreach ($tokens as $token) { if ($token->isAnyWhitespace()) { if (strpos($token->getValue(), "\n") !== false) { continue; } $this->raiseLintAtToken($token, pht('Unnecessary whitespace around paamayim nekudotayim ' . '(double colon) operator.'), ''); } } } }
public function process(XHPASTNode $root) { $class_declarations = $root->selectDescendantsOfType('n_CLASS_DECLARATION'); foreach ($class_declarations as $class_declaration) { $class_name = $class_declaration->getChildOfType(1, 'n_CLASS_NAME')->getConcreteString(); $class_static_accesses = $class_declaration->selectDescendantsOfType('n_CLASS_STATIC_ACCESS'); $closures = $this->getAnonymousClosures($class_declaration); foreach ($class_static_accesses as $class_static_access) { $double_colons = $class_static_access->selectTokensOfType('T_PAAMAYIM_NEKUDOTAYIM'); $class_ref = $class_static_access->getChildByIndex(0); if ($class_ref->getTypeName() != 'n_CLASS_NAME') { continue; } $class_ref_name = $class_ref->getConcreteString(); if (strtolower($class_name) == strtolower($class_ref_name)) { $in_closure = false; foreach ($closures as $closure) { if ($class_ref->isDescendantOf($closure)) { $in_closure = true; break; } } if (version_compare($this->version, '5.4.0', '>=') || !$in_closure) { $this->raiseLintAtNode($class_ref, pht('Use `%s` for local static member references.', 'self::'), 'self'); } } static $self_refs = array('parent', 'self', 'static'); if (!in_array(strtolower($class_ref_name), $self_refs)) { continue; } if ($class_ref_name != strtolower($class_ref_name)) { $this->raiseLintAtNode($class_ref, pht('PHP keywords should be lowercase.'), strtolower($class_ref_name)); } } } $double_colons = $root->selectTokensOfType('T_PAAMAYIM_NEKUDOTAYIM'); foreach ($double_colons as $double_colon) { $tokens = $double_colon->getNonsemanticTokensBefore() + $double_colon->getNonsemanticTokensAfter(); foreach ($tokens as $token) { if ($token->isAnyWhitespace()) { if (strpos($token->getValue(), "\n") !== false) { continue; } $this->raiseLintAtToken($token, pht('Unnecessary whitespace around double colon operator.'), ''); } } } }
public function process(XHPASTNode $root) { $operators = $root->selectTokensOfType('T_OBJECT_OPERATOR'); foreach ($operators as $operator) { $before = $operator->getNonsemanticTokensBefore(); $after = $operator->getNonsemanticTokensAfter(); if ($before) { $value = implode('', mpull($before, 'getValue')); if (strpos($value, "\n") !== false) { continue; } $this->raiseLintAtOffset(head($before)->getOffset(), pht('There should be no whitespace before the object operator.'), $value, ''); } if ($after) { $this->raiseLintAtOffset(head($after)->getOffset(), pht('There should be no whitespace after the object operator.'), implode('', mpull($before, 'getValue')), ''); } } }
public function process(XHPASTNode $root) { $tokens = $root->selectTokensOfType('T_OPEN_TAG'); foreach ($tokens as $token) { for ($next = $token->getNextToken(); $next; $next = $next->getNextToken()) { if ($next->getTypeName() == 'T_WHITESPACE' && preg_match('/\\n\\s*\\n/', $next->getValue())) { continue 2; } if ($token->getLineNumber() != $next->getLineNumber()) { break; } if ($next->getTypeName() == 'T_CLOSE_TAG') { continue 2; } } $next = $token->getNextToken(); $this->raiseLintAtToken($next, pht('`%s` should be separated from code by an empty line.', '<?php'), "\n" . $next->getValue()); } }
public function process(XHPASTNode $root) { $tokens = $root->selectTokensOfType('.'); foreach ($tokens as $token) { $prev = $token->getPrevToken(); $next = $token->getNextToken(); foreach (array('prev' => $prev, 'next' => $next) as $wtoken) { if ($wtoken->getTypeName() !== 'T_WHITESPACE') { continue; } $value = $wtoken->getValue(); if (strpos($value, "\n") !== false) { // If the whitespace has a newline, it's conventional. continue; } $next = $wtoken->getNextToken(); if ($next && $next->getTypeName() === 'T_COMMENT') { continue; } $this->raiseLintAtToken($wtoken, pht('Convention: no spaces around string concatenation operator.'), ''); } } }
private function lintPHP53Features(XHPASTNode $root) { $functions = $root->selectTokensOfType('T_FUNCTION'); foreach ($functions as $function) { $next = $function->getNextToken(); while ($next) { if ($next->isSemantic()) { break; } $next = $next->getNextToken(); } if ($next) { if ($next->getTypeName() === '(') { $this->raiseLintAtToken($function, pht('This codebase targets PHP %s, but anonymous ' . 'functions were not introduced until PHP 5.3.', $this->version)); } } } $namespaces = $root->selectTokensOfType('T_NAMESPACE'); foreach ($namespaces as $namespace) { $this->raiseLintAtToken($namespace, pht('This codebase targets PHP %s, but namespaces were not ' . 'introduced until PHP 5.3.', $this->version)); } // NOTE: This is only "use x;", in anonymous functions the node type is // n_LEXICAL_VARIABLE_LIST even though both tokens are T_USE. $uses = $root->selectDescendantsOfType('n_USE_LIST'); foreach ($uses as $use) { $this->raiseLintAtNode($use, pht('This codebase targets PHP %s, but namespaces were not ' . 'introduced until PHP 5.3.', $this->version)); } $statics = $root->selectDescendantsOfType('n_CLASS_STATIC_ACCESS'); foreach ($statics as $static) { $name = $static->getChildByIndex(0); if ($name->getTypeName() != 'n_CLASS_NAME') { continue; } if ($name->getConcreteString() === 'static') { $this->raiseLintAtNode($name, pht('This codebase targets PHP %s, but `%s` was not ' . 'introduced until PHP 5.3.', $this->version, 'static::')); } } $ternaries = $root->selectDescendantsOfType('n_TERNARY_EXPRESSION'); foreach ($ternaries as $ternary) { $yes = $ternary->getChildByIndex(2); if ($yes->getTypeName() === 'n_EMPTY') { $this->raiseLintAtNode($ternary, pht('This codebase targets PHP %s, but short ternary was ' . 'not introduced until PHP 5.3.', $this->version)); } } $heredocs = $root->selectDescendantsOfType('n_HEREDOC'); foreach ($heredocs as $heredoc) { if (preg_match('/^<<<[\'"]/', $heredoc->getConcreteString())) { $this->raiseLintAtNode($heredoc, pht('This codebase targets PHP %s, but nowdoc was not ' . 'introduced until PHP 5.3.', $this->version)); } } }
public function process(XHPASTNode $root) { foreach ($root->selectTokensOfType('T_CLOSE_TAG') as $token) { $this->raiseLintAtToken($token, pht('Do not use the PHP closing tag, "%s".', '?>')); } }
public function process(XHPASTNode $root) { $expressions = $root->selectDescendantsOfType('n_BINARY_EXPRESSION'); foreach ($expressions as $expression) { $operator = $expression->getChildByIndex(1); $operator_value = $operator->getConcreteString(); list($before, $after) = $operator->getSurroundingNonsemanticTokens(); $replace = null; if (empty($before) && empty($after)) { $replace = " {$operator_value} "; } else { if (empty($before)) { $replace = " {$operator_value}"; } else { if (empty($after)) { $replace = "{$operator_value} "; } } } if ($replace !== null) { $this->raiseLintAtNode($operator, pht('Convention: logical and arithmetic operators should be ' . 'surrounded by whitespace.'), $replace); } } $tokens = $root->selectTokensOfType(','); foreach ($tokens as $token) { $next = $token->getNextToken(); switch ($next->getTypeName()) { case ')': case 'T_WHITESPACE': break; default: $this->raiseLintAtToken($token, pht('Convention: comma should be followed by space.'), ', '); break; } } $tokens = $root->selectTokensOfType('T_DOUBLE_ARROW'); foreach ($tokens as $token) { $prev = $token->getPrevToken(); $next = $token->getNextToken(); $prev_type = $prev->getTypeName(); $next_type = $next->getTypeName(); $prev_space = $prev_type === 'T_WHITESPACE'; $next_space = $next_type === 'T_WHITESPACE'; $replace = null; if (!$prev_space && !$next_space) { $replace = ' => '; } else { if ($prev_space && !$next_space) { $replace = '=> '; } else { if (!$prev_space && $next_space) { $replace = ' =>'; } } } if ($replace !== null) { $this->raiseLintAtToken($token, pht('Convention: double arrow should be surrounded by whitespace.'), $replace); } } $parameters = $root->selectDescendantsOfType('n_DECLARATION_PARAMETER'); foreach ($parameters as $parameter) { if ($parameter->getChildByIndex(2)->getTypeName() == 'n_EMPTY') { continue; } $operator = head($parameter->selectTokensOfType('=')); $before = $operator->getNonsemanticTokensBefore(); $after = $operator->getNonsemanticTokensAfter(); $replace = null; if (empty($before) && empty($after)) { $replace = ' = '; } else { if (empty($before)) { $replace = ' ='; } else { if (empty($after)) { $replace = '= '; } } } if ($replace !== null) { $this->raiseLintAtToken($operator, pht('Convention: logical and arithmetic operators should be ' . 'surrounded by whitespace.'), $replace); } } }
protected function lintSemicolons(XHPASTNode $root) { $tokens = $root->selectTokensOfType(';'); foreach ($tokens as $token) { $prev = $token->getPrevToken(); if ($prev->isAnyWhitespace()) { $this->raiseLintAtToken($prev, self::LINT_SEMICOLON_SPACING, pht('Space found before semicolon.'), ''); } } }