Empty $woocommerce global variable (WC() works instead) - php

I have this code:
add_action('woocommerce_init', 'on_woocommerce');
function on_woocommerce(){
function log_cart(){
global $woocommerce;
$cart_content = $woocommerce->cart->get_cart();
log_debug($cart_content,'$cart_content');
}
if(HLP_DEBUG){
log_cart();
}
}
But it gives me error because $woocommerce is null.
Instead if I use this:
add_action('woocommerce_init', 'on_woocommerce');
function on_woocommerce(){
function log_cart(){
$cart_content = WC()->cart->get_cart();
log_debug($cart_content,'$cart_content');
}
if(HLP_DEBUG){
log_cart();
}
}
Everything it's right and working.
The code is inside another function executed on woocommerce_init HOOK. For what is my knowledge both should work.
Could you explain why the first one doesn't?

The behavior you describe is possible if you hook onto "woocommerce_loaded", but it is not possible if you hook onto "woocommerce_init".
The last line of wp-content/plugins/woocommerce/woocommerce.php is
// Global for backwards compatibility.
$GLOBALS['woocommerce'] = WC();
This suggests that using global $woocommerce is deprecated.
The last line is global code, that runs after the file is included. So, during the creation of the class WC the constructor gets run.
/**
* WooCommerce Constructor.
*/
public function __construct() {
$this->define_constants();
$this->includes();
$this->init_hooks();
do_action( 'woocommerce_loaded' );
}
init_hooks() adds WC's init function to the higher WP init action. Then the woocommerce_loaded action is fired. This is fired before the constructor completes, and before the object is added to the globals array.
After the woocommerce_loaded action is done, the constructor returns, and the WC object is added to the globals. There is no way WP's init action can be fired, and woocommerce is hooked into it, without the global being available.
This is for WC 2.5.5, I have checked the source code back to 2.3.0, other versions may be different.
You may have a rogue plugin or theme that is calling woocomerce_init before the WC() object is made, or before it returns (i.e. on the woocommerce_loaded action).
The only real explanation is that your function on_woocommerce() is being called from another hook or in another way accidentally. There is no way that the global function WC() could be defined, but the very next line of source code hasn't executed (which is store the result of WC() in a global variable).

Related

PHP, How maintain parent class data into child class?

So, I finally found a way to affect the desired function.
The function I was trying to modify is contained in a class so, I did something like this:
class my_Check extends Appointments {
function __construct() {
$this->unregister_parent_hook();
add_action( 'wp_ajax_post_confirmation', array( $this, 'post_confirmation' ) );
add_action( 'wp_ajax_nopriv_post_confirmation', array( $this, 'post_confirmation' ) );
}
function unregister_parent_hook() {
global $appointments; //this was the object created with the parent class
remove_action( 'wp_ajax_post_confirmation', array( $appointments, 'post_confirmation' ) );
remove_action( 'wp_ajax_nopriv_post_confirmation', array( $appointments, 'post_confirmation' ) );
}
function post_confirmation() {
...do the stuff with my mods...
}
}
$new_Check = new my_Check();
Only, I have a new problem now. The parent class does a lot more in the __construct() (many add_action()'s, etc. And $this is populated with a lot of data). The problem is, these other things and data do not seem to be carrying over into the child class. I tried adding a parent::__construct() in the child's __construct() function, but that did not seem to work.
The code with my mods works except for things that need more data in the $this carried over from the parent class.
How can I maintain all the parent class's variable, functions, hooks & filters, etc. into the child class?
And, I can't really change the file with the parent class because that is in a plugin core file which I don't want to modify directly.
I had seen many examples where people were using parent::__construct() to pass the parent's variables, etc. and they were placing it in the child's class __construct() function. That wasn't working for me.
However, I was finally able to get it to work by calling the parent constructor within the new function I was adding to the parent class. Like this:
function post_confirmation() {
parent::__construct();
...do the stuff with my mods...
}
Got the solution from this post > https://stackoverflow.com/a/32232406/1848815

do_action_ref_array within constructor with $this passed as argument

Can someone help me explain what is going on here? I am going through a Wordpress plugin. It has the following constructor function
protected function __construct() {
do_action_ref_array( 'plugin_specific_action_name', array( $this ) );
add_action( 'init', array( $this, 'init' ) );
}
My question is on the first line of the constructor. The action 'plugin_specific_action_name' didn't have any function associated anywhere. The action hook is plugin specific. Since it didn't have any function associated with it, what does it helps the plugin with.
The author has commented "Announce that the class is ready, and pass the object (for advanced use)". Can someone help me what exactly will that be used for? Why do I have to use this in the constructor function? What will be the advanced use cases I can use that for? Any help in clearing this for me will be appreciated
Ok. do_action or do_action_ref_array is just a placeholder. Any custom behavior can be introduced here with help of add_action. If nothing is specified the action will be muted. So it is a place holder. The eye opener for me here is do_action can exist without add_action

PHP hooks setting to be called through different classes

I'm going to structure this as best I can. Basically I have a Hook class that works by adding supplied hooks.
An example of this would be:
$this->registry->hook->add('HOOK_NAME', 'CLASS_NAME||METHOD_NAME')
The hook adding, calling, removing functions work great, now the issue resides when I set the hook in a separate method in a completely different class.
An example of this would be when a user logs in.
The path they take is User Controller -> Form Class -> Login Process Function
Now within this login process function, I'd like to set a hook to be called later to end the session. (Would be added as stated above)
The issue seems to be that it gets set but doesn't stay persistently, if that makes sense?
If anyone is interested, this is what the hook add function looks like:
function add($hook, $callback, $params = '') {
//make sure the hook is defined
if (!isset($this->hooks[$hook])) {
$this->hooks[$hook] = array();
}
//add the callback to the hook
$this->hooks[$hook]['callback'] = $callback;
// add the params if supplied
if (!empty($params)) {
$this->hooks[$hook]['params'] = $params;
}
}
Should I be using magic methods __set() and __get() ?
Any help would be appreciated! :)

