1

I'm trying to improve my website engine. So I can stop setting global $vars inside functions

So now I'm setting all my global site vars with this instead:

define('ROOT_prefix', 'mysitename_');
define('ROOT_support', 'support@mysite.com');

I can access them anywhere. But it does not feel as good (or smart) practice..

I know very little about classes.. but couldn't/should't I use a class for this instead?

This works:

class ROOT {
  public static $prefix = 'mysitename_';
  public static $support = 'support@mysite.com';
}

And then anywhere on my site I can use this (even inside functions):

echo '<h1>Please contact support at: '.ROOT::$support.' </h1>';

Is this a good way, or is there a better way?

mowgli
  • 2,796
  • 3
  • 31
  • 68
  • 1
    using constants is not a bad practice and if you keep things organized it should be fine. I don't see the purpose of using classes in this case – GabrielCol Jun 14 '14 at 19:39
  • 1
    If you had a `Config` class, you should use **Dependency Injection** (basically pass this into your objects that need the config). So: `public function __construct(RootConfigClass $config) { /** **/ }`. – Jimbo Jun 14 '14 at 19:45

4 Answers4

2

If the value of these "globals" will not be changed for the entire run-time of the script, then you absolutely should use constants, as this is exactly what they are for.

You should keep them all centralized in a common include file for readability.

(Edit based on comments follows)

Since it looks like you're using constants for some kind of localization of content, it might be prudent to use a class for this. As I have said: using constants for non-changing values in a procedurally oriented script isn't bad practice in itself, but in the context of localization, there are better ways.

One such would be to create a class with some static methods to translate a string based on the passed ini file, this would be in line with the dependency injection mentioned in other comments and answers here.

An example of such a class would look something like this:

class Localizer {

    public static function localize($langFile, $string) {
        if (!file_exists($langFile)) {
            throw new Exception($langFile . 'not found!');
        }

        $lang = parse_ini_file($langFile);

        return (!empty($lang[$string])) ? $lang[$string] : false;
    }

}

You can use it like this:

echo Localizer::localize('./english.ini', 'hello') . "\n";
echo Localizer::localize('./english.ini', 'email') . "\n";

This assumes an ini file that looks like this:

; english.ini
hello = 'Hello!'
email = 'test@test.com'

Realistically, this is probably a more "proper" way than declating a boat load of constants for each language your application runs in, but it is going to open the file every time you need to localize a string, which wouldn't be optimal for a very high volume application on a large system. But, as with a constant, you will be able to access the static methods of a class in the scope of any function in your application so long as the class was included beforehand. No need to use constants or declare globals.

The most proper and efficent way to do it would be to instantiate a class instead of using static methods, which would load the files into memory once and keep them there, eliminating the need to open the file for every string translated. But this would require that you are able to pass the variable containing the instantiation of this class to every function in your code that requires it, or declare it as global, which was exactly what you were trying to avoid in the first place.

So in order to do this, you would probably need to re-structure your code to allow for dependency injection throughout.

To continue with your current code and structure, you can continue using generated constants, which will be much messier, less "proper", and not expandable, but the advantage is that you will only read the ini files once, and keep them in memory.

Or you can use a static method, which is more "proper" but needs to read a file every time you localize a string, meaning that on large systems, it could cause some inefficiency. Realistically though, if your application in low volume, you will likely never see problems arise from this.

The main advantages of this method are expandability, and clean code. While declaring constants might be more efficient in terms of file opening and memory usage in the very short term, in most cases, it's not as expandable, because you can have an unlimited number of strings and language files, which means you could end up in a situation in the future where your loading thousands and thousands of constants every time your application loads.

If you use a class, and only load the files/strings that are needed by that specific user at run time, you can avoid this, no matter how many languages and strings you support.

