Custom Kirbytext hook parsing to split text

I’m trying to create a simple plugin to parse kirbytext and split the results into separately wrapped divs based on a certain symbol. I’m basing it off the Columns example but am getting a 500 error.

I’m pretty sure this is occurring when I try to parse the strings to apply regular kirbytext, but I can’t figure out why!

<?php
// Define plugin
Kirby::plugin('mp/kt-concat', [
  'hooks' => [
    'kirbytags:before' => function ($text) {
      $sections = preg_split('!(\n|\r\n)\+{4}\s+(\n|\r\n)!', $text);
      $html = [];
      foreach ( $sections as $section ) {
        $section = $this->kirbytext($section);
        $html[] = '<div class="t-section">' . $section . '</div>';
      }
      $text = '<div class="t-sections">' . implode($html) . '</div>';
      return $text;
    }
  ]
]);

$this cannot be used here, I think

When I dump $this in that context however I seem to get the Kirby object returned?

Ok, then it’s fine and then I don’t know atm.

Aren’t you constructing an endless loop by calling kirbytext() from within a kirbytags hook? Every time you call kirbytext() within that hook, the hook is executed again…

That could explain the server error.

Thanks for your input Sebastian. Hmm, I don’t think so :thinking: — wouldn’t this example run into the same issue then?

Could it have to do with the $data being passed into the kirbytext function in that example that I am not doing in mine?

Looking at the columns example from the Cookbook, it uses preg_replace_callback, i.e. executing the code manipulation – and calling kirbytext() – only for instances where the regex matches, while your code applies the manipulation to any content piped into kirbytext(). That’s where I’d suspect the eternal loop to stem from.

That’s a really good point. Sorry, I’m not very experienced with PHP, but any ideas how I could avoid that whilst also making sure that I’m manipulating the whole content piped into kirbytext()? I had a few passes at using preg_replace_callback with a regex expression matching everything, but had the same issue (predictably, I suppose).

Well, the reason the Cookbook example works is because

preg_replace_callback('!\(columns(…|\.{3})\)(.*?)\((…|\.{3})columns\)!is', function($matches) use($text, $data) {

only applies the cutting-into-columns routine on text wrapped between (columns) and (/columns) in the text. You’ll need some kind of check/filter to ensure that the calls to kirbytext() from within your function don’t trigger the same loop again.

The easiest would be to stick to the cookbook example where the text-to-be-divided is wrapped within such Kirbytags. Alternatively, you could try by introducing a filter to only do the “magic” after the regex actually split anything:

<?php
// Define plugin
Kirby::plugin('mp/kt-concat', [
  'hooks' => [
    'kirbytags:before' => function ($text) {
      $sections = preg_split('!(\n|\r\n)\+{4}\s+(\n|\r\n)!', $text);
      if (sizeof($sections) > 1) {
          foreach ($sections as $section) {
              $section = $this->kirbytext($section);
              $html[] = '<div class="t-section">' . $section . '</div>';
          }
          $text = '<div class="t-sections">' . implode($html) . '</div>';
      }
      return $text;
    }
  ]
]);

That way, your section-logic only applies if preg_split actually returns an array with more than one string. This may have performance implications, so I’d at least replace

$sections = preg_split('!(\n|\r\n)\+{4}\s+(\n|\r\n)!', $text);

with

$sections = explode('++++', $text);

(less expensive than a regex, but it does not check for the empty lines before and after the ++++).

Sebastian, thank you so much for your help here. That has solved the problem and really clarifies the root cause of it. I had a vague inkling about dealing with situations where this function would operate on content without this new separator, although I’m not sure I would have gotten there myself. Thank you.