How to set and use global variables? Or why not to use them at all

  • 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.

    Which link does this statement echo esc_url( $category_link_prop ); displays ? What is your expected link?

    Why would you not just use 'get_cat_ID( **** )' where ever you planned to use the global variable. I doubt there would be any speed advantage the way your doing it. From a readability standpoint, 'get_cat_ID( **** )' wins hands down.

    Can you reword? I read your question and I'm still unsure of what you want to do and why you want to do it. My general advice would be to not use global variables, and not to pollute the global scope

    @VinodDalvi I was hoping to get the link to the category with the slug proposal'.

    @TomJNowell I will edit in a minute to reflect that I'm using this for navigations, such they will be used on pretty much any page anyway.

    @ChrisStrutton get_cat_id() doesn't five me everything I need (ie the link, the title, etc.). Readability isn't a huge concern for me, I will be the only oen reading this.

    this is sounding a bit like an X/Y Problem. perhaps you should back up and explain exactly what your desired outcome is. I'm certain there's a far more elegant solution than setting a bunch of global vars to then just hardcode references to them in a nav elsewhere

    @Milo Excellent point. My actual problem is my theme has 4 different navigations (so far.) 1 that is a standard top bar, and 3 that only display based on certain conditions. They all show different combinations of roughly the same things and are in arbitrary orders and are going to change as the project goes on. My problem is if I hard-code them, I will have to hardcode the same thing over and over again, and then change the same thing 4 times every time something changes.

    create a function that outputs your menu based on the context you pass to it, that way you can keep all of the menu logic and associated vars encapsulated in one place.

    @Milo That's what I started with, but know I have one function for top bar navigation; one function for a side bar nav which just grew to 4 different versions based on conditionals; and one function for a sub-nav in a page template and there are going to be more of those as this goes on. I can't come up with a sane way of combining all of those into one function.

    @Milo Also I want to be able to use these as links to various parts of the site in the text of the site, either in posts (if that works) or in page templates.

  • 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.

    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 `` as per your suggestion. Thanks!

    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

    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...

    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

    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.)

    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

    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.

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM