Completed
Push — master ( d95d77...bb1a50 )
by Petr
07:29
created

ArraySource   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 207
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 94.95%

Importance

Changes 9
Bugs 2 Features 1
Metric Value
wmc 36
c 9
b 2
f 1
lcom 1
cbo 5
dl 0
loc 207
ccs 94
cts 99
cp 0.9495
rs 8.8

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
C makeWhere() 0 35 8
D compare() 0 41 10
A getCount() 0 4 1
A getData() 0 4 1
A filter() 0 6 2
A limit() 0 4 1
C sort() 0 27 7
B suggest() 0 26 5
1
<?php
2
3
/**
4
 * This file is part of the Grido (http://grido.bugyik.cz)
5
 *
6
 * Copyright (c) 2011 Petr Bugyík (http://petr.bugyik.cz)
7
 *
8
 * For the full copyright and license information, please view
9
 * the file LICENSE.md that was distributed with this source code.
10
 */
11
12
namespace Grido\DataSources;
13
14
use Grido\Exception;
15
use Grido\Components\Filters\Condition;
16
17
use Nette\Utils\Strings;
18
19
/**
20
 * Array data source.
21
 *
22
 * @package     Grido
23
 * @subpackage  DataSources
24
 * @author      Josef Kříž <[email protected]>
25
 * @author      Petr Bugyík
26
 *
27
 * @property-read array $data
28
 * @property-read int $count
29
 */
30 1
class ArraySource extends \Nette\Object implements IDataSource
31
{
32
    /** @var array */
33
    protected $data;
34
35
    /**
36
     * @param array $data
37
     */
38
    public function __construct(array $data)
39
    {
40 1
        $this->data = $data;
41 1
    }
42
43
    /**
44
     * This method needs tests!
45
     * @param Condition $condition
46
     * @param array $data
47
     * @return array
48
     */
49
    protected function makeWhere(Condition $condition, array $data = NULL)
50
    {
51
        $data = $data === NULL
52 1
            ? $this->data
53 1
            : $data;
54
55 1
        return array_filter($data, function ($row) use ($condition) {
56 1
            if ($condition->callback) {
57 1
                return call_user_func_array($condition->callback, [$condition->value, $row]);
58 1
            }
59
60 1
            $i = 0;
61 1
            $results = [];
62 1
            foreach ($condition->column as $column) {
63 1
                if (Condition::isOperator($column)) {
64 1
                    $results[] = " $column ";
65
66 1
                } else {
67 1
                    $i = count($condition->condition) > 1 ? $i : 0;
68 1
                    $results[] = (int) $this->compare(
69 1
                        $row[$column],
70 1
                        $condition->condition[$i],
71 1
                        isset($condition->value[$i]) ? $condition->value[$i] : NULL
72 1
                    );
73
74 1
                    $i++;
75
                }
76 1
            }
77
78 1
            $result = implode('', $results);
79 1
            return count($condition->column) === 1
80 1
                ? (bool) $result
81 1
                : eval("return $result;"); // QUESTION: How to remove this eval? hmmm?
1 ignored issue
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
82 1
        });
83
    }
84
85
    /**
86
     * @param string $actual
87
     * @param string $condition
88
     * @param mixed $expected
89
     * @throws Exception
90
     * @return bool
91
     */
92
    public function compare($actual, $condition, $expected)
93
    {
94 1
        $expected = (array) $expected;
95 1
        $expected = current($expected);
96 1
        $cond = str_replace(' ?', '', $condition);
97
98 1
        if ($cond === 'LIKE') {
99 1
            $actual = Strings::toAscii($actual);
100 1
            $expected = Strings::toAscii($expected);
101
102 1
            $pattern = str_replace('%', '(.|\s)*', preg_quote($expected, '/'));
103 1
            return (bool) preg_match("/^{$pattern}$/i", $actual);
104
105 1
        } elseif ($cond === '=') {
106 1
            return $actual == $expected;
107
108 1
        } elseif ($cond === '<>') {
109 1
            return $actual != $expected;
110
111 1
        } elseif ($cond === 'IS NULL') {
112 1
            return $actual === NULL;
113
114 1
        } elseif ($cond === 'IS NOT NULL') {
115 1
            return $actual !== NULL;
116
117 1
        } elseif ($cond === '<') {
118 1
            return (int) $actual < $expected;
119
120 1
        } elseif ($cond === '<=') {
121 1
            return (int) $actual <= $expected;
122
123 1
        } elseif ($cond === '>') {
124 1
            return (int) $actual > $expected;
125
126 1
        } elseif ($cond === '>=') {
127 1
            return (int) $actual >= $expected;
128
129
        } else {
130 1
            throw new Exception("Condition '$condition' not implemented yet.");
131
        }
132
    }
133
134
    /*********************************** interface IDataSource ************************************/
135
136
    /**
137
     * @return int
138
     */
139
    public function getCount()
140
    {
141 1
        return count($this->data);
142
    }
143
144
    /**
145
     * @return array
146
     */
147
    public function getData()
148
    {
149 1
        return $this->data;
150
    }
151
152
    /**
153
     * @param array $conditions
154
     */
155
    public function filter(array $conditions)
156
    {
157 1
        foreach ($conditions as $condition) {
158 1
            $this->data = $this->makeWhere($condition);
159 1
        }
160 1
    }
161
162
    /**
163
     * @param int $offset
164
     * @param int $limit
165
     */
166
    public function limit($offset, $limit)
167
    {
168 1
        $this->data = array_slice($this->data, $offset, $limit);
169 1
    }
170
171
    /**
172
     * @param array $sorting
173
     * @throws Exception
174
     */
175
    public function sort(array $sorting)
176
    {
177 1
        if (count($sorting) > 1) {
178
            throw new Exception('Multi-column sorting is not implemented yet.');
179
        }
180
181 1
        foreach ($sorting as $column => $sort) {
182 1
            $data = [];
183 1
            foreach ($this->data as $item) {
184 1
                $sorter = (string) $item[$column];
185 1
                $data[$sorter][] = $item;
186 1
            }
187
188 1
            if ($sort === 'ASC') {
189 1
                ksort($data);
190 1
            } else {
191 1
                krsort($data);
192
            }
193
194 1
            $this->data = [];
195 1
            foreach ($data as $i) {
196 1
                foreach ($i as $item) {
197 1
                    $this->data[] = $item;
198 1
                }
199 1
            }
200 1
        }
201 1
    }
202
203
    /**
204
     * @param mixed $column
205
     * @param array $conditions
206
     * @param int $limit
207
     * @return array
208
     * @throws Exception
209
     */
210
    public function suggest($column, array $conditions, $limit)
211
    {
212 1
        $data = $this->data;
213 1
        foreach ($conditions as $condition) {
214 1
            $data = $this->makeWhere($condition, $data);
215 1
        }
216
217 1
        array_slice($data, 1, $limit);
218
219 1
        $items = [];
220 1
        foreach ($data as $row) {
221 1
            if (is_string($column)) {
222 1
                $value = (string) $row[$column];
223 1
            } elseif (is_callable($column)) {
224
                $value = (string) $column($row);
225
            } else {
226
                $type = gettype($column);
227
                throw new Exception("Column of suggestion must be string or callback, $type given.");
228
            }
229
230 1
            $items[$value] = \Latte\Runtime\Filters::escapeHtml($value);
231 1
        }
232
233 1
        sort($items);
234 1
        return array_values($items);
235
    }
236
}
237