Completed
Push — master ( 07220b...a0a810 )
by Fabien
53:26
created

VidiDbBackend::addSysLanguageStatement()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 8.439
c 0
b 0
f 0
cc 6
eloc 16
nc 5
nop 3
1
<?php
2
namespace Fab\Vidi\Persistence\Storage;
3
4
/*
5
 * This file is part of the Fab/Vidi project under GPLv2 or later.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.md file that was distributed with this source code.
9
 */
10
11
use TYPO3\CMS\Backend\Utility\BackendUtility;
12
use TYPO3\CMS\Core\Utility\GeneralUtility;
13
use TYPO3\CMS\Core\Versioning\VersionState;
14
use TYPO3\CMS\Extbase\Persistence\Generic\Exception;
15
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap;
16
use TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface;
17
use TYPO3\CMS\Extbase\Persistence\Generic\Qom\DynamicOperandInterface;
18
use TYPO3\CMS\Extbase\Persistence\Generic\Qom\JoinInterface;
19
use TYPO3\CMS\Extbase\Persistence\Generic\Qom\LowerCaseInterface;
20
use TYPO3\CMS\Extbase\Persistence\Generic\Qom\PropertyValueInterface;
21
use TYPO3\CMS\Extbase\Persistence\Generic\Qom\SelectorInterface;
22
use TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface;
23
use TYPO3\CMS\Extbase\Persistence\Generic\Qom\UpperCaseInterface;
24
use TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface;
25
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
26
use TYPO3\CMS\Frontend\Page\PageRepository;
27
use Fab\Vidi\Tca\Tca;
28
29
/**
30
 * A Storage backend
31
 */
32
class VidiDbBackend
33
{
34
35
    const OPERATOR_EQUAL_TO_NULL = 'operatorEqualToNull';
36
    const OPERATOR_NOT_EQUAL_TO_NULL = 'operatorNotEqualToNull';
37
38
    /**
39
     * The TYPO3 database object
40
     *
41
     * @var \TYPO3\CMS\Core\Database\DatabaseConnection
42
     */
43
    protected $databaseHandle;
44
45
    /**
46
     * The TYPO3 page repository. Used for language and workspace overlay
47
     *
48
     * @var PageRepository
49
     */
50
    protected $pageRepository;
51
52
    /**
53
     * A first-level TypoScript configuration cache
54
     *
55
     * @var array
56
     */
57
    protected $pageTSConfigCache = [];
58
59
    /**
60
     * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
61
     * @inject
62
     */
63
    protected $configurationManager;
64
65
    /**
66
     * @var \TYPO3\CMS\Extbase\Service\CacheService
67
     * @inject
68
     */
69
    protected $cacheService;
70
71
    /**
72
     * @var \TYPO3\CMS\Core\Cache\CacheManager
73
     * @inject
74
     */
75
    protected $cacheManager;
76
77
    /**
78
     * @var \TYPO3\CMS\Extbase\Service\EnvironmentService
79
     * @inject
80
     */
81
    protected $environmentService;
82
83
    /**
84
     * @var \Fab\Vidi\Persistence\Query
85
     */
86
    protected $query;
87
88
    /**
89
     * Store some info related to table name and its aliases.
90
     *
91
     * @var array
92
     */
93
    protected $tableNameAliases = array(
94
        'aliases' => [],
95
        'aliasIncrement' => [],
96
    );
97
98
    /**
99
     * Use to store the current foreign table name alias.
100
     *
101
     * @var string
102
     */
103
    protected $currentChildTableNameAlias = '';
104
105
    /**
106
     * The default object type being returned.
107
     *
108
     * @var string
109
     */
110
    protected $objectType = 'Fab\Vidi\Domain\Model\Content';
111
112
    /**
113
     * Constructor. takes the database handle from $GLOBALS['TYPO3_DB']
114
     */
115
    public function __construct(QueryInterface $query)
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
116
    {
117
        $this->query = $query;
0 ignored issues
show
Documentation Bug introduced by
$query is of type object<TYPO3\CMS\Extbase...istence\QueryInterface>, but the property $query was declared to be of type object<Fab\Vidi\Persistence\Query>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

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 given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
118
        $this->databaseHandle = $GLOBALS['TYPO3_DB'];
119
    }
120
121
    /**
122
     * @param array $identifier
123
     * @return string
124
     */
125
    protected function parseIdentifier(array $identifier)
126
    {
127
        $fieldNames = array_keys($identifier);
128
        $suffixedFieldNames = [];
129
        foreach ($fieldNames as $fieldName) {
130
            $suffixedFieldNames[] = $fieldName . '=?';
131
        }
132
        return implode(' AND ', $suffixedFieldNames);
133
    }
134
135
    /**
136
     * Returns the result of the query
137
     */
138
    public function fetchResult()
139
    {
140
141
        $parameters = [];
142
        $statementParts = $this->parseQuery($this->query, $parameters);
143
        $statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts); // Mmm... check if that is the right way of doing that.
144
145
        $sql = $this->buildQuery($statementParts);
146
        $tableName = '';
147
        if (is_array($statementParts) && !empty(reset($statementParts['tables']))) {
148
            $tableName = reset($statementParts['tables']);
149
        }
150
        $this->replacePlaceholders($sql, $parameters, $tableName);
151
        #print $sql; exit(); // @debug
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
152
153
        $result = $this->databaseHandle->sql_query($sql);
154
        $this->checkSqlErrors($sql);
155
        $rows = $this->getRowsFromResult($result);
156
        $this->databaseHandle->sql_free_result($result);
157
158
        return $rows;
159
    }
160
161
    /**
162
     * Returns the number of tuples matching the query.
163
     *
164
     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\BadConstraintException
165
     * @return int The number of matching tuples
166
     */
167
    public function countResult()
168
    {
169
170
        $parameters = [];
171
        $statementParts = $this->parseQuery($this->query, $parameters);
172
        $statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts); // Mmm... check if that is the right way of doing that.
173
        // Reset $statementParts for valid table return
174
        reset($statementParts);
175
176
        // if limit is set, we need to count the rows "manually" as COUNT(*) ignores LIMIT constraints
