Fout bij links in CakePHP (versie 1.2.3.8166)

Voor degenen die hun websites bouwen met CakePHP, en merken dat een compleet serverpad in de URL staat als ze met de HTMLHelper of via de Router links laten maken, heb ik hier een mogelijke oplossing. Ik moet er wel gelijk even bij vermelden dat ik deze bug heb geconstateerd in CakePHP versie 1.2.3.8166, en niet heb gekeken of het in de meest recente versie nog steeds geldt. Mocht je die versie gebruiken, en is het niet mogelijk te upgraden, probeer dan dit.

Het komt weleens voor dat hosts, zeker in een shared hosting omgeving, symbolic links gebruiken in de DOCUMENT_ROOT, of in andere superglobals. Dat houdt in, dat bijvoorbeeld echo $_SERVER['DOCUMENT_ROOT']; iets teruggeeft als /var/www/example.com, terwijl de site in werkelijkheid staat in /usr/domains/example.com/htdocs. De map /var/www/example.com is een symbolic link naar /usr/domains/example.com/htdocs.

In zo’n geval komt het soms voor dat CakePHP bij het genereren van URLs het verkeerde pad als basis-url neemt, waardoor je in je html-code dit hebt staan: <a href="/var/www/example.com/pagina">Pagina</a>. De site werkt verder wel, de goede pagina’s worden ook geladen, maar het staat zo lelijk in je adresbalk.

Gelukkig is het heel simpel op te lossen. De basis-url die gebruikt wordt, wordt bepaald in /cake/dispatcher.php.

334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
function baseUrl() {
	$dir = $webroot = null;
	$config = Configure::read('App');
	extract($config);
 
	if (!$base) {
		$base = $this->base;
	}
	if ($base !== false) {
		$this->webroot = $base . '/';
		return $this->base = $base;
	}
 
	if (!$baseUrl) {
		$replace = array('<', '>', '*', '\'', '"');
		$base = str_replace($replace, '', dirname(env('PHP_SELF')));
 
		if ($webroot === 'webroot' && $webroot === basename($base)) {
			$base = dirname($base);
		}
		if ($dir === 'app' && $dir === basename($base)) {
			$base = dirname($base);
		}
 
		if ($base === DS || $base === '.') {
			$base = '';
		}
 
		$this->webroot = $base .'/';
		return $base;
	}
	$file = null;
 
	if ($baseUrl) {
		$file = '/' . basename($baseUrl);
		$base = dirname($baseUrl);
 
		if ($base === DS || $base === '.') {
			$base = '';
		}
		$this->webroot = $base .'/';
 
		if (strpos($this->webroot, $dir) === false) {
			$this->webroot .= $dir . '/' ;
		}
		if (strpos($this->webroot, $webroot) === false) {
			$this->webroot .= $webroot . '/';
		}
		return $base . $file;
	}
	return false;
}

Het gaat fout bij het volgende stuk:

347
348
349
	if (!$baseUrl) {
		$replace = array('<', '>', '*', '\'', '"');
		$base = str_replace($replace, '', dirname(env('PHP_SELF')));

Op de server waarbij dit fout ging, bevatte env(‘PHP_SELF’) een symbolic link. De oplossing was heel simpel:

347
348
349
	if (!$baseUrl) {
		$replace = array('<', '>', '*', '\'', '"');
		$base = str_replace($replace, '', dirname(realpath(env('PHP_SELF'))));

PHP heeft een ingebouwde functie realpath() die symbolic links volgt, en het echte pad teruggeeft. Na deze kleine aanpassing in de Dispatcher werkt alles weer zoals het hoort.

blog comments powered by Disqus