Zen Cart Logo
Forums / Code Collaboration / $messageStack->add(...) in observer won't work

$messageStack->add(...) in observer won't work

Views: 19,776

Results 1 to 13 of 13
03 Jun 2018, 19:26
#1
dave224 avatar

dave224

Zen Follower

Join Date:
Jun 2012
Posts:
481
Plugin Contributions:
0

$messageStack->add(...) in observer won't work

I'm writing an observer and need to display an error message under certain conditions. I'm getting a fatal error "Call to a member function add() on null in /includes/classes/observers/class.login_checks.php on line xx". The notifier for this observer is in includes/modules/pages/login/header_php.php (NOTIFY_PROCESS_3RD_PARTY_LOGINS). The logic works perfectly in zc 1.5.1 when the same logic is back in the header_php.php file. The parameters for the add function are not nulls (checked with var_dump). Since zc 1.5.5 has the notifier, I'm trying to move my changes out of this core file and use the observer instead. Code snippet follows:

// notify customer
$error = true;
$messageStack->add('login', LOGIN_VALIDATION_ERROR, 'error');

What does the error mean and how can I fix it?

zc v155f, PHP 5.6

03 Jun 2018, 19:34
#2
dave224 avatar

dave224

Zen Follower

Join Date:
Jun 2012
Posts:
481
Plugin Contributions:
0

Re: $messageStack->add(...) in observer won't work

Sorry everyone, problem solved. I forgot to pass $messageStack to the observer and back to the header. Works great now.

04 Jun 2018, 03:08
#3
mc12345678 avatar

mc12345678

Totally Zenned

Join Date:
Jul 2012
Posts:
16,908
Plugin Contributions:
2

Re: $messageStack->add(...) in observer won't work

Dave224:

Sorry everyone, problem solved. I forgot to pass $messageStack to the observer and back to the header. Works great now.

Can you be more specific about your method of resolving this? From the description I think you did more than was necessary. If what I thought was done, it seems that to provide similar messages and use other information in the future may require far more editing than what is minimally necessary.

I could have also misread the description of how it was resolved.

04 Jun 2018, 19:14
#4
dave224 avatar

dave224

Zen Follower

Join Date:
Jun 2012
Posts:
481
Plugin Contributions:
0

Re: $messageStack->add(...) in observer won't work

mc12345678:

Can you be more specific about your method of resolving this? From the description I think you did more than was necessary. If what I thought was done, it seems that to provide similar messages and use other information in the future may require far more editing than what is minimally necessary.

I could have also misread the description of how it was resolved.

Thank you for your interest! My solution was to include $messageStack in the notifier call along with other needed data (shown in red):

$zco_notifier->notify('NOTIFY_PROCESS_3RD_PARTY_LOGINS', $email_address, $password, $loginAuthorized,
 $check_customer->fields['customers_member_number'], $check_customer->fields['customers_id'], $messageStack);

and in the observer update function:

function update(&$class, $eventID, $paramsArray = array(), &$rwParm2, &$loginAuthorized,
 &$customers_member_number, &$customers_id, &$messageStack) {

Even though I had to modify the core file to get the data I need into the observer, that change -- add some data to the notifier call -- is better than having all the additional login validation code in the core file. Note: customers_member_number is a store unique variable added to the customers table. Another core file change in the login header file pulls that variable out of the table in the customer query in the file.

I'd certainly be interested in a simpler approach.

04 Jun 2018, 20:02
#5
lat9 avatar

lat9

Administrator

Join Date:
Sep 2009
Location:
Stuart, FL
Posts:
14,065
Plugin Contributions:
56

Re: $messageStack->add(...) in observer won't work

Rather than passing $messageStack to the observer-class, you can reference that object in your observer-class using

$GLOBALS['messageStack']->add(whatever);
04 Jun 2018, 20:44
#6
mc12345678 avatar

mc12345678

Totally Zenned

Join Date:
Jul 2012
Posts:
16,908
Plugin Contributions:
2

Re: $messageStack->add(...) in observer won't work

The same could be said for the data related to $check_customer variable, though perhaps the associated data could be obtained after the successful login as identified by the notifier 'NOTIFY_LOGIN_SUCCESS' where the data has been captured in the session...

That part depends on how the information is used to accomplish your desired task.

The thing to consider and why I commented is that the notifiers have been incorporated to attempt to provide trigger points/hooks where software can monitor operation. This makes the software also more flexible by not having to actually touch the core code. While a lot of thought and consideration has been given to each such notifier, sometimes it is not enough on its own and perhaps a change made, but in many cases where a notifier or group of them have recently been added it is likely that the needed tools are or easily can be made available.

Sometimes that means letting the code be "patient" for another piece of information to become available, and sometimes to just grab what is needed. :)

