• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / Yoast-SEO-for-TYPO3 / 25553882355

08 May 2026 11:47AM UTC coverage: 11.696% (+1.2%) from 10.457%
25553882355

push

github

web-flow
Merge pull request #651 from Yoast/task/optimize-bodyparser

[TASK] Optimized BodyParser to support nested TYPO3SEARCH markers and allow h6 headings in the content

33 of 35 new or added lines in 1 file covered. (94.29%)

320 of 2736 relevant lines covered (11.7%)

0.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.74
/Classes/PageParser/BodyParser.php
1
<?php
2

3
/**
4
 * This file is part of the "yoast_seo" extension for TYPO3 CMS.
5
 *
6
 * For the full copyright and license information, please read the
7
 * LICENSE.txt file that was distributed with this source code.
8
 */
9

10
declare(strict_types=1);
11

12
namespace YoastSeoForTypo3\YoastSeo\PageParser;
13

14
use MaxServ\FrontendRequest\Dto\RequestContext;
15

16
class BodyParser extends \MaxServ\FrontendRequest\PageParser\Parser\BodyParser
17
{
18
    protected const INDEXABLE_PATTERN = '/<!--\s*TYPO3SEARCH_(begin|end)\s*-->/i';
19
    protected const ALLOWED_TAGS = '<h1><h2><h3><h4><h5><h6><p><a><img>';
20
    protected const STRIP_WITH_CONTENT_TAGS = '<script><noscript>';
21

22
    public function parse(string $html, RequestContext $context): string
23
    {
24
        $body = parent::parse($html, $context);
33✔
25

26
        $sections = $this->extractIndexableSections($body);
33✔
27
        if ($sections !== []) {
33✔
28
            $body = implode(' ', array_map(trim(...), $sections));
12✔
29
        }
30

31
        return $this->prepareBody($body);
33✔
32
    }
33

34
    protected function prepareBody(string $body): string
35
    {
36
        $body = $this->stripTagsContent($body, self::STRIP_WITH_CONTENT_TAGS);
33✔
37
        $body = (string)preg_replace('/\s+/', ' ', $body);
33✔
38
        $body = strip_tags($body, self::ALLOWED_TAGS);
33✔
39

40
        return trim($body);
33✔
41
    }
42

43
    protected function stripTagsContent(string $text, string $tags = ''): string
44
    {
45
        if ($tags === '') {
33✔
NEW
46
            return $text;
×
47
        }
48

49
        preg_match_all('/<([a-z][a-z0-9]*)\b/i', $tags, $found);
33✔
50
        $names = array_unique($found[1]);
33✔
51
        if ($names === []) {
33✔
NEW
52
            return $text;
×
53
        }
54

55
        $pattern = '@<(' . implode('|', array_map('preg_quote', $names)) . ')\b[^>]*>.*?</\1\s*>@is';
33✔
56
        return (string)preg_replace($pattern, '', $text);
33✔
57
    }
58

59
    /**
60
     * @return list<string>
61
     */
62
    protected function extractIndexableSections(string $body): array
63
    {
64
        if (!preg_match_all(self::INDEXABLE_PATTERN, $body, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
33✔
65
            return [];
20✔
66
        }
67

68
        $sections = [];
13✔
69
        $depth = 0;
13✔
70
        $sectionStart = null;
13✔
71

72
        foreach ($matches as $match) {
13✔
73
            $markerText = $match[0][0];
13✔
74
            $markerOffset = (int)$match[0][1];
13✔
75
            $markerType = strtolower($match[1][0]);
13✔
76

77
            if ($markerType === 'begin') {
13✔
78
                if ($depth === 0) {
13✔
79
                    $sectionStart = $markerOffset + strlen($markerText);
13✔
80
                }
81
                $depth++;
13✔
82
                continue;
13✔
83
            }
84

85
            if ($depth === 0) {
12✔
86
                continue;
1✔
87
            }
88
            $depth--;
12✔
89

90
            if ($depth === 0 && $sectionStart !== null) {
12✔
91
                $sections[] = substr($body, $sectionStart, $markerOffset - $sectionStart);
12✔
92
                $sectionStart = null;
12✔
93
            }
94
        }
95

96
        return $sections;
13✔
97
    }
98
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc