Access variable in defined in a function outside of that function - php

Firstly: I know this has been asked before, but I've looked through a lot of questions/answers and I can't work out how to get any of them working. So, sorry about that.
So basically I'm writing some functions/shortcodes in Wordpress that mean that I can use a shortcode to publish Vine videos to a Wordpress blog:
function embedVine($atts) {
extract(shortcode_atts(array(
"id" => ''
), $atts));
$vine_id = $id;
// I'm then doing a whole load of stuff involving $vine_id, including using it as a parameter to pass to different functions I've written that are separate to this function.
// I should also mention that $vine_id is the id on the end of a Vine URL.
} add_shortcode("vine", "embedVine");
A user can then use the [vine id="..."] shortcode in the Wordpress editor.
I then have a function I've written but I don't want to execute it inside of the function above, otherwise it will run every time the function/shortcode runs which wouldn't be good. I instead need to execute it outside of the function but still use $vine_id as a parameter for it. However, as $vine_id was defined inside the function above, I cannot access it outside of the function.
Here is the second function:
function vineThumb($id) {
$vine = file_get_contents("http://vine.co/v/{$id}");
return $vine;
// of course, it's a lot more complicated than this but for the sake of this, it works.
} vineThumb($vine_id);
Executing the function would return http://vine.co/v/{$vine_id}. How would I make the $vine_id function accessible outside of the shortcode function (the first one)?
Hope I've explained that clearly enough, I'm not a PHP programmer as you can probably tell but I know enough to get by. This sort has me stumped though. Thanks for any help :)

I don't know if those two functions are located in the same file but try this. http://www.php.net/manual/en/language.variables.scope.php
$vine_id = null; //Define it outside the function
function embedVine($atts) {
global $vine_id; //Refers to $vine_id outside the function
extract(shortcode_atts(array(
"id" => ''
), $atts));
$vine_id = $id; //$vine_id now has the value of $id when embedVine exits
} add_shortcode("vine", "embedVine");

Related

wordpress get variable from empty do_action

/* I cant change this part (plugin core files) */
function test123(){
$secret = "hoho"; //i want to get this variable
do_action('custom'); //developer didnt pass any variable here
}
/* I cant change this part (plugin core files) */
add_action('custom',function() use ( $secret ) {
echo $secret; //didn't work
});
test123();
How to get $secret value? because the developer didnt pass any variable when using do_action
You can use like this:
function test123() {
$secret = 'hoho';
do_action( 'custom', $secret );
}
add_action('custom',function( $secret ) {
echo $secret;
});
test123();
Hope this works!
Your variable is out of scope
function test123(){
$secret = "hoho"; //i want to get this variable
do_action('custom'); //developer didnt pass any variable here
} //end of function scoope
add_action('custom',function() use ( $secret ) { //<-- $secret is undefined
echo $secret; //didn't work
});
There are several ways to fix this, but I cant really say how with so little context.
You could move add_action into the function
You could make $secret global etc.
modify the do action call to send it
make it a constant
make it global (keyword)
make it a property of class that maintains it's state (static etc.)
And so on, it depends how dynamic that value needs to be. Maybe that value is a static string, maybe it only exists in your function etc.
How many times you call that function may affect defining it inside, (add/remove action).
You can test this easly:
ini_set('display_errors', 1);
error_reporting(E_ALL);
add_action('custom',function() use ( $secret ) {
echo gettype($secret); //didn't work
});
Expected output
<br />
<b>Notice</b>: Undefined variable: secret in <b>...</b> on line <b>...</b><br />
NULL
UPDATE
I can't change test123()
I sort of figured that, which is why I only gave general solutions. In any case if it's just a plain old local variable within that function, there is no way to "get" its value outside of it short of editing the function in some way. This is a PHP thing (a language thing), not a WordPress thing. It has to do with how scope is handled within the function.
It may be possible to get that data some other way, that depends what it is. For example is it stored in the DB? You may be able to access that way etc... I can't say how to do that without knowing more about what it is.
It's not impossible to edit the original right. Obviously this is not ideal, but it's possible. Some tips are never edit the original. If it's part of a theme try to do it though a child theme. If it's plugin, or child theme doesn't work for it then you can duplicate it and change it's name.
I should mention depending how it's licensed this may be against that license.
You never want to edit the original because if you update the "plugin/theme" you may completely lose your changes. If it's in a copy you can comment around you changes like this:
/* START CUSTOM CODE */
And when you update the original "plugin/theme" you can easily find where your changes where (by searching for that comment text) and work them into the new version of the "plugin/theme". Which may be as simple as coping all the files but the one you changed (if nothing changed in it) etc.
As I said this isn't ideal, but it will work. It just depends how badly you want to implement it and how willing you are to maintain it.
Please see also
https://www.php.net/manual/en/language.variables.scope.php
within user-defined functions a local function scope is introduced. Any variable used inside a function is by default limited to the local function scope

