tao-test/app/generis/common/persistence/class.AdvKeyValuePersistence.php

240 lines
6.8 KiB
PHP

<?php
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2013-2017 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
*
* @author Patrick Plichart <patrick@taotesting.com>
* @author Camille Moyon <camille@taotesting.com>
* @license GPLv2
* @package generis
*
*/
class common_persistence_AdvKeyValuePersistence extends common_persistence_KeyValuePersistence
{
/**
* Set all $fields of a $key
* If one of field values is large, a map is created into storage, and reference map is the new value of the field/
*
* @param $key
* @param $fields
* @return bool
*/
public function hmSet($key, $fields)
{
if ($this->hasMaxSize()) {
foreach ($fields as $field => $value) {
try {
if ($this->isLarge($value)) {
$fields[$field] = $this->setLargeValue($this->getMappedKey($key, $field), $value, 0, false);
}
} catch (common_Exception $e) {
common_Logger::w('Max size value is misconfigured: ' . $e->getMessage());
}
}
}
return $this->getDriver()->hmSet($key, $fields);
}
/**
* Check if a $field exists for a given $key.
* Mapped $key will be ignored
*
* @param $key
* @param $field
* @return bool
*/
public function hExists($key, $field)
{
if ($this->isMappedKey($key) || $this->isMappedKey($field)) {
return false;
}
return (bool) $this->getDriver()->hExists($key, $field);
}
/**
* Fill a $key $field with the given value
* Check if old value is a map to delete all references
* Check if value is not too large, otherwise create a map
*
* @param $key
* @param $field
* @param $value
* @return mixed
* @throws common_Exception If the the size is misconfigured
*/
public function hSet($key, $field, $value)
{
if (!$this->hasMaxSize()) {
return $this->getDriver()->hSet($key, $field, $value);
}
if ($this->isLarge($value)) {
$value = $this->setLargeValue($this->getMappedKey($key, $field), $value, 0, false);
}
$oldValue = $this->getDriver()->hGet($key, $field);
if ($this->isSplit($oldValue)) {
$this->deleteMappedKey($field, $oldValue);
}
return $this->getDriver()->hSet($key, $field, $value);
}
/**
* Get the value of $field under $key
* Mapped key will be ignored
* Check if value is a map, in case of yes, join values
*
* @param $key
* @param $field
* @return bool|mixed|string
*/
public function hGet($key, $field)
{
if ($this->hasMaxSize()) {
if ($this->isMappedKey($key) || $this->isMappedKey($field)) {
return false;
}
}
$value = $this->getDriver()->hGet($key, $field);
if ($this->hasMaxSize()) {
if ($this->isSplit($value)) {
$value = $this->join($this->getMappedKey($key, $field), $value);
}
}
return $value;
}
/**
* Get old $field of a $key
* If one of values is a map, join related values
*
* @param $key
* @return array
*/
public function hGetAll($key)
{
$fields = $this->getDriver()->hGetAll($key);
if (empty($fields)) {
return $fields;
}
if ($this->hasMaxSize()) {
foreach ($fields as $field => $value) {
if ($this->isSplit($value)) {
$fields[$field] = $this->join($this->getMappedKey($key, $field), $value);
}
}
}
return $fields;
}
/**
* Deletes $field value.
*
* @param string $key
* @param string $field
* @return bool
*/
public function hDel($key, $field): bool
{
if (!$this->hasMaxSize()) {
return $this->getDriver()->hDel($key, $field);
}
if ($this->isMappedKey($key)) {
return false;
}
$success = true;
$value = $this->getDriver()->hGet($key, $field);
if ($this->isSplit($value)) {
$success = $success && $this->deleteMappedKey($key, $value);
}
return $success && $this->getDriver()->hDel($key, $field);
}
/**
* Get a list of existing $keys
* Mapped will be ignored
*
* IMPORTANT: Never use this method for production purposes.
* Time complexity: O(N) with N being the number of keys in the database,
* under the assumption that the key names in the database and the given pattern have limited length.
*
* @param $pattern
* @return array
*/
public function keys($pattern)
{
$keys = $this->getDriver()->keys($pattern);
if ($this->hasMaxSize()) {
foreach ($keys as $index => $key) {
if ($this->isMappedKey($key)) {
unset($keys[$index]);
}
}
}
return $keys;
}
/**
* Delete a key. If key is split, all associated mapped key are deleted too
*
* @param $key
* @return bool
*/
public function del($key)
{
if ($this->isMappedKey($key)) {
return false;
} else {
$success = true;
if ($this->hasMaxSize()) {
$fields = $this->getDriver()->hGetAll($key);
if (!empty($fields)) {
foreach ($fields as $subKey => $value) {
if ($this->isSplit($value)) {
$success = $success && $this->deleteMappedKey($subKey, $value);
}
}
}
$success = $success && $this->deleteMappedKey($key);
}
return $success && $this->getDriver()->del($key);
}
}
/**
* Get a serial to reference mapped $key
*
* @param $key
* @param $field
* @return string
*/
protected function getMappedKey($key, $field)
{
return $key . '.' . $field;
}
}