8

I have written this simple redirect code in the main index.php file. Now I need to know if this code can somehow cause problems with Craft:

    // Check if URI contains cpTrigger
    $pos = strpos($_SERVER['REQUEST_URI'], 'admin');

    if ($pos === false) {

        // Language paths
        $paths = array(
            'de'=>'/de/',
            'it'=>'/it/',
            'en'=>'/en/'
        );

        // Browser language
        $language = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);

        // Default language
        if (!isset($paths[$language])) {
            $language = 'de';
        }

        // Redirect
        header('HTTP/1.1 301 Moved Permanently'); 
        header('Location:' . $paths[$language]);

    } else {

        // Path to your craft/ folder
        $craftPath = '../craft';

        // Do not edit below this line
        $path = rtrim($craftPath, '/').'/app/index.php';

        if (!is_file($path)) {
            exit('Temporary error. Could not find the CMS folder.');
        }

        require_once $path;

    }

Only the 404 page doesn't load with www.example.com/idontexist but loads instead the homepage. (www.example.com/en/idontexist loads as expected the 404 page)

Perhaps somebody knows a better solution?

1
  • what exactly do you mean by main index.php? just the one in the public folder or also the ones in the sub-directories for the locales, e.g. public/de or public/es? – Tobias S. Apr 18 '18 at 10:31
4

I would suggest making the “admin” check a little more strict:

// Make sure this isn't a CP request
if (!isset($_GET['p']) || strncmp($_GET['p'], 'admin', 5) !== 0)
{
    // ...

(Assuming you’re using the recommended .htaccess redirect rule, by the time the request hits index.php, the original request URI will be available as $_GET['p'].)

The Accept-Language header can actually include multiple languages, so you might want to consider looking at all of them, sorted by weight. HttpRequestService::getBrowserLanguages() has some example code on how to do that.

A 302 redirect would probably be more appropriate than 301. You wouldn’t want Google to think that http://example.com/ should always really be http://example.com/en/ or whatever this script might end up sending it to, and people could theoretically change their browser’s language preferences.

You also might want to include $_GET['p'] in your redirect location. Right now you’re just sending all traffic off to the locale-specific homepage, which is why 404s aren’t working as expected.

To put it all together, here’s what you might want to do:

<?php

// Make sure this isn't a CP request
if (!isset($_GET['p']) || strncmp($_GET['p'], 'admin', 5) !== 0)
{
    // Language paths
    $paths = array(
        'de' => '/de/',
        'it' => '/it/',
        'en' => '/en/'
    );

    // Get the browser's preferred languages
    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && preg_match_all('/([\w\-_]+)\s*(?:;\s*q\s*=\s*(\d*\.\d*))?/', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches, PREG_SET_ORDER))
    {
        $languages = array();
        $weights = array();

        foreach ($matches as $match)
        {
            $languages[] = strtolower(substr($match[1], 0, 2));
            $weights[] = !empty($match[2]) ? floatval($match[2]) : 1;
        }

        // Sort the languages by their weight
        array_multisort($weights, SORT_NUMERIC, SORT_DESC, $languages);

        // Find the first match
        foreach ($languages as $language)
        {
            if (isset($paths[$language]))
            {
                $matchingLanguage = $language;
                break;
            }
        }
    }

    // Default to German
    if (!isset($matchingLanguage))
    {
        $matchingLanguage = 'de';
    }

    // Redirect
    $url = $paths[$matchingLanguage].(isset($_GET['p']) ? $_GET['p'] : '');
    header('Location: '.$url, true, 301);
}
else
{
    // Path to your craft/ folder
    $craftPath = '../craft';

    // Do not edit below this line
    $path = rtrim($craftPath, '/').'/app/index.php';

    if (!is_file($path))
    {
        exit('Temporary error. Could not find the CMS folder.');
    }

    require_once $path;
}
3
  • Please replace && with || in the complete code example. Works great. Thanks – Christian Steger Aug 24 '14 at 20:08
  • If I use this code on my site, it works great, but I'm unable to login to the admin panel anymore. It keeps giving me an "unknown error". Any ideas how to solve this? – Tom De Smet Jan 19 '15 at 21:15
  • @TomDeSmet Please have a look at my new answer. It solves your problem. – Christian Steger Feb 24 '15 at 23:27
2

I would like to share with you my new and improved index.php.

With the old version the control panel login didn't work all the time.

I've rearranged the code and replaced

if (!isset($_GET['p']) || strncmp($_GET['p'], 'admin', 5) !== 0)

with

if (strncmp($_SERVER['PATH_INFO'], '/admin', 6) === 0 || strncmp($_GET['p'], 'admin', 5) === 0)

This is the complete code example:

// Make sure this is a Control Panel request
if (strncmp($_SERVER['PATH_INFO'], '/admin', 6) === 0 || strncmp($_GET['p'], 'admin', 5) === 0)
{

    // Path to your craft/ folder
    $craftPath = '../craft';

    // Do not edit below this line
    $path = rtrim($craftPath, '/').'/app/index.php';

    if (!is_file($path))
    {
        if (function_exists('http_response_code'))
        {
            http_response_code(503);
        }
        exit('Temporary error. Could not find the CMS folder.');
    }
    require_once $path;

}
else
{

    // Set language paths
    $paths = array(
        'de' => '/de/',
        'it' => '/it/',
        'en' => '/en/'
    );

    // Get browser's preferred languages
    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && preg_match_all('/([\w\-_]+)\s*(?:;\s*q\s*=\s*(\d*\.\d*))?/', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches, PREG_SET_ORDER))
    {
        $languages = array();
        $weights   = array();

        // Fill arrays with all matches
        foreach ($matches as $match)
        {
            $languages[] = strtolower(substr($match[1], 0, 2));
            $weights[]   = !empty($match[2]) ? floatval($match[2]) : 1;
        }

        // Sort languages by their weight
        array_multisort($weights, SORT_NUMERIC, SORT_DESC, $languages);

        // Find first match
        foreach ($languages as $language)
        {
            if (isset($paths[$language]))
            {
                $matchingLanguage = $language;
                break;
            }
        }
    }

    // Set default language
    if (!isset($matchingLanguage))
    {
        $matchingLanguage = 'de';
    }

    // Redirect
    $url = $paths[$matchingLanguage].(isset($_GET['p']) ? $_GET['p'] : '');
    header('Location: '.$url, true, 302);

}
6
  • If you use the Sitemap plugin from PutYourLightsOn use this: if (strncmp($_SERVER['PATH_INFO'], '/admin', 6) === 0 || strncmp($_GET['p'], 'admin', 5) === 0 || strncmp($_GET['p'], 'sitemap.xml', 11) === 0) – Christian Steger Feb 25 '15 at 9:19
  • Hi, I used this code but an update to craft 2.5 and php7 somehow broke it. This is the error message I get: Notice: Undefined index: PATH_INFO in /home/...etc.../index.php on line 3. Any hint to why this happened? – outline4 Jan 29 '16 at 15:49
  • Sorry, no idea. I use this code actually with PHP 7.0.2 without errors. – Christian Steger Feb 4 '16 at 12:39
  • Hi, it was a server problem. They forgot to set the path_info value, and they've tuned some error settings. It's all good, and blazingly fast :) – outline4 Feb 4 '16 at 17:02
  • Is this still a viable solution in 2018? Looking for exactly the same functionality … – Tobias S. Apr 18 '18 at 10:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.