File: /www/wwwroot/www.cytocare.cn/wp-content/plugins/shopmagic-for-woocommerce/src/Workflow/Workflow.php
<?php
declare( strict_types=1 );
namespace WPDesk\ShopMagic\Workflow;
use ShopMagicVendor\Psr\Log\LoggerInterface;
use WPDesk\ShopMagic\Customer\Customer;
use WPDesk\ShopMagic\Workflow\Action\Action;
use WPDesk\ShopMagic\Workflow\ActionExecution\ExecutionCreator\ExecutionCreator;
use WPDesk\ShopMagic\Workflow\Automation\Automation;
use WPDesk\ShopMagic\Workflow\Event\DataLayer;
use WPDesk\ShopMagic\Workflow\Outcome\Meta\OutcomeMeta;
use WPDesk\ShopMagic\Workflow\Outcome\Outcome;
use WPDesk\ShopMagic\Workflow\Outcome\OutcomeManager;
use WPDesk\ShopMagic\Workflow\Outcome\OutcomeSaver;
use WPDesk\ShopMagic\Workflow\Validator\WorkflowValidator;
class Workflow implements Runner {
/** @var Automation|null */
private $automation;
/** @var ExecutionCreator */
private $execution_creator;
/** @var WorkflowValidator */
private $validator;
/** @var OutcomeSaver */
private $outcome_saver;
/** @var DataLayer */
private $data_layer;
/** @var LoggerInterface */
private $logger;
/** @var OutcomeManager */
private $outcome_manager;
public function __construct(
WorkflowValidator $validator,
ExecutionCreator $execution_creator,
OutcomeSaver $outcome_saver,
OutcomeManager $outcome_manager,
LoggerInterface $logger
) {
$this->validator = $validator;
$this->execution_creator = $execution_creator;
$this->outcome_saver = $outcome_saver;
$this->outcome_manager = $outcome_manager;
$this->logger = $logger;
}
public function run( DataLayer $data_layer ): void {
try {
$this->data_layer = $data_layer;
$this->do_run( $data_layer );
} catch ( \Throwable $e ) {
$this->logger->alert(
'Automation execution prevented by critical error. Reason: {error}',
[ 'error' => $e->getMessage() ]
);
$this->save_failure( $data_layer, $e );
}
}
private function do_run( DataLayer $data_layer ): void {
$this->setup( $data_layer );
$this->logger->debug( 'Validating automation with set of validators.' );
if ( $this->validator->valid( $data_layer ) ) {
$this->logger->debug( 'Validation succeeded. Proceeding to dispatch actions.' );
$this->execute_actions();
} else {
$this->logger->debug( 'Automation validation failed. Cleaning up and shutting down ShopMagic.' );
}
$this->cleanup();
}
private function setup( DataLayer $data_layer ): void {
$this->validator->set_provided_data( $data_layer );
do_action( 'shopmagic/core/automation/setup', $this );
}
private function execute_actions(): void {
$this->logger->debug( sprintf( 'Executing all actions for automation %d.', $this->automation->get_id() ) );
foreach ( array_values( $this->automation->get_actions() ) as $index => $action ) {
if ( $this->should_execute_action( $action ) ) {
$this->logger->debug( sprintf( 'Preparing action `%s` for dispatch.', get_class( $action ) ) );
$this->delegate_for_execution( $action, $index );
$this->logger->debug( sprintf( 'Action `%s` dispatched.', get_class( $action ) ) );
} else {
$this->logger->info( sprintf( 'Action `%s` skipped.', get_class( $action ) ) );
}
}
$this->logger->debug(
'Finished executing all actions for automation #{id}.',
[ 'id' => $this->automation->get_id() ]
);
}
private function should_execute_action( Action $action ): bool {
return apply_filters(
'shopmagic/core/automation/should_execute_action',
true,
$this->automation,
$this->automation->get_event(),
$action
);
}
private function delegate_for_execution( Action $action, int $index ): void {
$executor = $this->execution_creator->create_executor(
$this->automation,
$this->automation->get_event(),
$action
);
$unique_id = $this->outcome_saver->create_outcome( $this->automation, $this->data_layer, $index );
$this->logger->debug(
sprintf(
'Delegating action `%s` to executor `%s`.',
get_class( $action ),
get_class( $executor )
)
);
$executor->execute(
$this->automation,
$this->automation->get_event(),
$action,
$index,
(string) $unique_id
);
}
private function cleanup(): void {
do_action( 'shopmagic/core/automation/cleanup', $this );
}
public function initialize(): void {
$event = $this->automation->get_event();
$event->set_runner( $this );
$event->set_data_layer( new DataLayer( [ Automation::class => $this->automation ] ) );
$event->initialize();
}
public function set_automation( Automation $automation ): void {
$this->automation = $automation;
}
/**
* FIXME: This method shouldn't really be here. Workflow doesn't have to know anything about
* an automation, as it fully operates on DataLayer provided by event. Workflow's
* reponsibility should be to first validate if passed data has the automation, then to
* execute, but there possibly may be cases when Automation is not required as such by
* Workflow, so it should be possible to execute Workflow without it.
*
* @return Automation
*/
public function get_automation(): Automation {
return $this->automation;
}
/**
* We want to save any critical failure which occurred even before executing actions.
* At the moment this save method is kind of workaround as manager is not able to save
* outcome and note at once.
*/
public function save_failure( DataLayer $data_layer, \Throwable $e ): void {
$outcome = new Outcome();
$outcome->set_execution_id( uniqid( 'execute_', true ) );
$outcome->set_automation_id( $this->automation->get_id() );
$outcome->set_automation_name( $this->automation->get_name() );
$outcome->set_action_name( 'Unknown' );
$outcome->set_action_index( 0 );
if ( $data_layer->has( Customer::class ) ) {
$outcome->set_customer( $data_layer->get( Customer::class ) );
}
$outcome->set_finished( true );
$outcome->set_success( false );
$this->outcome_manager->save( $outcome );
$note = new OutcomeMeta(
sprintf( 'Automation execution prevented by critical error. Reason: %s', $e->getMessage() ),
[
'Error Code' => $e->getCode(),
'Trace' => $e->getTraceAsString(),
]
);
$note->set_execution_id( (string) $outcome->get_id() );
$outcome->set_note( $note );
$this->outcome_manager->save( $outcome );
}
}