XF 2.1 Exception when calling XF::app()->bbCode()->render


Dear Friends,

I'm writing an addon that extends the Rest Api with some custom endpoints. One of this endpoints takes a list of posts, end for each post renders the message to return HTML, by calling the following:

$html = XF::app()->bbCode()->render( $post->message, 'html', 'post', $post );

Where "$post" is an Object of the type Entity\Post.

The call above works, and returns the rendered HTML. But in the case the content is a BBCODE, although the Html code is returned correctly, an exception is logged each time the function is called:

[*]ErrorException: Template error: Cannot call method isIgnoring on a non-object (NULL)
[*]Generated by:
[*]Apr 19, 2020 at 2:44 PM
[SIZE=5][B]Stack trace[/B][/SIZE]
#0 [internal function]: XF\Template\Templater->handleTemplateError(512, 'Cannot call met...', '/var/www/laequi...', 984, Array)
#1 src/XF/Template/Templater.php(984): trigger_error('Cannot call met...', 512)
#2 internal_data/code_cache/templates/l5/s0/public/bb_code_tag_quote.php(10): XF\Template\Templater->method(NULL, 'isIgnoring', Array)
#3 src/XF/Template/Templater.php(1315): XF\Template\Templater->{closure}(Object(ThemeHouse\UIX\XF\Template\Templater), Array)
#4 src/XF/BbCode/Renderer/Html.php(959): XF\Template\Templater->renderTemplate('bb_code_tag_quo...', Array)
#5 src/XF/BbCode/Renderer/Html.php(950): XF\BbCode\Renderer\Html->getRenderedQuote('Aquí tienes un...', 'SombraGris', Array, Array)
#6 [internal function]: XF\BbCode\Renderer\Html->renderTagQuote(Array, 'SombraGris, pos...', Array, Array, Object(XF\BbCode\Renderer\Html))
#7 src/XF/BbCode/Renderer/Html.php(296): call_user_func(Array, Array, 'SombraGris, pos...', Array, Array, Object(XF\BbCode\Renderer\Html))
#8 src/XF/BbCode/Traverser.php(61): XF\BbCode\Renderer\Html->renderTag(Array, Array)
#9 src/XF/BbCode/Traverser.php(37): XF\BbCode\Traverser->renderSubTree(Array, Array)
#10 src/XF/BbCode/Traverser.php(20): XF\BbCode\Traverser->renderAst(Array, Object(XF\BbCode\RuleSet), Array)
#11 src/XF/SubContainer/BbCode.php(219): XF\BbCode\Traverser->render('[QUOTE="SombraG...', Object(XF\BbCode\Parser), Object(XF\BbCode\RuleSet), Array)
#12 src/addons/HellDesk/ClientApp/Api/Helpers/RenderHelper.php(24): XF\SubContainer\BbCode->render('[QUOTE="SombraG...', 'html', 'post', Object(XF\Entity\Post))
#13 src/addons/HellDesk/ClientApp/Api/Helpers/RenderHelper.php(42): HellDesk\ClientApp\Api\Helpers\RenderHelper->renderBBCodeToHtml(Object(XF\Entity\Post))
#14 src/addons/HellDesk/ClientApp/Api/Controller/ThreadController.php(174): HellDesk\ClientApp\Api\Helpers\RenderHelper->preparePostCompanion(Object(XF\Entity\Post))
#15 src/addons/HellDesk/ClientApp/Api/Controller/ThreadController.php(54): HellDesk\ClientApp\Api\Controller\ThreadController->getPostsInThreadPaginated(Object(XF\Entity\Thread), 1, 20, true)
#16 src/XF/Mvc/Dispatcher.php(350): HellDesk\ClientApp\Api\Controller\ThreadController->actionGetPosts(Object(XF\Mvc\ParameterBag))
#17 src/XF/Api/Mvc/Dispatcher.php(31): XF\Mvc\Dispatcher->dispatchClass('HellDesk\\Client...', 'Getposts', Object(XF\Api\Mvc\RouteMatch), Object(HellDesk\ClientApp\Api\Controller\ThreadController), NULL)
#18 src/XF/Mvc/Dispatcher.php(113): XF\Api\Mvc\Dispatcher->dispatchFromMatch(Object(XF\Api\Mvc\RouteMatch), Object(HellDesk\ClientApp\Api\Controller\ThreadController), NULL)
#19 src/XF/Mvc/Dispatcher.php(55): XF\Mvc\Dispatcher->dispatchLoop(Object(XF\Api\Mvc\RouteMatch))
#20 src/XF/App.php(2184): XF\Mvc\Dispatcher->run()
#21 src/XF.php(391): XF\App->run()
#22 index.php(16): XF::runApp('XF\\Api\\App')
#23 {main}

As said, although the HTML is rendered correctly, this exception is logged in the backend. Comparing what happens when this function is called through the rest API and my endpoint, vs when it is called from the framework when rendering the posts for a view, I found that in the case the function is called from my Rest Api endpoint, then the following file is loaded and executed:


And in this file, the following code line produces the exception:

$__vars['isIgnored'] = ($__vars['attributes']['member'] AND $__templater->method($__vars['xf']['visitor'], 'isIgnoring', array($__vars['attributes']['member'], )));
More concretely, the $__vars['xf']['visitor'] variable is null, and because of that the call to the method "isIgnoring" fails.

Well, comparing when the render function is called from the framework, I can only say that the bb_code_tag_quote.php file is not called and the code is not executed. I do not understand why, because I'm calling the XF::app()->bbCode()->render function with exactly the same parameters as when it is called from the framework.

As said above, this happens only when trying to render a "quote" bbcode. For other BBcodes, it seems to work.

Any ideas?

Lukas W.

Well-known member
There's a point in the code where the templater is populated with global data such as the visitor object. If you call the bb code renderer before that point, that's one of the errors you'll run into.


Thank you for the info and for pointing me in the right direction. Indeed, the templater is initialized by calling a function called "setupTemplaterObject", and this operation is not performed when calling rest API endpoints.

Then my next question is where should I initialize this object. Maybe should I create an event listener, for the event "template_setup" or the event "app_api_setup" and perform the initialization there?

And my last question, how do I obtain the parameter "Container" needed for the function call?

Thank you for your patience.


Well, I ended up with a workaround, but I'm pretty sure that this is not the right way to solve the problem.

I set the default parameter defaultParameters->xf->visitor for the Templater, before calling the renderer. I have no idea why the default parameters of the template are not initialized when calling a rest API endpoint., but this workaround solves the problem with the exception:
public function renderBBCodeToHtml( Post $post ): string {
   $html = XF::app()->bbCode()->render( $post->message, 'html', 'post', $post );

   return $html;
private function workAroundAddVisitorToTemplaterDefautParams(): void {
   XF::app()->templater()->addDefaultParam( 'xf', [ 'visitor' => $this->userHelper->visitor ] );

If someone knows the right solution to this problem, I would be very grateful.

Thanks in advance.