Now as far as using an additional notifier in your observer, instead of just using the update function, you can create a function that is specific to the notifier. In this case (and for all ZC 1.5.3 and above), it would be:

function updateNotifyProcess3rdPartyLogins(&$class, $notifier, &$,&$,&$) {
}

If there is something to be captured/held until reaching the next notifier, then store that value in a variable associated with the observer class: ie.

$this->loginAuthorized = $loginAuthorized;

Then when the next observer (assume the 'NOTIFY_LOGIN_SUCCESS') is reached, it can be retrieved and used from the same variable:

function updateNotifyLoginSuccess(&$class, $notifier) {
  $loginAuthorized = $this->loginAuthorized;
  // do other things that should be done when login is still successful
}

Or it can be used without that additional assignment, etc...

This all depends on what is needed where in the process, but so far there doesn't appear to be any reason to modify the notifier(s) to accomplish/retrieve the desired information. This will make things a little easier for you in the future.

I also suggest commenting in your observer the format of the notifier considered. While generally they are not expected to change, sometimes they do and comparing the what was to what is becomes much easier with that little bit of information with which to compare.

05 Jun 2018, 14:01
#7
dave224 avatar

dave224

Zen Follower

Join Date:
Jun 2012
Posts:
481
Plugin Contributions:
0

Re: $messageStack->add(...) in observer won't work

Wow! Thank you for taking the time to write such a detailed response. Your tips will be very helpful and are greatly appreciated. Could you please expand your tip on the update function name? Under what conditions should I use "update" and when should I use something else? In your example, you used update followed by a form of the notifier name. Is that on purpose or required, or could I use any name, for example, "my_function"? Could I pass a variable to the next notifier if the name were "update"? Could I pass a variable to other following notifiers, even if they are not next?

05 Jun 2018, 20:16
#8
mc12345678 avatar

mc12345678

Totally Zenned

Join Date:
Jul 2012
Posts:
16,908
Plugin Contributions:
2

Re: $messageStack->add(...) in observer won't work

Dave224:

Wow! Thank you for taking the time to write such a detailed response. Your tips will be very helpful and are greatly appreciated. Could you please expand your tip on the update function name? Under what conditions should I use "update" and when should I use something else? In your example, you used update followed by a form of the notifier name. Is that on purpose or required, or could I use any name, for example, "my_function"? Could I pass a variable to the next notifier if the name were "update"? Could I pass a variable to other following notifiers, even if they are not next?

Normally, I would prefer to answer questions top down; however, I think that I can address everything along the way but in a different order as I get the feeling by the questions that there is some understanding kicking in and desire to know more.

