diff --git a/Magento2/Sniffs/Legacy/EscapeMethodsOnBlockClassSniff.php b/Magento2/Sniffs/Legacy/EscapeMethodsOnBlockClassSniff.php
new file mode 100644
index 00000000..4d6d8288
--- /dev/null
+++ b/Magento2/Sniffs/Legacy/EscapeMethodsOnBlockClassSniff.php
@@ -0,0 +1,106 @@
+ true,
+ 'escapeHtml' => true,
+ 'escapeHtmlAttr' => true,
+ 'escapeJs' => true,
+ 'escapeJsQuote' => true,
+ 'escapeQuote' => true,
+ 'escapeUrl' => true,
+ 'escapeXssInUrl' => true,
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ public function register()
+ {
+ return [
+ T_OBJECT_OPERATOR,
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($stackPtr <= 1 || !isset($tokens[$stackPtr + 2])) {
+ return;
+ }
+
+ $objectPtr = $stackPtr - 1;
+ if ($tokens[$objectPtr]['code'] !== T_VARIABLE) {
+ $objectPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $objectPtr, null, true);
+
+ if (!$objectPtr) {
+ return;
+ }
+ }
+
+ if ($tokens[$objectPtr]['code'] !== T_VARIABLE
+ || $tokens[$objectPtr]['content'] !== '$block'
+ ) {
+ return;
+ }
+
+ $methodPtr = $stackPtr + 1;
+ if ($tokens[$methodPtr]['code'] !== T_STRING) {
+ $methodPtr = $phpcsFile->findNext(Tokens::$emptyTokens, $methodPtr, null, true);
+
+ if (!$methodPtr) {
+ return;
+ }
+ }
+
+ if ($tokens[$methodPtr]['code'] !== T_STRING
+ || !isset(self::ESCAPER_METHODS[$tokens[$methodPtr]['content']])
+ ) {
+ return;
+ }
+
+ $openParenPtr = $methodPtr + 1;
+ if ($tokens[$openParenPtr]['code'] !== T_OPEN_PARENTHESIS) {
+ $openParenPtr = $phpcsFile->findNext(Tokens::$emptyTokens, $openParenPtr, null, true);
+
+ if (!$openParenPtr) {
+ return;
+ }
+ }
+
+ if ($tokens[$openParenPtr]['code'] !== T_OPEN_PARENTHESIS) {
+ return;
+ }
+
+ $fix = $phpcsFile->addFixableWarning(
+ 'Using %s on $block is deprecated. Please use equivalent method on $escaper',
+ $methodPtr,
+ 'Found',
+ [
+ $tokens[$methodPtr]['content'], // method name
+ ]
+ );
+
+ if ($fix) {
+ $phpcsFile->fixer->replaceToken($objectPtr, '$escaper');
+ }
+ }
+}
diff --git a/Magento2/Tests/Legacy/EscapeMethodsOnBlockClassUnitTest.inc b/Magento2/Tests/Legacy/EscapeMethodsOnBlockClassUnitTest.inc
new file mode 100644
index 00000000..260f0c93
--- /dev/null
+++ b/Magento2/Tests/Legacy/EscapeMethodsOnBlockClassUnitTest.inc
@@ -0,0 +1,87 @@
+
+
+
+ This unescaped output is fine here; other sniffs will complain about it though.
+
+ getSomeString(); ?>
+ = $block->getSomeString(); ?>
+ = /** @noEscape */ $block->getSomeString(); ?>
+ = /** @escapeNotVerified */ $block->getSomeString(); ?>
+
+
+
+ These should be using equivalent methods on the `$escaper` class, not the `$block` class.
+
+ Note that I couldn't find any use of this method in any templates within Magento.
+ = $block->escapeCss($block->getSomeString()); ?>
+
+ = $block->escapeHtml(__($block->getSomeString())) ?>
+ = $block->escapeHtml(__($block->getSomeString())); ?>
+ = $block->escapeHtml(__($block->getSomeString()), ['strong', 'em', 'span']) ?>
+
+
+
+
+
+
+
+ The only example of this method being used was in a block class, rather than a template.
+ getItems() as $item) {
+ $item['sku'] = $block->escapeJsQuote($item['sku']);
+ }
+ ?>
+
+ The only example of this method being used was in a block class, rather than a template.
+ = $block->escapeQuote(__($block->getData('welcome'))); ?>
+
+ link text
+
+ Note that I couldn't find any use of this method in any templates within Magento.
+ = $block->escapeXssInUrl($block->getSomeString()); ?>
+
+
+
+ These are edge cases for formatting differences
+
+ escapeHtml('');
+ $block ->escapeHtml('');
+ $block-> escapeHtml('');
+ $block
+ ->escapeHtml('');
+ $block
+
+ ->escapeHtml('');
+ $block->
+ escapeHtml('');
+ $block-> // comment
+ escapeHtml('');
+ $block /* comment */
+ ->escapeHtml('');
+
+ $block /* comment */ -> /* comment */ escapeHtml('');
+ ?>
+
+
+
+ These close-matches shouldn't be flagged by this sniff.
+
+ = $block->escapeHTML(__($block->getSomeString())) ?>
+ = $block->escapeHtmlString(__($block->getSomeString())) ?>
+ = $block->escapeHtmlAttribute($block->getSomeString()) ?>
+ = $block->escapeCSS($block->getSomeString()); ?>
+ = $block->escapeJS($block->getData('html_id')) ?>
+ = $block->escapeJavaScript($block->getData('html_id')) ?>
+ = $block->escapeQuotes(__($block->getData('welcome'))); ?>
+ = $block->escapeURL($block->getUrl('adminhtml/notification/index')) ?>
+
diff --git a/Magento2/Tests/Legacy/EscapeMethodsOnBlockClassUnitTest.inc.fixed b/Magento2/Tests/Legacy/EscapeMethodsOnBlockClassUnitTest.inc.fixed
new file mode 100644
index 00000000..80c4f22c
--- /dev/null
+++ b/Magento2/Tests/Legacy/EscapeMethodsOnBlockClassUnitTest.inc.fixed
@@ -0,0 +1,87 @@
+
+
+
+ This unescaped output is fine here; other sniffs will complain about it though.
+
+ getSomeString(); ?>
+ = $block->getSomeString(); ?>
+ = /** @noEscape */ $block->getSomeString(); ?>
+ = /** @escapeNotVerified */ $block->getSomeString(); ?>
+
+
+
+ These should be using equivalent methods on the `$escaper` class, not the `$block` class.
+
+ Note that I couldn't find any use of this method in any templates within Magento.
+ = $escaper->escapeCss($block->getSomeString()); ?>
+
+ = $escaper->escapeHtml(__($block->getSomeString())) ?>
+ = $escaper->escapeHtml(__($block->getSomeString())); ?>
+ = $escaper->escapeHtml(__($block->getSomeString()), ['strong', 'em', 'span']) ?>
+
+
+
+
+
+
+
+ The only example of this method being used was in a block class, rather than a template.
+ getItems() as $item) {
+ $item['sku'] = $escaper->escapeJsQuote($item['sku']);
+ }
+ ?>
+
+ The only example of this method being used was in a block class, rather than a template.
+ = $escaper->escapeQuote(__($block->getData('welcome'))); ?>
+
+ link text
+
+ Note that I couldn't find any use of this method in any templates within Magento.
+ = $escaper->escapeXssInUrl($block->getSomeString()); ?>
+
+
+
+ These are edge cases for formatting differences
+
+ escapeHtml('');
+ $escaper ->escapeHtml('');
+ $escaper-> escapeHtml('');
+ $escaper
+ ->escapeHtml('');
+ $escaper
+
+ ->escapeHtml('');
+ $escaper->
+ escapeHtml('');
+ $escaper-> // comment
+ escapeHtml('');
+ $escaper /* comment */
+ ->escapeHtml('');
+
+ $escaper /* comment */ -> /* comment */ escapeHtml('');
+ ?>
+
+
+
+ These close-matches shouldn't be flagged by this sniff.
+
+ = $block->escapeHTML(__($block->getSomeString())) ?>
+ = $block->escapeHtmlString(__($block->getSomeString())) ?>
+ = $block->escapeHtmlAttribute($block->getSomeString()) ?>
+ = $block->escapeCSS($block->getSomeString()); ?>
+ = $block->escapeJS($block->getData('html_id')) ?>
+ = $block->escapeJavaScript($block->getData('html_id')) ?>
+ = $block->escapeQuotes(__($block->getData('welcome'))); ?>
+ = $block->escapeURL($block->getUrl('adminhtml/notification/index')) ?>
+
diff --git a/Magento2/Tests/Legacy/EscapeMethodsOnBlockClassUnitTest.php b/Magento2/Tests/Legacy/EscapeMethodsOnBlockClassUnitTest.php
new file mode 100644
index 00000000..feead3d1
--- /dev/null
+++ b/Magento2/Tests/Legacy/EscapeMethodsOnBlockClassUnitTest.php
@@ -0,0 +1,45 @@
+ 1,
+ 21 => 1,
+ 22 => 1,
+ 23 => 1,
+ 25 => 1,
+ 26 => 1,
+ 27 => 1,
+ 31 => 1,
+ 40 => 1,
+ 45 => 1,
+ 47 => 1,
+ 50 => 1,
+ 57 => 1,
+ 58 => 1,
+ 59 => 1,
+ 61 => 1,
+ 64 => 1,
+ 66 => 1,
+ 68 => 1,
+ 70 => 1,
+ 72 => 1,
+ ];
+ }
+}