Completed
Push — master ( 58e792...f01f49 )
by Matthew
05:34
created

JobManager::getJob()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 63
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 4.002

Importance

Changes 0
Metric Value
dl 0
loc 63
ccs 38
cts 40
cp 0.95
rs 8.8945
c 0
b 0
f 0
cc 4
eloc 42
nc 6
nop 4
crap 4.002

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
namespace Dtc\QueueBundle\ORM;
4
5
use Doctrine\DBAL\LockMode;
6
use Doctrine\ORM\EntityManager;
7
use Doctrine\ORM\EntityRepository;
8
use Doctrine\ORM\Id\AssignedGenerator;
9
use Doctrine\ORM\Mapping\ClassMetadata;
10
use Doctrine\ORM\QueryBuilder;
11
use Dtc\QueueBundle\Doctrine\BaseJobManager;
12
use Dtc\QueueBundle\Entity\Job;
13
use Dtc\QueueBundle\Model\BaseJob;
14
15
class JobManager extends BaseJobManager
16
{
17
    protected $formerIdGenerators;
18
    protected static $saveInsertCalled = null;
19
    protected static $resetInsertCalled = null;
20
21 8
    public function stopIdGenerator($objectName)
22
    {
23 8
        $objectManager = $this->getObjectManager();
24 8
        $repository = $objectManager->getRepository($objectName);
25
        /** @var ClassMetadata $metadata */
26 8
        $metadata = $objectManager->getClassMetadata($repository->getClassName());
27 8
        $this->formerIdGenerators[$objectName]['generator'] = $metadata->idGenerator;
28 8
        $this->formerIdGenerators[$objectName]['type'] = $metadata->generatorType;
29 8
        $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);
30 8
        $metadata->setIdGenerator(new AssignedGenerator());
31 8
    }
32
33 1
    public function restoreIdGenerator($objectName)
34
    {
35 1
        $objectManager = $this->getObjectManager();
36 1
        $repository = $objectManager->getRepository($objectName);
37
        /** @var ClassMetadata $metadata */
38 1
        $metadata = $objectManager->getClassMetadata($repository->getClassName());
39 1
        $generator = $this->formerIdGenerators[$objectName]['generator'];
40 1
        $type = $this->formerIdGenerators[$objectName]['type'];
41 1
        $metadata->setIdGeneratorType($type);
42 1
        $metadata->setIdGenerator($generator);
43 1
    }
44
45 3
    public function countJobsByStatus($objectName, $status, $workerName = null, $method = null)
46
    {
47
        /** @var EntityManager $objectManager */
48 3
        $objectManager = $this->getObjectManager();
49
50
        $qb = $objectManager
51 3
            ->createQueryBuilder()
52 3
            ->select('count(a.id)')
53 3
            ->from($objectName, 'a')
54 3
            ->where('a.status = :status');
55
56 3
        if (null !== $workerName) {
57 1
            $qb->andWhere('a.workerName = :workerName')
58 1
                ->setParameter(':workerName', $workerName);
59
        }
60
61 3
        if (null !== $method) {
62 1
            $qb->andWhere('a.method = :method')
63 1
                ->setParameter(':method', $workerName);
64
        }
65
66 3
        $count = $qb->setParameter(':status', $status)
67 3
            ->getQuery()->getSingleScalarResult();
68
69 3
        if (!$count) {
70 1
            return 0;
71
        }
72
73 3
        return $count;
74
    }
75
76
    /**
77
     * @param string|null $workerName
78
     * @param string|null $method
79
     *
80
     * @return int Count of jobs pruned
81
     */
82 1
    public function pruneErroneousJobs($workerName = null, $method = null)
83
    {
84
        /** @var EntityManager $objectManager */
85 1
        $objectManager = $this->getObjectManager();
86 1
        $qb = $objectManager->createQueryBuilder()->delete($this->getArchiveObjectName(), 'j');
87 1
        $qb->where('j.status = :status')
88 1
            ->setParameter(':status', BaseJob::STATUS_ERROR);
89
90 1
        $this->addWorkerNameCriterion($qb, $workerName, $method);
91 1
        $query = $qb->getQuery();
92
93 1
        return intval($query->execute());
94
    }
