1: <?php
2: namespace Module\FSDB;
3:
4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:
39: class Connection {
40: 41: 42: 43:
44: private $directory;
45:
46:
47: 48: 49: 50:
51: private $tables = array();
52:
53: 54: 55: 56: 57:
58: public function __construct($directory = '') {
59: if ($directory == '') {
60: $directory = getcwd() . "/data/";
61: }
62: if (is_dir($directory)) {
63: $this->directory = $directory;
64: } else {
65: $oldumask = umask(0);
66: if (@mkdir($directory, 0777)) {
67: $this->directory = $directory;
68: } else {
69: throw new \Exception("FSDB: Cannot create data directory at:" . $directory);
70: }
71: umask($oldumask);
72: }
73: }
74:
75: 76: 77: 78: 79: 80: 81: 82: 83:
84: public function __call($method, $args) {
85: if (method_exists("\Module\FSDB\_Table", $method)) {
86: $tableName = $args[0];
87: $filename = $args[0] . ".json";
88:
89: if (!isset($this->tables[$tableName])) {
90: $this->tables[$tableName] = new _Table($this->directory . $filename);
91: }
92:
93: array_shift($args);
94:
95: return call_user_func_array(array($this->tables[$tableName], $method), $args);
96:
97: } else {
98: throw new \Exception("FSDB: Method does not exist: $method");
99: }
100: }
101: }
102:
103: 104: 105: 106: 107: 108: 109: 110: 111:
112: class _Table {
113: 114: 115: 116:
117: private $edited = false;
118:
119:
120: 121: 122: 123:
124: private $locked = false;
125:
126: 127: 128: 129:
130: private $file;
131:
132:
133: 134: 135: 136:
137: private $handle;
138:
139:
140: 141: 142: 143:
144: private $data = array();
145:
146: 147: 148: 149: 150:
151: public function __construct($file) {
152: if (!file_exists($file)) {
153: $newFile = fopen($file, 'w');
154: fclose($newFile);
155: }
156:
157: $this->file = $file;
158:
159: $this->handle = fopen($this->file, "a+");
160:
161: if ($this->handle) {
162: fseek($this->handle, 0);
163: $this->getLock($file);
164: } else {
165: throw new \Exception('\FSDB\Table: Could not open file');
166: }
167: }
168:
169: 170: 171: 172:
173: public function __destruct() {
174: if ($this->edited) {
175: $this->save();
176: }
177: if ($this->locked) {
178: flock($this->handle, LOCK_UN);
179: }
180: if ($this->handle) {
181: fclose($this->handle);
182: }
183: }
184:
185: private function uuid($prefix = '') {
186: $chars = md5(uniqid(mt_rand(), true));
187: $uuid = substr($chars,0,8) . '-';
188: $uuid .= substr($chars,8,4) . '-';
189: $uuid .= substr($chars,12,4) . '-';
190: $uuid .= substr($chars,16,4) . '-';
191: $uuid .= substr($chars,20,12);
192: return $prefix . $uuid;
193: }
194:
195: 196: 197: 198: 199:
200: private function getLock($timeout) {
201: if ($this->locked == false) {
202: if (flock($this->handle, LOCK_EX)) {
203: $this->locked = true;
204: $this->load();
205: return true;
206: } else {
207: if ($timeout < 10) {
208: $this->getLock($timeout++);
209: } else {
210: throw new \Exception('\FSDB\Table: Could not lock file');
211: }
212: }
213: }
214:
215: }
216:
217: 218: 219: 220: 221:
222: private function load() {
223: if ($this->locked == false) {
224: throw new \Exception('\FSDB\Table: File in not locked yet');
225: }
226:
227: $data = "";
228:
229: while (($buffer = fgets($this->handle)) !== false) {
230: $data .= $buffer;
231: }
232:
233: if (!feof($this->handle)) {
234: throw new \Exception('\FSDB\Table: unexpected fgets() fail');
235: }
236:
237: $this->data = json_decode($data, false);
238: }
239:
240: 241: 242: 243: 244: 245:
246: private function save() {
247:
248: if ($this->handle) {
249: fseek($this->handle, 0);
250: ftruncate($this->handle, 0);
251:
252:
253: if (fwrite($this->handle, json_encode($this->data))) {
254: return true;
255: } else {
256: throw new \Exception("\FSDB\Connection: Cannot write data to:" . $this->file);
257: }
258: } else {
259: throw new \Exception('\FSDB\Connection\: File is not opened, can not save.');
260: }
261: }
262:
263: 264: 265: 266: 267: 268: 269:
270: public function select($column, $search = 0) {
271: $results = array();
272:
273: if ($column == "*") {
274: return $this->data;
275: }
276:
277: if (count($this->data) == 0) {
278: return array();
279: }
280:
281: foreach($this->data as $row) {
282: if ($row->$column == $search) {
283: $results[] = $row;
284: }
285: }
286:
287: return $results;
288: }
289:
290: 291: 292: 293: 294: 295: 296: 297:
298: public function selectRange($column, $lower, $upper) {
299: $results = array();
300:
301: foreach($this->data as $row) {
302: if ($row->$column > $lower && $row->column < $upper) {
303: $results[] = $row;
304: }
305: }
306:
307: return $results;
308: }
309:
310: 311: 312: 313: 314: 315: 316: 317: 318: 319:
320: public function update($column, $search = 0, $data = array()) {
321: $updated = 0;
322:
323: foreach($this->data as $key => $value) {
324: if ($this->data[$key]->$column == $search) {
325: if (is_object($data)) {
326: $this->data[$key] = (object) array_merge((array) $this->data[$key], (array) $data);
327: } else {
328: $this->data[$key]->$column = $data;
329: }
330: $updated++;
331: }
332: }
333:
334: if ($updated) {
335: $this->edited = true;
336: }
337:
338: return $updated;
339: }
340:
341: 342: 343: 344: 345: 346:
347: public function insert($data) {
348: $data->id = $this->uuid();
349: $this->data[] = $data;
350: $this->edited = true;
351: return true;
352: }
353:
354: 355: 356: 357: 358: 359: 360:
361: public function delete($column, $search = 0) {
362: $updated = 0;
363:
364: foreach($this->data as $key => $value) {
365: if ($value->$column == $search) {
366: unset($this->data[$key]);
367: $updated++;
368: }
369: }
370:
371: if ($updated > 0) {
372: $this->edited = true;
373: }
374:
375: return $updated;
376: }
377: }