@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorDataCacheSpec extends PhabricatorCacheSpec {
4
5 private $cacheSummary;
6
7 public function setCacheSummary(array $cache_summary) {
8 $this->cacheSummary = $cache_summary;
9 return $this;
10 }
11
12 public function getCacheSummary() {
13 return $this->cacheSummary;
14 }
15
16 public static function getActiveCacheSpec() {
17 $spec = new PhabricatorDataCacheSpec();
18
19 if (extension_loaded('apcu')) {
20 $spec->initAPCuSpec();
21 } else {
22 $spec->initNoneSpec();
23 }
24
25 return $spec;
26 }
27
28 private function initAPCuSpec() {
29 $this
30 ->setName(pht('APCu'))
31 ->setVersion(phpversion('apcu'));
32
33 if (ini_get('apc.enabled')) {
34 if (function_exists('apcu_clear_cache')) {
35 $clear_callback = 'apcu_clear_cache';
36 }
37
38 $this
39 ->setIsEnabled(true)
40 ->setClearCacheCallback($clear_callback);
41 $this->initAPCCommonSpec();
42 } else {
43 $this->setIsEnabled(false);
44 $this->raiseEnableAPCIssue();
45 }
46 }
47
48 private function initNoneSpec() {
49 $message = pht(
50 'Installing the "APCu" PHP extension will improve performance. '.
51 'This extension is strongly recommended. Without it, this software '.
52 'must rely on a very inefficient disk-based cache.');
53
54 $this
55 ->newIssue('extension.apcu')
56 ->setShortName(pht('APCu'))
57 ->setName(pht('PHP Extension "APCu" Not Installed'))
58 ->setMessage($message)
59 ->addPHPExtension('apcu');
60 }
61
62 private function initAPCCommonSpec() {
63 $state = array();
64
65 if (function_exists('apcu_sma_info')) {
66 $mem = apcu_sma_info();
67 $info = apcu_cache_info();
68 } else {
69 $mem = null;
70 }
71
72 if ($mem) {
73 $this->setTotalMemory($mem['num_seg'] * $mem['seg_size']);
74
75 $this->setUsedMemory($info['mem_size']);
76 $this->setEntryCount(count($info['cache_list']));
77
78 $cache = $info['cache_list'];
79 $state = array();
80 foreach ($cache as $item) {
81 // Some older versions of APCu report the cachekey as "key", while
82 // newer APCu report it as "info". Just check both indexes for
83 // compatibility. See T13164 for details.
84
85 $info = idx($item, 'info');
86 if ($info === null) {
87 $info = idx($item, 'key');
88 }
89
90 if ($info === null) {
91 $key = '<unknown-key>';
92 } else {
93 $key = self::getKeyPattern($info);
94 }
95
96 if (empty($state[$key])) {
97 $state[$key] = array(
98 'max' => 0,
99 'total' => 0,
100 'count' => 0,
101 );
102 }
103 $state[$key]['max'] = max($state[$key]['max'], $item['mem_size']);
104 $state[$key]['total'] += $item['mem_size'];
105 $state[$key]['count']++;
106 }
107 }
108
109 $this->setCacheSummary($state);
110 }
111
112 private static function getKeyPattern($key) {
113 // If this key isn't in the current cache namespace, don't reveal any
114 // information about it.
115 $namespace = PhabricatorEnv::getEnvConfig('phabricator.cache-namespace');
116 if (strncmp($key, $namespace.':', strlen($namespace) + 1)) {
117 return '<other-namespace>';
118 }
119
120 $key = preg_replace('/(?<![a-zA-Z])\d+(?![a-zA-Z])/', 'N', $key);
121 $key = preg_replace('/PHID-[A-Z]{4}-[a-z0-9]{20}/', 'PHID', $key);
122
123 // TODO: We should probably standardize how digests get embedded into cache
124 // keys to make this rule more generic.
125 $key = preg_replace('/:celerity:.*$/', ':celerity:X', $key);
126 $key = preg_replace('/:pkcs8:.*$/', ':pkcs8:X', $key);
127
128 return $key;
129 }
130}