Completed
Pull Request — master (#105)
by
unknown
02:01
created

FakeDatabase::find()   C

Complexity

Conditions 7
Paths 2

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 35
rs 6.7272
cc 7
eloc 19
nc 2
nop 3
1
<?php
2
/**
3
 * Manages {@link FakeObject} instances in a table-like storage with arbitrary keys. 
4
 * Enforces a unique identifier for each record, either manually set or created as a hash.
5
 * Since it stores arbitrary key/value data, its main purpose is to provide
6
 * lightweight object wrappers where no full ORM mapping exists, e.g. with transient
7
 * data stored in and retrieved from webservices.
8
 * 
9
 * The database is designed to be persisted as a flat file via json_encode() and json_decode().
10
 */
11
class FakeDatabase {
12
13
    protected $path;
14
15
    function __construct($path) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
16
        $this->path = $path;
17
    }
18
19
    protected function getData() {
20
21
        if(file_exists($this->path) && !is_readable($this->path)) {
22
            throw new LogicException(sprintf('FakeDatabase at %s is not readable'. $this->path));
23
        } 
24
25
        if(file_exists($this->path)) {
26
            $content = file_get_contents($this->path);
27
            return $content ? json_decode(file_get_contents($this->path), true) : array();
28
        } else {
29
            return array();
30
        }
31
        
32
    }
33
34
    protected function setData($data) {
35
        if(file_exists($this->path) && !is_writable($this->path)) {
36
            throw new LogicException(sprintf('FakeDatabase at %s is not writeable'. $this->path));
37
        }
38
        $old = umask(0);
39
        file_put_contents($this->path, json_encode($data, JSON_PRETTY_PRINT));
40
        chmod($this->path, 0777);
41
        umask($old);
42
    }
43
44
    /**
45
     * Finds a record matching a certain key/value set
46
     * Not a terribly efficient implementation, since it retrieves all records from this type
47
     * and searches in memory. Supports nested keys through dot notation.
48
     */
49
    public function find($type, $key, $value) {
50
        $data = $this->getData();
51
        $return = null;
52
        
53
        if(isset($data[$type])) {
54
            $keyParts = explode('.', $key);
55
            $records = $data[$type];
56
            foreach($records as $recordKey => $record) {
57
                $compare = $record;
58
                foreach($keyParts as $i => $keyPart) {
59
                    if($i < count($keyParts)) {
60
                        if(isset($compare[$keyPart])) {
61
                            $compare = $compare[$keyPart];
62
                        } else {
63
                            continue;
64
                        }
65
                    } 
66
                }
67
                if($compare == $value) {
68
                    $return = FakeObject::create_from_array($record);
69
                }
70
            }
71
        } else {
72
            $return = false;
73
        }
74
75
//        $this->log(sprintf(
76
//            'Find fake %s#%s: %s',
77
//            $type,
78
//            $key,
79
//            json_encode($return)
80
//        ));
81
82
        return $return;
83
    }
84
85 View Code Duplication
    public function get($type, $key) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
86
        $data = $this->getData();
87
        $return = (isset($data[$type][$key])) ? FakeObject::create_from_array($data[$type][$key]) : null;
88
        
89
//        $this->log(sprintf(
90
//            'Get fake %s#%s: %s',
91
//            $type,
92
//            $key,
93
//            json_encode($return)
94
//        ));
95
96
        return $return;
97
    }
98
99 View Code Duplication
    public function getAll($type) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
100
        $data = $this->getData();
101
        if(!isset($data[$type])) return false;
102
103
        $return = array();
104
        foreach($data[$type] as $record) {
105
            $return[] = FakeObject::create_from_array($record);
106
        }
107
108
        return $return;
109
    }
110
111
    /**
112
     * Sets a new record. Replaces existing records with the same key.
113
     * Use {@link update()} for updating existing records.
114
     * 
115
     * @param String     $type  
116
     * @param String     $key       
117
     * @param FakeObject $obj  
118
     */
119
    public function set($type, $key, FakeObject $obj) {
120
        $data = $this->getData();
121
        if(!isset($data[$type])) $data[$type] = array();
122
123
        $obj->_key = $key;
0 ignored issues
show
Bug introduced by
The property _key does not seem to exist in FakeObject.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
124
        $record = $obj->toArray();
125
        $data[$type][(string)$key] = $record;
126
        $this->setData($data);
127
128
//        $this->log(sprintf(
129
//            'Set fake %s#%s: %s',
130
//            $type,
131
//            $key,
132
//            json_encode($record)
133
//        ));
134
    }
135
136
    /**
137
     * Updates an existing record. Merges with existing data.
138
     * Use {@link set()} to unset values.
139
     * 
140
     * @param  String     $type  
141
     * @param  String     $key    
142
     * @param  FakeObject $obj   
143
     */
144
    public function update($type, $key, FakeObject $obj) {
145
        $existing = $this->get($type, $key);
146
        $record = $existing ? array_merge($existing->toArray(), $obj->toArray()) : $obj->toArray();
147
        $data = $this->getData();
148
        if(!isset($data[$type])) $data[$type] = array();
149
        $data[$type][$key] = $record;
150
        $this->setData($data);
151
152
//        $this->log(sprintf(
153
//            'Update fake %s#%s: %s',
154
//            $type,
155
//            $key,
156
//            json_encode($record)
157
//        ));
158
    }
159
160
    /**
161
     * Since stores (SQLite tables) aren't tracked reliably across processes,
162
     * we need to hard reset the DB and reinitialize it.
163
     */
164
    public function reset() {
165
        if(file_exists($this->path)) {
166
            unlink($this->path);
167
        }
168
169
//        $this->log('Reset');
170
    }
171
172
    public function toArray() {
173
        return $this->getData();
174
    }
175
176
    public function getPath() {
177
        return $this->path;
178
    }
179
180
//    protected function log($msg) {
181
//        MysiteLog::log($msg, MysiteLog::INFO);
182
//    }
183
184
}
185
186
class FakeDatabaseException extends Exception {}
187