diff --git a/.jscsrc b/.jscsrc new file mode 100644 index 0000000000..9dd5ab82e6 --- /dev/null +++ b/.jscsrc @@ -0,0 +1,78 @@ + +{ + "excludeFiles": ["node_modules/**", "**/build/**"], + "requireCurlyBraces": [ + "if", "else", "for", "while", "do", "try", "catch" + ], + "requireSpaceBeforeKeywords": [ + "else", "while", "catch" + ], + "requireSpaceAfterKeywords": [ + "do", "for", "if", "else", "switch", "case", "try", "catch", "while", "return", "typeof" + ], + "requireSpaceBeforeBlockStatements": true, + "requireParenthesesAroundIIFE": true, + "requireSpacesInConditionalExpression": { + "afterTest": true, + "beforeConsequent": true, + "afterConsequent": true, + "beforeAlternate": true + }, + "requireSpacesInAnonymousFunctionExpression": { + "beforeOpeningCurlyBrace": true + }, + "disallowSpacesInNamedFunctionExpression": { + "beforeOpeningRoundBrace": true + }, + "requireSpacesInFunction": { + "beforeOpeningCurlyBrace": true + }, + "disallowSpacesInCallExpression": true, + "requireBlocksOnNewline": true, + "requirePaddingNewlinesBeforeKeywords": ["case"], + "disallowEmptyBlocks": true, + "disallowSpacesInsideArrayBrackets": "nested", + "disallowSpacesInsideParentheses": true, + "requireSpacesInsideObjectBrackets": "all", + "disallowQuotedKeysInObjects": "allButReserved", + "disallowSpaceAfterObjectKeys": true, + "requireSpaceBeforeObjectValues": true, + "requireCommaBeforeLineBreak": true, + "requireOperatorBeforeLineBreak": [ + "?", "=", "+", "-", "/", "*", "===", "!==", ">", ">=", "<", "<=" + ], + "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], + "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], + "requireSpaceBeforeBinaryOperators": [ + "=", "+", "+=", "-", "-=", "/", "/=", "*", "*=", "===", "!==", "<", "<=", ">", ">=" + ], + "requireSpaceAfterBinaryOperators": [ + "=", "+", "+=", "-", "-=", "/", "/=", "*", "*=", "===", "!==", "<", "<=", ">", ">=" + ], + "disallowKeywords": ["with"], + "disallowMultipleLineStrings": true, + "disallowMixedSpacesAndTabs": "smart", + "disallowTrailingWhitespace": true, + "disallowTrailingComma": true, + "disallowKeywordsOnNewLine": ["else"], + "requireLineFeedAtFileEnd": true, + "maximumLineLength": { + "value": 120, + "tabSize": 4, + "allowUrlComments": true, + "allowRegex": true + }, + "requireCapitalizedConstructors": true, + "requireDotNotation": true, + "disallowYodaConditions": true, + "requireSpaceAfterLineComment": { + "allExcept": ["#", "="] + }, + "disallowNewlineBeforeBlockStatements": true, + "validateQuoteMarks": { + "mark": "'", + "escape": true + }, + "validateParameterSeparator": ", ", + "safeContextKeyword": ["that"] +} diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000000..90d3bb613e --- /dev/null +++ b/.jshintrc @@ -0,0 +1,24 @@ + +{ + "bitwise": true, + "curly": true, + "eqeqeq": true, + "es3": true, + "forin": false, + "freeze": true, + "newcap": true, + "noarg": true, + "noempty": true, + "nonbsp": true, + "undef": true, + "unused": true, + "strict": true, + + "browser": true, + "devel": true, + "jquery": true, + + "globals": { + "JSON": true + } +} diff --git a/.travis.yml b/.travis.yml index e68ba5f501..2542898324 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,6 +46,7 @@ script: - travis/check-sami-parse-errors.sh $DB $TRAVIS_PHP_VERSION - travis/check-image-icc-profiles.sh $DB $TRAVIS_PHP_VERSION - travis/check-executable-files.sh $DB $TRAVIS_PHP_VERSION ./ + - sh -c "if [ '$SLOWTESTS' != '1' -a '$DB' = 'mysqli' ]; then phpBB/vendor/bin/phpunit tests/lint_test.php; fi" - sh -c "if [ '$SLOWTESTS' != '1' ]; then phpBB/vendor/bin/phpunit --configuration travis/phpunit-$DB-travis.xml; fi" - sh -c "if [ '$SLOWTESTS' = '1' ]; then phpBB/vendor/bin/phpunit --configuration travis/phpunit-$DB-travis.xml --group slow; fi" - sh -c "if [ '$TRAVIS_PHP_VERSION' = '5.3.3' -a '$DB' = 'mysqli' -a '$TRAVIS_PULL_REQUEST' != 'false' ]; then git-tools/commit-msg-hook-range.sh origin/$TRAVIS_BRANCH..FETCH_HEAD; fi" diff --git a/build/build.xml b/build/build.xml index b0a9190898..408eee3335 100644 --- a/build/build.xml +++ b/build/build.xml @@ -2,9 +2,9 @@ - - - + + + @@ -75,14 +75,14 @@ - @@ -177,13 +177,13 @@ save/phpbb-${prevversion}_to_${newversion}_language.patch" /> save/phpbb-${prevversion}_to_${newversion}_prosilver.patch" /> save/phpbb-${prevversion}_to_${newversion}_subsilver2.patch" /> addError('Missing required file doc comment.', $stackPtr); return; } // Find comment end token - $end = $phpcsFile->findNext(T_DOC_COMMENT, $start + 1, null, true) - 1; + $end = $tokens[$start]['comment_closer']; // If there is no end, skip processing here if ($end === false) @@ -75,38 +75,30 @@ class phpbb_Sniffs_Commenting_FileCommentSniff implements PHP_CodeSniffer_Sniff return; } - // List of found comment tags - $tags = array(); - // check comment lines without the first(/**) an last(*/) line - for ($i = $start + 1, $c = $end - 1; $i <= $c; ++$i) + for ($token = $start + 1, $c = $end - 2; $token <= $c; ++$token) { - $line = $tokens[$i]['content']; - // Check that each line starts with a '*' - if (substr($line, 0, 1) !== '*' && substr($line, 0, 2) !== ' *') + if ($tokens[$token]['column'] === 1 && (($tokens[$token]['content'] !== '*' && $tokens[$token]['content'] !== ' ') || ($tokens[$token]['content'] === ' ' && $tokens[$token + 1]['content'] !== '*'))) { $message = 'The file doc comment should not be indented.'; - $phpcsFile->addWarning($message, $i); - } - else if (preg_match('/^[ ]?\*\s+@([\w]+)\s+(.*)$/', $line, $match) !== 0) - { - if (!isset($tags[$match[1]])) - { - $tags[$match[1]] = array(); - } - - $tags[$match[1]][] = array($match[2], $i); + $phpcsFile->addWarning($message, $token); } } // Check that the first and last line is empty - if (trim($tokens[$start + 1]['content']) !== '*') + // /**T_WHITESPACE + // (T_WHITESPACE)*T_WHITESPACE + // (T_WHITESPACE)* ... + // (T_WHITESPACE)*T_WHITESPACE + // T_WHITESPACE*/ + if (!(($tokens[$start + 2]['content'] !== '*' && $tokens[$start + 4]['content'] !== '*') || ($tokens[$start + 3]['content'] !== '*' && $tokens[$start + 6]['content'] !== '*'))) { $message = 'The first file comment line should be empty.'; $phpcsFile->addWarning($message, ($start + 1)); } - if (trim($tokens[$end - 1]['content']) !== '*') + + if ($tokens[$end - 3]['content'] !== '*' && $tokens[$end - 6]['content'] !== '*') { $message = 'The last file comment line should be empty.'; $phpcsFile->addWarning($message, $end - 1); @@ -114,8 +106,8 @@ class phpbb_Sniffs_Commenting_FileCommentSniff implements PHP_CodeSniffer_Sniff //$this->processPackage($phpcsFile, $start, $tags); //$this->processVersion($phpcsFile, $start, $tags); - $this->processCopyright($phpcsFile, $start, $tags); - $this->processLicense($phpcsFile, $start, $tags); + $this->processCopyright($phpcsFile, $start, $tokens[$start]['comment_tags']); + $this->processLicense($phpcsFile, $start, $tokens[$start]['comment_tags']); } /** @@ -176,17 +168,24 @@ class phpbb_Sniffs_Commenting_FileCommentSniff implements PHP_CodeSniffer_Sniff protected function processCopyright(PHP_CodeSniffer_File $phpcsFile, $ptr, $tags) { $copyright = '(c) phpBB Limited '; + $tokens = $phpcsFile->getTokens(); - if (!isset($tags['copyright'])) + foreach ($tags as $tag) { - $message = 'Missing require @copyright tag in file doc comment.'; - $phpcsFile->addError($message, $ptr); - } - else if ($tags['copyright'][0][0] !== $copyright) - { - $message = 'Invalid content found for the first @copyright tag, use "' . $copyright . '".'; - $phpcsFile->addError($message, $tags['copyright'][0][1]); + if ($tokens[$tag]['content'] === '@copyright') + { + if ($tokens[$tag + 2]['content'] !== $copyright) + { + $message = 'Invalid content found for the first @copyright tag, use "' . $copyright . '".'; + $phpcsFile->addError($message, $tags['copyright'][0][1]); + } + + return; + } } + + $message = 'Missing require @copyright tag in file doc comment.'; + $phpcsFile->addError($message, $ptr); } /** @@ -201,22 +200,33 @@ class phpbb_Sniffs_Commenting_FileCommentSniff implements PHP_CodeSniffer_Sniff protected function processLicense(PHP_CodeSniffer_File $phpcsFile, $ptr, $tags) { $license = 'GNU General Public License, version 2 (GPL-2.0)'; + $tokens = $phpcsFile->getTokens(); - if (!isset($tags['license'])) + $found = false; + foreach ($tags as $tag) + { + if ($tokens[$tag]['content'] === '@license') + { + if ($found) + { + $message = 'It must be only one @license tag in file doc comment.'; + $phpcsFile->addError($message, $ptr); + } + + $found = true; + + if ($tokens[$tag + 2]['content'] !== $license) + { + $message = 'Invalid content found for @license tag, use "' . $license . '".'; + $phpcsFile->addError($message, $tags['license'][0][1]); + } + } + } + + if (!$found) { $message = 'Missing require @license tag in file doc comment.'; $phpcsFile->addError($message, $ptr); } - else if (sizeof($tags['license']) !== 1) - { - $message = 'It must be only one @license tag in file doc comment.'; - $phpcsFile->addError($message, $ptr); - } - else if (trim($tags['license'][0][0]) !== $license) - { - $message = 'Invalid content found for @license tag, use ' - . '"' . $license . '".'; - $phpcsFile->addError($message, $tags['license'][0][1]); - } } } diff --git a/build/code_sniffer/phpbb/Sniffs/ControlStructures/OpeningBraceBsdAllmanSniff.php b/build/code_sniffer/phpbb/Sniffs/ControlStructures/OpeningBraceBsdAllmanSniff.php new file mode 100644 index 0000000000..885c38c5b4 --- /dev/null +++ b/build/code_sniffer/phpbb/Sniffs/ControlStructures/OpeningBraceBsdAllmanSniff.php @@ -0,0 +1,143 @@ + +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +/** + * Checks that the opening brace of a control structures is on the line after. + * From Generic_Sniffs_Functions_OpeningFunctionBraceBsdAllmanSniff + */ +class phpbb_Sniffs_ControlStructures_OpeningBraceBsdAllmanSniff implements PHP_CodeSniffer_Sniff +{ + /** + * Registers the tokens that this sniff wants to listen for. + */ + public function register() + { + return array( + T_IF, + T_ELSE, + T_FOREACH, + T_WHILE, + T_DO, + T_FOR, + T_SWITCH, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the + * stack passed in $tokens. + * + * @return void + */ + public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + if (isset($tokens[$stackPtr]['scope_opener']) === false) + { + return; + } + + /* + * ... + * } + * else if () + * { + * ... + */ + if ($tokens[$stackPtr]['code'] === T_ELSE && $tokens[$stackPtr + 2]['code'] === T_IF) + { + return; + } + + $openingBrace = $tokens[$stackPtr]['scope_opener']; + + /* + * ... + * do + * { + * + * } while(); + * ... + * } + * else + * { + * ... + */ + if ($tokens[$stackPtr]['code'] === T_DO ||$tokens[$stackPtr]['code'] === T_ELSE) + { + $cs_line = $tokens[$stackPtr]['line']; + } + else + { + // The end of the function occurs at the end of the argument list. Its + // like this because some people like to break long function declarations + // over multiple lines. + $cs_line = $tokens[$tokens[$stackPtr]['parenthesis_closer']]['line']; + } + + $braceLine = $tokens[$openingBrace]['line']; + + $lineDifference = ($braceLine - $cs_line); + + if ($lineDifference === 0) + { + $error = 'Opening brace should be on a new line'; + $phpcsFile->addError($error, $openingBrace, 'BraceOnSameLine'); + return; + } + + if ($lineDifference > 1) + { + $error = 'Opening brace should be on the line after the declaration; found %s blank line(s)'; + $data = array(($lineDifference - 1)); + $phpcsFile->addError($error, $openingBrace, 'BraceSpacing', $data); + return; + } + + // We need to actually find the first piece of content on this line, + // as if this is a method with tokens before it (public, static etc) + // or an if with an else before it, then we need to start the scope + // checking from there, rather than the current token. + $lineStart = $stackPtr; + while (($lineStart = $phpcsFile->findPrevious(array(T_WHITESPACE), ($lineStart - 1), null, false)) !== false) + { + if (strpos($tokens[$lineStart]['content'], $phpcsFile->eolChar) !== false) + { + break; + } + } + + // We found a new line, now go forward and find the first non-whitespace + // token. + $lineStart = $phpcsFile->findNext(array(T_WHITESPACE), $lineStart, null, true); + + // The opening brace is on the correct line, now it needs to be + // checked to be correctly indented. + $startColumn = $tokens[$lineStart]['column']; + $braceIndent = $tokens[$openingBrace]['column']; + + if ($braceIndent !== $startColumn) + { + $error = 'Opening brace indented incorrectly; expected %s spaces, found %s'; + $data = array( + ($startColumn - 1), + ($braceIndent - 1), + ); + $phpcsFile->addError($error, $openingBrace, 'BraceIndent', $data); + } + } +} diff --git a/build/code_sniffer/phpbb/Sniffs/ControlStructures/OpeningParenthesisSniff.php b/build/code_sniffer/phpbb/Sniffs/ControlStructures/OpeningParenthesisSniff.php new file mode 100644 index 0000000000..349bccbb02 --- /dev/null +++ b/build/code_sniffer/phpbb/Sniffs/ControlStructures/OpeningParenthesisSniff.php @@ -0,0 +1,60 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +/** + * Checks that there is exactly one space between the keyword and the opening + * parenthesis of a control structures. + */ +class phpbb_Sniffs_ControlStructures_OpeningParenthesisSniff implements PHP_CodeSniffer_Sniff +{ + /** + * Registers the tokens that this sniff wants to listen for. + */ + public function register() + { + return array( + T_IF, + T_FOREACH, + T_WHILE, + T_FOR, + T_SWITCH, + T_ELSEIF, + T_CATCH, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the + * stack passed in $tokens. + * + * @return void + */ + public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + if ($tokens[$stackPtr + 1]['content'] === '(') + { + $error = 'There should be exactly one space between the keyword and opening parenthesis'; + $phpcsFile->addError($error, $stackPtr, 'NoSpaceBeforeOpeningParenthesis'); + } + else if ($tokens[$stackPtr + 1]['content'] !== ' ') + { + $error = 'There should be exactly one space between the keyword and opening parenthesis'; + $phpcsFile->addError($error, $stackPtr, 'IncorrectSpaceBeforeOpeningParenthesis'); + } + } +} diff --git a/build/code_sniffer/phpbb/Sniffs/Namespaces/UnusedUseSniff.php b/build/code_sniffer/phpbb/Sniffs/Namespaces/UnusedUseSniff.php index 18cb8ba82e..7ffd1aadd6 100644 --- a/build/code_sniffer/phpbb/Sniffs/Namespaces/UnusedUseSniff.php +++ b/build/code_sniffer/phpbb/Sniffs/Namespaces/UnusedUseSniff.php @@ -138,6 +138,7 @@ class phpbb_Sniffs_Namespaces_UnusedUseSniff implements PHP_CodeSniffer_Sniff // Check docblocks $find = array( T_COMMENT, + T_DOC_COMMENT_CLOSE_TAG, T_DOC_COMMENT, T_CLASS, T_FUNCTION, @@ -147,43 +148,31 @@ class phpbb_Sniffs_Namespaces_UnusedUseSniff implements PHP_CodeSniffer_Sniff $comment_end = $phpcsFile->findPrevious($find, ($function_declaration - 1)); if ($comment_end !== false) { - if (!$tokens[$comment_end]['code'] !== T_DOC_COMMENT) + if ($tokens[$comment_end]['code'] === T_DOC_COMMENT_CLOSE_TAG) { - $comment_start = ($phpcsFile->findPrevious(T_DOC_COMMENT, ($comment_end - 1), null, true) + 1); - $comment = $phpcsFile->getTokensAsString($comment_start, ($comment_end - $comment_start + 1)); - - try - { - $comment_parser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($comment, $phpcsFile); - $comment_parser->parse(); - - // Check @param - foreach ($comment_parser->getParams() as $param) { - $type = $param->getType(); - $types = explode('|', str_replace('[]', '', $type)); - foreach ($types as $type) - { - $ok = $this->check($phpcsFile, $type, $class_name_full, $class_name_short, $param->getLine() + $comment_start) ? true : $ok; - } + $comment_start = $tokens[$comment_end]['comment_opener']; + foreach ($tokens[$comment_start]['comment_tags'] as $tag) { + if ($tokens[$tag]['content'] !== '@param' && $tokens[$tag]['content'] !== '@return' && $tokens[$tag]['content'] !== '@throws') { + continue; } - // Check @return - $return = $comment_parser->getReturn(); - if ($return !== null) + $classes = $tokens[($tag + 2)]['content']; + $space = strpos($classes, ' '); + if ($space !== false) { + $classes = substr($classes, 0, $space); + } + + $tab = strpos($classes, "\t"); + if ($tab !== false) { + $classes = substr($classes, 0, $tab); + } + + $classes = explode('|', str_replace('[]', '', $classes)); + foreach ($classes as $class) { - $type = $return->getValue(); - $types = explode('|', str_replace('[]', '', $type)); - foreach ($types as $type) - { - $ok = $this->check($phpcsFile, $type, $class_name_full, $class_name_short, $return->getLine() + $comment_start) ? true : $ok; - } + $ok = $this->check($phpcsFile, $class, $class_name_full, $class_name_short, $tokens[$tag + 2]['line']) ? true : $ok; } } - catch (PHP_CodeSniffer_CommentParser_ParserException $e) - { - $line = ($e->getLineWithinComment() + $comment_start); - $phpcsFile->addError($e->getMessage(), $line, 'FailedParse'); - } } } @@ -195,6 +184,20 @@ class phpbb_Sniffs_Namespaces_UnusedUseSniff implements PHP_CodeSniffer_Sniff } } + // Checks in catch blocks + $old_catch = $stackPtr; + while (($catch = $phpcsFile->findNext(T_CATCH, ($old_catch + 1))) !== false) + { + $old_catch = $catch; + + $caught_class_name_start = $phpcsFile->findNext(array(T_NS_SEPARATOR, T_STRING), $catch + 1); + $caught_class_name_end = $phpcsFile->findNext($find, $caught_class_name_start + 1, null, true); + + $caught_class_name = trim($phpcsFile->getTokensAsString($caught_class_name_start, ($caught_class_name_end - $caught_class_name_start))); + + $ok = $this->check($phpcsFile, $caught_class_name, $class_name_full, $class_name_short, $catch) ? true : $ok; + } + if (!$ok) { $error = 'There must not be unused USE statements.'; diff --git a/build/code_sniffer/ruleset-minimum.xml b/build/code_sniffer/ruleset-minimum.xml index 33d0177390..13f122cae7 100644 --- a/build/code_sniffer/ruleset-minimum.xml +++ b/build/code_sniffer/ruleset-minimum.xml @@ -12,4 +12,7 @@ + + + diff --git a/build/code_sniffer/ruleset-php-legacy.xml b/build/code_sniffer/ruleset-php-legacy.xml index b0110e8b12..c740c6080f 100644 --- a/build/code_sniffer/ruleset-php-legacy.xml +++ b/build/code_sniffer/ruleset-php-legacy.xml @@ -86,4 +86,7 @@ + + + diff --git a/build/sami-all.conf.php b/build/sami-all.conf.php index fb1a269206..9c10209e8b 100644 --- a/build/sami-all.conf.php +++ b/build/sami-all.conf.php @@ -25,6 +25,7 @@ $config['versions'] = Sami\Version\GitVersionCollection::create(__DIR__ . '/../' */ ->add('3.0.x') ->add('3.1.x') + ->add('master') ; return new Sami\Sami($iterator, $config); diff --git a/phpBB/adm/style/acp_ext_list.html b/phpBB/adm/style/acp_ext_list.html index f96da7e26a..f61be27c9b 100644 --- a/phpBB/adm/style/acp_ext_list.html +++ b/phpBB/adm/style/acp_ext_list.html @@ -7,7 +7,7 @@

{L_EXTENSIONS_EXPLAIN}

- {L_VERSIONCHECK_FORCE_UPDATE_ALL}{L_SETTINGS} + {L_BROWSE_EXTENSIONS_DATABASE}{L_VERSIONCHECK_FORCE_UPDATE_ALL}{L_SETTINGS}