Skip to content

Commit c0e7e0c

Browse files
authored
Merge pull request #102 from phug-php/feature/abstract-token-handler
Create AbstractTokenHandler split: 05844ddfee5f78a27d3b4079eaddded2d121513d
1 parent 8b5fa41 commit c0e7e0c

38 files changed

+327
-488
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
namespace Phug\Parser\TokenHandler;
4+
5+
use Phug\Lexer\TokenInterface;
6+
use Phug\Parser\Node\ElementNode;
7+
use Phug\Parser\Node\MixinCallNode;
8+
use Phug\Parser\State;
9+
use Phug\Parser\TokenHandlerInterface;
10+
11+
abstract class AbstractTokenHandler implements TokenHandlerInterface
12+
{
13+
const TOKEN_TYPE = TokenInterface::class;
14+
15+
public function handleToken(TokenInterface $token, State $state)
16+
{
17+
if (!is_a($token, static::TOKEN_TYPE)) {
18+
$name = $this->getTypeName(static::TOKEN_TYPE);
19+
$handler = $this->getClassLastPart(static::class);
20+
21+
throw new \RuntimeException(
22+
"You can only pass $name tokens to $handler"
23+
);
24+
}
25+
26+
$method = 'handle'.$this->getClassLastPart(get_class($token));
27+
$this->$method($token, $state);
28+
}
29+
30+
protected function onlyOnElement(TokenInterface $token, State $state)
31+
{
32+
$this->createElementNodeIfMissing($token, $state);
33+
$this->assertCurrentNodeIs($token, $state, [ElementNode::class, MixinCallNode::class]);
34+
}
35+
36+
protected function createElementNodeIfMissing(TokenInterface $token, State $state)
37+
{
38+
if (!$state->getCurrentNode()) {
39+
$state->setCurrentNode($state->createNode(ElementNode::class, $token));
40+
}
41+
}
42+
43+
protected function assertCurrentNodeIs(TokenInterface $token, State $state, array $nodeTypes)
44+
{
45+
if (!$state->currentNodeIs($nodeTypes)) {
46+
$nodeTypes = array_values($nodeTypes);
47+
$list = $this->getPluralTypeName(array_shift($nodeTypes));
48+
$count = count($nodeTypes);
49+
50+
foreach ($nodeTypes as $i => $nodeType) {
51+
$list .= ($i === $count - 1 ? ' and ' : ', ').$this->getPluralTypeName($nodeType);
52+
}
53+
54+
$state->throwException(
55+
ucfirst($this->getPluralTypeName(get_class($token))).' can only happen on '.$list,
56+
0,
57+
$token
58+
);
59+
}
60+
}
61+
62+
private function getClassLastPart($class)
63+
{
64+
$parts = explode('\\', $class);
65+
66+
return end($parts);
67+
}
68+
69+
private function getTypeName($class)
70+
{
71+
$name = preg_replace('/(Token|Node)$/', '', $this->getClassLastPart($class));
72+
$name = preg_replace('/([a-z])([A-Z])/', '$1-$2', $name);
73+
74+
return strtolower($name);
75+
}
76+
77+
private function getPluralTypeName($class)
78+
{
79+
$name = $this->getTypeName($class);
80+
81+
if ($name === 'class') {
82+
return 'classes';
83+
}
84+
85+
return $name.'s';
86+
}
87+
}

Parser/TokenHandler/AssignmentTokenHandler.php

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,18 @@
44

55
use Phug\Lexer\Token\AssignmentToken;
66
use Phug\Lexer\Token\AttributeStartToken;
7-
use Phug\Lexer\TokenInterface;
87
use Phug\Parser\Node\AssignmentNode;
98
use Phug\Parser\Node\ElementNode;
109
use Phug\Parser\Node\MixinCallNode;
1110
use Phug\Parser\State;
12-
use Phug\Parser\TokenHandlerInterface;
1311

