File manager - Edit - /home/verseaumee/maze/wp-content/plugins/packlink-pro-shipping/Components/Services/class-wordpress-task-executor.php
Back
<?php namespace Packlink\WooCommerce\Components\Services; use Logeecom\Infrastructure\Logger\Logger; use Logeecom\Infrastructure\ServiceRegister; use Logeecom\Infrastructure\TaskExecutor\Interfaces\TaskExecutorInterface; use Packlink\BusinessLogic\Scheduler\DTO\ScheduleConfig; use Packlink\BusinessLogic\Scheduler\Interfaces\SchedulerInterface; use Packlink\BusinessLogic\Tasks\Interfaces\BusinessTask; use Packlink\BusinessLogic\Tasks\Interfaces\TaskMetadataProviderInterface; use Packlink\BusinessLogic\Tasks\TaskExecutionConfig; class WordPress_Task_Executor implements TaskExecutorInterface { /** * Action Scheduler hook name for Packlink tasks. */ const HOOK_NAME = 'packlink_execute_task'; /** * Internal scheduler hook (debug/compat). */ const SCHEDULER_HOOK_NAME = 'packlink_scheduler'; /** * Task payload keys. */ const ARG_TASK_DATA = 'task_data'; const ARG_TASK_CLASS = 'task_class'; const ARG_CONTEXT = 'context'; /** * Priority bounds for Action Scheduler (0..10). */ const PRIORITY_MIN = 0; const PRIORITY_MAX = 10; /** * Delay bounds (seconds). */ const MAX_DELAY_SECONDS = 604800; // 7 days. /** * Error messages. */ const ERR_AS_NOT_AVAILABLE = 'Action Scheduler not available. Please install WooCommerce.'; const ERR_INVALID_TASK_PAYLOAD = 'Invalid task payload.'; const ERR_INVALID_TASK_CLASS = 'Invalid task class.'; const ERR_MISSING_EXECUTION_CONFIG = 'Execution configuration not available.'; const ERR_SCHEDULE_FAILED = 'Task scheduling failed.'; private $metadata_provider; private $scheduler; public function __construct() { $this->metadata_provider = ServiceRegister::getService( TaskMetadataProviderInterface::CLASS_NAME ); $this->scheduler = ServiceRegister::getService( SchedulerInterface::CLASS_NAME ); } /** * Enqueue business task via Action Scheduler. * * Task will be executed asynchronously via wp-cron or real cron. * * Uses TaskMetadataProvider to get execution configuration. * * @param BusinessTask $businessTask Business task (e.g., SendDraftBusinessTask). * * @return void */ public function enqueue( BusinessTask $businessTask ) { // Check if Action Scheduler is available if ( ! function_exists( 'as_enqueue_async_action' ) ) { $this->logAndThrow( self::ERR_AS_NOT_AVAILABLE ); } // Get execution configuration from metadata provider (BusinessLogic layer) $executionConfig = $this->getExecutionConfig( $businessTask ); $payload = $this->buildPayload( $businessTask, $executionConfig ); // Enqueue action with configuration $action_id = as_enqueue_async_action( self::HOOK_NAME, [ $payload ], (string) $executionConfig->getQueueName(), false, $this->convertPriority( $executionConfig->getPriority() ) ); if ( empty( $action_id ) ) { $this->logAndThrow( self::ERR_SCHEDULE_FAILED ); } } /** * Schedule delayed business task via Action Scheduler. * * Task will be executed after specified delay. * * Uses TaskMetadataProvider to get execution configuration. * * @param BusinessTask $businessTask Business task. * @param int $delaySeconds Delay in seconds before execution. * * @return void */ public function scheduleDelayed( BusinessTask $businessTask, int $delaySeconds ) { $delaySeconds = max( 0, (int) $delaySeconds ); if ( $delaySeconds > self::MAX_DELAY_SECONDS ) { $delaySeconds = self::MAX_DELAY_SECONDS; } // Prefer SchedulerInterface when available so scheduling is centralized. if ( $this->scheduler instanceof SchedulerInterface ) { $timestamp = time() + $delaySeconds; $this->scheduler->scheduleHourly( $businessTask, new ScheduleConfig( (int) date( 'N', $timestamp ), (int) date( 'H', $timestamp ), (int) date( 'i', $timestamp ), false ) ); return; } // Check if Action Scheduler is available if ( ! function_exists( 'as_schedule_single_action' ) ) { $this->logAndThrow( self::ERR_AS_NOT_AVAILABLE ); } // Get execution configuration from metadata provider (BusinessLogic layer) $executionConfig = $this->getExecutionConfig( $businessTask ); if ( ! $executionConfig instanceof TaskExecutionConfig ) { $this->logAndThrow( self::ERR_MISSING_EXECUTION_CONFIG ); } $payload = $this->buildPayload( $businessTask, $executionConfig ); // Schedule action with configuration $action_id = as_schedule_single_action( time() + $delaySeconds, self::HOOK_NAME, [ $payload ], (string) $executionConfig->getQueueName(), false, $this->convertPriority( $executionConfig->getPriority() ) ); if ( empty( $action_id ) ) { $this->logAndThrow( self::ERR_SCHEDULE_FAILED ); } } /** * Get execution configuration for business task. * * Uses TaskMetadataProvider service (BusinessLogic layer) to get configuration. * * @param BusinessTask $businessTask Business task. * * @return TaskExecutionConfig Execution configuration. */ private function getExecutionConfig( BusinessTask $businessTask ) { if ( ! $this->metadata_provider instanceof TaskMetadataProviderInterface ) { $this->logAndThrow( self::ERR_MISSING_EXECUTION_CONFIG ); } return $this->metadata_provider->getExecutionConfig( $businessTask ); } /** * Convert priority from infrastructure scale (0-100) to Action Scheduler scale (0-10). * * Infrastructure priorities: * - Priority::LOW = 0-33 * - Priority::NORMAL = 34-66 * - Priority::HIGH = 67-100 * * Action Scheduler priorities: * - 0 = lowest * - 10 = highest * * @param int $priority Priority from TaskExecutionConfig (0-100). * * @return int Priority for Action Scheduler (0-10). */ private function convertPriority( $priority ) { $priority = (int) $priority; $p = (int) round( $priority / 10 ); // Clamp to Action Scheduler allowed range return max( self::PRIORITY_MIN, min( self::PRIORITY_MAX, $p ) ); } /** * @return SchedulerInterface|null */ /** * Register Action Scheduler callback for task execution. * * Call this method during plugin initialization to register the callback. * * @return void */ public function registerExecutionCallback() { add_action( 'init', function () { $executor = ServiceRegister::getService( TaskExecutorInterface::CLASS_NAME ); $executor->registerExecutionCallback(); } ); add_action( self::HOOK_NAME, [ $this, 'executeTaskCallback' ], 10, 1 ); add_action( self::SCHEDULER_HOOK_NAME, [ $this, 'runSc' ], 10, 1 ); } public function runSc( $args ) { return $args['task_class']; } /** * Action Scheduler callback for task execution. * * Deserializes business task and executes it. * Handles yield-based progress tracking. * * @param array $args Task arguments from Action Scheduler. * * @return void * * @throws \Exception If task execution fails. */ public function executeTaskCallback( $args ) { if ( isset( $args[0] ) && is_array( $args[0] ) ) { $args = $args[0]; } if ( ! is_array( $args ) || ! isset( $args[ self::ARG_TASK_CLASS ], $args[ self::ARG_TASK_DATA ] ) ) { $this->logAndThrow( self::ERR_INVALID_TASK_PAYLOAD ); } $taskClass = $args[ self::ARG_TASK_CLASS ]; $taskData = $args[ self::ARG_TASK_DATA ]; $context = $args[ self::ARG_CONTEXT ] ?? ''; if ( ! is_string( $taskClass ) || ! class_exists( $taskClass ) || ! is_callable( [ $taskClass, 'fromArray' ] ) ) { $this->logAndThrow( self::ERR_INVALID_TASK_CLASS ); } // Reconstruct business task /** @var BusinessTask $businessTask */ $businessTask = call_user_func( [ $taskClass, 'fromArray' ], $taskData ); // Execute task try { $result = $businessTask->execute(); } catch ( \Throwable $e ) { Logger::logError( $e->getMessage(), 'Integration', array( 'task_class' => $taskClass, 'context' => $context, ) ); throw $e; } if ( $result instanceof \Generator ) { $this->executeWithProgressTracking( $result ); } // If not generator, task executed normally } /** * @param BusinessTask $businessTask * * @return void * * @throws \Throwable */ public function executeNow( BusinessTask $businessTask ) { try { $result = $businessTask->execute(); if ( $result instanceof \Generator ) { $this->executeWithProgressTracking( $result ); } } catch ( \Throwable $e ) { Logger::logError( $e->getMessage(), 'Integration', [ 'task_class' => get_class($businessTask), ] ); throw $e; } } /** * Execute generator with progress tracking. * * Iterates over generator and handles yielded values: * - null (yield;) → log keep-alive * - int/float (yield 50;) → log progress * - string (yield 'message';) → log message * * Note: Action Scheduler doesn't have reportProgress/reportAlive like QueueItem, * so we just log the progress for debugging purposes. * * @param \Generator $generator Generator returned by business task. * * @return void */ private function executeWithProgressTracking( \Generator $generator ) { foreach ( $generator as $value ) { if ( $value === null ) { // yield; (no value) → keep-alive signal Logger::logDebug( 'Task keep-alive signal', 'Integration' ); } elseif ( is_int( $value ) || is_float( $value ) ) { // yield 50; → report progress 50% Logger::logDebug( sprintf( 'Task progress: %s%%', $value ), 'Integration' ); } elseif ( is_string( $value ) ) { // yield 'Processing...'; → log message Logger::logDebug( sprintf( 'Task message: %s', $value ), 'Integration' ); } } } /** * Build Action Scheduler payload with constraints. * * @param BusinessTask $businessTask * @param TaskExecutionConfig $executionConfig * * @return array */ private function buildPayload( BusinessTask $businessTask, TaskExecutionConfig $executionConfig ) { return array( self::ARG_TASK_DATA => $businessTask->toArray(), self::ARG_TASK_CLASS => get_class( $businessTask ), self::ARG_CONTEXT => (string) $executionConfig->getContext(), ); } /** * Log error and throw exception. * * @param string $message * * @return void */ private function logAndThrow( $message ) { Logger::logError( $message, 'Integration' ); throw new \RuntimeException( $message ); } }
| ver. 1.4 |
Github
|
.
| PHP 8.5.7 | Generation time: 0 |
proxy
|
phpinfo
|
Settings