Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/cache/PhabricatorCachedClassMapQuery.php
12242 views
1
<?php
2
3
/**
4
* Cached @{class:PhutilClassMapQuery} which can perform lookups for single
5
* classes efficiently.
6
*
7
* Some class trees (like Conduit methods and PHID types) contain a huge number
8
* of classes but are frequently accessed by looking for a specific class by
9
* a known identifier (like a Conduit method name or a PHID type constant).
10
*
11
* Loading the entire class map for these cases has a small but measurable
12
* performance cost. Instead, we can build a cache from each Conduit method
13
* name to just the class required to serve that request. This means that we
14
* load fewer classes and have less overhead to execute API calls.
15
*/
16
final class PhabricatorCachedClassMapQuery
17
extends Phobject {
18
19
private $query;
20
private $queryCacheKey;
21
private $mapKeyMethod;
22
private $objectMap;
23
24
public function setClassMapQuery(PhutilClassMapQuery $query) {
25
$this->query = $query;
26
return $this;
27
}
28
29
public function setMapKeyMethod($method) {
30
$this->mapKeyMethod = $method;
31
return $this;
32
}
33
34
public function loadClasses(array $values) {
35
$cache = PhabricatorCaches::getRuntimeCache();
36
37
$cache_keys = $this->getCacheKeys($values);
38
$cache_map = $cache->getKeys($cache_keys);
39
40
$results = array();
41
$writes = array();
42
foreach ($cache_keys as $value => $cache_key) {
43
if (isset($cache_map[$cache_key])) {
44
$class_name = $cache_map[$cache_key];
45
try {
46
$result = $this->newObject($class_name);
47
if ($this->getObjectMapKey($result) === $value) {
48
$results[$value] = $result;
49
continue;
50
}
51
} catch (Exception $ex) {
52
// Keep going, we'll handle this immediately below.
53
}
54
55
// If we didn't "continue;" above, there was either a direct issue with
56
// the cache or the cached class did not generate the correct map key.
57
// Wipe the cache and pretend we missed.
58
$cache->deleteKey($cache_key);
59
}
60
61
if ($this->objectMap === null) {
62
$this->objectMap = $this->newObjectMap();
63
}
64
65
if (isset($this->objectMap[$value])) {
66
$results[$value] = $this->objectMap[$value];
67
$writes[$cache_key] = get_class($results[$value]);
68
}
69
}
70
71
if ($writes) {
72
$cache->setKeys($writes);
73
}
74
75
return $results;
76
}
77
78
public function loadClass($value) {
79
$result = $this->loadClasses(array($value));
80
return idx($result, $value);
81
}
82
83
private function getCacheKeys(array $values) {
84
if ($this->queryCacheKey === null) {
85
$this->queryCacheKey = $this->query->getCacheKey();
86
}
87
88
$key = $this->queryCacheKey;
89
$method = $this->mapKeyMethod;
90
91
$keys = array();
92
foreach ($values as $value) {
93
$keys[$value] = "classmap({$key}).{$method}({$value})";
94
}
95
96
return $keys;
97
}
98
99
private function newObject($class_name) {
100
return newv($class_name, array());
101
}
102
103
private function newObjectMap() {
104
$map = $this->query->execute();
105
106
$result = array();
107
foreach ($map as $object) {
108
$value = $this->getObjectMapKey($object);
109
if (isset($result[$value])) {
110
$other = $result[$value];
111
throw new Exception(
112
pht(
113
'Two objects (of classes "%s" and "%s") generate the same map '.
114
'value ("%s"). Each object must generate a unique map value.',
115
get_class($object),
116
get_class($other),
117
$value));
118
}
119
$result[$value] = $object;
120
}
121
122
return $result;
123
}
124
125
private function getObjectMapKey($object) {
126
return call_user_func(array($object, $this->mapKeyMethod));
127
}
128
129
}
130
131