Skip to content

Commit 4a84152

Browse files
author
Oleksandr Gorkun
committed
MC-17489: Require specific suffix for HTML binding
1 parent b223107 commit 4a84152

File tree

6 files changed

+167
-1
lines changed

6 files changed

+167
-1
lines changed

app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@
141141
</div>
142142
<div ifnot="options().length"
143143
class="admin__action-multiselect-empty-area">
144-
<ul data-bind="html: emptyOptionsHtml"/>
144+
<ul data-bind="
145+
html: emptyOptionsHtml
146+
"/>
145147
</div>
146148
<!-- /ko -->
147149
<ul class="admin__action-multiselect-menu-inner _root"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Sniffs\Html;
8+
9+
use PHP_CodeSniffer\Sniffs\Sniff;
10+
use PHP_CodeSniffer\Files\File;
11+
12+
class HtmlBindingSniff implements Sniff
13+
{
14+
/**
15+
* @inheritDoc
16+
*/
17+
public function register()
18+
{
19+
return [T_INLINE_HTML];
20+
}
21+
22+
/**
23+
* @inheritDoc
24+
*
25+
* Find HTML data bindings and check variables used.
26+
*/
27+
public function process(File $phpcsFile, $stackPtr)
28+
{
29+
if ($stackPtr === 0) {
30+
$html = $phpcsFile->getTokensAsString($stackPtr, count($phpcsFile->getTokens()));
31+
$dom = new \DOMDocument();
32+
try {
33+
@$dom->loadHTML($html);
34+
$loaded = true;
35+
} catch (\Throwable $exception) {
36+
//Invalid HTML, skipping
37+
$loaded = false;
38+
}
39+
if ($loaded) {
40+
$domXpath = new \DOMXPath($dom);
41+
$dataBindAttributes = $domXpath->query('//@*[name() = "data-bind"]');
42+
foreach ($dataBindAttributes as $dataBindAttribute) {
43+
$knockoutBinding = $dataBindAttribute->nodeValue;
44+
preg_match('/^(.+\s+)*?html\:\s*?([a-z0-9\.\_\(\)]+)/i', $knockoutBinding, $htmlBinding);
45+
if ($htmlBinding && !preg_match('/UnsanitizedHtml[\(\)]*?$/', $htmlBinding[2])) {
46+
$phpcsFile->addError(
47+
'Variables/functions used for HTML binding must have UnsanitizedHtml suffix, '
48+
.'consider using text binding if the value is supposed to be text',
49+
null,
50+
'UIComponentTemplate.HtmlSuffix'
51+
);
52+
}
53+
}
54+
}
55+
}
56+
}
57+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\TestFramework\CodingStandard\Tool\CodeSniffer;
8+
9+
/**
10+
* Add HTML files extension to config.
11+
*/
12+
class HtmlWrapper extends Wrapper
13+
{
14+
const FILE_EXTENSION = 'html';
15+
16+
private const TOKENIZER = 'PHP';
17+
18+
/**
19+
* @inheritDoc
20+
*/
21+
public function init()
22+
{
23+
parent::init();
24+
25+
$this->config->extensions += [self::FILE_EXTENSION => self::TOKENIZER];
26+
}
27+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Test\Html;
8+
9+
use Magento\Framework\App\Utility;
10+
use Magento\TestFramework\CodingStandard\Tool\CodeSniffer;
11+
use Magento\Framework\App\Utility\Files;
12+
use Magento\Test\Php\LiveCodeTest as PHPCodeTest;
13+
use PHPUnit\Framework\TestCase;
14+
15+
/**
16+
* Set of tests for static code style
17+
*/
18+
class LiveCodeTest extends TestCase
19+
{
20+
/**
21+
* @var string
22+
*/
23+
private static $reportDir = '';
24+
25+
/**
26+
* Setup basics for all tests
27+
*
28+
* @return void
29+
*/
30+
public static function setUpBeforeClass(): void
31+
{
32+
self::$reportDir = BP . '/dev/tests/static/report';
33+
if (!is_dir(self::$reportDir)) {
34+
mkdir(self::$reportDir, 0770);
35+
}
36+
}
37+
38+
/**
39+
* Run the magento specific coding standards on the code
40+
*
41+
* @return void
42+
*/
43+
public function testCodeStyle(): void
44+
{
45+
$reportFile = self::$reportDir . '/html_report.txt';
46+
$wrapper = new CodeSniffer\HtmlWrapper();
47+
$codeSniffer = new CodeSniffer(realpath(__DIR__ . '/_files/html'), $reportFile, $wrapper);
48+
if (!$codeSniffer->canRun()) {
49+
$this->markTestSkipped('PHP Code Sniffer is not installed.');
50+
}
51+
$codeSniffer->setExtensions([CodeSniffer\HtmlWrapper::FILE_EXTENSION]);
52+
//Looking for changed .html files
53+
$fileList = PHPCodeTest::getWhitelist([CodeSniffer\HtmlWrapper::FILE_EXTENSION], __DIR__, __DIR__);
54+
55+
$result = $codeSniffer->run($fileList);
56+
57+
$report = file_exists($reportFile) ? file_get_contents($reportFile) : "";
58+
$this->assertEquals(
59+
0,
60+
$result,
61+
"PHP Code Sniffer has found {$result} error(s): " . PHP_EOL . $report
62+
);
63+
}
64+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<ruleset name="Magento">
9+
<description>UI Component Coding Standard</description>
10+
<rule ref="Internal.NoCodeFound">
11+
<severity>0</severity>
12+
</rule>
13+
<rule ref="../../../../../../framework/Magento/Sniffs/Html"/>
14+
</ruleset>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Format: <componentType=module|library|theme|language|*> <componentName> <globPattern> or simply <globPattern>
2+
* * /

0 commit comments

Comments
 (0)