Outputting markup from closure in a snippet - bad practice?

I want to make a snippet that outputs static content before and after dynamic content. I have this:

default.php template:

<?php snippet('test', ['content' => function () { ?>
	<p>some very long content</p>
<?php }]) ?>

And in the test.php snippet:

<span>some markup</span>
<?php $content() ?>
<span>end markup</span>

I get no errors or warnings and in the page, I have:

<span>some markup</span>
	<p>some very long content</p>
<span>end markup</span>

So this approach works just the way I need it, but I’m concerned with whether this is considered good practice. Can it go wrong? Perhaps it wouldn’t work in an older PHP version? I haven’t seen it done before.

What would you advise?

Where’s the point of doing it like this?

That’s an interesting approach and I can see how it can be useful.

I don’t think it will break, but it’s a bit clunky and other devs who may work on the site later probably won’t immediately understand what’s going on (I’ve never seen it either). So I would recommend keeping it simple by splitting the snippet into a header and footer as usual.

Well, my actual use case is for header navigation links. Each nav item can have a dropdown or not, based on the page content, but I want that dropdown to have different formatting across some pages. So I pass the item’s $page and the $dropdown closure where I put the dropdown content (where you could easily pass the current nav item $page as a parameter). I also kind of lied about the static content part. It is mostly static, but there are some dynamic stuff based on the nav item’s $page. For example, and id attribute for the javascript (that toggles the dropdown) to use as hook.

So the end result is - a snippet that can output a nav item link with dynamic dropdown content. This is what it looks like:

<?php
	if (isset($item)) {
		$id = $item->id();
		$url = $item->url();
		$title = $item->title();
	} else {
		$url = $url ?? '#';
	}

	$itemId = 'nav-' . $id;
	$hasDropdown = is_callable($dropdown);
?>

<div id="<?= $itemId ?>" class="navbar__item flex">
	<a href="<?= $url ?>" class="flexed-center px-4">
		<?= $title ?>

		<?php if($hasDropdown): ?>
			<i class="fas fa-caret-down ml-2 text-accent-2"></i>
		<?php endif ?>
	</a>

	<?php if ($hasDropdown): ?>
		<div
			class="bg-white rounded"
			uk-dropdown="
				toggle: #<?= $itemId ?>;
				offset: 5;
				animation: uk-animation-slide-bottom-small;
				duration: 400;
			"
		>
			<?php $dropdown() ?>
		</div>
	<?php endif; ?>
</div>

$item is the $page here. I use UIkit for the dropdown.

1 Like

Thanks, yep, that looks much more useful than just passing a string in the example above.

1 Like

I’d say there’s nothing wrong with that approach from a technical perspective. :slight_smile:

1 Like