AbstractArrayTag::getExtractValueRegex()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 4
nop 0
dl 0
loc 11
ccs 9
cts 9
cp 1
crap 4
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Jasny\PhpdocParser\Tag;
6
7
use Jasny\PhpdocParser\PhpdocException;
8
9
use function Jasny\str_starts_with;
10
11
/**
12
 * Base class for array type tags
13
 */
14
abstract class AbstractArrayTag extends AbstractTag
15
{
16
    /**
17
     * @var string
18
     * @enum 'string', 'int', 'float'
19
     */
20
    protected $type;
21
22
    /**
23
     * Class constructor.
24
     *
25
     * @param string $name
26
     * @param string $type   ('string', 'int', 'float')
27
     */
28 38
    public function __construct(string $name, string $type = 'string')
29
    {
30 38
        if (!in_array($type, ['string', 'int', 'float'], true)) {
31 2
            throw new \InvalidArgumentException("Invalid type '$type'");
32
        }
33
34 36
        parent::__construct($name);
35
36 36
        $this->type = $type;
37
    }
38
39
    /**
40
     * Get the value type
41
     *
42
     * @return string  ('string', 'int', 'float')
43
     */
44 8
    public function getType(): string
45
    {
46 8
        return $this->type;
47
    }
48
49
    /**
50
     * Process the notation
51
     *
52
     * @param array  $notations
53
     * @param string $value
54
     * @return array
55
     */
56 26
    public function process(array $notations, string $value): array
57
    {
58 26
        if ($value === '') {
59 2
            $notations[$this->name] = [];
60
61 2
            return $notations;
62
        }
63
64 24
        $itemString = $this->stripParentheses($value);
65
66 24
        $items = $this->splitValue($itemString);
67
68
        try {
69 22
            $array = $this->toArray($items);
70 6
        } catch (PhpdocException $exception) {
71 4
            throw new PhpdocException(
72 4
                "Failed to parse '@{$this->name} {$value}': " . $exception->getMessage(),
73 4
                0,
74 4
                $exception
75 4
            );
76
        }
77
78 16
        $notations[$this->name] = $array;
79
80 16
        return $notations;
81
    }
82
83
    /**
84
     * Strip parentheses from value
85
     *
86
     * @param string $value
87
     * @return null|string|string[]
88
     */
89 24
    protected function stripParentheses(string $value)
90
    {
91 24
        return str_starts_with($value, '(')
92 4
            ? preg_replace('/^\(((?:"(?:[^"]++|\\\\.)*"|\'(?:[^\']++|\\\\.)*\'|[^\)]++|\))*)\).*$/', '$1', $value)
93 24
            : $value;
94
    }
95
96
    /**
97
     * Split value into items.
98
     *
99
     * @param string $value
100
     * @return array
101
     */
102
    abstract protected function splitValue(string $value): array;
103
104
105
    /**
106
     * Get regular expression to extract the value
107
     *
108
     * @return string
109
     * @throws \UnexpectedValueException
110
     */
111 22
    protected function getExtractValueRegex(): string
112
    {
113 22
        switch ($this->type) {
114 22
            case 'string':
115 12
                return '/^\s*(["\']?)(?<value>.*)\1\s*$/';
116 10
            case 'int':
117 4
                return '/^\s*(?<value>[\-+]?\d+)\s*$/';
118 6
            case 'float':
119 4
                return '/^\s*(?<value>[\-+]?\d+(?:\.\d+)?(?:e\d+)?)\s*$/';
120
            default:
121 2
                throw new \UnexpectedValueException("Unknown type '$this->type'");
122
        }
123
    }
124
125
    /**
126
     * Process matched items.
127
     *
128
     * @param array $items
129
     * @return array
130
     */
131 22
    protected function toArray(array $items): array
132
    {
133 22
        $result = [];
134
135 22
        $regex = $this->getExtractValueRegex();
136
137 20
        foreach ($items as $key => $item) {
138 20
            if (!preg_match($regex, $item, $matches)) {
139 4
                throw new PhpdocException("invalid value '" . addcslashes(trim($item), "'") . "'");
140
            }
141
142 20
            $value = $matches['value'];
143 20
            settype($value, $this->type);
144
145 20
            if (is_string($key)) {
146 9
                $key = trim($key, '\'"');
147
            }
148
149 20
            $result[$key] = $value;
150
        }
151
152 16
        return $result;
153
    }
154
}
155