3

I have a filter for the Gallery Shortcode where I grab IDs for images used in a post and using the IDs I grab their meta data. Now I need a way to use the $copyright variable in a filter for grabbing post_content so I can echo the content of that variable.

Is there a way to reuse a variable in different filter or maybe there is a better way for what I am trying to accomplish?

function wpse_get_full_size_gallery_images( $gallery, $post, $galleries ) { 
    $ids        = explode( ',', $galleries['include'] );
    $copyright  = array();

    foreach( $ids as $id )
        $copyright[] = get_post_meta( $id, 'wiki_copyright', true );

    return $gallery;
}
add_filter( 'shortcode_atts_gallery', 'wpse_get_full_size_gallery_images', 10, 3 );

function my_the_content_filter( $content ) {
    //pass here the copyright array and append it at the end
    $content .= $copyright;

    return $content;
}
add_filter( 'the_content', 'my_the_content_filter', 20 );
  • This is more of a PHP issue than a WordPress one. Really you would need to define some kind of Global Variable to access from function to function. – Howdy_McGee Dec 29 '15 at 19:47
  • @Howdy_McGee Do not globalize a variable, it is just bad practice and will have you chasing ghosts when something breaks that variable. There are much better ways to achieve this – Pieter Goosen Dec 30 '15 at 3:31
5

This is probably a good case for a global variable. Just make sure it is set to an array before you add to it and also when to attempt to output the content. Convert the array to a string before adding to content.

Also, make sure the meta data exists before adding to the array -- that way you're not adding empty copyright values.

GLOBAL VERSION

function wpse_get_full_size_gallery_images($gallery, $post, $galleries) {

    // reference the global variable
    global $copyright;

    // make sure it's set
    if( ! isset($copyright)) $copyright = array();

    // grab the IDS
    $ids = explode(',', $galleries[ 'include' ]);

    foreach($ids as $id) {
       // get the possible meta
       $meta = get_post_meta($id, 'wiki_copyright', true);

       // add them to the copyright array   
       // make sure to only add if it exists
       if( !empty($meta)) $copyright[] = $meta;
    }

    return $gallery;
}

add_filter('shortcode_atts_gallery', 'wpse_get_full_size_gallery_images', 10, 3);

function my_the_content_filter($content) {

    // reference the global variable
    global $copyright;

    // add copyright array converted to string if we have content
    if( ! empty($copyright)) $content .= implode(',', $copyright);

    return $content;
}

add_filter('the_content', 'my_the_content_filter', 20);

STATIC VERSION

If you don't like to use globals, for whatever reason, then consider a static as an alternate variable scope in PHP.

Another important feature of variable scoping is the static variable. A static variable exists only in a local function scope, but it does not lose its value when program execution leaves this scope.

Since the persistent variable is tied to a single function scope we'll need to create the function outside any loops then reuse it for adding values in addition to retrieving those values.

If you pass a new value; add it. If you don't pass a value; get all values.

if( ! function_exists('copyrights')) {
    function copyrights($value = '') {

        // only this function has access to $_copyrights
        static $_copyrights;

        // expressions can't be assigned as defaults
        // but we still want to initialize the value
        if( ! isset($_copyrights)) {
            $_copyrights = array();
        }

        // if we're passing new values then add them
        if( ! empty($value)) {
            $_copyrights[] = $value;

            return true; // success
        }
        else {
            // otherwise return uniques
            $unique_list = array_unique($_copyrights);

            // convert to string before they leave
            return implode(", ", $unique_list);
        }
    }
}

add_filter('shortcode_atts_gallery', function($gallery, $post, $galleries) {
    $ids = explode(',', $galleries[ 'include' ]);
    foreach($ids as $id) 
        if( ! empty($meta = get_post_meta($id, 'wiki_copyright', true)))
            copyrights($meta);

    return $gallery;
}, 10, 3);

add_filter('the_content', function($content) {
    return $content . copyrights();
}, 20);

TESTING

copyrights ( 123 );
copyrights ( 223 );
copyrights ( 333 );
copyrights ( 123 );
copyrights ( 111 );
copyrights ( 111 );
copyrights ( 111 );

echo ( copyrights() ); // 123, 223, 333, 111

SINGLETON VERSION

I actually never use globals because a Singleton Pattern is my preferred method.