So, a little understanding about what's going on... On each page load, basically the entire store is "built". There's an initial page load (first visit where a session hasn't begun) and then there is marching through the store or even jumping to one's favorite location. Before the first visit, session data for the anticipated customer doesn't exist or at least in a default store is not yet directly accessible. Once the individual arrives, a session data marker is attempted to be set, and from that point forward, they are basically associated with a session (related to $_SESSION). Now, though when the first and any subsequent page is loaded, basically the index.php file is loaded, which branches out and loads an entire series of logic as well as base code. In that initial load, the includes/auto_loaders folder is accessed as well as other folders such as includes/init_includes (there are many others, but not exactly pertinent to this notifier/observer discussion). As of ZC 1.5.3, part of loading the notifier/observer system is to allow for a file to "independently" serve as an observer (filename is like auto.xxxx.php). Prior to that, an observer had to be referenced/loaded and typically through a config.yyyy.php file found in the includes/auto_loaders directory.

The files in the includes/auto_loaders directory are accessed early enough in the load that the associated files can be mapped to a trigger point or load point. The default for the auto.xxxx.php file is a load point of 180. Anyways, that load point value relates to the sequence at which files/information is loaded. The lower the number the sooner it is loaded. Some things have to be loaded before others. For example to use the function zen_not_null, well the function file has to be loaded to memory. Just like you can't access the $messageStack variable before it is declared/built.

All of this is to say, that the typical generation of an observer is one that is loaded/activated during each page load, and that once the next click is made that basically/generally speaking any information that has been stored in that observer class is wiped to be created/recreated based on the next page load. There are ways around that (ie. $_SESSION, cookie, the database possibly, or if truly necessary written to a file), but the need to get around it depends on the need of the data. If generally just accept that on each page load information will/needs to be created as/where needed then at least won't get hung up on the smaller detail(s). Once something seems functional can consider some memory/time saving techniques.

So to try to answer the question:

Could I pass a variable to other following notifiers, even if they are not next?

Is in essence yes if by not next you still mean that at some point in the current page's load but possibly 1000's of lines of code later with unknown number of notifiers/observers between them, yes they can be "passed" (possibly internally stored to then be retrieved from outside of the current observer or from inside the current observer). If you mean can you store a value inside the observer class during login and on logout expect

Wait, more than one observer? Well yes, there could be two/three/four plugins/programs that all listen to the same code point. How they interact can be beneficial/problematic, the sequence that they load in may be helpful or a hindrance. But, save that thought for later. This is something likely to occur when two or more plugins deal with similar things. For example the notifier you chose to use relates to third party logins... If there were three different places in which to login using a single set of criteria, all of that could happen within a single notifier. But, the data internally stored to the observer class during the login, would not be accessible during the next page load of say logout... (Again there are ways to store data to persist across page loads, but internal to the class that is itself not maintained across page loads, $this->variable, don't work.)

So, regarding the two naming conventions of handling notifiers. The update function is generic and ZC 1.5.1 and before only had an update function and only allowed passing a single non-editable variable. The variable could be an array, but any changes made to it stay only in the observer class. As of the production release of ZC 1.5.3, 9 additional variables have become available where each of those variables could be used, edited, and the changes returned to the original function. They don't have to be edited, but they can be. In adding this feature, the Zen Cart developers decided that they would also expand the operation a little. So, they created the possibility for a function to be called that follows the naming convention update+Camelized Notifier, you can look up what camelize means, but basically it removes the underscores and then capitalizes the first character that follows it leaving the remaining characters lower-case. Fortunately for now, PHP doesn't distinguish between capitalization of functions, but does for variables. (ie: $db->Execute($sql); will perform the same as $db->execute($sql); for now...) No telling when the PHP group may go to even more stringent coding expectations and require function capitalization to be consistent between declaration and use.

Anyways, so you have two options, you can use the new method of updateCamelCase(....) or update(....). This naming convention is governed by code within the ZC system. If you wanted to have a my_func() type function, it would have to be called by some other code which could reside in the update() function. You can even have both functions updateCamelCase() and update() in the same observer class, but the way the code is written it will attempt to execute the camel case version first if found, if not found then attempt to execute the update function.

So, which is "better"? That really depends. I do know, that if your code is written using the update function only and default values are not set (update(&$class, $notifier, $arraylike, &$firstvalue, &$secondvalue)), and the code is installed to a vanilla install of ZC 1.5.1 or before, then the store will break. This is because in the earlier versions, they didn't know/recognize that there could be other variables, and therefore don't send a default, and then PHP says "HEY! YOU DIDN'T SET A VALUE AND NOW I DON'T KNOW WHAT TO DO!!!".

If you use the update version and you are observing more than one notifier, then within the update function you would want (maybe) to see what notifier was used to get into the update code so that action A is taken for notifier 1 but action B is taken for notifier 2... If they both have some common action, then the update function may be preferred. Otherwise, if they do different things, it may be desirable to use the camelcase version. I look at it as the code checks for the existence of the camelcase first, so why not use it? Also, the variables being received can be tailored to what they are rather than some generic $var1, $var2, $var3, etc... This improves code readability and can simplify operations. From that/there you can have other functions that are called from either. But this aspect is more about design, intent, and need.

As to the passing of variables, again, if the class is used in a single load sequence (like using both notifiers identified before), then if you use the camel case version, you'll need to store data internally when reaching the first notifier to be used by the second camelcase function (or update function if the camelcase function doesn't exist). If you just use the update function, then if you make the variable to be kept static it will persist, but otherwise will disappear when the second notifier is reached because the update function will basically be called again for the first time. (a bit of an oxymoron, I know)

06 Jun 2018, 16:33
#9
dave224 avatar

dave224

Zen Follower

Join Date:
Jun 2012
Posts:
481
Plugin Contributions:
0

Re: $messageStack->add(...) in observer won't work

Thank you so much MC12345678. Somewhere along the line between zc 1.5.1 and now, I have missed some memos. :smile:

06 Jun 2018, 18:02
#10
mc12345678 avatar

mc12345678

Totally Zenned

Join Date:
Jul 2012
Posts:
16,908
Plugin Contributions:
2

Re: $messageStack->add(...) in observer won't work

Dave224:

Thank you so much MC12345678. Somewhere along the line between zc 1.5.1 and now, I have missed some memos. :smile:

Well, don't know what your level of code comprehension is, but I would suggest looking at (in ZC 1.5.5) includes/init_includes/init_observers.php (pay particular attention to the notes at the top of the file) and includes/classes/class.base.php.
In the class.base.php suggest searching for observer and begin reading through the code/notes related to the base class and the notify function in there. There likely are some php functions that will need to be read about in the php manual, but that's basically where the nuts and bolts are in addition to the previous file.

I think I happened across the code when I was doing an upgrade from 1.5.1 and got curious about the changes. I had also previously seen some related discussion/questions on github that finally made sense. :)

