/** * Parses given number of points from native database value * * @param string $native native database value * @param int $pos position * @param int $count number of points to parse * @param bool $allowSquare whether square brackets [] are allowed around points * @return array * @throws TypeConversionException */ protected function parsePoints($native, &$pos, $count, $allowSquare = false) { $hasDelimiters = $squareDelimiter = false; $char = $this->nextChar($native, $pos); if ('[' === $char) { if (!$allowSquare) { throw TypeConversionException::parsingFailed($this, "'(' or numeric value", $native, $pos); } $hasDelimiters = $squareDelimiter = true; $pos++; } elseif ('(' === $char) { $nextPos = $pos + 1; if ($pos === call_user_func(self::$strrpos, $native, '(') || '(' === $this->nextChar($native, $nextPos)) { $hasDelimiters = true; $pos++; } } $points = array(); for ($i = 0; $i < $count; $i++) { if ($i > 0) { $this->expectChar($native, $pos, ','); } $points[] = $this->point->parseInput($native, $pos); } if ($hasDelimiters) { $this->expectChar($native, $pos, $squareDelimiter ? ']' : ')'); } if ($allowSquare) { $points['open'] = $squareDelimiter; } return $points; }
protected function parseInput($native, &$pos) { $result = array(); while (false !== ($char = $this->nextChar($native, $pos))) { $key = $this->_readString($native, $pos, '=', false); $this->expectChar($native, $pos, '='); // don't use expectChar as there can be no whitespace if ('>' !== $native[$pos]) { throw TypeConversionException::parsingFailed($this, "'=>'", $native, $pos - 1); } $pos++; // skip possible whitespace before value $this->nextChar($native, $pos); $result[$key] = $this->_readString($native, $pos, ',', true); // skip one comma after the pair if (',' === $this->nextChar($native, $pos)) { $pos++; } } return $result; }
/** * Throws an Exception if next non-whitespace character in input is not the given char * * @param string $string * @param int $pos * @param string $char * @throws TypeConversionException */ protected function expectChar($string, &$pos, $char) { if ($char !== $this->nextChar($string, $pos)) { throw TypeConversionException::parsingFailed($this, "'{$char}'", $string, $pos); } $pos++; }
protected function parseInput($native, &$pos) { $result = array(); $this->expectChar($native, $pos, '{'); // Leading "{". while ('}' !== ($char = $this->nextChar($native, $pos))) { // require a comma delimiter between elements if (!empty($result)) { if (',' !== $char) { throw TypeConversionException::parsingFailed($this, "','", $native, $pos); } $pos++; $char = $this->nextChar($native, $pos); } if ('{' === $char) { // parse sub-array $result[] = $this->parseInput($native, $pos); } elseif ('"' === $char) { // quoted string if (!preg_match('/"((?>[^"\\\\]+|\\\\.)*)"/As', $native, $m, 0, $pos)) { throw TypeConversionException::parsingFailed($this, 'quoted string', $native, $pos); } $result[] = $this->_item->input(stripcslashes($m[1])); $pos += call_user_func(self::$strlen, $m[0]); } else { // zero-length string can appear only quoted if (0 === ($len = strcspn($native, ",} \t\r\n", $pos))) { throw TypeConversionException::parsingFailed($this, 'subarray, quoted or unquoted string', $native, $pos); } $v = call_user_func(self::$substr, $native, $pos, $len); $result[] = strcasecmp($v, "null") ? $this->_item->input(stripcslashes($v)) : null; $pos += $len; } } $pos++; // skip trailing "}" return $result; }
protected function parseInput($native, &$pos) { reset($this->_items); $result = array(); $unescape = array_flip($this->_escapes); $this->expectChar($native, $pos, '('); // Leading "(" while (true) { /* @var $type TypeConverter */ if (!(list($field, $type) = each($this->_items))) { // Check if we have more fields left. throw TypeConversionException::parsingFailed($this, 'end of input: no more fields left', $native, $pos); } switch ($char = $this->nextChar($native, $pos)) { case ',': case ')': // Comma or end of row instead of value: treat as NULL. $result[$field] = null; break; case '"': // Quoted string. if (!preg_match('/"((?>[^"]+|"")*)"/As', $native, $m, 0, $pos)) { throw TypeConversionException::parsingFailed($this, 'quoted string', $native, $pos); } $result[$field] = $type->input(strtr($m[1], $unescape)); $pos += call_user_func(self::$strlen, $m[0]); $char = $this->nextChar($native, $pos); break; default: // Unquoted string. $len = strcspn($native, ',)', $pos); $result[$field] = $type->input(call_user_func(self::$substr, $native, $pos, $len)); $pos += $len; $char = $this->nextChar($native, $pos); break; } switch ($char) { // Expect delimiter after value case ')': $pos++; break 2; case ',': $pos++; break; default: throw TypeConversionException::parsingFailed($this, "',' or ')'", $native, $pos); } } if (list($field, ) = each($this->_items)) { // point error at preceding ')' throw TypeConversionException::parsingFailed($this, "value for '{$field}'", $native, $pos - 1); } return $result; }
/** * Parses a native value into PHP variable from given position * * @param string $native * @param int $pos * * @return Range * @throws TypeConversionException */ protected function parseInput($native, &$pos) { $char = $this->nextChar($native, $pos); if (('e' === $char || 'E' === $char) && preg_match('/empty/Ai', $native, $m, 0, $pos)) { $pos += 5; return call_user_func(array($this->resultClass, 'createEmpty')); } if ('(' === $char || '[' === $char) { $pos++; $lowerInclusive = '[' === $char; } else { throw TypeConversionException::parsingFailed($this, '[ or (', $native, $pos); } $lower = $this->_readRangeBound($native, $pos, ',)]'); $this->expectChar($native, $pos, ','); $upper = $this->_readRangeBound($native, $pos, ',])'); if (']' === $native[$pos]) { $upperInclusive = true; $pos++; } elseif (')' === $native[$pos]) { $upperInclusive = false; $pos++; } else { throw TypeConversionException::parsingFailed($this, '] or )', $native, $pos); } return new $this->resultClass($this->_subtype->input($lower), $this->_subtype->input($upper), $lowerInclusive, $upperInclusive); }
/** * Creates a DateInterval object from 'iso_8601' format interval string * * Unlike native DateInterval::__construct() this handles negative values * and fractional seconds. Only integer values are allowed for other units. * * @param string $native * @return DateInterval * @throws TypeConversionException */ private function _parseISO8601($native) { $interval = new DateInterval('PT0S'); $pos = 1; $length = call_user_func(self::$strlen, $native); $isTime = false; while ($pos < $length) { if ('T' === $native[$pos]) { $isTime = true; $pos++; continue; } $numpos = $pos; $value = $this->getStrspn($native, '0123456789-.', $pos); if (!is_numeric($value)) { throw TypeConversionException::parsingFailed($this, 'numeric value', $native, $pos); } $unit = $native[$pos]; if ($isTime && isset($this->_iso8601TimeUnits[$unit])) { $intervalKey = $this->_iso8601TimeUnits[$unit]; } elseif (!$isTime && isset($this->_iso8601DateUnits[$unit])) { $intervalKey = $this->_iso8601DateUnits[$unit]; } else { throw TypeConversionException::parsingFailed($this, 'ISO 8601 interval unit', $native, $pos); } $pos++; if (false === strpos($value, '.')) { $interval->{$intervalKey} = (int) $value; } else { if ('s' !== $intervalKey) { throw TypeConversionException::parsingFailed($this, 'integer value', $native, $numpos); } $value = (double) $value; $interval->s = $value > 0 ? (int) floor($value) : (int) -floor(-$value); $interval->fsec = $value - $interval->s; } } return $interval; }