Skip to content

Commit

Permalink
Merge pull request #74 from phug-php/feature/issue-70-disallow-new-li…
Browse files Browse the repository at this point in the history
…ne-in-interpolations

Disallow new line in interpolations
  • Loading branch information
kylekatarnls committed May 16, 2020
2 parents f86900b + 3b3df3c commit 5ace1d7
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 97 deletions.
26 changes: 6 additions & 20 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
---
engines:
duplication:
enabled: true
config:
languages:
- ruby
- javascript
- python
- php
version: "2"
checks:
fixme:
enabled: true
phpmd:
enabled: true
config:
rulesets: rulesets.xml
ratings:
paths:
- "**.inc"
- "**.js"
- "**.jsx"
- "**.module"
- "**.php"
- "**.py"
- "**.rb"
similar-code:
config:
threshold: 64
exclude_patterns:
- "**/tests/**"
- "**/tests/**"
2 changes: 2 additions & 0 deletions src/Phug/Lexer/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public function __construct($options = null)
'indent_style' => null,
'indent_width' => null,
'allow_mixed_indent' => true,
'multiline_interpolation' => true,
'multiline_markup_enabled' => true,
'encoding' => null,
'lexer_modules' => [],
Expand Down Expand Up @@ -216,6 +217,7 @@ public function lex($input, $path = null)
'indent_style' => $this->getOption('indent_style'),
'indent_width' => $this->getOption('indent_width'),
'allow_mixed_indent' => $this->getOption('allow_mixed_indent'),
'multiline_interpolation' => $this->getOption('multiline_interpolation'),
'multiline_markup_enabled' => $this->getOption('multiline_markup_enabled'),
'level' => $this->getOption('level'),
'mixin_keyword' => $this->getRegExpOption('mixin_keyword'),
Expand Down
116 changes: 67 additions & 49 deletions src/Phug/Lexer/Lexer/Analyzer/LineAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,63 +68,16 @@ public function disallowInterpolation()
$this->allowedInterpolation = false;
}

protected function getLine()
{
$line = [];
$indent = $this->reader->match('[ \t]+(?=\S)') ? mb_strlen($this->reader->getMatch(0)) : INF;
if ($indent < $this->maxIndent) {
$this->maxIndent = $indent;
}

if ($this->allowedInterpolation) {
foreach ($this->state->scan(InterpolationScanner::class) as $subToken) {
$line[] = $subToken instanceof TextToken ? $subToken->getValue() : $subToken;
}
}

if (($text = $this->reader->readUntilNewLine()) !== null) {
$line[] = $text;
}

return $line;
}

protected function recordLine()
{
$this->lines[] = $this->getLine();

if ($this->newLine = $this->reader->peekNewLine()) {
$this->reader->consume(1);
}
}