The idea is that you put all your methods on an instance of a class but only access it through a static method. By doing so, the class ensures there is only ever one single instance created.

if( ! class_exists('Copyrights')) {
    class Copyrights {

        private        $copyrights;
        private static $instance;

        // PRIVATE CONSTRUCTOR

        private function __construct() {

            // initialize the array
            $this->copyrights = array();
        }

        // STATIC SINGLETON

        public static function getInstance() {
            if(is_null(self::$instance)) {
                self::$instance = new self();
            }

            return self::$instance;
        }

        // PUBLIC METHODS

        public function toString() {
            $unique_list = array_unique($this->copyrights);

            return implode(", ", $unique_list);
        }

        public function add($value = '') {
            if( ! empty($value)) {
                $this->copyrights[] = $value;
            }
        }
    }
}

add_filter('shortcode_atts_gallery', function($gallery, $post, $galleries) {

    $ids = explode(',', $galleries[ 'include' ]);

    foreach($ids as $id)
        if( ! empty($meta = get_post_meta($id, 'wiki_copyright', true))) 
            Copyrights::getInstance()->add($meta);

    return $gallery;
}, 10, 3);

add_filter('the_content', function($content) {
    return $content . Copyrights::getInstance()->toString();
}, 20);

TESTING

Copyrights::getInstance()->add ( 123 );
Copyrights::getInstance()->add ( 223 );
Copyrights::getInstance()->add ( 333 );
Copyrights::getInstance()->add ( 123 );
Copyrights::getInstance()->add ( 111 );
Copyrights::getInstance()->add ( 111 );
Copyrights::getInstance()->add ( 111 );

echo Copyrights::getInstance()->toString(); // 123, 223, 333, 111

SINGLETON PER POST VERSION

Maybe the single most important aspect that has been overlooked in the debate of global vs. the sky is falling is the solution above (and alternates proposed) only cater to a single post. If you wanted to add your copyrights in a loop you would be screwed because all posts would be added to a single array.

With the singleton class, adding a per ID singleton is cake and arguably the best possible option.

if( ! class_exists('Copyrights')) {
    class Copyrights {

        private static $instance;
        private        $copyrights;
        public         $post_id;

        // PRIVATE CONSTRUCTOR

        private function __construct($post_id) {

            // save the id
            $this->post_id = $post_id;

            // initialize the array
            $this->copyrights = array();
        }

        // SINGLETON

        public static function getInstance($post_id = 'NO ID') {
            if(is_null(self::$instance)) self::$instance = array();
            if(is_null(self::$instance[ $post_id ])) {
                self::$instance[ $post_id ] = new self($post_id);
            }

            return self::$instance[ $post_id ];
        }

        // PUBLIC METHODS

        public function toString() {
            $unique_list = array_unique($this->copyrights);

            return implode(", ", $unique_list);
        }

        public function add($value = '') {
            if( ! empty($value)) {
                $this->copyrights[] = $value;
            }
        }
    }
}

add_filter('shortcode_atts_gallery', function($gallery, $post, $galleries) {

    $ids = explode(',', $galleries[ 'include' ]);

    foreach($ids as $id)
        if( ! empty($meta = get_post_meta($id, 'wiki_copyright', true)))
            Copyrights::getInstance($post->ID)->add($meta);

    return $gallery;
}, 10, 3);

add_filter('the_content', function($content) {
    global $post;
    return $content . Copyrights::getInstance($post->ID)->toString();
}, 20);

TESTING

As you can see, having the the option to use ID as a key keeps things flexible.

Copyrights::getInstance( 1 )->add ( 123 );
Copyrights::getInstance( 1 )->add ( 223 );
Copyrights::getInstance( 2 )->add ( 333 );
Copyrights::getInstance( 2 )->add ( 123 );
Copyrights::getInstance( 2 )->add ( 111 );
Copyrights::getInstance( 2 )->add ( 111 );
Copyrights::getInstance( 2 )->add ( 111 );
Copyrights::getInstance()->add ( 111 );
Copyrights::getInstance()->add ( 444 );
Copyrights::getInstance()->add ( 555 );
Copyrights::getInstance()->add ( 888 );

