Skip to content

Commit 7c05417

Browse files
authored
Merge pull request #1 from phug-php/prototype
Prototype
2 parents d17c82e + 497c150 commit 7c05417

5 files changed

Lines changed: 471 additions & 6 deletions

File tree

README.md

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Phug Component
22

3-
Extension for pug-php and phug to use components in templates
3+
Extension for Pug-php and Phug to use components in templates
44

5-
## Install
5+
## Installation
66

77
```
88
composer require phug/component
@@ -37,7 +37,7 @@ component alert
3737
3838
section
3939
//- Somewhere later in your template
40-
@alert
40+
+alert
4141
slot title
4242
| Hello #[em world]!
4343
@@ -57,3 +57,38 @@ Output:
5757
</div>
5858
</section>
5959
```
60+
61+
### Default slots
62+
63+
```pug
64+
component page
65+
header
66+
slot header
67+
| Default header
68+
69+
slot
70+
71+
footer
72+
slot footer
73+
| Default footer
74+
75+
+page
76+
| My page content
77+
78+
slot footer
79+
| Custom footer
80+
```
81+
82+
Output:
83+
84+
```html
85+
<header>
86+
Default header
87+
</header>
88+
89+
My page content
90+
91+
<footer>
92+
Custom footer
93+
</footer>
94+
```

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
},
2626
"require-dev": {
2727
"phpunit/phpunit": "^8",
28-
"pug-php/pug": "^3"
28+
"pug-php/pug": "^3",
29+
"machy8/xhtml-formatter": "^1.0"
2930
},
3031
"autoload": {
3132
"psr-4": {

src/Phug/Component/ComponentExtension.php

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,146 @@
22

33
namespace Phug\Component;
44

5-
class ComponentExtension
5+
use Closure;
6+
use Phug\AbstractExtension;
7+
use Phug\Ast\NodeInterface;
8+
use Phug\Compiler\Event\NodeEvent;
9+
use Phug\CompilerEvent;
10+
use Phug\Formatter\Element\KeywordElement;
11+
use Phug\Formatter\Element\MixinElement;
12+
use Phug\Parser\NodeInterface as ParserNodeInterface;
13+
use Phug\Parser\Node\CodeNode;
14+
use Phug\Parser\Node\KeywordNode;
15+
use Phug\Parser\Node\MixinCallNode;
16+
use Phug\Parser\Node\TextNode;
17+
use Phug\Phug;
18+
use Phug\Renderer;
19+
use Phug\RendererModuleInterface;
20+
use Phug\Util\Partial\OptionTrait;
21+
22+
class ComponentExtension extends AbstractExtension implements RendererModuleInterface
623
{
24+
use OptionTrait;
25+
26+
const PUG_SLOT_NAME_VARIABLE = 'pug_component_slot';
27+
28+
/**
29+
* @var Renderer
30+
*/
31+
private $renderer;
32+
33+
public function __construct(Renderer $renderer)
34+
{
35+
$this->renderer = $renderer->setOptions([
36+
'keywords' => $this->getKeywords(),
37+
]);
38+
}
39+
40+
public function getContainer(): Renderer
41+
{
42+
return $this->renderer;
43+
}
44+
45+
public static function enable(): void
46+
{
47+
Phug::addExtension(static::class);
48+
}
49+
50+
public static function disable(): void
51+
{
52+
Phug::removeExtension(static::class);
53+
}
54+
55+
public static function slot(string $name, array $definedVariables)
56+
{
57+
$children = $definedVariables['__pug_children'] ?? null;
58+
$callbackName = static::PUG_SLOT_NAME_VARIABLE.'_'.$name;
59+
60+
if (is_object($children) && $children instanceof Closure) {
61+
$called = false;
62+
$children(array_merge([
63+
static::PUG_SLOT_NAME_VARIABLE => $name ?: '__main__',
64+
$callbackName => static function () use (&$called) {
65+
$called = true;
66+
}
67+
], $definedVariables));
68+
69+
return !$called;
70+
}
71+
72+
if (($definedVariables[static::PUG_SLOT_NAME_VARIABLE] ?? null) === $name) {
73+
$callback = $definedVariables[$callbackName] ?? null;
74+
75+
if ($callback && $callback instanceof Closure) {
76+
$callback();
77+
}
78+
79+
return true;
80+
}
81+
82+
return false;
83+
}
84+
85+
public function getKeywords(): array
86+
{
87+
return [
88+
'component' => function (string $name, KeywordElement $keyword): string {
89+
$mixin = new MixinElement;
90+
$mixin->setName($name);
91+
$mixin->setChildren($keyword->getChildren());
92+
$keyword->removeChildren();
93+
94+
return $this->renderer->getCompiler()->getFormatter()->format($mixin);
95+
},
96+
'slot' => static function (string $name, KeywordElement $keyword): array {
97+
return [
98+
'begin' => '<?php if ('.static::class.'::slot('.var_export($name, true).', get_defined_vars())) { ?>',
99+
'end' => '<?php } ?>',
100+
];
101+
},
102+
];
103+
}
104+
105+
protected function getCodeNode(NodeInterface $linkedNode, ParserNodeInterface $parentNode = null, $value = null, array $children = null)
106+
{
107+
$code = new CodeNode($linkedNode->getToken(), null, $linkedNode->getLevel(), $parentNode, $children);
108+
109+
if ($value !== null) {
110+
$code->setValue($value);
111+
}
112+
113+
return $code;
114+
}
115+
116+
public function handleNodeEvent(NodeEvent $event): void
117+
{
118+
$call = $event->getNode();
119+
120+
if ($call instanceof MixinCallNode) {
121+
$call->setChildren(array_merge(
122+
[$this->getCodeNode($call, $call, '$'.static::PUG_SLOT_NAME_VARIABLE.' = null')],
123+
array_map(function (NodeInterface $node) use ($call) {
124+
if ($node instanceof KeywordNode && $node->getName() === 'slot') {
125+
return $node;
126+
}
127+
128+
return $this->getCodeNode($node, $call, null, [
129+
(new TextNode($node->getToken(), null, $node->getLevel()))->setValue('if (!isset($'.static::PUG_SLOT_NAME_VARIABLE.') || $'.static::PUG_SLOT_NAME_VARIABLE.' === "__main__")'),
130+
$this->getCodeNode($node, null, '// main slot'),
131+
$node,
132+
]);
133+
}, $call->getChildren())
134+
));
135+
}
136+
}
137+
138+
public function attachEvents(): void
139+
{
140+
$this->renderer->getCompiler()->attach(CompilerEvent::NODE, [$this, 'handleNodeEvent']);
141+
}
142+
143+
public function detachEvents(): void
144+
{
145+
$this->renderer->getCompiler()->detach(CompilerEvent::NODE, [$this, 'handleNodeEvent']);
146+
}
7147
}

src/Phug/Component/equiv.pug

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
mixin a(b)
2+
- ob_start()
3+
p=b
4+
__pug_slot_1
5+
span Slot
6+
foo
7+
block
8+
- contents = ob_get_contents()
9+
- ob_end_clean()
10+
- preg_match_all('/<__pug_slot_1>([\s\S]*)<\/__pug_slot_1>/U', contents, pug_slot_1, PREG_SET_ORDER)
11+
- pug_slot_1 = end(pug_slot_1)
12+
!=preg_replace_callback('/<__pug_slot_1>([\s\S]*)<\/__pug_slot_1>/U', function () {
13+
code = pug_slot_1[1];
14+
pug_slot_1[1] = '';
15+
return code;
16+
}, contents)
17+
18+
+a(4)
19+
__pug_slot_1
20+
b OverSlot
21+
div

0 commit comments

Comments
 (0)