/** * 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); }
/** * 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; }
/** * {@inheritdoc} */ public function withPort($port) { \Jivoo\Assume::that($port > 0 and $port < 65535); $uri = clone $this; $uri->port = $port; return $uri; }
/** * 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); }
/** * * @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)); }
/** * 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)); }
/** * Read definitions from a namespace. * * @param string $namespace * Namespace of schema classes. * @param string $dir * Location of schema classes. * @return DatabaseDefinitionBuilder Database schema. */ public function readDefinition($namespace, $dir) { $definition = new DatabaseDefinitionBuilder(); Assume::that(is_dir($dir)); $files = scandir($dir); if ($files !== false) { foreach ($files as $file) { $split = explode('.', $file); if (isset($split[1]) and $split[1] == 'php') { $class = rtrim($namespace, '\\') . '\\' . $split[0]; $definition->addDefinition($split[0], $class::getDefinition()); } } } return $definition; }
/** * 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(); } }
/** * 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']); }