echo Copyrights::getInstance( 1 )->toString(); // 123, 223
echo Copyrights::getInstance( 2 )->toString(); // 333, 123, 111
echo Copyrights::getInstance()->toString(); // 111, 444, 555, 888

At the point that someone thinks it's a good idea to store a temporary value in the database because they don't understand PHP then it's a good time to rethink the problem.


UPDATE

I solved the question using a global variable but rethinking the issue you can do away with a lot of complexities by justing using the content filter hook alone -- which keeps the issue localized.

In this case, when the_content runs, we check for all galleries and output only the relevant data.

No need for globals, statics, singletons, options or extra functions.

Best of all, this will work in a loop without complication.

add_filter('the_content', 'add_copyright_to_content');

function add_copyright_to_content($content) {

    //  pull all galleries from the post
    $galleries = get_post_galleries(get_post(), false);

    //  no gallery? then let's get out of here
    if(empty($galleries)) return $content;

    $all = '';

    //  gather all ids
    foreach($galleries as $gallery) $all .= $gallery[ 'ids' ] . ',';

    //  convert them to an array with unique ids
    $all = array_unique(array_filter(explode(',', $all)));

    //  replace id for copyright info on the image meta
    foreach($all as $key => $id) $all [ $key ] = get_post_meta($id, 'wiki_copyright', true);

    //  return non-empty values to the end of the content
    return $content . implode(', ', array_filter($all));
}
3

...maybe there is a better way for what I am trying to accomplish?

There are always extra options to take into consideration. Globalizing a variable is certainly not one of them and you should avoid doing that. Globalizing is an easy way out to solve any issue, but believe me you'll spend days debugging an issue which does not give you any debugging errors when something breaks your global.

WordPress alreay made a huge mess with globals. Just go through questions and answers on this site and check how many are using the globals $wp_query, $posts and $post as local variables.

I personally use $copyright as a variable to hold date info. If we globalize $copyright with a string of id's as in your case, either my variable is going to break the global or the global is going to break my variable. Worst is, I would not get any debug errors and I would not know where my values are changed. So never ever globalize variables, IMHO, it is really bad practice as there are many other options

A quick option that comes to mind is storing your data with the Options API. It is easy and does not require huge overheads as options are cached, and they are save to use.

Lets try the following: (NOTE: All code is untested so make sure to test this on local install first. The code also requires PHP5.4+)

add_filter( 'shortcode_atts_gallery', function ( $gallery, $post, $galleries ) 
{ 
    // Get all the ids
    $ids = $galleries['include'];
    // Instead of overloading the shortcode, lets just save the id's for later use
    if ( !$ids ) // Always make sure we have values
        return $gallery;

    // Add our ids in an option
    $option = get_option( 'custom_copyright_ids' );
    if (    false !== $option // Make sure that the option does not yet exist
         || $option !== $ids // We only want the update if $id's change
    ) {
        // Lets update or add our option
        update_option( 'custom_copyright_ids', $ids );
    }

    return $gallery;
}, 10, 3 );

We can nowcreate a "global" function which we can carry around and use (safely) where we want to use it. (The function below is static, you can make it dynamic)

function get_global_copyright_ids()
{
    $output = '';

    // Get our ids
    $ids = get_option( 'custom_copyright_ids' );

    // Make sure we have a value for $option, either return an empty string
    if ( false === $ids )
        return $output;

    // We have ids, lets continue
    // We expect $ids to be an array, so lets turn the string into an array
    $ids = explode( ',', trim( $ids ) );

    foreach ( $ids as $id ) {
        // Get our post meta
        $meta = get_post_meta( $id, 'wiki_copyright', true );
        if ( !$meta )
            continue;
        // We have meta, add it to our $output string. Add any markup here
        $output .= $meta
    } // endforeach $ids

    // Return our string of post meta
    return $output;
}

We can now use get_global_copyright_ids() anywhere where we want to

add_filter( 'the_content', function ( $content )
{
    // Get our content to add from our "global" function
    $extra = '';
    if ( function_exists( 'get_global_copyright_ids' ) ) {
        $extra = get_global_copyright_ids();
    }

    if ( !$extra )
        return $content;

    // Append $extra to $content
    return $content . $extra;
}):

And there you have a much better and safer system which is really easy to debug and best of all YOU KEPT THE GLOBAL SCOPE CLEAN!!!

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.