177
        if (!empty($statementParts['limit'])) {
178
            $statement = $this->buildQuery($statementParts);
179
            $this->replacePlaceholders($statement, $parameters, current($statementParts['tables']));
180
            #print $statement; exit(); // @debug
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
181
            $result = $this->databaseHandle->sql_query($statement);
182
            $this->checkSqlErrors($statement);
183
            $count = $this->databaseHandle->sql_num_rows($result);
184
        } else {
185
            $statementParts['fields'] = array('COUNT(*)');
186
            // having orderings without grouping is not compatible with non-MySQL DBMS
187
            $statementParts['orderings'] = [];
188
            if (isset($statementParts['keywords']['distinct'])) {
189
                unset($statementParts['keywords']['distinct']);
190
                $distinctField = $this->query->getDistinct() ? $this->query->getDistinct() : 'uid';
191
                $statementParts['fields'] = array('COUNT(DISTINCT ' . reset($statementParts['tables']) . '.' . $distinctField . ')');
192
            }
193
194
            $statement = $this->buildQuery($statementParts);
195
            $this->replacePlaceholders($statement, $parameters, current($statementParts['tables']));
196
197
            #print $statement; exit(); // @debug
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
198
            $result = $this->databaseHandle->sql_query($statement);
199
            $this->checkSqlErrors($statement);
200
            $count = 0;
201
            if ($result) {
202
                $row = $this->databaseHandle->sql_fetch_assoc($result);
203
                $count = current($row);
204
            }
205
        }
206
        $this->databaseHandle->sql_free_result($result);
207
        return (int)$count;
208
    }
209
210
    /**
211
     * Parses the query and returns the SQL statement parts.
212
     *
213
     * @param QueryInterface $query The query
214
     * @param array &$parameters
215
     * @return array The SQL statement parts
216
     */
217
    public function parseQuery(QueryInterface $query, array &$parameters)
218
    {
219
        $statementParts = [];
220
        $statementParts['keywords'] = [];
221
        $statementParts['tables'] = [];
222
        $statementParts['unions'] = [];
223
        $statementParts['fields'] = [];
224
        $statementParts['where'] = [];
225
        $statementParts['additionalWhereClause'] = [];
226
        $statementParts['orderings'] = [];
227
        $statementParts['limit'] = [];
228
        $source = $query->getSource();
229
        $this->parseSource($source, $statementParts);
230
        $this->parseConstraint($query->getConstraint(), $source, $statementParts, $parameters);
231
        $this->parseOrderings($query->getOrderings(), $source, $statementParts);
232
        $this->parseLimitAndOffset($query->getLimit(), $query->getOffset(), $statementParts);
233
        $tableNames = array_unique(array_keys($statementParts['tables'] + $statementParts['unions']));
234
        foreach ($tableNames as $tableNameOrAlias) {
235
            if (is_string($tableNameOrAlias) && strlen($tableNameOrAlias) > 0) {
236
                $this->addAdditionalWhereClause($query->getQuerySettings(), $tableNameOrAlias, $statementParts);
237
            }
238
        }
239
240
        return $statementParts;
241
    }
242
243
    /**
244
     * Fiddle with the statement structure to handle recursive MM relations.
245
     * For the recursive MM query to work, we must invert some values.
246
     * Let see if that is the best way of doing that...
247
     *
248
     * @param array $statementParts
249
     * @return array
250
     */
251
    public function processStatementStructureForRecursiveMMRelation(array $statementParts)
252
    {
253
254
        if ($this->hasRecursiveMMRelation()) {
255
            $tableName = $this->query->getType();
256
257
            // In order the MM query to work for a recursive MM query, we must invert some values.
258
            // tx_domain_model_foo0 (the alias) <--> tx_domain_model_foo (the origin table name)
259
            $values = [];
260 View Code Duplication
            foreach ($statementParts['fields'] as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
261
                $values[$key] = str_replace($tableName, $tableName . '0', $value);
262
            }
263
            $statementParts['fields'] = $values;
264
265
            // Same comment as above.
266
            $values = [];
267 View Code Duplication
            foreach ($statementParts['where'] as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
268
                $values[$key] = str_replace($tableName . '0', $tableName, $value);
269
            }
270
            $statementParts['where'] = $values;
271
272
            // We must be more restrictive by transforming the "left" union by "inner"
273
            $values = [];
274
            foreach ($statementParts['unions'] as $key => $value) {
275
                $values[$key] = str_replace('LEFT JOIN', 'INNER JOIN', $value);
276
            }
277
            $statementParts['unions'] = $values;
278
        }
279
280
        return $statementParts;
281
    }
282
283
    /**
284
     * Tell whether there is a recursive MM relation.
285
     *
286
     * @return bool
287
     */
288
    public function hasRecursiveMMRelation()
289
    {
290
        return isset($this->tableNameAliases['aliasIncrement'][$this->query->getType()]);
291
292
    }
293
294
    /**
295
     * Returns the statement, ready to be executed.
296
     *
297
     * @param array $statementParts The SQL statement parts
298
     * @return string The SQL statement
299
     */
300
    public function buildQuery(array $statementParts)
301
    {
302
303
        // Add more statement to the UNION part.
304
        if (!empty($statementParts['unions'])) {
305
            foreach ($statementParts['unions'] as $tableName => $unionPart) {
306
                if (!empty($statementParts['additionalWhereClause'][$tableName])) {
307
                    $statementParts['unions'][$tableName] .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$tableName]);
308
                }
309
            }
310
        }
311
312
        $statement = 'SELECT ' . implode(' ', $statementParts['keywords']) . ' ' . implode(',', $statementParts['fields']) . ' FROM ' . implode(' ', $statementParts['tables']) . ' ' . implode(' ', $statementParts['unions']);
