403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/cvetdv.ru/bitrix/modules/bizproc/classes/general/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/cvetdv.ru/bitrix/modules/bizproc/classes/general/workflow.php
<?php

use Bitrix\Bizproc\Workflow\Entity\WorkflowUserTable;

/**
* Workflow instance.
*/
class CBPWorkflow
{
	private bool $isNew = false;
	private bool $isAbandoned = false;
	private string $instanceId;

	protected CBPRuntime $runtime;
	protected CBPWorkflowPersister $persister;

	protected ?CBPCompositeActivity $rootActivity;

	protected array $activitiesQueue = [];
	protected array $eventsQueue = [];

	private array $activitiesNamesMap = [];

	/************************  PROPERTIES  *******************************/

	public function getInstanceId()
	{
		return $this->instanceId;
	}

	/**
	 * @return CBPRuntime
	 */
	public function getRuntime()
	{
		return $this->runtime;
	}

	public function getRootActivity(): CBPCompositeActivity
	{
		return $this->rootActivity;
	}

	private function getWorkflowStatus()
	{
		return $this->rootActivity->getWorkflowStatus();
	}

	protected function setWorkflowStatus($newStatus)
	{
		$this->rootActivity->setWorkflowStatus($newStatus);
		$this->getRuntime()->onWorkflowStatusChanged($this->getInstanceId(), $newStatus);
		$this->syncStatus($newStatus);
	}

	public function getService($name)
	{
		return $this->runtime->getService($name);
	}

	public function getDocumentId()
	{
		return $this->rootActivity->getDocumentId();
	}

	public function getDocumentType()
	{
		return $this->rootActivity->getDocumentType();
	}

	public function getTemplateId(): int
	{
		return (int)$this->rootActivity->getWorkflowTemplateId();
	}

	public function getStartedBy(): ?int
	{
		$startedBy = (int)CBPHelper::stripUserPrefix($this->rootActivity->{\CBPDocument::PARAM_TAGRET_USER});

		return $startedBy ?: null;
	}

	public function getPersister(): CBPWorkflowPersister
	{
		return $this->persister;
	}

	/************************  CONSTRUCTORS  ****************************************************/

	/**
	* Public constructor initializes a new workflow instance with the specified ID.
	*
	* @param mixed $instanceId - ID of the new workflow instance.
	* @param mixed $runtime - Runtime object.
	*/
	public function __construct($instanceId, CBPRuntime $runtime)
	{
		if (!$instanceId)
		{
			throw new Exception("instanceId");
		}

		$this->instanceId = $instanceId;
		$this->runtime = $runtime;
		$this->persister = CBPWorkflowPersister::GetPersister();
	}

	/**
	 * Remove workflow object from serialized data
	 * @return array
	 */
	public function __sleep()
	{
		return [];
	}

	/************************  CREATE / LOAD WORKFLOW  ****************************************/

	public function initialize(
		CBPActivity $rootActivity,
		$documentId,
		$workflowParameters = [],
		$workflowVariablesTypes = [],
		$workflowParametersTypes = [],
		$workflowTemplateId = 0
	)
	{
		$this->rootActivity = $rootActivity;
		$rootActivity->setWorkflow($this);
		if (method_exists($rootActivity, 'setWorkflowTemplateId'))
		{
			$rootActivity->setWorkflowTemplateId($workflowTemplateId);
		}

		if (method_exists($rootActivity, 'setTemplateUserId'))
		{
			$rootActivity->setTemplateUserId(
				CBPWorkflowTemplateLoader::getTemplateUserId($workflowTemplateId)
			);
		}

		$arDocumentId = CBPHelper::parseDocumentId($documentId);

		$rootActivity->setDocumentId($arDocumentId);

		$documentService = $this->getService("DocumentService");
		$documentType = $workflowParameters[CBPDocument::PARAM_DOCUMENT_TYPE]
			?? $documentService->getDocumentType($arDocumentId)
		;

		unset($workflowParameters[CBPDocument::PARAM_DOCUMENT_TYPE]);

		if ($documentType !== null)
		{
			$rootActivity->setDocumentType($documentType);
			$rootActivity->setFieldTypes($documentService->getDocumentFieldTypes($documentType));
		}

		$rootActivity->setProperties($workflowParameters);

		$rootActivity->setVariablesTypes($workflowVariablesTypes);
		if (is_array($workflowVariablesTypes))
		{
			foreach ($workflowVariablesTypes as $k => $v)
			{
				$variableValue = $v["Default"] ?? null;
				if ($documentType && $fieldTypeObject = $documentService->getFieldTypeObject($documentType, $v))
				{
					$fieldTypeObject->setDocumentId($arDocumentId);
					$variableValue = $fieldTypeObject->internalizeValue('Variable', $variableValue);
				}

				//set defaults on start
				$rootActivity->setVariable($k, $variableValue);
			}
		}

		$rootActivity->setPropertiesTypes($workflowParametersTypes);
	}

