4

I have a .ini file as configuration of my project. Now I want to make it accessible everywhere. Here is a simplified of what I'm trying to do:

<?php

$config = [];
$config['base_url'] = '/mypath/';

class myclass{

    public function myfunc(){
        print_r($config);
    }
}

$obj = new myclass;
$obj->myfunc;

As you see in the fiddle, it throws:

Notice: Undefined property: myclass::$myfunc in /in/iNZOv on line 14

Noted that when I use global keyword for it, it throws syntax error.


Look, I can pass the array to the class like this:

$obj = new myclass($config);

public $config;
public function __construct($config)
{
    $this->config = $config;
}

But I cannot do that every time for all classes. Anyway, I want to know, is it possible to make an array accessible in the global scope?

tereško
  • 58,060
  • 25
  • 98
  • 150
Martin AJ
  • 6,261
  • 8
  • 53
  • 111

6 Answers6

1

Working with global. Also you need $obj->myfunc(); instead of $obj->myfunc(); as it is member function

$config = [];
$config['base_url'] = '/mypath/';

class myclass{

    public function myfunc(){
        global $config;
        print_r($config);
    }
}

$obj = new myclass;
$obj->myfunc();
B. Desai
  • 16,414
  • 5
  • 26
  • 47
1

myfunc is a method, not a property. So $obj->myfunc; should be $obj->myfunc();

And though not recommended, you can use global to access config inside your function.

$config = [];
$config['base_url'] = '/mypath/';

class myclass{

    public function myfunc(){
        global $config;
        print_r($config);
    }
}

$obj = new myclass;
$obj->myfunc();

Here's a good read on why Globals are evil.

Rajdeep Paul
  • 16,887
  • 3
  • 18
  • 37
  • So if *globals* are evil, is there any better solution? btw, what about *constants* ? – Martin AJ Jul 22 '17 at 10:29
  • @MartinAJ Yes, you can have a constant like this: `define("CONFIG", "/mypath/");` and use it in your classes `print_r(CONFIG);` without any `global` keyword. But basically the point is, you should not rely anything coming from outside of the function/method. I believe your second approach is much more better. – Rajdeep Paul Jul 22 '17 at 10:33
1

When you define a variable outside a function it is already in the global scope.

You can access them through the "super global" $_GLOBAL["variable_name"] as well;

But your problem is that you are calling a method as a property.

Sasa Blagojevic
  • 2,110
  • 17
  • 22
  • 1
    Thank you, you are my first upvote ^_^. From php version >= 7.0 you can define arrays as constants, so you can use this as well `define('CONFIG', ['key' => 'value']);` – Sasa Blagojevic Jul 22 '17 at 10:50
1

Don't rely on globals. Never. In a perfect world, what you need is writing a proper factory for your class. Instead of typing $obj = new MyClass(); everywhere when you need, having a factory and create MyClass instances there is far more better approach.

Basically your question is getting into the dependency injection topic. A simple array or object, it doesn't matter. Your MyClass has a dependency here and this dependency may change in the future. This problem is a basic reason behind the invention of the Dependency Injection Containers.

I would give a chance to Pimple or Aura DI to getting familiar with the containers.

Here is an example for Pimple (assuming your project uses composer). Go to project root and get pimple:

 $ composer require pimple/pimple ~3.0

Create and configure a container in early steps of your app:

use Pimple\Container;

$dic = new Container();
$dic['config'] = function ($c) {
    return ['base_url' => '/mypath/'];
};

$dic['myclass'] = function ($c) {
    return new MyClass($c['config']);
};

Now you can get your class anywhere in your application by simply typing:

$obj = $dic['myclass']; // $obj is a new, shiny MyClass instance here

Your MyClass signature should be look like:

private $config; // Always start with reduced visibility

public function __construct(array $config)
{
    $this->config = $config;
}

public function myfunc() {
    print_r($this->config);
}
edigu
  • 9,878
  • 5
  • 57
  • 80
  • It is highlighted in a question that passing `config` as a variable is not an option no matter as "as-is" variable or a part of a container. The "passing" is an obstacle. Container can be available statically though, what could probably solve an issue and meet the requirements. – Axalix Jul 22 '17 at 12:34
  • I am trying to show the correct way. Injecting (passing) the configuration into your class is way to go. Why injection is not an option? Can you elobrate the reason behind this? – edigu Jul 22 '17 at 22:10
  • @edigu injection is an option and a correct way in many applications. But the question was different. It was said "But I cannot do that every time for all classes." An author didn't want to pass a variable all the time (either `$container` or `$config`). DI is a pattern that works in many cases, but what's the point to use it in a "Hello World"-like application? I never recommend to use complex solutions for simple tasks and DI is a complex pattern. – Axalix Jul 23 '17 at 18:05
0

The best solution is using a user-defined function like this:

<?php

function config( $key = null ){

    // $config = parse_ini_file('../out_of_root/app.ini');
    $config = [];
    $config['base_url'] = '/mypath/';

    if ( is_null($key) ){
        return $config;
    } else if ( array_key_exists($key, $config) ) {
        return $config[$key];
    } else {
        return "key doesn't exist";
    }
}

class myclass{

    public function myfunc(){
        echo config('base_url');
    }
}

$obj = new myclass;
$obj->myfunc();

Demo

Martin AJ
  • 6,261
  • 8
  • 53
  • 111
  • Just need to hope it's not called 1000 times in the script life cycle. A lot of I/O there. DI is the answer to this type of problem. – Claudio Pinto Jul 22 '17 at 12:38
0

I wouldn't recommend using global either. Containers are good in general, but sometimes it's just too much. Here's a simple solution with a static variable and method.

class Config
{
    public static $values = [
        'base_url' => '/mypath/'
    ];

    /**
     * @param string $key
     * @return null|string|int|mixed
     */
    public static function get($key)
    {
        return empty(self::$values[$key]) ? null : self::$values[$key]; // alternatively you can throw an exception for a missing key.
    }
}

class Foo
{
    public function printConfig()
    {
        print_r(Config::$values);
    }
}

(new Foo)->printConfig();
echo Config::get('base_url');
Axalix
  • 2,831
  • 1
  • 20
  • 37