Completed
Pull Request — 3.4 (#46)
by David
12:52 queued 01:27
created

InnerResultIterator::rewind()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 4
nc 1
nop 0
1
<?php
2
namespace Mouf\Database\TDBM;
3
4
use Doctrine\DBAL\Driver\Connection;
5
use Doctrine\DBAL\Statement;
6
use Mouf\Database\MagicQuery;
7
8
/*
9
 Copyright (C) 2006-2016 David Négrier - THE CODING MACHINE
10
11
 This program is free software; you can redistribute it and/or modify
12
 it under the terms of the GNU General Public License as published by
13
 the Free Software Foundation; either version 2 of the License, or
14
 (at your option) any later version.
15
16
 This program is distributed in the hope that it will be useful,
17
 but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 GNU General Public License for more details.
20
21
 You should have received a copy of the GNU General Public License
22
 along with this program; if not, write to the Free Software
23
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24
 */
25
26
27
/**
28
 * Iterator used to retrieve results.
29
 *
30
 */
31
class InnerResultIterator implements \Iterator, \Countable, \ArrayAccess {
32
33
	/**
34
	 *
35
	 * @var Statement
36
	 */
37
	protected $statement;
38
	
39
	protected $fetchStarted = false;
40
	private $objectStorage;
41
	private $className;
42
43
	private $tdbmService;
44
	private $magicSql;
45
	private $parameters;
46
	private $limit;
47
	private $offset;
48
	private $columnDescriptors;
49
	private $magicQuery;
50
51
	/**
52
	 * The key of the current retrieved object.
53
	 *
54
	 * @var int
55
	 */
56
	protected $key = -1;
57
58
	protected $current = null;
59
60
	private $databasePlatform;
61
62
	private $totalCount;
0 ignored issues
show
Unused Code introduced by
The property $totalCount is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
63
	
64 View Code Duplication
	public function __construct($magicSql, array $parameters, $limit, $offset, array $columnDescriptors, $objectStorage, $className, TDBMService $tdbmService, MagicQuery $magicQuery)
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...
65
	{
66
		$this->magicSql = $magicSql;
67
		$this->objectStorage = $objectStorage;
68
		$this->className = $className;
69
		$this->tdbmService = $tdbmService;
70
		$this->parameters = $parameters;
71
		$this->limit = $limit;
72
		$this->offset = $offset;
73
		$this->columnDescriptors = $columnDescriptors;
74
		$this->magicQuery = $magicQuery;
75
		$this->databasePlatform = $this->tdbmService->getConnection()->getDatabasePlatform();
76
	}
77
78
	protected function executeQuery() {
79
		$sql = $this->magicQuery->build($this->magicSql, $this->parameters);
80
		$sql = $this->tdbmService->getConnection()->getDatabasePlatform()->modifyLimitQuery($sql, $this->limit, $this->offset);
81
82
		$this->statement = $this->tdbmService->getConnection()->executeQuery($sql, $this->parameters);
83
84
		$this->fetchStarted = true;
85
	}
86
87
	/**
88
	 * Counts found records (this is the number of records fetched, taking into account the LIMIT and OFFSET settings)
89
	 * @return int
90
	 */
91
	public function count()
92
	{
93
		if (!$this->fetchStarted) {
94
			$this->executeQuery();
95
		}
96
		return $this->statement->rowCount();
97
	}
98
99
	/**
100
	 * Fetches record at current cursor.
101
	 * @return AbstractTDBMObject|null
102
	 */
103
	public function current()
104
	{
105
		return $this->current;
106
	}
107
108
	/**
109
	 * Returns the current result's key
110
	 * @return int
111
	 */
112
	public function key()
113
	{
114
		return $this->key;
115
	}
116
117
	/**
118
	 * Advances the cursor to the next result.
119
	 * Casts the database result into one (or several) beans.
120
	 */
121
	public function next()
122
	{
123
		$row = $this->statement->fetch(\PDO::FETCH_NUM);
124
		if ($row) {
125
126
			// array<tablegroup, array<table, array<column, value>>>
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
127
			$beansData = [];
128
			foreach ($row as $i => $value) {
129
				$columnDescriptor = $this->columnDescriptors[$i];
130
				// Let's cast the value according to its type
131
				$value = $columnDescriptor['type']->convertToPHPValue($value, $this->databasePlatform);
132
133
				$beansData[$columnDescriptor['tableGroup']][$columnDescriptor['table']][$columnDescriptor['column']] = $value;
134
			}
135
136
			$firstBean = true;
137
			foreach ($beansData as $beanData) {
138
139
				// Let's find the bean class name associated to the bean.
140
141
				list($actualClassName, $mainBeanTableName) = $this->tdbmService->_getClassNameFromBeanData($beanData);
142
143
144
				if ($this->className !== null) {
145
					$actualClassName = $this->className;
146
				}
147
148
				// Must we create the bean? Let's see in the cache if we have a mapping DbRow?
149
				// Let's get the first object mapping a row:
150
				// We do this loop only for the first table
151
152
				$primaryKeys = $this->tdbmService->_getPrimaryKeysFromObjectData($mainBeanTableName, $beanData[$mainBeanTableName]);
153
				$hash = $this->tdbmService->getObjectHash($primaryKeys);
154
155
				if ($this->objectStorage->has($mainBeanTableName, $hash)) {
156
					$dbRow = $this->objectStorage->get($mainBeanTableName, $hash);
157
					$bean = $dbRow->getTDBMObject();
158
				} else {
159
					// Let's construct the bean
160
					if (!isset($reflectionClassCache[$actualClassName])) {
161
						$reflectionClassCache[$actualClassName] = new \ReflectionClass($actualClassName);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$reflectionClassCache was never initialized. Although not strictly required by PHP, it is generally a good practice to add $reflectionClassCache = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
162
					}
163
					// Let's bypass the constructor when creating the bean!
164
					$bean = $reflectionClassCache[$actualClassName]->newInstanceWithoutConstructor();
0 ignored issues
show
Bug introduced by
The variable $reflectionClassCache does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
165
					$bean->_constructFromData($beanData, $this->tdbmService);
166
				}
167
168
				// The first bean is the one containing the main table.
169
				if ($firstBean) {
170
					$firstBean = false;
171
					$this->current = $bean;
172
				}
173
			}
174
175
			$this->key++;
176
		} else {
177
			$this->current = null;
178
		}
179
	}
180
	
181
	/**
182
	 * Moves the cursor to the beginning of the result set
183
	 */
184
	public function rewind()
185
	{
186
		$this->executeQuery();
187
		$this->key = -1;
188
		$this->next();
189
	}
190
	/**
191
	 * Checks if the cursor is reading a valid result.
192
	 *
193
	 * @return boolean
194
	 */
195
	public function valid()
196
	{
197
		return $this->current !== null;
198
	}
199
200
	/**
201
	 * Fetches all records (this could impact into your site performance) and rewinds the cursor
202
	 * @param boolean $asRecords Bind into record class?
203
	 * @return array[Record_PDO]|array[array] Array of records or arrays (depends on $asRecords)
204
	 */
205
	/*public function getAll($asRecords = true)
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
206
	{
207
		$all = array();
208
		$this->rewind();
209
		foreach ($this->pdoStatement as $id => $doc) {
210
			if ($asRecords)
211
				$all[$id] = $this->cast($doc);
212
			else
213
				$all[$id] = $doc;
214
		}
215
		return $all;
216
	}*/
217
	/**
218
	 * @return PDOStatement
219
	 */
220
	/*public function getPDOStatement()
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
221
	{
222
		return $this->pdoStatement;
223
	}*/
224
225
	/**
226
	 * Whether a offset exists
227
	 * @link http://php.net/manual/en/arrayaccess.offsetexists.php
228
	 * @param mixed $offset <p>
229
	 * An offset to check for.
230
	 * </p>
231
	 * @return boolean true on success or false on failure.
232
	 * </p>
233
	 * <p>
234
	 * The return value will be casted to boolean if non-boolean was returned.
235
	 * @since 5.0.0
236
	 */
237
	public function offsetExists($offset)
238
	{
239
		throw new TDBMInvalidOperationException('You cannot access this result set via index because it was fetched in CURSOR mode. Use ARRAY_MODE instead.');
240
	}
241
242
	/**
243
	 * Offset to retrieve
244
	 * @link http://php.net/manual/en/arrayaccess.offsetget.php
245
	 * @param mixed $offset <p>
246
	 * The offset to retrieve.
247
	 * </p>
248
	 * @return mixed Can return all value types.
249
	 * @since 5.0.0
250
	 */
251
	public function offsetGet($offset)
252
	{
253
		throw new TDBMInvalidOperationException('You cannot access this result set via index because it was fetched in CURSOR mode. Use ARRAY_MODE instead.');
254
	}
255
256
	/**
257
	 * Offset to set
258
	 * @link http://php.net/manual/en/arrayaccess.offsetset.php
259
	 * @param mixed $offset <p>
260
	 * The offset to assign the value to.
261
	 * </p>
262
	 * @param mixed $value <p>
263
	 * The value to set.
264
	 * </p>
265
	 * @return void
266
	 * @since 5.0.0
267
	 */
268
	public function offsetSet($offset, $value)
269
	{
270
		throw new TDBMInvalidOperationException('You can set values in a TDBM result set.');
271
	}
272
273
	/**
274
	 * Offset to unset
275
	 * @link http://php.net/manual/en/arrayaccess.offsetunset.php
276
	 * @param mixed $offset <p>
277
	 * The offset to unset.
278
	 * </p>
279
	 * @return void
280
	 * @since 5.0.0
281
	 */
282
	public function offsetUnset($offset)
283
	{
284
		throw new TDBMInvalidOperationException('You can unset values in a TDBM result set.');
285
	}
286
}
287