thunderer / Shortcode

Advanced shortcode (BBCode) parser and engine for PHP
http://kowalczyk.cc
MIT License
378 stars 29 forks source link

Nested shortcodes #109

Closed SHELA closed 1 year ago

SHELA commented 1 year ago

Hello, In already have example code for nested #93 but what if more nested levels

    $customers = [
        ['id' => 1, 'name' => 'X', 'contact' => 'XC', 'sub'=>[['a'=>'1'],['b'=>'2']]],
        ['id' => 2, 'name' => 'Y', 'contact' => 'YC','sub'=>[['a'=>'3'],['b'=>'4']]],
    ];
    $text = <<<EOF
[customers]
    ID: [id /]
    [sub]
        [a /]
        [b /]
    [/sub]
[/customers]';
EOF;

Have anyone tried to make that? The main issue is that "sub" do not have its own index, and always error "Invalid name or duplicate shortcode handler"

thunderer commented 1 year ago

Can you share the full code snippet that reproduces the issue? The exception you mentioned is thrown in HandlerContainer when you attempt to register a handler with an empty or already existing name. I just tested locally the code from #93 with your changes above applied, and it works fine - in this example I'm just passing sub through and handling a and b inside:

<?php
declare(strict_types=1);
namespace X;

use Thunder\Shortcode\HandlerContainer\HandlerContainer;
use Thunder\Shortcode\Parser\RegularParser;
use Thunder\Shortcode\Processor\Processor;
use Thunder\Shortcode\Shortcode\ProcessedShortcode;
use Thunder\Shortcode\Shortcode\ShortcodeInterface;

require __DIR__.'/vendor/autoload.php';

$customers = [
    ['id' => 1, 'name' => 'X', 'contact' => 'XC', 'sub'=>[['a'=>'1'],['b'=>'2']]],
    ['id' => 2, 'name' => 'Y', 'contact' => 'YC','sub'=>[['a'=>'3'],['b'=>'4']]],
];
$text = <<<EOF
[customers]
    ID: [id /]
    [sub]
        [a /]
        [b /]
    [/sub]
[/customers]';
EOF;

$handlers = new HandlerContainer();
$handlers->add('customers', function(ProcessedShortcode $shortcode) use($customers) {
    $result = '';
    foreach($customers as $customer) {
        $handlers = new HandlerContainer();
        $handlers->add('id', fn() => $customer['id']);
        $handlers->add('sub', fn(ShortcodeInterface $s) => $s->getContent());
        $handlers->add('a', fn() => $customer['name']);
        $handlers->add('b', fn() => $customer['contact']);
        $processor = new Processor(new RegularParser(), $handlers);

        $result .= $processor->process($shortcode->getTextContent());
    }

    return $result;
});
$processor = new Processor(new RegularParser(), $handlers);
var_dump($processor->process($text));

Result;

    ID: 1

        X
        XC

    ID: 2

        Y
        YC
SHELA commented 1 year ago

"sub" is multidimensional

Array
(
    [0] => Array
        (
            [a] => 1
        )

    [1] => Array
        (
            [b] => 2
        )

)
 $handlers = new HandlerContainer();
    $handlers->add('customers', function(ProcessedShortcode $shortcode) use($customers) {
        $result = '';
        foreach($customers as $customer) {
            $handlers = new HandlerContainer();
            $handlers->add('id', fn() => $customer['id']);
            $handlers->add('sub', fn(ShortcodeInterface $s) => $s->getContent());
            foreach($customer['sub'] as $sub){
                $handlers->add('a', fn() => $sub['a']);
                $handlers->add('b', fn() => $sub['b']);
            }
            $processor = new Processor(new RegularParser(), $handlers);

            $result .= $processor->process($shortcode->getTextContent());
        }

        return $result;
    });
    $processor = new Processor(new RegularParser(), $handlers);
    var_dump($processor->process($text));

Result:

 RuntimeException 

  Invalid name or duplicate shortcode handler for a!
thunderer commented 1 year ago

Sure, I get you now. Notice that you're adding a handler for a in a loop, which indeed will result in an exception. Every shortcode can have only one handler in the context of a Processor, so if you want to do nest it, you need to include the logic inside the handler once again:

<?php
declare(strict_types=1);
namespace X;

use Thunder\Shortcode\HandlerContainer\HandlerContainer;
use Thunder\Shortcode\Parser\RegularParser;
use Thunder\Shortcode\Processor\Processor;
use Thunder\Shortcode\Shortcode\ProcessedShortcode;
use Thunder\Shortcode\Shortcode\ShortcodeInterface;

require __DIR__.'/vendor/autoload.php';

$customers = [
    ['id' => 1, 'name' => 'X', 'contact' => 'XC', 'sub' => [['a' => '1'], ['b' => '2']]],
    ['id' => 2, 'name' => 'Y', 'contact' => 'YC', 'sub' => [['a' => '3'], ['b' => '4']]],
];
$text = <<<EOF
[customers]
    Customer [id /], [name /], [contact /]
    Subs
    [sub]
        A[a /] B[b /]
    [/sub]
[/customers]';
EOF;

$handlers = new HandlerContainer();
$handlers->add('customers', function(ProcessedShortcode $shortcode) use($customers) {
    $result = '';
    foreach($customers as $customer) {
        $handlers = new HandlerContainer();
        $handlers->add('id', fn() => $customer['id']);
        $handlers->add('name', fn() => $customer['name']);
        $handlers->add('contact', fn() => $customer['contact']);
        $handlers->add('sub', function(ShortcodeInterface $s) use($customer) {
            $result = '';
            foreach($customer['sub'] as $sub) {
                $subHandlers = new HandlerContainer();
                $subHandlers->add('a', fn() => $sub['a'] ?? '-');
                $subHandlers->add('b', fn() => $sub['b'] ?? '-');
                $processor = new Processor(new RegularParser(), $subHandlers);

                $result .= $processor->process($s->getTextContent());
            }

            return $result;
        });
        $processor = new Processor(new RegularParser(), $handlers);

        $result .= $processor->process($shortcode->getTextContent());
    }

    return $result;
});
$processor = new Processor(new RegularParser(), $handlers);
var_dump($processor->process($text));

Result:

    Customer 1, X, XC
    Subs

        A1 B-

        A- B2

    Customer 2, Y, YC
    Subs

        A3 B-

        A- B4
thunderer commented 1 year ago

@SHELA I'm closing this issue as resolved, please open a new one if you have more things I could help with. Hope the code above was helpful. :)