add_action in wordpress using OOP?

I am learning about OOP, and I think I am getting the hang of it. My question is why the author of a wordpress boilerplate plugin wrote the add action function like this
add_action('admin_init', array(&$this, 'admin_init'));
According to the codex I understand the add_action hook, the paramaters to be passed are $tag "The name of the action to which $function_to_add is hooked", and the function that you want hooked $function_to_add.
Well I understand the functions in OOP are methods, and I can see why that may change the syntax, but that is just a vague representation, I want a clear answer why the author uses an array, then uses &$this. I understand why one would use $this->property but not so sure about &$this.
Is it just how you refer to the method? If so I still dont understand the array, why wouldnt it look something like $this->admin_init.
By the way the structure looks kind of like this
class my_plugin_settings {
public function __construct() {
// register actions
add_action('admin_init', array(&$this, 'admin_init'));
}
public function admin_init() {
//Settings here
}
}
&this provides context to the PHP functions under the hood that hook it up to your admin_init function. Check out the declaration of add_action or take a look at http://www.php.net/manual/en/function.call-user-func-array.php.
That's how PHP knows to use the function in that object, not a global-level function

PHP class_exists always returns true

I have a PHP class that needs some pre-defined globals before the file is included:
File: includes/Product.inc.php
if (class_exists('Product')) {
return;
}
// This class requires some predefined globals
if ( !isset($gLogger) || !isset($db) || !isset($glob) ) {
return;
}
class Product
{
...
}
The above is included in other PHP files that need to use Product using require_once. Anyone who wants to use Product must however ensure those globals are available, at least that's the idea.
I recently debugged an issue in a function within the Product class which was caused because $gLogger was null. The code requiring the above Product.inc.php had not bothered to create the $gLogger. So The question is how was this class ever included if $gLogger was null?
I tried to debug the code (xdebug in NetBeans), put a breakpoint at the start of Product.inc.php to find out and every time it came to the if (class_exists('Product')) clause it would simply step in and return thus never getting to the global checks. So how was it ever included the first time?
This is PHP 5.1+ running under MAMP (Apache/MySQL). I don't have any auto loaders defined.
Thanks for the informative answers guys. My belief was that when you
include a file PHP starts executing it line by line from line one, so
it would not allow me to include the file if the globals were not
defined. I will move the checks into the constructor. Based on the
original question, I accept the answer from #deceze
The file is parsed before it is executed. Classes are "loaded" by parsing, but functions are executed after the parsing. By putting the function call in the same file as the class, the class is always parsed and "loaded" before that function executes, thereby it's always true.
If you are always including the file using require_once (which is good), there's no point in that check anyway. A class definition shouldn't conditionally depend on some global variables. Rethink what you're doing here.
I see a main issue here:
// This class requires some predefined globals
That might be surprising to you, but I think what you actually want to do is, that if that is the case, you don't check that when you define the class, but when you instantiate it.
When a class is instantiated, it's constructor function is called. This seems like the perfect place to me to check for that:
class Product
{
public function __construct() {
// This class requires some predefined globals
$this->needGlobal('gLogger', 'db', 'glob');
}
private function needGlobal() {
foreach (func_get_args() as $global) {
if (!isset($GLOBALS[$global])) {
throw new RuntimeException(sprintf('Global %s needed but not set.', $global));
}
}
}
...
}
When you instantiate a Product it then automatically checks if the preconditions are met:
$blueShoes = new Product();
This will not work if the pre-conditions are not met, but it will work if.
But that is only partially solving your problem. The real problem with your code is that the Product needs global variables to work.
Instead make the product just take the things it needs to work with:
class Product
{
private $gLogger;
private $db;
private $glob;
public function __construct(LoggerInterface $gLogger, DbInterface $db, GlobInterface $glob) {
$this->gLogger = $gLogger;
$this->db = $db;
$this->glob = $glob;
}
...
}
Usage:
$redShoes = new Product($gLogger, $db, $glob);
And then you don't need to care about anything global inside Product any longer.
You commented you want to gradually improve the code. You can do so, here is how. As written the second variant above is the way to go, but currently the legacy code is not compatible with it. In any case if the Product class is new code, you should write it with dependency injection. This is important to separate the legacy code from the new code. You don't want to have the legacy stuff swallowed by your new code. That would make new code legacy code, so you would not be able to gradually improve. You would just add new legacy code.
So take the class definition with the dependency injection. For your legacy needs write a second class that is shielding this:
class ProductLegacy extends Product
{
public function __construct() {
// This class requires some predefined globals
list($gLogger, $db, $glob) = $this->needGlobal('gLogger', 'db', 'glob');
parent::__construct($gLogger, $db, $glob);
}
private function needGlobal() {
$variables = array();
foreach (func_get_args() as $global) {
if (!isset($GLOBALS[$global])) {
throw new RuntimeException(sprintf('Global %s needed but not set.', $global));
}
$variables[] = $GLOBALS[$global];
}
return $variables;
}
}
As you can see, this little stub brings together the global way of doing things with the new way. You can use the Product class in your new code, and if you need to interface with old code, you use the ProductLegacy class that works with the global variables for class instantiation.
You could also create a helper function that is doing this, so you can use it for different classes. Depends a bit on your needs. Just find a border where you can draw a clear line between old code and new code.

Categories