Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
| 1 | <?php |
||
| 36 | class JobList implements IJobList { |
||
| 37 | |||
| 38 | /** @var IDBConnection */ |
||
| 39 | protected $connection; |
||
| 40 | |||
| 41 | /**@var IConfig */ |
||
| 42 | protected $config; |
||
| 43 | |||
| 44 | /**@var ITimeFactory */ |
||
| 45 | protected $timeFactory; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * @param IDBConnection $connection |
||
| 49 | * @param IConfig $config |
||
| 50 | * @param ITimeFactory $timeFactory |
||
| 51 | */ |
||
| 52 | public function __construct(IDBConnection $connection, IConfig $config, ITimeFactory $timeFactory) { |
||
| 53 | $this->connection = $connection; |
||
| 54 | $this->config = $config; |
||
| 55 | $this->timeFactory = $timeFactory; |
||
| 56 | } |
||
| 57 | |||
| 58 | /** |
||
| 59 | * @param IJob|string $job |
||
| 60 | * @param mixed $argument |
||
| 61 | */ |
||
| 62 | public function add($job, $argument = null) { |
||
| 63 | if (!$this->has($job, $argument)) { |
||
| 64 | if ($job instanceof IJob) { |
||
| 65 | $class = \get_class($job); |
||
| 66 | } else { |
||
| 67 | $class = $job; |
||
| 68 | } |
||
| 69 | |||
| 70 | $argument = \json_encode($argument); |
||
| 71 | if (\strlen($argument) > 4000) { |
||
| 72 | throw new \InvalidArgumentException('Background job arguments can\'t exceed 4000 characters (json encoded)'); |
||
| 73 | } |
||
| 74 | |||
| 75 | $query = $this->connection->getQueryBuilder(); |
||
| 76 | $query->insert('jobs') |
||
| 77 | ->values([ |
||
| 78 | 'class' => $query->createNamedParameter($class), |
||
| 79 | 'argument' => $query->createNamedParameter($argument), |
||
| 80 | 'last_run' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT), |
||
| 81 | 'last_checked' => $query->createNamedParameter($this->timeFactory->getTime(), IQueryBuilder::PARAM_INT), |
||
| 82 | ]); |
||
| 83 | $query->execute(); |
||
| 84 | } |
||
| 85 | } |
||
| 86 | |||
| 87 | /** |
||
| 88 | * @param IJob|string $job |
||
| 89 | * @param mixed $argument |
||
| 90 | */ |
||
| 91 | public function remove($job, $argument = null) { |
||
| 107 | |||
| 108 | /** |
||
| 109 | * @param int $id |
||
| 110 | */ |
||
| 111 | public function removeById($id) { |
||
| 117 | |||
| 118 | /** |
||
| 119 | * check if a job is in the list |
||
| 120 | * |
||
| 121 | * @param IJob|string $job |
||
| 122 | * @param mixed $argument |
||
| 123 | * @return bool |
||
| 124 | */ |
||
| 125 | public function has($job, $argument) { |
||
| 146 | |||
| 147 | /** |
||
| 148 | * get all jobs in the list |
||
| 149 | * |
||
| 150 | * @return IJob[] |
||
| 151 | * @deprecated 9.0.0 - This method is dangerous since it can cause load and |
||
| 152 | * memory problems when creating too many instances. |
||
| 153 | */ |
||
| 154 | public function getAll() { |
||
| 171 | |||
| 172 | /** |
||
| 173 | * get the next job in the list |
||
| 174 | * |
||
| 175 | * @return IJob|null |
||
| 176 | */ |
||
| 177 | public function getNext() { |
||
| 178 | $query = $this->connection->getQueryBuilder(); |
||
| 179 | $query->select('*') |
||
| 180 | ->from('jobs') |
||
| 181 | ->where($query->expr()->lte('reserved_at', $query->createNamedParameter($this->timeFactory->getTime() - 12 * 3600, IQueryBuilder::PARAM_INT))) |
||
| 182 | ->orderBy('last_checked', 'ASC') |
||
| 183 | ->setMaxResults(1); |
||
| 184 | |||
| 185 | $update = $this->connection->getQueryBuilder(); |
||
| 186 | $update->update('jobs') |
||
| 187 | ->set('reserved_at', $update->createNamedParameter($this->timeFactory->getTime())) |
||
| 188 | ->set('last_checked', $update->createNamedParameter($this->timeFactory->getTime())) |
||
| 189 | ->where($update->expr()->eq('id', $update->createParameter('jobid'))) |
||
| 190 | ->andWhere($update->expr()->eq('reserved_at', $update->createParameter('reserved_at'))) |
||
| 191 | ->andWhere($update->expr()->eq('last_checked', $update->createParameter('last_checked'))); |
||
| 192 | |||
| 193 | $result = $query->execute(); |
||
| 194 | $row = $result->fetch(); |
||
| 195 | $result->closeCursor(); |
||
| 196 | |||
| 197 | if ($row) { |
||
| 198 | $update->setParameter('jobid', $row['id']); |
||
| 199 | $update->setParameter('reserved_at', $row['reserved_at']); |
||
| 200 | $update->setParameter('last_checked', $row['last_checked']); |
||
| 201 | $count = $update->execute(); |
||
| 202 | |||
| 203 | if ($count === 0) { |
||
| 204 | // Background job already executed elsewhere, try again. |
||
| 205 | return $this->getNext(); |
||
| 206 | } |
||
| 207 | $job = $this->buildJob($row); |
||
| 208 | |||
| 209 | if ($job === null) { |
||
| 210 | // Background job from disabled app, try again. |
||
| 211 | return $this->getNext(); |
||
| 212 | } |
||
| 213 | |||
| 214 | return $job; |
||
| 215 | } else { |
||
| 216 | return null; |
||
| 217 | } |
||
| 218 | } |
||
| 219 | |||
| 220 | /** |
||
| 221 | * @param int $id |
||
| 222 | * @return IJob|null |
||
| 223 | */ |
||
| 224 | View Code Duplication | public function getById($id) { |
|
| 239 | |||
| 240 | /** |
||
| 241 | * get the job object from a row in the db |
||
| 242 | * |
||
| 243 | * @param array $row |
||
| 244 | * @return IJob|null |
||
| 245 | */ |
||
| 246 | private function buildJob($row) { |
||
| 271 | |||
| 272 | /** |
||
| 273 | * set the job that was last ran |
||
| 274 | * |
||
| 275 | * @param IJob $job |
||
| 276 | */ |
||
| 277 | public function setLastJob($job) { |
||
| 281 | |||
| 282 | /** |
||
| 283 | * Remove the reservation for a job |
||
| 284 | * |
||
| 285 | * @param IJob $job |
||
| 286 | */ |
||
| 287 | View Code Duplication | public function unlockJob($job) { |
|
| 294 | |||
| 295 | /** |
||
| 296 | * get the id of the last ran job |
||
| 297 | * |
||
| 298 | * @return int |
||
| 299 | * @deprecated 9.1.0 - The functionality behind the value is deprecated, it |
||
| 300 | * only tells you which job finished last, but since we now allow multiple |
||
| 301 | * executors to run in parallel, it's not used to calculate the next job. |
||
| 302 | */ |
||
| 303 | public function getLastJob() { |
||
| 306 | |||
| 307 | /** |
||
| 308 | * @inheritdoc |
||
| 309 | */ |
||
| 310 | public function setLastRun($job) { |
||
| 317 | |||
| 318 | View Code Duplication | public function setExecutionTime($job, $timeTaken) { |
|
| 325 | |||
| 326 | /** |
||
| 327 | * @inheritdoc |
||
| 328 | */ |
||
| 329 | public function listJobs(\Closure $callback) { |
||
| 345 | } |
||
| 346 |
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.