Completed
Push — master ( 6736b6...005fc9 )
by Andreas
27:47 queued 10s
created

midcom_services_cache   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 106
Duplicated Lines 0 %

Test Coverage

Coverage 44.9%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 40
c 2
b 0
f 0
dl 0
loc 106
ccs 22
cts 49
cp 0.449
rs 10
wmc 16

7 Methods

Rating   Name   Duplication   Size   Complexity  
A handle_update() 0 12 3
A handle_create() 0 5 2
A getSubscribedEvents() 0 8 1
A add_module() 0 5 2
A invalidate() 0 15 4
A handle_event() 0 4 1
A invalidate_all() 0 16 3
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
use Symfony\Component\HttpKernel\KernelEvents;
14
15
/**
16
 * This class is the central access point for all registered caching services. Currently
17
 * this includes the NAP, Memcache, Content and PHPscripts cache databases.
18
 *
19
 * The system is twofold:
20
 *
21
 * There are cache backends, which are responsible for the actual storage and retrieval of
22
 * cache information, and cache modules, which provide caching services to the application
23
 * developer.
24
 *
25
 * Check the documentation of the backends for configuring the cache on your live site
26
 * (as an administrator).
27
 *
28
 * Check the documentation of the cache modules to learn how to take advantage of the cache
29
 * services available (as a component/core author).
30
 *
31
 * The cache service is independent from the MidCOM Core, as it has to be started up at the
32
 * beginning of the request. Cache modules are loaded on-demand.
33
 *
34
 * This class will be available throughout he midcom service getter under the handle cache.
35
 * The content cache module, for backwards compatibility, will be available as $midcom->cache.
36
 *
37
 * All loaded modules will also be available as direct members of this class, you have to ensure
38
 * the module is loaded in advance though. The class will automatically load all modules which
39
 * are configured in the autoload_queue in the cache configuration.
40
 *
41
 * @property midcom_services_cache_module_content $content
42
 * @package midcom.services
43
 */
44
class midcom_services_cache implements EventSubscriberInterface
45
{
46
    /**
47
     * List of all loaded modules, indexed by their class name.
48
     *
49
     * @var midcom_services_cache_module[]
50
     */
51
    private $_modules = [];
52
53
    public static function getSubscribedEvents()
54
    {
55
        return [
56
            dbaevent::CREATE => ['handle_create'],
57
            dbaevent::UPDATE => ['handle_update'],
58
            dbaevent::DELETE => ['handle_event'],
59
            dbaevent::APPROVE => ['handle_event'],
60
            dbaevent::UNAPPROVE => ['handle_event'],
61
        ];
62
    }
63
64 184
    public function handle_event(dbaevent $event)
65
    {
66 184
        $object = $event->get_object();
67 184
        $this->invalidate($object);
68 184
    }
69
70 282
    public function handle_create(dbaevent $event)
71
    {
72 282
        if ($parent = $event->get_object()->get_parent()) {
73
            // Invalidate parent from cache so content caches have chance to react
74 167
            $this->invalidate($parent);
75
        }
76 282
    }
77
78 113
    public function handle_update(dbaevent $event)
79
    {
80 113
        $object = $event->get_object();
81 113
        $this->invalidate($object);
82
83 113
        if (midcom::get()->config->get('attachment_cache_enabled')) {
84
            foreach ($object->list_attachments() as $att) {
85
                $this->invalidate($att->guid);
86
                // This basically ensures that attachment cache links are
87
                // deleted when their parent is no longer readable by everyone
88
                // @todo: The only question is: Does it even get triggered on privilege changes?
89
                $att->update_cache();
90
            }
91
        }
92 113
    }
93
94
    /**
95
     * Add the specified cache module (if not already present), add it to the _modules array
96
     * and assign it to a member variable named after the module.
97
     */
98
    public function add_module(string $name, midcom_services_cache_module $module)
99
    {
100
        if (!isset($this->_modules[$name])) {
101
            $this->_modules[$name] = $module;
102
            $this->$name =& $this->_modules[$name];
103
        }
104
    }
105
106
    /**
107
     * Invalidate all caches completely.
108
     *
109
     * Use this, if you have, f.x. changes in the layout. The URL function
110
     * midcom-cache-invalidate will trigger this function.
111
     */
112
    public function invalidate_all()
113
    {
114
        foreach ($this->_modules as $name => $module) {
115
            debug_add("Invalidating the cache module {$name} completely.");
116
            $module->invalidate_all();
117
        }
118
        midcom::get()->dispatcher->addListener(KernelEvents::TERMINATE, function() {
119
            $fs = new Filesystem;
120
            $fs->remove([midcom::get()->getCacheDir()]);
121
            // see https://github.com/symfony/symfony/pull/36540
122
            if (function_exists('opcache_reset')) {
123
                opcache_reset();
124
            }
125
        });
126
127
        connection::invalidate_cache();
128
    }
129
130
    /**
131
     * Invalidates all cache records associated with a given content object.
132
     *
133
     * @param mixed $guid This is either a GUID or a MidgardObject, in which case the Guid is auto-determined.
134
     */
135 288
    public function invalidate($guid)
136
    {
137 288
        $object = null;
138 288
        if (is_object($guid)) {
139 246
            $object = $guid;
140 246
            $guid = $object->guid;
141
        }
142 288
        if (empty($guid)) {
143
            debug_add("Called for empty GUID, ignoring invalidation request.");
144
            return;
145
        }
146
147 288
        foreach ($this->_modules as $name => $module) {
148 288
            debug_add("Invalidating the cache module {$name} for GUID {$guid}.");
149 288
            $module->invalidate($guid, $object);
150
        }
151 288
    }
152
}
153