95
96 11
    protected function resetSaveOk($function) {
97 11
        $objectManager = $this->getObjectManager();
98 11
        $splObjectHash = spl_object_hash($objectManager);
99
100 11
        if ($function === 'save') {
101 11
            $compare = static::$resetInsertCalled;
102
        }
103
        else {
104 1
            $compare = static::$saveInsertCalled;
105
        }
106
107 11
        if ($splObjectHash === $compare)
108
        {
109
            // Insert SQL is cached...
110
            $msg = "Can't call save and reset within the same process cycle (or using the same EntityManager)";
111
            throw new \Exception($msg);
112
        }
113
114 11
        if ($function === 'save') {
115 11
            static::$saveInsertCalled = spl_object_hash($objectManager);
116
        }
117
        else {
118 1
            static::$resetInsertCalled = spl_object_hash($objectManager);
119
        }
120 11
    }
121
122
123 7
    protected function addWorkerNameCriterion(QueryBuilder $queryBuilder, $workerName = null, $method = null)
124
    {
125 7
        if (null !== $workerName) {
126 3
            $queryBuilder->andWhere('j.workerName = :workerName')->setParameter(':workerName', $workerName);
127
        }
128
129 7
        if (null !== $method) {
130 2
            $queryBuilder->andWhere('j.method = :method')->setParameter(':method', $method);
131
        }
132 7
    }
133
134 1
    protected function updateExpired($workerName = null, $method = null)
135
    {
136
        /** @var EntityManager $objectManager */
137 1
        $objectManager = $this->getObjectManager();
138 1
        $qb = $objectManager->createQueryBuilder()->update($this->getObjectName(), 'j');
139 1
        $qb->set('j.status', ':newStatus');
140 1
        $qb->where('j.expiresAt <= :expiresAt')
141 1
            ->setParameter(':expiresAt', new \DateTime());
142 1
        $qb->andWhere('j.status = :status')
143 1
            ->setParameter(':status', BaseJob::STATUS_NEW)
144 1
            ->setParameter(':newStatus', Job::STATUS_EXPIRED);
145
146 1
        $this->addWorkerNameCriterion($qb, $workerName, $method);
147 1
        $query = $qb->getQuery();
148
149 1
        return intval($query->execute());
150
    }
151
152
    /**
153
     * Removes archived jobs older than $olderThan.
154
     *
155
     * @param \DateTime $olderThan
156
     */
157 1
    public function pruneArchivedJobs(\DateTime $olderThan)
158
    {
159
        /** @var EntityManager $objectManager */
160 1
        $objectManager = $this->getObjectManager();
161 1
        $qb = $objectManager->createQueryBuilder()->delete($this->getArchiveObjectName(), 'j');
162
        $qb = $qb
163 1
            ->where('j.updatedAt < :updatedAt')
164 1
            ->setParameter(':updatedAt', $olderThan);
165
166 1
        $query = $qb->getQuery();
167
168 1
        return $query->execute();
169
    }
170
171 1
    public function getJobCount($workerName = null, $method = null)
172
    {
173
        /** @var EntityManager $objectManager */
174 1
        $objectManager = $this->getObjectManager();
175 1
        $qb = $objectManager->createQueryBuilder();
176
177 1
        $qb = $qb->select('count(j)')->from($this->getObjectName(), 'j');
178
179 1
        $where = 'where';
180 1
        if (null !== $workerName) {
181
            if (null !== $method) {
182
                $qb->where($qb->expr()->andX(
183
                    $qb->expr()->eq('j.workerName', ':workerName'),
184
                                                $qb->expr()->eq('j.method', ':method')
185
                ))
186
                    ->setParameter(':method', $method);
187
            } else {
188
                $qb->where('j.workerName = :workerName');
189
            }
190
            $qb->setParameter(':workerName', $workerName);
191
            $where = 'andWhere';
192 1
        } elseif (null !== $method) {
193
            $qb->where('j.method = :method')->setParameter(':method', $method);
194
            $where = 'andWhere';
195
        }
196
197 1
        $dateTime = new \DateTime();
198
        // Filter
199
        $qb
200 1
            ->$where($qb->expr()->orX(
201 1
                $qb->expr()->isNull('j.whenAt'),
202 1
                                        $qb->expr()->lte('j.whenAt', ':whenAt')
203
            ))
204 1
            ->andWhere($qb->expr()->orX(
205 1
                $qb->expr()->isNull('j.expiresAt'),
206 1
                $qb->expr()->gt('j.expiresAt', ':expiresAt')
207
            ))
208 1
            ->andWhere('j.locked is NULL')
209 1
            ->setParameter(':whenAt', $dateTime)
210 1
            ->setParameter(':expiresAt', $dateTime);
211
212 1
        $query = $qb->getQuery();
213
214 1
        return $query->getSingleScalarResult();
215
    }
216
217
    /**
218
     * Get Jobs statuses.
219
     */
