diff --git a/Magento2/Sniffs/Legacy/ClassReferencesInConfigurationFilesSniff.php b/Magento2/Sniffs/Legacy/ClassReferencesInConfigurationFilesSniff.php
new file mode 100644
index 00000000..f2b5b400
--- /dev/null
+++ b/Magento2/Sniffs/Legacy/ClassReferencesInConfigurationFilesSniff.php
@@ -0,0 +1,221 @@
+ 0) {
+ return;
+ }
+
+ // We need to format the incoming XML to avoid tags split into several lines. In that case, PHP's DOMElement
+ // returns the position of the closing /> as the position of the tag, and we need the position of <
+ // instead, as it is the one we compare with $stackPtr later on.
+ $xml = simplexml_load_string($this->getFormattedXML($phpcsFile));
+ if ($xml === false) {
+ $phpcsFile->addError(
+ sprintf(
+ "Couldn't parse contents of '%s', check that they are in valid XML format",
+ $phpcsFile->getFilename(),
+ ),
+ $stackPtr,
+ self::ERROR_CODE_CONFIG
+ );
+ }
+
+ $classes = $this->collectClassesInConfig($xml);
+ $this->assertNonFactoryName($phpcsFile, $classes);
+
+ $modules = $this->getValuesFromXmlTagAttribute($xml, '//@module', 'module');
+ $this->assertNonFactoryNameModule($phpcsFile, $modules);
+ }
+
+ /**
+ * Check whether specified class names are right according PSR-1 Standard.
+ *
+ * @param File $phpcsFile
+ * @param array $elements
+ */
+ private function assertNonFactoryName(File $phpcsFile, array $elements)
+ {
+ foreach ($elements as $element) {
+ if (stripos($element['value'], 'Magento') === false) {
+ continue;
+ }
+ if (preg_match('/^([A-Z][a-z\d\\\\]+)+$/', $element['value']) !== 1) {
+ $phpcsFile->addError(
+ self::ERROR_MESSAGE_CONFIG,
+ $element['lineNumber'],
+ self::ERROR_CODE_CONFIG,
+ );
+ }
+ }
+ }
+
+ /**
+ * Check whether specified class names in modules are right according PSR-1 Standard.
+ *
+ * @param File $phpcsFile
+ * @param array $classes
+ */
+ private function assertNonFactoryNameModule(File $phpcsFile, array $classes)
+ {
+ foreach ($classes as $element) {
+ if (preg_match('/^([A-Z][A-Za-z\d_]+)+$/', $element['value']) !== 1) {
+ $phpcsFile->addError(
+ self::ERROR_MESSAGE_MODULE,
+ $element['lineNumber'],
+ self::ERROR_CODE_MODULE,
+ );
+ }
+ }
+ }
+
+ /**
+ * Format the incoming XML to avoid tags split into several lines.
+ *
+ * @param File $phpcsFile
+ * @return false|string
+ */
+ private function getFormattedXML(File $phpcsFile)
+ {
+ $doc = new DomDocument('1.0');
+ $doc->formatOutput = true;
+ $doc->loadXML($phpcsFile->getTokensAsString(0, count($phpcsFile->getTokens())));
+ return $doc->saveXML();
+ }
+
+ /**
+ * Parse an XML for references to PHP class names in selected tags or attributes
+ *
+ * @param SimpleXMLElement $xml
+ * @return array
+ */
+ private function collectClassesInConfig(SimpleXMLElement $xml): array
+ {
+ $classes = $this->getValuesFromXmlTagContent(
+ $xml,
+ '
+ /config//resource_adapter | /config/*[not(name()="sections")]//class[not(ancestor::observers)]
+ | //model[not(parent::connection)] | //backend_model | //source_model | //price_model
+ | //model_token | //writer_model | //clone_model | //frontend_model | //working_model
+ | //admin_renderer | //renderer',
+ );
+
+ $classes = array_merge(
+ $classes,
+ $this->getValuesFromXmlTagAttribute(
+ $xml,
+ '//@backend_model',
+ 'backend_model'
+ ),
+ $this->getValuesFromXmlTagAttribute(
+ $xml,
+ '/config//preference',
+ 'type'
+ ),
+ $this->getValuesFromXmlTagName(
+ $xml,
+ '/logging/*/expected_models/* | /logging/*/actions/*/expected_models/*',
+ )
+ );
+
+ $classes = array_map(
+ function (array $extendedNode) {
+ $extendedNode['value'] = explode('::', trim($extendedNode['value']))[0];
+ return $extendedNode;
+ },
+ $classes
+ );
+
+ return $classes;
+ }
+
+ /**
+ * Extract value from tag contents which exist in the XML path
+ *
+ * @param SimpleXMLElement $xml
+ * @param string $xPath
+ * @return array
+ */
+ private function getValuesFromXmlTagContent(SimpleXMLElement $xml, string $xPath): array
+ {
+ $nodes = $xml->xpath($xPath) ?: [];
+ return array_map(function ($item) {
+ return [
+ 'value' => (string)$item,
+ 'lineNumber' => dom_import_simplexml($item)->getLineNo()-1,
+ ];
+ }, $nodes);
+ }
+
+ /**
+ * Extract value from tag names which exist in the XML path
+ *
+ * @param SimpleXMLElement $xml
+ * @param string $xPath
+ * @return array
+ */
+ private function getValuesFromXmlTagName(SimpleXMLElement $xml, string $xPath): array
+ {
+ $nodes = $xml->xpath($xPath) ?: [];
+ return array_map(function ($item) {
+ return [
+ 'value' => $item->getName(),
+ 'lineNumber' => dom_import_simplexml($item)->getLineNo()-1,
+ ];
+ }, $nodes);
+ }
+
+ /**
+ * Extract value from tag attributes which exist in the XML path
+ *
+ * @param SimpleXMLElement $xml
+ * @param string $xPath
+ * @param string $attr
+ * @return array
+ */
+ private function getValuesFromXmlTagAttribute(SimpleXMLElement $xml, string $xPath, string $attr): array
+ {
+ $nodes = $xml->xpath($xPath) ?: [];
+ return array_map(function ($item) use ($attr) {
+ $nodeArray = (array)$item;
+ if (isset($nodeArray['@attributes'][$attr])) {
+ return [
+ 'value' => $nodeArray['@attributes'][$attr],
+ 'lineNumber' => dom_import_simplexml($item)->getLineNo()-1,
+ ];
+ }
+ }, $nodes);
+ }
+}
diff --git a/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.1.xml b/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.1.xml
new file mode 100644
index 00000000..864a154e
--- /dev/null
+++ b/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.1.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+ Magento\Config\Model\Config\Source\Yesno
+
+
+
+
+
+
+ Magento\CONFIG\Model\Config\Source\Yesno
+
+
+
+
+
+
+ Config\Model\Config\Source\Yesno
+
+
+
+
+
+
+ Sales\MODEL\Config\Source\Order\Status\NewStatus
+
+
+
+ MAGENTO\Payment\Model\Config\Source\Allspecificcountries
+
+
+
+
+
diff --git a/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.2.xml b/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.2.xml
new file mode 100644
index 00000000..2753291f
--- /dev/null
+++ b/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.2.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+ Magento\Config\Model\Config\Source\Yesno
+
+
+
+
+
+
+
+ Magento\CONFIG\Model\Config\Source\Yesno
+
+
+
+
+
+
+
+ Config\Model\Config\Source\Yesno
+
+
+
+
+
+
+ Sales\MODEL\Config\Source\Order\Status\NewStatus
+
+
+
+
+
+
+ MAGENTO\Payment\Model\Config\Source\Allspecificcountries
+
+
+
+
+
+
+
diff --git a/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.3.xml b/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.3.xml
new file mode 100644
index 00000000..4884a838
--- /dev/null
+++ b/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.3.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.4.xml b/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.4.xml
new file mode 100644
index 00000000..a99f0589
--- /dev/null
+++ b/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.4.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+ system/currency/installed
+
+
+
+
+ Magento\Framework\Communication\Config\Reader\XmlReader\Converter
+ Magento\Framework\Communication\Config\Reader\XmlReader\SchemaLocator
+ communication.xml
+
+ - name
+ - name
+
+
+
+
+
+ Magento
+
+
+
diff --git a/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.php b/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.php
new file mode 100644
index 00000000..83bb6f1d
--- /dev/null
+++ b/Magento2/Tests/Legacy/ClassReferencesInConfigurationFilesUnitTest.php
@@ -0,0 +1,49 @@
+ 1,
+ 40 => 1,
+ ];
+ }
+ if ($testFile === 'ClassReferencesInConfigurationFilesUnitTest.2.xml') {
+ return [
+ 22 => 1,
+ 42 => 1,
+ ];
+ }
+ if ($testFile === 'ClassReferencesInConfigurationFilesUnitTest.3.xml') {
+ return [
+ 10 => 1,
+ ];
+ }
+ if ($testFile === 'ClassReferencesInConfigurationFilesUnitTest.4.xml') {
+ return [
+ 10 => 1,
+ ];
+ }
+ return [];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getWarningList($testFile = '')
+ {
+ return [];
+ }
+}
diff --git a/Magento2/ruleset.xml b/Magento2/ruleset.xml
index 1b1c7f1b..751f2cda 100644
--- a/Magento2/ruleset.xml
+++ b/Magento2/ruleset.xml
@@ -148,6 +148,14 @@
*\.xml$
*\.js$
+
+ *\/etc/*.xml$
+ *\/etc/wsdl.xml$
+ *\/etc/wsdl2.xml$
+ *\/etc/wsi.xml$
+ 10
+ error
+
9