PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.34.2
MailPoet – Newsletters, Email Marketing, and Automation v5.34.2
5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / Cron / Workers / InactiveSubscribersMaintenance.php
mailpoet / lib / Cron / Workers Last commit date
Automations 1 year ago KeyCheck 1 year ago SendingQueue 6 days ago StatsNotifications 2 months ago AuthorizedSendingEmailsCheck.php 3 years ago BackfillEngagementData.php 1 year ago Bounce.php 6 days ago BounceTaskSubscribersCleanup.php 1 month ago BulkConfirmationEmailResend.php 2 months ago ExportFilesCleanup.php 2 months ago InactiveSubscribersMaintenance.php 2 weeks ago LogCleanup.php 10 months ago Mixpanel.php 9 months ago NewsletterTemplateThumbnails.php 1 year ago ReEngagementEmailsScheduler.php 1 year ago Scheduler.php 2 months ago SendingQueueBodyCleanup.php 2 months ago SendingTaskSubscribersCleanup.php 2 months ago SimpleWorker.php 1 year ago StatisticsExport.php 2 months ago SubscriberLimitNotificationWorker.php 2 months ago SubscriberLinkTokens.php 1 year ago SubscribersCountCacheRecalculation.php 2 weeks ago SubscribersEngagementScore.php 4 days ago SubscribersLastEngagement.php 2 months ago SubscribersSegmentsCountSync.php 2 weeks ago SubscribersStatsReport.php 1 year ago Tracks.php 9 months ago UnconfirmedSubscribersCleanup.php 2 months ago UnsubscribeTokens.php 1 month ago WooCommercePastOrders.php 1 year ago WooCommerceSync.php 3 years ago WorkersFactory.php 2 weeks ago index.php 3 years ago
InactiveSubscribersMaintenance.php
149 lines
1 <?php declare(strict_types = 1);
2
3 namespace MailPoet\Cron\Workers;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Entities\ScheduledTaskEntity;
9 use MailPoet\Settings\SettingsController;
10 use MailPoet\Settings\TrackingConfig;
11 use MailPoet\Subscribers\InactiveSubscribersController;
12 use MailPoet\Subscribers\SubscribersEmailCountsController;
13 use MailPoet\Subscribers\SubscribersRepository;
14 use MailPoetVendor\Carbon\Carbon;
15 use MailPoetVendor\Doctrine\ORM\EntityManager;
16
17 class InactiveSubscribersMaintenance extends SimpleWorker {
18 const TASK_TYPE = 'inactive_subscribers_maintenance';
19 const BATCH_SIZE = 1000;
20 const SUPPORT_MULTIPLE_INSTANCES = false;
21 const LAST_EMAIL_COUNT_AT_SETTING = 'inactive_subscribers_maintenance_last_email_count_at';
22
23 /** @var SubscribersEmailCountsController */
24 private $subscribersEmailCountsController;
25
26 /** @var InactiveSubscribersController */
27 private $inactiveSubscribersController;
28
29 /** @var SubscribersRepository */
30 private $subscribersRepository;
31
32 /** @var SettingsController */
33 private $settings;
34
35 /** @var TrackingConfig */
36 private $trackingConfig;
37
38 /** @var EntityManager */
39 private $entityManager;
40
41 public function __construct(
42 SubscribersEmailCountsController $subscribersEmailCountsController,
43 InactiveSubscribersController $inactiveSubscribersController,
44 SubscribersRepository $subscribersRepository,
45 SettingsController $settings,
46 TrackingConfig $trackingConfig,
47 EntityManager $entityManager
48 ) {
49 $this->subscribersEmailCountsController = $subscribersEmailCountsController;
50 $this->inactiveSubscribersController = $inactiveSubscribersController;
51 $this->subscribersRepository = $subscribersRepository;
52 $this->settings = $settings;
53 $this->trackingConfig = $trackingConfig;
54 $this->entityManager = $entityManager;
55 parent::__construct();
56 }
57
58 public function processTaskStrategy(ScheduledTaskEntity $task, $timer) {
59 $meta = $task->getMeta();
60 $meta = is_array($meta) ? $meta : [];
61
62 // A partially counted run persists its cursor and frozen cutoff but writes the baseline only
63 // when it finishes. If the feature is disabled before it resumes, the early-return below
64 // completes the task with counting half-done. Drop the baseline in that case so the next
65 // enabled run does a full recount (which resets email_count) instead of incrementing the
66 // already-counted windows again.
67 $hasPartialEmailCountProgress = isset($meta['last_subscriber_id'], $meta['email_count_cutoff']);
68
69 if (!$this->trackingConfig->isEmailTrackingEnabled()) {
70 if ($hasPartialEmailCountProgress) {
71 $this->settings->delete(self::LAST_EMAIL_COUNT_AT_SETTING);
72 }
73 $this->schedule();
74 return true;
75 }
76
77 $daysToInactive = (int)$this->settings->get('deactivate_subscriber_after_inactive_days');
78 if ($daysToInactive === 0) {
79 if ($hasPartialEmailCountProgress) {
80 $this->settings->delete(self::LAST_EMAIL_COUNT_AT_SETTING);
81 }
82 $this->inactiveSubscribersController->reactivateInactiveSubscribers();
83 $this->schedule();
84 return true;
85 }
86
87 // Freeze one cutoff for the whole run (persisted so resumes reuse it) so every window
88 // counts up to the same point and the stored baseline matches it exactly. Otherwise late
89 // cron (scheduled_at != now) or a multi-tick run would re-count or skip a sliver of tasks.
90 $countCutoff = isset($meta['email_count_cutoff']) && is_string($meta['email_count_cutoff'])
91 ? Carbon::parse($meta['email_count_cutoff'])
92 : new Carbon();
93 $meta['email_count_cutoff'] = $countCutoff->format(\DateTimeInterface::ATOM);
94
95 $dateFromLastRun = $this->getDateFromLastRun();
96 $refreshCounts = $dateFromLastRun === null || $this->subscribersEmailCountsController->hasNewSendingTasksSince($dateFromLastRun, $countCutoff);
97
98 $startId = isset($meta['last_subscriber_id']) ? (int)$meta['last_subscriber_id'] : 0;
99
100 while (true) {
101 [$count, $endId] = $this->subscribersRepository->getNextIdWindow($startId, self::BATCH_SIZE);
102 if ($count === 0) {
103 break;
104 }
105
106 // Count and advance the cursor atomically: the count increment is not idempotent, so a
107 // failure must not leave the count applied while the cursor still points at this window.
108 $this->entityManager->wrapInTransaction(function() use ($refreshCounts, $dateFromLastRun, $startId, $endId, $countCutoff, $task, &$meta): void {
109 if ($refreshCounts) {
110 $this->subscribersEmailCountsController->updateSubscribersEmailCounts($dateFromLastRun, $startId, $endId, $countCutoff);
111 }
112 $meta['last_subscriber_id'] = $endId + 1;
113 $task->setMeta($meta);
114 $this->scheduledTasksRepository->persist($task);
115 $this->scheduledTasksRepository->flush();
116 });
117
118 // Marking is idempotent (recomputed from current state each pass), so it stays outside the
119 // transaction -- a marking failure cannot trigger a re-count of an already-counted window.
120 $this->inactiveSubscribersController->markInactiveSubscribers($daysToInactive, $startId, $endId);
121 $this->cronHelper->enforceExecutionLimit($timer);
122 $startId = $endId + 1;
123 }
124
125 // Baseline = the frozen cutoff, matching exactly what was counted, so the next run resumes
126 // from the right point even if this run's cron was late or spanned several ticks.
127 $this->rememberLastEmailCountDate($countCutoff);
128
129 while ($this->inactiveSubscribersController->markActiveSubscribers($daysToInactive, self::BATCH_SIZE) === self::BATCH_SIZE) {
130 $this->cronHelper->enforceExecutionLimit($timer);
131 }
132
133 $this->schedule();
134 return true;
135 }
136
137 private function getDateFromLastRun(): ?\DateTimeInterface {
138 // Baseline is the last run that actually counted, not the last completed task -- runs
139 // while inactivity detection is off complete without counting and must not move it. On
140 // upgrade the migration seeds this setting from the last legacy email-count task.
141 $lastEmailCountAt = $this->settings->get(self::LAST_EMAIL_COUNT_AT_SETTING);
142 return is_string($lastEmailCountAt) && $lastEmailCountAt !== '' ? Carbon::parse($lastEmailCountAt) : null;
143 }
144
145 private function rememberLastEmailCountDate(\DateTimeInterface $countCutoff): void {
146 $this->settings->set(self::LAST_EMAIL_COUNT_AT_SETTING, $countCutoff->format(\DateTimeInterface::ATOM));
147 }
148 }
149