Passed
Push — master ( 87e940...662c3c )
by Jitendra
01:27
created

Parser::needsSrc()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 3
c 1
b 0
f 0
nc 3
nop 2
dl 0
loc 8
rs 10
1
<?php
2
3
/*
4
 * This file is part of the TWIG-YALL package.
5
 *
6
 * (c) Jitendra Adhikari <[email protected]>
7
 *     <https://github.com/adhocore>
8
 *
9
 * Licensed under MIT license.
10
 */
11
12
namespace Ahc\TwigYall;
13
14
use Twig\Node\Node;
15
use Twig\Node\TextNode;
16
use Twig\Token;
17
use Twig\TokenParser\AbstractTokenParser;
18
19
class Parser extends AbstractTokenParser
20
{
21
    /** @var string yall lazyClass */
22
    protected $lazyClass;
23
24
    /** @var string URI for placeholder image */
25
    protected $placeholder;
26
27
    /**
28
     * Constructor.
29
     *
30
     * @param string $lazyClass
31
     * @param string $placeholder
32
     */
33
    public function __construct(string $lazyClass, string $placeholder)
34
    {
35
        $this->lazyClass   = $lazyClass;
36
        $this->placeholder = $placeholder;
37
    }
38
39
    /**
40
     * Parse `{% lazyload %}...{% endlazyload %}` block.
41
     *
42
     * @param Token $token
43
     *
44
     * @return Node
45
     */
46
    public function parse(Token $token): Node
47
    {
48
        $stream = $this->parser->getStream();
49
50
        $stream->expect(Token::BLOCK_END_TYPE);
51
        $body = $this->parser->subparse([$this, 'isLazyloadEnd'], true);
52
        $stream->expect(Token::BLOCK_END_TYPE);
53
54
        return $this->traverse($body);
55
    }
56
57
    protected function traverse(Node $node)
58
    {
59
        if ($node instanceof TextNode) {
60
            $node->setAttribute('data', $this->doLazyload($node->getAttribute('data')));
61
        }
62
63
        foreach ($node as $sub) {
64
            $this->traverse($sub);
65
        }
66
67
        return $node;
68
    }
69
70
    protected function doLazyload(string $html): string
71
    {
72
        return \preg_replace_callback('/<(img|source|video)([^>]+)>/m', function ($match) {
73
            list($all, $tag, $props) = $match;
74
75
            if (empty(\trim($props, ' /')) || \strpos($props, "no-{$this->lazyClass}") !== false) {
76
                return $all;
77
            }
78
79
            // Already there
80
            if (\preg_match('/data-(src|poster)/i', $props)) {
81
                return $all;
82
            }
83
84
            $needsSrc = $this->needsSrc($tag, $props);
85
            if (\stripos($props, ' class') === false) {
86
                $tag .= " class=\"{$this->lazyClass} yall\"";
87
            }
88
            if (\stripos($props, ' poster') !== false) {
89
                $tag .= " poster=\"{$this->placeholder}\"";
90
            }
91
92
            $src = $needsSrc ? " src=\"{$this->placeholder}\"" : '';
93
94
            return "<$tag$src" . $this->doReplacements($props) . '>';
95
        }, $html);
96
    }
97
98
    protected function needsSrc(string $tag, string $props): bool
99
    {
100
        // For source no need src, for video no need src if not already there!
101
        if ($tag === 'source') {
102
            return false;
103
        }
104
105
        return $tag !== 'video' || \stripos($props, 'src') !== false;
106
    }
107
108
    protected function doReplacements(string $props): string
109
    {
110
        $replacements = [
111
            ' src='      => ' data-src=',
112
            ' src ='     => ' data-src=',
113
            ' srcset='   => ' data-srcset=',
114
            ' srcset ='  => ' data-srcset=',
115
            ' class="'   => " class=\"{$this->lazyClass} yall ",
116
            ' class ="'  => " class=\"{$this->lazyClass} yall ",
117
            ' class = "' => " class=\"{$this->lazyClass} yall ",
118
            " class='"   => " class='{$this->lazyClass} yall ",
119
            " class ='"  => " class='{$this->lazyClass} yall ",
120
            " class = '" => " class='{$this->lazyClass} yall ",
121
            ' poster='   => ' data-poster=',
122
            ' poster ='  => ' data-poster=',
123
        ];
124
125
        return \strtr($props, $replacements);
126
    }
127
128
    /**
129
     * @internal
130
     */
131
    public function isLazyloadEnd(Token $token): bool
132
    {
133
        return $token->test('endlazyload');
134
    }
135
136
    /**
137
     * Gets the tag name used in block.
138
     *
139
     * @return string
140
     */
141
    public function getTag(): string
142
    {
143
        return 'lazyload';
144
    }
145
}
146