Skip to content

Commit

Permalink
Check namespaces and class imports for leading backslash
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewnicols committed Sep 17, 2023
1 parent 9292e56 commit 41d22bd
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 43 deletions.
71 changes: 71 additions & 0 deletions moodle/Sniffs/Namespaces/NoLeadingSlashSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Checks that each file contains the standard GPL comment.
*
* @package moodle-cs
* @copyright 2023 Andrew Lyons <[email protected]>
* @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 NoLeadingSlashSniff 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,
'NamespaceStartsWithSlash',
[$fqdn]
);

if ($fix) {
$file->fixer->beginChangeset();
$file->fixer->replaceToken($checkPtr, '');
$file->fixer->endChangeset();
}
}
}
}
53 changes: 10 additions & 43 deletions moodle/Tests/MoodleCSBaseTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,53 +80,20 @@ public function set_component_mapping(array $mapping): void {
*
* @param string $standard name of the standard to be tested.
*/
protected function set_standard($standard) {
protected function set_standard(string$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);
}
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.');
}
}

/**
Expand Down
76 changes: 76 additions & 0 deletions moodle/Tests/Sniffs/Namespaces/NoLeadingSlashSniffTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

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 <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*
* @covers \MoodleHQ\MoodleCS\moodle\Sniffs\Namespaces\NoLeadingSlashSniff
*/
class NoLeadingSlashSniffTest extends MoodleCSBaseTestCase
{
public static function no_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 no_leading_slash_provider
*/
public function test_no_leading_slash(
string $fixture,
array $warnings,
array $errors
): void
{
$this->set_standard('moodle');
$this->set_sniff('moodle.Namespaces.NoLeadingSlash');
$this->set_fixture(sprintf("%s/fixtures/%s.php", __DIR__, $fixture));
$this->set_warnings($warnings);
$this->set_errors($errors);

$this->verify_cs_results();
}
}
8 changes: 8 additions & 0 deletions moodle/Tests/Sniffs/Namespaces/fixtures/correct_namespace.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\Namespaces;

defined('MOODLE_INTERNAL') || die(); // Make this always the 1st line in all CS fixtures.

class correct_test extends base_test {
}
5 changes: 5 additions & 0 deletions moodle/Tests/Sniffs/Namespaces/fixtures/curly_namespace.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace \MoodleHQ\MoodleCS\moodle\Tests\Sniffs\Namespaces {
class leading_backslash {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\Namespaces {
class leading_backslash {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\Namespaces {
class leading_backslash {}
}
8 changes: 8 additions & 0 deletions moodle/Tests/Sniffs/Namespaces/fixtures/leading_backslash.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace \MoodleHQ\MoodleCS\moodle\Tests\Sniffs\Namespaces;

defined('MOODLE_INTERNAL') || die(); // Make this always the 1st line in all CS fixtures.

class leading_backslash extends base_test {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\Namespaces;

defined('MOODLE_INTERNAL') || die(); // Make this always the 1st line in all CS fixtures.

class leading_backslash extends base_test {
}
7 changes: 7 additions & 0 deletions moodle/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@
<severity>0</severity>
</rule>

<!--
Namespace statements, and class imports (use statements) should not use a leading backslash.
Class imports are already handled by PSR12.Files.ImportStatement.LeadingSlash
-->
<rule ref="moodle.Namespaces.NoLeadingSlash"/>

<!-- Let's add the complete PHPCompatibility standard -->
<rule ref="PHPCompatibility" />

Expand Down

0 comments on commit 41d22bd

Please sign in to comment.