View Single Post
Old Apr 16th, 2007, 11:06 PM   #1
grimpirate
King of Portal
 
grimpirate's Avatar
 
Join Date: Sep 2005
Posts: 431
Rep Power: 4 grimpirate is on a distinguished road
Send a message via Yahoo to grimpirate
An Attempt at a DBMS

My current project which is my forum software board uses a flat file database in order to store the forum's data (user data, post data, etc.). I grew curious and starting wondering how actual databases work, and realized that the grimBase class I created is a sort of pseudo-relational database. However, what I always seem to read is that irrelevant of the database, data is stored in tables in a column and row format of some sort. I don't know how the actual implementations go, but I wanted to address a problem in my implementation. I would imagine this falls under the category of creating locks for a database. So here's the code for the grimBase class.[PHP]<?php
echo '<pre>';
$blah = new grimBase('foo.php');
$blah->generateGlyph(array(0, 1, 2, 3, 4));
print_r($blah->parseGlyph());
echo '</pre>';

class grimBase{
var $gbArtefact;
var $gbTablet;
var $gbGlyph;
var $gbArtefactExists = false;
var $gbIsTablet = false;
var $gbGlyphData = null;

function grimBase($artefactID){
if(!isset($artefactID)) trigger_error("grimBase ( string filename) &rarr; Undefined argument filename", E_USER_ERROR);
if(!is_string($artefactID)) trigger_error("grimBase ( string filename) &rarr; filename is an incorrect type", E_USER_ERROR);
if(strlen($artefactID) == 0) trigger_error("grimBase ( string filename) &rarr; filename cannot be an empty string", E_USER_ERROR);
$slashPos = strpos($artefactID, '/');
if($slashPos === 0 || $slashPos === strlen($artefactID) - 1) trigger_error("grimBase ( string filename) &rarr; filename cannot start with a / or end with a /", E_USER_ERROR);
$this->gbArtefact = $artefactID;
$this->gbArtefactExists = file_exists($this->gbArtefact);
$this->gbIsTablet = is_dir($artefactID);
$gbTemp = explode('/', $artefactID);
$dotPos = strpos(end($gbTemp), '.');
if($dotPos !== false && $dotPos !== strlen(current($gbTemp)) - 1){
$this->gbGlyph = array_pop($gbTemp);
}else{
$this->gbGlyph = '';
}
if(count($gbTemp) > 1){
$this->gbTablet = implode('/', $gbTemp);
}else{
$this->gbTablet = reset($gbTemp);
}
}

function artefact(){
return $this->gbArtefact;
}

function tablet(){
return $this->gbTablet;
}

function glyph(){
return $this->gbGlyph;
}

function artefactExists(){
return $this->gbArtefactExists;
}

function artefactCount(){
if(!$this->gbArtefactExists) return !trigger_error("int grimBase->artefactCount ( void) &rarr; Tablet does not exist", E_USER_ERROR);
return count($this->getArtefacts());
}

function getArtefacts(){
if(!$this->gbArtefactExists) return !trigger_error("array grimBase->getArtefacts ( void) &rarr; Tablet does not exist", E_USER_ERROR);
$far = array();
if($handle = opendir($this->gbTablet)){
while(false !== ($file = readdir($handle))){
if($file != "." && $file != ".."){
array_push($far, $file);
}
}
closedir($handle);
}
return $far;
}

function generateTablet(){
if(file_exists($this->gbTablet)) return true;
if(fluctuate($this->gbTablet, 0777)){
$this->gbArtefactExists = true;
}
return $this->gbArtefactExists;
}

function generateGlyph($glyphData){
if(!isset($glyphData)) return !trigger_error("bool grimBase->generateGlyph ( array &array) &rarr; Undefined argument &array", E_USER_ERROR);
if(!is_array($glyphData)) return !trigger_error("bool grimBase->generateGlyph ( array &array) &rarr; &array is an incorrect type", E_USER_ERROR);
if(count($glyphData) == 0) return !trigger_error("bool grimBase->generateGlyph ( array &array) &rarr; &array is empty", E_USER_ERROR);
$this->gbGlyphData = $glyphData;
$fout = '<?php /*' . chr(30);
foreach($glyphData as $glyphKey => $glyphValue){
$fout .= $glyphKey;
$fout .= ' = ';
$fout .= serialize($glyphValue);
$fout .= chr(30);
}
$fout .= '*/ ?>';
if(fluctuate($this->gbArtefact, 'w', $fout, 0777)){
$this->gbArtefactExists = true;
$this->gbIsTablet = false;
$this->gbGlyphData = $glyphData;
return true;
}else{
$this->gbArtefactExists = false;
$this->gbGlyphData = null;
return false;
}
}

function scanGlyph($glyphID){
if(!$this->gbArtefactExists) return !trigger_error("mixed grimBase->scanGlyph ( string arrayKey) &rarr; Glyph does not exist", E_USER_ERROR);
if($this->gbIsTablet) return !trigger_error("mixed grimBase->scanGlyph ( string arrayKey) &rarr; Cannot parse a tablet", E_USER_ERROR);
if(!isset($this->gbGlyphData)){
$this->gbGlyphData = $this->parseGlyph();
}
return $this->gbGlyphData[$glyphID];
}

function parseGlyph(){
if(!$this->gbArtefactExists) return !trigger_error("array grimBase->parseGlyph ( void) &rarr; Glyph does not exist", E_USER_ERROR);
if($this->gbIsTablet) return !trigger_error("array grimBase->parseGlyph ( void) &rarr; Cannot parse a tablet", E_USER_ERROR);
$parseContent = file_get_contents($this->gbArtefact);
if(false === $parseContent) return !trigger_error("array grimBase->parseGlyph ( void) &rarr; Unable to retrieve glyph contents", E_USER_ERROR);
$contentParser = explode(chr(30), $parseContent);
$parseContent = array();
array_shift($contentParser);
array_pop($contentParser);
reset($contentParser);
foreach($contentParser as $contentValue){
$ePos = strpos($contentValue, '=');
$parseKey = trim(substr($contentValue, 0, $ePos - 1));
$parseValue = unserialize(substr($contentValue, $ePos + 2));
$parseContent[$parseKey] = $parseValue;
}
$this->gbGlyphData = $parseContent;
return $parseContent;
}
}

