diff options
Diffstat (limited to 'w/class/dbengine.php')
-rw-r--r-- | w/class/dbengine.php | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/w/class/dbengine.php b/w/class/dbengine.php new file mode 100644 index 0000000..7895a1a --- /dev/null +++ b/w/class/dbengine.php @@ -0,0 +1,309 @@ +<?php + +class Dbengine +{ + public function __construct(string $dataDir) + { + $this->init($dataDir); + } + + public function init($dataDir) + { + // Handle directory path ending. + if (substr($dataDir, -1) !== '/') $dataDir = $dataDir . '/'; + + if (!file_exists($dataDir)) { + // The directory was not found, create one. + if (!mkdir($dataDir, 0777, true)) throw new \Exception('Unable to create the data directory at ' . $dataDir); + } + // Check if PHP has write permission in that directory. + if (!is_writable($dataDir)) throw new \Exception('Data directory is not writable at "' . $dataDir . '." Please change data directory permission.'); + // Finally check if the directory is readable by PHP. + if (!is_readable($dataDir)) throw new \Exception('Data directory is not readable at "' . $dataDir . '." Please change data directory permission.'); + // Set the data directory. + $this->dataDirectory = $dataDir; + } + + // Initialize the store. + public function store($storeName = false) + { + if (!$storeName or empty($storeName)) throw new \Exception('Store name was not valid'); + $this->storeName = $storeName; + // Boot store. + $this->bootStore(); + // Initialize variables for the store. + $this->initVariables(); + return $this; + } + + + public function insert(Art2 $art) + { + $artdata = $art->dry(); + $storableJSON = json_encode($artdata); + if ($storableJSON === false) throw new \Exception('Unable to encode the art object'); + $storePath = $this->storePath . $art->id() . '.json'; + if (!file_put_contents($storePath, $storableJSON)) { + throw new \Exception("Unable to write the object file! Please check if PHP has write permission."); + } + return true; + } + + public function get($id) + { + $filepath = $this->storePath . $id . '.json'; + if (file_exists($filepath)) { + $data = json_decode(file_get_contents($filepath), true); + if ($data !== false) return $data; + } else { + return false; + } + } + + public function update($id, $data) + { + foreach ($data as $key => $value) { + // Do not update the _id reserved index of a store. + if ($key != 'id') { + $data[$key] = $value; + } + } + $storePath = $this->storePath . $id . '.json'; + if (file_exists($storePath)) { + // Wait until it's unlocked, then update data. + file_put_contents($storePath, json_encode($data), LOCK_EX); + } + return true; + } + + + public function getidlist() + { + $lengh = strlen($this->storePath); + $list = []; + foreach (glob($listPath . '*.json') as $filename) { + $list[] = substr(substr($filename, $lengh), 0, -5); + } + return $list; + } + + // Method to boot a store. + protected function bootStore() + { + $store = trim($this->storeName); + // Validate the store name. + if (!$store || empty($store)) throw new \Exception('Invalid store name was found'); + // Prepare store name. + if (substr($store, -1) !== '/') $store = $store . '/'; + // Store directory path. + $this->storePath = $this->dataDirectory . $store; + // Check if the store exists. + if (!file_exists($this->storePath)) { + // The directory was not found, create one with cache directory. + if (!mkdir($this->storePath, 0777, true)) throw new \Exception('Unable to create the store path at ' . $this->storePath); + } + // Check if PHP has write permission in that directory. + if (!is_writable($this->storePath)) throw new \Exception('Store path is not writable at "' . $this->storePath . '." Please change store path permission.'); + // Finally check if the directory is readable by PHP. + if (!is_readable($this->storePath)) throw new \Exception('Store path is not readable at "' . $this->storePath . '." Please change store path permission.'); + } + + // Init data that SleekDB required to operate. + protected function initVariables() + { + // Set empty results + $this->results = []; + // Set a default limit + $this->limit = 0; + // Set a default skip + $this->skip = 0; + // Set default conditions + $this->conditions = []; + // Set default group by value + $this->orderBy = [ + 'order' => false, + 'field' => '_id' + ]; + // Set the default search keyword as an empty string. + $this->searchKeyword = ''; + } + + + + +// ______________________________________ analyse _______________________________ + + + // Find store objects with conditions, sorting order, skip and limits. + protected function findStoreDocuments() + { + $found = []; + $searchRank = []; + // Start collecting and filtering data. + foreach ($this->getidlist() as $id) { + // Collect data of current iteration. + $data = $this->get($id); + if (!empty($data)) { + // Filter data found. + if (empty($this->conditions)) { + // Append all data of this store. + $found[] = $data; + } else { + // Append only passed data from this store. + $storePassed = true; + // Iterate each conditions. + foreach ($this->conditions as $condition) { + // Check for valid data from data source. + $validData = true; + $fieldValue = ''; + try { + $fieldValue = $this->getNestedProperty($condition['fieldName'], $data); + } catch (\Exception $e) { + $validData = false; + $storePassed = false; + } + if ($validData === true) { + // Check the type of rule. + if ($condition['condition'] === '=') { + // Check equal. + if ($fieldValue != $condition['value']) $storePassed = false; + } else if ($condition['condition'] === '!=') { + // Check not equal. + if ($fieldValue == $condition['value']) $storePassed = false; + } else if ($condition['condition'] === '>') { + // Check greater than. + if ($fieldValue <= $condition['value']) $storePassed = false; + } else if ($condition['condition'] === '>=') { + // Check greater equal. + if ($fieldValue < $condition['value']) $storePassed = false; + } else if ($condition['condition'] === '<') { + // Check less than. + if ($fieldValue >= $condition['value']) $storePassed = false; + } else if ($condition['condition'] === '<=') { + // Check less equal. + if ($fieldValue > $condition['value']) $storePassed = false; + } + } + } + // Check if current store is updatable or not. + if ($storePassed === true) { + // Append data to the found array. + $found[] = $data; + } + } + } + } + if (count($found) > 0) { + // Check do we need to sort the data. + if ($this->orderBy['order'] !== false) { + // Start sorting on all data. + $found = $this->sortArray($this->orderBy['field'], $found, $this->orderBy['order']); + } + // If there was text search then we would also sort the result by search ranking. + if (!empty($this->searchKeyword)) { + $found = $this->performSerach($found); + } + // Skip data + if ($this->skip > 0) $found = array_slice($found, $this->skip); + // Limit data. + if ($this->limit > 0) $found = array_slice($found, 0, $this->limit); + } + return $found; + } + + + + // Sort store objects. + protected function sortArray($field, $data, $order = 'ASC') + { + $dryData = []; + // Check if data is an array. + if (is_array($data)) { + // Get value of the target field. + foreach ($data as $value) { + $dryData[] = $this->getNestedProperty($field, $value); + } + } + // Descide the order direction. + if (strtolower($order) === 'asc') asort($dryData); + else if (strtolower($order) === 'desc') arsort($dryData); + // Re arrange the array. + $finalArray = []; + foreach ($dryData as $key => $value) { + $finalArray[] = $data[$key]; + } + return $finalArray; + } + + // Get nested properties of a store object. + protected function getNestedProperty($field = '', $data) + { + if (is_array($data) and !empty($field)) { + // Dive deep step by step. + foreach (explode('.', $field) as $i) { + // If the field do not exists then insert an empty string. + if (!isset($data[$i])) { + $data = ''; + throw new \Exception('"' . $i . '" index was not found in the provided data array'); + break; + } else { + // The index is valid, collect the data. + $data = $data[$i]; + } + } + return $data; + } + } + + // Do a search in store objects. This is like a doing a full-text search. + protected function performSerach($data = []) + { + if (empty($data)) return $data; + $nodesRank = []; + // Looping on each store data. + foreach ($data as $key => $value) { + // Looping on each field name of search-able fields. + foreach ($this->searchKeyword['field'] as $field) { + try { + $nodeValue = $this->getNestedProperty($field, $value); + // The searchable field was found, do comparison against search keyword. + similar_text(strtolower($nodeValue), strtolower($this->searchKeyword['keyword']), $perc); + if ($perc > 50) { + // Check if current store object already has a value, if so then add the new value. + if (isset($nodesRank[$key])) $nodesRank[$key] += $perc; + else $nodesRank[$key] = $perc; + } + } catch (\Exception $e) { + continue; + } + } + } + if (empty($nodesRank)) { + // No matched store was found against the search keyword. + return []; + } + // Sort nodes in descending order by the rank. + arsort($nodesRank); + // Map original nodes by the rank. + $nodes = []; + foreach ($nodesRank as $key => $value) { + $nodes[] = $data[$key]; + } + return $nodes; + } + + + + + +} + + + + + + + + + +?>
\ No newline at end of file |