diff --git a/src/Ruleset.php b/src/Ruleset.php
index 9a9f959163..b6279bf9f3 100644
--- a/src/Ruleset.php
+++ b/src/Ruleset.php
@@ -1409,6 +1409,24 @@ public function registerSniffs($files, $restrictions, $exclusions)
continue;
}
+ if ($reflection->implementsInterface('PHP_CodeSniffer\Sniffs\Sniff') === false) {
+ // Skip classes which don't implement the register() or process() methods.
+ if (method_exists($className, 'register') === false
+ || method_exists($className, 'process') === false
+ ) {
+ $errorMsg = 'Sniff class %s is missing required method %s().';
+ if (method_exists($className, 'register') === false) {
+ $this->msgCache->add(sprintf($errorMsg, $className, 'register'), MessageCollector::ERROR);
+ }
+
+ if (method_exists($className, 'process') === false) {
+ $this->msgCache->add(sprintf($errorMsg, $className, 'process'), MessageCollector::ERROR);
+ }
+
+ continue;
+ }
+ }//end if
+
$listeners[$className] = $className;
if (PHP_CODESNIFFER_VERBOSITY > 2) {
diff --git a/tests/Core/Ruleset/Fixtures/TestStandard/Sniffs/InvalidSniffError/NoImplementsNoProcessSniff.php b/tests/Core/Ruleset/Fixtures/TestStandard/Sniffs/InvalidSniffError/NoImplementsNoProcessSniff.php
new file mode 100644
index 0000000000..d6aa4ee6a8
--- /dev/null
+++ b/tests/Core/Ruleset/Fixtures/TestStandard/Sniffs/InvalidSniffError/NoImplementsNoProcessSniff.php
@@ -0,0 +1,17 @@
+
+
diff --git a/tests/Core/Ruleset/RegisterSniffsRejectsInvalidSniffNoImplementsNoProcessTest.xml b/tests/Core/Ruleset/RegisterSniffsRejectsInvalidSniffNoImplementsNoProcessTest.xml
new file mode 100644
index 0000000000..6b70d32d85
--- /dev/null
+++ b/tests/Core/Ruleset/RegisterSniffsRejectsInvalidSniffNoImplementsNoProcessTest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/tests/Core/Ruleset/RegisterSniffsRejectsInvalidSniffNoImplementsNoRegisterOrProcessTest.xml b/tests/Core/Ruleset/RegisterSniffsRejectsInvalidSniffNoImplementsNoRegisterOrProcessTest.xml
new file mode 100644
index 0000000000..06e2028375
--- /dev/null
+++ b/tests/Core/Ruleset/RegisterSniffsRejectsInvalidSniffNoImplementsNoRegisterOrProcessTest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/tests/Core/Ruleset/RegisterSniffsRejectsInvalidSniffNoImplementsNoRegisterTest.xml b/tests/Core/Ruleset/RegisterSniffsRejectsInvalidSniffNoImplementsNoRegisterTest.xml
new file mode 100644
index 0000000000..9e0913f9cc
--- /dev/null
+++ b/tests/Core/Ruleset/RegisterSniffsRejectsInvalidSniffNoImplementsNoRegisterTest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/tests/Core/Ruleset/RegisterSniffsRejectsInvalidSniffTest.php b/tests/Core/Ruleset/RegisterSniffsRejectsInvalidSniffTest.php
new file mode 100644
index 0000000000..1efe5cdba0
--- /dev/null
+++ b/tests/Core/Ruleset/RegisterSniffsRejectsInvalidSniffTest.php
@@ -0,0 +1,80 @@
+
+ * @copyright 2025 PHPCSStandards and contributors
+ * @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Ruleset;
+
+use PHP_CodeSniffer\Ruleset;
+use PHP_CodeSniffer\Tests\ConfigDouble;
+use PHP_CodeSniffer\Tests\Core\Ruleset\AbstractRulesetTestCase;
+
+/**
+ * Tests that invalid sniffs will be rejected with an informative error message.
+ *
+ * @covers \PHP_CodeSniffer\Ruleset::registerSniffs
+ */
+final class RegisterSniffsRejectsInvalidSniffTest extends AbstractRulesetTestCase
+{
+
+
+ /**
+ * Verify that an error is thrown if an invalid sniff class is loaded.
+ *
+ * @param string $standard The standard to use for the test.
+ * @param string $methodName The name of the missing method.
+ *
+ * @dataProvider dataExceptionIsThrownOnMissingInterfaceMethod
+ *
+ * @return void
+ */
+ public function testExceptionIsThrownOnMissingInterfaceMethod($standard, $methodName)
+ {
+ // Set up the ruleset.
+ $standard = __DIR__.'/'.$standard;
+ $config = new ConfigDouble(["--standard=$standard"]);
+
+ $regex = "`(^|\R)ERROR: Sniff class \S+Sniff is missing required method $methodName\(\)\.\R`";
+ $this->expectRuntimeExceptionRegex($regex);
+
+ new Ruleset($config);
+
+ }//end testExceptionIsThrownOnMissingInterfaceMethod()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testExceptionIsThrownOnMissingInterfaceMethod()
+ *
+ * @return array>
+ */
+ public static function dataExceptionIsThrownOnMissingInterfaceMethod()
+ {
+ return [
+ 'Missing register() method' => [
+ 'standard' => 'RegisterSniffsRejectsInvalidSniffNoImplementsNoRegisterTest.xml',
+ 'methodName' => 'register',
+ ],
+ 'Missing process() method' => [
+ 'standard' => 'RegisterSniffsRejectsInvalidSniffNoImplementsNoProcessTest.xml',
+ 'methodName' => 'process',
+ ],
+ 'Missing both, checking register() method' => [
+ 'standard' => 'RegisterSniffsRejectsInvalidSniffNoImplementsNoRegisterOrProcessTest.xml',
+ 'methodName' => 'register',
+ ],
+ 'Missing both, checking process() method' => [
+ 'standard' => 'RegisterSniffsRejectsInvalidSniffNoImplementsNoRegisterOrProcessTest.xml',
+ 'methodName' => 'process',
+ ],
+ ];
+
+ }//end dataExceptionIsThrownOnMissingInterfaceMethod()
+
+
+}//end class
diff --git a/tests/Core/Ruleset/ShowSniffDeprecationsTest.xml b/tests/Core/Ruleset/ShowSniffDeprecationsTest.xml
index 802bd3c0a9..ab632b1959 100644
--- a/tests/Core/Ruleset/ShowSniffDeprecationsTest.xml
+++ b/tests/Core/Ruleset/ShowSniffDeprecationsTest.xml
@@ -6,6 +6,7 @@
+