| 
<?phpdeclare(strict_types=1);
 
 namespace ParagonIE\Iaso;
 use ParagonIE\ConstantTime\Base64UrlSafe;
 
 /**
 * Class ResultSet
 * @package ParagonIE\Iaso
 */
 abstract class ResultSet implements \ArrayAccess
 {
 /**
 * @var array
 */
 protected $iasoProtectedInnerResults = [];
 
 /**
 * Convert this ResultSet to a naked array.
 * Optionally, can be performed recursively.
 *
 * @param bool $recurse
 * @return array
 */
 public function asArray(bool $recurse = false)
 {
 $array = [];
 foreach ($this->iasoProtectedInnerResults as $idx => $map) {
 list ($key, $value) = [$map['key'], $map['value']];
 if (($value instanceof ResultSet) && $recurse) {
 $value = $value->asArray($recurse);
 }
 $array[$key] = $value;
 }
 $this->sort($array);
 return $array;
 }
 
 /**
 * Does an element exist?
 *
 * @param string|int $key
 * @return bool
 */
 public function offsetExists($key)
 {
 return \array_key_exists(
 $this->getIndex($key),
 $this->iasoProtectedInnerResults
 );
 }
 
 /**
 * Get an element.
 *
 * @param string|int $key
 * @return mixed
 */
 public function offsetGet($key)
 {
 if (!$this->offsetExists($key)) {
 return null;
 }
 $idx = $this->getIndex($key);
 return $this->iasoProtectedInnerResults[$idx]['value'];
 }
 
 /**
 * Store an element.
 *
 * @param string|int $key
 * @param mixed      $value
 * @return mixed
 */
 public function offsetSet($key, $value)
 {
 $idx = $this->getIndex($key);
 return $this->iasoProtectedInnerResults[$idx] = [
 'key' => $key,
 'value' => $value
 ];
 }
 
 /**
 * Remove an entry from our collection
 *
 * @param string|int $key
 * @return void
 */
 public function offsetUnset($key)
 {
 $idx = $this->getIndex($key);
 unset($this->iasoProtectedInnerResults[$idx]);
 }
 
 /**
 * Redirect __get() to offsetGet()
 *
 * @param mixed $name
 * @return mixed
 */
 public function __get($name)
 {
 return $this->offsetGet($name);
 }
 
 /**
 * Redirect __set() to offsetSet()
 *
 * @param mixed $name
 * @param mixed $value
 * @return void
 */
 public function __set($name, $value)
 {
 $this->offsetSet($name, $value);
 }
 
 /**
 * Ensure cloned objects are behaving correctly.
 */
 public function __clone()
 {
 $newInnerResults = [];
 foreach ($this->iasoProtectedInnerResults as $idx => $map) {
 $key = $map['key'];
 $idx = $this->getIndex($key);
 $newInnerResults[$idx] = $map;
 }
 $this->iasoProtectedInnerResults = $newInnerResults;
 }
 
 /**
 * Sort keys sensibly. Depends on the exact ResultSet implementation.
 *
 * @param array $array
 * @return void
 */
 abstract protected function sort(array &$array);
 
 /**
 * Get the index to be used. Depends on the exact ResultSet implementation.
 *
 * @param string|int $key
 * @return string
 * @throws \TypeError
 */
 abstract protected function getIndex($key): string;
 
 /**
 * Get a collision-resistant hash for indexing array elements
 *
 * First half: SipHash of both the object hash and this array key
 * Second half: SipHash of just this array key
 *
 * @param string $key
 * @return string
 */
 protected function getHash(string $key): string
 {
 $leftKey = \spl_object_hash($this) . '##' . $key;
 return \rtrim(
 Base64UrlSafe::encode(
 \ParagonIE_Sodium_Compat::crypto_shorthash($leftKey, JSON::getSeed())
 ),
 '='
 );
 }
 }
 
 |