Passed
Push — master ( 482ae9...59f703 )
by Andreas
22:54
created

midcom_services_cache   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 104
Duplicated Lines 0 %

Test Coverage

Coverage 45.83%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 39
c 3
b 0
f 0
dl 0
loc 104
ccs 22
cts 48
cp 0.4583
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 14 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
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
    public static function getSubscribedEvents()
53
    {
54
        return [
55
            dbaevent::CREATE => ['handle_create'],
56
            dbaevent::UPDATE => ['handle_update'],
57
            dbaevent::DELETE => ['handle_event'],
58
            dbaevent::APPROVE => ['handle_event'],
59
            dbaevent::UNAPPROVE => ['handle_event'],
60
        ];
61
    }
62
63 184
    public function handle_event(dbaevent $event)
64
    {
65 184
        $object = $event->get_object();
66 184
        $this->invalidate($object);
67 184
    }
68
69 282
    public function handle_create(dbaevent $event)
70
    {
71 282
        if ($parent = $event->get_object()->get_parent()) {
72
            // Invalidate parent from cache so content caches have chance to react
73 168
            $this->invalidate($parent);
74
        }
75 282
    }
76
77 115
    public function handle_update(dbaevent $event)
78
    {
79 115
        $object = $event->get_object();
80 115
        $this->invalidate($object);
81
82 115
        if (midcom::get()->config->get('attachment_cache_enabled')) {
83
            foreach ($object->list_attachments() as $att) {
84
                $this->invalidate($att->guid);
85
                // This basically ensures that attachment cache links are
86
                // deleted when their parent is no longer readable by everyone
87
                // @todo: The only question is: Does it even get triggered on privilege changes?
88
                $att->update_cache();
89
            }
90
        }
91 115
    }
92
93
    /**
94
     * Add the specified cache module (if not already present), add it to the _modules array
95
     * and assign it to a member variable named after the module.
96
     */
97
    public function add_module(string $name, midcom_services_cache_module $module)
98
    {
99
        if (!isset($this->_modules[$name])) {
100
            $this->_modules[$name] = $module;
101
            $this->$name =& $this->_modules[$name];
102
        }
103
    }
104
105
    /**
106
     * Invalidate all caches completely.
107
     *
108
     * Use this, if you have, f.x. changes in the layout. The URL function
109
     * midcom-cache-invalidate will trigger this function.
110
     */
111
    public function invalidate_all()
112
    {
113
        foreach ($this->_modules as $name => $module) {
114
            debug_add("Invalidating the cache module {$name} completely.");
115
            $module->invalidate_all();
116
        }
117
        $fs = new Filesystem;
118
        $fs->remove([midcom::get()->getCacheDir()]);
119
        // see https://github.com/symfony/symfony/pull/36540
120
        if (function_exists('opcache_reset')) {
121
            opcache_reset();
122
        }
123
124
        connection::invalidate_cache();
125
    }
126
127
    /**
128
     * Invalidates all cache records associated with a given content object.
129
     *
130
     * @param mixed $guid This is either a GUID or a MidgardObject, in which case the Guid is auto-determined.
131
     */
132 288
    public function invalidate($guid)
133
    {
134 288
        $object = null;
135 288
        if (is_object($guid)) {
136 245
            $object = $guid;
137 245
            $guid = $object->guid;
138
        }
139 288
        if (empty($guid)) {
140
            debug_add("Called for empty GUID, ignoring invalidation request.");
141
            return;
142
        }
143
144 288
        foreach ($this->_modules as $name => $module) {
145 288
            debug_add("Invalidating the cache module {$name} for GUID {$guid}.");
146 288
            $module->invalidate($guid, $object);
147
        }
148 288
    }
149
}
150