08 Jun 2018, 16:32
#11
dave224 avatar

dave224

Zen Follower

Join Date:
Jun 2012
Posts:
481
Plugin Contributions:
0

Re: $messageStack->add(...) in observer won't work

mc12345678,
Some further clarification please...

The notes in includes/init_includes/init_observers.php refer to the naming convention of the class in includes/classes/observers/auto.xxxx.php as "zcObserver" + the camelcased xxxx string, and in includes/classes/class_base.php notes, the naming convention of the class method is "update" + camelcased xxxx string or just update. But I could also use the zc version below 1.5.3 method of freely naming the class zzzz in class file class.yyyy in includes/classes/observers with the method name of update, as long as the config.yyyy is present in includes/auto_loaders and it defines the class name as zzzz. Do I have this all correct?

Also, if two or more observers are triggered by the same notifier, in what order are the observers executed? DrByte said in a post referring to zc 1.5.1 that the order of instantiation was the order of execution. Is that still true for zc 1.5.5 and what is the order of instantiation? Unfortunately, I could not determine the order of instantiation from a quick look at class_base.php.

Thanks again for your help!

13 Jun 2018, 14:49
#12
dave224 avatar

dave224

Zen Follower

Join Date:
Jun 2012
Posts:
481
Plugin Contributions:
0

Re: $messageStack->add(...) in observer won't work

Just wanted to correct the above post (correction in red)...

The notes in includes/init_includes/init_observers.php refer to the naming convention of the class in includes/classes/observers/auto.xxxx.php as "zcObserver" + the camelcased xxxx string, and in includes/classes/class_base.php notes, the naming convention of the class method is "update" + camelcased notifier string or just update. But I could also use the zc version below 1.5.3 method of freely naming the class zzzz in class file class.yyyy in includes/classes/observers with the method name of update, as long as the config.yyyy is present in includes/auto_loaders and it defines the class name as zzzz. Do I have this all correct?

13 Jun 2018, 19:17
#13
mc12345678 avatar

mc12345678

Totally Zenned

Join Date:
Jul 2012
Posts:
16,908
Plugin Contributions:
2

Re: $messageStack->add(...) in observer won't work

Dave224:

Just wanted to correct the above post (correction in red)...

The notes in includes/init_includes/init_observers.php refer to the naming convention of the class in includes/classes/observers/auto.xxxx.php as "zcObserver" + the camelcased xxxx string, and in includes/classes/class_base.php notes, the naming convention of the class method is "update" + camelcased notifier string or just update. But I could also use the zc version below 1.5.3 method of freely naming the class zzzz in class file class.yyyy in includes/classes/observers with the method name of update, as long as the config.yyyy is present in includes/auto_loaders and it defines the class name as zzzz. Do I have this all correct?

To go slightly a step further and to be sure that x, y, z and the new w match up see the blue text edits below:

The notes in includes/init_includes/init_observers.php refer to the naming convention of the class in includes/classes/observers/auto.xxxx.php as "zcObserver" + the camelcased xxxx string, and in includes/classes/class_base.php notes, the naming convention of the class method is "update" + camelcased notifier string or just update (when more than one notifier is present then it may be necessary to detect which notifier is present to take only the specific action for the specific notifier). But I could also use the zc version below 1.5.3 method of freely naming the class zzzz in class file class.yyyy in includes/classes/observers with the method name of update (note that any other functions in this file pre-1.5.3 can be called from the standard update method), as long as the config.wwww.php file is present in includes/auto_loaders, identifies the stored location of the class as being in the observers directory and it defines the class name as zzzz. Do I have this all correct?