Issues (3627)

Executioner/Dispatcher/LegacyEventDispatcher.php (1 issue)

1
<?php
2
3
/*
4
 * @copyright   2017 Mautic Contributors. All rights reserved
5
 * @author      Mautic, Inc.
6
 *
7
 * @link        https://mautic.org
8
 *
9
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
10
 */
11
12
namespace Mautic\CampaignBundle\Executioner\Dispatcher;
13
14
use Doctrine\Common\Collections\ArrayCollection;
15
use Mautic\CampaignBundle\CampaignEvents;
16
use Mautic\CampaignBundle\Entity\LeadEventLog;
17
use Mautic\CampaignBundle\Event\CampaignDecisionEvent;
18
use Mautic\CampaignBundle\Event\CampaignExecutionEvent;
19
use Mautic\CampaignBundle\Event\DecisionEvent;
20
use Mautic\CampaignBundle\Event\EventArrayTrait;
21
use Mautic\CampaignBundle\Event\ExecutedBatchEvent;
22
use Mautic\CampaignBundle\Event\ExecutedEvent;
23
use Mautic\CampaignBundle\Event\FailedEvent;
24
use Mautic\CampaignBundle\Event\PendingEvent;
25
use Mautic\CampaignBundle\EventCollector\Accessor\Event\AbstractEventAccessor;
26
use Mautic\CampaignBundle\Executioner\Helper\NotificationHelper;
27
use Mautic\CampaignBundle\Executioner\Scheduler\EventScheduler;
28
use Mautic\CoreBundle\Factory\MauticFactory;
29
use Mautic\LeadBundle\Tracker\ContactTracker;
30
use Psr\Log\LoggerInterface;
31
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
32
33
/**
34
 * Class LegacyEventDispatcher.
35
 *
36
 * @deprecated 2.13.0 to be removed in 3.0; BC support for old listeners
37
 */