14-
class AssignmentTokenHandler implements TokenHandlerInterface
12+
class AssignmentTokenHandler extends AbstractTokenHandler
1513
{
16-
public function handleToken(TokenInterface $token, State $state)
17-
{
18-
if (!($token instanceof AssignmentToken)) {
19-
throw new \RuntimeException(
20-
'You can only pass Assignment tokens to this token handler'
21-
);
22-
}
23-
24-
if (!$state->getCurrentNode()) {
25-
$state->setCurrentNode($state->createNode(ElementNode::class, $token));
26-
}
14+
const TOKEN_TYPE = AssignmentToken::class;
2715

28-
if (!$state->currentNodeIs([ElementNode::class, MixinCallNode::class])) {
29-
$state->throwException(
30-
'Assignments can only happen on elements and mixinCalls',
31-
0,
32-
$token
33-
);
34-
}
16+
public function handleAssignmentToken(AssignmentToken $token, State $state)
17+
{
18+
$this->onlyOnElement($token, $state);
3519

3620
/** @var AssignmentNode $node */
3721
$node = $state->createNode(AssignmentNode::class, $token);

Parser/TokenHandler/AttributeEndTokenHandler.php

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,13 @@
33
namespace Phug\Parser\TokenHandler;
44

55
use Phug\Lexer\Token\AttributeEndToken;
6-
use Phug\Lexer\TokenInterface;
7-
use Phug\Parser\State;
8-
use Phug\Parser\TokenHandlerInterface;
96

10-
class AttributeEndTokenHandler implements TokenHandlerInterface
7+
class AttributeEndTokenHandler extends AbstractTokenHandler
118
{
12-
public function handleToken(TokenInterface $token, State $state)
9+
const TOKEN_TYPE = AttributeEndToken::class;
10+
11+
public function handleAttributeEndToken()
1312
{
14-
if (!($token instanceof AttributeEndToken)) {
15-
throw new \RuntimeException(
16-
'You can only pass attribute end tokens to this token handler'
17-
);
18-
}
13+
// noop
1914
}
2015
}

Parser/TokenHandler/AttributeStartTokenHandler.php

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use Phug\Lexer\Token\AttributeEndToken;
66
use Phug\Lexer\Token\AttributeStartToken;
77
use Phug\Lexer\Token\AttributeToken;
8-
use Phug\Lexer\TokenInterface;
98
use Phug\Parser\Node\AssignmentNode;
109
use Phug\Parser\Node\ElementNode;
1110
use Phug\Parser\Node\FilterNode;
@@ -14,35 +13,20 @@
1413
use Phug\Parser\Node\MixinNode;
1514
use Phug\Parser\Node\VariableNode;
1615
use Phug\Parser\State;
17-
use Phug\Parser\TokenHandlerInterface;
1816

19-
class AttributeStartTokenHandler implements TokenHandlerInterface
17+
class AttributeStartTokenHandler extends AbstractTokenHandler
2018
{
21-
public function handleToken(TokenInterface $token, State $state)
22-
{
23-
if (!($token instanceof AttributeStartToken)) {
24-
throw new \RuntimeException(
25-
'You can only pass attribute start tokens to this token handler'
26-
);
27-
}
19+
const TOKEN_TYPE = AttributeStartToken::class;
2820

29-
if (!$state->getCurrentNode()) {
30-
$state->setCurrentNode($state->createNode(ElementNode::class, $token));
31-
}
32-
33-
if (!$state->currentNodeIs([
21+
public function handleAttributeStartToken(AttributeStartToken $token, State $state)
22+
{
23+
$this->createElementNodeIfMissing($token, $state);
24+
$this->assertCurrentNodeIs($token, $state, [
3425
ElementNode::class, AssignmentNode::class,
3526
ImportNode::class, VariableNode::class,
3627
MixinNode::class, MixinCallNode::class,
3728
FilterNode::class,
38-
])) {
39-
$state->throwException(
40-
'Attributes can only be placed on element, assignment, '
41-
.'import, variable, filter, mixin and mixinCall',
42-
0,
43-
$token
44-
);
45-
}
29+
]);
4630

4731
foreach ($state->lookUpNext([AttributeToken::class]) as $subToken) {
4832
$state->handleToken($subToken);

Parser/TokenHandler/AttributeTokenHandler.php

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,21 @@
33
namespace Phug\Parser\TokenHandler;
44

55
use Phug\Lexer\Token\AttributeToken;
6-
use Phug\Lexer\TokenInterface;
76
use Phug\Parser\Node\AssignmentNode;
87
use Phug\Parser\Node\AttributeNode;
98
use Phug\Parser\Node\ElementNode;
109
use Phug\Parser\Node\MixinCallNode;
1110
use Phug\Parser\State;
12-
use Phug\Parser\TokenHandlerInterface;
1311
use Phug\Util\AttributesOrderInterface;
1412
use Phug\Util\OrderableInterface;
1513

16-
class AttributeTokenHandler implements TokenHandlerInterface
14+
class AttributeTokenHandler extends AbstractTokenHandler
1715
{
18-
public function handleToken(TokenInterface $token, State $state)
19-
{
20-
if (!($token instanceof AttributeToken)) {
21-
throw new \RuntimeException(
22-
'You can only pass attribute tokens to this token handler'
23-
);
24-
}
16+
const TOKEN_TYPE = AttributeToken::class;
2517

26-
if (!$state->getCurrentNode()) {
27-
$state->setCurrentNode($state->createNode(ElementNode::class, $token));
28-
}
18+
public function handleAttributeToken(AttributeToken $token, State $state)
19+
{
20+
$this->createElementNodeIfMissing($token, $state);
2921

3022
/** @var AttributeNode $node */
3123
$node = $state->createNode(AttributeNode::class, $token);
@@ -40,12 +32,13 @@ public function handleToken(TokenInterface $token, State $state)
4032
// Mixin calls and assignments take the first
4133
// expression set as the name as the value
4234
if (($value === '' || $value === null) &&
43-
$state->currentNodeIs([MixinCallNode::class, AssignmentNode::class])
35+
(
36+
$state->currentNodeIs([AssignmentNode::class]) ||
37+
($state->currentNodeIs([MixinCallNode::class]) && !$state->getCurrentNode()->areArgumentsCompleted())
38+
)
4439
) {
45-
if (!$state->currentNodeIs([MixinCallNode::class]) || !$state->getCurrentNode()->areArgumentsCompleted()) {
46-
$node->setValue($name);
47-
$node->setName(null);
48-
}
40+
$node->setValue($name);
41+
$node->setName(null);
4942
}
5043

5144
/** @var ElementNode|MixinCallNode|AssignmentNode $current */

Parser/TokenHandler/AutoCloseTokenHandler.php

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,16 @@
33
namespace Phug\Parser\TokenHandler;
44

55
use Phug\Lexer\Token\AutoCloseToken;
6-
use Phug\Lexer\TokenInterface;
76
use Phug\Parser\Node\ElementNode;
87
use Phug\Parser\State;
9-
use Phug\Parser\TokenHandlerInterface;
108

11-
class AutoCloseTokenHandler implements TokenHandlerInterface
9+
class AutoCloseTokenHandler extends AbstractTokenHandler
1210
{
13-
public function handleToken(TokenInterface $token, State $state)
14-
{
15-
if (!($token instanceof AutoCloseToken)) {
16-
throw new \RuntimeException(
17-
'You can only pass auto-close tokens to this token handler'
18-
);
19-
}
11+
const TOKEN_TYPE = AutoCloseToken::class;
2012

21-
if (!$state->currentNodeIs([ElementNode::class])) {
22-
$state->throwException(
23-
'Auto-close operators can only be used on elements',
24-
0,
25-
$token
26-
);
27-
}
13+
public function handleAutoCloseToken(AutoCloseToken $token, State $state)
14+
{
15+
$this->assertCurrentNodeIs($token, $state, [ElementNode::class]);
2816

2917
$state->getCurrentNode()->autoClose();
3018
}

Parser/TokenHandler/BlockTokenHandler.php

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,15 @@
33
namespace Phug\Parser\TokenHandler;
44

55
use Phug\Lexer\Token\BlockToken;
6-
use Phug\Lexer\TokenInterface;
76
use Phug\Parser\Node\BlockNode;
87
use Phug\Parser\State;
9-
use Phug\Parser\TokenHandlerInterface;
108

11-
class BlockTokenHandler implements TokenHandlerInterface
9+
class BlockTokenHandler extends AbstractTokenHandler
1210
{
13-
public function handleToken(TokenInterface $token, State $state)
14-
{
15-
if (!($token instanceof BlockToken)) {
16-
throw new \RuntimeException(
17-
'You can only pass block tokens to this token handler'
18-
);
19-
}
11+
const TOKEN_TYPE = BlockToken::class;
2012

13+
public function handleBlockToken(BlockToken $token, State $state)
14+
{
2115
/** @var BlockNode $node */
2216
$node = $state->createNode(BlockNode::class, $token);
2317
$node->setName($token->getName());

Parser/TokenHandler/CaseTokenHandler.php

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,15 @@
33
namespace Phug\Parser\TokenHandler;
44

55
use Phug\Lexer\Token\CaseToken;
6-
use Phug\Lexer\TokenInterface;
76
use Phug\Parser\Node\CaseNode;
87
use Phug\Parser\State;
9-
use Phug\Parser\TokenHandlerInterface;
108

11-
class CaseTokenHandler implements TokenHandlerInterface
9+
class CaseTokenHandler extends AbstractTokenHandler
1210
{
13-
public function handleToken(TokenInterface $token, State $state)
14-
{
15-
if (!($token instanceof CaseToken)) {
16-
throw new \RuntimeException(
17-
'You can only pass case tokens to this token handler'
18-
);
19-
}
11+
const TOKEN_TYPE = CaseToken::class;
2012

13+
public function handleCaseToken(CaseToken $token, State $state)
14+
{
2115
/** @var CaseNode $node */
2216
$node = $state->createNode(CaseNode::class, $token);
2317
$node->setSubject($token->getSubject());

Parser/TokenHandler/ClassTokenHandler.php

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,17 @@
33
namespace Phug\Parser\TokenHandler;
44

55
use Phug\Lexer\Token\ClassToken;
6-
use Phug\Lexer\TokenInterface;
7-
use Phug\Parser\Node\AttributeNode;
8-
use Phug\Parser\Node\ElementNode;
9-
use Phug\Parser\Node\MixinCallNode;
106
use Phug\Parser\State;
11-
use Phug\Parser\TokenHandlerInterface;
7+
use Phug\Parser\TokenHandler\Partial\StaticAttributeTrait;
128

13-
class ClassTokenHandler implements TokenHandlerInterface
9+
class ClassTokenHandler extends AbstractTokenHandler
1410
{
15-
public function handleToken(TokenInterface $token, State $state)
16-
{
17-
if (!($token instanceof ClassToken)) {
18-
throw new \RuntimeException(
19-
'You can only pass class tokens to this token handler'
20-
);
21-
}
22-
23-
if (!$state->getCurrentNode()) {
24-
$state->setCurrentNode($state->createNode(ElementNode::class, $token));
25-
}
11+
use StaticAttributeTrait;
2612

27-
if (!$state->currentNodeIs([ElementNode::class, MixinCallNode::class])) {
28-
$state->throwException(
29-
'Classes can only be used on elements and mixin calls',
30-
0,
31-
$token
32-
);
33-
}
13+
const TOKEN_TYPE = ClassToken::class;
3414

35-
//We actually create a fake class attribute
36-
/** @var AttributeNode $attr */
37-
$attr = $state->createNode(AttributeNode::class, $token);
38-
$attr->setName('class');
39-
$attr->setValue(var_export($token->getName(), true));
40-
$attr->unescape()->uncheck();
41-
42-
/** @var ElementNode|MixinCallNode $current */
43-
$current = $state->getCurrentNode();
44-
$current->getAttributes()->attach($attr);
15+
public function handleClassToken(ClassToken $token, State $state)
16+
{
17+
$this->attachStaticAttribute('class', $token, $state);
4518
}
4619
}

0 commit comments

Comments
 (0)