GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

GlossaryReplacer::internalSaveAnchorLink()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.9666
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
/*
3
 * This file is part of the trefoil application.
4
 *
5
 * (c) Miguel Angel Gabriel <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace Trefoil\Helpers;
11
12
/**
13
 * Replaces terms of a glossary into a given text (HTML).
14
 *
15
 * 'options' parameter can have the following values:
16
 *
17
 *     'coverage': ['all', 'item', 'first'], where:
18
 *          'all' : replace all ocurrences of term with a link to the definition.
19
 *          'item': (default) only first ocurrence into current text.
20
 *          'first': first ocurrence in this or other text pieces (for this to work the Glossary object
21
 *                  instance received must be the that was passed to each of the GlossaryReplacer objects
22
 *                  that process all the text pieces).
23
 *     'elements' ['chapter'] # items where global terms should be replaced.
24
 *
25
 * Other options are ignored.
26
 */
27
class GlossaryReplacer
28
{
29
    /**
30
     * The Glossary object
31
     *
32
     * @var Glossary
33
     */
34
    protected $glossary;
35
36
    /**
37
     * The HTML text to replace into
38
     *
39
     * @var string
40
     */
41
    protected $text;
42
43
    /**
44
     * The options for glossary processing
45
     *
46
     * @var array
47
     */
48
    protected $glossaryOptions = array();
49
50
    /**
51
     * Identifier for the text to be used in setting cross-references
52
     *
53
     * @var string
54
     */
55
    protected $textId;
56
57
    /**
58
     * The TextPreserver instance
59
     *
60
     * @var TextPreserver
61
     */
62
    protected $textPreserver;
63
64
    /**
65
     * A Twig instance to render the template
66
     *
67
     * @var \Twig_Environment
68
     */
69
    private $twig;
70
71
    /**
72
     * @return string
73
     *
74
     * @internal Should be protected but made public for PHP 5.3 compat
75
     */
76 1
    public function getTextId()
77
    {
78 1
        return $this->textId;
79
    }
80
81
    /**
82
     * @return array
83
     *
84
     * @internal Should be protected but made public for PHP 5.3 compat
85
     */
86 1
    public function getGlossaryOptions()
87
    {
88 1
        return $this->glossaryOptions;
89
    }
90
91
    /**
92
     * @param Glossary          $glossary        The glossary object
93
     * @param TextPreserver     $textPreserver   A TextPreserver instance
94
     * @param string            $text            The text to replace into
95
     * @param string            $textId          The id of the text, for cross-reference
96
     * @param array             $glossaryOptions The options to apply
97
     * @param \Twig_Environment $twig            Twig loader to load the template from
98
     */
99 1
    public function __construct(Glossary $glossary,
100
                                TextPreserver $textPreserver,
101
                                $text,
102
                                $textId,
103
                                $glossaryOptions = array(),
104
                                \Twig_Environment $twig)
105
    {
106 1
        $this->glossary = $glossary;
107 1
        $this->textPreserver = $textPreserver;
108 1
        $this->text = $text;
109 1
        $this->textId = $textId;
110 1
        $this->glossaryOptions = $glossaryOptions;
111 1
        $this->twig = $twig;
112 1
    }
113
114
    /**
115
     * Do al the replacements of glossary terms into text, returning the result.
116
     * Also, the glossary object will be modified for each term with the anchorlinks created into the text
117
     * and the Xrefs for further reference.
118
     *
119
     * @return string
120
     */
121 1
    public function replace()
122
    {
123 1
        if (!$this->glossary->count()) {
124
            // no glossary terms
125
            return $this->text;
126
        }
127
128
        // set the TextPeserver instance for this text processing
129 1
        $this->textPreserver->setText($this->text);
130
131
        // save existing values of tags contents we don't want to get modified into
132 1
        $this->textPreserver->preserveHtmlTags(array('a', 'pre'));
133
134
        // save existing values of attributes we don't want to get modified into
135 1
        $this->textPreserver->preserveHtmlTagAttributes(array('title', 'alt', 'src', 'href'));
136
137
        // get the modified text
138 1
        $text = $this->textPreserver->getText();
139
140
        // process each variant of each term
141 1
        foreach ($this->glossary as $glossaryItem/* @var $glossaryItem GlossaryItem */) {
142
143 1
            foreach ($glossaryItem->getVariants() as $variant) {
144 1
                $newText = $this->replaceTermVariant($text, $glossaryItem, $variant);
145
146 1
                if ($newText != $text) {
147
                    // at least a replacement ocurred
148 1
                    if ('item' == $this->glossaryOptions['coverage']) {
149
                        // already replaced once in this item, so ignore subsequent ocurrences and variants
150 1
                        $text = $newText;
151 1
                        break;
152
                    }
153
                }
154
155 1
                $text = $newText;
156 1
            }
157 1
        }
158
159
        // refresh the modified text into the TextPreserver and restore all saved strings
160 1
        $this->textPreserver->setText($text);
161 1
        $text = $this->textPreserver->restore();
162
163 1
        return $text;
164
    }
165
166
    /**
167
     * Replace a term variant into the content of certain tags
168
     *
169
     * @param string       $text
170
     * @param GlossaryItem $glossaryItem
171
     * @param string       $variant      The variant to replace
172
     *
173
     * @return string
174
     */
175 1
    protected function replaceTermVariant($text, GlossaryItem $glossaryItem, $variant)
176
    {
177
        // construct regexp to replace only into certain tags
178 1
        $tags = array('p', 'li', 'dd');
179
180 1
        $patterns = array();
181 1
        foreach ($tags as $tag) {
182 1
            $patterns[] = sprintf('/<(?<tag>%s)>(?<content>.*)<\/%s>/Ums', $tag, $tag);
183 1
        }
184
185
        // replace all occurrences of $variant into text $item with a glossary link
186
        // PHP 5.3 compat
187 1
        $me = $this;
188
189 1
        $text = preg_replace_callback(
190 1
            $patterns,
191
            function ($matches) use ($me, $glossaryItem, $variant) {
192
                // extract what to replace
193 1
                $tag = $matches['tag'];
194 1
                $tagContent = $matches['content'];
195
196
                // do the replacement
197 1
                $newContent = $me->internalReplaceTermVariantIntoString($tagContent, $glossaryItem, $variant);
198
199
                // reconstruct the original tag with the modified text
200 1
                return sprintf('<%s>%s</%s>', $tag, $newContent, $tag);
201 1
            },
202
            $text
203 1
        );
204
205 1
        return $text;
206
    }
207
208
    /**
209
     * Replace a term variant into a given string
210
     * 
211
     * The rendering expects a Twig template called "auto-glossary-term.twig" to be loadable.
212
     * Sample template:
213
     * 
214
     *      {% spaceless %}
215
     *      <span class="auto-glossary-term">
216
     *          <a href="#auto-glossary-{{ reference }}" id="auto-glossary-term-{{ reference }}">{{ term }}</a>
217
     *      </span>
218
     *      {% endspaceless %}
219
     * 
220
     * @param string       $text
221
     * @param GlossaryItem $glossaryItem
222
     * @param string       $variant      The variant to replace
223
     *
224
     * @return string
225
     *
226
     * @internal Should be protected but made public for PHP 5.3 compat
227
     */
228 1
    public function internalReplaceTermVariantIntoString($text, GlossaryItem $glossaryItem, $variant)
229
    {
230
        // construct the regexp to replace inside the tag content
231 1
        $regExp = '/';
232 1
        $regExp .= '(^|\W)'; // $1 = previous delimiter or start of string
233 1
        $regExp .= '(' . $variant . ')'; // $2 = the term to replace
234 1
        $regExp .= '(\W|$)'; // $3 = following delimiter or end of string
235 1
        $regExp .= '/ui'; // unicode, case-insensitive
236
237
        // replace all ocurrences of $variant into $tagContent with a glossary link
238
239
        // PHP 5.3 compat
240 1
        $me = $this;
241 1
        $textPreserver = $this->textPreserver;
242 1
        $twig = $this->twig;
243
244 1
        $text = preg_replace_callback(
245 1
            $regExp,
246 1
            function ($matches) use ($me, $glossaryItem, $variant, $textPreserver, $twig) {
247
                // look if already replaced once in this item, so just leave it unchanged
248 1
                $options = $me->getGlossaryOptions();
249 1
                if ('item' == $options['coverage']) {
250 1
                    foreach ($glossaryItem->getXref() as $variant => $xRefs) {
251 1
                        if (isset($xRefs[$me->getTextId()])) {
252 1
                            return $matches[0];
253
                        }
254 1
                    }
255 1
                }
256
257
                /* if coverage type is "first" and term is already defined,
258
                 * don't replace the term again
259
                */
260 1
                if ('first' == $options['coverage'] && $glossaryItem->getXref()) {
261
                    // already replaced elsewhere, just leave it unchanged
262
                    return $matches[0];
263
                }
264
265
                /* create the anchor link from the slug
266
                 * and get the number given to the anchor link just created
267
                 */
268 1
                $anchorLink = $me->internalSaveAnchorLink($glossaryItem);
269
270
                // save the placeholder for this slug to be replaced later
271 1
                $placeHolder = $textPreserver->internalCreatePlacehoder($anchorLink);
272
273
                // save the placeholder for this term (to avoid further unwanted matches into)
274 1
                $placeHolder2 = $textPreserver->internalCreatePlacehoder($matches[2]);
275
276
                // create replacement for link
277 1
                $repl = $twig->render('auto-glossary-term.twig',
278
                                      array(
279 1
                                          'reference' => $placeHolder,
280 1
                                          'term'      => $placeHolder2, 
281
                                          'item'      => $glossaryItem
282 1
                                      ));
283
284
                // save xref
285 1
                $glossaryItem->addXref($variant, $me->getTextId());
286
287
                // return reconstructed match
288 1
                return $matches[1] . $repl . $matches[3];
289 1
            },
290
            $text
291 1
        );
292
293 1
        return $text;
294
    }
295
296
    /**
297
     * Save an anchor link to be registered later
298
     *
299
     * @param GlossaryItem $glossaryItem
300
     *
301
     * @return int The anchor link saved
302
     *
303
     * @internal Should be protected but made public for PHP 5.3 compat
304
     */
305 1
    public function internalSaveAnchorLink(GlossaryItem $glossaryItem)
306
    {
307 1
        $count = count($glossaryItem->getAnchorLinks());
308
309 1
        $savedAnchorLink = $glossaryItem->getSlug() . '-' . $count;
310 1
        $glossaryItem->addAnchorLink($savedAnchorLink);
311
312 1
        return $savedAnchorLink;
313
    }
314
}
315