Defining variables in functions.php and access them inside functions hook in Wordpress

I am trying to understand Wordpress structure in more details. As advised to me, using global keyword is not a good idea.
Here is what I am trying to accomplish:
Inside functions.php
$affID = '12334'; //defining a variable
add_shortcode('affiliate_link', function() {
$newLink = 'https://example.com?id=' . $affID;
return $newLink;
}
When I run the shortcode, I only get https://example.com?id= part of it.
Of course, I can pass the $affID as an argument, but I would like to set up a big list of variables that I am going to use inside different functions, and I think it's not a good idea to pass such a huge list of arguments.
Here are just a few Ideas. Really this is a problem of "scope" more than anything else.
USE
Simply make use of the use part of the closure:
$affID = '12334'; //defining a variable
add_shortcode('affiliate_link', function() use ($affID){
$newLink = 'https://example.com?id=' . $affID;
return $newLink;
});
I was actally happy when I seen the closure, this is probably the easiest way.
CLASS
Another way would be to maintain the state of it using a class instance (you could do it static if you really wanted to). This makes sense if you need a class for this functionality or the general affiliate system any way. Then you can just tack this in, with no big deal:
class foo{
protected $affID = '12334';
public function affiliate_link_callback(){
$newLink = 'https://example.com?id=' . $this->affID;
return $newLink;
}
}
//to use a class as a call back it works like call_user_func, [obj, method]
add_shortcode('affiliate_link', [new foo, 'affiliate_link_callback']);
GLOBAL
Least desirable would be to use a global, which I personally detest and never use them (but it is a option, I suppose). Globals are bad because it can be a real challenge to find out (or track) where they were set (or modified) which makes debugging it a real chore.
global $affID;
$affID = '12334'; //defining a variable
add_shortcode('affiliate_link', function(){
global $affID;
$newLink = 'https://example.com?id=' . $affID;
return $newLink;
});
CONSTANT
You can define it as a constant, this only really makes sense if you use it a lot in other parts of your code as it makes maintaining it easier. It's not impossible you would need this ID in several places, and a constant gives you a global way to handle it. It's also conceivable that you wouldn't be modifying this at run time. It's better then a "global" because it can't be changed at runtime, so that negates the problems with keeping track of where it was changed in your code (because it cant be). Of course it has it's own set of limitations because of that. Anyway it's simple to do:
define('AFF_ID','12334'); //defining a variable
add_shortcode('affiliate_link', function(){
global $affID;
$newLink = 'https://example.com?id=' . AFF_ID;
return $newLink;
});
DATABASE
And last depending on what $affID is you may be able to store it in wordpress DB. I would probably go for the user meta so it's linked to a users account. But, if it's a global value for the site, you could use the the options setting stuff instead (I would have to look that up though, example). Don't quote me on the code for this one as I forget exactly what get_user_meta returns, but I know you should almost always return a single value (the third arg should be true)
add_shortcode('affiliate_link', function(){
$meta = get_user_meta(get_current_user_id(), 'affiliate_id', true);
$newLink = 'https://example.com?id=' . $meta['affiliate_id'];
return $newLink;
});
Of course in this last example you have to save the data at some point, but I am sure you can figure that out.
SHORTCODE ATTR
I think you mentioned this in the question, but you can send it as part of the shortcode too:
add_shortcode('affiliate_link', function($attr){
extract(shortcode_atts( array(
'id' => false,
), $atts, 'affiliate_link' ));
if(!$affiliate_id) return '';
$newLink = 'https://example.com?id='.$id;
return $newLink;
});
I used extract above, which is a fun little function PHP has. I wouldn't recommend it for everything as it can pollute your variables and there are some security concerns.
http://php.net/manual/en/function.extract.php
Import variables from an array into the current symbol table.
Checks each key to see whether it has a valid variable name. It also checks for collisions with existing variables in the symbol table.
Warning
Do not use extract() on untrusted data, like user input (e.g. $_GET, $_FILES).
Basically it takes ['id'=>'12345'] and makes a variable named $id that has 12345 as it's value (in this example). You don't have to use it, and I generally avoid it. But I thought it would be fun to use in this example.
Other Thoughts
One additional thought is that I would return the link in it's entirety instead of just the href. So instead of doing this
Link
You would do this:
[affiliate_link]Link[/affiliate_link]
// [affiliate_link id="12345" ]Link[/affiliate_link]
The main reason is in the first case, if your shortcode fails it will leave a useless link on the page. A user won't be able to tell this unless they pay close attention to the actual link destination. However, if you generate teh entire link and it fails, nothing gets put on the page. This also works nice for users that don't have an affiliate id (if you use the user meta example, and it's linked to that). In this case nothing would show for the link if they don't have the affiliate Id, but if they do the link would show. As I mentioned if you put the link in the post and then they didn't have it, you would still get a link, but with an empty destination (hope that makes sense).
If you do that then I would add the content in as well, for the link text. Using the last example (above):
add_shortcode('affiliate_link', function($attr, $content=''){
extract(shortcode_atts( array(
'id' => false,
), $atts, 'affiliate_link' ));
if(!$affiliate_id) return '';
$newLink = ''.$content.'';
return $newLink;
});
//this shortcode
//[affiliate_link id="12345" ]Link[/affiliate_link]
//generates this link
//Link
That said, I have no idea if this can be used only in links, images etc. etc. So I just wanted to add that for the sake of completeness.
Really it depends how you use it, how "static" the ID is. Is it site specific, user specific, can it change dynamically etc... ?
Sorry this was so long, I just recently wrote 4 wordpress plugins that add simple shortcodes ... lol. So I was just recently thinking about them. And I really love shortcodes.
Hope that helps.

How to pass additional parameter to wordpress filter?

add_filter('wp_list_pages_excludes', 'gr_wp_list_pages_excludes');
function gr_wp_list_pages_excludes($exclude_array) {
$id_array=$array('22');
$exclude_array=array_merge($id_array, $exclude_array);
return $exclude_array;
}
I'm a newbie to wordpress. The above code works fine. But I need to pass additional argument, say $mu_cust_arg to the function gr_wp_list_pages_excludes. How can I make use of it via apply_filters, or any other methods?
Any help is appreciated.
Thanks in advance.
You can indeed add multiple arguments to a filter/action, you just need to tell WordPress how many arguments to expect
Example, which won't work:
add_filter('some_filter', function($argument_one, $argument_two) {
// won't work
});
apply_filters('some_filter', 'foo', 'bar'); // won't work
It will fail with an error that too many arguments was provided.
Instead, you need to add this:
add_filter('some_filter', function($argument_one, $argument_two) {
// works!
$arugment_one; // foo
$arugment_two; // bar
}, 10, 2); // 2 == amount of arguments expected
apply_filters('some_filter', 'foo', 'bar');
Because WP doesn't accept closures as callbacks (at least, certainly not for add_filter()) the short answer is "you can't". At least, not in a tidy way.
There are a couple of options here, depending on what you are doing. The first is the best, but you may not be able to use it:
Write a wrapper function that calls your function:
function gr_wp_list_pages_excludes_1 ($exclude_array) {
$custom_arg = 'whatever';
gr_wp_list_pages_excludes_1($exclude_array, $custom_arg)
}
This will only work if you are always passing the same custom argument in a given situation - you would write one of these wrapper functions for each different situation, and pass the name of the wrapper function to add_filter(). Alternatively, if you want it to be truly dynamic, you would need to...
Use a global variable: (Ref: Variable scope, $GLOBALS)
function gr_wp_list_pages_excludes($exclude_array) {
global $gr_wp_list_pages_excludes_custom_arg;
$id_array=$array('22');
$exclude_array=array_merge($id_array, $exclude_array);
return $exclude_array;
}
Using this approach means that you can pass any data you like into the function by assigning it to $gr_wp_list_pages_excludes_custom_arg in the global scope. This is generally regarded as bad practice and heavily frowned upon, because it makes for messy and unreadable code and leaves the memory space littered with extra variables. Note that I have made the variable name very long and specific to the function to avoid collisions - another problem with using global variables. While this will work, only use it if you absolutely have to.
Very simple!
add_filter('filter_name','my_func',10,3); //three parameters lets say..
my_func($first,$second,$third){
//............
}
then
echo apply_filters('filter_name',$a,$b,$c);

Drupal - How can I make an array globally accessible?

I'm using this code in a views field template (in this case views-view-field--all-members--uid.tpl.php):
<?php
$users_friends = flag_friend_get_friends($user->uid);
$users_friends_ids = array();
foreach ($users_friends as $id => $value) {
$users_friends_ids[] = $id;
}
?>
It basically gets the user ids of friends and puts them in an array so I can check if the field matches any of the user ids.
So my problem is that I don't want to have this within this template (for a few reasons), but if I don't I can't access the array. How can I make this array globally accessible?
Without knowing your "few reasons", I can't say if this is the answer for sure. My own reasons would probably be that I don't want the same code executing a bunch of times, and I'd rather not have the same exact code in multiple places.
I would then create a function with a static variable to hold the friends array.
function mymodule_get_friends_ids() {
// pull in the current global user variable
global $user;
// call up the static variable
static $users_friends_ids;
// return if this static var has already been set
if (is_array($users_friends_ids)) {
return $users_friends_ids;
}
// if we hit here, then this function has not been
// run yet for this page load.
// init array
$users_friends_ids = array();
// if user is anon, no need to go on
if (user_is_anonymous()) {
return $users_friends_ids;
}
// get friends array
$users_friends = flag_friend_get_friends($user->uid);
// build ids array
foreach ($users_friends as $id => $value) {
$users_friends_ids[] = $id;
}
return $users_friends_ids;
}
Now in your templates, you can call mymodule_get_friends_ids() in as many places as you want, and the working code below the first return will only get executed the first time it is called.
Coder1's advice is very good - it keeps you from populating your global variable namespace with a lot of junk. It's probably the most "elegant." It might not be the easiest to use if you are rather new to PHP (which I'm guessing might be the case if it's hard to get your head around returning arrays, but that's ok).
However, if this is really a priority, you probably don't care about having one extra global variable.
I suppose I may be stating the obvious here - but you can, at pretty much any point in execution (provided the information you need has already been generated - e.g., the $user variable has been populated), do this:
$GLOBALS['users_friends_ids'] = /* your code goes here */
Then in your template, you access this by ...
$friendsArray = $GLOBALS['users_friends_ids'];
Or you can simply use the construct
global $user_friends_ids;
when you want to initialize the variable, or access it inside a function or class (which is the case for your template files - they are called inside functions, so you need to globalize or use the $GLOBALS array, which is "automagically" all of the variables active in the global namespace).
The most "logical" place to do this would be inside a module using one of the many hooks available, to execute this code only once. hook_init() might do it for you, if the user object is already loaded at this point (not sure, you'll have to test). But you might not want to figure out making Drupal modules (it's not that difficult).
If you are doing this inside a template (and though it's not good practice, many Drupal site owners with a beginning knowledge of PHP put everything in templates), you'll want to know which template code is being executed when. Node template code tends to be executed before page template code - which is logical, since otherwise the variables for node content in the page template wouldn't be populated.
If you have listings of nodes, they'll be calling this code multiple times, so you'll end up doing something similar to what Coder1 is describing. If you don't want to create your own small module, you could put the function declaration he's written in your theme's template.php file, since it's called only once. You don't want to put function declarations in the tpl.php files, since they are sometimes called more than once (and you aren't allowed to declare functions more than once).
If you have a hard time understanding the function and the return, you can always do something like this in your code (which is very, very inelegant - but it's better to have inelegant code that you do understand, than elegant code that's you don't).
if(!isset($GLOBALS['users_friends_ids'])) {
$GLOBALS['users_friends_ids'] = /* your code here */
}

Using function prototypes dynamically in PHP

I'm writing a construct in PHP where a parser determins which function to call dynamically, kind of like this:
// The definition of what to call
$function_call_spec = array( "prototype" => "myFunction",
"parameters" => array( "first_par" => "Hello",
"second_par" => "World"));
// Dispatch
$funcPrototype = $function_call_spec["prototype"];
$funcPrototype(); // Here we call function 'myFunction'.
This is all fine and dandy. But now comes the next step, passing the parameters, which I don't really know if it's possible the way I want to do it. It never stops amazing me however what script languages can do these days, so here goes:
One could pass the parameters to the function like this:
// Here we call function 'myFunction' with the array of parameters.
$funcPrototype( $function_call_spec["parameters"] );
However, I want to declare 'myFunction' properly with clear arguments etc:
function myFunction( $first_par, $second_par )
{
}
The question then follows - Is there any way to pass parameters to a function dynamically simply by looping through the parameter array?
To clarify, I don't want to do it like this:
$funcPrototype( $function_call_spec["parameters"]["first_par"],
$function_call_spec["parameters"]["second_par"] );
Because this requires my code to statically know details about myFunction, which goes against the whole idea.
Instead I would want to do it in some way like this maybe:
// Special magic PHP function which can be used for invoking functions dynamically
InvokeFunction( $funcPrototype, $function_call_spec["parameters"] );
Which then results in myFunction being called and all parameters in the array gets passed to each individual parameter variable in the prototype.
Any comments are welcome.
Regards.
/R
PS: None of the code in this post has been tested for typos etc.
You should use call_user_func_array which can call any function or method and takes parameteres from an array.
Alternatively you can use ReflectionFunction::invokeArgs, but there's no benefit over call_user_func_array unless you already use this class for someting else (like checking whether function you call accepts appropriate number and types of arguments).
call_user_func_array($funcPrototype, $function_call_spec["parameters"]);
You might want to create a wrapper that names the function to your preference, such as:
function InvokeFunction($function, $args = array()) {
return call_user_func_array($function, (array)$args);
}
With this function you can call it in 3 different ways:
$return = InvokeFunction('doStuff');
$return = InvokeFunction('doStuff', $single_arg);
$return = InvokeFunction('doStuff', $multiple_args);
call_user_func_array() is the best choice if you don't need to enforce the contract, otherwise use ReflectionFunction.
http://us2.php.net/create_function
When you use create_function(), your arguments are not evaluated until runtime. Pretty sweet.

Categories