	public function reload(CBPActivity $rootActivity)
	{
		$this->rootActivity = $rootActivity;
		$rootActivity->setWorkflow($this);

		switch ($this->getWorkflowStatus())
		{
			case CBPWorkflowStatus::Completed:
			case CBPWorkflowStatus::Terminated:
				throw new Exception("InvalidAttemptToLoad");
		}
	}

	/************************  EXECUTE WORKFLOW  ************************************************/

	/**
	* Starts new workflow instance.
	*
	*/
	public function start(): void
	{
		if ($this->getWorkflowStatus() !== CBPWorkflowStatus::Created)
		{
			throw new Exception("CanNotStartInstanceTwice");
		}

		$this->isNew = true;
		$this->run();
	}

	/**
	* Resume existing workflow.
	*
	*/
	public function resume(): void
	{
		if ($this->getWorkflowStatus() !== CBPWorkflowStatus::Suspended)
		{
			throw new Exception("CanNotResumeInstance");
		}

		$this->run();
	}

	private function run(): void
	{
		try
		{
			$this->setWorkflowStatus(CBPWorkflowStatus::Running);
			if ($this->isNew)
			{
				$this->rootActivity->setReadOnlyData(
					$this->rootActivity->pullProperties()
				);

				$this->initializeActivity($this->rootActivity);
				$this->executeActivity($this->rootActivity);
			}

			$this->runQueue();
		}
		catch (Exception $e)
		{
			$this->terminate($e);

			throw $e;
		}

		if ($this->rootActivity->executionStatus === CBPActivityExecutionStatus::Closed)
		{
			$this->setWorkflowStatus(CBPWorkflowStatus::Completed);
		}
		elseif ($this->getWorkflowStatus() === CBPWorkflowStatus::Running)
		{
			$this->setWorkflowStatus(CBPWorkflowStatus::Suspended);
		}

		$this->persister->saveWorkflow($this->rootActivity, true);
	}

	public function isNew()
	{
		return $this->isNew;
	}

	public function abandon(): void
	{
		$this->isAbandoned = true;
	}

	public function isAbandoned(): bool
	{
		return $this->isAbandoned;
	}

	public function isFinished(): bool
	{
		if ($this->isAbandoned())
		{
			return true;
		}

		return CBPWorkflowStatus::isFinished((int)$this->getWorkflowStatus());
	}

	/**********************  EXTERNAL EVENTS  **************************************************************/

	/**
	* Resume the workflow instance and transfer the specified event to it.
	*
	* @param string $eventName - Event name.
	* @param array $eventParameters - Event parameters.
	*/
	public function sendExternalEvent(string $eventName, array $eventParameters = []): void
	{
		$this->addEventToQueue($eventName, $eventParameters);

		if ($this->getWorkflowStatus() !== CBPWorkflowStatus::Running)
		{
			$this->resume();
		}
	}

	/***********************  SEARCH ACTIVITY BY NAME  ****************************************************/

	private function fillNameActivityMapInternal(CBPActivity $activity)
	{
		$this->activitiesNamesMap[$activity->getName()] = $activity;

		if ($activity instanceof \CBPCompositeActivity)
		{
			$arSubActivities = $activity->collectNestedActivities();
			foreach ($arSubActivities as $subActivity)
			{
				$this->fillNameActivityMapInternal($subActivity);
			}
		}
	}

	private function fillNameActivityMap()
	{
		if (!is_array($this->activitiesNamesMap))
		{
			$this->activitiesNamesMap = [];
		}

		if (count($this->activitiesNamesMap) > 0)
		{
			return;
		}

		$this->fillNameActivityMapInternal($this->rootActivity);
	}

