@@ -27,7 +27,49 @@ class ExtractorDefinitionBuilder implements ExtractorDefinitionBuilderInterface
2727 /**
2828 * @var string
2929 */
30- protected $ type_regexp = '/^((?<name>[-_\w]+)(?:\s*\(\s*(?<param>(?-3)*|[\w \\\\]+)\s*\))?(?:\s*\|\s*(?<alt>(?-4)))?)$/ ' ;
30+ protected $ type_regexp = '/
31+ ^
32+ (
33+ (?<name>
34+ [-_\w]*
35+ )
36+ (?:
37+ \s*
38+ (?<group>
39+ \(
40+ \s*
41+ (?<param>
42+ (?-4)*
43+ |
44+ [\w \\\\]+
45+ )
46+ \s*
47+ \)
48+ )
49+ )?
50+ (?:
51+ \s*
52+ (?<arr>(?:\s*\[\s*\]\s*)+)
53+ )?
54+ (?:
55+ \s*
56+ \|
57+ \s*
58+ (?<alt>(?-6))
59+ )?
60+ )
61+ $
62+ /xi ' ;
63+
64+ protected $ type_regexp2 = '/
65+ ^
66+ (?:((\w+\b(?:\(.*\))?(?:\s*\[\s*\])?)(?:\s*\|\s*(?-1))*))
67+ |
68+ (?:\(\s*(?-2)\s*\)(?:\s*\[\s*\])?)
69+ |
70+ (\[\s*\])
71+ $
72+ /xi ' ;
3173
3274 /**
3375 * {@inheritdoc}
@@ -40,32 +82,53 @@ public function build(string $definition): ExtractorDefinitionInterface
4082 throw new ExtractorDefinitionBuilderException ('Definition must be non-empty string ' );
4183 }
4284
43- if (preg_match ($ this ->type_regexp , $ definition , $ matches )) {
44- return $ this ->buildExtractor ($ matches ['name ' ], $ matches ['param ' ] ?? null , $ matches ['alt ' ] ?? null );
85+ try {
86+ if (preg_match ($ this ->type_regexp , $ definition , $ matches )) {
87+ $ extractor = $ this ->buildExtractor ($ matches ['name ' ], $ matches ['param ' ] ?? '' , $ matches ['alt ' ] ?? '' , $ this ->getDepth ($ matches ), $ this ->hasGroups ($ matches ));
88+
89+ return $ extractor ;
90+ }
91+ } catch (ExtractorDefinitionBuilderException $ e ) {
92+ // We don't care about what specific issue we hit inside,
93+ // for API user it means that the definition is invalid
4594 }
4695
4796 throw new ExtractorDefinitionBuilderException ("Unable to parse definition: ' {$ definition }' " );
4897 }
4998
5099 /**
51- * @param string $name
100+ * @param string $name
52101 * @param null|string $param
53102 * @param null|string $alt_definitions
103+ * @param int $depth
104+ * @param bool $groups
54105 *
55106 * @return ExtractorDefinitionInterface
56107 * @throws ExtractorDefinitionBuilderException
57108 */
58- protected function buildExtractor (string $ name , ? string $ param , ? string $ alt_definitions ): ExtractorDefinitionInterface
109+ protected function buildExtractor (string $ name , string $ param , string $ alt_definitions, int $ depth , bool $ groups ): ExtractorDefinitionInterface
59110 {
60111 $ next = null ;
61112
62- if ($ param && preg_match ($ this ->type_regexp , $ param , $ matches )) {
63- $ next = $ this ->buildExtractor ($ matches ['name ' ], $ matches ['param ' ] ?? null , $ matches ['alt ' ] ?? null );
113+ if ('' !== $ param && preg_match ($ this ->type_regexp , $ param , $ matches )) {
114+ $ next = $ this ->buildExtractor ($ matches ['name ' ], $ matches ['param ' ] ?? '' , $ matches ['alt ' ] ?? '' , $ this ->getDepth ($ matches ), $ this ->hasGroups ($ matches ));
115+ }
116+
117+ if ($ name ) {
118+ $ definition = new PlainExtractorDefinition ($ name , $ next );
119+ } else {
120+ $ definition = $ next ;
121+ }
122+
123+ if ($ depth > 0 ) {
124+ $ definition = $ this ->buildArrayDefinition ($ definition , $ depth , $ groups );
64125 }
65126
66- $ definition = new PlainExtractorDefinition ($ name , $ next );
127+ if (!$ definition ) {
128+ throw new ExtractorDefinitionBuilderException ('Empty group is not allowed ' );
129+ }
67130
68- if ($ alt_definitions ) {
131+ if ('' !== $ alt_definitions ) {
69132 $ definition = $ this ->buildVariableDefinition ($ definition , $ alt_definitions );
70133 }
71134
@@ -74,7 +137,7 @@ protected function buildExtractor(string $name, ?string $param, ?string $alt_def
74137
75138 /**
76139 * @param PlainExtractorDefinitionInterface $definition
77- * @param string $alt_definitions
140+ * @param string $alt_definitions
78141 *
79142 * @return VariableExtractorDefinition
80143 * @throws ExtractorDefinitionBuilderException
@@ -83,19 +146,61 @@ protected function buildVariableDefinition(PlainExtractorDefinitionInterface $de
83146 {
84147 $ alt = [$ definition ];
85148
86- while ($ alt_definitions && preg_match ($ this ->type_regexp , $ alt_definitions , $ matches )) {
149+ while ('' !== $ alt_definitions && preg_match ($ this ->type_regexp , $ alt_definitions , $ matches )) {
87150 // build alt
88- $ alt [] = $ this ->buildExtractor ($ matches ['name ' ], $ matches ['param ' ] ?? null , null );
151+ $ alt [] = $ this ->buildExtractor ($ matches ['name ' ], $ matches ['param ' ] ?? '' , '' , $ this -> getDepth ( $ matches ), $ this -> hasGroups ( $ matches ) );
89152
90- $ alt_definitions = $ matches ['alt ' ] ?? null ;
153+ $ alt_definitions = trim ( $ matches ['alt ' ] ?? '' ) ;
91154 }
92155
93- if ($ alt_definitions ) {
156+ if ('' !== $ alt_definitions ) {
157+ // UNEXPECTED
94158 // this should not be possible, but just in case we will ever get here
95159 throw new ExtractorDefinitionBuilderException ('Invalid varying definition ' );
96160 }
97161
98162 return new VariableExtractorDefinition (...$ alt );
99163 }
100164
165+ /**
166+ * @param null|ExtractorDefinitionInterface $definition
167+ * @param int $depth
168+ * @param bool $groups
169+ *
170+ * @return ExtractorDefinitionInterface
171+ * @throws ExtractorDefinitionBuilderException
172+ */
173+ protected function buildArrayDefinition (?ExtractorDefinitionInterface $ definition , int $ depth , bool $ groups ): ExtractorDefinitionInterface
174+ {
175+ if (!$ definition && $ groups ) {
176+ throw new ExtractorDefinitionBuilderException ('Empty group is not allowed ' );
177+ }
178+
179+ while ($ depth ) {
180+ $ depth --;
181+ // arrayed definition
182+ $ definition = new PlainExtractorDefinition ('[] ' , $ definition );
183+ }
184+
185+ return $ definition ;
186+ }
187+
188+ /**
189+ * @param array $matches
190+ *
191+ * @return int
192+ */
193+ private function getDepth (array $ matches ): int
194+ {
195+ if (!isset ($ matches ['arr ' ]) || '' === $ matches ['arr ' ]) {
196+ return 0 ;
197+ }
198+
199+ return substr_count ($ matches ['arr ' ], '[ ' );
200+ }
201+
202+ private function hasGroups (array $ matches ): bool
203+ {
204+ return isset ($ matches ['group ' ]) && '' !== $ matches ['group ' ];
205+ }
101206}
0 commit comments