forked from eMAGTechLabs/SynchronizedBundle
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDecorator.php
More file actions
118 lines (100 loc) · 3.48 KB
/
Decorator.php
File metadata and controls
118 lines (100 loc) · 3.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<?php
namespace Sms\SynchronizedBundle;
use Sms\SynchronizedBundle\Event\LockEvent;
use Sms\SynchronizedBundle\Exception\CannotAquireLockException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class Decorator
{
/**
*
* @var Lock[]
*/
private $locks = array();
private $lockPrefix;
private $originalService;
private $originalServiceClass;
/**
*
* @var EventDispatcherInterface
*/
private $eventDispatcher;
/**
*
* @var \Psr\Log\LoggerInterface;
*/
private $logger;
public function __construct($originalService, $lockPrefix)
{
$this->originalService = $originalService;
$this->originalServiceClass = get_class($originalService);
$this->lockPrefix = $lockPrefix;
}
public function addLock(Lock $lock)
{
$this->locks[] = $lock;
}
public function __call($name, $arguments)
{
foreach ($this->locks as $lock) {
if ($name === $lock->getMethod()) {
$this->executeCriticalSection($lock, $name, $arguments);
}
}
return call_user_func_array(array($this->originalService, $name), $arguments);
}
private function executeCriticalSection(Lock $lock, $method, $arguments)
{
$lockName = $this->getLockName($lock, $arguments);
$this->dispatchAndLogEvent(LockEvent::EVENT_BEFORE_GET_LOCK, $lock, $lockName);
if (!$lock->getDriver()->getLock($lockName)) {
$this->dispatchAndLogEvent(LockEvent::EVENT_FAILURE_GET_LOCK, $lock, $lockName);
throw new CannotAquireLockException($lockName);
}
$this->dispatchAndLogEvent(LockEvent::EVENT_SUCCESS_GET_LOCK, $lock, $lockName);
$return = call_user_func_array(array($this->originalService, $method), $arguments);
$this->dispatchAndLogEvent(LockEvent::EVENT_BEFORE_RELEASE_LOCK, $lock, $lockName);
$lock->getDriver()->releaseLock($lockName);
$this->dispatchAndLogEvent(LockEvent::EVENT_AFTER_RELEASE_LOCK, $lock, $lockName);
return $return;
}
private function dispatchAndLogEvent($name, Lock $lock, $lockName)
{
if ($this->eventDispatcher) {
$this->eventDispatcher->dispatch($name, new LockEvent($lock, $this->originalService));
}
if ($this->logger) {
$context = array(
'service' => $this->originalServiceClass,
'method' => $lock->getMethod(),
'lock' => $lockName
);
$this->logger->log(\Psr\Log\LogLevel::INFO, $name, $context);
}
}
private function getLockName(Lock $lock, $arguments)
{
$lockName = $this->lockPrefix . '_' . $lock->getMethod();
if (array_key_exists($lock->getArgumentIndex(), $arguments)) {
$argumentHash = $this->getHashFromValue($arguments[$lock->getArgumentIndex()]);
$lockName .= sprintf('_%s_%s', $lock->getArgumentIndex(), $argumentHash);
}
return $lockName;
}
private function getHashFromValue($value)
{
if (is_array($value) || is_object($value)) {
return md5(serialize($value));
}
return $value;
}
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
return $this;
}
public function setLogger(\Psr\Log\LoggerInterface $logger)
{
$this->logger = $logger;
return $this;
}
}