Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
declare(strict_types=1);
4
 
5
/*
6
 * This file is part of the league/commonmark package.
7
 *
8
 * (c) Colin O'Dell <colinodell@gmail.com>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
 
14
namespace League\CommonMark\Extension\ExternalLink;
15
 
16
use League\CommonMark\Event\DocumentParsedEvent;
17
use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
18
use League\Config\ConfigurationInterface;
19
 
20
final class ExternalLinkProcessor
21
{
22
    public const APPLY_NONE     = '';
23
    public const APPLY_ALL      = 'all';
24
    public const APPLY_EXTERNAL = 'external';
25
    public const APPLY_INTERNAL = 'internal';
26
 
27
    /** @psalm-readonly */
28
    private ConfigurationInterface $config;
29
 
30
    public function __construct(ConfigurationInterface $config)
31
    {
32
        $this->config = $config;
33
    }
34
 
35
    public function __invoke(DocumentParsedEvent $e): void
36
    {
37
        $internalHosts   = $this->config->get('external_link/internal_hosts');
38
        $openInNewWindow = $this->config->get('external_link/open_in_new_window');
39
        $classes         = $this->config->get('external_link/html_class');
40
 
41
        foreach ($e->getDocument()->iterator() as $link) {
42
            if (! ($link instanceof Link)) {
43
                continue;
44
            }
45
 
46
            $host = \parse_url($link->getUrl(), PHP_URL_HOST);
47
            if (! \is_string($host)) {
48
                // Something is terribly wrong with this URL
49
                continue;
50
            }
51
 
52
            if (self::hostMatches($host, $internalHosts)) {
53
                $link->data->set('external', false);
54
                $this->applyRelAttribute($link, false);
55
                continue;
56
            }
57
 
58
            // Host does not match our list
59
            $this->markLinkAsExternal($link, $openInNewWindow, $classes);
60
        }
61
    }
62
 
63
    private function markLinkAsExternal(Link $link, bool $openInNewWindow, string $classes): void
64
    {
65
        $link->data->set('external', true);
66
        $this->applyRelAttribute($link, true);
67
 
68
        if ($openInNewWindow) {
69
            $link->data->set('attributes/target', '_blank');
70
        }
71
 
72
        if ($classes !== '') {
73
            $link->data->append('attributes/class', $classes);
74
        }
75
    }
76
 
77
    private function applyRelAttribute(Link $link, bool $isExternal): void
78
    {
79
        $options = [
80
            'nofollow'   => $this->config->get('external_link/nofollow'),
81
            'noopener'   => $this->config->get('external_link/noopener'),
82
            'noreferrer' => $this->config->get('external_link/noreferrer'),
83
        ];
84
 
85
        foreach ($options as $type => $option) {
86
            switch (true) {
87
                case $option === self::APPLY_ALL:
88
                case $isExternal && $option === self::APPLY_EXTERNAL:
89
                case ! $isExternal && $option === self::APPLY_INTERNAL:
90
                    $link->data->append('attributes/rel', $type);
91
            }
92
        }
93
    }
94
 
95
    /**
96
     * @internal This method is only public so we can easily test it. DO NOT USE THIS OUTSIDE OF THIS EXTENSION!
97
     *
98
     * @param mixed $compareTo
99
     */
100
    public static function hostMatches(string $host, $compareTo): bool
101
    {
102
        foreach ((array) $compareTo as $c) {
103
            if (\strpos($c, '/') === 0) {
104
                if (\preg_match($c, $host)) {
105
                    return true;
106
                }
107
            } elseif ($c === $host) {
108
                return true;
109
            }
110
        }
111
 
112
        return false;
113
    }
114
}