/** * parseInternal * @param $mapping * @param $until * @return void * @throws CatchableException * @throws \Exception */ private function parseInternal($mapping, $until) { $mappingLength = strlen($mapping); $currentMapping = ''; $inQuote = false; $closingParenthesisFound = false; for ($i = 0; $i < $mappingLength; $i++) { $char = $mapping[$i]; $atEnd = $i + 1 == $mappingLength; $nextChar = $atEnd ? null : $mapping[$i + 1]; if ($char == '\\') { if ($atEnd) { $this->throwMappingException('An escape character "\\" must either be escaped or not at the end of a mapping', $mapping, $i); } //Add the next character since we are escaping it $currentMapping .= $nextChar; $i++; //Skip the next character continue; } if ($inQuote) { if ($char == '"') { if (!($atEnd || $nextChar == '+' || $nextChar == '.' || $until == self::UNTIL_CLOSING_COMMA_OR_CLOSING_PARENTHESIS && ($nextChar == ')' || $nextChar == ','))) { $this->throwMappingException('A closing quote "\\"" must either be followed by the end of the mapping, a closing parenthesis, a plus, a "," or a .', $mapping, $i); } $inQuote = false; if (is_numeric($currentMapping)) { $object = new Number($currentMapping); } else { $object = new String($currentMapping); } $this->addToCurrentMethod(new Method(new RawValue($object))); $currentMapping = ''; continue; } $currentMapping .= $char; continue; } if ($char == '"') { if ($currentMapping != '') { $this->throwMappingException('A beginning quote "\\"" must be at the beginning of a mapping', $mapping, $i); } $inQuote = true; continue; } if ($char == '(') { /** @type Method[] $parameters */ $parameters = []; $i++; //Offset for opening parenthesis $subMapping = substr($mapping, $i); if ($subMapping == '') { $this->throwMappingException('There are more opening parenthesis than closing parenthesis', $mapping, $i); } while (strlen($subMapping) > 0) { $parse = new Parser(); $parse->setOriginalMapping($this->getOriginalMapping()); $parse->parseInternal($subMapping, self::UNTIL_CLOSING_COMMA_OR_CLOSING_PARENTHESIS); $parameter = $parse->getMethods(); $isLastParameter = substr($subMapping, $parse->getLastIndex(), 1) == ')'; if (!$isLastParameter || $parse->getLastIndex() > 0) { $parameters[] = $parameter; } $i += $parse->getLastIndex() + 1; $subMapping = substr($subMapping, $parse->getLastIndex() + 1); if ($isLastParameter) { $i -= 1; break; //Found closing parenthesis } } $method = new Method($currentMapping); $method->setParameters($parameters); $method->setRawMapping($mapping); $method->setRawMappingIndex($i); $this->addToCurrentMethod($method); $currentMapping = ''; continue; } if ($char == ')') { if ($until != self::UNTIL_CLOSING_COMMA_OR_CLOSING_PARENTHESIS) { $this->throwMappingException('There are more closing parenthesis than opening parenthesis', $mapping, $i); } $closingParenthesisFound = true; $this->setLastIndex($i); break; } if ($char == ',') { if ($until != self::UNTIL_CLOSING_COMMA_OR_CLOSING_PARENTHESIS) { $this->throwMappingException('Commas are only allowed inside of parameters for methods', $mapping, $i); } $closingParenthesisFound = true; $this->setLastIndex($i); break; } if ($char == '+') { $method = new Method($currentMapping); $method->setRawMapping($mapping); $method->setRawMappingIndex($i); $this->addToCurrentMethod($method); $this->finishCurrentMethod(); //Reset mapping/object for next part $currentMapping = ''; continue; } if ($char == '.') { $method = new Method($currentMapping); $method->setRawMapping($mapping); $method->setRawMappingIndex($i); $this->addToCurrentMethod($method); $currentMapping = ''; continue; } $currentMapping .= $char; } if (!$closingParenthesisFound && $until == self::UNTIL_CLOSING_COMMA_OR_CLOSING_PARENTHESIS) { $this->throwMappingException('There are more opening parenthesis than closing parenthesis', $mapping, $i); } if ($inQuote) { $this->throwMappingException('There is no matching closing quote to an opening quote', $mapping, $mappingLength); } $method = new Method($currentMapping); $method->setRawMapping($mapping); $method->setRawMappingIndex($i); $this->addToCurrentMethod($method); $this->finishCurrentMethod(); }