	/**
	* Returns activity by its name.
	*
	* @param mixed $activityName - Activity name.
	* @return CBPActivity - Returns activity object or null if activity is not found.
	*/
	public function getActivityByName($activityName)
	{
		if ($activityName == '')
		{
			throw new Exception('activityName');
		}

		$activity = null;

		$this->fillNameActivityMap();

		if (array_key_exists($activityName, $this->activitiesNamesMap))
		{
			$activity = $this->activitiesNamesMap[$activityName];
		}

		return $activity;
	}

	/************************  ACTIVITY EXECUTION  *************************************************/

	/**
	* Initializes the specified activity by calling its method Initialize.
	*
	* @param CBPActivity $activity
	*/
	public function initializeActivity(CBPActivity $activity)
	{
		if ($activity->executionStatus !== CBPActivityExecutionStatus::Initialized)
		{
			throw new Exception("InvalidInitializingState");
		}

		$activity->initialize();
	}

	/**
	* Plans specified activity for execution.
	*
	* @param CBPActivity $activity - Activity object.
	* @param mixed $eventParameters - Optional parameters.
	*/
	public function executeActivity(CBPActivity $activity, array $eventParameters = [])
	{
		if ($activity->executionStatus !== CBPActivityExecutionStatus::Initialized)
		{
			throw new Exception("InvalidExecutionState");
		}

		$activity->setStatus(CBPActivityExecutionStatus::Executing, $eventParameters);
		$this->addItemToQueue([$activity, CBPActivityExecutorOperationType::Execute]);
	}

	/**
	* Close specified activity.
	*
	* @param CBPActivity $activity - Activity object.
	* @param mixed $arEventParameters - Optional parameters.
	*/
	public function closeActivity(CBPActivity $activity, $arEventParameters = [])
	{
		switch ($activity->executionStatus)
		{
			case CBPActivityExecutionStatus::Executing:
				$activity->markCompleted($arEventParameters);
				return;

			case CBPActivityExecutionStatus::Canceling:
				$activity->markCanceled($arEventParameters);
				return;

			case CBPActivityExecutionStatus::Closed:
				return;

			case CBPActivityExecutionStatus::Faulting:
				$activity->markFaulted($arEventParameters);
				return;
		}

		throw new Exception("InvalidClosingState");
	}

	/**
	* Cancel specified activity.
	*
	* @param CBPActivity $activity - Activity object.
	* @param mixed $arEventParameters - Optional parameters.
	*/
	public function cancelActivity(CBPActivity $activity, $arEventParameters = [])
	{
		if ($activity->executionStatus !== CBPActivityExecutionStatus::Executing)
		{
			throw new Exception("InvalidCancelingState");
		}

		$activity->setStatus(CBPActivityExecutionStatus::Canceling, $arEventParameters);
		$this->addItemToQueue(array($activity, CBPActivityExecutorOperationType::Cancel));
	}

	public function faultActivity(CBPActivity $activity, Exception $e, $arEventParameters = [])
	{
		if ($activity->executionStatus === CBPActivityExecutionStatus::Closed)
		{
			if ($activity->parent === null)
			{
				$this->Terminate($e);
			}
			else
			{
				$this->FaultActivity($activity->parent, $e, $arEventParameters);
			}
		}
		else
		{
			$activity->setStatus(CBPActivityExecutionStatus::Faulting);
			$this->addItemToQueue(array($activity, CBPActivityExecutorOperationType::HandleFault, $e));
		}
	}

	/************************  ACTIVITIES QUEUE  ***********************************************/

	private function addItemToQueue($item)
	{
		$this->activitiesQueue[] = $item;
	}

	protected function runQueue()
	{
		$canRun = $this->runStep();

		while ($canRun)
		{
			$canRun = $this->runStep();
		}
	}

	protected function runStep(): bool
	{
		if (empty($this->activitiesQueue))
		{
			$this->ProcessQueuedEvents();
		}

		$item = array_shift($this->activitiesQueue);
		if ($item === null)
		{
			return false;
		}

		try
		{
			$this->runQueuedItem($item[0], $item[1], (count($item) > 2 ? $item[2] : null));
		}
		catch (Exception $e)
		{
			$this->faultActivity($item[0], $e);

			if ($this->getWorkflowStatus() === CBPWorkflowStatus::Terminated)
			{
				return false;
			}
		}

		return true;
	}

