'generateNumber', self::TYPE_STRING => 'generateString', self::TYPE_BYTES => 'generateBytes', ); if ( !isset($mapping[$type]) ) { throw new InvalidArgumentException('The "' . $type . '" promise type is not supported.'); } $this->method = $mapping[$type]; $this->arguments = $arguments; } /** * Makes sure, that resolved value isn't already used in the given database table's column. * * @param string $prefix_or_table Unit config prefix or table name. * @param string $column Column in specified table. * * @return mixed * @throws LogicException When after 10 retries still unable to generate unique value for database. */ public function resolveForPersisting($prefix_or_table, $column) { $application =& kApplication::Instance(); if ( $application->prefixRegistred($prefix_or_table) ) { $table = $application->getUnitOption($prefix_or_table, 'TableName'); } else { $table = $prefix_or_table; } $retries = 0; do { $resolved_value = $this->resolve(); $sql = 'SELECT ' . $column . ' FROM ' . $table . ' WHERE ' . $column . ' = ' . $application->Conn->qstr($resolved_value); $found = $application->Conn->GetOne($sql) !== false; if ( $found ) { $this->resolvedValue = null; $retries++; } } while ( $found && $retries < 10 ); if ( $found ) { throw new LogicException(sprintf( 'Unable to generate unique value for "%s" column with current generator configuration.', $table . '.' . $column )); } return $resolved_value; } /** * Resolves a promise. * * @return mixed */ public function resolve() { if ( !$this->isResolved() ) { $this->resolvedValue = call_user_func_array(array($this, $this->method), $this->arguments); } if ( !$this->asSignature ) { return $this->resolvedValue; } $application =& kApplication::Instance(); /** @var SecurityEncrypter $encrypter */ $encrypter = $application->recallObject('SecurityEncrypter'); return $encrypter->createSignature( $this->resolvedValue, $this->signatureRawOutput, $this->signatureKeyOverride ); } /** * Configures promise to return signature of a resolved value. * * @param boolean $raw_output Return raw signature value during resolving. * @param string|null $key_override Alternative key used for creating signature of resolved value. * * @return self */ public function asSignature($raw_output = false, $key_override = null) { $this->asSignature = true; $this->signatureRawOutput = $raw_output; $this->signatureKeyOverride = $key_override; return $this; } /** * Configures promise to return resolved value as-is. * * @return self */ public function asValue() { $this->asSignature = false; $this->signatureRawOutput = false; $this->signatureKeyOverride = null; return $this; } /** * Generate a random string of specified length using supplied character list. * * @param integer $length The length of the generated string. * @param mixed $characters List of characters to use. * * @return string */ protected function generateString($length, $characters) { $ret = ''; $max_index = mb_strlen($characters) - 1; for ( $i = 0; $i < $length; $i++ ) { $ret .= mb_substr($characters, $this->generateNumber(0, $max_index), 1); } return $ret; } /** * Generates a random number. * * @param integer $min Smallest value. * @param integer $max Largest value. * * @return integer */ protected function generateNumber($min, $max) { return random_int($min, $max); } /** * Generates random bytes. * * @param integer $length Raw result length. * @param boolean $raw_output Return raw result. * * @return string */ protected function generateBytes($length = 16, $raw_output = false) { $ret = random_bytes($length); return $raw_output ? $ret : bin2hex($ret); } /** * Determines if promise was already resolved. * * @return boolean */ protected function isResolved() { return $this->resolvedValue !== null; } /** * Returns promise resolved value casted to string. * * @return string */ public function __toString() { return (string)$this->resolve(); } }