graphql-php/src/Utils/MixedStore.php

250 lines
6.8 KiB
PHP
Raw Normal View History

<?php
2018-08-22 01:20:49 +02:00
declare(strict_types=1);
namespace GraphQL\Utils;
2018-09-26 10:55:09 +02:00
use ArrayAccess;
2018-08-22 01:20:49 +02:00
use GraphQL\Type\Definition\EnumValueDefinition;
2018-09-26 10:55:09 +02:00
use InvalidArgumentException;
use SplObjectStorage;
2018-08-22 01:20:49 +02:00
use function array_key_exists;
use function array_search;
use function array_splice;
use function is_array;
use function is_float;
use function is_int;
use function is_object;
use function is_string;
/**
* Similar to PHP array, but allows any type of data to act as key (including arrays, objects, scalars)
*
* Note: unfortunately when storing array as key - access and modification is O(N)
* (yet this should be really rare case and should be avoided when possible)
*/
2018-09-26 10:55:09 +02:00
class MixedStore implements ArrayAccess
{
2018-08-22 01:20:49 +02:00
/** @var EnumValueDefinition[] */
private $standardStore;
2018-08-22 01:20:49 +02:00
/** @var mixed[] */
private $floatStore;
2018-09-26 10:55:09 +02:00
/** @var SplObjectStorage */
private $objectStore;
2018-08-22 01:20:49 +02:00
/** @var callable[] */
private $arrayKeys;
2018-08-22 01:20:49 +02:00
/** @var EnumValueDefinition[] */
private $arrayValues;
2018-08-22 01:20:49 +02:00
/** @var callable[] */
private $lastArrayKey;
2018-08-22 01:20:49 +02:00
/** @var mixed */
private $lastArrayValue;
2018-08-22 01:20:49 +02:00
/** @var mixed */
private $nullValue;
2018-08-22 01:20:49 +02:00
/** @var bool */
private $nullValueIsSet;
2018-08-22 01:20:49 +02:00
/** @var mixed */
private $trueValue;
2018-08-22 01:20:49 +02:00
/** @var bool */
private $trueValueIsSet;
2018-08-22 01:20:49 +02:00
/** @var mixed */
private $falseValue;
2018-08-22 01:20:49 +02:00
/** @var bool */
private $falseValueIsSet;
public function __construct()
{
2018-08-22 01:20:49 +02:00
$this->standardStore = [];
$this->floatStore = [];
2018-09-26 10:55:09 +02:00
$this->objectStore = new SplObjectStorage();
2018-08-22 01:20:49 +02:00
$this->arrayKeys = [];
$this->arrayValues = [];
$this->nullValueIsSet = false;
$this->trueValueIsSet = false;
$this->falseValueIsSet = false;
}
/**
* Whether a offset exists
2018-09-26 10:55:09 +02:00
*
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
2018-09-26 10:55:09 +02:00
*
* @param mixed $offset <p>
* An offset to check for.
* </p>
2018-09-26 10:55:09 +02:00
*
2018-08-22 01:20:49 +02:00
* @return bool true on success or false on failure.
* </p>
* <p>
* The return value will be casted to boolean if non-boolean was returned.
*/
public function offsetExists($offset)
{
2018-08-22 01:20:49 +02:00
if ($offset === false) {
return $this->falseValueIsSet;
}
2018-08-22 01:20:49 +02:00
if ($offset === true) {
return $this->trueValueIsSet;
}
if (is_int($offset) || is_string($offset)) {
return array_key_exists($offset, $this->standardStore);
}
if (is_float($offset)) {
return array_key_exists((string) $offset, $this->floatStore);
}
if (is_object($offset)) {
return $this->objectStore->offsetExists($offset);
}
if (is_array($offset)) {
foreach ($this->arrayKeys as $index => $entry) {
if ($entry === $offset) {
2018-08-22 01:20:49 +02:00
$this->lastArrayKey = $offset;
$this->lastArrayValue = $this->arrayValues[$index];
2018-08-22 01:20:49 +02:00
return true;
}
}
}
2018-08-22 01:20:49 +02:00
if ($offset === null) {
return $this->nullValueIsSet;
}
2018-08-22 01:20:49 +02:00
return false;
}
/**
* Offset to retrieve
2018-09-26 10:55:09 +02:00
*
* @link http://php.net/manual/en/arrayaccess.offsetget.php
2018-09-26 10:55:09 +02:00
*
* @param mixed $offset <p>
* The offset to retrieve.
* </p>
2018-09-26 10:55:09 +02:00
*
* @return mixed Can return all value types.
*/
public function offsetGet($offset)
{
2018-08-22 01:20:49 +02:00
if ($offset === true) {
return $this->trueValue;
}
2018-08-22 01:20:49 +02:00
if ($offset === false) {
return $this->falseValue;
}
if (is_int($offset) || is_string($offset)) {
return $this->standardStore[$offset];
}
if (is_float($offset)) {
2018-08-22 01:20:49 +02:00
return $this->floatStore[(string) $offset];
}
if (is_object($offset)) {
return $this->objectStore->offsetGet($offset);
}
if (is_array($offset)) {
// offsetGet is often called directly after offsetExists, so optimize to avoid second loop:
if ($this->lastArrayKey === $offset) {
return $this->lastArrayValue;
}
foreach ($this->arrayKeys as $index => $entry) {
if ($entry === $offset) {
return $this->arrayValues[$index];
}
}
}
2018-08-22 01:20:49 +02:00
if ($offset === null) {
return $this->nullValue;
}
2018-08-22 01:20:49 +02:00
return null;
}
/**
* Offset to set
2018-09-26 10:55:09 +02:00
*
* @link http://php.net/manual/en/arrayaccess.offsetset.php
2018-09-26 10:55:09 +02:00
*
* @param mixed $offset <p>
* The offset to assign the value to.
* </p>
2018-08-22 01:20:49 +02:00
* @param mixed $value <p>
* The value to set.
* </p>
2018-09-26 10:55:09 +02:00
*
* @return void
*/
public function offsetSet($offset, $value)
{
2018-08-22 01:20:49 +02:00
if ($offset === false) {
$this->falseValue = $value;
$this->falseValueIsSet = true;
2018-08-22 01:20:49 +02:00
} elseif ($offset === true) {
$this->trueValue = $value;
$this->trueValueIsSet = true;
2018-08-22 01:20:49 +02:00
} elseif (is_int($offset) || is_string($offset)) {
$this->standardStore[$offset] = $value;
2018-08-22 01:20:49 +02:00
} elseif (is_float($offset)) {
$this->floatStore[(string) $offset] = $value;
} elseif (is_object($offset)) {
$this->objectStore[$offset] = $value;
2018-08-22 01:20:49 +02:00
} elseif (is_array($offset)) {
$this->arrayKeys[] = $offset;
$this->arrayValues[] = $value;
2018-08-22 01:20:49 +02:00
} elseif ($offset === null) {
$this->nullValue = $value;
$this->nullValueIsSet = true;
} else {
2018-09-26 10:55:09 +02:00
throw new InvalidArgumentException('Unexpected offset type: ' . Utils::printSafe($offset));
}
}
/**
* Offset to unset
2018-09-26 10:55:09 +02:00
*
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
2018-09-26 10:55:09 +02:00
*
* @param mixed $offset <p>
* The offset to unset.
* </p>
2018-09-26 10:55:09 +02:00
*
* @return void
*/
public function offsetUnset($offset)
{
2018-08-22 01:20:49 +02:00
if ($offset === true) {
$this->trueValue = null;
$this->trueValueIsSet = false;
2018-08-22 01:20:49 +02:00
} elseif ($offset === false) {
$this->falseValue = null;
$this->falseValueIsSet = false;
2018-08-22 01:20:49 +02:00
} elseif (is_int($offset) || is_string($offset)) {
unset($this->standardStore[$offset]);
2018-08-22 01:20:49 +02:00
} elseif (is_float($offset)) {
unset($this->floatStore[(string) $offset]);
} elseif (is_object($offset)) {
$this->objectStore->offsetUnset($offset);
2018-08-22 01:20:49 +02:00
} elseif (is_array($offset)) {
$index = array_search($offset, $this->arrayKeys, true);
2018-08-22 01:20:49 +02:00
if ($index !== false) {
array_splice($this->arrayKeys, $index, 1);
array_splice($this->arrayValues, $index, 1);
}
2018-08-22 01:20:49 +02:00
} elseif ($offset === null) {
$this->nullValue = null;
$this->nullValueIsSet = false;
}
}
}