No Code Attached Yet
avatar Spudley
Spudley
22 May 2017

During a recent piece of work, I enabled the Joomla debug mode. With debug activated, I found that certain pages within my site started giving me an error dump screen with the following error:

Exception
Serialization of 'Closure' is not allowed
.../libraries/joomla/cache/controller/callback.php:184

For ref, line 184 looks like this:

$this->cache->store(serialize($data), $id);

The error occurs when I view a page that shows a K2 category list. I don't believe that K2 is directly at fault here though because K2 has not had any version updates lately and debugging has worked just fine previously. It also works fine when debug mode is switched off, so it's something specific to debug.

Things that have changed on my site since the last time I used debug include the 3.7.0 and 3.7.1 updates, and various relatively minor extension updates.

I have done some debugging and used print_r() to dump the contents of $data into a file. It does indeed contain a number of closures within the callStacks array. (I guess that the callStacks array is only kept in debug mode, hence why the error only occurs with debug enabled?)

My dump file is 90MB in size, so I won't attach it here, but it shows that the closures which are triggering the error are at:

libraries/cms/component/helper.php:443
libraries/cms/plugin/helper.php:298
libraries/cms/menu/site.php:72
libraries/cms/library/helper.php:156

Looking at the code for all of these, I see that they all had similar changes made for the 3.7.0 release to wrap a DB query in a closure.

I would suggest that something needs to be corrected here in core. The obvious quick fix would be to remove the closures (maybe convert them into regular methods), but that wouldn't be good long-term, and would mean that we couldn't use closures anywhere. A better fix would be to sanitise the callStacks array so that closures make it don't blow up when we try to serialize. Alternatively if serializing the stack for the cache is going to be a problem, maybe just remove callStacks from the data being cached?

Steps to reproduce the issue

I haven't had time to set up an isolated system to find a minimum configuration to reproduce this issue, but it seems like it ought to be fairly easy to create a scenario that triggers it.

  • Set up a Joomla instance
  • Build a page that will result in a call to one or more of the closures listed above.

    (in my case it is a K2 category listing page, but I suspect it could be triggered in plenty of other ways)
  • Switch Debug on.
  • Navigate to your page.

Expected result

The page ought to render normally (but with the debug block at the foot)

Actual result

Error page showing exception and line where it occurred.

System information (as much as possible)

Joomla 3.7.1 with a bunch of extensions installed and debug mode switched on.

Additional comments

avatar Spudley Spudley - open - 22 May 2017
avatar joomla-cms-bot joomla-cms-bot - change - 22 May 2017
Labels Added: ?
avatar joomla-cms-bot joomla-cms-bot - labeled - 22 May 2017
avatar mbabker
mbabker - comment - 22 May 2017

JCacheControllerCallback was updated at 3.7 to support Closures (included in #10795). If you're getting this error, it points to a potentially failed upgrade or an extension overloading that class with a custom version.

avatar Spudley
Spudley - comment - 22 May 2017

Damn, that was a quick response! 😮

I think I can rule out a failed Joomla upgrade, as I did a "re-install all Joomla files" as part of the process of trying to work this out.

I did see the change to JCacheControllerCallback (I dug into the recent git history), but the changes were relevant to the passed $callback being a closure, whereas the crash is occurring because the returned result from $callback contains closures.

In my case, this returned $result is a massive TableK2Category object, but any return value that contains closures will presumably have the same effect.

avatar mbabker
mbabker - comment - 22 May 2017

That's not going to be unique to 3.7 then or related to us now using Closures for some places where cache is used. Whomever is hooking into cache downstream is probably going to have to do some more screening of their data to ensure they aren't caching something that has a Closure attached to it.

This sounds like a K2 issue in that they are caching the call stack data somehow (Joomla core to the best of my knowledge doesn't cache a call stack anywhere and you'd have to be caching an object that includes something like a JLogEntry instance or the database driver to get something that internally tracks a call stack for debugging purposes). There isn't a standardized object or structure to the data being processed so I don't think there is anything we can do to add post processing steps to our cache controllers.

avatar Spudley
Spudley - comment - 22 May 2017

Joomla core to the best of my knowledge doesn't cache a call stack anywhere

A quick search for callStacks in Joomla core in my IDE gives 86 results, and the same search in the K2 codebase gives zero, so I don't think this is K2 storing the stacks; it's Joomla's debug mode.

avatar mbabker
mbabker - comment - 22 May 2017

Right. JLogEntry and JDatabaseDriver subclasses have call stacks for different purposes. But, Joomla core isn't caching anything that contains these objects. I don't know the structure of the TableK2Category object, but if it's either a subclass of JTable or has a parent that's something like that, it probably does have a JDatabaseDriver object as a class property, and that is inherently causing the issue. I'd say that it's a bad idea for this object to be cached without implementing the Serializable interface and limiting what data actually does get serialized out of it. We can implement Serializable on JTable, but we can't (nor should we) try to pre/post process a data object and manipulate what gets serialized at the cache layer.

avatar Spudley
Spudley - comment - 22 May 2017

Yes. I think we're getting to the nub of the issue now.

Yes, I can confirm -- the print_r() output does show that TableK2Category has a _db property, and callStacks is within that, so it looks like you're right on the button.

So... the answer is to implement Serializable on JTable so that it doesn't serialize its DB property.

avatar franz-wohlkoenig franz-wohlkoenig - change - 22 May 2017
Status New Discussion
avatar franz-wohlkoenig franz-wohlkoenig - change - 22 May 2017
Category com_cache
avatar brianteeman
brianteeman - comment - 23 May 2017

Closed as we have a PR

avatar brianteeman brianteeman - change - 23 May 2017
Status Discussion Closed
Closed_Date 0000-00-00 00:00:00 2017-05-23 16:20:51
Closed_By brianteeman
avatar brianteeman brianteeman - close - 23 May 2017
avatar jwaisner
jwaisner - comment - 11 Mar 2020

Reopening as PR for this issue has been abandoned.

avatar jwaisner jwaisner - change - 11 Mar 2020
Status Closed New
Closed_Date 2017-05-23 16:20:51
Closed_By brianteeman
avatar jwaisner jwaisner - reopen - 11 Mar 2020
avatar brianteeman
brianteeman - comment - 23 Feb 2022

I guess nothing is going to happen here now and this should be closed. Not my call though

avatar brianteeman
brianteeman - comment - 23 Aug 2022

This should be closed. It really shouldnt be relevant for J4 and its such an old issue that even if someone has this error they will nebver find this issue

avatar zero-24 zero-24 - change - 23 Aug 2022
Status New Closed
Closed_Date 0000-00-00 00:00:00 2022-08-23 19:37:03
Closed_By zero-24
Labels Added: No Code Attached Yet
Removed: ?
avatar zero-24 zero-24 - close - 23 Aug 2022

Add a Comment

Login with GitHub to post a comment