38
class LegacyEventDispatcher
39
{
40
    use EventArrayTrait;
41
42
    /**
43
     * @var EventDispatcherInterface
44
     */
45
    private $dispatcher;
46
47
    /**
48
     * @var EventScheduler
49
     */
50
    private $scheduler;
51
52
    /**
53
     * @var LoggerInterface
54
     */
55
    private $logger;
56
57
    /**
58
     * @var NotificationHelper
59
     */
60
    private $notificationHelper;
61
62
    /**
63
     * @var MauticFactory
64
     */
65
    private $factory;
66
67
    /**
68
     * @var ContactTracker
69
     */
70
    private $contactTracker;
71
72
    /**
73
     * LegacyEventDispatcher constructor.
74
     */
75
    public function __construct(
76
        EventDispatcherInterface $dispatcher,
77
        EventScheduler $scheduler,
78
        LoggerInterface $logger,
79
        NotificationHelper $notificationHelper,
80
        MauticFactory $factory,
81
        ContactTracker $contactTracker
82
    ) {
83
        $this->dispatcher         = $dispatcher;
84
        $this->scheduler          = $scheduler;
85
        $this->logger             = $logger;
86
        $this->notificationHelper = $notificationHelper;
87
        $this->factory            = $factory;
88
        $this->contactTracker     = $contactTracker;
89
    }
90
91
    /**
92
     * @param $wasBatchProcessed
93
     */
94
    public function dispatchCustomEvent(
95
        AbstractEventAccessor $config,
96
        ArrayCollection $logs,
97
        $wasBatchProcessed,
98
        PendingEvent $pendingEvent
99
    ) {
100
        $settings = $config->getConfig();
101
102
        if (!isset($settings['eventName']) && !isset($settings['callback'])) {
103
            // Bad plugin but only fail if the new event didn't already process the log
104
            if (!$wasBatchProcessed) {
105
                $pendingEvent->failAll('Invalid event configuration');
106
            }
107
108
            return;
109
        }
110
111
        $rescheduleFailures = new ArrayCollection();
112
113
        /** @var LeadEventLog $log */
114
        foreach ($logs as $log) {
115
            $this->contactTracker->setSystemContact($log->getLead());
116
117
            if (isset($settings['eventName'])) {
118
                $result = $this->dispatchEventName($settings['eventName'], $settings, $log);
119
            } else {
120
                if (!is_callable($settings['callback'])) {
121
                    // No use to keep trying for the other logs as it won't ever work
122
                    break;
123
                }
124
125
                $result = $this->dispatchCallback($settings, $log);
126
            }
127
128
            // If the new batch event was handled, the $log was already processed so only process legacy logs if false
129
            if (!$wasBatchProcessed) {
130
                $this->dispatchExecutionEvent($config, $log, $result);
131
132
                if (!is_bool($result)) {
133
                    $log->appendToMetadata($result);
134
                }
135
136
                // Dispatch new events for legacy processed logs
137
                if ($this->isFailed($result)) {
138
                    $this->processFailedLog($result, $log, $pendingEvent);
139
140
                    $rescheduleFailures->set($log->getId(), $log);
141
142
                    $this->dispatchFailedEvent($config, $log);
143
144
                    continue;
145
                }
146
147
                if (is_array($result) && !empty($result['failed']) && isset($result['reason'])) {
148
                    $pendingEvent->passWithError($log, (string) $result['reason']);
149
                } else {
150
                    $pendingEvent->pass($log);
151
                }
152
153
                $this->dispatchExecutedEvent($config, $log);
154
            }
155
        }
156
157
        if ($rescheduleFailures->count()) {
158
            $this->scheduler->rescheduleFailures($rescheduleFailures);
159
        }
160
161
        $this->contactTracker->setSystemContact(null);
162
    }
163
164
    /**
165
     * Execute the new ON_EVENT_FAILED and ON_EVENT_EXECUTED events for logs processed by BC code.
166
     */
167
    public function dispatchExecutionEvents(AbstractEventAccessor $config, ArrayCollection $success, ArrayCollection $failures)
168
    {
169
        foreach ($success as $log) {
170
            $this->dispatchExecutionEvent($config, $log, true);
171
        }
172
173
        foreach ($failures as $log) {
174
            $this->dispatchExecutionEvent($config, $log, false);
175
        }
176
    }
177
178
    public function dispatchDecisionEvent(DecisionEvent $decisionEvent)
179
    {
180
        if ($this->dispatcher->hasListeners(CampaignEvents::ON_EVENT_DECISION_TRIGGER)) {
181
            $log   = $decisionEvent->getLog();
182
            $event = $log->getEvent();
183
184
            $legacyDecisionEvent = $this->dispatcher->dispatch(
185
                CampaignEvents::ON_EVENT_DECISION_TRIGGER,
186
                new CampaignDecisionEvent(
187
                    $log->getLead(),
188
                    $event->getType(),
189
                    $decisionEvent->getEventConfig()->getConfig(),
190
                    $this->getLegacyEventsArray($log),
191
                    $this->getLegacyEventsConfigArray($event, $decisionEvent->getEventConfig()),
192
                    0 === $event->getOrder(),
193
                    [$log]
194
                )
195
            );
196
197
            if ($legacyDecisionEvent->wasDecisionTriggered()) {
198
                $decisionEvent->setAsApplicable();
199
            }
200
        }
201
    }
202
203
    /**
204
     * @param $eventName
205
     *
206
     * @return bool
207
     */
208
    private function dispatchEventName($eventName, array $settings, LeadEventLog $log)
209
    {
210
        @trigger_error('eventName is deprecated. Convert to using batchEventName.', E_USER_DEPRECATED);
211
212
        $campaignEvent = new CampaignExecutionEvent(
213
            [
214
                'eventSettings'   => $settings,
215
                'eventDetails'    => null,
216
                'event'           => $log->getEvent(),
217
                'lead'            => $log->getLead(),
218
                'systemTriggered' => $log->getSystemTriggered(),
219
            ],
220
            null,
221
            $log
222
        );
223
224
        $this->dispatcher->dispatch($eventName, $campaignEvent);
225
226
        if ($channel = $campaignEvent->getChannel()) {
227
            $log->setChannel($channel)
228
                ->setChannelId($campaignEvent->getChannelId());
229
        }
230
231
        return $campaignEvent->getResult();
232
    }
233
234
    /**
235
     * @return mixed
236
     */
237
    private function dispatchCallback(array $settings, LeadEventLog $log)
238
    {
239
        @trigger_error('callback is deprecated. Convert to using batchEventName.', E_USER_DEPRECATED);
240
241
        $eventArray = $this->getEventArray($log->getEvent());
242
        $args       = [
243
            'eventSettings'   => $settings,
244
            'eventDetails'    => null, // @todo fix when procesing decisions,
245
            'event'           => $eventArray,
246
            'lead'            => $log->getLead(),
247
            'factory'         => $this->factory,
248
            'systemTriggered' => $log->getSystemTriggered(),
249
            'config'          => $eventArray['properties'],
250
        ];
251
252
        try {
253
            if (is_array($settings['callback'])) {
254
                $reflection = new \ReflectionMethod($settings['callback'][0], $settings['callback'][1]);
255
            } elseif (false !== strpos($settings['callback'], '::')) {
256
                $parts      = explode('::', $settings['callback']);
257
                $reflection = new \ReflectionMethod($parts[0], $parts[1]);
258
            } else {
259
                $reflection = new \ReflectionMethod(null, $settings['callback']);
260
            }
261
262
            $pass = [];
263
            foreach ($reflection->getParameters() as $param) {
264
                if (isset($args[$param->getName()])) {
265
                    $pass[] = $args[$param->getName()];
266
                } else {
267
                    $pass[] = null;
268
                }
269
            }
270
271
            return $reflection->invokeArgs($this, $pass);
272
        } catch (\ReflectionException $exception) {
273
            return false;
274
        }
275
    }
276
277
    /**
278
     * @param $result
279
     */
280
    private function dispatchExecutionEvent(AbstractEventAccessor $config, LeadEventLog $log, $result)
281
    {
282
        $eventArray = $this->getEventArray($log->getEvent());
283
284
        $this->dispatcher->dispatch(
285
            CampaignEvents::ON_EVENT_EXECUTION,
286
            new CampaignExecutionEvent(
287
                [
288
                    'eventSettings'   => $config->getConfig(),
289
                    'eventDetails'    => null, // @todo fix when procesing decisions,
290
                    'event'           => $eventArray,
291
                    'lead'            => $log->getLead(),
292
                    'systemTriggered' => $log->getSystemTriggered(),
293
                    'config'          => $eventArray['properties'],
294
                ],
295
                $result,
296
                $log
297
            )
298
        );
299
    }
300
301
    private function dispatchExecutedEvent(AbstractEventAccessor $config, LeadEventLog $log)
302
    {
303
        $this->dispatcher->dispatch(
304
            CampaignEvents::ON_EVENT_EXECUTED,
305
            new ExecutedEvent($config, $log)
306
        );
307
308
        $collection = new ArrayCollection();
309
        $collection->set($log->getId(), $log);
310
        $this->dispatcher->dispatch(
311
            CampaignEvents::ON_EVENT_EXECUTED_BATCH,
312
            new ExecutedBatchEvent($config, $log->getEvent(), $collection)
313
        );
314
    }
315
316
    private function dispatchFailedEvent(AbstractEventAccessor $config, LeadEventLog $log)
317
    {
318
        $this->dispatcher->dispatch(
319
            CampaignEvents::ON_EVENT_FAILED,
320
            new FailedEvent($config, $log)
321
        );
322
323
        $this->notificationHelper->notifyOfFailure($log->getLead(), $log->getEvent());
324
    }
325
326
    /**
327
     * @param $result
328
     *
329
     * @return bool
330
     */
331
    private function isFailed($result)
332
    {
333
        return
334
            false === $result
335
            || (is_array($result) && isset($result['result']) && false === $result['result']);
336
    }
337
338
    /**
339
     * @param $result
340
     */
341
    private function processFailedLog($result, LeadEventLog $log, PendingEvent $pendingEvent)
0 ignored issues
show
The parameter $result is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

341
    private function processFailedLog(/** @scrutinizer ignore-unused */ $result, LeadEventLog $log, PendingEvent $pendingEvent)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
342
    {
343
        $this->logger->debug(
344
            'CAMPAIGN: '.ucfirst($log->getEvent()->getEventType()).' ID# '.$log->getEvent()->getId().' for contact ID# '.$log->getLead()->getId()
345
        );
346
347
        $metadata = $log->getMetadata();
348
349
        $reason = null;
350
        if (isset($metadata['errors'])) {
351
            $reason = (is_array($metadata['errors'])) ? implode('<br />', $metadata['errors']) : $metadata['errors'];
352
        } elseif (isset($metadata['reason'])) {
353
            $reason = $metadata['reason'];
354
        }
355
356
        $pendingEvent->fail($log, $reason);
357
    }
358
}
359