CodeIgniter4: Bug: can't change and override valid locales

Problem System does not change incoming request locale with overriding it from outer source (not App config, like database) using filter approach

Describe the bug

  • all my routes has {locale} in front as first url segment ‘en/dashboard’, ‘lv/dashboard’ etc
  • supported locales are stored in local mysql database and managed through admin area
  • my project will populate / manage languages only through database and admin area, no need of App config $supportedLocales as client will not change and re-commit files
  • to avoid caching locale change in frontend, i have setup mainFilter which caches potential locale change at any point (route) within the system
  • IncomingRequest class is initialised before filters, so if my filter override supportedLocales - it does not change in further system loding

CodeIgniter 4 version 4.1.1

Solution 1:

  • IncomingRequest class - variable $validLocales change from protected to public
  • this way it is possible to override values if needed

Affected module(s) /system/HTTP/IncomingRequest.php

https://github.com/codeigniter4/CodeIgniter4/blob/develop/system/HTTP/IncomingRequest.php#L99

Solution 2:

  • as App config does not have $validLocales variable
  • as IncomingRequest class - variable $validLocales is not heavily used elsewhere
  • as IncomingRequest class controller mandatory requests App config as first method param
  • remove variable $validLocales from IncomingRequest class
  • as IncomingRequest class method setLocale() uses $validLocales, replace it with already loaded $this->config->supportedLocales

Affected module(s) /system/HTTP/IncomingRequest.php

remove: https://github.com/codeigniter4/CodeIgniter4/blob/develop/system/HTTP/IncomingRequest.php#L99

remove: https://github.com/codeigniter4/CodeIgniter4/blob/develop/system/HTTP/IncomingRequest.php#L162

update: https://github.com/codeigniter4/CodeIgniter4/blob/develop/system/HTTP/IncomingRequest.php#L225


	/**
	 * Sets the locale string for this request.
	 *
	 * @param string $locale
	 *
	 * @return IncomingRequest
	 */
	public function setLocale(string $locale)
	{
		// If it's not a valid locale, set it
		// to the default locale for the site.
		if (! in_array($locale, $this->config->supportedLocales))
		{
			$locale = $this->defaultLocale;
		}

		$this->locale = $locale;

		// If the intl extension is loaded, make sure
		// that we set the locale for it... if not, though,
		// don't worry about it.
		// this should not block code coverage thru unit testing
		// @codeCoverageIgnoreStart
		try
		{
			if (class_exists('\Locale', false))
			{
				\Locale::setDefault($locale);
			}
		}
		catch (\Exception $e)
		{
		}
		// @codeCoverageIgnoreEnd

		return $this;
	}

For reference my MainFilter class

<?php namespace Modules\Core\Base\Filters;

use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
use Config\App;
use Config\Database;

class MainFilter implements FilterInterface
{
    /**
     * Do whatever processing this filter needs to do.
     * By default it should not return anything during
     * normal execution. However, when an abnormal state
     * is found, it should return an instance of
     * CodeIgniter\HTTP\Response. If it does, script
     * execution will end and that Response will be
     * sent back to the client, allowing for error pages,
     * redirects, etc.
     *
     * @param \CodeIgniter\HTTP\RequestInterface $request
     *
     * @param null $arguments
     * @return mixed
     */
	public function before(RequestInterface $request, $arguments = null)
	{
        $locales = [];

	    // get all languages from database
        $db = Database::connect();

        $results = $db->table('core_languages')
            ->select('language')
            ->where('published', 1)
            ->get()
            ->getResultArray();

        if (!empty($results)) {
            foreach ($results as $result) {
                $locales[] = $result['language'];
            }
        }

        if (!empty($locales)) {
            /** @var App $config */
            $config = Config('App');
            $config->supportedLocales = $locales;

            $request->config = $config;

            $locale = $request->uri->getSegment(1);

            if (in_array($locale, $locales)) {
                $request->setLocale($locale);
            } else {
                $request->setLocale($config->defaultLocale);
            }
        }
	}

	//--------------------------------------------------------------------

    /**
     * Allows After filters to inspect and modify the response
     * object as needed. This method does not allow any way
     * to stop execution of other after filters, short of
     * throwing an Exception or Error.
     *
     * @param \CodeIgniter\HTTP\RequestInterface $request
     * @param \CodeIgniter\HTTP\ResponseInterface $response
     *
     * @param null $arguments
     * @return mixed
     */
	public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
	{

	}

	//--------------------------------------------------------------------
}

Context

  • OS: Ubuntu 20.04 LTS
  • Web server: Apache 2.4.41
  • PHP version 7.4 and 8.0

My suggestion I vote for solution 2 my-self and i can create a PR if needed after discussion and decision

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 37 (20 by maintainers)

Commits related to this issue

Most upvoted comments

Could you share your code that didn’t work? I will try to recreate this on Playground and see if I can track down what needs to happen.

Something as simple as this will give me a fatal error “allowed memory exhausted”. Maybe it generates some infinite loop…

class Registrars
{
    public static function App()
    {
        $session = \Config\Services::session();
    }
}