PSR12
This commit is contained in:
838
src/Dbjson.php
838
src/Dbjson.php
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/** DomFramework
|
||||
* @package domframework
|
||||
* @author Dominique Fournier <dominique@fournier38.fr>
|
||||
@@ -23,416 +24,457 @@ namespace Domframework;
|
||||
*/
|
||||
class Dbjson
|
||||
{
|
||||
/** The DSN of the connection */
|
||||
private $dsn = "";
|
||||
/** The database file */
|
||||
private $dbfile = "";
|
||||
/** The lock file */
|
||||
private $dbfileLock = "";
|
||||
/** The last Insert Id */
|
||||
private $lastInsertId = 0;
|
||||
/** The database content */
|
||||
private $db;
|
||||
/** The DSN of the connection */
|
||||
private $dsn = "";
|
||||
/** The database file */
|
||||
private $dbfile = "";
|
||||
/** The lock file */
|
||||
private $dbfileLock = "";
|
||||
/** The last Insert Id */
|
||||
private $lastInsertId = 0;
|
||||
/** The database content */
|
||||
private $db;
|
||||
|
||||
/** The constructor
|
||||
* @param string $dsn The DSN of the connection
|
||||
*/
|
||||
public function __construct ($dsn)
|
||||
{
|
||||
if (! function_exists ("openssl_random_pseudo_bytes"))
|
||||
throw new \Exception ("Function openssl_random_pseudo_bytes missing",
|
||||
500);
|
||||
$pos = strpos ($dsn, "://");
|
||||
if ($pos === false)
|
||||
throw new \Exception (dgettext ("domframework",
|
||||
"No DSN provided to dbjson"), 500);
|
||||
if (substr ($dsn, 0, $pos) !== "dbjson")
|
||||
throw new \Exception (dgettext ("domframework",
|
||||
"Invalid database type provided in dbjson"), 500);
|
||||
$this->dbfile = substr ($dsn, $pos+3);
|
||||
$directory = dirname ($this->dbfile);
|
||||
if (! file_exists ($directory))
|
||||
@mkdir ($directory, 0777, true);
|
||||
if (! file_exists ($directory))
|
||||
throw new \Exception (sprintf (dgettext ("domframework",
|
||||
"Directory '%s' doesn't exists"), $directory), 500);
|
||||
if (! file_exists ($this->dbfile))
|
||||
/** The constructor
|
||||
* @param string $dsn The DSN of the connection
|
||||
*/
|
||||
public function __construct($dsn)
|
||||
{
|
||||
if (! is_readable ($directory))
|
||||
throw new \Exception (sprintf (
|
||||
dgettext ("domframework",
|
||||
"Directory '%s' not writeable and dbfile '%s' not exists"),
|
||||
$directory, $this->dbfile), 500);
|
||||
touch ($this->dbfile);
|
||||
}
|
||||
if (! is_readable ($this->dbfile))
|
||||
throw new \Exception(sprintf (dgettext ("domframework",
|
||||
"File '%s' not readable"), $this->dbfile), 500);
|
||||
if (! is_writeable ($this->dbfile))
|
||||
throw new \Exception(sprintf (dgettext ("domframework",
|
||||
"File '%s' not readable"), $this->dbfile), 500);
|
||||
$this->dsn = $dsn;
|
||||
$this->dbfile = $this->dbfile;
|
||||
}
|
||||
|
||||
/** Store one document in database
|
||||
* @param string $collection The collection name
|
||||
* @param array $document The document to insert
|
||||
* @return integer return The number of document inserted in the database
|
||||
*/
|
||||
public function insertOne ($collection, $document)
|
||||
{
|
||||
$uniqueKey = $this->uniqueKey ();
|
||||
$this->lockEX ();
|
||||
$this->db = $this->readDB ();
|
||||
$this->db[$collection]["content"][$uniqueKey] = array_merge (
|
||||
array ("_id"=>$uniqueKey),
|
||||
$document);
|
||||
$this->writeDB ();
|
||||
$this->lockUN ();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Store multiple documents in database
|
||||
* @param string $collection The collection name
|
||||
* @param array $documents array(array ())
|
||||
* @return integer The number of documents inserted in the database
|
||||
*/
|
||||
public function insertMany ($collection, $documents)
|
||||
{
|
||||
foreach ($documents as $document)
|
||||
if (! is_array ($document))
|
||||
throw new \Exception ("insertMany need an array of array", 406);
|
||||
$this->lockEX ();
|
||||
$this->db = $this->readDB ();
|
||||
foreach ($documents as $document)
|
||||
{
|
||||
$uniqueKey = $this->uniqueKey ();
|
||||
$this->db[$collection]["content"][$uniqueKey] = array_merge (
|
||||
array ("_id"=>$uniqueKey),
|
||||
$document);
|
||||
}
|
||||
$this->writeDB ();
|
||||
$this->db = null;
|
||||
$this->lockUN ();
|
||||
return count ($documents);
|
||||
}
|
||||
|
||||
/** Look at the documents matching $filter (all by default).
|
||||
* Then return only the $fields (all by default).
|
||||
* The field _id is always returned
|
||||
* Return $limit maximum documents (no limit by default)
|
||||
* @param string $collection The collection name
|
||||
* @param array $filter The filter to apply to found the documents
|
||||
* @param array|string $fields The fields to display (* for all, empty array
|
||||
* for none)
|
||||
* @param integer $limit The number of documents to display
|
||||
* @return array The documents matching the parameters
|
||||
*/
|
||||
public function find ($collection, $filter = array (), $fields = "*",
|
||||
$limit = null)
|
||||
{
|
||||
$this->lockSH ();
|
||||
$this->db = $this->readDB ();
|
||||
// Get the keys of the documents based on the filter
|
||||
$keys = $this->filter ($collection, $filter);
|
||||
$res = array ();
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
// Limit the fields
|
||||
$tmp = array ();
|
||||
if ($fields === "*")
|
||||
$tmp = $this->db[$collection]["content"][$key];
|
||||
elseif (is_array ($fields))
|
||||
{
|
||||
if (! in_array ("_id", $fields))
|
||||
array_unshift ($fields, "_id");
|
||||
foreach ($fields as $field)
|
||||
{
|
||||
if (array_key_exists ($field,
|
||||
$this->db[$collection]["content"][$key]))
|
||||
$tmp[$field] = $this->db[$collection]["content"][$key][$field];
|
||||
if (! function_exists("openssl_random_pseudo_bytes")) {
|
||||
throw new \Exception(
|
||||
"Function openssl_random_pseudo_bytes missing",
|
||||
500
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
throw new \Exception ("Invalid field list provided", 500);
|
||||
$res[$key] = $tmp;
|
||||
// Limit the number of results
|
||||
if ($limit !== null && count ($res) >= $limit)
|
||||
break;
|
||||
}
|
||||
$this->lockUN ();
|
||||
$this->db = null;
|
||||
return $res;
|
||||
}
|
||||
|
||||
/** Update some existing documents. Do not change the _id keys
|
||||
* @param string $collection The collection name
|
||||
* @param array $filter The filter to apply to found the documents
|
||||
* @param array $document The data to update
|
||||
* @return integer The number of modified documents
|
||||
* To unset a field, add in the document array a "_unset"=>array("field)"
|
||||
*/
|
||||
public function update ($collection, $filter, $document)
|
||||
{
|
||||
$this->lockEX ();
|
||||
$this->db = $this->readDB ();
|
||||
// Get the keys of the documents based on the filter
|
||||
$keys = $this->filter ($collection, $filter);
|
||||
$unset = array ();
|
||||
if (array_key_exists ("_unset", $document))
|
||||
{
|
||||
$unset = $document["_unset"];
|
||||
unset ($document["_unset"]);
|
||||
}
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
// Merge the new document with the old
|
||||
$tmp = $this->db[$collection]["content"][$key];
|
||||
// We need to merge the old and the new document.
|
||||
// If there is an array in the document, it is overwrited
|
||||
foreach ($document as $k=>$v)
|
||||
{
|
||||
if (is_array ($v))
|
||||
{
|
||||
if (isset ($tmp[$k]))
|
||||
$tmp[$k] = array_merge ($tmp[$k], $v);
|
||||
else
|
||||
$tmp[$k] = $v;
|
||||
$pos = strpos($dsn, "://");
|
||||
if ($pos === false) {
|
||||
throw new \Exception(dgettext(
|
||||
"domframework",
|
||||
"No DSN provided to dbjson"
|
||||
), 500);
|
||||
}
|
||||
else
|
||||
{
|
||||
$tmp[$k] = $v;
|
||||
if (substr($dsn, 0, $pos) !== "dbjson") {
|
||||
throw new \Exception(dgettext(
|
||||
"domframework",
|
||||
"Invalid database type provided in dbjson"
|
||||
), 500);
|
||||
}
|
||||
}
|
||||
$this->db[$collection]["content"][$key] = $tmp;
|
||||
// Remove the needed unset fields
|
||||
foreach ($unset as $field)
|
||||
if (array_key_exists ($field,
|
||||
$this->db[$collection]["content"][$key]))
|
||||
unset ($this->db[$collection]["content"][$key][$field]);
|
||||
}
|
||||
$this->writeDB ();
|
||||
$this->lockUN ();
|
||||
$this->db = null;
|
||||
return count ($keys);
|
||||
}
|
||||
|
||||
/** Replace some existing documents. Do not change the _id keys
|
||||
* @param string $collection The collection name
|
||||
* @param array $filter The filter to apply to found the documents
|
||||
* @param array $document The data to update
|
||||
* @return integer The number of modified documents
|
||||
*/
|
||||
public function replace ($collection, $filter, $document)
|
||||
{
|
||||
$this->lockEX ();
|
||||
$this->db = $this->readDB ();
|
||||
// Get the keys of the documents based on the filter
|
||||
$keys = $this->filter ($collection, $filter);
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
$tmp = $this->db[$collection]["content"][$key];
|
||||
$replace = array ();
|
||||
$replace["_id"] = $tmp["_id"];
|
||||
$replace = array_merge ($replace, $document);
|
||||
$this->db[$collection]["content"][$key] = $replace;
|
||||
}
|
||||
$this->writeDB ();
|
||||
$this->lockUN ();
|
||||
$this->db = null;
|
||||
return count ($keys);
|
||||
}
|
||||
|
||||
/** Delete the first document matching the filter
|
||||
* @param string $collection The collection name
|
||||
* @param array $filter The filter to found the documents
|
||||
* @return integer The number of deleted documents
|
||||
*/
|
||||
public function deleteOne ($collection, $filter)
|
||||
{
|
||||
$this->lockEX ();
|
||||
$this->db = $this->readDB ();
|
||||
// Get the keys of the documents based on the filter
|
||||
$keys = $this->filter ($collection, $filter);
|
||||
if (count ($keys) === 0)
|
||||
return 0;
|
||||
reset ($keys);
|
||||
$key = reset ($keys);
|
||||
unset ($this->db[$collection]["content"][$key]);
|
||||
$this->writeDB ();
|
||||
$this->lockUN ();
|
||||
$this->db = null;
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/** Delete all the documents matching the filter
|
||||
* @param string $collection The collection name
|
||||
* @param array $filter The filter to apply to found the documents
|
||||
* @return integer The number of deleted documents
|
||||
*/
|
||||
public function deleteMany ($collection, $filter)
|
||||
{
|
||||
$this->lockEX ();
|
||||
$this->db = $this->readDB ();
|
||||
// Get the keys of the documents based on the filter
|
||||
$keys = $this->filter ($collection, $filter);
|
||||
foreach ($keys as $key)
|
||||
unset ($this->db[$collection]["content"][$key]);
|
||||
$this->writeDB ();
|
||||
$this->lockUN ();
|
||||
$this->db = null;
|
||||
return count ($keys);
|
||||
}
|
||||
|
||||
/** Look for the keys corresponding to the filter in the collection
|
||||
* Don't manage the locks !
|
||||
* @param string $collection The collection name
|
||||
* @param array $filter The filter to apply to found the documents
|
||||
* - A filter is an array containing the fields and the values to found
|
||||
* array () <== Look for all the documents (no
|
||||
* filter)
|
||||
* array ("key"=>"val") <== Look for the key equal val
|
||||
* array ("key=>array ("val", "<=")) <== Look for the key lighter or
|
||||
* equal than val
|
||||
* array ("key"=>"val", "key2"=>"val2") <== Look for two parameters
|
||||
* array ("key"=>array ("val", "=="),
|
||||
* "key2"=>array ("val2", "==")) <== Look for two complex parameters
|
||||
* Here is the comparison types available : ==,
|
||||
* @return array the keys matching the filter
|
||||
*/
|
||||
public function filter ($collection, $filter)
|
||||
{
|
||||
if ($this->db === null)
|
||||
$this->db = $this->readDB ();
|
||||
$keys = array ();
|
||||
if (! array_key_exists ($collection, $this->db))
|
||||
$this->db[$collection]["content"] = array ();
|
||||
foreach ($this->db[$collection]["content"] as $key=>$document)
|
||||
{
|
||||
if ($filter === array ())
|
||||
{
|
||||
$keys[] = $key;
|
||||
continue;
|
||||
}
|
||||
$matchFilter = false;
|
||||
foreach ($filter as $fkey=>$fvals)
|
||||
{
|
||||
if (is_array ($fvals))
|
||||
{
|
||||
// $fvals = array ("key=>array ("val", "<="))
|
||||
if (array_key_exists ($fkey, $document))
|
||||
{
|
||||
if ($fvals[1] !== "==" &&
|
||||
$fvals[1] !== "<=" &&
|
||||
$fvals[1] !== ">=" &&
|
||||
$fvals[1] !== "<" &&
|
||||
$fvals[1] !== ">" &&
|
||||
$fvals[1] !== "exists" &&
|
||||
$fvals[1] !== "not exists" &&
|
||||
$fvals[1] !== "in_array")
|
||||
throw new \Exception ("Invalid filter operator provided", 500);
|
||||
if ($fvals[1] === "==" && $document[$fkey] === $fvals[0])
|
||||
$matchFilter = true;
|
||||
elseif ($fvals[1] === "<=" && $document[$fkey] <= $fvals[0])
|
||||
$matchFilter = true;
|
||||
elseif ($fvals[1] === ">=" && $document[$fkey] >= $fvals[0])
|
||||
$matchFilter = true;
|
||||
elseif ($fvals[1] === "<" && $document[$fkey] < $fvals[0])
|
||||
$matchFilter = true;
|
||||
elseif ($fvals[1] === ">" && $document[$fkey] > $fvals[0])
|
||||
$matchFilter = true;
|
||||
elseif (strtolower ($fvals[1]) === "exists" &&
|
||||
array_key_exists ($fkey, $document))
|
||||
$matchFilter = true;
|
||||
elseif (strtolower ($fvals[1]) === "not exists" &&
|
||||
! array_key_exists ($fkey, $document))
|
||||
$matchFilter = true;
|
||||
elseif (strtolower ($fvals[1]) === "in_array" &&
|
||||
in_array ($fvals[0], $document[$fkey]))
|
||||
$matchFilter = true;
|
||||
else
|
||||
{
|
||||
$matchFilter = false;
|
||||
break;
|
||||
$this->dbfile = substr($dsn, $pos + 3);
|
||||
$directory = dirname($this->dbfile);
|
||||
if (! file_exists($directory)) {
|
||||
@mkdir($directory, 0777, true);
|
||||
}
|
||||
if (! file_exists($directory)) {
|
||||
throw new \Exception(sprintf(dgettext(
|
||||
"domframework",
|
||||
"Directory '%s' doesn't exists"
|
||||
), $directory), 500);
|
||||
}
|
||||
if (! file_exists($this->dbfile)) {
|
||||
if (! is_readable($directory)) {
|
||||
throw new \Exception(sprintf(
|
||||
dgettext(
|
||||
"domframework",
|
||||
"Directory '%s' not writeable and dbfile '%s' not exists"
|
||||
),
|
||||
$directory,
|
||||
$this->dbfile
|
||||
), 500);
|
||||
}
|
||||
}
|
||||
touch($this->dbfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
// $fvals = array ("key"=>"val")
|
||||
if (array_key_exists ($fkey, $document) &&
|
||||
$document[$fkey] === $fvals)
|
||||
$matchFilter = true;
|
||||
else
|
||||
{
|
||||
$matchFilter = false;
|
||||
break;
|
||||
}
|
||||
if (! is_readable($this->dbfile)) {
|
||||
throw new \Exception(sprintf(dgettext(
|
||||
"domframework",
|
||||
"File '%s' not readable"
|
||||
), $this->dbfile), 500);
|
||||
}
|
||||
}
|
||||
if ($matchFilter === true)
|
||||
$keys[] = $key;
|
||||
if (! is_writeable($this->dbfile)) {
|
||||
throw new \Exception(sprintf(dgettext(
|
||||
"domframework",
|
||||
"File '%s' not readable"
|
||||
), $this->dbfile), 500);
|
||||
}
|
||||
$this->dsn = $dsn;
|
||||
$this->dbfile = $this->dbfile;
|
||||
}
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/** Generate a unique key
|
||||
* @return string the Unique key generated
|
||||
*/
|
||||
private function uniqueKey ()
|
||||
{
|
||||
$data = openssl_random_pseudo_bytes(16);
|
||||
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0010
|
||||
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
|
||||
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||
}
|
||||
|
||||
/** Exclusive lock the database file */
|
||||
private function lockEX ()
|
||||
{
|
||||
$this->dbfileLock = fopen ($this->dbfile, "rt");
|
||||
if (flock ($this->dbfileLock, LOCK_EX) === false)
|
||||
throw new \Exception ("Can't get exclusive lock on dbfile", 500);
|
||||
}
|
||||
|
||||
/** Shared lock the database file */
|
||||
private function lockSH ()
|
||||
{
|
||||
$this->dbfileLock = fopen ($this->dbfile, "rt");
|
||||
if (flock ($this->dbfileLock, LOCK_SH) === false)
|
||||
throw new \Exception ("Can't get shared lock on dbfile", 500);
|
||||
}
|
||||
|
||||
/** Unlock the database file */
|
||||
private function lockUN ()
|
||||
{
|
||||
if ($this->dbfileLock !== null)
|
||||
/** Store one document in database
|
||||
* @param string $collection The collection name
|
||||
* @param array $document The document to insert
|
||||
* @return integer return The number of document inserted in the database
|
||||
*/
|
||||
public function insertOne($collection, $document)
|
||||
{
|
||||
flock ($this->dbfileLock, LOCK_UN);
|
||||
fclose ($this->dbfileLock);
|
||||
$this->dbfileLock = null;
|
||||
$uniqueKey = $this->uniqueKey();
|
||||
$this->lockEX();
|
||||
$this->db = $this->readDB();
|
||||
$this->db[$collection]["content"][$uniqueKey] = array_merge(
|
||||
array("_id" => $uniqueKey),
|
||||
$document
|
||||
);
|
||||
$this->writeDB();
|
||||
$this->lockUN();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Read the dbfile and return an array containing the data. This function
|
||||
* don't do locks !
|
||||
* @return array The database content from the dbfile
|
||||
*/
|
||||
private function readDB ()
|
||||
{
|
||||
$res = json_decode (file_get_contents ($this->dbfile), true);
|
||||
if ($res === null)
|
||||
$res = array ();
|
||||
return $res;
|
||||
}
|
||||
/** Store multiple documents in database
|
||||
* @param string $collection The collection name
|
||||
* @param array $documents array(array ())
|
||||
* @return integer The number of documents inserted in the database
|
||||
*/
|
||||
public function insertMany($collection, $documents)
|
||||
{
|
||||
foreach ($documents as $document) {
|
||||
if (! is_array($document)) {
|
||||
throw new \Exception("insertMany need an array of array", 406);
|
||||
}
|
||||
}
|
||||
$this->lockEX();
|
||||
$this->db = $this->readDB();
|
||||
foreach ($documents as $document) {
|
||||
$uniqueKey = $this->uniqueKey();
|
||||
$this->db[$collection]["content"][$uniqueKey] = array_merge(
|
||||
array("_id" => $uniqueKey),
|
||||
$document
|
||||
);
|
||||
}
|
||||
$this->writeDB();
|
||||
$this->db = null;
|
||||
$this->lockUN();
|
||||
return count($documents);
|
||||
}
|
||||
|
||||
/** Write the dbfile with the provided data. This function don't do locks !
|
||||
* @return bool True if the recording is OK, false if there is a problem
|
||||
*/
|
||||
private function writeDB ()
|
||||
{
|
||||
return !! file_put_contents ($this->dbfile, json_encode ($this->db));
|
||||
}
|
||||
/** Look at the documents matching $filter (all by default).
|
||||
* Then return only the $fields (all by default).
|
||||
* The field _id is always returned
|
||||
* Return $limit maximum documents (no limit by default)
|
||||
* @param string $collection The collection name
|
||||
* @param array $filter The filter to apply to found the documents
|
||||
* @param array|string $fields The fields to display (* for all, empty array
|
||||
* for none)
|
||||
* @param integer $limit The number of documents to display
|
||||
* @return array The documents matching the parameters
|
||||
*/
|
||||
public function find(
|
||||
$collection,
|
||||
$filter = array(),
|
||||
$fields = "*",
|
||||
$limit = null
|
||||
) {
|
||||
$this->lockSH();
|
||||
$this->db = $this->readDB();
|
||||
// Get the keys of the documents based on the filter
|
||||
$keys = $this->filter($collection, $filter);
|
||||
$res = array();
|
||||
foreach ($keys as $key) {
|
||||
// Limit the fields
|
||||
$tmp = array();
|
||||
if ($fields === "*") {
|
||||
$tmp = $this->db[$collection]["content"][$key];
|
||||
} elseif (is_array($fields)) {
|
||||
if (! in_array("_id", $fields)) {
|
||||
array_unshift($fields, "_id");
|
||||
}
|
||||
foreach ($fields as $field) {
|
||||
if (
|
||||
array_key_exists(
|
||||
$field,
|
||||
$this->db[$collection]["content"][$key]
|
||||
)
|
||||
) {
|
||||
$tmp[$field] = $this->db[$collection]["content"][$key][$field];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new \Exception("Invalid field list provided", 500);
|
||||
}
|
||||
$res[$key] = $tmp;
|
||||
// Limit the number of results
|
||||
if ($limit !== null && count($res) >= $limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->lockUN();
|
||||
$this->db = null;
|
||||
return $res;
|
||||
}
|
||||
|
||||
/** Update some existing documents. Do not change the _id keys
|
||||
* @param string $collection The collection name
|
||||
* @param array $filter The filter to apply to found the documents
|
||||
* @param array $document The data to update
|
||||
* @return integer The number of modified documents
|
||||
* To unset a field, add in the document array a "_unset"=>array("field)"
|
||||
*/
|
||||
public function update($collection, $filter, $document)
|
||||
{
|
||||
$this->lockEX();
|
||||
$this->db = $this->readDB();
|
||||
// Get the keys of the documents based on the filter
|
||||
$keys = $this->filter($collection, $filter);
|
||||
$unset = array();
|
||||
if (array_key_exists("_unset", $document)) {
|
||||
$unset = $document["_unset"];
|
||||
unset($document["_unset"]);
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
// Merge the new document with the old
|
||||
$tmp = $this->db[$collection]["content"][$key];
|
||||
// We need to merge the old and the new document.
|
||||
// If there is an array in the document, it is overwrited
|
||||
foreach ($document as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
if (isset($tmp[$k])) {
|
||||
$tmp[$k] = array_merge($tmp[$k], $v);
|
||||
} else {
|
||||
$tmp[$k] = $v;
|
||||
}
|
||||
} else {
|
||||
$tmp[$k] = $v;
|
||||
}
|
||||
}
|
||||
$this->db[$collection]["content"][$key] = $tmp;
|
||||
// Remove the needed unset fields
|
||||
foreach ($unset as $field) {
|
||||
if (
|
||||
array_key_exists(
|
||||
$field,
|
||||
$this->db[$collection]["content"][$key]
|
||||
)
|
||||
) {
|
||||
unset($this->db[$collection]["content"][$key][$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->writeDB();
|
||||
$this->lockUN();
|
||||
$this->db = null;
|
||||
return count($keys);
|
||||
}
|
||||
|
||||
/** Replace some existing documents. Do not change the _id keys
|
||||
* @param string $collection The collection name
|
||||
* @param array $filter The filter to apply to found the documents
|
||||
* @param array $document The data to update
|
||||
* @return integer The number of modified documents
|
||||
*/
|
||||
public function replace($collection, $filter, $document)
|
||||
{
|
||||
$this->lockEX();
|
||||
$this->db = $this->readDB();
|
||||
// Get the keys of the documents based on the filter
|
||||
$keys = $this->filter($collection, $filter);
|
||||
foreach ($keys as $key) {
|
||||
$tmp = $this->db[$collection]["content"][$key];
|
||||
$replace = array();
|
||||
$replace["_id"] = $tmp["_id"];
|
||||
$replace = array_merge($replace, $document);
|
||||
$this->db[$collection]["content"][$key] = $replace;
|
||||
}
|
||||
$this->writeDB();
|
||||
$this->lockUN();
|
||||
$this->db = null;
|
||||
return count($keys);
|
||||
}
|
||||
|
||||
/** Delete the first document matching the filter
|
||||
* @param string $collection The collection name
|
||||
* @param array $filter The filter to found the documents
|
||||
* @return integer The number of deleted documents
|
||||
*/
|
||||
public function deleteOne($collection, $filter)
|
||||
{
|
||||
$this->lockEX();
|
||||
$this->db = $this->readDB();
|
||||
// Get the keys of the documents based on the filter
|
||||
$keys = $this->filter($collection, $filter);
|
||||
if (count($keys) === 0) {
|
||||
return 0;
|
||||
}
|
||||
reset($keys);
|
||||
$key = reset($keys);
|
||||
unset($this->db[$collection]["content"][$key]);
|
||||
$this->writeDB();
|
||||
$this->lockUN();
|
||||
$this->db = null;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Delete all the documents matching the filter
|
||||
* @param string $collection The collection name
|
||||
* @param array $filter The filter to apply to found the documents
|
||||
* @return integer The number of deleted documents
|
||||
*/
|
||||
public function deleteMany($collection, $filter)
|
||||
{
|
||||
$this->lockEX();
|
||||
$this->db = $this->readDB();
|
||||
// Get the keys of the documents based on the filter
|
||||
$keys = $this->filter($collection, $filter);
|
||||
foreach ($keys as $key) {
|
||||
unset($this->db[$collection]["content"][$key]);
|
||||
}
|
||||
$this->writeDB();
|
||||
$this->lockUN();
|
||||
$this->db = null;
|
||||
return count($keys);
|
||||
}
|
||||
|
||||
/** Look for the keys corresponding to the filter in the collection
|
||||
* Don't manage the locks !
|
||||
* @param string $collection The collection name
|
||||
* @param array $filter The filter to apply to found the documents
|
||||
* - A filter is an array containing the fields and the values to found
|
||||
* array () <== Look for all the documents (no
|
||||
* filter)
|
||||
* array ("key"=>"val") <== Look for the key equal val
|
||||
* array ("key=>array ("val", "<=")) <== Look for the key lighter or
|
||||
* equal than val
|
||||
* array ("key"=>"val", "key2"=>"val2") <== Look for two parameters
|
||||
* array ("key"=>array ("val", "=="),
|
||||
* "key2"=>array ("val2", "==")) <== Look for two complex parameters
|
||||
* Here is the comparison types available : ==,
|
||||
* @return array the keys matching the filter
|
||||
*/
|
||||
public function filter($collection, $filter)
|
||||
{
|
||||
if ($this->db === null) {
|
||||
$this->db = $this->readDB();
|
||||
}
|
||||
$keys = array();
|
||||
if (! array_key_exists($collection, $this->db)) {
|
||||
$this->db[$collection]["content"] = array();
|
||||
}
|
||||
foreach ($this->db[$collection]["content"] as $key => $document) {
|
||||
if ($filter === array()) {
|
||||
$keys[] = $key;
|
||||
continue;
|
||||
}
|
||||
$matchFilter = false;
|
||||
foreach ($filter as $fkey => $fvals) {
|
||||
if (is_array($fvals)) {
|
||||
// $fvals = array ("key=>array ("val", "<="))
|
||||
if (array_key_exists($fkey, $document)) {
|
||||
if (
|
||||
$fvals[1] !== "==" &&
|
||||
$fvals[1] !== "<=" &&
|
||||
$fvals[1] !== ">=" &&
|
||||
$fvals[1] !== "<" &&
|
||||
$fvals[1] !== ">" &&
|
||||
$fvals[1] !== "exists" &&
|
||||
$fvals[1] !== "not exists" &&
|
||||
$fvals[1] !== "in_array"
|
||||
) {
|
||||
throw new \Exception("Invalid filter operator provided", 500);
|
||||
}
|
||||
if ($fvals[1] === "==" && $document[$fkey] === $fvals[0]) {
|
||||
$matchFilter = true;
|
||||
} elseif ($fvals[1] === "<=" && $document[$fkey] <= $fvals[0]) {
|
||||
$matchFilter = true;
|
||||
} elseif ($fvals[1] === ">=" && $document[$fkey] >= $fvals[0]) {
|
||||
$matchFilter = true;
|
||||
} elseif ($fvals[1] === "<" && $document[$fkey] < $fvals[0]) {
|
||||
$matchFilter = true;
|
||||
} elseif ($fvals[1] === ">" && $document[$fkey] > $fvals[0]) {
|
||||
$matchFilter = true;
|
||||
} elseif (
|
||||
strtolower($fvals[1]) === "exists" &&
|
||||
array_key_exists($fkey, $document)
|
||||
) {
|
||||
$matchFilter = true;
|
||||
} elseif (
|
||||
strtolower($fvals[1]) === "not exists" &&
|
||||
! array_key_exists($fkey, $document)
|
||||
) {
|
||||
$matchFilter = true;
|
||||
} elseif (
|
||||
strtolower($fvals[1]) === "in_array" &&
|
||||
in_array($fvals[0], $document[$fkey])
|
||||
) {
|
||||
$matchFilter = true;
|
||||
} else {
|
||||
$matchFilter = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// $fvals = array ("key"=>"val")
|
||||
if (
|
||||
array_key_exists($fkey, $document) &&
|
||||
$document[$fkey] === $fvals
|
||||
) {
|
||||
$matchFilter = true;
|
||||
} else {
|
||||
$matchFilter = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($matchFilter === true) {
|
||||
$keys[] = $key;
|
||||
}
|
||||
}
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/** Generate a unique key
|
||||
* @return string the Unique key generated
|
||||
*/
|
||||
private function uniqueKey()
|
||||
{
|
||||
$data = openssl_random_pseudo_bytes(16);
|
||||
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0010
|
||||
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
|
||||
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||
}
|
||||
|
||||
/** Exclusive lock the database file */
|
||||
private function lockEX()
|
||||
{
|
||||
$this->dbfileLock = fopen($this->dbfile, "rt");
|
||||
if (flock($this->dbfileLock, LOCK_EX) === false) {
|
||||
throw new \Exception("Can't get exclusive lock on dbfile", 500);
|
||||
}
|
||||
}
|
||||
|
||||
/** Shared lock the database file */
|
||||
private function lockSH()
|
||||
{
|
||||
$this->dbfileLock = fopen($this->dbfile, "rt");
|
||||
if (flock($this->dbfileLock, LOCK_SH) === false) {
|
||||
throw new \Exception("Can't get shared lock on dbfile", 500);
|
||||
}
|
||||
}
|
||||
|
||||
/** Unlock the database file */
|
||||
private function lockUN()
|
||||
{
|
||||
if ($this->dbfileLock !== null) {
|
||||
flock($this->dbfileLock, LOCK_UN);
|
||||
fclose($this->dbfileLock);
|
||||
$this->dbfileLock = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Read the dbfile and return an array containing the data. This function
|
||||
* don't do locks !
|
||||
* @return array The database content from the dbfile
|
||||
*/
|
||||
private function readDB()
|
||||
{
|
||||
$res = json_decode(file_get_contents($this->dbfile), true);
|
||||
if ($res === null) {
|
||||
$res = array();
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/** Write the dbfile with the provided data. This function don't do locks !
|
||||
* @return bool True if the recording is OK, false if there is a problem
|
||||
*/
|
||||
private function writeDB()
|
||||
{
|
||||
return !! file_put_contents($this->dbfile, json_encode($this->db));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user