diff --git a/.phplint.yml b/.phplint.yml index 35dda97..fd69190 100644 --- a/.phplint.yml +++ b/.phplint.yml @@ -2,3 +2,4 @@ path: ./ exclude: - vendor - moodle/Tests/fixtures + - moodle/Tests/Sniffs/Namespaces/fixtures diff --git a/moodle/Sniffs/Namespaces/NamespaceStatementSniff.php b/moodle/Sniffs/Namespaces/NamespaceStatementSniff.php new file mode 100644 index 0000000..19923b3 --- /dev/null +++ b/moodle/Sniffs/Namespaces/NamespaceStatementSniff.php @@ -0,0 +1,71 @@ +. + +/** + * Checks that each file contains the standard GPL comment. + * + * @package moodle-cs + * @copyright 2023 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace MoodleHQ\MoodleCS\moodle\Sniffs\Namespaces; + +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Util\Tokens; + +// phpcs:disable moodle.NamingConventions + +class NamespaceStatementSniff implements Sniff { + public function register() + { + return [ + T_NAMESPACE, + ]; + } + + public function process(File $file, $stackPtr) + { + $tokens = $file->getTokens(); + // Format should be: + // - T_NAMESPACE + // - T_WHITESPACE + // - T_STRING + + $checkPtr = $stackPtr + 2; + $token = $tokens[$checkPtr]; + if ($token['code'] === T_NS_SEPARATOR) { + $fqdn = ''; + $stop = $file->findNext(Tokens::$emptyTokens, ($stackPtr + 2)); + for ($i = $stackPtr + 2; $i < $stop; $i++) { + $fqdn .= $tokens[$i]['content']; + } + $fix = $file->addFixableError( + 'Namespace should not start with a slash: %s', + $checkPtr, + 'LeadingSlash', + [$fqdn] + ); + + if ($fix) { + $file->fixer->beginChangeset(); + $file->fixer->replaceToken($checkPtr, ''); + $file->fixer->endChangeset(); + } + } + } +} diff --git a/moodle/Tests/MoodleCSBaseTestCase.php b/moodle/Tests/MoodleCSBaseTestCase.php index a2dfaaf..623e15c 100644 --- a/moodle/Tests/MoodleCSBaseTestCase.php +++ b/moodle/Tests/MoodleCSBaseTestCase.php @@ -80,53 +80,18 @@ public function set_component_mapping(array $mapping): void { * * @param string $standard name of the standard to be tested. */ - protected function set_standard($standard) { - $installedStandards = \PHP_CodeSniffer\Util\Standards::getInstalledStandardDetails(); - - foreach (array_keys($installedStandards) as $standard) { - if (\PHP_CodeSniffer\Util\Standards::isInstalledStandard($standard) === false) { - // They didn't select a valid coding standard, so help them - // out by letting them know which standards are installed. - $error = 'ERROR: the "'.$standard.'" coding standard is not installed. '; - ob_start(); - \PHP_CodeSniffer\Util\Standards::printInstalledStandards(); - $error .= ob_get_contents(); - ob_end_clean(); - throw new \PHP_CodeSniffer\Exceptions\DeepExitException($error, 3); - } + protected function set_standard(string$standard) { + if (\PHP_CodeSniffer\Util\Standards::isInstalledStandard($standard) === false) { + // They didn't select a valid coding standard, so help them + // out by letting them know which standards are installed. + $error = "ERROR: the '{$standard}' coding standard is not installed.\n"; + ob_start(); + \PHP_CodeSniffer\Util\Standards::printInstalledStandards(); + $error .= ob_get_contents(); + ob_end_clean(); + throw new \PHP_CodeSniffer\Exceptions\DeepExitException($error, 3); } $this->standard = $standard; - return; - // Since 2.9 arbitrary standard directories are not allowed by default, - // only those under the CodeSniffer/Standards dir are detected. Other base - // dirs containing standards can be added using CodeSniffer.conf or the - // PHP_CODESNIFFER_CONFIG_DATA global (installed_paths setting). - // We are using the global way here to avoid changes in the phpcs import. - // phpcs:disable - if (!isset($GLOBALS['PHP_CODESNIFFER_CONFIG_DATA']['installed_paths'])) { - $localcodecheckerpath = realpath(__DIR__ . '/../'); - $GLOBALS['PHP_CODESNIFFER_CONFIG_DATA'] = ['installed_paths' => $localcodecheckerpath]; - } - // phpcs:enable - - // Basic search of standards in the allowed directories. - $stdsearch = array( - __DIR__ . '/../phpcs/src/Standards', // PHPCS standards dir. - __DIR__ . '/..', // Plugin local_codechecker dir, allowed above via global. - ); - - foreach ($stdsearch as $stdpath) { - $stdpath = realpath($stdpath . '/' . $standard); - $stdfile = $stdpath . '/ruleset.xml'; - if (file_exists($stdfile)) { - $this->standard = $stdpath; // Need to pass the path here. - break; - } - } - // Standard not found, fail. - if ($this->standard === null) { - $this->fail('Standard "' . $standard . '" not found.'); - } } /** diff --git a/moodle/Tests/Sniffs/Namespaces/NamespaceStatementSniffTest.php b/moodle/Tests/Sniffs/Namespaces/NamespaceStatementSniffTest.php new file mode 100644 index 0000000..d2a0d98 --- /dev/null +++ b/moodle/Tests/Sniffs/Namespaces/NamespaceStatementSniffTest.php @@ -0,0 +1,76 @@ +. + +namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\Namespaces; + +use MoodleHQ\MoodleCS\moodle\Tests\MoodleCSBaseTestCase; + +// phpcs:disable moodle.NamingConventions + +/** + * Test the NoLeadingSlash sniff. + * + * @package moodle-cs + * @category test + * @copyright 2023 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * + * @covers \MoodleHQ\MoodleCS\moodle\Sniffs\Namespaces\NoLeadingSlashSniff + */ +class NamespaceStatementSniffTest extends MoodleCSBaseTestCase +{ + public static function leading_slash_provider(): array + { + return [ + [ + 'fixture' => 'correct_namespace', + 'warnings' => [], + 'errors' => [], + ], + [ + 'fixture' => 'leading_backslash', + 'warnings' => [], + 'errors' => [ + 3 => 'Namespace should not start with a slash: \MoodleHQ\MoodleCS\moodle\Tests\Sniffs\Namespaces', + ], + ], + [ + 'fixture' => 'curly_namespace', + 'warnings' => [], + 'errors' => [ + 3 => 'Namespace should not start with a slash: \MoodleHQ\MoodleCS\moodle\Tests\Sniffs\Namespaces', + ], + ], + ]; + } + /** + * @dataProvider leading_slash_provider + */ + public function test_leading_slash( + string $fixture, + array $warnings, + array $errors + ): void + { + $this->set_standard('moodle'); + $this->set_sniff('moodle.Namespaces.NamespaceStatement'); + $this->set_fixture(sprintf("%s/fixtures/%s.php", __DIR__, $fixture)); + $this->set_warnings($warnings); + $this->set_errors($errors); + + $this->verify_cs_results(); + } +} diff --git a/moodle/Tests/Sniffs/Namespaces/fixtures/correct_namespace.php b/moodle/Tests/Sniffs/Namespaces/fixtures/correct_namespace.php new file mode 100644 index 0000000..26c4931 --- /dev/null +++ b/moodle/Tests/Sniffs/Namespaces/fixtures/correct_namespace.php @@ -0,0 +1,8 @@ +0 + + + +