@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 AphrontIsolatedDatabaseConnection
4 extends AphrontDatabaseConnection {
5
6 private $configuration;
7 private static $nextInsertID;
8 private $insertID;
9
10 private $transcript = array();
11
12 private $allResults;
13 private $affectedRows;
14
15 public function __construct(array $configuration) {
16 $this->configuration = $configuration;
17
18 if (self::$nextInsertID === null) {
19 // Generate test IDs into a distant ID space to reduce the risk of
20 // collisions and make them distinctive.
21 self::$nextInsertID = 55555000000 + mt_rand(0, 1000);
22 }
23 }
24
25 public function openConnection() {
26 return;
27 }
28
29 public function close() {
30 return;
31 }
32
33 public function escapeUTF8String($string) {
34 return '<S>';
35 }
36
37 public function escapeBinaryString($string) {
38 return '<B>';
39 }
40
41 public function escapeColumnName($name) {
42 return '<C>';
43 }
44
45 public function escapeMultilineComment($comment) {
46 return '<K>';
47 }
48
49 public function escapeStringForLikeClause($value) {
50 return '<L>';
51 }
52
53 private function getConfiguration($key, $default = null) {
54 return idx($this->configuration, $key, $default);
55 }
56
57 public function getInsertID() {
58 return $this->insertID;
59 }
60
61 public function getAffectedRows() {
62 return $this->affectedRows;
63 }
64
65 public function selectAllResults() {
66 return $this->allResults;
67 }
68
69 public function executeQuery(PhutilQueryString $query) {
70
71 // NOTE: "[\s<>K]*" allows any number of (properly escaped) comments to
72 // appear prior to the allowed keyword, since this connection escapes
73 // them as "<K>" (above).
74
75 $display_query = $query->getMaskedString();
76 $raw_query = $query->getUnmaskedString();
77
78 $keywords = array(
79 'INSERT',
80 'UPDATE',
81 'DELETE',
82 'START',
83 'SAVEPOINT',
84 'COMMIT',
85 'ROLLBACK',
86 );
87 $preg_keywords = array();
88 foreach ($keywords as $key => $word) {
89 $preg_keywords[] = preg_quote($word, '/');
90 }
91 $preg_keywords = implode('|', $preg_keywords);
92
93 if (!preg_match('/^[\s<>K]*('.$preg_keywords.')\s*/i', $raw_query)) {
94 throw new AphrontNotSupportedQueryException(
95 pht(
96 "Database isolation currently only supports some queries. You are ".
97 "trying to issue a query which does not begin with an allowed ".
98 "keyword (%s): '%s'.",
99 implode(', ', $keywords),
100 $display_query));
101 }
102
103 $this->transcript[] = $display_query;
104
105 // NOTE: This method is intentionally simplified for now, since we're only
106 // using it to stub out inserts/updates. In the future it will probably need
107 // to grow more powerful.
108
109 $this->allResults = array();
110
111 // NOTE: We jitter the insert IDs to keep tests honest; a test should cover
112 // the relationship between objects, not their exact insertion order. This
113 // guarantees that IDs are unique but makes it impossible to hard-code tests
114 // against this specific implementation detail.
115 self::$nextInsertID += mt_rand(1, 10);
116 $this->insertID = self::$nextInsertID;
117 $this->affectedRows = 1;
118 }
119
120 public function executeRawQueries(array $raw_queries) {
121 $results = array();
122 foreach ($raw_queries as $id => $raw_query) {
123 $results[$id] = array();
124 }
125 return $results;
126 }
127
128 public function getQueryTranscript() {
129 return $this->transcript;
130 }
131
132}