FootnotesRule   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 179
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 96.43%

Importance

Changes 0
Metric Value
wmc 16
lcom 1
cbo 1
dl 0
loc 179
ccs 81
cts 84
cp 0.9643
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A init() 0 6 1
A preSave() 0 13 3
A generateRandomFootnoteIdentifier() 0 15 3
B render() 0 51 5
B postRender() 0 32 3
1
<?php
2
3
namespace Kaloa\Renderer\Xml\Rule;
4
5
use DOMElement;
6
use Kaloa\Renderer\Xml\Rule\AbstractRule;
7
8
/**
9
 *
10
 */
11
final class FootnotesRule extends AbstractRule
12
{
13
    /**
14
     *
15
     * @var array
16
     */
17
    private $config;
18
19
    /**
20
     *
21
     * @var array
22
     */
23
    private $footnotes;
24
25
    /**
26
     *
27
     * @var array
28
     */
29
    private $randomIdStore;
30
31
    /**
32
     *
33
     * @var array
34
     */
35
    private $usedIdentifiers;
36
37
    /**
38
     *
39
     * @param array $config
40
     */
41 1
    public function __construct(array $config = array())
42
    {
43
        $configDefault = array(
44 1
            'footnoteIdFormat'       => 'fn:%s',
45 1
            'footnoteRefIdFormat'    => 'fnref:%s',
46
            'useRandomIdsNotNumbers' => true
47 1
        );
48
49 1
        $this->config = array_merge($configDefault, $config);
50 1
    }
51
52
    /**
53
     *
54
     */
55 1
    public function init()
56
    {
57 1
        $this->footnotes       = array();
58 1
        $this->randomIdStore   = array();
59 1
        $this->usedIdentifiers = array();
60 1
    }
61
62
    /**
63
     *
64
     */
65 1
    public function preSave()
66
    {
67 1
        if ($this->config['useRandomIdsNotNumbers']) {
68
            // Generate random footnote identifier for all footnotes without name
69
            // attribute
70 1
            foreach ($this->runXpathQuery('//footnote[not(@name)]') as $node) {
71 1
                $node->setAttribute(
72 1
                    'name',
73 1
                    $this->generateRandomFootnoteIdentifier()
74 1
                );
75 1
            }
76 1
        }
77 1
    }
78
79
    /**
80
     *
81
     * @return string
82
     */
83 1
    private function generateRandomFootnoteIdentifier()
84
    {
85 1
        $letters = range('a', 'z');
86
87
        do {
88 1
            $randomId = '';
89 1
            for ($i = 0; $i < 5; $i++) {
90 1
                $randomId .= $letters[mt_rand(0, 25)];
91 1
            }
92 1
        } while (in_array($randomId, $this->randomIdStore));
93
94 1
        $this->randomIdStore[] = $randomId;
95
96 1
        return $randomId;
97
    }
98
99
    /**
100
     *
101
     */
102 1
    public function render()
103
    {
104 1
        foreach ($this->runXpathQuery('//footnote') as $node) {
105 1
            $parent = $node->parentNode;
106
107
            /* @var $node DOMElement */
108
109 1
            $fragment = $this->getDocument()->createDocumentFragment();
110
111 1
            $identifier = (string) $node->getAttribute('name');
112 1
            $text = $this->getInnerXml($node);
113
114 1
            if ($identifier === '') {
115
                // This should only happen when a footnote has no name
116
                // attribute or when a name was already taken
117
                $identifier = count($this->footnotes) + 1;
118
            }
119
120 1
            $i = 0;
121 1
            $identifierClean = '';
122 1
            while (in_array($identifier, $this->usedIdentifiers)) {
123 1
                if ($i === 0) {
124 1
                    $identifier = count($this->footnotes) + 1;
125 1
                    $identifierClean = $identifier;
126 1
                } else {
127
                    $identifier = $identifierClean . '-' . $i;
128
                }
129 1
                $i++;
130 1
            }
131
132 1
            $this->usedIdentifiers[] = $identifier;
133
134 1
            $this->footnotes[] = array(
135 1
                'identifier' => $identifier,
136
                'text'       => $text
137 1
            );
138
139 1
            $number = count($this->footnotes);
140
141 1
            $id = sprintf($this->config['footnoteIdFormat'], $identifier);
142 1
            $idRef = sprintf($this->config['footnoteRefIdFormat'], $identifier);
143
144
            $xml = <<<EOT
145 1
<span id="$idRef"><a href="#$id">[$number]</a></span>
146 1
EOT;
147
148 1
            $fragment->appendXML($xml);
149
150 1
            $parent->replaceChild($fragment, $node);
151 1
        }
152 1
    }
153
154
    /**
155
     *
156
     */
157 1
    public function postRender()
158
    {
159 1
        if (count($this->footnotes) === 0) {
160 1
            return;
161
        }
162
163 1
        $fragment = $this->getDocument()->createDocumentFragment();
164
165 1
        $i = 1;
166 1
        $xml = '';
167
168 1
        $xml .= '<ol class="footnotes">';
169 1
        foreach ($this->footnotes as $element) {
170 1
            $identifier = $element['identifier'];
171 1
            $text       = $element['text'];
172
173 1
            $id    = sprintf($this->config['footnoteIdFormat'], $identifier);
174 1
            $idRef = sprintf($this->config['footnoteRefIdFormat'], $identifier);
175
176 1
            $xml .= '<li id="' . $id . '">';
177 1
            $xml .= $text;
178 1
            $xml .= ' <a href="#' . $idRef . '">' . "\xE2\x86\x91" . '</a>';
179 1
            $xml .= '</li>';
180
181 1
            $i++;
182 1
        }
183 1
        $xml .= '</ol>';
184
185 1
        $fragment->appendXML($xml);
186
187 1
        $this->getDocument()->documentElement->appendChild($fragment);
188 1
    }
189
}
190