wordpress get variable from empty do_action - php

/* 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

Related

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.

Ajax, JavaScript, PHP/HTML and variables

Ok... so I have a game I'm developing and I'm running into a scope problem I think...
It starts in my js file fired by a click on the card the player wants to play:
It starts in my js file:
function playCard(playerId, card){
$.post("support_files/Blades_functions.php", {
method:'playCardFromHand',
playerID:playerId,
cardName:card
},function(data){
var attr = new Array();
attr = data.split("/");
alert(data);
$("#playerAttackPower").html(attr[0]);
$("#playerDefensePower").html(attr[1]);
$("#playerHealth").html(attr[2]);
$("#playerAttackMod").html(attr[3]);
$("#playerChargeCounters").html(attr[4]);
$("#playerSpecialAttackCounters").html(attr[5]);
});
}
Then into my php file
function playCardFromHand(){
$weapons = new WeaponCards(); // this is basically a class with variables(card objects) (my answer to php not having enums really)
$player = $theGame->gamePlayers[$_POST['playerID']]; // this is the variable ($theGame) that I declare in my main php file with all the markup on it. I'm including my functions file in my main file so the variable should be visible... I've tried using "global" keyword as well and that didn't do anything different...
$cardName = $_POST['cardName'];
$card = $weapons->$cardName;
$card->applyCard($player);
echo $player->getAttack()."/".$player->getDefense()."/".$player->getLife()."/".$player->getAttackMultiplier()."/".$player->getChargeCounters()."/".$player->getSpecAttackCounters();
}
So my problem is that the $player variable is null so nothing is displayed. Basically, as you can see in the callback on the js function, I'm just parsing out the string and updating the player's stats on the main php page. So is this a scope thing? Is there some way to make PHP persist that $theGame variable, which is an object itself, so that I can access/modify it in my functions file?
Any ideas would be most helpful... I've tried using the global keyword, as I said... tried making the variables in the $theGame object public, tried making them private and using getters and setters... I've had a separate problem with that actually, can't seem to get my functions to work on my objects in general, have just been making my variables public and accessing them directly instead of using the getters and setters. Anyway. Please help! I'm so frustrated!!! lol
Thanks,
Jon
Make sure that you declare your PHP variable as global, otherwise you have to get the POST variable inside of your function.

Get ID of the last product added to the cart in magento

I am trying to get the ID of the product that was most recently added to a user’s cart. A quick google search revealed this function
Mage::getSingleton('checkout/session')->getLastAddedProductId(true);
which is also used in Mage_Checkout_Block_Cart_Crosssell. When I try calling the function in my own controller however, it returns nothing.
I have tried to instantiate a core session via
Mage::getSingleton('core/session', array('name'=>'frontend'))
however this approach does not seem to work. I also tried to create the Crossell block and making the protected method that wraps around getLastAddedProductId function public however that returns null just like it does when I try calling it on its own.
Is there something I have to call or instantiate in order to use this function? Here’s my source listing for reference.
class Mymodule_Addcartaftermath_ItemaddedController extends Mage_Core_Controller_Front_Action {
public function generatemessageAction() {
$parameters = $this->getRequest()->getParams();
if (isset($parameters['ajax_call'])) {
$latest_item_id = Mage::getSingleton('checkout/session')->getLastAddedProductId(true);
$response = array('response' => $latest_item_id);
echo json_encode($response);
} else {
$this->_redirect('/');
}
}
}
I tried poking through the source code, particularly the checkout/model/session.php file in the core and I cannot seem to find the definition of the function. I also looked at it’s parent’s class definition but could not find it there either.
If this method is not available to me is there another way of retrieving the most recent item added? I know that the items are added sequentially and I could perhaps just get the last item of the list of items from the cart however this would not work in the case where the user adds the same item to the cart essentially increasing the quantity rather than actual item itself (e.g. the user adds a laptop the cart when there already is one)
The call to
Mage::getSingleton('checkout/session')->getLastAddedProductId(true);
Is actually clearing the session variable after it is read. Magento uses magic methods extensively. In this case you are using the __call magic method which in turn uses the getData() method. In Mage_Core_Model_Session_Abstract_Varien you will see that they override the default behaviour of getData() to expect the second parameter to be a boolean (The first parameter to getData is the key name for the value you are looking for). That boolean is a flag telling the session to clear the variable after reading.
You could always listen for the checkout_cart_product_add_after event and add the item to your own variable in the session. That event is actually fired on the line before setLastAddedProductId() is called.
try to grep the variable you are looking for. As they are coming from magic methods then its hard to find the exact function you are after so it's easier to see the places where data gets set than where it is used
grep '>setLastAddedProductId' app/code -rsn
to see where the product id gets set to that session variable
app/code/core/Mage/Checkout/Model/Cart.php:255: $this->getCheckoutSession()->setLastAddedProductId($product->getId());
and then you can ask this variable (if it is set else empty())
Mage::getSingleton('checkout/session')->getLastAddedProductId();
and you can see all the things that are in checkout/session and verify if the data is there.
var_dump(Mage::getSingleton('checkout/session'));
Haven't a clue why it works this way but it works for me in Magento 1.6...
<?php
require_once ( "app/Mage.php" );
umask(0);
Mage::app("default");
Mage::getSingleton('core/session', array('name'=>'frontend'));
$session = Mage::getSingleton('checkout/session');
$lastadded = $session->getData("last_added_product_id");
print_r($lastadded);
Apparently you have to instantiate the core/session and then the checkout/session. I've tried it every other way but this is the only way I've found it to work. Perhaps someone can explain why it works this way. Hope this helps you out!

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 */
}

