2

I have a class file Settings.php that loads an ini file and assigns the content to a constant for global access and reducing clutter. The class file contains the logic for loading the settings and define the constant (sorry for the large sample didn't see a way to compress this:

<?php declare(strict_types=1);
namespace Utils;

if( !defined( SETTINGS ) ) {
  if( !defined( 'SETTINGS_FILE' ) ) {
    define( 'SETTINGS_FILE', $_SERVER[ 'DOCUMENT_ROOT' ] . '/settings.ini' );
  }
  class Settings
  {
    public static function getSettings(): array
    {
      return parse_ini_file( SETTINGS_FILE, true );
    }
  }

  define( 'SETTINGS', Settings::getSettings() );
}

With the composer auto loader included the Settings class gets loaded with \Utils\Settings-> getSettings(); and as side effect the SETTINGS is available with the content of the settings.ini file or another one if specified in SETTINGS_FILE.

This works and something like SETTINGS['db']['username'] is available which is a nice compact and highly self-documenting access to the settings. I wonder however if this code smells in any shape or form and if there is a better way to do this.

1 Answer 1

2

The code you have shown actually doesn't follow the Singleton pattern, it instead uses a global constant to store configuration data. It doesn't prevent creation of multiple Settings instances, which is the core feature of the Singleton pattern.

In PHP, a Singleton typically looks something like this:

<?php
class Singleton {
    private static $instance = null;

    private function __construct() {
        // private to prevent direct object creation
    }

    public static function getInstance() {
        if (null === self::$instance) {
            self::$instance = new static();
        }

        return self::$instance;
    }
}

To create a Singleton settings class that loads an INI file, you could do something like this:

<?php declare(strict_types=1);
namespace Utils;

class Settings
{
    private static $instance = null;
    private $settings;

    private function __construct()
    {
        $settingsFile = $_SERVER[ 'DOCUMENT_ROOT' ] . '/settings.ini';
        $this->settings = parse_ini_file($settingsFile, true);
    }

    public static function getInstance(): self
    {
        if (null === self::$instance) {
            self::$instance = new static();
        }

        return self::$instance;
    }

    public function getSettings(): array
    {
        return $this->settings;
    }
}

Then, to use it:

<?php
$settings = \Utils\Settings::getInstance()->getSettings();

echo $settings['db']['username'];

This prevents multiple INI file parses, as the settings are only loaded once when the Settings object is first created.

In general, this is better than using a global constant because:

  1. Global state is avoided.
  2. It's easier to unit test (you can mock the Settings class).
  3. It respects the single responsibility principle (a class should only have one job).

Remember, Singleton pattern should be used wisely because it introduces a global state into an application and should be used sparingly. It's not necessarily bad, but it can make your code more difficult to reason about and more difficult to test.

If the settings are likely to change or if you may need to have multiple configurations in the future, the Singleton approach might not be the best choice. You may want to consider dependency injection instead.

4
  • Ofcourse you are right. But it requires quite some typing :-). I thought to deviate from the correct oop-patern and leverage a specific feature from PHP; user definable global constants.
    – theking2
    Jun 26, 2023 at 7:47
  • ...but the corridor : dependency injection is interesting corridor and I keep it under consideration
    – theking2
    Jun 26, 2023 at 7:57
  • Is global state really avoided? That static $instance variable looks like global state to me.
    – bdsl
    Jun 29, 2023 at 19:17
  • The Singleton's static instance essentially functions as a globally accessible object. However, the Singleton pattern does offer a degree of control and organization over this global state that raw global variables do not. Jul 1, 2023 at 13:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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