Test Failed
Pull Request — master (#10)
by Joao
05:30
created

AnyDataset   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 301
Duplicated Lines 0 %

Test Coverage

Coverage 87.39%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 43
eloc 106
c 1
b 0
f 0
dl 0
loc 301
rs 8.96
ccs 104
cts 119
cp 0.8739

15 Methods

Rating   Name   Duplication   Size   Complexity  
A import() 0 4 2
A defineSavePath() 0 15 5
A getIterator() 0 7 2
A getFilename() 0 3 1
A createFrom() 0 22 5
A appendRow() 0 17 4
A xml() 0 3 1
A insertRowBefore() 0 11 3
A save() 0 8 2
A removeRow() 0 21 6
A __construct() 0 9 2
A sort() 0 9 2
A quickSortExec() 0 22 4
A addField() 0 6 2
A getArray() 0 9 2

How to fix   Complexity   

Complex Class

Complex classes like AnyDataset often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AnyDataset, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace ByJG\AnyDataset\Core;
4
5
use ByJG\AnyDataset\Core\Exception\DatabaseException;
6
use ByJG\AnyDataset\Core\Formatter\XmlFormatter;
7
use ByJG\Util\XmlUtil;
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
15
 * and IteratorFilter. Each row have a class Row.
16
17
 * XML Structure
18
 * <code>
19
 * <anydataset>
20
 *    <row>
21
 *        <field name="fieldname1">value of fieldname 1</field>
22
 *        <field name="fieldname2">value of fieldname 2</field>
23
 *        <field name="fieldname3">value of fieldname 3</field>
24
 *    </row>
25
 *    <row>
26
 *        <field name="fieldname1">value of fieldname 1</field>
27
 *        <field name="fieldname4">value of fieldname 4</field>
28
 *    </row>
29
 * </anydataset>
30
 * </code>
31
32
 * How to use:
33
 * <code>
34
 * $any = new AnyDataset();
35
 * </code>
36
37
 *
38
*@see Row
39
 * @see AnyIterator
40
 * @see IteratorFilter
41
42
 */
43
class AnyDataset
44
{
45
46
    /**
47
     * Internal structure represent the current Row
48
     *
49
     * @var Row[]
50
     */
51
    private $collection;
52
53
    /**
54
     * Current node anydataset works
55
     * @var int
56
     */
57
    private $currentRow;
58
59
    /**
60
     * Path to anydataset file
61
     * @var string
62
     */
63
    private $filename;
64
65
    /**
66
     * @param string $filename
67
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
68
     * @throws \ByJG\Util\Exception\XmlUtilException
69 15
     */
70
    public function __construct($filename = null)
71 15
    {
72 15
        $this->collection = array();
73
        $this->currentRow = -1;
74 15
75
        $this->filename = null;
76 15
        $this->defineSavePath($filename, function () {
77 7
            if (!is_null($this->filename)) {
0 ignored issues
show
introduced by
The condition is_null($this->filename) is always false.
Loading history...
78
                $this->createFrom($this->filename);
79 15
            }
80 15
        });
81
    }
82
83
    public function getFilename()
84
    {
85
        return $this->filename;
86
    }
87 15
88
    private function defineSavePath($file, $closure)
89 15
    {
90 7
        if (!is_null($file)) {
91
            if (!is_string($file)) {
92
                throw new InvalidArgumentException('I expected a string as a file name');
93
            }
94 7
95 7
            $ext = pathinfo($file, PATHINFO_EXTENSION);
96 7
            if (empty($ext) && substr($file, 0, 6) !== "php://") {
97
                $file .= '.anydata.xml';
98 7
            }
99
            $this->filename = $file;
100
        }
101 15
102 15
        $closure();
103
    }
104
105
    /**
106
     * Private method used to read and populate anydataset class from specified file
107
     *
108
     * @param string $filepath Path and Filename to be read
109
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
110
     * @throws \ByJG\Util\Exception\XmlUtilException
111 7
     */
112
    private function createFrom($filepath)
113 7
    {
114 7
        if (file_exists($filepath)) {
115 7
            $anyDataSet = XmlUtil::createXmlDocumentFromFile($filepath);
116
            $this->collection = array();
117 7
118 7
            $rows = $anyDataSet->getElementsByTagName("row");
119 7
            foreach ($rows as $row) {
120 7
                $sr = new Row();
121 7
                $fields = $row->getElementsByTagName("field");
122 7
                foreach ($fields as $field) {
123 7
                    $attr = $field->attributes->getNamedItem("name");
124
                    if (is_null($attr)) {
125
                        throw new InvalidArgumentException('Malformed anydataset file ' . basename($filepath));
126
                    }
127 7
128
                    $sr->addField($attr->nodeValue, $field->nodeValue);
129 7
                }
130 7
                $sr->acceptChanges();
131
                $this->collection[] = $sr;
132 7
            }
133
            $this->currentRow = count($this->collection) - 1;
134 7
        }
135
    }
136
137
    /**
138
     * Returns the AnyDataset XML representative structure.
139
     *
140
     * @return string XML String
141
     * @throws \ByJG\Util\Exception\XmlUtilException
142 1
     */
143
    public function xml()
144 1
    {
145
        return (new XmlFormatter($this->getIterator()))->toText();
146
    }
147
148
    /**
149
     * @param string $filename
150
     * @throws DatabaseException
151
     * @throws \ByJG\Util\Exception\XmlUtilException
152
     */
153 3
    public function save($filename = null)
154
    {
155 3
        $this->defineSavePath($filename, function () {
156 3
            if (is_null($this->filename)) {
0 ignored issues
show
introduced by
The condition is_null($this->filename) is always false.
Loading history...
157 3
                throw new DatabaseException("No such file path to save anydataset");
158 3
            }
159 3
160 3
            (new XmlFormatter($this->getIterator()))->saveToFile($this->filename);
161 3
        });
162
    }
163
164 3
    /**
165
     * Append one row to AnyDataset.
166
     *
167
     * @param Row|array|\stdClass|object $singleRow
168
     * @return void
169
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
170
     */
171
    public function appendRow($singleRow = [])
172 2
    {
173
        if (!empty($singleRow)) {
174
            if ($singleRow instanceof Row) {
175 2
                $this->collection[] = $singleRow;
176
                $singleRow->acceptChanges();
177
            } elseif (is_array($singleRow)) {
178
                $this->collection[] = new Row($singleRow);
179 2
            } else {
180 2
                throw new InvalidArgumentException("You must pass an array or a Row object");
181 2
            }
182
        } else {
183
            $singleRow = new Row();
184
            $this->collection[] = $singleRow;
185
            $singleRow->acceptChanges();
186
        }
187
        $this->currentRow = count($this->collection) - 1;
188
    }
189
190 11
    /**
191
     * Enter description here...
192 11
     *
193 5
     * @param GenericIterator $iterator
194 3
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
195 3
     */
196 3
    public function import($iterator)
197 3
    {
198
        foreach ($iterator as $singleRow) {
199 5
            $this->appendRow($singleRow);
200
        }
201
    }
202 6
203 6
    /**
204 6
     * Insert one row before specified position.
205
     *
206 11
     * @param int $rowNumber
207 11
     * @param mixed $row
208
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
209
     */
210
    public function insertRowBefore($rowNumber, $row = null)
211
    {
212
        if ($rowNumber > count($this->collection)) {
213
            $this->appendRow($row);
214
        } else {
215 2
            $singleRow = $row;
216
            if (!($row instanceof Row)) {
217 2
                $singleRow = new Row($row);
218 2
            }
219
            array_splice($this->collection, $rowNumber, 0, '');
220 2
            $this->collection[$rowNumber] = $singleRow;
221
        }
222
    }
223
224
    /**
225
     *
226
     * @param mixed $row
227
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
228
     */
229 1
    public function removeRow($row = null)
230
    {
231 1
        if (is_null($row)) {
232
            $row = $this->currentRow;
233
        }
234 1
        if ($row instanceof Row) {
235 1
            $iPos = 0;
236 1
            foreach ($this->collection as $sr) {
237
                if ($sr->toArray() == $row->toArray()) {
238 1
                    $this->removeRow($iPos);
239 1
                    break;
240
                }
241 1
                $iPos++;
242
            }
243
            return;
244
        }
245
246
        if ($row == 0) {
247
            $this->collection = array_slice($this->collection, 1);
248 2
        } else {
249
            $this->collection = array_slice($this->collection, 0, $row) + array_slice($this->collection, $row);
250 2
        }
251
    }
252
253 2
    /**
254
     * Add a single string field to an existing row
255
     *
256
     * @param string $name - Field name
257
     * @param string $value - Field value
258
     * @return void
259
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
260
     */
261
    public function addField($name, $value)
262
    {
263
        if ($this->currentRow < 0) {
264
            $this->appendRow();
265 2
        }
266 1
        $this->collection[$this->currentRow]->addField($name, $value);
267
    }
268 1
269
    /**
270 2
     * Get an Iterator filtered by an IteratorFilter
271
     * @param IteratorFilter $itf
272
     * @return GenericIterator
273
     */
274
    public function getIterator(IteratorFilter $itf = null)
275
    {
276
        if (is_null($itf)) {
277
            return new AnyIterator($this->collection);
278
        }
279
280 5
        return new AnyIterator($itf->match($this->collection));
281
    }
282 5
283 2
    /**
284
     * Undocumented function
285 5
     *
286 5
     * @param string $fieldName
287
     * @param IteratorFilter $itf
288
     * @return array
289
     */
290
    public function getArray($fieldName, $itf = null)
291
    {
292
        $iterator = $this->getIterator($itf);
293 14
        $result = array();
294
        while ($iterator->hasNext()) {
295 14
            $singleRow = $iterator->moveNext();
296 14
            $result[] = $singleRow->get($fieldName);
297
        }
298
        return $result;
299 1
    }
300
301
    /**
302
     *
303
     * @param string $field
304
     * @return void
305
     */
306
    public function sort($field)
307
    {
308 1
        if (count($this->collection) == 0) {
309
            return;
310 1
        }
311 1
312 1
        $this->collection = $this->quickSortExec($this->collection, $field);
313 1
314 1
        return;
315
    }
316 1
317
    /**
318
     * @param Row[] $seq
319
     * @param $field
320
     * @return array
321
     */
322
    protected function quickSortExec($seq, $field)
323
    {
324 1
        if (!count($seq)) {
325
            return $seq;
326 1
        }
327
328
        $key = $seq[0];
329
        $left = $right = array();
330 1
331
        $cntSeq = count($seq);
332 1
        for ($i = 1; $i < $cntSeq; $i ++) {
333
            if ($seq[$i]->get($field) <= $key->get($field)) {
334
                $left[] = $seq[$i];
335
            } else {
336
                $right[] = $seq[$i];
337
            }
338
        }
339
340 1
        return array_merge(
341
            $this->quickSortExec($left, $field),
342 1
            [ $key ],
343 1
            $this->quickSortExec($right, $field)
344
        );
345
    }
346
}
347