How to check whether or not a logged-in user has access to the php script that is currently being called

I've read quite a few posts that are very similar to the question I'm about to ask, but I just wanted to be sure that there wasn't a more sophisticated way to do this. Any feedback is greatly appreciated.
I want to create a mechanism to check whether or not a logged-in user has access to the php script that is currently being called. If so, the script will continue on; if not, the script just fails out using something like die('you have no access').
I came up with two ways of accomplishing this:
(please assume my session stuff is coded/working fine - i.e. I call session_start(), set up the session vars properly and etc)
Define a global variable first, then check the global variable in a required header file. For example:
Content of current_executing_script.php:
// the role the logged in user must have to continue on
$roleNeedToAccessThisFile = 'r';
require 'checkRole.php''
Content of checkRole.php:
if ($_SESSION['user_role'] != $roleNeedToAccessThisFile) die('no access for you');
Define a function within the header file and call the function immediately after including/requiring it:
Content of checkRole.php:
function checkRole($roleTheUserNeedsToAccessTheFile) {
return ($_SESSION['user_role'] == $roleTheUserNeedsToAccessTheFile);
}
Content of current_executing_script.php:
require 'checkRole.php';
checkRole('r') or die('no access for you');
I'm wondering if there is a way to basically just pass a parameter to checkRole.php as part of the include or require construct?
Thanks in advance.
There isn't a way to pass parameters to include or require.
However the code that is included joins the program flow at the point where you include it, so it will inherit any variables that are in scope. So for example if you set $myflag=true immediately before the include, your included code will be able to check what $myflag is set to.
That said, I wouldn't suggest using that technique. Far better for your include file to contain functions (or a class) rather than code that gets run straight off. If you've included a file containing functions then you can call your functions with whatever parameters you want at any point in your program. It's much more flexible, and generally a better programming technique.
Hope that helps.
This could be a useful workaround.
Register a function in say functions.php:
function get_template_partial($relative_include_path, $scoped_parameters)
{
$base_partial_directory = get_template_directory() . '/partials/';
return include $base_partial_directory . $relative_include_path;
}
Use the $scoped_parameters in the partial /partials/role-check.php:
$args = [
'id' => $scoped_parameters['user_id']
];
// A few moments later...
return [
'role_required' => 'admin'
];
Altogether now...
$partial_return_data = get_template_partial('role-check.php', [
'user_id' => 328
]);
echo $partial_return_data['role_required']; // admin
The only thing I can remember with this is if you're using an IDE it might complain that $scoped_parameters is undefined but it's not a third world issue I suppose.
You could have the required file return an anonymous function, and then call it immediately after.
//required.php
$test = function($param)
{
//do stuff
}
return $test
//main.php
$testing = require 'required.php';
$testing($arg);
In the past, many people have disagreed with this approach. But I think it is a matter of opinion.
You can't pass _GET or _POST param via a require() or include() , but you can you first set a _SESSION key/value and pull it on the other side.

Categories