Commit | Line | Data |
---|---|---|
728bbd5b TO |
1 | <?php |
2 | namespace Civi\Test; | |
3 | ||
4 | use Civi\Test\CiviEnvBuilder\CallbackStep; | |
5 | use Civi\Test\CiviEnvBuilder\ExtensionStep; | |
6 | use Civi\Test\CiviEnvBuilder\SqlFileStep; | |
7 | use Civi\Test\CiviEnvBuilder\SqlStep; | |
8 | use Civi\Test\CiviEnvBuilder\StepInterface; | |
9 | use RuntimeException; | |
10 | ||
11 | /** | |
12 | * Class CiviEnvBuilder | |
13 | * | |
14 | * Provides a fluent interface for tracking a set of steps. | |
15 | * By computing and storing a signature for the list steps, we can | |
16 | * determine whether to (a) do nothing with the list or (b) | |
17 | * reapply all the steps. | |
18 | */ | |
19 | class CiviEnvBuilder { | |
20 | protected $name; | |
21 | ||
22 | private $steps = array(); | |
23 | ||
24 | /** | |
25 | * @var string|NULL | |
26 | * A digest of the values in $steps. | |
27 | */ | |
28 | private $targetSignature = NULL; | |
29 | ||
30 | public function __construct($name) { | |
31 | $this->name = $name; | |
32 | } | |
33 | ||
34 | public function addStep(StepInterface $step) { | |
35 | $this->targetSignature = NULL; | |
36 | $this->steps[] = $step; | |
37 | return $this; | |
38 | } | |
39 | ||
40 | public function callback($callback, $signature = NULL) { | |
41 | return $this->addStep(new CallbackStep($callback, $signature)); | |
42 | } | |
43 | ||
44 | public function sql($sql) { | |
45 | return $this->addStep(new SqlStep($sql)); | |
46 | } | |
47 | ||
48 | public function sqlFile($file) { | |
49 | return $this->addStep(new SqlFileStep($file)); | |
50 | } | |
51 | ||
52 | /** | |
53 | * Require an extension (based on its name). | |
54 | * | |
55 | * @param string $name | |
56 | * @return \CiviEnvBuilder | |
57 | */ | |
58 | public function ext($name) { | |
59 | return $this->addStep(new ExtensionStep($name)); | |
60 | } | |
61 | ||
62 | /** | |
63 | * Require an extension (based on its directory). | |
64 | * | |
65 | * @param $dir | |
66 | * @return \CiviEnvBuilder | |
67 | * @throws \CRM_Extension_Exception_ParseException | |
68 | */ | |
69 | public function extDir($dir) { | |
70 | while ($dir && dirname($dir) !== $dir && !file_exists("$dir/info.xml")) { | |
71 | $dir = dirname($dir); | |
72 | } | |
73 | if (file_exists("$dir/info.xml")) { | |
74 | $info = \CRM_Extension_Info::loadFromFile("$dir/info.xml"); | |
75 | $name = $info->key; | |
76 | } | |
77 | return $this->addStep(new ExtensionStep($name)); | |
78 | } | |
79 | ||
80 | protected function assertValid() { | |
81 | foreach ($this->steps as $step) { | |
82 | if (!$step->isValid()) { | |
83 | throw new RuntimeException("Found invalid step: " . var_dump($step, 1)); | |
84 | } | |
85 | } | |
86 | } | |
87 | ||
88 | /** | |
89 | * @return string | |
90 | */ | |
91 | protected function getTargetSignature() { | |
92 | if ($this->targetSignature === NULL) { | |
93 | $buf = ''; | |
94 | foreach ($this->steps as $step) { | |
95 | $buf .= $step->getSig(); | |
96 | } | |
97 | $this->targetSignature = md5($buf); | |
98 | } | |
99 | ||
100 | return $this->targetSignature; | |
101 | } | |
102 | ||
103 | /** | |
104 | * @return string | |
105 | */ | |
106 | protected function getSavedSignature() { | |
107 | $liveSchemaRev = NULL; | |
108 | $pdo = \Civi\Test::pdo(); | |
109 | $pdoStmt = $pdo->query(sprintf( | |
110 | "SELECT rev FROM %s.civitest_revs WHERE name = %s", | |
111 | \Civi\Test::dsn('database'), | |
112 | $pdo->quote($this->name) | |
113 | )); | |
114 | foreach ($pdoStmt as $row) { | |
115 | $liveSchemaRev = $row['rev']; | |
116 | } | |
117 | return $liveSchemaRev; | |
118 | } | |
119 | ||
120 | /** | |
121 | * @param $newSignature | |
122 | */ | |
123 | protected function setSavedSignature($newSignature) { | |
124 | $pdo = \Civi\Test::pdo(); | |
125 | $query = sprintf( | |
126 | 'INSERT INTO %s.civitest_revs (name,rev) VALUES (%s,%s) ' | |
127 | . 'ON DUPLICATE KEY UPDATE rev = %s;', | |
128 | \Civi\Test::dsn('database'), | |
129 | $pdo->quote($this->name), | |
130 | $pdo->quote($newSignature), | |
131 | $pdo->quote($newSignature) | |
132 | ); | |
133 | ||
134 | if (\Civi\Test::execute($query) === FALSE) { | |
135 | throw new RuntimeException("Failed to flag schema version: $query"); | |
136 | } | |
137 | } | |
138 | ||
139 | /** | |
140 | * Determine if the schema is correct. If necessary, destroy and recreate. | |
141 | * | |
142 | * @param bool $force | |
143 | * @return $this | |
144 | */ | |
145 | public function apply($force = FALSE) { | |
146 | $dbName = \Civi\Test::dsn('database'); | |
147 | $query = "USE {$dbName};" | |
148 | . "CREATE TABLE IF NOT EXISTS civitest_revs (name VARCHAR(64) PRIMARY KEY, rev VARCHAR(64));"; | |
149 | ||
150 | if (\Civi\Test::execute($query) === FALSE) { | |
151 | throw new \RuntimeException("Failed to flag schema version: $query"); | |
152 | } | |
153 | ||
154 | $this->assertValid(); | |
155 | ||
156 | if (!$force && $this->getSavedSignature() === $this->getTargetSignature()) { | |
157 | return $this; | |
158 | } | |
159 | foreach ($this->steps as $step) { | |
160 | $step->run($this); | |
161 | } | |
162 | $this->setSavedSignature($this->getTargetSignature()); | |
163 | return $this; | |
164 | } | |
165 | ||
166 | } |