Skip to content

Commit 38f4130

Browse files
authored
Merge pull request #88 from cosmocode/animalurl
Allow to set animals as URL on the command line
2 parents 197a8fc + f64a85f commit 38f4130

File tree

6 files changed

+482
-53
lines changed

6 files changed

+482
-53
lines changed

DokuWikiFarmCore.php

Lines changed: 169 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22

33
// phpcs:disable PSR1.Files.SideEffects
4+
45
/**
56
* Core Manager for the Farm functionality
67
*
@@ -130,69 +131,194 @@ public function getAnimalBaseDir()
130131
return getBaseURL() . '!' . $this->getAnimal();
131132
}
132133

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+
133224
/**
134225
* Detect the current animal
135226
*
136227
* Sets internal members $animal, $notfound and $hostbased
137228
*
138229
* 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
139232
*/
140-
protected function detectAnimal()
233+
protected function detectAnimal($sapi = null)
141234
{
142-
$farmdir = $this->config['base']['farmdir'];
235+
$sapi = $sapi ?: PHP_SAPI;
143236
$farmhost = $this->config['base']['farmhost'];
144237

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']);
162262
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;
165267

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) {
169270
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'])) {
171278
$this->notfound = true;
172-
return;
173279
}
174280
}
281+
}
175282

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';
182307
}
308+
$_SERVER['SCRIPT_NAME'] = $path;
183309

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';
192315
}
193316

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+
}
196322
}
197323

198324
/**
@@ -338,7 +464,7 @@ protected function adjustCascade()
338464
$prepend = [];
339465
if ($key == 'main') {
340466
$prepend = [
341-
'protected' => [DOKU_INC . 'conf/local.protected.php']
467+
'protected' => [DOKU_INC . 'conf/local.protected.php']
342468
];
343469
$append = [
344470
'default' => [DOKU_INC . 'conf/local.php'],

0 commit comments

Comments
 (0)