313
        if (!empty($statementParts['where'])) {
314
            $statement .= ' WHERE ' . implode('', $statementParts['where']);
315 View Code Duplication
            if (!empty($statementParts['additionalWhereClause'][$this->query->getType()])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
316
                $statement .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
317
            }
318 View Code Duplication
        } elseif (!empty($statementParts['additionalWhereClause'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
319
            $statement .= ' WHERE ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
320
        }
321
        if (!empty($statementParts['orderings'])) {
322
            $statement .= ' ORDER BY ' . implode(', ', $statementParts['orderings']);
323
        }
324
        if (!empty($statementParts['limit'])) {
325
            $statement .= ' LIMIT ' . $statementParts['limit'];
326
        }
327
328
        return $statement;
329
    }
330
331
    /**
332
     * Transforms a Query Source into SQL and parameter arrays
333
     *
334
     * @param SourceInterface $source The source
335
     * @param array &$sql
336
     * @return void
337
     */
338
    protected function parseSource(SourceInterface $source, array &$sql)
339
    {
340
        if ($source instanceof SelectorInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...c\Qom\SelectorInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
341
            $tableName = $source->getNodeTypeName();
342
            $sql['fields'][$tableName] = $tableName . '.*';
343
            $sql['tables'][$tableName] = $tableName;
344
            if ($this->query->getDistinct()) {
345
                $sql['fields'][$tableName] = $tableName . '.' . $this->query->getDistinct();
346
                $sql['keywords']['distinct'] = 'DISTINCT';
347
            }
348
        } elseif ($source instanceof JoinInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...neric\Qom\JoinInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
349
            $this->parseJoin($source, $sql);
350
        }
351
    }
352
353
    /**
354
     * Transforms a Join into SQL and parameter arrays
355
     *
356
     * @param JoinInterface $join The join
357
     * @param array &$sql The query parts
358
     * @return void
359
     */
360
    protected function parseJoin(JoinInterface $join, array &$sql)
361
    {
362
        $leftSource = $join->getLeft();
363
        $leftTableName = $leftSource->getSelectorName();
364
        // $sql['fields'][$leftTableName] = $leftTableName . '.*';
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...
365
        $rightSource = $join->getRight();
366
        if ($rightSource instanceof JoinInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...neric\Qom\JoinInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
367
            $rightTableName = $rightSource->getLeft()->getSelectorName();
368
        } else {
369
            $rightTableName = $rightSource->getSelectorName();
370
            $sql['fields'][$leftTableName] = $rightTableName . '.*';
371
        }
372
        $sql['tables'][$leftTableName] = $leftTableName;
373
        $sql['unions'][$rightTableName] = 'LEFT JOIN ' . $rightTableName;
374
        $joinCondition = $join->getJoinCondition();
375
        if ($joinCondition instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\EquiJoinCondition) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...c\Qom\EquiJoinCondition does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
376
            $column1Name = $joinCondition->getProperty1Name();
377
            $column2Name = $joinCondition->getProperty2Name();
378
            $sql['unions'][$rightTableName] .= ' ON ' . $joinCondition->getSelector1Name() . '.' . $column1Name . ' = ' . $joinCondition->getSelector2Name() . '.' . $column2Name;
379
        }
380
        if ($rightSource instanceof JoinInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...neric\Qom\JoinInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
381
            $this->parseJoin($rightSource, $sql);
382
        }
383
    }
384
385
    /**
386
     * Transforms a constraint into SQL and parameter arrays
387
     *
388
     * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint The constraint
389
     * @param SourceInterface $source The source
390
     * @param array &$sql The query parts
391
     * @param array &$parameters The parameters that will replace the markers
392
     * @return void
393
     */
394
    protected function parseConstraint(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint = null, SourceInterface $source, array &$sql, array &$parameters)
395
    {
396
        if ($constraint instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\AndInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...eneric\Qom\AndInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
397
            $sql['where'][] = '(';
398
            $this->parseConstraint($constraint->getConstraint1(), $source, $sql, $parameters);
399
            $sql['where'][] = ' AND ';
400
            $this->parseConstraint($constraint->getConstraint2(), $source, $sql, $parameters);
401
            $sql['where'][] = ')';
402
        } elseif ($constraint instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\OrInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...Generic\Qom\OrInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
403
            $sql['where'][] = '(';
404
            $this->parseConstraint($constraint->getConstraint1(), $source, $sql, $parameters);
405
            $sql['where'][] = ' OR ';
406
            $this->parseConstraint($constraint->getConstraint2(), $source, $sql, $parameters);
407
            $sql['where'][] = ')';
408
        } elseif ($constraint instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\NotInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...eneric\Qom\NotInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
409
            $sql['where'][] = 'NOT (';
410
            $this->parseConstraint($constraint->getConstraint(), $source, $sql, $parameters);
411
            $sql['where'][] = ')';
412
        } elseif ($constraint instanceof ComparisonInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...Qom\ComparisonInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
413
            $this->parseComparison($constraint, $source, $sql, $parameters);
414
        }
415
    }
416
417
    /**
418
     * Parse a Comparison into SQL and parameter arrays.
419
     *
420
     * @param ComparisonInterface $comparison The comparison to parse
421
     * @param SourceInterface $source The source
422
     * @param array &$sql SQL query parts to add to
423
     * @param array &$parameters Parameters to bind to the SQL
424
     * @throws Exception\RepositoryException
425
     * @return void
426
     */
427
    protected function parseComparison(ComparisonInterface $comparison, SourceInterface $source, array &$sql, array &$parameters)