public function analyze($quitOnOutdent, array $breakChars = [])
{
$this->outdent = false;
$this->level = $this->state->getLevel();
$this->newLevel = $this->level;
$breakChars = array_merge($breakChars, [' ', "\t", "\n"]);
$this->newLine = false;
$first = true;

while ($this->reader->hasLength()) {
$this->newLine = true;
$indentationScanner = new IndentationScanner();
$newLevel = $indentationScanner->getIndentLevel($this->state, $this->level);

if (!$first || $newLevel > $this->newLevel) {
$this->newLevel = $newLevel;
}

$first = false;

if (!$this->reader->peekChars($breakChars)) {
$this->outdent = $this->newLevel < $this->level;

break;
}

if ($this->newLevel < $this->level && $this->reader->match('[ \t]*\n')) {
foreach ($this->hasChunksUntil($breakChars) as $lowerLevel) {
if ($lowerLevel && $this->reader->match('[ \t]*\n')) {
$this->reader->consume(mb_strlen($this->reader->getMatch(0)));
$this->lines[] = [];

Expand Down Expand Up @@ -202,4 +155,69 @@ public function getNewLevel()
{
return $this->newLevel;
}

protected function getLineChunks()
{
$line = [];

if ($this->allowedInterpolation) {
foreach ($this->state->scan(InterpolationScanner::class) as $subToken) {
$line[] = $subToken instanceof TextToken ? $subToken->getValue() : $subToken;
}
}

if (($text = $this->reader->readUntilNewLine()) !== null) {
$line[] = $text;
}

return $line;
}

protected function getLine()
{
$indent = $this->reader->match('[ \t]+(?=\S)') ? mb_strlen($this->reader->getMatch(0)) : INF;

if ($indent < $this->maxIndent) {
$this->maxIndent = $indent;
}

return $this->getLineChunks();
}

protected function recordLine()
{
$this->lines[] = $this->getLine();

if ($this->newLine = $this->reader->peekNewLine()) {
$this->reader->consume(1);
}
}

protected function setNewLevel($newLevel, $first = false)
{
if (!$first || $newLevel > $this->newLevel) {
$this->newLevel = $newLevel;
}
}

protected function hasChunksUntil($breakChars)
{
$first = true;

while ($this->reader->hasLength()) {
$this->newLine = true;
$indentationScanner = new IndentationScanner();
$newLevel = $indentationScanner->getIndentLevel($this->state, $this->level);
$this->setNewLevel($newLevel, $first);
$first = false;

if (!$this->reader->peekChars($breakChars)) {
$this->outdent = $this->newLevel < $this->level;

break;
}

yield $this->newLevel < $this->level;
}
}
}
56 changes: 41 additions & 15 deletions src/Phug/Lexer/Lexer/Scanner/InterpolationScanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,45 @@
use Phug\Lexer\Token\ExpressionToken;
use Phug\Lexer\Token\InterpolationEndToken;
use Phug\Lexer\Token\InterpolationStartToken;
use Phug\Lexer\Token\NewLineToken;
use Phug\Lexer\Token\TagInterpolationEndToken;
use Phug\Lexer\Token\TagInterpolationStartToken;
use Phug\Lexer\Token\TextToken;

class InterpolationScanner implements ScannerInterface
{
protected function scanInterpolation(State $state, $tagInterpolation, $interpolation, $escape)
protected function throwEndOfLineExceptionIf(State $state, $condition)
{
if ($tagInterpolation) {
/** @var TagInterpolationStartToken $start */
$start = $state->createToken(TagInterpolationStartToken::class);
/** @var TagInterpolationEndToken $end */
$end = $state->createToken(TagInterpolationEndToken::class);
if ($condition) {
$state->throwException('End of line was reached with no closing bracket for interpolation.');
}
}

$start->setEnd($end);
$end->setStart($start);
protected function scanTagInterpolation(State $state, $tagInterpolation)
{
/** @var TagInterpolationStartToken $start */
$start = $state->createToken(TagInterpolationStartToken::class);
/** @var TagInterpolationEndToken $end */
$end = $state->createToken(TagInterpolationEndToken::class);

$lexer = $state->getLexer();
$start->setEnd($end);
$end->setStart($start);

yield $start;
foreach ($lexer->lex($tagInterpolation) as $token) {
yield $token;
}
yield $end;
$lexer = $state->getLexer();

yield $start;

foreach ($lexer->lex($tagInterpolation) as $token) {
$this->throwEndOfLineExceptionIf($state, $token instanceof NewLineToken);

return;
yield $token;
}

yield $end;
}

protected function scanExpressionInterpolation(State $state, $interpolation, $escape)
{
/** @var InterpolationStartToken $start */
$start = $state->createToken(InterpolationStartToken::class);
/** @var InterpolationEndToken $end */
Expand All @@ -50,6 +61,7 @@ protected function scanInterpolation(State $state, $tagInterpolation, $interpola
/** @var ExpressionToken $token */
$token = $state->createToken(ExpressionToken::class);
$token->setValue($interpolation);

if ($escape === '#') {
$token->escape();
}
Expand All @@ -59,6 +71,20 @@ protected function scanInterpolation(State $state, $tagInterpolation, $interpola
yield $end;
}

protected function scanInterpolation(State $state, $tagInterpolation, $interpolation, $escape)
{
$this->throwEndOfLineExceptionIf(
$state,
!$state->getOption('multiline_interpolation') && strpos($interpolation, "\n") !== false
);

if ($tagInterpolation) {
return $this->scanTagInterpolation($state, $tagInterpolation);
}

return $this->scanExpressionInterpolation($state, $interpolation, $escape);
}

protected function needSeparationBlankLine(State $state)
{
$reader = $state->getReader();
Expand Down
16 changes: 10 additions & 6 deletions tests/Phug/Lexer/Scanner/CommentScannerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ class CommentScannerTest extends AbstractLexerTest
* @covers \Phug\Lexer\Scanner\CommentScanner
* @covers \Phug\Lexer\Scanner\CommentScanner::scan
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::<public>
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::hasChunksUntil
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::setNewLevel
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::recordLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLineChunks
*/
public function testVisibleSingleLineComment()
{
Expand All @@ -42,9 +44,11 @@ public function testVisibleSingleLineComment()
* @covers \Phug\Lexer\Scanner\CommentScanner
* @covers \Phug\Lexer\Scanner\CommentScanner::scan
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::<public>
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::hasChunksUntil
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::setNewLevel
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::recordLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLineChunks
*/
public function testInvisibleSingleLineComment()
{
Expand All @@ -67,7 +71,7 @@ public function testInvisibleSingleLineComment()
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::<public>
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::recordLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLineChunks
*/
public function testVisibleMultiLineComment()
{
Expand All @@ -93,7 +97,7 @@ public function testVisibleMultiLineComment()
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::<public>
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::recordLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLineChunks
*/
public function testInvisibleMultiLineComment()
{
Expand All @@ -119,7 +123,7 @@ public function testInvisibleMultiLineComment()
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::<public>
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::recordLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLineChunks
*/
public function testCommentInIndent()
{
Expand Down Expand Up @@ -148,7 +152,7 @@ public function testCommentInIndent()
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::<public>
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::recordLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLineChunks
*/
public function testCommentQuit()
{
Expand Down
6 changes: 4 additions & 2 deletions tests/Phug/Lexer/Scanner/FilterScannerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ class FilterScannerTest extends AbstractLexerTest
* @covers \Phug\Lexer\Scanner\FilterScanner
* @covers \Phug\Lexer\Scanner\FilterScanner::scan
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::<public>
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::hasChunksUntil
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::setNewLevel
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::recordLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLineChunks
*/
public function testFilter()
{
Expand Down Expand Up @@ -106,7 +108,7 @@ public function testFilter()
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::<public>
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::recordLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLine
* @covers \Phug\Lexer\Analyzer\LineAnalyzer::getLineChunks
*/
public function testStylusFilter()
{
Expand Down
27 changes: 27 additions & 0 deletions tests/Phug/Lexer/Scanner/InterpolationScannerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,31 @@ public function testTokenInLineAnalyzer()
]);
$analyzer->getFlatLines();
}

/**
* @covers \Phug\Lexer\Scanner\InterpolationScanner::scanTagInterpolation
* @covers \Phug\Lexer\Scanner\InterpolationScanner::throwEndOfLineExceptionIf
* @covers \Phug\Lexer\Scanner\InterpolationScanner::scanInterpolation
*/
public function testNewLineInTagInterpolation()
{
$this->expectMessageToBeThrown('End of line was reached with no closing bracket for interpolation.');

$input = "p #[em\n]";
$tokens = iterator_to_array($this->lexer->lex($input));
}

/**
* @covers \Phug\Lexer\Scanner\InterpolationScanner::scanExpressionInterpolation
* @covers \Phug\Lexer\Scanner\InterpolationScanner::throwEndOfLineExceptionIf
* @covers \Phug\Lexer\Scanner\InterpolationScanner::scanInterpolation
*/
public function testNewLineInInterpolation()
{
$this->expectMessageToBeThrown('End of line was reached with no closing bracket for interpolation.');

$this->lexer->setOption('multiline_interpolation', false);
$input = "p #{em\n}";
iterator_to_array($this->lexer->lex($input));
}
}
Loading

0 comments on commit 5ace1d7

Please sign in to comment.