Completed
Push — EZP-26146-location-swap-urlali... ( 03f4c9...c13c1a )
by
unknown
29:41
created

DoctrineDatabase::cleanupAfterSwap()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 51
Code Lines 33

Duplication

Lines 6
Ratio 11.76 %

Importance

Changes 0
Metric Value
cc 3
eloc 33
nc 3
nop 4
dl 6
loc 51
rs 9.4109
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * File containing the DoctrineDatabase UrlAlias Gateway class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 *
9
 * @version //autogentag//
10
 */
11
namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway;
12
13
use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway;
14
use eZ\Publish\Core\Persistence\Database\DatabaseHandler;
15
use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator;
16
use eZ\Publish\Core\Persistence\Database\Query;
17
use PDO;
18
19
/**
20
 * UrlAlias Gateway.
21
 */
22
class DoctrineDatabase extends Gateway
23
{
24
    /**
25
     * 2^30, since PHP_INT_MAX can cause overflows in DB systems, if PHP is run
26
     * on 64 bit systems.
27
     */
28
    const MAX_LIMIT = 1073741824;
29
30
    /**
31
     * Columns of database tables.
32
     *
33
     * @var array
34
     *
35
     * @todo remove after testing
36
     */
37
    protected $columns = array(
38
        'ezurlalias_ml' => array(
39
            'action',
40
            'action_type',
41
            'alias_redirects',
42
            'id',
43
            'is_alias',
44
            'is_original',
45
            'lang_mask',
46
            'link',
47
            'parent',
48
            'text',
49
            'text_md5',
50
        ),
51
    );
52
53
    /**
54
     * Doctrine database handler.
55
     *
56
     * @var \eZ\Publish\Core\Persistence\Database\DatabaseHandler
57
     */
58
    protected $dbHandler;
59
60
    /**
61
     * Language mask generator.
62
     *
63
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator
64
     */
65
    protected $languageMaskGenerator;
66
67
    /**
68
     * Creates a new DoctrineDatabase UrlAlias Gateway.
69
     *
70
     * @param \eZ\Publish\Core\Persistence\Database\DatabaseHandler $dbHandler
71
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator $languageMaskGenerator
72
     */
73
    public function __construct(
74
        DatabaseHandler $dbHandler,
75
        LanguageMaskGenerator $languageMaskGenerator
76
    ) {
77
        $this->dbHandler = $dbHandler;
78
        $this->languageMaskGenerator = $languageMaskGenerator;
79
    }
80
81
    /**
82
     * Loads list of aliases by given $locationId.
83
     *
84
     * @param mixed $locationId
85
     * @param bool $custom
86
     * @param mixed $languageId
87
     *
88
     * @return array
89
     */
90
    public function loadLocationEntries($locationId, $custom = false, $languageId = false)
91
    {
92
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
93
        $query = $this->dbHandler->createSelectQuery();
94
        $query->select(
95
            $this->dbHandler->quoteColumn('id'),
96
            $this->dbHandler->quoteColumn('link'),
97
            $this->dbHandler->quoteColumn('is_alias'),
98
            $this->dbHandler->quoteColumn('alias_redirects'),
99
            $this->dbHandler->quoteColumn('lang_mask'),
100
            $this->dbHandler->quoteColumn('is_original'),
101
            $this->dbHandler->quoteColumn('parent'),
102
            $this->dbHandler->quoteColumn('text'),
103
            $this->dbHandler->quoteColumn('text_md5'),
104
            $this->dbHandler->quoteColumn('action')
105
        )->from(
106
            $this->dbHandler->quoteTable('ezurlalias_ml')
107
        )->where(
108
            $query->expr->lAnd(
109
                $query->expr->eq(
110
                    $this->dbHandler->quoteColumn('action'),
111
                    $query->bindValue("eznode:{$locationId}", null, \PDO::PARAM_STR)
112
                ),
113
                $query->expr->eq(
114
                    $this->dbHandler->quoteColumn('is_original'),
115
                    $query->bindValue(1, null, \PDO::PARAM_INT)
116
                ),
117
                $query->expr->eq(
118
                    $this->dbHandler->quoteColumn('is_alias'),
119
                    $query->bindValue(
120
                        $custom ? 1 : 0,
121
                        null,
122
                        \PDO::PARAM_INT
123
                    )
124
                )
125
            )
126
        );
127
128 View Code Duplication
        if ($languageId !== false) {
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...
129
            $query->where(
130
                $query->expr->gt(
131
                    $query->expr->bitAnd(
132
                        $this->dbHandler->quoteColumn('lang_mask'),
133
                        $query->bindValue($languageId, null, \PDO::PARAM_INT)
134
                    ),
135
                    0
136
                )
137
            );
138
        }
139
140
        $statement = $query->prepare();
141
        $statement->execute();
142
143
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
144
    }
145
146
    /**
147
     * Loads paged list of global aliases.
148
     *
149
     * @param string|null $languageCode
150
     * @param int $offset
151
     * @param int $limit
152
     *
153
     * @return array
154
     */
155
    public function listGlobalEntries($languageCode = null, $offset = 0, $limit = -1)
156
    {
157
        $limit = $limit === -1 ? self::MAX_LIMIT : $limit;
158
159
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
160
        $query = $this->dbHandler->createSelectQuery();
161
        $query->select(
162
            $this->dbHandler->quoteColumn('action'),
163
            $this->dbHandler->quoteColumn('id'),
164
            $this->dbHandler->quoteColumn('link'),
165
            $this->dbHandler->quoteColumn('is_alias'),
166
            $this->dbHandler->quoteColumn('alias_redirects'),
167
            $this->dbHandler->quoteColumn('lang_mask'),
168
            $this->dbHandler->quoteColumn('is_original'),
169
            $this->dbHandler->quoteColumn('parent'),
170
            $this->dbHandler->quoteColumn('text_md5')
171
        )->from(
172
            $this->dbHandler->quoteTable('ezurlalias_ml')
173
        )->where(
174
            $query->expr->lAnd(
175
                $query->expr->eq(
176
                    $this->dbHandler->quoteColumn('action_type'),
177
                    $query->bindValue('module', null, \PDO::PARAM_STR)
178
                ),
179
                $query->expr->eq(
180
                    $this->dbHandler->quoteColumn('is_original'),
181
                    $query->bindValue(1, null, \PDO::PARAM_INT)
182
                ),
183
                $query->expr->eq(
184
                    $this->dbHandler->quoteColumn('is_alias'),
185
                    $query->bindValue(1, null, \PDO::PARAM_INT)
186
                )
187
            )
188
        )->limit(
189
            $limit,
190
            $offset
191
        );
192 View Code Duplication
        if (isset($languageCode)) {
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...
193
            $query->where(
194
                $query->expr->gt(
195
                    $query->expr->bitAnd(
196
                        $this->dbHandler->quoteColumn('lang_mask'),
197
                        $query->bindValue(
198
                            $this->languageMaskGenerator->generateLanguageIndicator($languageCode, false),
199
                            null,
200
                            \PDO::PARAM_INT
201
                        )
202
                    ),
203
                    0
204
                )
205
            );
206
        }
207
        $statement = $query->prepare();
208
        $statement->execute();
209
210
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
211
    }
212
213
    /**
214
     * Returns boolean indicating if the row with given $id is special root entry.
215
     *
216
     * Special root entry entry will have parentId=0 and text=''.
217
     * In standard installation this entry will point to location with id=2.
218
     *
219
     * @param mixed $id
220
     *
221
     * @return bool
222
     */
223
    public function isRootEntry($id)
224
    {
225
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
226
        $query = $this->dbHandler->createSelectQuery();
227
        $query->select(
228
            $this->dbHandler->quoteColumn('text'),
229
            $this->dbHandler->quoteColumn('parent')
230
        )->from(
231
            $this->dbHandler->quoteTable('ezurlalias_ml')
232
        )->where(
233
            $query->expr->eq(
234
                $this->dbHandler->quoteColumn('id'),
235
                $query->bindValue($id, null, \PDO::PARAM_INT)
236
            )
237
        );
238
        $statement = $query->prepare();
239
        $statement->execute();
240
        $row = $statement->fetch(\PDO::FETCH_ASSOC);
241
242
        return strlen($row['text']) == 0 && $row['parent'] == 0;
243
    }
244
245
    /**
246
     * Downgrades autogenerated entry matched by given $action and $languageId and negatively matched by
247
     * composite primary key.
248
     *
249
     * If language mask of the found entry is composite (meaning it consists of multiple language ids) given
250
     * $languageId will be removed from mask. Otherwise entry will be marked as history.
251
     *
252
     * @param string $action
253
     * @param mixed $languageId
254
     * @param mixed $newId
255
     * @param mixed $parentId
256
     * @param string $textMD5
257
     */
258
    public function cleanupAfterPublish($action, $languageId, $newId, $parentId, $textMD5)
259
    {
260
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
261
        $query = $this->dbHandler->createSelectQuery();
262
        $query->select(
263
            $this->dbHandler->quoteColumn('parent'),
264
            $this->dbHandler->quoteColumn('text_md5'),
265
            $this->dbHandler->quoteColumn('lang_mask')
266
        )->from(
267
            $this->dbHandler->quoteTable('ezurlalias_ml')
268
        )->where(
269
            $query->expr->lAnd(
270
                // 1) Autogenerated aliases that match action and language...
271
                $query->expr->eq(
272
                    $this->dbHandler->quoteColumn('action'),
273
                    $query->bindValue($action, null, \PDO::PARAM_STR)
274
                ),
275
                $query->expr->eq(
276
                    $this->dbHandler->quoteColumn('is_original'),
277
                    $query->bindValue(1, null, \PDO::PARAM_INT)
278
                ),
279
                $query->expr->eq(
280
                    $this->dbHandler->quoteColumn('is_alias'),
281
                    $query->bindValue(0, null, \PDO::PARAM_INT)
282
                ),
283
                $query->expr->gt(
284
                    $query->expr->bitAnd(
285
                        $this->dbHandler->quoteColumn('lang_mask'),
286
                        $query->bindValue($languageId, null, \PDO::PARAM_INT)
287
                    ),
288
                    0
289
                ),
290
                // 2) ...but not newly published entry
291
                $query->expr->not(
292
                    $query->expr->lAnd(
293
                        $query->expr->eq(
294
                            $this->dbHandler->quoteColumn('parent'),
295
                            $query->bindValue($parentId, null, \PDO::PARAM_INT)
296
                        ),
297
                        $query->expr->eq(
298
                            $this->dbHandler->quoteColumn('text_md5'),
299
                            $query->bindValue($textMD5, null, \PDO::PARAM_STR)
300
                        )
301
                    )
302
                )
303
            )
304
        );
305
306
        $statement = $query->prepare();
307
        $statement->execute();
308
        $row = $statement->fetch(\PDO::FETCH_ASSOC);
309
310
        if (!empty($row)) {
311
            // If language mask is composite (consists of multiple languages) then remove given language from entry
312
            if ($row['lang_mask'] & ~($languageId | 1)) {
313
                $this->removeTranslation($row['parent'], $row['text_md5'], $languageId);
314
            } else {
315
                // Otherwise mark entry as history
316
                $this->historize($row['parent'], $row['text_md5'], $newId);
317
            }
318
        }
319
    }
320
321 View Code Duplication
    public function historizeBeforeSwap($action, $languageMask)
322
    {
323
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
324
        $query = $this->dbHandler->createUpdateQuery();
325
        $query->update(
326
            $this->dbHandler->quoteTable('ezurlalias_ml')
327
        )->set(
328
            $this->dbHandler->quoteColumn('is_original'),
329
            $query->bindValue(0, null, \PDO::PARAM_INT)
330
        )->set(
331
            $this->dbHandler->quoteColumn('id'),
332
            $query->bindValue(
333
                $this->getNextId(),
334
                null,
335
                \PDO::PARAM_INT
336
            )
337
        )->where(
338
            $query->expr->lAnd(
339
                $query->expr->eq(
340
                    $this->dbHandler->quoteColumn('action'),
341
                    $query->bindValue($action, null, \PDO::PARAM_STR)
342
                ),
343
                $query->expr->eq(
344
                    $this->dbHandler->quoteColumn('is_original'),
345
                    $query->bindValue(1, null, \PDO::PARAM_INT)
346
                ),
347
                $query->expr->gt(
348
                    $query->expr->bitAnd(
349
                        $this->dbHandler->quoteColumn('lang_mask'),
350
                        $query->bindValue($languageMask, null, \PDO::PARAM_INT)
351
                    ),
352
                    0
353
                )
354
            )
355
        );
356
        $query->prepare()->execute();
357
    }
358
359
    /**
360
     * Updates single row matched by composite primary key.
361
     *
362
     * Sets "is_original" to 0 thus marking entry as history.
363
     *
364
     * Re-links history entries.
365
     *
366
     * When location alias is published we need to check for new history entries created with self::downgrade()
367
     * with the same action and language, update their "link" column with id of the published entry.
368
     * History entry "id" column is moved to next id value so that all active (non-history) entries are kept
369
     * under the same id.
370
     *
371
     * @param mixed $parentId
372
     * @param string $textMD5
373
     */
374 View Code Duplication
    protected function historize($parentId, $textMD5, $newId)
375
    {
376
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
377
        $query = $this->dbHandler->createUpdateQuery();
378
        $query->update(
379
            $this->dbHandler->quoteTable('ezurlalias_ml')
380
        )->set(
381
            $this->dbHandler->quoteColumn('is_original'),
382
            $query->bindValue(0, null, \PDO::PARAM_INT)
383
        )->set(
384
            $this->dbHandler->quoteColumn('link'),
385
            $query->bindValue($newId, null, \PDO::PARAM_INT)
386
        )->set(
387
            $this->dbHandler->quoteColumn('id'),
388
            $query->bindValue(
389
                $this->getNextId(),
390
                null,
391
                \PDO::PARAM_INT
392
            )
393
        )->where(
394
            $query->expr->lAnd(
395
                $query->expr->eq(
396
                    $this->dbHandler->quoteColumn('parent'),
397
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
398
                ),
399
                $query->expr->eq(
400
                    $this->dbHandler->quoteColumn('text_md5'),
401
                    $query->bindValue($textMD5, null, \PDO::PARAM_STR)
402
                )
403
            )
404
        );
405
        $query->prepare()->execute();
406
    }
407
408
    /**
409
     * Updates single row data matched by composite primary key.
410
     *
411
     * Removes given $languageId from entry's language mask
412
     *
413
     * @param mixed $parentId
414
     * @param string $textMD5
415
     * @param mixed $languageId
416
     */
417 View Code Duplication
    protected function removeTranslation($parentId, $textMD5, $languageId)
418
    {
419
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
420
        $query = $this->dbHandler->createUpdateQuery();
421
        $query->update(
422
            $this->dbHandler->quoteTable('ezurlalias_ml')
423
        )->set(
424
            $this->dbHandler->quoteColumn('lang_mask'),
425
            $query->expr->bitAnd(
426
                $this->dbHandler->quoteColumn('lang_mask'),
427
                $query->bindValue(~$languageId, null, \PDO::PARAM_INT)
428
            )
429
        )->where(
430
            $query->expr->lAnd(
431
                $query->expr->eq(
432
                    $this->dbHandler->quoteColumn('parent'),
433
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
434
                ),
435
                $query->expr->eq(
436
                    $this->dbHandler->quoteColumn('text_md5'),
437
                    $query->bindValue($textMD5, null, \PDO::PARAM_STR)
438
                )
439
            )
440
        );
441
        $query->prepare()->execute();
442
    }
443
444
    /**
445
     * Marks all entries with given $id as history entries.
446
     *
447
     * This method is used by Handler::locationMoved(). For this reason rows are not updated with next id value as
448
     * all entries with given id are being marked as history and there is no need for id separation.
449
     * Thus only "link" and "is_original" columns are updated.
450
     *
451
     * @param mixed $id
452
     * @param mixed $link
453
     */
454 View Code Duplication
    public function historizeId($id, $link)
455
    {
456
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
457
        $query = $this->dbHandler->createUpdateQuery();
458
        $query->update(
459
            $this->dbHandler->quoteTable('ezurlalias_ml')
460
        )->set(
461
            $this->dbHandler->quoteColumn('is_original'),
462
            $query->bindValue(0, null, \PDO::PARAM_INT)
463
        )->set(
464
            $this->dbHandler->quoteColumn('link'),
465
            $query->bindValue($link, null, \PDO::PARAM_INT)
466
        )->where(
467
            $query->expr->lAnd(
468
                $query->expr->eq(
469
                    $this->dbHandler->quoteColumn('is_alias'),
470
                    $query->bindValue(0, null, \PDO::PARAM_INT)
471
                ),
472
                $query->expr->eq(
473
                    $this->dbHandler->quoteColumn('action_type'),
474
                    $query->bindValue('eznode', null, \PDO::PARAM_STR)
475
                ),
476
                $query->expr->eq(
477
                    $this->dbHandler->quoteColumn('link'),
478
                    $query->bindValue($id, null, \PDO::PARAM_INT)
479
                )
480
            )
481
        );
482
        $query->prepare()->execute();
483
    }
484
485
    /**
486
     * Updates parent id of autogenerated entries.
487
     *
488
     * Update includes history entries.
489
     *
490
     * @param mixed $oldParentId
491
     * @param mixed $newParentId
492
     */
493 View Code Duplication
    public function reparent($oldParentId, $newParentId)
494
    {
495
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
496
        $query = $this->dbHandler->createUpdateQuery();
497
        $query->update(
498
            $this->dbHandler->quoteTable('ezurlalias_ml')
499
        )->set(
500
            $this->dbHandler->quoteColumn('parent'),
501
            $query->bindValue($newParentId, null, \PDO::PARAM_INT)
502
        )->where(
503
            $query->expr->lAnd(
504
                $query->expr->eq(
505
                    $this->dbHandler->quoteColumn('is_alias'),
506
                    $query->bindValue(0, null, \PDO::PARAM_INT)
507
                ),
508
                $query->expr->eq(
509
                    $this->dbHandler->quoteColumn('parent'),
510
                    $query->bindValue($oldParentId, null, \PDO::PARAM_INT)
511
                )
512
            )
513
        );
514
515
        $query->prepare()->execute();
516
    }
517
518
    /**
519
     * Updates single row data matched by composite primary key.
520
     *
521
     * Use optional parameter $languageMaskMatch to additionally limit the query match with languages.
522
     *
523
     * @param mixed $parentId
524
     * @param string $textMD5
525
     * @param array $values associative array with column names as keys and column values as values
526
     */
527 View Code Duplication
    public function updateRow($parentId, $textMD5, array $values)
528
    {
529
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
530
        $query = $this->dbHandler->createUpdateQuery();
531
        $query->update($this->dbHandler->quoteTable('ezurlalias_ml'));
532
        $this->setQueryValues($query, $values);
533
        $query->where(
534
            $query->expr->lAnd(
535
                $query->expr->eq(
536
                    $this->dbHandler->quoteColumn('parent'),
537
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
538
                ),
539
                $query->expr->eq(
540
                    $this->dbHandler->quoteColumn('text_md5'),
541
                    $query->bindValue($textMD5, null, \PDO::PARAM_STR)
542
                )
543
            )
544
        );
545
        $query->prepare()->execute();
546
    }
547
548
    /**
549
     * Inserts new row in urlalias_ml table.
550
     *
551
     * @param array $values
552
     *
553
     * @return mixed
554
     */
555
    public function insertRow(array $values)
556
    {
557
        // @todo remove after testing
558
        if (
559
            !isset($values['text']) ||
560
            !isset($values['text_md5']) ||
561
            !isset($values['action']) ||
562
            !isset($values['parent']) ||
563
            !isset($values['lang_mask'])) {
564
            throw new \Exception('value set is incomplete: ' . var_export($values, true) . ", can't execute insert");
565
        }
566
        if (!isset($values['id'])) {
567
            $values['id'] = $this->getNextId();
568
        }
569
        if (!isset($values['link'])) {
570
            $values['link'] = $values['id'];
571
        }
572
        if (!isset($values['is_original'])) {
573
            $values['is_original'] = ($values['id'] == $values['link'] ? 1 : 0);
574
        }
575
        if (!isset($values['is_alias'])) {
576
            $values['is_alias'] = 0;
577
        }
578
        if (!isset($values['alias_redirects'])) {
579
            $values['alias_redirects'] = 0;
580
        }
581
        if (!isset($values['action_type'])) {
582
            if (preg_match('#^(.+):.*#', $values['action'], $matches)) {
583
                $values['action_type'] = $matches[1];
584
            }
585
        }
586
        if ($values['is_alias']) {
587
            $values['is_original'] = 1;
588
        }
589
        if ($values['action'] === 'nop:') {
590
            $values['is_original'] = 0;
591
        }
592
593
        /** @var $query \eZ\Publish\Core\Persistence\Database\InsertQuery */
594
        $query = $this->dbHandler->createInsertQuery();
595
        $query->insertInto($this->dbHandler->quoteTable('ezurlalias_ml'));
596
        $this->setQueryValues($query, $values);
597
        $query->prepare()->execute();
598
599
        return $values['id'];
600
    }
601
602
    /**
603
     * Sets value for insert or update query.
604
     *
605
     * @param \eZ\Publish\Core\Persistence\Database\Query|\eZ\Publish\Core\Persistence\Database\InsertQuery|\eZ\Publish\Core\Persistence\Database\UpdateQuery $query
606
     * @param array $values
607
     *
608
     * @throws \Exception
609
     */
610
    protected function setQueryValues(Query $query, $values)
611
    {
612
        foreach ($values as $column => $value) {
613
            // @todo remove after testing
614
            if (!in_array($column, $this->columns['ezurlalias_ml'])) {
615
                throw new \Exception("unknown column '$column' for table 'ezurlalias_ml'");
616
            }
617
            switch ($column) {
618
                case 'text':
619
                case 'action':
620
                case 'text_md5':
621
                case 'action_type':
622
                    $pdoDataType = \PDO::PARAM_STR;
623
                    break;
624
                default:
625
                    $pdoDataType = \PDO::PARAM_INT;
626
            }
627
            $query->set(
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface eZ\Publish\Core\Persistence\Database\Query as the method set() does only exist in the following implementations of said interface: eZ\Publish\Core\Persiste...ine\InsertDoctrineQuery, eZ\Publish\Core\Persiste...ine\UpdateDoctrineQuery.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
628
                $this->dbHandler->quoteColumn($column),
629
                $query->bindValue($value, null, $pdoDataType)
630
            );
631
        }
632
    }
633
634
    /**
635
     * Returns next value for "id" column.
636
     *
637
     * @return mixed
638
     */
639 View Code Duplication
    public function getNextId()
640
    {
641
        $sequence = $this->dbHandler->getSequenceName('ezurlalias_ml_incr', 'id');
642
        /** @var $query \eZ\Publish\Core\Persistence\Database\InsertQuery */
643
        $query = $this->dbHandler->createInsertQuery();
644
        $query->insertInto(
645
            $this->dbHandler->quoteTable('ezurlalias_ml_incr')
646
        );
647
        // ezcDatabase does not abstract the "auto increment id"
648
        // INSERT INTO ezurlalias_ml_incr VALUES(DEFAULT) is not an option due
649
        // to this mysql bug: http://bugs.mysql.com/bug.php?id=42270
650
        // as a result we are forced to check which database is currently used
651
        // to generate the correct SQL query
652
        // see https://jira.ez.no/browse/EZP-20652
653
        if ($this->dbHandler->useSequences()) {
654
            $query->set(
655
                $this->dbHandler->quoteColumn('id'),
656
                "nextval('{$sequence}')"
657
            );
658
        } else {
659
            $query->set(
660
                $this->dbHandler->quoteColumn('id'),
661
                $query->bindValue(null, null, \PDO::PARAM_NULL)
662
            );
663
        }
664
        $query->prepare()->execute();
665
666
        return $this->dbHandler->lastInsertId($sequence);
667
    }
668
669
    /**
670
     * Loads single row data matched by composite primary key.
671
     *
672
     * @param mixed $parentId
673
     * @param string $textMD5
674
     *
675
     * @return array
676
     */
677 View Code Duplication
    public function loadRow($parentId, $textMD5)
678
    {
679
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
680
        $query = $this->dbHandler->createSelectQuery();
681
        $query->select('*')->from(
682
            $this->dbHandler->quoteTable('ezurlalias_ml')
683
        )->where(
684
            $query->expr->lAnd(
685
                $query->expr->eq(
686
                    $this->dbHandler->quoteColumn('parent'),
687
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
688
                ),
689
                $query->expr->eq(
690
                    $this->dbHandler->quoteColumn('text_md5'),
691
                    $query->bindValue($textMD5, null, \PDO::PARAM_STR)
692
                )
693
            )
694
        );
695
696
        $statement = $query->prepare();
697
        $statement->execute();
698
699
        return $statement->fetch(\PDO::FETCH_ASSOC);
700
    }
701
702
    /**
703
     * Loads complete URL alias data by given array of path hashes.
704
     *
705
     * @param string[] $urlHashes URL string hashes
706
     *
707
     * @return array
708
     */
709
    public function loadUrlAliasData(array $urlHashes)
710
    {
711
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
712
        $query = $this->dbHandler->createSelectQuery();
713
714
        $count = count($urlHashes);
715
        foreach ($urlHashes as $level => $urlPartHash) {
716
            $tableName = 'ezurlalias_ml' . ($level === $count - 1 ? '' : $level);
717
718
            if ($level === $count - 1) {
719
                $query->select(
720
                    $this->dbHandler->quoteColumn('id', $tableName),
721
                    $this->dbHandler->quoteColumn('link', $tableName),
722
                    $this->dbHandler->quoteColumn('is_alias', $tableName),
723
                    $this->dbHandler->quoteColumn('alias_redirects', $tableName),
724
                    $this->dbHandler->quoteColumn('is_original', $tableName),
725
                    $this->dbHandler->quoteColumn('action', $tableName),
726
                    $this->dbHandler->quoteColumn('action_type', $tableName),
727
                    $this->dbHandler->quoteColumn('lang_mask', $tableName),
728
                    $this->dbHandler->quoteColumn('text', $tableName),
729
                    $this->dbHandler->quoteColumn('parent', $tableName),
730
                    $this->dbHandler->quoteColumn('text_md5', $tableName)
731
                )->from(
732
                    $this->dbHandler->quoteTable('ezurlalias_ml')
733
                );
734
            } else {
735
                $query->select(
736
                    $this->dbHandler->aliasedColumn($query, 'id', $tableName),
737
                    $this->dbHandler->aliasedColumn($query, 'link', $tableName),
738
                    $this->dbHandler->aliasedColumn($query, 'is_alias', $tableName),
739
                    $this->dbHandler->aliasedColumn($query, 'alias_redirects', $tableName),
740
                    $this->dbHandler->aliasedColumn($query, 'is_original', $tableName),
741
                    $this->dbHandler->aliasedColumn($query, 'action', $tableName),
742
                    $this->dbHandler->aliasedColumn($query, 'action_type', $tableName),
743
                    $this->dbHandler->aliasedColumn($query, 'lang_mask', $tableName),
744
                    $this->dbHandler->aliasedColumn($query, 'text', $tableName),
745
                    $this->dbHandler->aliasedColumn($query, 'parent', $tableName),
746
                    $this->dbHandler->aliasedColumn($query, 'text_md5', $tableName)
747
                )->from(
748
                    $query->alias('ezurlalias_ml', $tableName)
749
                );
750
            }
751
752
            $query->where(
753
                $query->expr->lAnd(
754
                    $query->expr->eq(
755
                        $this->dbHandler->quoteColumn('text_md5', $tableName),
756
                        $query->bindValue($urlPartHash, null, \PDO::PARAM_STR)
757
                    ),
758
                    $query->expr->eq(
759
                        $this->dbHandler->quoteColumn('parent', $tableName),
760
                        // root entry has parent column set to 0
761
                        isset($previousTableName) ? $this->dbHandler->quoteColumn('link', $previousTableName) : $query->bindValue(0, null, \PDO::PARAM_INT)
762
                    )
763
                )
764
            );
765
766
            $previousTableName = $tableName;
767
        }
768
        $query->limit(1);
769
770
        $statement = $query->prepare();
771
        $statement->execute();
772
773
        return $statement->fetch(\PDO::FETCH_ASSOC);
774
    }
775
776
    /**
777
     * Loads autogenerated entry id by given $action and optionally $parentId.
778
     *
779
     * @param string $action
780
     * @param mixed|null $parentId
781
     *
782
     * @return array
783
     */
784 View Code Duplication
    public function loadAutogeneratedEntry($action, $parentId = null)
785
    {
786
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
787
        $query = $this->dbHandler->createSelectQuery();
788
        $query->select(
789
            '*'
790
        )->from(
791
            $this->dbHandler->quoteTable('ezurlalias_ml')
792
        )->where(
793
            $query->expr->lAnd(
794
                $query->expr->eq(
795
                    $this->dbHandler->quoteColumn('action'),
796
                    $query->bindValue($action, null, \PDO::PARAM_STR)
797
                ),
798
                $query->expr->eq(
799
                    $this->dbHandler->quoteColumn('is_original'),
800
                    $query->bindValue(1, null, \PDO::PARAM_INT)
801
                ),
802
                $query->expr->eq(
803
                    $this->dbHandler->quoteColumn('is_alias'),
804
                    $query->bindValue(0, null, \PDO::PARAM_INT)
805
                )
806
            )
807
        );
808
809
        if (isset($parentId)) {
810
            $query->where(
811
                $query->expr->eq(
812
                    $this->dbHandler->quoteColumn('parent'),
813
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
814
                )
815
            );
816
        }
817
818
        $statement = $query->prepare();
819
        $statement->execute();
820
821
        return $statement->fetch(\PDO::FETCH_ASSOC);
822
    }
823
824
    /**
825
     * Loads all data for the path identified by given $id.
826
     *
827
     * @throws \RuntimeException
828
     *
829
     * @param mixed $id
830
     *
831
     * @return array
832
     */
833
    public function loadPathData($id)
834
    {
835
        $pathData = array();
836
837
        while ($id != 0) {
838
            /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
839
            $query = $this->dbHandler->createSelectQuery();
840
            $query->select(
841
                $this->dbHandler->quoteColumn('parent'),
842
                $this->dbHandler->quoteColumn('lang_mask'),
843
                $this->dbHandler->quoteColumn('text')
844
            )->from(
845
                $this->dbHandler->quoteTable('ezurlalias_ml')
846
            )->where(
847
                $query->expr->eq(
848
                    $this->dbHandler->quoteColumn('id'),
849
                    $query->bindValue($id, null, \PDO::PARAM_INT)
850
                )
851
            );
852
853
            $statement = $query->prepare();
854
            $statement->execute();
855
856
            $rows = $statement->fetchAll(\PDO::FETCH_ASSOC);
857
            if (empty($rows)) {
858
                // Normally this should never happen
859
                // @todo remove throw when tested
860
                $path = implode('/', $pathData);
861
                throw new \RuntimeException("Path ({$path}...) is broken, last id is '{$id}': " . __METHOD__);
862
            }
863
864
            $id = $rows[0]['parent'];
865
            array_unshift($pathData, $rows);
866
        }
867
868
        return $pathData;
869
    }
870
871
    /**
872
     * Loads path data identified by given ordered array of hierarchy data.
873
     *
874
     * The first entry in $hierarchyData corresponds to the top-most path element in the path, the second entry the
875
     * child of the first path element and so on.
876
     * This method is faster than self::getPath() since it can fetch all elements using only one query, but can be used
877
     * only for autogenerated paths.
878
     *
879
     * @param array $hierarchyData
880
     *
881
     * @return array
882
     */
883
    public function loadPathDataByHierarchy(array $hierarchyData)
884
    {
885
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
886
        $query = $this->dbHandler->createSelectQuery();
887
888
        $hierarchyConditions = array();
889
        foreach ($hierarchyData as $levelData) {
890
            $hierarchyConditions[] = $query->expr->lAnd(
891
                $query->expr->eq(
892
                    $this->dbHandler->quoteColumn('parent'),
893
                    $query->bindValue(
894
                        $levelData['parent'],
895
                        null,
896
                        \PDO::PARAM_INT
897
                    )
898
                ),
899
                $query->expr->eq(
900
                    $this->dbHandler->quoteColumn('action'),
901
                    $query->bindValue(
902
                        $levelData['action'],
903
                        null,
904
                        \PDO::PARAM_STR
905
                    )
906
                ),
907
                $query->expr->eq(
908
                    $this->dbHandler->quoteColumn('id'),
909
                    $query->bindValue(
910
                        $levelData['id'],
911
                        null,
912
                        \PDO::PARAM_INT
913
                    )
914
                )
915
            );
916
        }
917
918
        $query->select(
919
            $this->dbHandler->quoteColumn('action'),
920
            $this->dbHandler->quoteColumn('lang_mask'),
921
            $this->dbHandler->quoteColumn('text')
922
        )->from(
923
            $this->dbHandler->quoteTable('ezurlalias_ml')
924
        )->where(
925
            $query->expr->lOr($hierarchyConditions)
926
        );
927
928
        $statement = $query->prepare();
929
        $statement->execute();
930
931
        $rows = $statement->fetchAll(\PDO::FETCH_ASSOC);
932
        $rowsMap = array();
933
        foreach ($rows as $row) {
934
            $rowsMap[$row['action']][] = $row;
935
        }
936
937
        if (count($rowsMap) !== count($hierarchyData)) {
938
            throw new \RuntimeException('The path is corrupted.');
939
        }
940
941
        $data = array();
942
        foreach ($hierarchyData as $levelData) {
943
            $data[] = $rowsMap[$levelData['action']];
944
        }
945
946
        return $data;
947
    }
948
949
    /**
950
     * Deletes single custom alias row matched by composite primary key.
951
     *
952
     * @param mixed $parentId
953
     * @param string $textMD5
954
     *
955
     * @return bool
956
     */
957
    public function removeCustomAlias($parentId, $textMD5)
958
    {
959
        /** @var $query \eZ\Publish\Core\Persistence\Database\DeleteQuery */
960
        $query = $this->dbHandler->createDeleteQuery();
961
        $query->deleteFrom(
962
            $this->dbHandler->quoteTable('ezurlalias_ml')
963
        )->where(
964
            $query->expr->lAnd(
965
                $query->expr->eq(
966
                    $this->dbHandler->quoteColumn('parent'),
967
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
968
                ),
969
                $query->expr->eq(
970
                    $this->dbHandler->quoteColumn('text_md5'),
971
                    $query->bindValue($textMD5, null, \PDO::PARAM_STR)
972
                ),
973
                $query->expr->eq(
974
                    $this->dbHandler->quoteColumn('is_alias'),
975
                    $query->bindValue(1, null, \PDO::PARAM_INT)
976
                )
977
            )
978
        );
979
        $statement = $query->prepare();
980
        $statement->execute();
981
982
        return $statement->rowCount() === 1 ?: false;
983
    }
984
985
    /**
986
     * Deletes all rows with given $action and optionally $id.
987
     *
988
     * If $id is set only autogenerated entries will be removed.
989
     *
990
     * @param mixed $action
991
     * @param mixed|null $id
992
     *
993
     * @return bool
994
     */
995 View Code Duplication
    public function remove($action, $id = null)
996
    {
997
        /** @var $query \eZ\Publish\Core\Persistence\Database\DeleteQuery */
998
        $query = $this->dbHandler->createDeleteQuery();
999
        $query->deleteFrom(
1000
            $this->dbHandler->quoteTable('ezurlalias_ml')
1001
        )->where(
1002
            $query->expr->eq(
1003
                $this->dbHandler->quoteColumn('action'),
1004
                $query->bindValue($action, null, \PDO::PARAM_STR)
1005
            )
1006
        );
1007
1008
        if ($id !== null) {
1009
            $query->where(
1010
                $query->expr->lAnd(
1011
                    $query->expr->eq(
1012
                        $this->dbHandler->quoteColumn('is_alias'),
1013
                        $query->bindValue(0, null, \PDO::PARAM_INT)
1014
                    ),
1015
                    $query->expr->eq(
1016
                        $this->dbHandler->quoteColumn('id'),
1017
                        $query->bindValue($id, null, \PDO::PARAM_INT)
1018
                    )
1019
                )
1020
            );
1021
        }
1022
1023
        $query->prepare()->execute();
1024
    }
1025
1026
    /**
1027
     * Loads all autogenerated entries with given $parentId with optionally included history entries.
1028
     *
1029
     * @param mixed $parentId
1030
     * @param bool $includeHistory
1031
     *
1032
     * @return array
1033
     */
1034 View Code Duplication
    public function loadAutogeneratedEntries($parentId, $includeHistory = false)
1035
    {
1036
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
1037
        $query = $this->dbHandler->createSelectQuery();
1038
        $query->select(
1039
            '*'
1040
        )->from(
1041
            $this->dbHandler->quoteTable('ezurlalias_ml')
1042
        )->where(
1043
            $query->expr->lAnd(
1044
                $query->expr->eq(
1045
                    $this->dbHandler->quoteColumn('parent'),
1046
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
1047
                ),
1048
                $query->expr->eq(
1049
                    $this->dbHandler->quoteColumn('action_type'),
1050
                    $query->bindValue('eznode', null, \PDO::PARAM_STR)
1051
                ),
1052
                $query->expr->eq(
1053
                    $this->dbHandler->quoteColumn('is_alias'),
1054
                    $query->bindValue(0, null, \PDO::PARAM_INT)
1055
                )
1056
            )
1057
        );
1058
1059
        if (!$includeHistory) {
1060
            $query->where(
1061
                $query->expr->eq(
1062
                    $this->dbHandler->quoteColumn('is_original'),
1063
                    $query->bindValue(1, null, \PDO::PARAM_INT)
1064
                )
1065
            );
1066
        }
1067
1068
        $statement = $query->prepare();
1069
        $statement->execute();
1070
1071
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
1072
    }
1073
}
1074