Passed
Push — master ( bb4bd6...a24c1e )
by Andreas
33:34
created

midcom_services_cache::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package midcom.services
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
10
use midcom\events\dbaevent;
11
use midgard\portable\storage\connection;
12
use Symfony\Component\Filesystem\Filesystem;
13
14
/**
15
 * This class is the central access point for all registered caching services. Currently
16
 * this includes the NAP, Memcache, Content and PHPscripts cache databases.
17
 *
18
 * The system is twofold:
19
 *
20
 * There are cache backends, which are responsible for the actual storage and retrieval of
21
 * cache information, and cache modules, which provide caching services to the application
22
 * developer.
23
 *
24
 * Check the documentation of the backends for configuring the cache on your live site
25
 * (as an administrator).
26
 *
27
 * Check the documentation of the cache modules to learn how to take advantage of the cache
28
 * services available (as a component/core author).
29
 *
30
 * The cache service is independent from the MidCOM Core, as it has to be started up at the
31
 * beginning of the request. Cache modules are loaded on-demand.
32
 *
33
 * This class will be available throughout he midcom service getter under the handle cache.
34
 * The content cache module, for backwards compatibility, will be available as $midcom->cache.
35
 *
36
 * All loaded modules will also be available as direct members of this class, you have to ensure
37
 * the module is loaded in advance though. The class will automatically load all modules which
38
 * are configured in the autoload_queue in the cache configuration.
39
 *
40
 * @property midcom_services_cache_module_content $content
41
 * @package midcom.services
42
 */
43
class midcom_services_cache implements EventSubscriberInterface
44
{
45
    /**
46
     * List of all loaded modules, indexed by their class name.
47
     *
48
     * @var midcom_services_cache_module[]
49
     */
50
    private $_modules = [];
51
52
    /**
53
     * Cache service startup. It initializes all cache modules configured in the
54
     * global configuration as outlined in the class introduction.
55
     *
56
     * It will load the content cache module as the first one, the rest will be
57
     * loaded in their order of appearance in the Array.
58
     */
59
    public function __construct()
60
    {
61
        array_map([$this, 'load_module'], midcom::get()->config->get('cache_autoload_queue'));
0 ignored issues
show
Bug introduced by
It seems like midcom::get()->config->g...'cache_autoload_queue') can also be of type null; however, parameter $arr1 of array_map() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

61
        array_map([$this, 'load_module'], /** @scrutinizer ignore-type */ midcom::get()->config->get('cache_autoload_queue'));
Loading history...
62
        midcom::get()->dispatcher->addSubscriber($this);
63
    }
64
65
    public static function getSubscribedEvents()
66
    {
67
        return [
68
            dbaevent::CREATE => ['handle_create'],
69
            dbaevent::UPDATE => ['handle_update'],
70
            dbaevent::DELETE => ['handle_event'],
71
            dbaevent::APPROVE => ['handle_event'],
72
            dbaevent::UNAPPROVE => ['handle_event'],
73
        ];
74
    }
75
76 183
    public function handle_event(dbaevent $event)
77
    {
78 183
        $object = $event->get_object();
79 183
        $this->invalidate($object);
80 183
    }
81
82 281
    public function handle_create(dbaevent $event)
83
    {
84 281
        $object = $event->get_object();
85 281
        $parent = $object->get_parent();
86 281
        if (!empty($parent->guid)) {
87
            // Invalidate parent from cache so content caches have chance to react
88 168
            $this->invalidate($parent);
89
        }
90 281
    }
91
92 115
    public function handle_update(dbaevent $event)
93
    {
94 115
        $object = $event->get_object();
95 115
        $this->invalidate($object);
96
97 115
        if (midcom::get()->config->get('attachment_cache_enabled')) {
98
            foreach ($object->list_attachments() as $att) {
99
                $this->invalidate($att->guid);
100
                // This basically ensures that attachment cache links are
101
                // deleted when their parent is no longer readable by everyone
102
                // @todo: The only question is: Does it even get triggered on privilege changes?
103
                $att->update_cache();
104
            }
105
        }
106 115
    }
107
108
    /**
109
     * Load the specified cache module (if not already loaded), add it to the _modules array
110
     * and assign it to a member variable named after the module.
111
     *
112
     * @param string $name The name of the cache module to load.
113
     */
114
    private function load_module(string $name)
115
    {
116
        if (isset($this->_modules[$name])) {
117
            return;
118
        }
119
120
        $classname = "midcom_services_cache_module_{$name}";
121
122
        if (!class_exists($classname)) {
123
            throw new midcom_error("Tried to load the cache module {$name}, but the class {$classname} was not found");
124
        }
125
126
        $this->_modules[$name] = new $classname();
127
        $this->_modules[$name]->initialize();
128
        $this->$name =& $this->_modules[$name];
129
    }
130
131
    /**
132
     * Invalidate all caches completely.
133
     *
134
     * Use this, if you have, f.x. changes in the layout. The URL function
135
     * midcom-cache-invalidate will trigger this function.
136
     */
137
    public function invalidate_all()
138
    {
139
        foreach ($this->_modules as $name => $module) {
140
            debug_add("Invalidating the cache module {$name} completely.");
141
            $module->invalidate_all();
142
        }
143
        $fs = new Filesystem;
144
        $fs->remove([midcom::get()->getCacheDir()]);
145
        // see https://github.com/symfony/symfony/pull/36540
146
        if (function_exists('opcache_reset')) {
147
            opcache_reset();
148
        }
149
150
        connection::invalidate_cache();
151
    }
152
153
    /**
154
     * Invalidates all cache records associated with a given content object.
155
     *
156
     * @param mixed $guid This is either a GUID or a MidgardObject, in which case the Guid is auto-determined.
157
     */
158 287
    public function invalidate($guid)
159
    {
160 287
        $object = null;
161 287
        if (is_object($guid)) {
162 244
            $object = $guid;
163 244
            $guid = $object->guid;
164
        }
165 287
        if (empty($guid)) {
166
            debug_add("Called for empty GUID, ignoring invalidation request.");
167
            return;
168
        }
169
170 287
        foreach ($this->_modules as $name => $module) {
171 287
            debug_add("Invalidating the cache module {$name} for GUID {$guid}.");
172 287
            $module->invalidate($guid, $object);
173
        }
174 287
    }
175
}
176