428
    {
429
        $operand1 = $comparison->getOperand1();
430
        $operator = $comparison->getOperator();
431
        $operand2 = $comparison->getOperand2();
432
        if ($operator === QueryInterface::OPERATOR_IN) {
433
            $items = [];
434
            $hasValue = false;
435
            foreach ($operand2 as $value) {
436
                $value = $this->getPlainValue($value);
437
                if ($value !== null) {
438
                    $items[] = $value;
439
                    $hasValue = true;
440
                }
441
            }
442
            if ($hasValue === false) {
443
                $sql['where'][] = '1<>1';
444
            } else {
445
                $this->parseDynamicOperand($operand1, $operator, $source, $sql, $parameters, null);
446
                $parameters[] = $items;
447
            }
448
        } elseif ($operator === QueryInterface::OPERATOR_CONTAINS) {
449
            if ($operand2 === null) {
450
                $sql['where'][] = '1<>1';
451
            } else {
452
                throw new \Exception('Not implemented! Contact extension author.', 1412931227);
453
                # @todo re-implement me if necessary.
454
                #$tableName = $this->query->getType();
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...
455
                #$propertyName = $operand1->getPropertyName();
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% 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...
456
                #while (strpos($propertyName, '.') !== false) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% 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...
457
                #	$this->addUnionStatement($tableName, $propertyName, $sql);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% 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...
458
                #}
459
                #$columnName = $propertyName;
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
460
                #$columnMap = $propertyName;
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
461
                #$typeOfRelation = $columnMap instanceof ColumnMap ? $columnMap->getTypeOfRelation() : null;
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
462
                #if ($typeOfRelation === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% 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...
463
                #	$relationTableName = $columnMap->getRelationTableName();
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% 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...
464
                #	$sql['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $columnMap->getChildKeyFieldName() . '=?)';
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% 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...
465
                #	$parameters[] = intval($this->getPlainValue($operand2));
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% 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...
466
                #} elseif ($typeOfRelation === ColumnMap::RELATION_HAS_MANY) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% 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...
467
                #	$parentKeyFieldName = $columnMap->getParentKeyFieldName();
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% 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...
468
                #	if (isset($parentKeyFieldName)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% 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...
469
                #		$childTableName = $columnMap->getChildTableName();
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% 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...
470
                #		$sql['where'][] = $tableName . '.uid=(SELECT ' . $childTableName . '.' . $parentKeyFieldName . ' FROM ' . $childTableName . ' WHERE ' . $childTableName . '.uid=?)';
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
471
                #		$parameters[] = intval($this->getPlainValue($operand2));
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% 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...
472
                #	} else {
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
473
                #		$sql['where'][] = 'FIND_IN_SET(?,' . $tableName . '.' . $columnName . ')';
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
474
                #		$parameters[] = intval($this->getPlainValue($operand2));
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% 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...
475
                #	}
476
                #} else {
477
                #	throw new Exception\RepositoryException('Unsupported or non-existing property name "' . $propertyName . '" used in relation matching.', 1327065745);
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% 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...
478
                #}
479
            }
480
        } else {
481
            if ($operand2 === null) {
482
                if ($operator === QueryInterface::OPERATOR_EQUAL_TO) {
483
                    $operator = self::OPERATOR_EQUAL_TO_NULL;
484
                } elseif ($operator === QueryInterface::OPERATOR_NOT_EQUAL_TO) {
485
                    $operator = self::OPERATOR_NOT_EQUAL_TO_NULL;
486
                }
487
            }
488
            $this->parseDynamicOperand($operand1, $operator, $source, $sql, $parameters);
489
            $parameters[] = $this->getPlainValue($operand2);
490
        }
491
    }
492
493
    /**
494
     * Returns a plain value, i.e. objects are flattened out if possible.
495
     *
496
     * @param mixed $input
497
     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException
498
     * @return mixed
499
     */
500
    protected function getPlainValue($input)
501
    {
502
        if (is_array($input)) {
503
            throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException('An array could not be converted to a plain value.', 1274799932);
504
        }
505
        if ($input instanceof \DateTime) {
506
            return $input->format('U');
507
        } elseif (is_object($input)) {
508
            if ($input instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...eneric\LazyLoadingProxy does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
509
                $realInput = $input->_loadRealInstance();
510
            } else {
511
                $realInput = $input;
512
            }
513
            if ($realInput instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Domain...t\DomainObjectInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
514
                return $realInput->getUid();
515
            } else {
516
                throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException('An object of class "' . get_class($realInput) . '" could not be converted to a plain value.', 1274799934);
517
            }
518
        } elseif (is_bool($input)) {
519
            return $input === true ? 1 : 0;
520
        } else {
521
            return $input;
522
        }
523
    }
524
525
    /**
526
     * Parse a DynamicOperand into SQL and parameter arrays.
527
     *
528
     * @param DynamicOperandInterface $operand
529
     * @param string $operator One of the JCR_OPERATOR_* constants
530
     * @param SourceInterface $source The source
531
     * @param array &$sql The query parts
532
     * @param array &$parameters The parameters that will replace the markers
533
     * @param string $valueFunction an optional SQL function to apply to the operand value
534
     * @return void
535
     */
536
    protected function parseDynamicOperand(DynamicOperandInterface $operand, $operator, SourceInterface $source, array &$sql, array &$parameters, $valueFunction = null)
537
    {
538
        if ($operand instanceof LowerCaseInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...\Qom\LowerCaseInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
539
            $this->parseDynamicOperand($operand->getOperand(), $operator, $source, $sql, $parameters, 'LOWER');
540
        } elseif ($operand instanceof UpperCaseInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...\Qom\UpperCaseInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
541
            $this->parseDynamicOperand($operand->getOperand(), $operator, $source, $sql, $parameters, 'UPPER');
542
        } elseif ($operand instanceof PropertyValueInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...\PropertyValueInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
543
            $propertyName = $operand->getPropertyName();
544
545
            // Reset value.
546
            $this->currentChildTableNameAlias = '';
547
548
            if ($source instanceof SelectorInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...c\Qom\SelectorInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
549
                $tableName = $this->query->getType();
550
                while (strpos($propertyName, '.') !== false) {
551
                    $this->addUnionStatement($tableName, $propertyName, $sql);
552
                }
553
            } elseif ($source instanceof JoinInterface) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Extbase\Persis...neric\Qom\JoinInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
554
                $tableName = $source->getJoinCondition()->getSelector1Name();
555
            }
556
557
            $columnName = $propertyName;
558
            $operator = $this->resolveOperator($operator);
559
            $constraintSQL = '';
560
            if ($valueFunction === null) {
561
                $constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $operator . ' ?';
562
            } else {
563
                $constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName . ') ' . $operator . ' ?';
564
            }
565
566
            if (isset($tableName) && !empty($this->currentChildTableNameAlias)) {
567
                $constraintSQL = $this->replaceTableNameByAlias($tableName, $this->currentChildTableNameAlias, $constraintSQL);
568
            }
569
            $sql['where'][] = $constraintSQL;
570
        }
571
    }
572
573
    /**
574
     * @param string &$tableName
575
     * @param array &$propertyPath
576
     * @param array &$sql
577
     * @throws Exception
578
     * @throws Exception\InvalidRelationConfigurationException
579
     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\MissingColumnMapException
580
     */
581
    protected function addUnionStatement(&$tableName, &$propertyPath, array &$sql)
