gateway = $gateway; $this->app = $app; $this->db = $database; $this->confService = $confService; $this->eventService = $eventService; } /** * @param array $option * * @throws PipedriveRequestQueuesException * * @return int */ public function addRequest(array $option): int { $default = [ 'check_sum' => '', 'command' => '', 'on_after_done' => '', 'not_before' => 0, 'call_type' => 'pipedrive', 'is_looped' => 0, 'setting_name' => '', ]; $hCommand = []; if (array_key_exists('method', $option)) { $hCommand['method'] = $option['method']; unset($option['method']); } if (array_key_exists('args', $option)) { $hCommand['args'] = $option['args']; unset($option['args']); } if (!empty($hCommand)) { $option['command'] = json_encode($hCommand, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); } if (!empty($option['check_sum'])) { $default['check_sum'] = $option['check_sum']; } else { $default['check_sum'] = array_key_exists('command', $option) ? md5($option['command']) : ''; } $option = array_merge($default, $option); if (!array_key_exists('runner', $option)) { throw new PipedriveRequestQueuesException('Runner is missing!'); } if (!empty($option['on_after_done']) && is_array($option['on_after_done'])) { $option['on_after_done'] = json_encode( $option['on_after_done'], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT ); } $check = 'SELECT EXISTS( SELECT prq.id FROM `pipedrive_request_queues` AS `prq` WHERE prq.deleted=0 AND prq.completed=0 AND prq.check_sum=:check_sum AND prq.runner=:runner )'; if (empty( $this->db->fetchValue( $check, ['runner' => $option['runner'], 'check_sum' => $option['check_sum']] ) )) { $add = 'INSERT INTO `pipedrive_request_queues` (`command`, `on_after_done`, `runner`, `not_before`, `check_sum`, `call_type`, `is_looped`, `setting_name`, `created_at` ) VALUES (:command, :on_after_done, :runner, :not_before, :check_sum, :call_type, :is_looped, :setting_name, NOW())'; try { $this->db->perform($add, $option); } catch (DatabaseExceptionInterface $exception) { throw new PipedriveRequestQueuesException($exception->getMessage()); } return $this->db->lastInsertId(); } return 0; } /** * @param string|null $callType * * @throws PipedriveConfigurationException * @throws PipedriveEventException * @throws PipedriveRequestQueuesException * @throws PipedriveMetaException * @throws Exception * * @return void */ public function execute(?string $callType = null): void { $callType = $callType ?? 'pipedrive'; $jobs = $this->gateway->getNewRequestsByCallType($callType); if (!empty($jobs)) { $batch_loop = self::_BATCH; $iCount = 0; $bSkipWait = count($jobs) <= 1; foreach ($jobs as $job) { $settings = $this->confService->getSettings(); // HANDLE CAN EXECUTE if (!empty($job['setting_name']) && array_key_exists( $job['setting_name'], $settings ) && $settings[$job['setting_name']] === false) { // SKIP continue; } // HANDLE is_looped if (!empty($job['is_looped']) && !empty($job['setting_name'])) { $settingInterval = sprintf('%s_interval', $job['setting_name']); if (!array_key_exists($settingInterval, $settings)) { // SKIP or ERROR? continue; } $interval = $settings[$settingInterval]; $modifiedAt = new DateTime($job['modified_at']); $iModifiedAt = $modifiedAt->getTimestamp(); if (time() < $iModifiedAt + $interval) { // SKIP, waiting for next running time continue; } } if (!empty($job['is_looped'])) { $this->db->perform( 'UPDATE `pipedrive_request_queues` SET `modified_at` = NOW() WHERE `id` = :id', ['id' => $job['id']] ); } else { $this->db->perform( 'UPDATE `pipedrive_request_queues` SET `amount_attempts` = `amount_attempts` +1 WHERE `id` = :id', ['id' => $job['id']] ); } try { $oClass = $this->app->Container->get($job['runner']); } catch (ServiceNotFoundException $exception) { throw new PipedriveRequestQueuesException($exception->getMessage()); } $hCommand = json_decode($job['command'], true); if (is_array($hCommand)) { $xArg = $hCommand['args']; $sMethod = $hCommand['method']; try { /** @var PipedriveServerResponseInterface $xReturn */ $xReturn = call_user_func_array([$oClass, $sMethod], $xArg); } catch (RuntimeException $exception) { $this->eventService->add($exception->getMessage()); continue; } $isLooped = !empty($job['is_looped']); if (empty($job['on_after_done'])) { $this->markJobAsComplete($job['id'], $isLooped); } else { $this->onAfterDone($job['id'], $xReturn, $job['on_after_done'], $isLooped); } //BATCH PROCESS CHECK - take a nap if ($bSkipWait === false) { if ($iCount === $batch_loop) { $batch_loop += self::_BATCH; @usleep(self::_WAITING_TIME); } echo 'Script always alive... '; } } else { continue; } } } } /** * @param int $id * @param PipedriveServerResponseInterface|null $response * @param string|null $onAfter * @param bool $looped * * @throws PipedriveEventException * * @return void */ private function onAfterDone( int $id, ?PipedriveServerResponseInterface $response, ?string $onAfter = null, bool $looped = false ): void { $hOnAfter = !empty($onAfter) ? json_decode($onAfter, true) : []; if (!empty($hOnAfter) && array_key_exists('runner', $hOnAfter) && array_key_exists( 'method', $hOnAfter ) && array_key_exists('args', $hOnAfter)) { $oClass = $this->app->Container->get($hOnAfter['runner']); if (!empty($hOnAfter['replace_in_args']) && in_array($response->getStatusCode(), [200, 201], true)) { $jsonData = $response->getData(); foreach ($hOnAfter['args'] as &$xArg) { if (is_string($xArg)) { foreach ($hOnAfter['replace_in_args'] as $replace_with) { if (!empty($jsonData[$replace_with])) { $xArg = sprintf($xArg, $jsonData[$replace_with]); } } } } } unset($xArg); try { call_user_func_array( [$oClass, $hOnAfter['method']], $hOnAfter['args'] ); // ADD EVENT if (array_key_exists('event', $hOnAfter) && !empty($hOnAfter['event']) && is_string( $hOnAfter['event'] ) && in_array($response->getStatusCode(), [200, 201], true)) { $this->eventService->add($hOnAfter['event']); } } catch (RuntimeException $exception) { $this->eventService->add($exception->getMessage()); } } $this->markJobAsComplete($id, $looped); } /** * @param int $id * @param bool $looped * * @return void */ private function markJobAsComplete(int $id, bool $looped): void { if (is_numeric($id) && $looped === false) { $this->db->perform('UPDATE `pipedrive_request_queues` SET `completed` = 1 WHERE `id` = :id', ['id' => $id]); $this->completedIds[] = $id; } } /** * @return void */ public function cleanup(): void { $this->db->perform('DELETE FROM `pipedrive_request_queues` WHERE `completed` = 1'); $this->eventService->deleteByInterval(); unset($this->completedIds); } }