	/**
	 * @throws Exception
	 */
	private function runQueuedItem(CBPActivity $activity, $activityOperation, Exception $exception = null): void
	{
		match ($activityOperation)
		{
			CBPActivityExecutorOperationType::Execute => $this->runExecuteActivityOperation($activity),
			CBPActivityExecutorOperationType::Cancel => $this->runCancelActivityOperation($activity),
			CBPActivityExecutorOperationType::HandleFault => $this->runHandleFaultActivityOperation($activity, $exception),
		};
	}

	/**
	 * @param CBPActivity $activity
	 * @return void
	 * @throws Exception
	 */
	private function runExecuteActivityOperation(CBPActivity $activity): void
	{
		if ($activity->executionStatus !== CBPActivityExecutionStatus::Executing)
		{
			return;
		}

		$newStatus = CBPActivityExecutionStatus::Closed;
		if ($activity->isActivated())
		{
			/** @var CBPTrackingService $trackingService */
			$trackingService = $this->getService('TrackingService');
			$trackingService->write(
				$this->getInstanceId(),
				CBPTrackingType::ExecuteActivity,
				$activity->getName(),
				$activity->executionStatus,
				$activity->executionResult,
				$activity->getTitle(),
				''
			);
			$newStatus = $activity->execute();
		}

		if ($newStatus === CBPActivityExecutionStatus::Closed)
		{
			$this->closeActivity($activity);
		}
		elseif ($newStatus !== CBPActivityExecutionStatus::Executing)
		{
			throw new Exception('InvalidExecutionStatus');
		}
	}

	/**
	 * @param CBPActivity $activity
	 * @return void
	 * @throws Exception
	 */
	private function runCancelActivityOperation(CBPActivity $activity): void
	{
		if ($activity->executionStatus !== CBPActivityExecutionStatus::Canceling)
		{
			return;
		}

		/** @var CBPTrackingService $trackingService */
		$trackingService = $this->getService("TrackingService");
		$trackingService->write(
			$this->getInstanceId(),
			CBPTrackingType::CancelActivity,
			$activity->getName(),
			$activity->executionStatus,
			$activity->executionResult,
			$activity->getTitle()
		);

		$newStatus = $activity->cancel();

		if ($newStatus === CBPActivityExecutionStatus::Closed)
		{
			$this->closeActivity($activity);
		}
		elseif ($newStatus !== CBPActivityExecutionStatus::Canceling)
		{
			throw new Exception("InvalidExecutionStatus");
		}
	}

	/**
	 * @param CBPActivity $activity
	 * @param Exception|null $exception
	 * @return void
	 * @throws Exception
	 */
	private function runHandleFaultActivityOperation(CBPActivity $activity, ?Exception $exception): void
	{
		if ($activity->executionStatus !== CBPActivityExecutionStatus::Faulting)
		{
			return;
		}

		/** @var CBPTrackingService $trackingService */
		$trackingService = $this->getService("TrackingService");
		$trackingService->write(
			$this->getInstanceId(),
			CBPTrackingType::FaultActivity,
			$activity->getName(),
			$activity->executionStatus,
			$activity->executionResult,
			$activity->getTitle(),
			($exception ? ($exception->getCode() ? "[" . $exception->getCode() . "] " : '') . $exception->getMessage() : "")
		);

		$newStatus = $activity->handleFault($exception);

		if ($newStatus === CBPActivityExecutionStatus::Closed)
		{
			$this->closeActivity($activity);
		}
		elseif ($newStatus !== CBPActivityExecutionStatus::Faulting)
		{
			throw new Exception("InvalidExecutionStatus");
		}
	}