582
    {
583
584
        $table = Tca::table($tableName);
585
586
        $explodedPropertyPath = explode('.', $propertyPath, 2);
587
        $fieldName = $explodedPropertyPath[0];
588
589
        // Field of type "group" are special because property path must contain the table name
590
        // to determine the relation type. Example for sys_category, property path will look like "items.sys_file"
591
        if ($table->field($fieldName)->isGroup()) {
592
            $parts = explode('.', $propertyPath, 3);
593
            $explodedPropertyPath[0] = $parts[0] . '.' . $parts[1];
594
            $explodedPropertyPath[1] = $parts[2];
595
            $fieldName = $explodedPropertyPath[0];
596
        }
597
598
        $parentKeyFieldName = $table->field($fieldName)->getForeignField();
599
        $childTableName = $table->field($fieldName)->getForeignTable();
600
601
        if ($childTableName === null) {
602
            throw new Exception\InvalidRelationConfigurationException('The relation information for property "' . $fieldName . '" of class "' . $tableName . '" is missing.', 1353170925);
603
        }
604
605
        if ($table->field($fieldName)->hasOne()) { // includes relation "one-to-one" and "many-to-one"
606
            // sometimes the opposite relation is not defined. We don't want to force this config for backward compatibility reasons.
607
            // $parentKeyFieldName === null does the trick somehow. Before condition was if (isset($parentKeyFieldName))
608
            if ($table->field($fieldName)->hasRelationManyToOne() || $parentKeyFieldName === null) {
609
                $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $fieldName . '=' . $childTableName . '.uid';
610
            } else {
611
                $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
612
            }
613
        } elseif ($table->field($fieldName)->hasRelationManyToMany()) {
614
            $relationTableName = $table->field($fieldName)->getManyToManyTable();
615
616
            $parentKeyFieldName = $table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
617
            $childKeyFieldName = !$table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
618
619
            // MM table e.g sys_category_record_mm
620
            $relationTableNameAlias = $this->generateAlias($relationTableName);
621
            $join = sprintf(
622
                'LEFT JOIN %s AS %s ON %s.uid=%s.%s', $relationTableName,
623
                $relationTableNameAlias,
624
                $tableName,
625
                $relationTableNameAlias,
626
                $parentKeyFieldName
627
            );
628
            $sql['unions'][$relationTableNameAlias] = $join;
629
630
            // Foreign table e.g sys_category
631
            $childTableNameAlias = $this->generateAlias($childTableName);
632
            $this->currentChildTableNameAlias = $childTableNameAlias;
633
            $join = sprintf(
634
                'LEFT JOIN %s AS %s ON %s.%s=%s.uid',
635
                $childTableName,
636
                $childTableNameAlias,
637
                $relationTableNameAlias,
638
                $childKeyFieldName,
639
                $childTableNameAlias
640
            );
641
            $sql['unions'][$childTableNameAlias] = $join;
642
643
            // Find a possible table name for a MM condition.
644
            $tableNameCondition = $table->field($fieldName)->getAdditionalTableNameCondition();
645
            if ($tableNameCondition) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $tableNameCondition of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
646
647
                // If we can find a source file name,  we can then retrieve more MM conditions from the TCA such as a field name.
648
                $sourceFileName = $this->query->getSourceFieldName();
649
                if (empty($sourceFileName)) {
650
                    $additionalMMConditions = array(
651
                        'tablenames' => $tableNameCondition,
652
                    );
653
                } else {
654
                    $additionalMMConditions = Tca::table($tableNameCondition)->field($sourceFileName)->getAdditionalMMCondition();
655
                }
656
657
                foreach ($additionalMMConditions as $additionalFieldName => $additionalMMCondition) {
658
                    $additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
659
                    $sql['unions'][$relationTableNameAlias] .= $additionalJoin;
660
661
                    $additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
662
                    $sql['unions'][$childTableNameAlias] .= $additionalJoin;
663
                }
664
665
            }
666
667
668
        } elseif ($table->field($fieldName)->hasMany()) { // includes relations "many-to-one" and "csv" relations
669
            $childTableNameAlias = $this->generateAlias($childTableName);
670
            $this->currentChildTableNameAlias = $childTableNameAlias;
671
672
            if (isset($parentKeyFieldName)) {
673
                $join = sprintf(
674
                    'LEFT JOIN %s AS %s ON %s.uid=%s.%s',
675
                    $childTableName,
676
                    $childTableNameAlias,
677
                    $tableName,
678
                    $childTableNameAlias,
679
                    $parentKeyFieldName
680
                );
681
                $sql['unions'][$childTableNameAlias] = $join;
682
            } else {
683
                $join = sprintf(
684
                    'LEFT JOIN %s AS %s ON (FIND_IN_SET(%s.uid, %s.%s))',
685
                    $childTableName,
686
                    $childTableNameAlias,
687
                    $childTableNameAlias,
688
                    $tableName,
689
                    $fieldName
690
                );
691
                $sql['unions'][$childTableNameAlias] = $join;
692
            }
693
        } else {
694
            throw new Exception('Could not determine type of relation.', 1252502725);
695
        }
696
697
        // TODO check if there is another solution for this
698
        $sql['keywords']['distinct'] = 'DISTINCT';
699
        $propertyPath = $explodedPropertyPath[1];
700
        $tableName = $childTableName;
701
    }
702
703
    /**
704
     * Returns the SQL operator for the given JCR operator type.
705
     *
706
     * @param string $operator One of the JCR_OPERATOR_* constants
707
     * @throws Exception
708
     * @return string an SQL operator
709
     */
710
    protected function resolveOperator($operator)
711
    {
712
        switch ($operator) {
713
            case self::OPERATOR_EQUAL_TO_NULL:
714
                $operator = 'IS';
715
                break;
716
            case self::OPERATOR_NOT_EQUAL_TO_NULL:
717
                $operator = 'IS NOT';
718
                break;
719
            case QueryInterface::OPERATOR_IN:
720
                $operator = 'IN';
721
                break;
722
            case QueryInterface::OPERATOR_EQUAL_TO:
723
                $operator = '=';
724
                break;
725
            case QueryInterface::OPERATOR_NOT_EQUAL_TO:
726
                $operator = '!=';
727
                break;
728
            case QueryInterface::OPERATOR_LESS_THAN:
729
                $operator = '<';
730
                break;
731
            case QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO:
732
                $operator = '<=';
733
                break;
734
            case QueryInterface::OPERATOR_GREATER_THAN:
735
                $operator = '>';
736
                break;
737
            case QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO:
738
                $operator = '>=';
739
                break;
740
            case QueryInterface::OPERATOR_LIKE:
741
                $operator = 'LIKE';
742
                break;
743
            default:
744
                throw new Exception('Unsupported operator encountered.', 1242816073);
745
        }
746
        return $operator;
747
    }
748
749
    /**
750
     * Replace query placeholders in a query part by the given
751
     * parameters.
752
     *
753
     * @param string &$sqlString The query part with placeholders
754
     * @param array $parameters The parameters
755
     * @param string $tableName
756
     *
757
     * @throws Exception
758
     */
759
    protected function replacePlaceholders(&$sqlString, array $parameters, $tableName = 'foo')
760
    {
761
        // TODO profile this method again
762
        if (substr_count($sqlString, '?') !== count($parameters)) {
763
            throw new Exception('The number of question marks to replace must be equal to the number of parameters.', 1242816074);
764
        }
765
        $offset = 0;
766
        foreach ($parameters as $parameter) {
767
            $markPosition = strpos($sqlString, '?', $offset);
768
            if ($markPosition !== false) {
769
                if ($parameter === null) {
770
                    $parameter = 'null';
771
                } elseif (is_array($parameter) || $parameter instanceof \ArrayAccess || $parameter instanceof \Traversable) {
772
                    $items = [];
773
                    foreach ($parameter as $item) {
774
                        $items[] = $this->databaseHandle->fullQuoteStr($item, $tableName);
775
                    }
776
                    $parameter = '(' . implode(',', $items) . ')';
777
                } else {
778
                    $parameter = $this->databaseHandle->fullQuoteStr($parameter, $tableName);
779
                }
780
                $sqlString = substr($sqlString, 0, $markPosition) . $parameter . substr($sqlString, ($markPosition + 1));
781
            }
782
            $offset = $markPosition + strlen($parameter);
783
        }
784
    }
785
786
    /**
787
     * Adds additional WHERE statements according to the query settings.
788
     *
789
     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
790
     * @param string $tableNameOrAlias The table name to add the additional where clause for
791
     * @param array &$statementParts
792
     * @return void
793
     */
794
    protected function addAdditionalWhereClause(QuerySettingsInterface $querySettings, $tableNameOrAlias, &$statementParts)
795
    {
796
        $this->addVisibilityConstraintStatement($querySettings, $tableNameOrAlias, $statementParts);
797
        if ($querySettings->getRespectSysLanguage()) {
798
            $this->addSysLanguageStatement($tableNameOrAlias, $statementParts, $querySettings);
799
        }
800
    }
801
802
    /**
803
     * Adds enableFields and deletedClause to the query if necessary
804
     *
805
     * @param QuerySettingsInterface $querySettings
806
     * @param string $tableNameOrAlias The database table name
807
     * @param array &$statementParts The query parts
808
     * @return void
809
     */
810
    protected function addVisibilityConstraintStatement(QuerySettingsInterface $querySettings, $tableNameOrAlias, array &$statementParts)
0 ignored issues
show
Coding Style introduced by
addVisibilityConstraintStatement uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
811
    {
812
        $statement = '';
813
        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
814
        if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
815
            $ignoreEnableFields = $querySettings->getIgnoreEnableFields();
816
            $enableFieldsToBeIgnored = $querySettings->getEnableFieldsToBeIgnored();
817
            $includeDeleted = $querySettings->getIncludeDeleted();
818
            if ($this->environmentService->isEnvironmentInFrontendMode()) {
819
                $statement .= $this->getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted);
820
            } else {
821
                // TYPO3_MODE === 'BE'
822
                $statement .= $this->getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted);
823
            }
824
825
            // Remove the prefixing "AND" if any.
826
            if (!empty($statement)) {
827
                $statement = strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement;
828
                $statementParts['additionalWhereClause'][$tableNameOrAlias][] = $statement;
829
            }
830
        }
831
    }
832
833
    /**
834
     * Returns constraint statement for frontend context
835
     *
836
     * @param string $tableNameOrAlias
837
     * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
838
     * @param array $enableFieldsToBeIgnored If $ignoreEnableFields is true, this array specifies enable fields to be ignored. If it is null or an empty array (default) all enable fields are ignored.
839
     * @param boolean $includeDeleted A flag indicating whether deleted records should be included
840
     * @return string
841
     * @throws Exception\InconsistentQuerySettingsException
842
     */
843
    protected function getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored = [], $includeDeleted)
844
    {
845
        $statement = '';
846
        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
847
        if ($ignoreEnableFields && !$includeDeleted) {
848
            if (count($enableFieldsToBeIgnored)) {
849
                // array_combine() is necessary because of the way \TYPO3\CMS\Frontend\Page\PageRepository::enableFields() is implemented
850
                $statement .= $this->getPageRepository()->enableFields($tableName, -1, array_combine($enableFieldsToBeIgnored, $enableFieldsToBeIgnored));
851
            } else {
852
                $statement .= $this->getPageRepository()->deleteClause($tableName);
853
            }
854
        } elseif (!$ignoreEnableFields && !$includeDeleted) {
855
            $statement .= $this->getPageRepository()->enableFields($tableName);
856
        } elseif (!$ignoreEnableFields && $includeDeleted) {
857
            throw new Exception\InconsistentQuerySettingsException('Query setting "ignoreEnableFields=false" can not be used together with "includeDeleted=true" in frontend context.', 1327678173);
858
        }
859
        return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
860
    }
861
862
    /**
863
     * Returns constraint statement for backend context
864
     *
865
     * @param string $tableNameOrAlias
866
     * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
867
     * @param boolean $includeDeleted A flag indicating whether deleted records should be included
868
     * @return string
869
     */
870
    protected function getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted)
871
    {
872
        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
873
        $statement = '';
874
        if (!$ignoreEnableFields) {
875
            $statement .= BackendUtility::BEenableFields($tableName);
876
        }
877
878
        // If the table is found to have "workspace" support, add the corresponding fields in the statement.
879
        if (Tca::table($tableName)->hasWorkspaceSupport()) {
880
            if ($this->getBackendUser()->workspace === 0) {
881
                $statement .= ' AND ' . $tableName . '.t3ver_state<=' . new VersionState(VersionState::DEFAULT_STATE);
882
            } else {
883
                // Show only records of live and of the current workspace
884
                // In case we are in a Versioning preview
885
                $statement .= ' AND (' .
886
                    $tableName . '.t3ver_wsid=0 OR ' .
887
                    $tableName . '.t3ver_wsid=' . (int)$this->getBackendUser()->workspace .
888
                    ')';
889
            }
890
891
            // Check if this segment make sense here or whether it should be in the "if" part when we have workspace = 0
892
            $statement .= ' AND ' . $tableName . '.pid<>-1';
893
        }
894
895
        if (!$includeDeleted) {
896
            $statement .= BackendUtility::deleteClause($tableName);
897
        }
898
899
        return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
900
    }
901
902
    /**
903
     * Builds the language field statement
904
     *
905
     * @param string $tableNameOrAlias The database table name
906
     * @param array &$statementParts The query parts
907
     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
908
     * @throws Exception
909
     * @return void
910
     */
911
    protected function addSysLanguageStatement($tableNameOrAlias, array &$statementParts, $querySettings)
0 ignored issues
show
Coding Style introduced by
addSysLanguageStatement uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
912
    {
913
914
        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
915
        if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
916
            if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])) {
917
                // Select all entries for the current language
918
                $additionalWhereClause = $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' IN (' . intval($querySettings->getLanguageUid()) . ',-1)';
919
                // If any language is set -> get those entries which are not translated yet
920
                // They will be removed by t3lib_page::getRecordOverlay if not matching overlay mode
921
                if (isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
922
                    && $querySettings->getLanguageUid() > 0
923
                ) {
924
                    $additionalWhereClause .= ' OR (' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
925
                        ' AND ' . $tableNameOrAlias . '.uid NOT IN (SELECT ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
926
                        ' FROM ' . $tableName .
927
                        ' WHERE ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '>0' .
928
                        ' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '>0';
929
930
                    // Add delete clause to ensure all entries are loaded
931
                    if (isset($GLOBALS['TCA'][$tableName]['ctrl']['delete'])) {
932
                        $additionalWhereClause .= ' AND ' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] . '=0';
933
                    }
934
                    $additionalWhereClause .= '))';
935
                }
936
                $statementParts['additionalWhereClause'][$tableNameOrAlias][] = '(' . $additionalWhereClause . ')';
937
            }
938
        }
939
    }
940
941
    /**
942
     * Transforms orderings into SQL.
943
     *
944
     * @param array $orderings An array of orderings (Tx_Extbase_Persistence_QOM_Ordering)
945
     * @param SourceInterface $source The source
946
     * @param array &$sql The query parts
947
     * @throws Exception\UnsupportedOrderException
948
     * @return void
949
     */
950
    protected function parseOrderings(array $orderings, SourceInterface $source, array &$sql)
0 ignored issues
show
Unused Code introduced by
The parameter $source is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
951
    {
952
        foreach ($orderings as $fieldNameAndPath => $order) {
953
            switch ($order) {
954
                case QueryInterface::ORDER_ASCENDING:
955
                    $order = 'ASC';
956
                    break;
957
                case QueryInterface::ORDER_DESCENDING:
958
                    $order = 'DESC';
959
                    break;
960
                default:
961
                    throw new Exception\UnsupportedOrderException('Unsupported order encountered.', 1456845126);
962
            }
963
964
            $tableName = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->query->getType());
965
            $fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $tableName);
966
            $sql['orderings'][] = sprintf('%s.%s %s', $tableName, $fieldName, $order);
967
        }
968
    }
969
970
    /**
971
     * Transforms limit and offset into SQL
972
     *
973
     * @param int $limit
974
     * @param int $offset
975
     * @param array &$sql
976
     * @return void
977
     */
978
    protected function parseLimitAndOffset($limit, $offset, array &$sql)
979
    {
980
        if ($limit !== null && $offset !== null) {
981
            $sql['limit'] = intval($offset) . ', ' . intval($limit);
982
        } elseif ($limit !== null) {
983
            $sql['limit'] = intval($limit);
984
        }
985
    }
986
987
    /**
988
     * Transforms a Resource from a database query to an array of rows.
989
     *
990
     * @param resource $result The result
991
     * @return array The result as an array of rows (tuples)
992
     */
993
    protected function getRowsFromResult($result)
994
    {
995
        $rows = [];
996
        while ($row = $this->databaseHandle->sql_fetch_assoc($result)) {
997
            if (is_array($row)) {
998
999
                // Get language uid from querySettings.
1000
                // Ensure the backend handling is not broken (fallback to Get parameter 'L' if needed)
1001
                $overlaidRow = $this->doLanguageAndWorkspaceOverlay($this->query->getSource(), $row, $this->query->getQuerySettings());
1002
                $contentObject = GeneralUtility::makeInstance($this->objectType, $this->query->getType(), $overlaidRow);
1003
                $rows[] = $contentObject;
1004
            }
1005
        }
1006
1007
        return $rows;
1008
    }
1009
1010
    /**
1011
     * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
1012
     * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
1013
     *
1014
     * @param SourceInterface $source The source (selector od join)
1015
     * @param array $row
1016
     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
1017
     * @return array
1018
     */
1019
    protected function doLanguageAndWorkspaceOverlay(SourceInterface $source, array $row, $querySettings)
