These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace ByJG\AnyDataset\Repository; |
||
4 | |||
5 | use ByJG\AnyDataset\Exception\DatabaseException; |
||
6 | use ByJG\Util\XmlUtil; |
||
7 | use ForceUTF8\Encoding; |
||
8 | use InvalidArgumentException; |
||
9 | |||
10 | /** |
||
11 | * AnyDataset is a simple way to store data using only XML file. |
||
12 | * Your structure is hierarquical and each "row" contains "fields" but these structure can vary for each row. |
||
13 | * Anydataset files have extension ".anydata.xml" and have many classes to put and get data into anydataset xml file. |
||
14 | * Anydataset class just read and write files. To search elements you need use AnyIterator and IteratorFilter. Each row have a class SingleRow. |
||
15 | * |
||
16 | * XML Structure |
||
17 | * <code> |
||
18 | * <anydataset> |
||
19 | * <row> |
||
20 | * <field name="fieldname1">value of fieldname 1</field> |
||
21 | * <field name="fieldname2">value of fieldname 2</field> |
||
22 | * <field name="fieldname3">value of fieldname 3</field> |
||
23 | * </row> |
||
24 | * <row> |
||
25 | * <field name="fieldname1">value of fieldname 1</field> |
||
26 | * <field name="fieldname4">value of fieldname 4</field> |
||
27 | * </row> |
||
28 | * </anydataset> |
||
29 | * </code> |
||
30 | * |
||
31 | * How to use: |
||
32 | * <code> |
||
33 | * $any = new AnyDataset(); |
||
34 | * </code> |
||
35 | * |
||
36 | * @see SingleRow |
||
37 | * @see AnyIterator |
||
38 | * @see IteratorFilter |
||
39 | * |
||
40 | */ |
||
41 | class AnyDataset |
||
42 | { |
||
43 | |||
44 | /** |
||
45 | * Internal structure represent the current SingleRow |
||
46 | * @var SingleRow[] |
||
47 | */ |
||
48 | private $_collection; |
||
49 | |||
50 | /** |
||
51 | * Current node anydataset works |
||
52 | * @var int |
||
53 | */ |
||
54 | private $_currentRow; |
||
55 | |||
56 | /** |
||
57 | * Path to anydataset file |
||
58 | * @var string |
||
59 | */ |
||
60 | private $_path; |
||
61 | |||
62 | /** |
||
63 | * |
||
64 | * @param string $file |
||
65 | * @throws InvalidArgumentException |
||
66 | */ |
||
67 | 8 | public function __construct($file = null) |
|
68 | { |
||
69 | 8 | $this->_collection = array(); |
|
70 | 8 | $this->_currentRow = -1; |
|
71 | |||
72 | 8 | $this->_path = null; |
|
73 | 8 | if (!is_null($file)) { |
|
74 | 1 | if (is_string($file)) { |
|
75 | 1 | $ext = pathinfo($file, PATHINFO_EXTENSION); |
|
76 | 1 | if (empty($ext)) { |
|
77 | 1 | $file .= '.anydata.xml'; |
|
78 | 1 | } |
|
79 | 1 | $this->_path = $file; |
|
80 | 1 | } else { |
|
81 | throw new \InvalidArgumentException('I expected a string as a file name'); |
||
82 | } |
||
83 | 1 | $this->createFrom($this->_path); |
|
84 | 1 | } |
|
85 | 8 | } |
|
86 | |||
87 | /** |
||
88 | * Private method used to read and populate anydataset class from specified file |
||
89 | * @param string $filepath Path and Filename to be read |
||
90 | * @return null |
||
91 | */ |
||
92 | private function createFrom($filepath) |
||
93 | { |
||
94 | if (file_exists($filepath)) { |
||
95 | $anyDataSet = XmlUtil::createXmlDocumentFromFile($filepath); |
||
96 | $this->_collection = array(); |
||
97 | |||
98 | $rows = $anyDataSet->getElementsByTagName("row"); |
||
99 | foreach ($rows as $row) { |
||
100 | $sr = new SingleRow(); |
||
101 | $fields = $row->getElementsByTagName("field"); |
||
102 | foreach ($fields as $field) { |
||
103 | $attr = $field->attributes->getNamedItem("name"); |
||
104 | if (!is_null($attr)) { |
||
105 | $sr->addField($attr->nodeValue, $field->nodeValue); |
||
106 | } else { |
||
107 | throw new \InvalidArgumentException('Malformed anydataset file ' . basename($filepath)); |
||
108 | } |
||
109 | } |
||
110 | $sr->acceptChanges(); |
||
111 | $this->_collection[] = $sr; |
||
112 | } |
||
113 | $this->_currentRow = sizeof($this->_collection) - 1; |
||
0 ignored issues
–
show
|
|||
114 | } |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * Returns the AnyDataset XML representative structure. |
||
119 | * @return string XML String |
||
120 | */ |
||
121 | 1 | public function xml() |
|
122 | { |
||
123 | 1 | return $this->getDomObject()->saveXML(); |
|
124 | } |
||
125 | |||
126 | /** |
||
127 | * Returns the AnyDataset XmlDocument representive object |
||
128 | * @return \DOMDocument XmlDocument object |
||
129 | */ |
||
130 | 1 | public function getDomObject() |
|
131 | { |
||
132 | 1 | $anyDataSet = XmlUtil::createXmlDocumentFromStr("<anydataset/>"); |
|
133 | 1 | $nodeRoot = $anyDataSet->getElementsByTagName("anydataset")->item(0); |
|
134 | 1 | foreach ($this->_collection as $sr) { |
|
135 | 1 | $row = $sr->getDomObject(); |
|
136 | 1 | $nodeRow = $row->getElementsByTagName("row")->item(0); |
|
137 | 1 | $newRow = XmlUtil::createChild($nodeRoot, "row"); |
|
138 | 1 | XmlUtil::addNodeFromNode($newRow, $nodeRow); |
|
139 | 1 | } |
|
140 | |||
141 | 1 | return $anyDataSet; |
|
142 | } |
||
143 | |||
144 | /** |
||
145 | * |
||
146 | * @param string $file |
||
147 | * @throws DatabaseException |
||
148 | */ |
||
149 | 1 | public function save($file = null) |
|
150 | { |
||
151 | 1 | if (!is_null($file)) { |
|
152 | 1 | if (is_string($file)) { |
|
153 | 1 | $this->_path = $file; |
|
154 | 1 | } else { |
|
155 | throw new InvalidArgumentException('Invalid file name'); |
||
156 | } |
||
157 | 1 | } |
|
158 | |||
159 | 1 | if (is_null($this->_path)) { |
|
160 | throw new DatabaseException("No such file path to save anydataset"); |
||
161 | } |
||
162 | |||
163 | 1 | XmlUtil::saveXmlDocument($this->getDomObject(), $this->_path); |
|
164 | 1 | } |
|
165 | |||
166 | /** |
||
167 | * Append one row to AnyDataset. |
||
168 | * @param SingleRow $sr |
||
169 | * @return void |
||
170 | */ |
||
171 | 8 | public function appendRow($sr = null) |
|
172 | { |
||
173 | 8 | if (!is_null($sr)) { |
|
174 | 1 | if ($sr instanceof SingleRow) { |
|
175 | 1 | $this->_collection[] = $sr; |
|
176 | 1 | $sr->acceptChanges(); |
|
177 | 1 | } elseif (is_array($sr)) { |
|
178 | $this->_collection[] = new SingleRow($sr); |
||
179 | } else { |
||
180 | throw new InvalidArgumentException("You must pass an array or a SingleRow object"); |
||
181 | } |
||
182 | 1 | } else { |
|
183 | 7 | $sr = new SingleRow(); |
|
184 | 7 | $this->_collection[] = $sr; |
|
185 | 7 | $sr->acceptChanges(); |
|
186 | } |
||
187 | 8 | $this->_currentRow = count($this->_collection) - 1; |
|
188 | 8 | } |
|
189 | |||
190 | /** |
||
191 | * Enter description here... |
||
192 | * |
||
193 | * @param IteratorInterface $it |
||
194 | */ |
||
195 | 1 | public function import(IteratorInterface $it) |
|
196 | { |
||
197 | 1 | foreach ($it as $singleRow) { |
|
0 ignored issues
–
show
|
|||
198 | 1 | $this->appendRow($singleRow); |
|
199 | 1 | } |
|
200 | 1 | } |
|
201 | |||
202 | /** |
||
203 | * Insert one row before specified position. |
||
204 | * @param int $rowNumber |
||
205 | * @param mixed $row |
||
206 | */ |
||
207 | 1 | public function insertRowBefore($rowNumber, $row = null) |
|
208 | { |
||
209 | 1 | if ($rowNumber > count($this->_collection)) { |
|
210 | $this->appendRow($row); |
||
211 | } else { |
||
212 | 1 | $singleRow = $row; |
|
213 | 1 | if (!($row instanceof SingleRow)) { |
|
214 | 1 | $singleRow = new SingleRow($row); |
|
215 | 1 | } |
|
216 | 1 | array_splice($this->_collection, $rowNumber, 0, ''); |
|
217 | 1 | $this->_collection[$rowNumber] = $singleRow; |
|
218 | } |
||
219 | 1 | } |
|
220 | |||
221 | /** |
||
222 | * |
||
223 | * @param mixed $row |
||
224 | * @return null |
||
225 | */ |
||
226 | 2 | public function removeRow($row = null) |
|
227 | { |
||
228 | 2 | if (is_null($row)) { |
|
229 | $row = $this->_currentRow; |
||
230 | } |
||
231 | 2 | if ($row instanceof SingleRow) { |
|
232 | $i = 0; |
||
233 | foreach ($this->_collection as $sr) { |
||
234 | if ($sr->toArray() == $row->toArray()) { |
||
235 | $this->removeRow($i); |
||
236 | break; |
||
237 | } |
||
238 | $i++; |
||
239 | } |
||
240 | return; |
||
241 | } |
||
242 | |||
243 | 2 | if ($row == 0) { |
|
244 | 1 | $this->_collection = array_slice($this->_collection, 1); |
|
245 | 1 | } else { |
|
246 | 1 | $this->_collection = array_slice($this->_collection, 0, $row) + array_slice($this->_collection, $row); |
|
247 | } |
||
248 | 2 | } |
|
249 | |||
250 | /** |
||
251 | * Add a single string field to an existing row |
||
252 | * @param string $name - Field name |
||
253 | * @param string $value - Field value |
||
254 | * @return void |
||
255 | */ |
||
256 | 7 | public function addField($name, $value) |
|
257 | { |
||
258 | 7 | if ($this->_currentRow < 0) { |
|
259 | 1 | $this->appendRow(); |
|
260 | 1 | } |
|
261 | 7 | $this->_collection[$this->_currentRow]->addField($name, $value); |
|
262 | 7 | } |
|
263 | |||
264 | /** |
||
265 | * Get an Iterator filtered by an IteratorFilter |
||
266 | * @param IteratorFilter $itf |
||
267 | * @return IteratorInterface |
||
268 | */ |
||
269 | 7 | public function getIterator(IteratorFilter $itf = null) |
|
270 | { |
||
271 | 7 | if (is_null($itf)) { |
|
272 | 7 | return new AnyIterator($this->_collection); |
|
273 | } else { |
||
274 | return new AnyIterator($itf->match($this->_collection)); |
||
275 | } |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * @desc |
||
280 | * @param IteratorFilter $itf |
||
281 | * @param string $fieldName |
||
282 | * @return array |
||
283 | */ |
||
284 | 1 | View Code Duplication | public function getArray($itf, $fieldName) |
285 | { |
||
286 | 1 | $it = $this->getIterator($itf); |
|
287 | 1 | $result = array(); |
|
288 | 1 | while ($it->hasNext()) { |
|
289 | 1 | $sr = $it->moveNext(); |
|
290 | 1 | $result [] = $sr->getField($fieldName); |
|
291 | 1 | } |
|
292 | 1 | return $result; |
|
293 | } |
||
294 | |||
295 | /** |
||
296 | * |
||
297 | * @param string $field |
||
298 | * @return void |
||
299 | */ |
||
300 | 1 | public function sort($field) |
|
301 | { |
||
302 | 1 | if (count($this->_collection) == 0) { |
|
303 | return; |
||
304 | } |
||
305 | |||
306 | 1 | $this->_collection = $this->quickSortExec($this->_collection, $field); |
|
307 | |||
308 | 1 | return; |
|
309 | } |
||
310 | |||
311 | protected function quickSortExec($seq, $field) |
||
312 | { |
||
313 | if (!count($seq)) return $seq; |
||
314 | |||
315 | $k = $seq [0]; |
||
316 | $x = $y = array(); |
||
317 | |||
318 | $cntSeq = count($seq); |
||
319 | for ($i = 1; $i < $cntSeq; $i ++) { |
||
320 | if ($seq[$i]->getField($field) <= $k->getField($field)) { |
||
321 | $x [] = $seq [$i]; |
||
322 | } else { |
||
323 | $y [] = $seq [$i]; |
||
324 | } |
||
325 | } |
||
326 | |||
327 | return array_merge($this->quickSortExec($x, $field), array($k), $this->quickSortExec($y, $field)); |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * @param $document |
||
332 | * @return array|string |
||
333 | */ |
||
334 | public static function fixUTF8($document) |
||
335 | { |
||
336 | return Encoding::fixUTF8(Encoding::removeBOM($document), Encoding::ICONV_TRANSLIT); |
||
337 | } |
||
338 | } |
||
339 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.