	public function terminate(Exception $e = null, $stateTitle = '')
	{
		CBPTaskService::deleteByWorkflow($this->getInstanceId(), \CBPTaskStatus::Running);

		$this->setWorkflowStatus(CBPWorkflowStatus::Terminated);

		$this->persister->SaveWorkflow($this->rootActivity, true);

		/** @var CBPStateService $stateService */
		$stateService = $this->GetService("StateService");
		$stateService->SetState(
			$this->instanceId,
			[
				"STATE" => "Terminated",
				"TITLE" => $stateTitle ?: GetMessage("BPCGWF_TERMINATED_MSGVER_1"),
				"PARAMETERS" => [],
			],
			false
		);

		if ($e)
		{
			/** @var CBPTrackingService $trackingService */
			$trackingService = $this->getService("TrackingService");
			$trackingService->write(
				$this->instanceId,
				CBPTrackingType::FaultActivity,
				"none",
				CBPActivityExecutionStatus::Faulting,
				CBPActivityExecutionResult::Faulted,
				GetMessage('BPCGWF_EXCEPTION_TITLE'),
				($e->getCode() ? "[" . $e->getCode() . "] " : '') . $e->getMessage()
			);
		}
	}

	/**
	 * @param CBPActivity $activity
	 * @throws CBPArgumentNullException
	 * @throws Exception
	 */
	public function finalizeActivity(CBPActivity $activity)
	{
		$activity->finalize();
	}

	/************************  EVENTS QUEUE  ********************************************************/

	private function addEventToQueue($eventName, $arEventParameters = array())
	{
		$this->eventsQueue[] = [$eventName, $arEventParameters];
	}

	private function processQueuedEvents()
	{
		while (true)
		{
			$event = array_shift($this->eventsQueue);
			if ($event === null)
			{
				return;
			}

			[$eventName, $eventParameters] = $event;

			$this->processQueuedEvent($eventName, $eventParameters);
		}
	}

	private function processQueuedEvent($eventName, $eventParameters = [])
	{
		if (!array_key_exists($eventName, $this->rootActivity->arEventsMap))
		{
			return;
		}

		foreach ($this->rootActivity->arEventsMap[$eventName] as $eventHandler)
		{
			if (!empty($eventParameters['DebugEvent']) && $eventHandler instanceof IBPActivityDebugEventListener)
			{
				$eventHandler->onDebugEvent($eventParameters);

				continue;
			}

			if ($eventHandler instanceof IBPActivityExternalEventListener)
			{
				$eventHandler->onExternalEvent($eventParameters);
			}
		}
	}

	private function syncStatus(int $status): void
	{
		if ($status < CBPWorkflowStatus::Completed) // skip Created and Running
		{
			return;
		}

		WorkflowUserTable::syncOnWorkflowUpdated($this, $status);
	}

	/**
	* Add new event handler to the specified event.
	*
	* @param mixed $eventName - Event name.
	* @param IBPActivityExternalEventListener $eventHandler - Event handler.
	*/
	public function addEventHandler($eventName, IBPActivityExternalEventListener $eventHandler)
	{
		if (!is_array($this->rootActivity->arEventsMap))
		{
			$this->rootActivity->arEventsMap = [];
		}

		if (!array_key_exists($eventName, $this->rootActivity->arEventsMap))
		{
			$this->rootActivity->arEventsMap[$eventName] = [];
		}

		$this->rootActivity->arEventsMap[$eventName][] = $eventHandler;
	}

	public function getEventsMap(): array
	{
		return is_array($this->rootActivity->arEventsMap) ? $this->rootActivity->arEventsMap : [];
	}

	/**
	* Remove the event handler from the specified event.
	*
	* @param mixed $eventName - Event name.
	* @param IBPActivityExternalEventListener $eventHandler - Event handler.
	*/
	public function removeEventHandler($eventName, IBPActivityExternalEventListener $eventHandler)
	{
		if (!is_array($this->rootActivity->arEventsMap))
		{
			$this->rootActivity->arEventsMap = [];
		}

		if (!array_key_exists($eventName, $this->rootActivity->arEventsMap))
		{
			$this->rootActivity->arEventsMap[$eventName] = [];
		}

		$idx = array_search($eventHandler, $this->rootActivity->arEventsMap[$eventName], true);
		if ($idx !== false)
		{
			unset($this->rootActivity->arEventsMap[$eventName][$idx]);
		}

		if (count($this->rootActivity->arEventsMap[$eventName]) <= 0)
		{
			unset($this->rootActivity->arEventsMap[$eventName]);
		}
	}

	public function isDebug(): bool
	{
		return false;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit