Passed
Pull Request — main (#986)
by Colin
02:07
created

AttributesHelper::parseAttributes()   C

Complexity

Conditions 15
Paths 18

Size

Total Lines 72
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 34
CRAP Score 15.0386

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 15
eloc 36
c 2
b 0
f 0
nc 18
nop 1
dl 0
loc 72
ccs 34
cts 36
cp 0.9444
crap 15.0386
rs 5.9166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Util;
16
17
use League\CommonMark\Node\Node;
18
use League\CommonMark\Parser\Cursor;
19
use League\CommonMark\Util\RegexHelper;
20
21
/**
22
 * @internal
23
 */
24
final class AttributesHelper
25
{
26
    private const SINGLE_ATTRIBUTE = '\s*([.#][_a-z0-9-]+|' . RegexHelper::PARTIAL_ATTRIBUTENAME . RegexHelper::PARTIAL_ATTRIBUTEVALUESPEC . '?)\s*';
27
    private const ATTRIBUTE_LIST   = '/^{:?(' . self::SINGLE_ATTRIBUTE . ')+}/i';
28
29
    /**
30
     * @return array<string, mixed>
31
     */
32 98
    public static function parseAttributes(Cursor $cursor): array
33
    {
34 98
        $state = $cursor->saveState();
35 98
        $cursor->advanceToNextNonSpaceOrNewline();
36
37
        // Quick check to see if we might have attributes
38 98
        if ($cursor->getCharacter() !== '{') {
39 14
            $cursor->restoreState($state);
40
41 14
            return [];
42
        }
43
44
        // Attempt to match the entire attribute list expression
45
        // While this is less performant than checking for '{' now and '}' later, it simplifies
46
        // matching individual attributes since they won't need to look ahead for the closing '}'
47
        // while dealing with the fact that attributes can technically contain curly braces.
48
        // So we'll just match the start and end braces up front.
49 96
        $attributeExpression = $cursor->match(self::ATTRIBUTE_LIST);
50 96
        if ($attributeExpression === null) {
51 8
            $cursor->restoreState($state);
52
53 8
            return [];
54
        }
55
56
        // Trim the leading '{' or '{:' and the trailing '}'
57 90
        $attributeExpression = \ltrim(\substr($attributeExpression, 1, -1), ':');
58 90
        $attributeCursor     = new Cursor($attributeExpression);
59
60
        /** @var array<string, mixed> $attributes */
61 90
        $attributes = [];
62 90
        while ($attribute = \trim((string) $attributeCursor->match('/^' . self::SINGLE_ATTRIBUTE . '/i'))) {
63 90
            if ($attribute[0] === '#') {
64 36
                $attributes['id'] = \substr($attribute, 1);
65
66 36
                continue;
67
            }
68
69 64
            if ($attribute[0] === '.') {
70 20
                $attributes['class'][] = \substr($attribute, 1);
71
72 20
                continue;
73
            }
74
75 52
            $parts = \explode('=', $attribute, 2);
76 52
            if (\count($parts) === 1) {
77 10
                $attributes[$attribute] = true;
78 10
                continue;
79
            }
80
81
            /** @psalm-suppress PossiblyUndefinedArrayOffset */
82 48
            [$name, $value] = $parts;
83
84 48
            $first = $value[0];
85 48
            $last  = \substr($value, -1);
86 48
            if (($first === '"' && $last === '"') || ($first === "'" && $last === "'") && \strlen($value) > 1) {
87 30
                $value = \substr($value, 1, -1);
88
            }
89
90 48
            if (\strtolower(\trim($name)) === 'class') {
91
                foreach (\array_filter(\explode(' ', \trim($value))) as $class) {
92
                    $attributes['class'][] = $class;
93
                }
94
            } else {
95 48
                $attributes[\trim($name)] = \trim($value);
96
            }
97
        }
98
99 90
        if (isset($attributes['class'])) {
100 20
            $attributes['class'] = \implode(' ', (array) $attributes['class']);
101
        }
102
103 90
        return $attributes;
104
    }
105
106
    /**
107
     * @param Node|array<string, mixed> $attributes1
108
     * @param Node|array<string, mixed> $attributes2
109
     *
110
     * @return array<string, mixed>
111
     */
112 48
    public static function mergeAttributes($attributes1, $attributes2): array
113
    {
114 48
        $attributes = [];
115 48
        foreach ([$attributes1, $attributes2] as $arg) {
116 48
            if ($arg instanceof Node) {
117 28
                $arg = $arg->data->get('attributes');
118
            }
119
120
            /** @var array<string, mixed> $arg */
121 48
            $arg = (array) $arg;
122 48
            if (isset($arg['class'])) {
123 34
                if (\is_string($arg['class'])) {
124 34
                    $arg['class'] = \array_filter(\explode(' ', \trim($arg['class'])));
125
                }
126
127 34
                foreach ($arg['class'] as $class) {
128 34
                    $attributes['class'][] = $class;
129
                }
130
131 34
                unset($arg['class']);
132
            }
133
134 48
            $attributes = \array_merge($attributes, $arg);
135
        }
136
137 48
        if (isset($attributes['class'])) {
138 34
            $attributes['class'] = \implode(' ', $attributes['class']);
139
        }
140
141 48
        return $attributes;
142
    }
143
}
144