Completed
Push — master ( 74371a...6da8c0 )
by Vladimir
02:21
created

TableOfContentsFilter::get()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
/**
3
 * @copyright 2018 Vladimir Jimenez
4
 * @license   https://github.com/allejo/stakx/blob/master/LICENSE.md MIT
5
 */
6
7
namespace allejo\stakx\Templating\Twig\Extension;
8
9
use allejo\stakx\Utilities\HtmlUtils;
10
11
class TableOfContentsFilter extends AbstractTwigExtension implements TwigFilterInterface
12
{
13
    /**
14
     * @param string      $html  The HTML we'll be parsing
15
     * @param string|null $id    The ID to assign to the generated TOC
16
     * @param string|null $class The class to assign to the generated TOC
17
     * @param int         $hMin  The heading minimum that'll be included
18
     * @param int         $hMax  The heading maximum that'll be included
19
     *
20
     * @link https://git.io/vdnEM Modified from @mattfarina's implementation
21
     *
22
     * @return string
23
     */
24 7
    public function __invoke($html, $id = null, $class = null, $hMin = 1, $hMax = 6)
25
    {
26 7
        if (!function_exists('simplexml_load_string'))
27
        {
28
            trigger_error('XML support is not available with the current PHP installation.', E_USER_WARNING);
29
            return '';
30
        }
31
32 7
        $dom = new \DOMDocument();
33 7
        $headings = HtmlUtils::htmlXPath($dom, $html, '//h1|//h2|//h3|//h4|//h5|//h6');
34
35 7
        $toc = '';
36 7
        $curr = $last = 0;
37
38
        /**
39
         * @var int         $index
40
         * @var \DOMElement $heading
41
         */
42 7
        foreach ($headings as $index => $heading)
43
        {
44 7
            if ($heading->attributes->getNamedItem('id') === null)
45
            {
46
                continue;
47
            }
48
49 7
            sscanf($heading->tagName, 'h%u', $curr);
50
51 7
            if (!($hMin <= $curr && $curr <= $hMax))
52
            {
53 2
                continue;
54
            }
55
56 7
            $headingID = $heading->attributes->getNamedItem('id');
57
58 7
            if ($curr > $last) // If the current level is greater than the last level indent one level
59
            {
60 7
                $toc .= '<ul>';
61
            }
62 5
            elseif ($curr < $last) // If the current level is less than the last level go up appropriate amount.
63
            {
64 3
                $toc .= str_repeat('</li></ul>', $last - $curr) . '</li>';
65
            }
66
            else // If the current level is equal to the last.
67
            {
68 2
                $toc .= '</li>';
69
            }
70
71 7
            $toc .= '<li><a href="#' . $headingID->nodeValue . '">' . $heading->nodeValue . '</a>';
72 7
            $last = $curr;
73
        }
74
75 7
        $toc .= str_repeat('</li></ul>', ($last - ($hMin - 1)));
76
77 7
        if ($id !== null || $class !== null)
78
        {
79 3
            $attributes = [];
80
81 3
            if ($id !== null)
82
            {
83 2
                $attributes[] = sprintf('id="%s"', $id);
84
            }
85
86 3
            if ($class !== null)
87
            {
88 2
                $attributes[] = sprintf('class="%s"', $class);
89
            }
90
91 3
            $toc = substr_replace($toc, sprintf('<ul %s', implode(' ', $attributes)), 0, 3);
92
        }
93
94 7
        return $toc;
95
    }
96
97
    /**
98
     * @return \Twig_SimpleFilter
99
     */
100
    public static function get()
101
    {
102
        return (new \Twig_SimpleFilter('toc', new self()));
103
    }
104
}
105