/** * Construct stream. * * @param string|resource $streamOrPath A resource or path. * @param string $mode Stream mode, see {@see fopen}. * @throws InvalidArgumentException If the stream can not be opened. */ public function __construct($streamOrPath, $mode = 'rb') { if (is_resource($streamOrPath)) { $this->stream = $streamOrPath; } else { \Jivoo\Assume::isString($streamOrPath); $error = ErrorHandler::detect(function () use($streamOrPath, $mode) { $this->stream = fopen($streamOrPath, $mode); }); if ($error or $this->stream === false) { throw new InvalidArgumentException('Could not open stream', 0, $error); } } }
/** * Get values of an enum class. * * @param string $class * Class name. * @throws InvalidEnumException If the class invalid or does not contain * constants. * @return string[] Enum values. */ public static function getValues($class = null) { if (!isset($class)) { $class = get_called_class(); } if (!isset(self::$values[$class])) { if (!self::classExists($class)) { throw new InvalidEnumException('Enum class not found: ' . $class); } $class = self::$classes[$class]; Assume::isSubclassOf($class, 'Jivoo\\Models\\Enum'); $ref = new \ReflectionClass($class); self::$values[$class] = array_flip($ref->getConstants()); if (count(self::$values[$class]) < 1) { throw new InvalidEnumException('Enum type "' . $class . '" must contain at least one constant'); } } return self::$values[$class]; }
/** * Converts expiration timestamp, interval, {\DateInterval} or {\DateTime} * to a {\DateTime} or null (for no expiration date). * * @param int|\DateInterval|\DateTime|null $expiration * Timestamp or interval. * Null and the integer 0 is interpreted as 'no expiration date'. * If the integer is less than or equal to 2,592,000 (30 days), the time * is relative to the current timestamp, otherwise it is interpreted as an * absolute UNIX timestamp. * @return \DateTime|null */ public static function convertExpiration($expiration) { if ($expiration === null or $expiration === 0) { return null; } if (is_int($expiration)) { if ($expiration <= 2592000) { $expiration += time(); } return \DateTime::createFromFormat('U', $expiration); } if ($expiration instanceof \DateTime) { return $expiration; } Assume::that($expiration instanceof \DateInterval); $d = new \DateTime(); return $d->add($expiration); }
/** * {@inheritdoc} */ public function withPort($port) { \Jivoo\Assume::that($port > 0 and $port < 65535); $uri = clone $this; $uri->port = $port; return $uri; }
/** * Prepare selection, e.g. * by joining with join table. * * @param BasicSelection $selection * Input selection or null for source. * @return ReadSelection Resulting selection. */ private function prepareSelection(BasicSelection $selection = null) { if (!isset($selection)) { return $this->source; } $selection->alias($this->alias); if (isset($this->join)) { Assume::that($selection instanceof ReadSelection); $selection = $selection->leftJoin($this->join, $this->otherPrimary . '= J.' . $this->otherKey, 'J')->where('J.' . $this->thisKey . ' = ?', $this->recordId); } else { $selection = $selection->where($this->thisKey . ' = ?', $this->recordId); if ($selection instanceof SelectionBuilder) { $selection = $selection->toReadSelection(); } } if (isset($this->condition)) { $selection = $selection->where($this->condition); } return $selection; }
/** * * @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); }
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; }
/** * Construct data type. * * @param int $type * Type. * @param bool $null * Null. * @param mixed $default * Default value. * @param int $flags * Integer flags. * @param int|null $length * String length. * @throws \InvalidArgumentException When type is invalid. */ protected function __construct($type, $null = false, $default = null, $flags = 0, $length = null) { Assume::that($type >= 1 and $type <= 10); $this->type = $type; $this->length = $length; $this->default = $default; $this->null = $null; if ($type == self::INTEGER) { $this->signed = ($flags & self::UNSIGNED) == 0; $this->serial = ($flags & self::SERIAL) != 0; $this->size = $flags & 0x30; } }
/** * Translate a string containing a numeric value, e.g. * <code>$l->nget('This post has %1 comments', 'This post has %1 comment', $numcomments);</code> * * @param string $plural * Message in english (plural). * @param string $singular * Singular version of message in english. * @param int|array|\Countable $n * The integer to test, replaces the * %1-placeholder in the message. May also be an array or a {@see \Countable}, * in which case {@see count} will be called on the value. * @param mixed $vars,... * Values for additional placholders starting from %2. * @return Translated string. * @throws \Jivoo\InvalidArgumentException If $n is not an integer, an * array, or a {@see \Countable}. */ public function nget($plural, $singular, $n) { if (is_array($n) or $n instanceof \Countable) { $n = count($n); } Assume::that(is_scalar($n)); $n = intval($n); if (isset($this->messages[$plural])) { $i = intval(eval($this->pluralExpr)); $message = $this->messages[$plural][0]; if (isset($this->messages[$plural][$i])) { $message = $this->messages[$plural][$i]; } } elseif (abs($n) == 1) { $message = $singular; } else { $message = $plural; } return $this->replacePlaceholders($message, array_slice(func_get_args(), 2)); }
/** * Make a database connection. * * @param string|Document|array $name * Name of database connection. * @param DatabaseDefinition $definition * Database definition (collecton of table definitions). * @throws ConfigurationException If the $options-array does not * contain the necessary information for a connection to be made. * @throws InvalidSchemaException If one of the schema names listed * in the $schemas-parameter is unknown. * @throws ConnectionException If the connection fails. * @return LoadableDatabase A database object. */ public function connect($name, DatabaseDefinition $definition = null) { if (is_string($name)) { if (!isset($this->config[$name])) { throw new ConfigurationException('Database "' . $name . '" not configured'); } $config = $this->config->getSubset($name); } elseif (is_array($name)) { $config = new Document($name); unset($name); } else { Assume::isInstanceOf($name, 'Jivoo\\Store\\Document'); $config = $name; unset($name); } $driver = $config->get('driver', null); if (!isset($driver)) { throw new ConfigurationException('Database driver not set'); } try { $driverInfo = $this->checkDriver($driver); } catch (InvalidDriverException $e) { throw new ConnectionException('Invalid database driver: ' . $e->getMessage(), 0, $e); } foreach ($driverInfo['requiredOptions'] as $option) { if (!isset($config[$option])) { throw new ConfigurationException('Database option missing: "' . $option . '"'); } } try { $class = 'Jivoo\\Data\\Database\\Drivers\\' . $driver . '\\' . $driver . 'Database'; Assume::isSubclassOf($class, 'Jivoo\\Data\\Database\\LoadableDatabase'); if (!isset($definition)) { $definition = new DatabaseDefinitionBuilder([]); } $object = new $class($definition, $config); $object->setLogger($this->logger); if (isset($name)) { $this->connections[$name] = new DatabaseSchema($object); } return $object; } catch (ConnectionException $exception) { throw new ConnectionException('Database connection failed (' . $driver . '): ' . $exception->getMessage(), 0, $exception); } }
/** * Update a document key. * * @param string $key * The document key to access. * @param mixed $value * The value to associate with the key. */ public function set($key, $value) { Assume::that(is_scalar($key)); if (isset($this->emptySubset)) { $this->createTrueSubset(); } $oldValue = null; if (isset($this->data[$key])) { $oldValue = $this->data[$key]; } if (isset($key) and isset($value) and $key !== '') { $this->data[$key] = $value; } else { $this->data[$key] = null; } if (!$this->root->updated and $oldValue !== $value) { $this->root->updated = true; $this->root->update(); } }
/** * {@inheritdoc} */ public function saveDeferred(CacheItem $item) { if (!isset($this->data)) { $this->read(); } Assume::isInstanceOf($item, 'Jivoo\\Cache\\MutableItem'); $expiration = $item->getExpiration(); if (isset($expiration)) { $expiration = $expiration->getTimestamp(); } $this->data[$item->getKey()] = array($item->get(), $expiration); return true; }
/** * 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; }
/** * Set association. * * @param ActiveRecord $record * A record. * @param array $association * Association options. * @param ActiveRecord|Selection|ActiveRecord[] $value * New value. * @throws InvalidAssociationException If association type unknown. */ public function setAssociation(ActiveRecord $record, $association, $value) { switch ($association['type']) { case 'belongsTo': if (!isset($value)) { $this->unsetAssociation($record, $association); return; } Assume::that($value instanceof ActiveRecord); Assume::that($value->getModel() == $association['model']); $key = $association['otherKey']; $otherId = $association['model']->primaryKey; $record->{$key} = $value->{$otherId}; return; case 'hasOne': Assume::that($value instanceof ActiveRecord); Assume::that($value->getModel() == $association['model']); $this->unsetAssociation($record, $association); $key = $association['thisKey']; $id = $this->primaryKey; $value->{$key} = $record->{$id}; $value->save(); return; case 'hasMany': $key = $association['thisKey']; $id = $this->primaryKey; $idValue = $record->{$id}; if ($value instanceof Selection) { $value->set($key, $idValue)->update(); return; } if (!is_array($value)) { $value = array($value); } $this->unsetAssociation($record, $association); foreach ($value as $item) { Assume::that($item instanceof ActiveRecord); Assume::that($item->getModel() == $association['model']); $item->{$key} = $idValue; if (!$item->isNew()) { $item->save(); } } return; case 'hasAndBelongsToMany': return; } throw new InvalidAssociationException('Unknown association type: ' . $association['type']); }