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

Yoast / Yoast-SEO-for-TYPO3 / 25442389023

06 May 2026 02:43PM UTC coverage: 10.38% (-0.08%) from 10.457%
25442389023

Pull #651

github

web-flow
Merge ba6a3dd54 into 4d4ae0d15
Pull Request #651: [TASK] Optimized BodyParser to support nested TYPO3SEARCH markers and allow h6 headings in the content

0 of 35 new or added lines in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

284 of 2736 relevant lines covered (10.38%)

0.29 hits per line

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

0.0
/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);
×
25

NEW
26
        $sections = $this->extractIndexableSections($body);
×
NEW
27
        if ($sections !== []) {
×
NEW
28
            $body = implode('', $sections);
×
29
        }
30

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

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

40
        return trim($body);
×
41
    }
42

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

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

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

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

NEW
68
        $sections = [];
×
NEW
69
        $depth = 0;
×
NEW
70
        $sectionStart = null;
×
71

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

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

NEW
85
            if ($depth === 0) {
×
NEW
86
                continue;
×
87
            }
NEW
88
            $depth--;
×
89

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

NEW
96
        return $sections;
×
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