Path: blob/master/src/applications/cache/PhabricatorCachedClassMapQuery.php
12242 views
<?php12/**3* Cached @{class:PhutilClassMapQuery} which can perform lookups for single4* classes efficiently.5*6* Some class trees (like Conduit methods and PHID types) contain a huge number7* of classes but are frequently accessed by looking for a specific class by8* a known identifier (like a Conduit method name or a PHID type constant).9*10* Loading the entire class map for these cases has a small but measurable11* performance cost. Instead, we can build a cache from each Conduit method12* name to just the class required to serve that request. This means that we13* load fewer classes and have less overhead to execute API calls.14*/15final class PhabricatorCachedClassMapQuery16extends Phobject {1718private $query;19private $queryCacheKey;20private $mapKeyMethod;21private $objectMap;2223public function setClassMapQuery(PhutilClassMapQuery $query) {24$this->query = $query;25return $this;26}2728public function setMapKeyMethod($method) {29$this->mapKeyMethod = $method;30return $this;31}3233public function loadClasses(array $values) {34$cache = PhabricatorCaches::getRuntimeCache();3536$cache_keys = $this->getCacheKeys($values);37$cache_map = $cache->getKeys($cache_keys);3839$results = array();40$writes = array();41foreach ($cache_keys as $value => $cache_key) {42if (isset($cache_map[$cache_key])) {43$class_name = $cache_map[$cache_key];44try {45$result = $this->newObject($class_name);46if ($this->getObjectMapKey($result) === $value) {47$results[$value] = $result;48continue;49}50} catch (Exception $ex) {51// Keep going, we'll handle this immediately below.52}5354// If we didn't "continue;" above, there was either a direct issue with55// the cache or the cached class did not generate the correct map key.56// Wipe the cache and pretend we missed.57$cache->deleteKey($cache_key);58}5960if ($this->objectMap === null) {61$this->objectMap = $this->newObjectMap();62}6364if (isset($this->objectMap[$value])) {65$results[$value] = $this->objectMap[$value];66$writes[$cache_key] = get_class($results[$value]);67}68}6970if ($writes) {71$cache->setKeys($writes);72}7374return $results;75}7677public function loadClass($value) {78$result = $this->loadClasses(array($value));79return idx($result, $value);80}8182private function getCacheKeys(array $values) {83if ($this->queryCacheKey === null) {84$this->queryCacheKey = $this->query->getCacheKey();85}8687$key = $this->queryCacheKey;88$method = $this->mapKeyMethod;8990$keys = array();91foreach ($values as $value) {92$keys[$value] = "classmap({$key}).{$method}({$value})";93}9495return $keys;96}9798private function newObject($class_name) {99return newv($class_name, array());100}101102private function newObjectMap() {103$map = $this->query->execute();104105$result = array();106foreach ($map as $object) {107$value = $this->getObjectMapKey($object);108if (isset($result[$value])) {109$other = $result[$value];110throw new Exception(111pht(112'Two objects (of classes "%s" and "%s") generate the same map '.113'value ("%s"). Each object must generate a unique map value.',114get_class($object),115get_class($other),116$value));117}118$result[$value] = $object;119}120121return $result;122}123124private function getObjectMapKey($object) {125return call_user_func(array($object, $this->mapKeyMethod));126}127128}129130131