AttributesListener   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 53
dl 0
loc 124
ccs 53
cts 53
cp 1
rs 9.68
c 0
b 0
f 0
wmc 34

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getPrevious() 0 13 5
A isAttributesNode() 0 3 2
B processDocument() 0 25 9
C findTargetAndDirection() 0 38 13
A getNext() 0 7 4
A __construct() 0 4 1
1
<?php
2
3
/*
4
 * This file is part of the league/commonmark package.
5
 *
6
 * (c) Colin O'Dell <[email protected]>
7
 * (c) 2015 Martin Hasoň <[email protected]>
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
declare(strict_types=1);
14
15
namespace League\CommonMark\Extension\Attributes\Event;
16
17
use League\CommonMark\Event\DocumentParsedEvent;
18
use League\CommonMark\Extension\Attributes\Node\Attributes;
19
use League\CommonMark\Extension\Attributes\Node\AttributesInline;
20
use League\CommonMark\Extension\Attributes\Util\AttributesHelper;
21
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
22
use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
23
use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
24
use League\CommonMark\Node\Inline\AbstractInline;
25
use League\CommonMark\Node\Node;
26
27
final class AttributesListener
28
{
29
    private const DIRECTION_PREFIX = 'prefix';
30
    private const DIRECTION_SUFFIX = 'suffix';
31
32
    /** @var list<string> */
0 ignored issues
show
Bug introduced by
The type League\CommonMark\Extension\Attributes\Event\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
33
    private array $allowList;
34
    private bool $allowUnsafeLinks;
35
36
    /**
37
     * @param list<string> $allowList
38
     */
39 22
    public function __construct(array $allowList = [], bool $allowUnsafeLinks = true)
40
    {
41 22
        $this->allowList        = $allowList;
0 ignored issues
show
Documentation Bug introduced by
It seems like $allowList of type array is incompatible with the declared type League\CommonMark\Extension\Attributes\Event\list of property $allowList.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
42 22
        $this->allowUnsafeLinks = $allowUnsafeLinks;
43
    }
44
45 22
    public function processDocument(DocumentParsedEvent $event): void
46
    {
47 22
        foreach ($event->getDocument()->iterator() as $node) {
48 22
            if (! ($node instanceof Attributes || $node instanceof AttributesInline)) {
49 22
                continue;
50
            }
51
52 22
            [$target, $direction] = self::findTargetAndDirection($node);
53
54 22
            if ($target instanceof Node) {
55 22
                $parent = $target->parent();
56 22
                if ($parent instanceof ListItem && $parent->parent() instanceof ListBlock && $parent->parent()->isTight()) {
57 2
                    $target = $parent;
58
                }
59
60 22
                if ($direction === self::DIRECTION_SUFFIX) {
61 18
                    $attributes = AttributesHelper::mergeAttributes($target, $node->getAttributes());
62
                } else {
63 12
                    $attributes = AttributesHelper::mergeAttributes($node->getAttributes(), $target);
64
                }
65
66 22
                $target->data->set('attributes', AttributesHelper::filterAttributes($attributes, $this->allowList, $this->allowUnsafeLinks));
67
            }
68
69 22
            $node->detach();
70
        }
71
    }
72
73
    /**
74
     * @param Attributes|AttributesInline $node
75
     *
76
     * @return array<Node|string|null>
77
     */
78 22
    private static function findTargetAndDirection($node): array
79
    {
80 22
        $target    = null;
81 22
        $direction = null;
82 22
        $previous  = $next = $node;
83 22
        while (true) {
84 22
            $previous = self::getPrevious($previous);
85 22
            $next     = self::getNext($next);
86
87 22
            if ($previous === null && $next === null) {
88 10
                if (! $node->parent() instanceof FencedCode) {
89 10
                    $target    = $node->parent();
90 10
                    $direction = self::DIRECTION_SUFFIX;
91
                }
92
93 10
                break;
94
            }
95
96 22
            if ($node instanceof AttributesInline && ($previous === null || ($previous instanceof AbstractInline && $node->isBlock()))) {
97 10
                continue;
98
            }
99
100 22
            if ($previous !== null && ! self::isAttributesNode($previous)) {
101 18
                $target    = $previous;
102 18
                $direction = self::DIRECTION_SUFFIX;
103
104 18
                break;
105
            }
106
107 12
            if ($next !== null && ! self::isAttributesNode($next)) {
108 12
                $target    = $next;
109 12
                $direction = self::DIRECTION_PREFIX;
110
111 12
                break;
112
            }
113
        }
114
115 22
        return [$target, $direction];
116
    }
117
118
    /**
119
     * Get any previous block (sibling or parent) this might apply to
120
     */
121 22
    private static function getPrevious(?Node $node = null): ?Node
122
    {
123 22
        if ($node instanceof Attributes) {
124 14
            if ($node->getTarget() === Attributes::TARGET_NEXT) {
125 12
                return null;
126
            }
127
128 8
            if ($node->getTarget() === Attributes::TARGET_PARENT) {
129 2
                return $node->parent();
130
            }
131
        }
132
133 18
        return $node instanceof Node ? $node->previous() : null;
134
    }
135
136
    /**
137
     * Get any previous block (sibling or parent) this might apply to
138
     */
139 22
    private static function getNext(?Node $node = null): ?Node
140
    {
141 22
        if ($node instanceof Attributes && $node->getTarget() !== Attributes::TARGET_NEXT) {
142 8
            return null;
143
        }
144
145 22
        return $node instanceof Node ? $node->next() : null;
146
    }
147
148 22
    private static function isAttributesNode(Node $node): bool
149
    {
150 22
        return $node instanceof Attributes || $node instanceof AttributesInline;
151
    }
152
}
153