NxTIC/build/lightdb/core/lightdb.php

419 lines
11 KiB
PHP
Raw Normal View History

<?php
namespace lightdb\core;
2016-05-17 13:24:14 +00:00
class lightdb{
2016-05-17 13:24:14 +00:00
// REPERTOIRE RACINE DE TOUTES LES BDD
public static function default_root(){ return __BUILD__.'/lightdb/storage'; }
2016-05-17 13:24:14 +00:00
// ATTRIBUTS
private $root;
private $dbname;
private $dir;
private $index;
private $date;
2016-05-17 13:24:14 +00:00
private $driver;
private $line;
private $tmp;
/* CONSTRUCTEUR -> CREER LES FICHIERS S'ILS N'EXISTENT PAS SINON, RECUPERE LES DONNES
*
* @dbname<String> Nom de la base de données
*
*/
public function __construct($dbname, $root=null){
2016-05-17 13:24:14 +00:00
/* [0] On récupère les attributs
=========================================================*/
2016-10-19 06:03:36 +00:00
$this->root = is_null($root) ? self::default_root().'/' : $root;
$this->dbname = $dbname;
$this->dir = $this->root.$dbname.'/';
2016-05-17 13:24:14 +00:00
/* [1] Création du répertoire s'il n'existe pas
=========================================================*/
if( !is_dir($this->dir) )
mkdir($this->dir);
/* [2] Création du fichier d'index ou récupération
=========================================================*/
/* (1) Si le fichier n'existe pas, on le crée */
2016-05-17 14:51:24 +00:00
if( !file_exists($this->dir.'index') ){
$fIndex = new \SplFileObject($this->dir.'index', 'w');
$fIndex->fwrite('[]');
$fIndex = null;
}
2016-05-17 13:24:14 +00:00
/* (2) On récupère le contenu du fichier */
$fIndex = new \SplFileObject($this->dir.'index');
$fIndex->seek(0);
2016-05-17 13:24:14 +00:00
$index = json_decode( $fIndex->fgets(), true );
2016-05-17 13:24:14 +00:00
// Si erreur de parsage, on retourne une erreur
2016-11-22 16:17:04 +00:00
if( is_null($index) ) throw new \Exception('[lightdb] index is null');
$this->index = $index;
2016-05-17 13:24:14 +00:00
/* [3] Initialisation du gestionnaire d'acces (SplFileObject)
=========================================================*/
/* (1) Si le fichier n'existe pas, on le crée */
$this->data = $this->dir.'data';
if( !file_exists($this->data) )
file_put_contents($this->data, '' );
2016-05-17 13:24:14 +00:00
/* (2) On place un 'driver' sur le fichier */
$this->driver = new \SplFileObject($this->data, 'r+');
2016-05-17 13:24:14 +00:00
// $this->driver->setFlags( \SplFileObject::SKIP_EMPTY );
/* (3) On récupère le nombre de lignes */
$this->line = -1;
2016-12-17 23:12:23 +00:00
while( $this->driver->valid() ){
2016-05-17 13:24:14 +00:00
$this->line++;
$this->driver->fgetcsv();
}
}
2016-05-17 13:24:14 +00:00
public function close(){ $this->driver = null; }
/* FLUSH LA BDD (erase all)
*
*/
public function flush(){
/* (1) On flush les index */
$fIndex = new \SplFileObject($this->dir.'index', 'w');
$fIndex->fwrite('[]');
$fIndex = null;
$this->line = 0;
$this->index = [];
/* (2) On flush les data */
file_put_contents($this->dir.'data', '' );
}
/* RETOURNE UN INDEX
2016-05-17 13:24:14 +00:00
*
* @i<String> [OPT] Index pour lequel on veut la ligne et le hash
2016-05-17 13:24:14 +00:00
*
* @return Index<Array> Tableau associatif contenant le hash et la ligne
*
*/
public function index($i=null){
return is_numeric($i) ? $this->index[$i] : $this->index;
}
2016-05-17 13:24:14 +00:00
/* INSERTION D'UNE ENTREE DANS LA BASE DE DONNEES
*
* @key<String> Clé qui permettra l'accès direct
* @data<mixed*> Objet qui sera enregistré dans la base
*
* @return status<Boolean> Retourne TRUE si tout s'est bien passé, sinon FALSE
*
*/
public function insert($key, $data){
2016-05-17 13:24:14 +00:00
/* (1) On vérifie que la clé est unique */
if( array_key_exists($key, $this->index) )
2016-12-17 23:12:23 +00:00
return false;
2016-05-17 13:24:14 +00:00
2016-05-18 07:41:36 +00:00
$key = (string) $key;
2016-05-17 13:24:14 +00:00
/* (2) On ajoute les données aux fichier */
$json_data = json_encode($data);
$this->driver->seek($this->line);
2016-12-17 23:12:23 +00:00
$written = $this->driver->fwrite( $json_data."\n" );
2016-05-17 13:24:14 +00:00
// Si erreur d'écriture, on retourne FALSE
if( is_null($written) )
return false;
/* (3) On enregistre l'index */
2016-12-17 23:12:23 +00:00
$this->index[$key] = [ 'line' => $this->line++ ];
2016-05-17 13:24:14 +00:00
/* (4) On enregistre le fichier index */
$fIndex = new \SplFileObject($this->dir.'index', 'w');
$fIndex->fwrite( json_encode($this->index) );
$fIndex = null;
return true;
}
/* INSERTION D'UNE ENTREE DANS LA BASE DE DONNEES
2016-05-17 13:24:14 +00:00
*
* @dataset<Array> Tableau de 'clés'->'valeurs' à insérer
* @data<mixed*> Objet qui sera enregistré dans la base
2016-05-17 13:24:14 +00:00
*
* @return status<Boolean> Retourne TRUE si tout s'est bien passé, sinon FALSE
2016-05-17 13:24:14 +00:00
*
*/
public function insertAll($dataset){
/* (1) On vérifie que la clé est unique */
foreach($dataset as $key=>$data)
if( array_key_exists($key, $this->index) )
unset($dataset[$key]);
2016-05-17 10:25:34 +00:00
/* (2) On ajoute les données aux fichier */
$this->driver->seek($this->line);
foreach($dataset as $key=>$data){
$json_data = json_encode($data);
$this->line++;
2016-12-17 23:12:23 +00:00
$written = $this->driver->fwrite( $json_data."\n" );
2016-05-17 10:25:34 +00:00
/* (3) On enregistre les index */
$this->index[$key] = [
'line' => $this->line - 1//,
//'hash' => sha1($json_data)
];
}
/* (4) On enregistre le fichier index */
$fIndex = new \SplFileObject($this->dir.'index', 'w');
$fIndex->fwrite( json_encode($this->index) );
$fIndex = null;
return true;
}
/* RENVOIE LES DONNEES ASSOCIEES A UNE CLE DONNEE
*
* @key<String> Clé associée à la valeur à récupérer
*
* @return data<mixed*> Renvoie la valeur associée à la clé, FALSE si erreur
*
*/
public function fetch($key){
/* (1) On vérifie que la clé existe bien */
if( !array_key_exists($key, $this->index) )
return false;
/* (2) On récupère la ligne */
$line = $this->index[$key]['line'];
/* (3) On récupère le contenu */
$this->driver->seek($line);
$json = json_decode( $this->driver->current(), true );
// Si erreur de parsage
if( is_null($json) )
return false;
return $json;
}
/* RENVOIE LES DONNEES ASSOCIEES AUX CLES DONNEES
*
* @keys<Array> Clés associées aux valeurs à récupérer
*
* @return data<mixed*> Renvoie les valeurs associées aux clé, ou un tableau vide si erreur
*
*/
public function fetchAll($keys){
$data = [];
/* (0) Pour chaque clé */
foreach($keys as $i=>$key){
/* (1) On ne prend pas en compte les clés qui n'existent pas */
if( !array_key_exists($key, $this->index) )
continue;
/* (2) On récupère la ligne */
$line = $this->index[$key]['line'];
/* (3) On récupère le contenu */
$this->driver->seek($line);
$json = json_decode( $this->driver->current(), true );
/* (4) Si pas d'erreur de parsage, On enregistre */
if( !is_null($json) )
$data[$key] = $json;
}
return $data;
2016-05-17 13:24:14 +00:00
}
2016-05-17 13:24:14 +00:00
/* SUPPRIME UNE ENTREE DE CLE DONNEE DE LA BASE DE DONNEES
*
* @key<String> Clé de l'entrée à supprimer
*
* @return status<Boolean> Retourne TRUE si tout s'est bien passé, sinon FALSE
*
*/
public function delete($key){
/* (1) On vérifie l'existence de la clé */
if( !array_key_exists($key, $this->index) )
2016-12-17 23:12:23 +00:00
return false; // On considère que l'action souhaitée est effectuée
2016-05-17 10:25:34 +00:00
2016-05-18 07:41:36 +00:00
$line = $this->index[$key]['line'];
/* (2) On réarrange la bd pour supprimer la ligne */
2016-11-06 11:05:29 +00:00
$tmpfilename = __TMP__.'/'.uniqid().'.dat';
2016-05-18 07:41:36 +00:00
$tmpfile = new \SplFileObject($tmpfilename, 'w');
$this->driver->seek(0);
// On recopie toutes les lignes sauf celle à supprimer dans un fichier temporaire
2016-12-17 23:12:23 +00:00
foreach($this->driver as $k=>$content){
// Only valuable lines (not the last linebreak)
2016-12-17 23:12:23 +00:00
if( $k >= $this->line )
break;
2016-05-18 07:41:36 +00:00
// On n'écrit pas la ligne à supprimer
2016-12-17 23:12:23 +00:00
if( $k != $line )
$tmpfile->fwrite( $content );
2016-05-18 07:41:36 +00:00
}
// On décrémente le nb de lignes
$this->line--;
$tmpfile = null;
/* (3) On remplace le fichier original par le fichier temporaire */
$this->driver = null;
rename($tmpfilename, $this->dir.'data');
$this->driver = new \SplFileObject($this->dir.'data', 'r+');
/* (3) On supprime la ligne de l'index */
2016-05-17 13:24:14 +00:00
unset( $this->index[$key] );
2016-05-18 07:41:36 +00:00
/* (4) On met à jour les index des lignes déplacées */
foreach($this->index as $i=>$indexData)
if( $indexData['line'] > $line )
$this->index[$i]['line']--; // on décrémente les lignes au dessus de la ligne supprimée
/* (5) On enregistre le fichier index */
$fIndex = new \SplFileObject($this->dir.'index', 'w');
$fIndex->fwrite( json_encode($this->index) );
$fIndex = null;
return true;
}
/* SUPPRIME PLUSIEURS ENTREES DE CLES DONNEES DE LA BASE DE DONNEES
*
* @keys<Array> Clés des entrées à supprimer
*
* @return status<Boolean> Retourne TRUE si tout s'est bien passé, sinon FALSE
*
*/
public function deleteAll($keys){
$keyLines = [];
/* [1] On récupère la ligne associée à chaque clé
=========================================================*/
foreach($keys as $k=>$key){
/* (1) Si la clé n'existe pas, on passe à la suivante */
if( !array_key_exists($key, $this->index) )
continue;
/* (2) On récupère la ligne de la clé */
$keyLines[$key] = $this->index[$key]['line'];
}
if( count($keyLines) == 0 )
return true;
/* [2] On trie les clés en fonction de leur ligne
=========================================================*/
$sorted = [];
// Tant que toute les clés ne sont pas triées
while( count($keyLines) > 0 ){
// Contiendra la clé de la plus petite valeur
$min = null;
// On cherche la ligne la plus petite
foreach($keyLines as $key=>$line)
if( is_null($min) || $line < $keyLines[$min] ) // Si valeur inf à min
$min = $key;
// On ajoute la plus petite clé trouvée a la liste
$sorted[$min] = $keyLines[$min];
// On la supprime du tableau à trier
unset($keyLines[$min]);
}
/* [3] On supprime les lignes à supprimer
=========================================================*/
/* (1) On réarrange la bd pour supprimer la ligne */
2016-11-06 11:05:29 +00:00
$tmpfilename = __TMP__.'/'.uniqid().'.dat';
$tmpfile = new \SplFileObject($tmpfilename, 'w');
$this->driver->seek(0);
/* (2) On recopie toutes les lignes sauf celles à supprimer dans un fichier temporaire */
foreach($this->driver as $key=>$content){
// Only valuable lines (not the last linebreak)
if( $key >= $this->line ) break;
// On n'écrit pas la ligne à supprimer
if( !in_array($key, $sorted) )
$tmpfile->fwrite( $content );
}
$tmpfile = null;
/* (3) On remplace le fichier original par le fichier temporaire */
$this->driver = null;
rename($tmpfilename, $this->dir.'data');
$this->driver = new \SplFileObject($this->dir.'data', 'r+');
/* [4] On met à jour les index
=========================================================*/
$step = 0;
foreach($sorted as $key=>$line){
/* (1) On décrémente le nb de lignes */
$this->line--;
/* (2) On supprime la ligne de l'index */
unset( $this->index[$key] );
/* (3) On met à jour les index des lignes déplacées du nombre d'index qu'on a supprimé */
foreach($this->index as $i=>$indexData)
if( $indexData['line'] > $line-$step )
$this->index[$i]['line']--; // on décrémente les lignes au dessus de la ligne supprimée
$step++;
}
/* (4) On enregistre le fichier index */
$fIndex = new \SplFileObject($this->dir.'index', 'w');
$fIndex->fwrite( json_encode($this->index) );
$fIndex = null;
2016-05-17 13:24:14 +00:00
return true;
}
}
2016-12-17 23:12:23 +00:00