DataReader   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 236
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 89
dl 0
loc 236
rs 10
c 0
b 0
f 0
wmc 29

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 17 3
A __destruct() 0 3 1
A getIsClosed() 0 3 1
A next() 0 6 1
A setEntries() 0 11 2
A valid() 0 3 1
B toArray() 0 30 6
A key() 0 3 1
A close() 0 13 3
A count() 0 3 1
B current() 0 39 7
A rewind() 0 9 2
1
<?php
2
/**
3
 * @link      https://github.com/chrmorandi/yii2-ldap for the source repository
4
 * @package   yii2-ldap
5
 * @author    Christopher Mota <[email protected]>
6
 * @license   MIT License - view the LICENSE file that was distributed with this source code.
7
 * @since     1.0.0
8
 */
9
10
namespace chrmorandi\ldap;
11
12
use Countable;
13
use Iterator;
14
use Yii;
15
use yii\base\InvalidCallException;
16
use yii\base\BaseObject;
17
use yii\caching\Cache;
18
use yii\caching\TagDependency;
19
20
/**
21
 * DataReader represents a forward-only stream of rows from a query result set.
22
 *
23
 * The method returns [[toArray()]] all the rows in a single array.
24
 * Rows of data can also be read by iterating through the reader. For example,
25
 *
26
 * ```php
27
 * $command = $connection->createCommand('SELECT * FROM post');
28
 * $reader = $command->query();
29
 *
30
 * while ($row = $reader->read()) {
31
 *     $rows[] = $row;
32
 * }
33
 *
34
 * // equivalent to:
35
 * foreach ($reader as $row) {
36
 *     $rows[] = $row;
37
 * }
38
 *
39
 * // equivalent to:
40
 * $rows = $reader->readAll();
41
 * ```
42
 *
43
 * Note that since DataReader is a forward-only stream, you can only traverse it once.
44
 * Doing it the second time will throw an exception.
45
 *
46
 * @property integer $columnCount The number of columns in the result set. This property is read-only.
47
 * @property bool $isClosed Whether the reader is closed or not. This property is read-only.
48
 * @property integer $rowCount Number of rows contained in the result. This property is read-only.
49
 *
50
 * @author Christopher Mota <[email protected]>
51
 * @since 1.0.0
52
 */