function fluctuate(){
$args = func_get_args();
switch(func_num_args()){
case 1:
if(!is_string($args[0])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; <em>filename</em> is not a string", E_USER_ERROR);
if(is_file($args[0])){
if(unlink($args[0])) return true;
else return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to delete file", E_USER_WARNING);
}elseif(is_dir($args[0])){
if(rmdir($args[0])) return true;
else return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to delete folder", E_USER_WARNING);
}else return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Nonexistent file/directory cannot be deleted", E_USER_NOTICE);
case 2:
if(!is_string($args[0])) return !trigger_error("bool fluctuate ( string dirname [, int mode]) &rarr; <em>filename</em> is not a string", E_USER_ERROR);
if(!is_int($args[1])) return !trigger_error("bool fluctuate ( string dirname [, int mode]) &rarr; <em>mode</em> is not an int", E_USER_ERROR);
if(mkdir($args[0], $args[1])){
return true;
}else{
return !trigger_error("bool fluctuate ( string dirname [, int mode]) &rarr; Unable to create directory", E_USER_ERROR);
}
case 4:
if(!is_string($args[0])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; <em>filename</em> is not a string", E_USER_ERROR);
if(!is_string($args[1]) || strlen($args[1]) !== 1) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; <em>mode</em> is not a character", E_USER_ERROR);
if(!is_int($args[3])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; <em>mode</em> is not an int", E_USER_ERROR);
$args[1] = strtolower($args[1]);
$ffd = null;
switch($args[1]){
case 'a':
if(!file_exists($args[0])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Cannot append to nonexistent file", E_USER_WARNING);
if(!is_writeable($args[0])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; File is not writeable", E_USER_WARNING);
if(false === ($ffd = fopen($args[0], "a"))) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to open file", E_USER_WARNING);
break;
case 'w':
if(false === ($ffd = fopen($args[0], "w"))) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to open file", E_USER_WARNING);
if(!file_exists($args[0])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Cannot write to nonexistent file", E_USER_WARNING);
if(!is_writeable($args[0])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; File is not writeable", E_USER_WARNING);
break;
default:
return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Illegal mode specified", E_USER_ERROR);
}
if(stream_set_write_buffer($ffd, 0) !== -1) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to buffer stream", E_USER_WARNING);
if(fwrite($ffd, $args[2]) === false) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to write to file", E_USER_WARNING);
if(!fclose($ffd)) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to close file", E_USER_WARNING);
if(!chmod($args[0], $args[3])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to change file permissions", E_USER_WARNING);
return true;
default:
return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Incorrect quantity of arguments supplied", E_USER_ERROR);
}
}
?>[/PHP]Now to compare actual database tables to my own there's 3 things to know about the grimBase class. It is composed of what I call Artefacts, Tablets, and Glyphs. Tablets and Glyphs are both Artefacts. On your local computer Tablets are the equivalent of folders/directories and Glyphs are ascii text files. So for instance when I want to create a database table (what I call a Tablet) I would code something like
$foo = new grimBase('foo');
$foo->generateTablet();
Now if I wanted to add an row to the table I would code something like the following
$foo = new grimBase('foo/0.php');
$foo->generateGlyph(array(0, 1, 2, 3, 4));
And that adds a row to the table (the row itself is what I call a Glyph). Therefore a database table with various entries in it in my grimBase is equivalent to a Tablet with various Glyphs in it. Physically this is a folder with various files in it. Now as far as writing to the same Glyph at the exact same time that can't happen because of the set_stream_write_buffer function. However, let's say two users want to reply to a topic in the forum at the same time, the forum automatically creates a Glyph reference to the next needed file, so now two users are referencing the same Glyph record. So now an overwrite of one of the two user's posts can occur. Whoever submits their post last I would imagine. There's an intermediate step where a final check is performed to ensure that that post hasn't already been created, but I imagine there's still a possibility that due to timing the post may still get overriden. So my question is how can I possibly create a lock to prevent that from happening? Or am I looking at this database thing all wrong in terms of the implementation? Should I rather than using individual files use one file and then just add rows? I realize this may be more complicated than what I think, and I have been reading on the subject and book on DBMS where the author stipulates these locks are necessary and I understand why, it's just it's a conceptual book and doesn't actually deal with the physical aspect of programming it.
__________________
Lo, there do I see my father. 'Lo, there do I see My mother, and my sisters, and my brothers. 'Lo, there do I see The line of my people... Back to the beginning. 'Lo, they do call to me. They bid me take my place among them. In the halls of Valhalla... Where the brave... May live... ...forever.. GrimBB | Mimesis

Last edited by grimpirate; Apr 16th, 2007 at 11:19 PM.
grimpirate is offline   Reply With Quote