jesse_galley
  • 1,676
  • 4
  • 18
  • 30
  • They will not change. I have them in a required file as first thing on all pages. So using define() is ok? – mowgli Jun 14 '14 at 19:38
  • Absolutely, it's not only OK, it's the proper thing to do in most cases. Especially where the entire application is procedural, and not object oriented. – jesse_galley Jun 14 '14 at 19:39
  • Even if I set 200 vars (constants) with define()? I'm thinking language file inclusion: $lang = parse_ini_file('english.ini'); foreach ($lang as $key=>$value) { define ($key, $value); } – mowgli Jun 14 '14 at 19:44
  • @mowgli You shouldn't use constants for internationalization purpose. Build up a class where you can pass the language you want to use to it and then retrieve the language file in class, parse the content and save it to a variable array. Then, use a get function and specify the string you want to retrieve. You should read this as well: http://stackoverflow.com/questions/6953528/best-way-to-internationalize-simple-php-website – GabrielCol Jun 14 '14 at 19:50
  • 1
    There's no problem with procedurally generating constants from a text base config file like that in and of itself, especially in a procedural application and not an object oriented one. I've seen many applications that do the same. But you should be mindful of the context in which you are doing this. Specifically, there might be better ways to handle localization like you seem to be implying with your comment. Could you give a little bit more information about the context here? what exactly are you doing with the constants, are they ALL for getting strings in different languages? – jesse_galley Jun 14 '14 at 19:55
  • @jesse_galley They are only for the static site names and sentences. I'm going to detect user language and include a .ini file for the current language (english as default): and in that file (english.ini for example) I will have something like: MENU_ABOUT = 'About Us' (NEWLINE) SITE_ERRORHEADER = 'Ooops, an error occurred:'.. And set them all as constants with define(), so I can access them on any page with echo '

    '.SITE_ERRORHEADER.'

    ';
    – mowgli Jun 14 '14 at 20:13
  • 1
    @mowgli static site names and sentences are usually what constants are used for, but doing this for localization purposes is not expandable since you could end up with thousands of constants being created, and probably have to deal with managing more complex naming conventions for them, complex ways for enumerating them, etc. It could get messy. Please read my edited answer for some other options. – jesse_galley Jun 14 '14 at 20:40
  • @jesse_galley Wow, thank you for all that :) I will experiment some and find the best way for my site.. Keeping in mind what you say. It's going to be a pretty small and simple site to start with, but that could easily change with time. So it's good to be ready for that. But the good thing about define, is that all I have to do is define it, and then I can echo it with just '.ROOT_header.' anywhere.. makes it easier to build the pages – mowgli Jun 14 '14 at 20:52
  • If you do insist on using constants like that, at least ensure that you are only parsing the language files that you need based on the users language preferences, and not loading all language files in at run-time, because that would make for a *very* messy situation. – jesse_galley Jun 14 '14 at 21:07
  • I have messed a bit with it, and made what I believe is an ok solution: http://pastebin.com/XL3qSiRF It it's not finished, but it works, is simple and easy to understand for me (classes makes me dizzy ;)) – mowgli Jun 14 '14 at 21:33
  • As to define() on my global site vars, I think I'll just let them be that way. There are not that many.. max 20-30 total (not many more will come), so I guess it's fine. Thanks for help :) – mowgli Jun 14 '14 at 21:40
  • @mowgli If this answered your questions, you should accept it as such so that it no longer appears in the "unanswered" category. – jesse_galley Jun 26 '14 at 17:01
  • The very first paragraph in the answer was mostly useful to me. But thanks for the explanations – mowgli Jul 24 '14 at 21:46
1

Static class variables aren't any better than constants. They're still globally accessible values. There's no real change.

If you want to be improving your style, you should be using dependency injection. This simply means that you pass all variables that a function or class needs into the function/class as parameters. It's that simple, really. If you want to decouple your code, you need to create borders between different pieces. That means one piece does not "reach out" and get a global variable; instead you define that piece as accepting a parameter and write another piece that passes it that parameter.

Please read How Not To Kill Your Testability Using Statics for an in-depth explanation of this topic.

deceze
  • 510,633
  • 85
  • 743
  • 889
-1

Declaring your properties as public allows for their modification.

If you want them to be constants, as they were when created with define, you'll have to declare them as protected and use methods to access them :

class ROOT {
    protected $prefix = 'mysitename_';
    protected $support = 'support@mysite.com';

    public static getPrefix(){
        return $this->prefix;
    }
    public static getSupport(){
        return $this->getSupport;
    }
}

This way is actually quite better than using define() actually.

It's a step forward to singleton patterns (http://en.wikipedia.org/wiki/Singleton_pattern).

Next step is the building of an Application class (ROOT name sounds fine) which would contain these constants, and perhaps load them from a configuration file.

In this Application singleton, you can build some main function, like an init() for a bootstrap, inclusion of other classes, database configuration, logging system, templating system, and so on...

Loïc
  • 11,804
  • 1
  • 31
  • 49
-1

You may set variable to global when you need it. Just use global ${$variablename};.

where $variablename contain name of needs variable. For example it may be array keys or values.

CnapoB
  • 665
  • 1
  • 9
  • 16