220
    public function getStatus()
221
    {
222
        $result = [];
223
        $this->getStatusByEntityName($this->getObjectName(), $result);
224
        $this->getStatusByEntityName($this->getObjectName(), $result);
225
226
        $finalResult = [];
227
        foreach ($result as $key => $item) {
228
            ksort($item);
229
            $finalResult[$key] = $item;
230
        }
231
232
        return $finalResult;
233
    }
234
235
    /**
236
     * @param string $entityName
237
     */
238
    protected function getStatusByEntityName($entityName, array &$result)
239
    {
240
        /** @var EntityManager $objectManager */
241
        $objectManager = $this->getObjectManager();
242
        $result1 = $objectManager->getRepository($entityName)->createQueryBuilder('j')->select('j.workerName, j.method, j.status, count(j) as c')
243
            ->groupBy('j.workerName, j.method, j.status')->getQuery()->getArrayResult();
244
245
        foreach ($result1 as $item) {
246
            $method = $item['workerName'].'->'.$item['method'];
247
            if (!isset($result[$method])) {
248
                $result[$method] = [BaseJob::STATUS_NEW => 0,
249
                    BaseJob::STATUS_RUNNING => 0,
250
                    BaseJob::STATUS_SUCCESS => 0,
251
                    BaseJob::STATUS_ERROR => 0, ];
252
            }
253
            $result[$method][$item['status']] += intval($item['c']);
254
        }
255
    }
256
257
    /**
258
     * Get the next job to run (can be filtered by workername and method name).
259
     *
260
     * @param string $workerName
261
     * @param string $methodName
262
     * @param bool   $prioritize
263
     *
264
     * @return Job|null
265
     */
266 5
    public function getJob($workerName = null, $methodName = null, $prioritize = true, $runId = null)
267
    {
268 5
        $uniqid = uniqid(gethostname().'-'.getmypid(), true);
269 5
        $hash = hash('sha256', $uniqid);
270
271
        /** @var EntityManager $objectManager */
272 5
        $objectManager = $this->getObjectManager();
273
274 5
        $objectManager->beginTransaction();
275
276
        /** @var EntityRepository $repository */
277 5
        $repository = $this->getRepository();
278 5
        $qb = $repository->createQueryBuilder('j');
279 5
        $dateTime = new \DateTime();
280
        $qb
281 5
            ->select('j')
282 5
            ->where('j.status = :status')->setParameter(':status', BaseJob::STATUS_NEW)
283 5
            ->andWhere('j.locked is NULL')
284 5
            ->andWhere($qb->expr()->orX(
285 5
                $qb->expr()->isNull('j.whenAt'),
286 5
                        $qb->expr()->lte('j.whenAt', ':whenAt')
287
            ))
288 5
            ->andWhere($qb->expr()->orX(
289 5
                $qb->expr()->isNull('j.expiresAt'),
290 5
                        $qb->expr()->gt('j.expiresAt', ':expiresAt')
291
            ))
292 5
            ->setParameter(':whenAt', $dateTime)
293 5
            ->setParameter(':expiresAt', $dateTime);
294
295 5
        $this->addWorkerNameCriterion($qb, $workerName, $methodName);
296
297 5
        if ($prioritize) {
298 5
            $qb->add('orderBy', 'j.priority ASC, j.whenAt ASC');
0 ignored issues
show
Documentation introduced by
'j.priority ASC, j.whenAt ASC' is of type string, but the function expects a object<Doctrine\ORM\Query\Expr\Base>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
299
        } else {
300
            $qb->orderBy('j.whenAt', 'ASC');
301
        }
302 5
        $qb->setMaxResults(1);
303
304
        /** @var QueryBuilder $qb */
305 5
        $query = $qb->getQuery();
306 5
        $query->setLockMode(LockMode::PESSIMISTIC_WRITE);
307 5
        $jobs = $query->getResult();
308
309 5
        if ($jobs) {
310
            /** @var Job $job */
311 4
            $job = $jobs[0];
312 4
            if (!$job) {
313
                throw new \Exception("No job found for $hash, even though last result was count ".count($jobs));
314
            }
315 4
            $job->setLocked(true);
316 4
            $job->setLockedAt(new \DateTime());
317 4
            $job->setStatus(BaseJob::STATUS_RUNNING);
318 4
            $job->setRunId($runId);
319 4
            $objectManager->commit();
320 4
            $objectManager->flush();
321
322 4
            return $job;
323
        }
324
325 2
        $objectManager->rollback();
326
327 2
        return null;
328
    }
329
}
330