Db::fetchLatestRow()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 0
cts 10
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 8
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * @author    Flipbox Factory
5
 * @copyright Copyright (c) 2017, Flipbox Digital
6
 * @link      https://github.com/flipbox/queue/releases/latest
7
 * @license   https://github.com/flipbox/queue/blob/master/LICENSE
8
 */
9
10
namespace flipbox\queue\queues;
11
12
use flipbox\queue\jobs\JobInterface;
13
use yii\db\Connection;
14
use yii\db\Expression;
15
use yii\db\Query;
16
use yii\di\Instance;
17
18
/**
19
 * @author Flipbox Factory <[email protected]>
20
 * @since 1.0.0
21
 */
22
class Db extends AbstractQueue
23
{
24
    /**
25
     * Status when the job is ready.
26
     */
27
    const STATUS_READY = 0;
28
29
    /**
30
     * Status when the job is running by the worker.
31
     */
32
    const STATUS_ACTIVE = 1;
33
34
    /**
35
     * Status when the job is deleted.
36
     */
37
    const STATUS_DELETED = 2;
38
39
    /**
40
     * The database used for the queue.
41
     *
42
     * This will use default `db` component from Craft application.
43
     * @var string|\yii\db\Connection
44
     */
45
    public $db = 'db';
46
47
    /**
48
     * The name of the table to store the queue.
49
     *
50
     * The table should be pre-created as follows for MySQL:
51
     *
52
     * ```php
53
     * CREATE TABLE queue (
54
     *     id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
55
     *     status TINYINT NOT NULL DEFAULT 0,
56
     *     timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
57
     *     data LONGBLOB
58
     * );
59
     * ```
60
     * @var string
61
     */
62
    public $tableName = '{{%queue}}';
63
64
    /**
65
     * Whether to do hard delete of the deleted job, instead of just flagging the
66
     * status.
67
     * @var boolean
68
     */
69
    public $hardDelete = true;
70
71
    /**
72
     * @inheritdoc
73
     */
74
    public function init()
75
    {
76
        parent::init();
77
        $this->db = Instance::ensure($this->db, Connection::class);
78
    }
79
80
    /**
81
     * @inheritdoc
82
     */
83
    protected function fetchJob()
84
    {
85
        //Avoiding multiple job.
86
        $transaction = $this->db->beginTransaction();
87
        $row = $this->fetchLatestRow();
88
        if ($row == false || !$this->flagRunningRow($row)) {
89
            $transaction->rollBack();
90
            return false;
91
        }
92
        $transaction->commit();
93
94
        $job = $this->deserialize($row['data']);
95
        $job->setId($row['id']);
96
        $job->setHeader('timestamp', $row['timestamp']);
97
98
        return $job;
99
    }
100
101
    /**
102
     * Fetch latest ready job from the table.
103
     *
104
     * Due to the use of AUTO_INCREMENT ID, this will fetch the job with the
105
     * largest ID.
106
     *
107
     * @return array
108
     */
109
    protected function fetchLatestRow()
110
    {
111
        return (new Query())
112
            ->select('*')
113
            ->from($this->tableName)
114
            ->where(['status' => self::STATUS_READY])
115
            ->orderBy(['id' => SORT_ASC])
116
            ->limit(1)
117
            ->one($this->db);
0 ignored issues
show
Bug introduced by
It seems like $this->db can also be of type string; however, yii\db\Query::one() does only seem to accept object<yii\db\Connection>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
118
    }
119
120
    /**
121
     * Flag a row as running. This will update the row ID and status if ready.
122
     *
123
     * @param array $row The row to update.
124
     * @return boolean Whether successful or not.
125
     */
126
    protected function flagRunningRow(array $row)
127
    {
128
        $updated = $this->db->createCommand()
129
            ->update(
130
                $this->tableName,
131
                ['status' => self::STATUS_ACTIVE],
132
                [
133
                    'id' => $row['id'],
134
                    'status' => self::STATUS_READY,
135
                ]
136
            )->execute();
137
        return $updated == 1;
138
    }
139
140
    /**
141
     * @inheritdoc
142
     */
143
    protected function postJob(JobInterface $job, array $options = []): bool
144
    {
145
        return $this->db->createCommand()->insert(
146
            $this->tableName,
147
            [
148
                'timestamp' => new Expression('NOW()'),
149
                'data' => $this->serialize($job),
150
            ]
151
        )->execute() == 1;
152
    }
153
154
    /**
155
     * @inheritdoc
156
     */
157
    public function deleteJob(JobInterface $job): bool
158
    {
159
        if ($this->hardDelete) {
160
            return $this->db->createCommand()->delete(
161
                $this->tableName,
162
                ['id' => $job->getId()]
163
            )->execute() == 1;
164
        } else {
165
            return $this->db->createCommand()->update(
166
                $this->tableName,
167
                ['status' => self::STATUS_DELETED],
168
                ['id' => $job->getId()]
169
            )->execute() == 1;
170
        }
171
    }
172
173
    /**
174
     * @inheritdoc
175
     */
176
    public function releaseJob(JobInterface $job): bool
177
    {
178
        return $this->db->createCommand()->update(
179
            $this->tableName,
180
            ['status' => self::STATUS_READY],
181
            ['id' => $job->getId()]
182
        )->execute() == 1;
183
    }
184
185
    /**
186
     * @inheritdoc
187
     */
188
    public function getSize(): int
189
    {
190
        return (new Query())
191
            ->select('*')
192
            ->from($this->tableName)
193
            ->where(['status' => self::STATUS_READY])
194
            ->count('*', $this->db);
0 ignored issues
show
Bug introduced by
It seems like $this->db can also be of type string; however, yii\db\Query::count() does only seem to accept object<yii\db\Connection>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
195
    }
196
197
    /**
198
     * @inheritdoc
199
     */
200
    public function purge(): bool
201
    {
202
        return false;
203
    }
204
}
205