1
\$\begingroup\$

I'm learning PHP and I know too many ways to configure the application.

I use this method:

Folder:

  • app
    • config
      • app.php
      • db.php
  • src
    • class.php
  • index.php

File:

// app.php
return [
    'environment' => 'development',

    'meta' => [
        'description' => 'Description',

        'title' => 'Title'
    ],

    ..
];

// db.php
return [
    'connection' => 'mysql',

    ..
];

And some class for configuration:

namespace App;

use Closure;

class Singleton
{

    private static $instance = [];

    public static function get($name)
    {
        $instance = self::$instance[$name];

        if ($instance instanceof Closure) {
            $instance = $instance();
        }

        return $instance;
    }

    public static function set($name, $instance)
    {
        if (is_string($instance)) {
            $instance = new $instance();
        }

        self::$instance[$name] = $instance;
    }

    private function __clone() {}

    private function __construct() {}

    private function __wakeup() {}
}

class Config
{

    public static $data = [];

    public static function forget($key)
    {
        Arr::forget(self::$data, $key);
    }

    public static function get($key, $default = null)
    {
        $segment = explode('.', $key);

        if (! array_key_exists($file = current($segment), self::$data)) {
            if (is_readable($path = 'app' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . $file . '.php')) {
                self::$data[$file] = require_once $path;
            }
        }

        return Arr::get(self::$data, $key, $default);
    }

    public static function set($key, $value)
    {
        Arr::set(self::$data, $key, $value);
    }

    public static function __callStatic($method, $argument)
    {
        $key = $method;
        $default = null;

        if (count($argument)) {
            $key .= '.' . array_shift($argument);
            $default = array_shift($argument);
        }

        return self::get($key, $default);
    }

    public function __call($method, $argument)
    {
        return self::__callStatic($method, $argument);
    }
}

class Arr
{

    public static function forget(&$array, $key)
    {
        $temp = &$array;

        foreach ((array) $key as $path) {
            $segment = explode('.', $path);

            while (count($segment) > 1) {
                $part = array_shift($segment);

                if (self::has($array, $part)) {
                    $array = &$array[$part];
                }
            }

            unset($array[array_shift($segment)]);

            $array = &$temp;
        }
    }

    public static function get($array, $key, $default = null)
    {
        if (is_null($key)) {
            return $array;
        }

        if (array_key_exists($key, $array)) {
            return $array[$key];
        }

        foreach (explode('.', $key) as $segment) {
            if (! is_array($array) || ! array_key_exists($segment, $array)) {
                return $default;
            }

            $array = $array[$segment];
        }

        return $array;
    }

    public static function has($array, $key)
    {
        if (empty($array) || is_null($key)) {
            return false;
        }

        if (array_key_exists($key, $array)) {
            return true;
        }

        foreach (explode('.', $key) as $segment) {
            if (! is_array($array) || ! array_key_exists($segment, $array)) {
                return false;
            }

            $array = $array[$segment];
        }

        return true;
    }

    public static function set(&$array, $key, $value)
    {
        if (is_null($key)) {
            return $array = $value;
        }

        $data = explode('.', $key);

        while (count($data) > 1) {
            $key = array_shift($data);

            if (! is_array($array) || ! array_key_exists($key, $array)) {
                $array[$key] = [];
            }

            $array = &$array[$key];
        }

        $array[array_shift($data)] = $value;

        return $array;
    }
}

This is how it works:

function app($key = null, $default = null) {
    App\Singleton::set('config', 'App\Config');

    return $key ? App\Singleton::get('config')->get('app.' . $key, $default) : App\Singleton::get('config');
}

echo app('environment');
echo app('domain', 'http://domain.com');
echo app('meta.title');
echo app()->db('connection');

How it should be? Is this the best way?

\$\endgroup\$
1
\$\begingroup\$

Singletons should be avoided if possible, but I have found that when programming PHP, a Config is the one exception where a singleton always seems to make sense.

There is one rule though:

I strongly discourage using it in other objects. Not even with dependency injection or anything like that. (Not that DI really applies to singletons). Instead, what I do is on each PHP page, right at the top, (or in a bootstrap file for commonly used variables and classes that use them), write code which creates the necessary objects, and takes the data they need from the Config class. This means the objects don't need to know about how configuration values are obtained, or even what class obtains them, or how that class works.

This actually falls into the Controller pattern, even though you're not using any kind of MVC framework or controller objects. Each php page (entry point, e.g. the URL the user navigates to) acts as a controller. IMO it aids implementing MVC in a way that is native to PHP with minimal code.

\$\endgroup\$

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.