53
class DataReader extends BaseObject implements Iterator, Countable
54
{
55
    const CACHE_TAG = 'ldap.data';
56
57
    /**
58
     * @var array data
59
     */
60
    private $entries = [];
61
62
    /**
63
     * @var Connection
64
     */
65
    private $_conn;
66
    private $_closed = false;
67
    private $_row;
68
    private $_index  = -1;
69
    private $_count  = 0;
70
    private $_results;
71
72
    /**
73
     * Constructor.
74
     * @param Connection $conn connection interact with result
75
     * @param resource[]|resource $results result array of search in ldap directory
76
     * @param array $config name-value pairs that will be used to initialize the object properties
77
     */
78
    public function __construct(Connection $conn, $results, $config = [])
79
    {
80
        $this->_conn    = $conn;
81
        $this->_results = $results;
82
83
        if (is_array($this->_results)) {
84
            foreach ($this->_results as $result) {
85
                $this->_count += $this->_conn->countEntries($result);
86
                //$this->setEntries($result);
87
            }
88
        } else {
89
            $this->_count += $this->_conn->countEntries($this->_results);
90
            //$this->setEntries($this->_results);
91
        }
92
93
94
        parent::__construct($config);
95
    }
96
97
    public function __destruct()
98
    {
99
        $this->close();
100
    }
101
102
    /**
103
     *
104
     * @param resource $result
105
     * @return void
106
     */
107
    protected function setEntries($result)
108
    {
109
        $identifier = $this->_conn->getFirstEntry($result);
110
111
        while (false !== $identifier) {
112
            $this->entries[] = [
113
                'resource'  => $identifier,
114
                'sortValue' => '',
115
            ];
116
117
            $identifier = $this->_conn->getNextEntry($identifier);
118
        }
119
    }
120
121
    /**
122
     * Get all entries as an array
123
     * @return array
124
     */
125
    public function toArray()
126
    {
127
        if ($this->_count <= 0) {
128
            return [];
129
        }
130
131
        $token = 'Get entries with limit pagination ' . $this->_conn->pageSize;
132
        Yii::beginProfile($token, __METHOD__);
133
        if ($this->_conn->offset > 0) {
134
            $this->setEntries($this->_results[intval($this->_conn->offset / $this->_conn->pageSize)]);
135
        } else {
136
            if (is_array($this->_results)) {
137
                foreach ($this->_results as $result) {
138
                    $this->setEntries($result);
139
                }
140
            } else {
141
                $this->setEntries($this->_results);
142
            }
143
        }
144
        Yii::endProfile($token, __METHOD__);
145
146
        $token = 'Get Attributes of entries with limit pagination in ' . $this->_conn->pageSize;
147
        Yii::beginProfile($token, __METHOD__);
148
        $data  = [];
149
        foreach ($this as $item) {
150
            $data[] = $item;
151
        }
152
        Yii::endProfile($token, __METHOD__);
153
154
        return $data;
155
    }
156
157
    /**
158
     * Closes the reader.
159
     * This frees up the resources allocated for executing this SQL statement.
160
     * Read attempts after this method call are unpredictable.
161
     */
162
    public function close()
163
    {
164
        if (is_array($this->_results)) {
165
            foreach ($this->_results as $result) {
166
                $this->_conn->freeResult($result);
167
            }
168
        } else {
169
            $this->_conn->freeResult($this->_results);
170
        }
171
172
        $this->_closed  = true;
173
        $this->_results = null;
174
        $this->_row     = null;
175
    }
176
177
    /**
178
     * whether the reader is closed or not.
179
     * @return bool whether the reader is closed or not.
180
     */
181
    public function getIsClosed()
182
    {
183
        return $this->_closed;
184
    }
185
186
    /**
187
     * Returns the number of rows in the result set.
188
     * This method is required by the Countable interface.
189
     * @return integer number of entries stored in the result.
190
     */
191
    public function count()
192
    {
193
        return $this->_count;
194
    }
195
196
    /**
197
     * Resets the iterator to the initial state.
198
     * This method is required by the interface [[\Iterator]].
199
     * @throws InvalidCallException if this method is invoked twice
200
     */
201
    public function rewind()
202
    {
203
        if ($this->_index < 0) {
204
            reset($this->entries);
205
            $nextEntry    = current($this->entries);
206
            $this->_row   = $nextEntry['resource'];
207
            $this->_index = 0;
208
        } else {
209
            throw new InvalidCallException('DataReader cannot rewind. It is a forward-only reader.');
210
        }
211
    }
212
213
    /**
214
     * Returns the result of the current item.
215
     * This method is required by the interface [[\Iterator]].
216
     * @return string the index of the current row.
217
     */
218
    public function key()
219
    {
220
        return $this->_conn->getDn($this->_row);
221
    }
222
223
    /**
224
     * Returns the current row.
225
     * This method is required by the interface [[\Iterator]].
226
     * @return mixed the current row.
227
     */
228
    public function current()
229
    {
230
        $entry = ['dn' => $this->key()];
231
232
        $info = $this->_conn->getCacheInfo(3600, new TagDependency(['tags' => self::CACHE_TAG]));
233
        if ($info !== NULL) {
234
            /* @var $cache Cache */
235
            $cache    = $info[0];
236
            $cacheKey = [__CLASS__, $entry['dn']];
237
            $result   = $cache->get($cacheKey);
238
            if (is_array($result) && isset($result[0])) {
239
                Yii::debug('Query result served from cache', __METHOD__);
240
                return $result[0];
241
            }
242
        }
243
244
        $name = $this->_conn->getFirstAttribute($this->_row);
245
246
        while ($name) {
247
            $data = $this->_conn->getValuesLen($this->_row, $name);
248
249
            if (isset($data['count'])) {
250
                unset($data['count']);
251
            }
252
253
            $attrName         = $name;
254
            $entry[$attrName] = implode(",", $data);
255
256
            $name = $this->_conn->getNextAttribute($this->_row);
257
        }
258
259
        ksort($entry, SORT_LOCALE_STRING);
260
261
        if (isset($cache, $cacheKey, $info)) {
262
            $cache->set($cacheKey, [$entry], $info[1], $info[2]);
263
            Yii::debug('Saved query result in cache', __METHOD__);
264
        }
265
266
        return $entry;
267
    }
268
269
    /**
270
     * Moves the internal pointer to the next row.
271
     * This method is required by the interface [[\Iterator]].
272
     */
273
    public function next()
274
    {
275
        next($this->entries);
276
        $nextEntry  = current($this->entries);
277
        $this->_row = $nextEntry['resource'];
278
        $this->_index++;
279
    }
280
281
    /**
282
     * Returns whether there is a row of resource at current position.
283
     * This method is required by the interface [[\Iterator]].
284
     * @return bool whether there is a row of data at current position.
285
     */
286
    public function valid()
287
    {
288
        return (is_resource($this->_row));
289
    }
290
291
}
292