/** * * @param string $expression * @return ParseInput */ public static function lex($expression, $vars = array()) { $lexer = new RegexLexer(true, 'i'); $lexer->is = 'is'; $lexer->not = 'not'; $lexer->bool = 'true|false'; $lexer->null = 'null'; $lexer->operator = 'like|in|!=|<>|>=|<=|!<|!>|=|<|>|and|or'; $lexer->dot = '\\.'; $lexer->name = '[a-z][a-z0-9]*'; $lexer->model = '\\{(.+?)\\}'; $lexer->modelPlaceholder = '%(model|m)'; $lexer->field = '\\[(.+?)\\]'; $lexer->fieldPlaceholder = '%(column|field|c)'; $lexer->number = '-?(0|[1-9]\\d*)(\\.\\d+)?([eE][+-]?\\d+)?'; $lexer->string = '"((?:[^"\\\\]|\\\\.)*)"'; $lexer->placeholder = '((\\?)|%([a-z_\\\\]+))(\\(\\))?'; $lexer->map('model', function ($value, $matches) { return $matches[1]; }); $lexer->map('field', function ($value, $matches) { return $matches[1]; }); $lexer->map('number', function ($value) { if (strpos($value, '.') !== false or stripos($value, 'e') !== false) { return new Literal(DataType::float(), floatval($value)); } else { return new Literal(DataType::integer(), intval($value)); } }); $lexer->mapType('number', 'literal'); $lexer->map('string', function ($value, $matches) { return new Literal(DataType::text(), stripslashes($matches[1])); }); $lexer->mapType('string', 'literal'); $lexer->map('bool', function ($value) { return new Literal(DataType::boolean(), strtolower($value) == 'true'); }); $lexer->mapType('bool', 'literal'); $lexer->map('model', function ($value, $matches) { return $matches[1]; }); $lexer->map('field', function ($value, $matches) { return $matches[1]; }); $i = 0; $lexer->map('modelPlaceholder', function ($value, $matches) use(&$i, $vars) { $value = $vars[$i]; $i++; if (!is_string($value)) { Assume::that($value instanceof Model); $value = $value->getName(); } return $value; }); $lexer->mapType('modelPlaceholder', 'model'); $lexer->map('fieldPlaceholder', function ($value, $matches) use(&$i, $vars) { $value = $vars[$i]; $i++; Assume::that(is_string($value)); return $value; }); $lexer->mapType('fieldPlaceholder', 'field'); $lexer->map('placeholder', function ($value, $matches) use(&$i, $vars) { $value = $vars[$i]; $i++; $type = null; if (isset($matches[3])) { if ($matches[3] == '_') { if (!is_string($value)) { Assume::that($value instanceof DataType); $value = $value->placeholder; } $matches[3] = ltrim($value, '%'); $value = $vars[$i]; $i++; } if ($matches[3] == 'e' or $matches[3] == 'expr' or $matches[3] == 'expression') { Assume::that($value instanceof Expression); return $value; } if ($matches[3] != '()') { $type = DataType::fromPlaceholder($matches[3]); } } if (!isset($type)) { $type = DataType::detectType($value); } if (isset($matches[4]) or isset($matches[3]) and $matches[3] == '()') { Assume::isArray($value); foreach ($value as $key => $v) { $value[$key] = $v; } return new ArrayLiteral($type, $value); } return new Literal($type, $value); }); $lexer->mapType('placeholder', 'literal'); return new ParseInput($lexer($expression)); }
/** * Substitute and encode variables in an expression. * * Placeholders (see also {@see DataType::fromPlaceHolder()}: * <code> * true // Boolean true * false // Boolean false * {AnyModelName} // A model name * [anyFieldName] // A column/field name * "any string" // A string * ? // Any scalar value. * %e %expr %expression // A subexpression (instance of {@see Expression}) * %m %model // A table/model object or name * %c %column %field // A column/field name * %_ // A placeholder placeholder, can also be a type, e.g. where(..., 'id = %_', $type, $value) * %i %int %integer // An integer value * %f %float // A floating point value * %s %str %string // A string * %t $text // Text * %b %bool %boolean // A boolean value * %date // A date value * %d %datetime // A date/time value * %n %bin %binary // A binary object * %AnyEnumClassName // An enum value of that class * %anyPlaceholder() // A tuple of values * </code> * * @param string|Condition $format * Expression format, use placeholders instead of values. * @param mixed[] $vars * List of values to replace placeholders with. * @param Quoter $quoter * Quoter object for quoting identifieres and literals. * @return string The interpolated expression. */ public static function interpolate($format, $vars, Quoter $quoter) { if ($format instanceof self) { return $format->toString($quoter); } Assume::isString($format); $boolean = DataType::boolean(); $true = $quoter->quoteLiteral($boolean, true); $false = $quoter->quoteLiteral($boolean, false); $format = preg_replace('/\\btrue\\b/i', $true, $format); $format = preg_replace('/\\bfalse\\b/i', $false, $format); $string = DataType::text(); $format = preg_replace_callback('/"((?:[^"\\\\]|\\\\.)*)"|\\{(.+?)\\}|\\[(.+?)\\]/', function ($matches) use($quoter, $string) { if (isset($matches[3])) { return $quoter->quoteField($matches[3]); } elseif (isset($matches[2])) { return $quoter->quoteModel($matches[2]); } else { return $quoter->quoteLiteral($string, stripslashes($matches[1])); } }, $format); $i = 0; return preg_replace_callback('/((\\?)|%([a-z_\\\\]+))(\\(\\))?/i', function ($matches) use($vars, &$i, $quoter) { $value = $vars[$i]; $i++; $type = null; if (isset($matches[3])) { if ($matches[3] == '_') { if (!is_string($value)) { Assume::that($value instanceof DataType); $value = $value->placeholder; } $matches[3] = ltrim($value, '%'); $value = $vars[$i]; $i++; } if ($matches[3] == 'e' or $matches[3] == 'expr' or $matches[3] == 'expression') { Assume::that($value instanceof Expression); return '(' . $value->toString($quoter) . ')'; } if ($matches[3] == 'm' or $matches[3] == 'model') { if (!is_string($value)) { Assume::that($value instanceof Model); $value = $value->getName(); } return $quoter->quoteModel($value); } if ($matches[3] == 'c' or $matches[3] == 'column' or $matches[3] == 'field') { Assume::isString($value); return $quoter->quoteField($value); } if ($matches[3] != '()') { $type = DataType::fromPlaceholder($matches[3]); } } if (!isset($type)) { $type = DataType::detectType($value); } if (isset($matches[4]) or isset($matches[3]) and $matches[3] == '()') { Assume::isArray($value); foreach ($value as $key => $v) { $value[$key] = $quoter->quoteLiteral($type, $v); } return '(' . implode(', ', $value) . ')'; } return $quoter->quoteLiteral($type, $value); }, $format); }
/** * Set the value of a property. * * @param string $property * Property name. * @param mixed $value * Value. * @throws InvalidPropertyException If property undefined. */ public function __set($property, $value) { switch ($property) { case 'defaults': if ($value instanceof Document) { $value = $value->toArray(); } Assume::isArray($value); $array = $value; foreach ($array as $key => $value) { $this->setDefault($key, $value); } return; case 'override': if ($value instanceof Document) { $value = $value->toArray(); } Assume::isArray($value); $array = $value; foreach ($array as $key => $value) { $this->setRecursive($key, $value); } return; } throw new InvalidPropertyException('Invalid property: ' . $property); }
public static function sort($data, $field, $descending = false, $assoc = true) { Assume::isArray($data); $func = function (array $a, array $b) use($field, $descending) { if ($a[$field] == $b[$field]) { return 0; } if ($descending) { if (is_numeric($a[$field])) { return $b[$field] - $a[$field]; } return strcmp($b[$field], $a[$field]); } else { if (is_numeric($a[$field])) { return $a[$field] - $b[$field]; } return strcmp($a[$field], $b[$field]); } }; if ($assoc) { uasort($data, $func); } else { usort($data, $func); } return $data; }
/** * Validate a route. * * @param string|array|Route|HasRoute $route A route array, string, or * object. * @return Route\Route Validated route. * @throws Route\RouteError If the route is invalid. * @throws \Jivoo\InvalidArgumentException If `$route` is not a recognized * type. */ public function validate($route) { if ($route instanceof Route\Route) { return $route; } if ($route instanceof Route\HasRoute) { return $this->validate($route->getRoute()); } if (is_callable($route)) { return new Route\CallableRoute($route); } if (is_string($route)) { if ($route == '') { $route = $this->findMatch([], 'GET'); if (!isset($route)) { throw new Route\RouteException('No root route'); } return $route; } else { if (preg_match('/^([a-zA-Z0-9\\.\\-+]+):/', $route, $matches) === 1) { $prefix = $matches[1]; if (isset($this->schemePrefixes[$prefix])) { $scheme = $this->schemePrefixes[$prefix]; return $scheme->fromString($route); } throw new Route\RouteException('Unknown route scheme: ' . $prefix); } // TODO: use current scheme .. e.g. 'action:' if in a controller throw new Route\RouteException('Missing route scheme'); } } \Jivoo\Assume::isArray($route); $default = ['parameters' => [], 'query' => [], 'fragment' => '', 'mergeQuery' => false]; $scheme = null; $parameters = []; foreach ($route as $key => $value) { if (is_int($key)) { $parameters[] = $value; } elseif ($key == 'parameters') { $parameters = array_merge($parameters, $value); } elseif (in_array($key, ['query', 'fragment', 'mergeQuery'])) { $default[$key] = $value; } elseif (isset($this->schemeKeys[$key])) { $default[$key] = $value; if (!isset($scheme)) { $scheme = $this->schemeKeys[$key]; } } else { throw new Route\RouteException('Undefined key in route: ' . $key); } } $route = $default; if (count($parameters)) { $route['parameters'] = $parameters; } if ($route['mergeQuery']) { $query = []; if (isset($this->request)) { $query = $this->request->getQueryParams(); } if (isset($route['query'])) { $query = merge_array($query, $route['query']); } $route['query'] = $query; } unset($route['mergeQuery']); if (isset($scheme)) { return $scheme->fromArray($route); } if (!isset($this->route)) { throw new Route\RouteException('Unknown route scheme'); } $copy = $this->route; if (isset($route['parameters'])) { $copy = $copy->withParameters($route['parameters']); } if (isset($route['query'])) { $copy = $copy->withQuery($route['query']); } if (isset($route['fragment'])) { $copy = $copy->withFragment($route['fragment']); } return $copy; }