By JPollock


2013-03-04 06:23:04 8 Comments

UPDATE: My original question has been solved, but this is turning into a valid discussion about why not to use global variables, so I am updating the question to reflect that. The solution was <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?> as @TomJNowell suggested.

UPDATE 2: I now have it doing exactly what I wanted. But I'm still using global scope and would be happy to find a better way.

I am trying to set up a whole bunch of global variables for the permalinks to categories to be used in various places in my theme. The main reason for this is for use in both the main navigation, as well as in a series of sub navigations that are chosen based on what category the current post is in. This is not a theme I will be releasing for use by others, but is built for one very specific purpose.

This is how I am currently creating them (I've only pasted in a few of the variables).

function set_global_nav_var()
{
    //proposal
    global $prop;
    // Get the ID of a given category
    $category_id_prop = get_cat_ID( 'proposal' );
    // Get the URL of this category
    $category_link_prop = get_category_link( $category_id_prop );
    $prop = '<a href="' .esc_url( $category_link_prop ). '" title="Proposal">Proposal</a>';

    //Calvinball
    global $cb;
    // Get the ID of a given category
    $category_id_cb = get_cat_ID( 'calvinball' );
    // Get the URL of this category
    $category_link_cb = get_category_link( $category_id_cb );
    $cb = '<a href="' .esc_url( $category_link_cb). '" title="Calvinball">Calvinball</a>';
}
add_action( 'init', 'set_global_nav_var' );

I can now do <?php global $prop; echo $prop; ?> int he 4 places that goes and get back the whole link for the code. When that changes I only need to change it in one place. I'm open to alternatives that do not involve the global scope.

4 comments

@Tom J Nowell 2013-03-04 13:05:02

While I strongly advise against this, and it will not speed things up, your usage is incorrect.

WordPress already caches these things in the object cache, you don't need to store the result and reuse, WP does that already.

It's very likely your code is running slower as a result of this micro-optimisation, not faster!

How To Use Globals

When you try to use a global you must specify the global keyword first. You have specified it here when defining its value, but outside of that scope it needs to be redeclared as a global scope variable.

e.g. in functions.php :

    function test() {
        global $hello;
        $hello = 'hello world';
    }
    add_action( 'after_setup_theme', 'test' );

In single.php, this will not work:

    echo $hello;

Because $hello is undefined. This however will work:

    global $hello;
    echo $hello;

Of course you should do neither. WordPress already attempts to cache these things in the object cache.

Disadvantages and Dangers of Global Variables

You will see no speed increase from doing this ( you may see a tiny speed decrease ), all you will get is additional complexity and the need to type out a lot of global declarations that aren't necessary.

You'll also encounter other issues:

  • code that's impossible to write tests for
  • code that behaves differently every time it runs
  • clashes in variable names from a shared name space
  • accidental bugs from forgetting to declare global
  • a complete lack of structure to your codes data storage
  • and many more

What Should You Use Instead?

You would be better off using structured data, such as objects or dependency injection, or in your case, a set of function.

Static Variables

Static variables aren't good, but think of them as the slightly less evil cousin of global variables. Static variables are to global variables, what mud covered bread is to cyanide.

For example, here is a means of doing something similar via static variables e.g.

    function awful_function( $new_hello='' ) {
        static $hello;
        if ( !empty( $new_hello ) ) {
            $hello = $new_hello;
        }
        return $hello;
    }

    awful_function( 'telephone' );
    echo awful_function(); // prints telephone
    awful_function( 'banana');
    echo awful_function(); // prints banana

Singletons

Singletons are like static variables, except the class contains a static variable with an instance of that class. They're just as bad as global variables, just with different syntax. Avoid them.

WP_Cache, The Thing You Tried to Do But WP Already Does It

If you really want to save time by storing data somewhere to re-use, consider using the WP_Cache system with wp_cache_get etc e.g.

$value = wp_cache_get( 'hello' );
if ( false === $value ) {
    // not found, set the default value
    wp_cache_set( 'hello', 'world' );
}

Now the value will get cached for the life of the request by WordPress, show up in debugging tools, and if you have an object cache it'll persist across requests


Sidenote 1: I would note, that some people try to persist data in global variables across requests, unaware that this is not how PHP works. Unlike a Node application, each request loads a fresh copy of the application, which then dies when the request is completed. For this reason global variables set on one request do not survive to the next request

Sidenote 2: Judging from the updated question, your global variables give you no performance gain at all. You should just generate the HTML as and when you need it and it would run just as fast, perhaps even a tiny bit faster. This is micro-optimisation.

@JPollock 2013-03-05 00:34:04

I know it's a little nuts to use the global scope, but most, if not all of these variables will be used on every page. I'm open to better ideas. I am going to edit the question to make my intent a little clearer. BTW it works perfectly fine when I do <?php global $category_link_prop; echo esc_url( $category_link_prop ); ?> as per your suggestion. Thanks!

@Tom J Nowell 2013-03-05 09:59:13

Ah if my solution works, could you mark as accepted? Your global variables are just as fast as making the original call, you may want to try instead using functions so you don't need to type out 2 lines, better yet, a singleton, better yet, make all of that dynamic and in a template part included via get_template_part

@JPollock 2013-03-06 10:12:12

Marked as accepted as its what I am doing now though I may go with one of the strategies @MarkKaplun is suggesting below. Using get_template_part() is an interesting idea, but I'm not sure I want to have a dir full of short files like that...

@Tom J Nowell 2013-03-06 10:45:46

oooh no no you wouldn't want a file for each category, you'd want just the one that grabs the current category name and uses that. You shouldn't have to hardcode anything, imagine the hassle of hardcoding it all

@ycc_swe 2018-06-03 14:37:02

I put the code in my child-functions.php which is active. But I can not access the variable in a php-include file I call from a "normal" database-generated post. Please advise me, what do I do wrong? (I define it as global, of course.)

@Tom J Nowell 2018-06-03 16:48:58

You have to declare global every time you use it. It isn't a use once works everywhere affair, you have to use it each, and every, single time, no exceptions of any kind. But as I say in my question, global variables are bad practice, problematic, and not the solution you're looking for to your problem. Even the evil that is singletons would be a better solution

@ycc_swe 2018-06-03 17:20:09

Thank you, I appreciate your comment. I declared it and used it but somewhere I must have made a mistake. It just didn't work for me. I tried different approaches. Finally this one worked. I agree with what you say on globals, but sometimes a quick fix is needed for a personal site. Thanks.

@jgraup 2015-11-25 10:35:03

You can always use a singleton pattern via static getters.

<ul>
    <li><?php echo MyGlobals::get_nav_prop( 'proposal' )[ 'html' ]; ?></li>
    <li><?php echo MyGlobals::get_nav_prop( 'calvinball', 'html' ); ?></li>
</ul>


<?php

if ( ! class_exists('MyGlobals') ):

class MyGlobals {

    public $props;

    public function __construct(){
      $this->props = array (
        'proposal' => array( 'title' => 'Proposal', 'text' => 'Proposal' ),
        'calvinball' => array( 'title' => 'Calvinball', 'text' => 'Calvinball' ),
      );
    }

    public function get_nav_prop ( $term, $prop = false )
    {
      $o = self::instance();
      if ( ! isset( $o->props[$term] ) ) {  return falst; }
      if ( ! isset( $o->props[$term][ 'html' ] ) ) {
          $id = get_cat_ID( $term );
          $link = esc_url ( get_category_link( $id ) );
          $title = $o->props[$term]['title'];
          $text = $o->props[$term]['text'];
          $o->props[$term]['html'] = '<a href="'.$link.'" title="'.$title.'">'.$text.'</a>';
          $o->props[$term]['link'] = $link;
          $o->props[$term]['id'] = $id;
      }

      if($prop){ return isset($o->props[$term][$prop]) ? $o->props[$term][$prop] : null; }

      return $o->props[$term];
    }

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

    private static $_instance;

    public static function instance(){

      if(!isset(self::$_instance)) {
        self::$_instance = new MyGlobals();
      }
      return self::$_instance;
    }

}

endif; // end MyGlobals

@Mark Kaplun 2013-03-04 07:09:37

Don't use global variables, as simple as that.

Why not to use globals

Because the use of globals makes it harder to maintain the software in the long term.

  • A global can be declared anywhere in the code, or nowhere at all, therefor there is no place in which you can instinctivly look at to find some comment about what the global is used for
  • While reading code you usually assume that variables are local to the function and don't understand that changing their value in a function might have a system wide change.
  • If they don't handle input, functions should return the same value/output when they are called with the same parameters. The use of globals in a function introduce additional parameters which are not document in the function declaration.
  • globals don't have any specific initialization construct and therefor you can never be sure when you can access the value of the global, and you don't get any error when trying to access the global before initialization.
  • Someone else (a plugin maybe) might use globals with the same name, ruining your code, or you ruining its depending on initialization order.

WordPress core has way way way much to much use of globals. While trying to understand how basic functions like the_content work, you suddenly realize that the $more variable is not local but global and need to search whole of the core files to understand when is it set to true.

So what can be done when trying to stop copy&pasting several lines of code instead of storing the first run result in a global? There are several approaches, functional and OOP.

The sweetener function. It is simply a wrapper/macro for saving the copy/paste

// input: $id - the category id
// returns: the foo2 value of the category
function notaglobal($id) {
  $a = foo1($id);
  $b = foo2($a);
  return $b;
}

The benefits are that now there is a documentation to what the former global does, and you have an obvious point for debugging when the value being returned is not the one you expect.

Once you have a sweetener it is easy to cache the result if needed (do it only if you discover that this function takes a long time to execute)

function notaglobal($id) {
  static $cache;

  if (!isset($cache)) {
    $a = foo1($id);
    $b = foo2($a);
    $cache = $b;
  } 
  return $cache;
} 

This gives you the same behavior of a global but with the advantage of having an assured initialization every time you access it.

You can have similar patterns with OOP. I find that OOP usually doesn't add any value in plugins and themes, but this is a different discussion

class notaglobal {
   var latestfoo2;

   __constructor($id) {
     $a = foo1($id);
     $this->latestfoo2 = foo2($a)
   }
}

$v = new notaglobal($cat_id);
echo $v->latestfoo2;

This is a clumsier code, but if you have several values that you would like to precompute because they are always being used, this can be a way to go. Basically this is an object that contain all of your globals in an organized way. To avoid making an instance of this object a global (you want ont one instance otherwise you recompute the values) you might want to use a singleton pattern (some people argue it is a bad idea, YMMV)

I don't like to access an object attribute directly, so in my code it will warpe some more

class notaglobal {
   var latestfoo2;

   __constructor() {}

   foo2($id) {  
     if (!isset($this->latestfoo2)) {    
       $a = foo1($id);
       $b = foo2($a);
       $this->latestfoo2= $b;
     } 
     return $this->latestfoo2;
   }
}

$v = new notaglobal();
echo $v->foo2($cat_id);

@brasofilo 2013-03-04 08:04:23

Please, don't shout. Mind to explain why and provide some kind of citation?

@Mark Kaplun 2013-03-04 12:11:43

I think that you misunderstood the answer. If he wasn't trying to do early optimization by storing values in global variables his code would have worked. The shouting is because following basic established software development principles is something that can't be emphasized enough. People who do not understand those basic principle (available at your local google) should not spread code over the net.

@brasofilo 2013-03-04 12:37:46

Hi, Mark, apologies, my Comment was as short as your Answer and I've should made myself clearer: 1) IMO, bold is enough to make a point. 2) Although sometimes there's nothing more to say, I suspect of one line Answers: Is it okay to post a one-line answer, or would those be better as comments?

@Mark Kaplun 2013-03-04 12:40:41

yeh, only after I have posted I realized I should have used bold. will fix that asspect

@Mark Kaplun 2013-03-04 12:43:47

IMO this is an answer, people who come here form google should see that it is a bad idea to even think about using globals right away.

@Tom J Nowell 2013-03-04 13:00:22

It's not enough to say dont do X, you have to explain why or you look like you're saying it on a whim

@JPollock 2013-03-05 00:53:16

@MarkKaplun What would you do instead to avoid having to write the same set thing over and over again, and then have to change each one manually if any part of it changes?

@Mark Kaplun 2013-03-05 07:28:05

@JPollock, edited the answer.

@Mark Kaplun 2013-03-05 08:27:33

@TomJNowell, I find it funny that I was the only one downvoting the question itself, as it was obviously outside of the scope of WASE. I didn't see the value of expanding on a subject which should not have been started here at all.

@JPollock 2013-03-06 07:44:16

@MarkKaplun Awesome. Your first solution sounds like the best. I will experiment with caching, which will probably be necessary. I'm not sure why this is outside the scope of this stackexchange? The question is about PHP, but it turns out it has everything to do with how WordPress deals with globals. Also the case is specific to WordPress navigation menus.

@R Porter 2014-04-02 13:35:14

"WordPress core has way way way much to much use of globals." I'd say Wordpress having any globals at all is way too many, but that's just me.

@klewis 2016-10-21 15:25:53

@MarkKaplun thank you for your functional approach. In the event that we take it, could you make an update to show us how it should look like with a fallback value of $ID, if for some reason it didn't exist, was not set or was not a positive integer?

@Joel M 2018-02-18 06:15:10

globals can often be bad for many reasons but so are people who say never use globals.

@Jesse 2013-03-04 09:04:33

Your question is involved with how php works.

Take $wpdb as example

$wpdb is a well-known global variable.

Do you know when it'll be declared and assigned with values ?

Every page loaded, yep, every time you visit your wordpress site.

Similarly, you need to make sure those variables that you want to be globalized will be declared and assigned with corresponding values every page loaded.

Although I'm not a theme designer, I can tell the after_setup_theme is one time hook. it'll only be triggered when theme activated.

If I were you, I'll use init or other hooks. No, if I were you, I won't use global variables at all...

I'm really not good at explaining things. So, you should pick up a book if you want to delve into PHP.

Related Questions

Sponsored Content

2 Answered Questions

[SOLVED] Create a global variable for use in all templates

2 Answered Questions

3 Answered Questions

[SOLVED] How to: inspect global variables in WordPress

  • 2011-03-25 14:02:31
  • kaiser
  • 6603 View
  • 22 Score
  • 3 Answer
  • Tags:   globals

2 Answered Questions

[SOLVED] Override global query results without hooks

1 Answered Questions

[SOLVED] Should we trust the post globals?

2 Answered Questions

[SOLVED] When to use global $post and other global variables?

  • 2014-12-05 09:44:43
  • alemur
  • 25125 View
  • 6 Score
  • 2 Answer
  • Tags:   variables globals

1 Answered Questions

[SOLVED] How to set global variables in template page?

1 Answered Questions

[SOLVED] get_posts() and global variables

Sponsored Content