onDuplicateAction; if (func_num_args() && $action !== 'error' && $action !== 'ignore' && $action !== 'replace') { throw new InvalidArgumentException("Invalid onDuplicate action '" . $action . "'. Expected: 'error', 'ignore' or 'replace'"); } $this->onDuplicateAction = $action; return $old; } //========================================================================== // Overridable methods //========================================================================== /** * Return the exception that is thrown when creating an item using a key that already exists * * @param string $key Item's key * @return RuntimeException */ protected function getAlreadyExistsException($key) { return new RuntimeException("Item '" . $key . "' already exists"); } /** * Return the exception that is thrown when accessing an item that does not exist * * @param string $key Item's key * @return RuntimeException */ protected function getNotExistException($key) { return new RuntimeException("Item '" . $key . "' does not exist"); } /** * Normalize an item's key * * This method can be overridden to implement keys normalization or implement constraints * * @param string $key Original key * @return string Normalized key */ public function normalizeKey($key) { return $key; } /** * Normalize a value for storage * * This method can be overridden to implement value normalization * * @param mixed $value Original value * @return mixed Normalized value */ public function normalizeValue($value) { return $value; } //========================================================================== // Items access/manipulation //========================================================================== /** * Add an item to this collection * * NOTE: relies on exists() to check the key for invalid values and on set() to normalize it * * @param string $key Item's key * @param mixed $value Item's value * @return mixed Normalized value */ public function add($key, $value = null) { // Test whether this key is already in use if ($this->exists($key)) { // If the action is "ignore" we return the old value, if it's "error" we throw an // exception. Otherwise, we keep going and replace the value if ($this->onDuplicateAction === 'ignore') { return $this->get($key); } elseif ($this->onDuplicateAction === 'error') { throw $this->getAlreadyExistsException($key); } } return $this->set($key, $value); } /** * Test whether a given value is present in this collection * * @param mixed $value Original value * @return bool Whether the normalized value was found in this collection */ public function contains($value) { return in_array($this->normalizeValue($value), $this->items); } /** * Delete an item from this collection * * @param string $key Item's key * @return void */ public function delete($key) { try { $key = $this->normalizeKey($key); unset($this->items[$key]); } catch (InvalidArgumentException $e) { // Do nothing } } /** * Test whether an item of given key exists * * @param string $key Item's key * @return bool Whether this key exists in this collection */ public function exists($key) { try { $key = $this->normalizeKey($key); } catch (InvalidArgumentException $e) { return false; } return array_key_exists($key, $this->items); } /** * Return a value from this collection * * @param string $key Item's key * @return mixed Normalized value */ public function get($key) { if (!$this->exists($key)) { throw $this->getNotExistException($key); } $key = $this->normalizeKey($key); return $this->items[$key]; } /** * Find the index of a given value * * Will return the first key associated with the given value, or FALSE if the value is not found * * @param mixed $value Original value * @return mixed Index of the value, or FALSE if not found */ public function indexOf($value) { return array_search($this->normalizeValue($value), $this->items); } /** * Set and overwrite a value in this collection * * @param string $key Item's key * @param mixed $value Item's value * @return mixed Normalized value */ public function set($key, $value) { $key = $this->normalizeKey($key); $this->items[$key] = $this->normalizeValue($value); return $this->items[$key]; } //========================================================================== // ArrayAccess stuff //========================================================================== /** * @param string|integer $offset * @return bool */ public function offsetExists($offset): bool { return $this->exists($offset); } /** * @param string|integer $offset * @return mixed */ #[\ReturnTypeWillChange] public function offsetGet($offset) { return $this->get($offset); } /** * @param string|integer $offset * @param mixed $value * @return void */ public function offsetSet($offset, $value): void { $this->set($offset, $value); } /** * @param string|integer $offset * @return void */ public function offsetUnset($offset): void { $this->delete($offset); } }