|
1 | 1 | <?php
|
2 | 2 |
|
3 | 3 | // phpcs:disable PSR1.Files.SideEffects
|
| 4 | + |
4 | 5 | /**
|
5 | 6 | * Core Manager for the Farm functionality
|
6 | 7 | *
|
@@ -130,69 +131,194 @@ public function getAnimalBaseDir()
|
130 | 131 | return getBaseURL() . '!' . $this->getAnimal();
|
131 | 132 | }
|
132 | 133 |
|
| 134 | + /** |
| 135 | + * Set the animal |
| 136 | + * |
| 137 | + * Checks if the animal exists and is a valid directory name. |
| 138 | + * |
| 139 | + * @param mixed $animal the animal name |
| 140 | + * @return bool returns true if the animal was set successfully, false otherwise |
| 141 | + */ |
| 142 | + protected function setAnimal($animal) |
| 143 | + { |
| 144 | + $farmdir = $this->config['base']['farmdir']; |
| 145 | + |
| 146 | + // invalid animal stuff is always a not found |
| 147 | + if (!is_string($animal) || strpbrk($animal, '\\/') !== false) { |
| 148 | + $this->notfound = true; |
| 149 | + return false; |
| 150 | + } |
| 151 | + $animal = strtolower($animal); |
| 152 | + |
| 153 | + // check if animal exists |
| 154 | + if (is_dir("$farmdir/$animal/conf")) { |
| 155 | + $this->animal = $animal; |
| 156 | + $this->notfound = false; |
| 157 | + return true; |
| 158 | + } else { |
| 159 | + $this->notfound = true; |
| 160 | + return false; |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | + /** |
| 165 | + * Detect the animal from the given query string |
| 166 | + * |
| 167 | + * This removes the animal parameter from the given string and sets the animal |
| 168 | + * |
| 169 | + * @param string $queryString The query string to extract the animal from, will be modified |
| 170 | + * @return bool true if the animal was set successfully, false otherwise |
| 171 | + */ |
| 172 | + protected function detectAnimalFromQueryString(string &$queryString): bool |
| 173 | + { |
| 174 | + $params = []; |
| 175 | + parse_str($queryString, $params); |
| 176 | + if (!isset($params['animal'])) return false; |
| 177 | + $animal = $params['animal']; |
| 178 | + unset($params['animal']); |
| 179 | + $queryString = http_build_query($params); |
| 180 | + |
| 181 | + $this->hostbased = false; |
| 182 | + return $this->setAnimal($animal); |
| 183 | + } |
| 184 | + |
| 185 | + /** |
| 186 | + * Detect the animal from the bang path |
| 187 | + * |
| 188 | + * This is used to detect the animal from a bang path like `/!animal/my:page` or '/dokuwiki/!animal/my:page'. |
| 189 | + * |
| 190 | + * @param string $path The bang path to extract the animal from |
| 191 | + * @return bool true if the animal was set successfully, false otherwise |
| 192 | + */ |
| 193 | + protected function detectAnimalFromBangPath(string $path): bool |
| 194 | + { |
| 195 | + $bangregex = '#^(/(?:[^/]*/)*)!([^/]+)/#'; |
| 196 | + if (preg_match($bangregex, $path, $matches)) { |
| 197 | + // found a bang path |
| 198 | + $animal = $matches[2]; |
| 199 | + |
| 200 | + $this->hostbased = false; |
| 201 | + return $this->setAnimal($animal); |
| 202 | + } |
| 203 | + return false; |
| 204 | + } |
| 205 | + |
| 206 | + /** |
| 207 | + * Detect the animal from the host name |
| 208 | + * |
| 209 | + * @param string $host The hostname |
| 210 | + * @return bool true if the animal was set successfully, false otherwise |
| 211 | + */ |
| 212 | + protected function detectAnimalFromHostName(string $host): bool |
| 213 | + { |
| 214 | + $possible = $this->getAnimalNamesForHost($host); |
| 215 | + foreach ($possible as $animal) { |
| 216 | + if ($this->setAnimal($animal)) { |
| 217 | + $this->hostbased = true; |
| 218 | + return true; |
| 219 | + } |
| 220 | + } |
| 221 | + return false; |
| 222 | + } |
| 223 | + |
133 | 224 | /**
|
134 | 225 | * Detect the current animal
|
135 | 226 | *
|
136 | 227 | * Sets internal members $animal, $notfound and $hostbased
|
137 | 228 | *
|
138 | 229 | * This borrows form DokuWiki's inc/farm.php but does not support a default conf dir
|
| 230 | + * |
| 231 | + * @params string|null $sapi the SAPI to use. Only changed during testing |
139 | 232 | */
|
140 |
| - protected function detectAnimal() |
| 233 | + protected function detectAnimal($sapi = null) |
141 | 234 | {
|
142 |
| - $farmdir = $this->config['base']['farmdir']; |
| 235 | + $sapi = $sapi ?: PHP_SAPI; |
143 | 236 | $farmhost = $this->config['base']['farmhost'];
|
144 | 237 |
|
145 |
| - // check if animal was set via rewrite parameter |
146 |
| - $animal = ''; |
147 |
| - if (isset($_GET['animal'])) { |
148 |
| - $animal = $_GET['animal']; |
149 |
| - // now unset the parameter to not leak into new queries |
150 |
| - unset($_GET['animal']); |
151 |
| - $params = []; |
152 |
| - parse_str($_SERVER['QUERY_STRING'], $params); |
153 |
| - if (isset($params['animal'])) unset($params['animal']); |
154 |
| - $_SERVER['QUERY_STRING'] = http_build_query($params); |
155 |
| - } |
156 |
| - // get animal from CLI parameter |
157 |
| - if ('cli' == PHP_SAPI && isset($_SERVER['animal'])) $animal = $_SERVER['animal']; |
158 |
| - if ($animal) { |
159 |
| - // check that $animal is a string and just a directory name and not a path |
160 |
| - if (!is_string($animal) || strpbrk($animal, '\\/') !== false) { |
161 |
| - $this->notfound = true; |
| 238 | + if ('cli' == $sapi) { |
| 239 | + if (!isset($_SERVER['animal'])) return; // no animal parameter given - we're the farmer |
| 240 | + |
| 241 | + if (preg_match('#^https?://#i', $_SERVER['animal'])) { |
| 242 | + // CLI animal parameter is a URL |
| 243 | + $urlparts = parse_url($_SERVER['animal']); |
| 244 | + $urlparts['query'] ??= ''; |
| 245 | + |
| 246 | + // detect the animal from the URL |
| 247 | + $this->detectAnimalFromQueryString($urlparts['query']) || |
| 248 | + $this->detectAnimalFromBangPath($urlparts['path']) || |
| 249 | + $this->detectAnimalFromHostName($urlparts['host']); |
| 250 | + |
| 251 | + // fake baseurl etc. |
| 252 | + $this->injectServerEnvironment($urlparts); |
| 253 | + } else { |
| 254 | + // CLI animal parameter is just a name |
| 255 | + $this->setAnimal(strtolower($_SERVER['animal'])); |
| 256 | + } |
| 257 | + } else { |
| 258 | + // an animal url parameter has been set |
| 259 | + if (isset($_GET['animal'])) { |
| 260 | + $this->detectAnimalFromQueryString($_SERVER['QUERY_STRING']); |
| 261 | + unset($_GET['animal']); |
162 | 262 | return;
|
163 |
| - }; |
164 |
| - $animal = strtolower($animal); |
| 263 | + } |
| 264 | + |
| 265 | + // no host - no host based setup. if we're still here then it's the farmer |
| 266 | + if (empty($_SERVER['HTTP_HOST'])) return; |
165 | 267 |
|
166 |
| - // check if animal exists |
167 |
| - if (is_dir("$farmdir/$animal/conf")) { |
168 |
| - $this->animal = $animal; |
| 268 | + // is this the farmer? |
| 269 | + if (strtolower($_SERVER['HTTP_HOST']) == $farmhost) { |
169 | 270 | return;
|
170 |
| - } else { |
| 271 | + } |
| 272 | + |
| 273 | + // we're in host based mode now |
| 274 | + $this->hostbased = true; |
| 275 | + |
| 276 | + // we should get an animal now |
| 277 | + if (!$this->detectAnimalFromHostName($_SERVER['HTTP_HOST'])) { |
171 | 278 | $this->notfound = true;
|
172 |
| - return; |
173 | 279 | }
|
174 | 280 | }
|
| 281 | + } |
175 | 282 |
|
176 |
| - // no host - no host based setup. if we're still here then it's the farmer |
177 |
| - if (!isset($_SERVER['HTTP_HOST'])) return; |
178 |
| - |
179 |
| - // is this the farmer? |
180 |
| - if (strtolower($_SERVER['HTTP_HOST']) == $farmhost) { |
181 |
| - return; |
| 283 | + /** |
| 284 | + * Create Server environment variables for the current animal |
| 285 | + * |
| 286 | + * This is called when the animal is initialized on the command line using a full URL. |
| 287 | + * Since the initialization is running before any configuration is loaded, we instead |
| 288 | + * set the $_SERVER variables that will later be used to autodetect the base URL. This |
| 289 | + * way a manually set base URL will still take precedence. |
| 290 | + * |
| 291 | + * @param array $urlparts A parse_url() result array |
| 292 | + * @return void |
| 293 | + * @see is_ssl() |
| 294 | + * @see getBaseURL() |
| 295 | + */ |
| 296 | + protected function injectServerEnvironment(array $urlparts) |
| 297 | + { |
| 298 | + // prepare data for DOKU_REL |
| 299 | + $path = $urlparts['path'] ?? '/'; |
| 300 | + if (($bangpos = strpos($path, '!')) !== false) { |
| 301 | + // strip from the bang path |
| 302 | + $path = substr($path, 0, $bangpos); |
| 303 | + } |
| 304 | + if (!str_ends_with($path, '.php')) { |
| 305 | + // make sure we have a script name |
| 306 | + $path = rtrim($path, '/') . '/doku.php'; |
182 | 307 | }
|
| 308 | + $_SERVER['SCRIPT_NAME'] = $path; |
183 | 309 |
|
184 |
| - // still here? check for host based |
185 |
| - $this->hostbased = true; |
186 |
| - $possible = $this->getAnimalNamesForHost($_SERVER['HTTP_HOST']); |
187 |
| - foreach ($possible as $animal) { |
188 |
| - if (is_dir("$farmdir/$animal/conf/")) { |
189 |
| - $this->animal = $animal; |
190 |
| - return; |
191 |
| - } |
| 310 | + // prepare data for is_ssl() |
| 311 | + if (($urlparts['scheme'] ?? '') === 'https') { |
| 312 | + $_SERVER['HTTPS'] = 'on'; |
| 313 | + } else { |
| 314 | + $_SERVER['HTTPS'] = 'off'; |
192 | 315 | }
|
193 | 316 |
|
194 |
| - // no hit |
195 |
| - $this->notfound = true; |
| 317 | + // prepare data for DOKU_URL |
| 318 | + $_SERVER['HTTP_HOST'] = $urlparts['host'] ?? ''; |
| 319 | + if (isset($urlparts['port'])) { |
| 320 | + $_SERVER['HTTP_HOST'] .= ':' . $urlparts['port']; |
| 321 | + } |
196 | 322 | }
|
197 | 323 |
|
198 | 324 | /**
|
@@ -338,7 +464,7 @@ protected function adjustCascade()
|
338 | 464 | $prepend = [];
|
339 | 465 | if ($key == 'main') {
|
340 | 466 | $prepend = [
|
341 |
| - 'protected' => [DOKU_INC . 'conf/local.protected.php'] |
| 467 | + 'protected' => [DOKU_INC . 'conf/local.protected.php'] |
342 | 468 | ];
|
343 | 469 | $append = [
|
344 | 470 | 'default' => [DOKU_INC . 'conf/local.php'],
|
|
0 commit comments