Issues (35)

lib/Phpfastcache/EventManager.php (1 issue)

Severity
1
<?php
2
3
/**
4
 *
5
 * This file is part of Phpfastcache.
6
 *
7
 * @license MIT License (MIT)
8
 *
9
 * For full copyright and license information, please see the docs/CREDITS.txt and LICENCE files.
10
 *
11
 * @author Georges.L (Geolim4)  <[email protected]>
12
 * @author Contributors  https://github.com/PHPSocialNetwork/phpfastcache/graphs/contributors
13
 */
14
15
declare(strict_types=1);
16
17
namespace Phpfastcache;
18
19
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
20
use Phpfastcache\Event\EventManagerInterface;
21
use Phpfastcache\Exceptions\PhpfastcacheEventManagerException;
22
use Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException;
23
use Phpfastcache\Helper\UninstanciableObjectTrait;
24
25
class EventManager implements EventManagerInterface
26
{
27
    use UninstanciableObjectTrait;
28
29
    public const ON_EVERY_EVENT = '__every';
30
31
    protected static EventManagerInterface $instance;
32
33
    protected bool $isScopedEventManager = false;
34
35
    protected ?ExtendedCacheItemPoolInterface $itemPoolContext = null;
36
37
    /** @var array<string, array<string, callable>> */
38
    protected array $events = [
39
        self::ON_EVERY_EVENT => []
40
    ];
41
42
    /**
43
     * @return EventManagerInterface
44
     */
45
    public static function getInstance(): EventManagerInterface
46
    {
47
        return (self::$instance ?? self::$instance = new static());
48
    }
49
50
    /**
51
     * @param EventManagerInterface $eventManagerInstance
52
     * @return void
53
     */
54
    public static function setInstance(EventManagerInterface $eventManagerInstance): void
55
    {
56
        self::$instance = $eventManagerInstance;
57
    }
58
59
    public function dispatch(string $eventName, ...$args): void
60
    {
61
        /**
62
         * Replace array_key_exists by isset
63
         * due to performance issue on huge
64
         * loop dispatching operations
65
         */
66
        if (isset($this->events[$eventName]) && $eventName !== self::ON_EVERY_EVENT) {
67
            $loopArgs = array_merge($args, [$eventName]);
68
            foreach ($this->events[$eventName] as $event) {
69
                /**
70
                 * @todo V10: BC Break: Put eventName as first parameter (like self::ON_EVERY_EVENT)
71
                 */
72
                $event(...$loopArgs);
73
            }
74
        }
75
        foreach ($this->events[self::ON_EVERY_EVENT] as $event) {
76
            $event($eventName, ...$args);
77
        }
78
    }
79
80
    /**
81
     * @inheritDoc
82
     * @throws PhpfastcacheInvalidArgumentException
83
     * @throws PhpfastcacheEventManagerException
84
     */
85
    public function __call(string $name, array $arguments): void
86
    {
87
        if (\str_starts_with($name, 'on')) {
88
            $name = \substr($name, 2);
89
            if (\is_callable($arguments[0])) {
90
                if (isset($arguments[1]) && \is_string($arguments[0])) {
91
                    $this->events[$name][$arguments[1]] = $arguments[0];
92
                } else {
93
                    $this->events[$name][] = $arguments[0];
94
                }
95
            } else {
96
                throw new PhpfastcacheInvalidArgumentException(\sprintf('Expected Callable, got "%s"', \gettype($arguments[0])));
97
            }
98
        } else {
99
            throw new PhpfastcacheEventManagerException('An event must start with "on" such as "onCacheGetItem"');
100
        }
101
    }
102
103
    /**
104
     * @param callable $callback
105
     * @param string $callbackName
106
     * @throws PhpfastcacheEventManagerException
107
     */
108
    public function onEveryEvents(callable $callback, string $callbackName): void
109
    {
110
        if ($callbackName === '') {
111
            throw new PhpfastcacheEventManagerException('Callbackname cannot be empty');
112
        }
113
        $this->events[self::ON_EVERY_EVENT][$callbackName] = $callback;
114
    }
115
116
117
    /**
118
     * @throws PhpfastcacheEventManagerException
119
     */
120
    public function on(array|string $events, callable $callback): void
121
    {
122
        if (is_string($events)) {
0 ignored issues
show
The condition is_string($events) is always false.
Loading history...
123
            $events = [$events];
124
        }
125
        foreach ($events as $event) {
126
            if (!\preg_match('#^([a-zA-Z])*$#', $event)) {
127
                throw new PhpfastcacheEventManagerException(\sprintf('Invalid event name "%s"', $event));
128
            }
129
130
            $this->{'on' . \ucfirst($event)}($callback);
131
        }
132
    }
133
134
    /**
135
     * @param string $eventName
136
     * @param string $callbackName
137
     * @return bool
138
     */
139
    public function unbindEventCallback(string $eventName, string $callbackName): bool
140
    {
141
        $return = isset($this->events[$eventName][$callbackName]);
142
        unset($this->events[$eventName][$callbackName]);
143
144
        return $return;
145
    }
146
147
    /**
148
     * @return bool
149
     */
150
    public function unbindAllEventCallbacks(): bool
151
    {
152
        $this->events = [
153
            self::ON_EVERY_EVENT => []
154
        ];
155
156
        return true;
157
    }
158
159
    public function __clone(): void
160
    {
161
        $this->isScopedEventManager = true;
162
        $this->unbindAllEventCallbacks();
163
    }
164
165
    /**
166
     * @param ExtendedCacheItemPoolInterface $pool
167
     * @return EventManagerInterface
168
     * @throws PhpfastcacheEventManagerException
169
     */
170
    public function getScopedEventManager(ExtendedCacheItemPoolInterface $pool): EventManagerInterface
171
    {
172
        return (clone $this)->setItemPoolContext($pool);
173
    }
174
175
    /**
176
     * @param ExtendedCacheItemPoolInterface $pool
177
     * @return EventManagerInterface
178
     * @throws PhpfastcacheEventManagerException
179
     */
180
    public function setItemPoolContext(ExtendedCacheItemPoolInterface $pool): EventManagerInterface
181
    {
182
        if (!$this->isScopedEventManager) {
183
            throw new PhpfastcacheEventManagerException('Cannot set itemPool context on unscoped event manager instance.');
184
        }
185
        $this->itemPoolContext = $pool;
186
187
        $this->onEveryEvents(function (string $eventName, ...$args) {
188
            EventManager::getInstance()->dispatch($eventName, ...$args);
189
        }, 'Scoped' . $pool->getDriverName() . spl_object_hash($this));
190
191
        return $this;
192
    }
193
}
194
195
// phpcs:disable
196
\class_alias(EventManager::class, PhpfastcacheEventManager::class); // @phpstan-ignore-line
197
// phpcs:enable
198