Passed
Push — master ( 662c3c...9a9571 )
by Jitendra
01:23
created

Parser::getSrc()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 5
c 1
b 0
f 0
nc 3
nop 2
dl 0
loc 13
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, ' /'))) {
76
                return $all;
77
            }
78
79
            // Already there or no-flagged
80
            if (\preg_match('/(data-(src|poster)|no-' . $this->lazyClass . ')/i', $props)) {
81
                return $all;
82
            }
83
84
            $src = $this->getSrc($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
            return "<$tag$src" . $this->doReplacements($props) . '>';
93
        }, $html);
94
    }
95
96
    protected function getSrc(string $tag, string $props): string
97
    {
98
        // For source no need src
99
        if ($tag === 'source') {
100
            return '';
101
        }
102
103
        // For video no need src if not already there!
104
        if ($tag === 'video' && \stripos($props, 'src') === false) {
105
            return '';
106
        }
107
108
        return ' src="' . $this->placeholder . '"';
109
    }
110
111
    protected function doReplacements(string $props): string
112
    {
113
        $replacements = [
114
            ' src='      => ' data-src=',
115
            ' src ='     => ' data-src=',
116
            ' srcset='   => ' data-srcset=',
117
            ' srcset ='  => ' data-srcset=',
118
            ' class="'   => " class=\"{$this->lazyClass} yall ",
119
            ' class ="'  => " class=\"{$this->lazyClass} yall ",
120
            ' class = "' => " class=\"{$this->lazyClass} yall ",
121
            " class='"   => " class='{$this->lazyClass} yall ",
122
            " class ='"  => " class='{$this->lazyClass} yall ",
123
            " class = '" => " class='{$this->lazyClass} yall ",
124
            ' poster='   => ' data-poster=',
125
            ' poster ='  => ' data-poster=',
126
        ];
127
128
        return \strtr($props, $replacements);
129
    }
130
131
    /**
132
     * @internal
133
     */
134
    public function isLazyloadEnd(Token $token): bool
135
    {
136
        return $token->test('endlazyload');
137
    }
138
139
    /**
140
     * Gets the tag name used in block.
141
     *
142
     * @return string
143
     */
144
    public function getTag(): string
145
    {
146
        return 'lazyload';
147
    }
148
}
149