0 ignored issues
show
Coding Style introduced by
doLanguageAndWorkspaceOverlay uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1020
    {
1021
1022
        /** @var SelectorInterface $source */
1023
        $tableName = $source->getSelectorName();
1024
1025
        $pageRepository = $this->getPageRepository();
1026
        if (is_object($GLOBALS['TSFE'])) {
1027
            $languageMode = $GLOBALS['TSFE']->sys_language_mode;
1028
            if ($this->isBackendUserLogged() && $this->getBackendUser()->workspace !== 0) {
1029
                $pageRepository->versioningWorkspaceId = $this->getBackendUser()->workspace;
1030
            }
1031
        } else {
1032
            $languageMode = '';
1033
            $workspaceUid = $this->getBackendUser()->workspace;
1034
            $pageRepository->versioningWorkspaceId = $workspaceUid;
1035
            if ($this->getBackendUser()->workspace !== 0) {
1036
                $pageRepository->versioningPreview = 1;
1037
            }
1038
        }
1039
1040
        // If current row is a translation select its parent
1041
        if (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
1042
            && isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
1043
        ) {
1044
            if (isset($row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']])
1045
                && $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] > 0
1046
            ) {
1047
                $row = $this->databaseHandle->exec_SELECTgetSingleRow(
1048
                    $tableName . '.*',
1049
                    $tableName,
1050
                    $tableName . '.uid=' . (int)$row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] .
1051
                    ' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0'
1052
                );
1053
            }
1054
        }
1055
1056
        // Retrieve the original uid; Used for Workspaces!
1057
        if (TYPO3_MODE !== 'BE') {
1058
            $pageRepository->versionOL($tableName, $row, true, true);
1059
        } else {
1060
            BackendUtility::workspaceOL($tableName, $row);
1061
        }
1062
        if ($pageRepository->versioningPreview && isset($row['_ORIG_uid'])) {
1063
            $row['uid'] = $row['_ORIG_uid'];
1064
        }
1065
1066
        // Special case for table "pages"
1067
        if ($tableName == 'pages') {
1068
            $row = $pageRepository->getPageOverlay($row, $querySettings->getLanguageUid());
1069
        } elseif (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
1070
            && $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] !== ''
1071
        ) {
1072
            if (in_array($row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']], array(-1, 0))) {
1073
                $overlayMode = $languageMode === 'strict' ? 'hideNonTranslated' : '';
1074
                $row = $pageRepository->getRecordOverlay($tableName, $row, $querySettings->getLanguageUid(), $overlayMode);
1075
            }
1076
        }
1077
1078
        return $row;
1079
    }
1080
1081
    /**
1082
     * Return a resolved table name given a possible table name alias.
1083
     *
1084
     * @param string $tableNameOrAlias
1085
     * @return string
1086
     */
1087
    protected function resolveTableNameAlias($tableNameOrAlias)
1088
    {
1089
        $resolvedTableName = $tableNameOrAlias;
1090
        if (!empty($this->tableNameAliases['aliases'][$tableNameOrAlias])) {
1091
            $resolvedTableName = $this->tableNameAliases['aliases'][$tableNameOrAlias];
1092
        }
1093
        return $resolvedTableName;
1094
    }
1095
1096
    /**
1097
     * Generate a unique table name alias for the given table name.
1098
     *
1099
     * @param string $tableName
1100
     * @return string
1101
     */
1102
    protected function generateAlias($tableName)
1103
    {
1104
1105
        if (!isset($this->tableNameAliases['aliasIncrement'][$tableName])) {
1106
            $this->tableNameAliases['aliasIncrement'][$tableName] = 0;
1107
        }
1108
1109
        $numberOfAliases = $this->tableNameAliases['aliasIncrement'][$tableName];
1110
        $tableNameAlias = $tableName . $numberOfAliases;
1111
1112
        $this->tableNameAliases['aliasIncrement'][$tableName]++;
1113
        $this->tableNameAliases['aliases'][$tableNameAlias] = $tableName;
1114
1115
        return $tableNameAlias;
1116
    }
1117
1118
    /**
1119
     * Replace the table names by its table name alias within the given statement.
1120
     *
1121
     * @param string $tableName
1122
     * @param string $tableNameAlias
1123
     * @param string $statement
1124
     * @return string
1125
     */
1126
    protected function replaceTableNameByAlias($tableName, $tableNameAlias, $statement)
1127
    {
1128
        if ($statement && $tableName !== $tableNameAlias) {
1129
            $statement = str_replace($tableName, $tableNameAlias, $statement);
1130
        }
1131
        return $statement;
1132
    }
1133
1134
    /**
1135
     * Returns an instance of the current Backend User.
1136
     *
1137
     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1138
     */
1139
    protected function getBackendUser()
0 ignored issues
show
Coding Style introduced by
getBackendUser uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1140
    {
1141
        return $GLOBALS['BE_USER'];
1142
    }
1143
1144
    /**
1145
     * Tell whether a Backend User is logged in.
1146
     *
1147
     * @return bool
1148
     */
1149
    protected function isBackendUserLogged()
0 ignored issues
show
Coding Style introduced by
isBackendUserLogged uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1150
    {
1151
        return is_object($GLOBALS['BE_USER']);
1152
    }
1153
1154
    /**
1155
     * @return PageRepository
1156
     */
1157
    protected function getPageRepository()
0 ignored issues
show
Coding Style introduced by
getPageRepository uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1158
    {
1159
        if (!$this->pageRepository instanceof PageRepository) {
0 ignored issues
show
Bug introduced by
The class TYPO3\CMS\Frontend\Page\PageRepository does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1160
            if ($this->environmentService->isEnvironmentInFrontendMode() && is_object($GLOBALS['TSFE'])) {
1161
                $this->pageRepository = $GLOBALS['TSFE']->sys_page;
1162
            } else {
1163
                $this->pageRepository = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
1164
            }
1165
        }
1166
1167
        return $this->pageRepository;
1168
    }
1169
1170
    /**
1171
     * @return \Fab\Vidi\Resolver\FieldPathResolver
1172
     */
1173
    protected function getFieldPathResolver()
1174
    {
1175
        return GeneralUtility::makeInstance('Fab\Vidi\Resolver\FieldPathResolver');
1176
    }
1177
1178
    /**
1179
     * Checks if there are SQL errors in the last query, and if yes, throw an exception.
1180
     *
1181
     * @return void
1182
     * @param string $sql The SQL statement
1183
     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\SqlErrorException
1184
     */
1185
    protected function checkSqlErrors($sql = '')
1186
    {
1187
        $error = $this->databaseHandle->sql_error();
1188
        if ($error !== '') {
1189
            $error .= $sql ? ': ' . $sql : '';
1190
            throw new \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\SqlErrorException($error, 1247602160);
1191
        }
1192
    }
1193
}
1194