There are limitations to using the static website generator Hugo. With PHP, you can build server-side dynamics. This article uses examples to explain the configuration and different types of PHP calls in Hugo.
This tutorial is based on the blog article (resource unavailable at the time of writing) and on the know-how of Christoph Lik (totoff) - Hugo Forum - How to include a PHP form in HUGO - many thanks for that.

Setting up PHP recognition in Hugo

[!info] Hugo can output content in a variety of formats, including calendar events, ebook formats, Google AMP, and JSON search indexes, as well as any custom text format. You can read about setting up your site with media types and output formats, as well as where to create templates for your custom outputs here

Hugo does not work with PHP in its default configuration. The media type and output format must be configured in config.toml as follows:

[mediaTypes]
[mediaTypes."application/x-php"]
suffixes = ["php"]

[outputFormats]
[outputFormats.PHP]
mediaType = "application/x-php"
isPlainText = true
baseName = "index"
Media Types

mediaTypes recognizes the filename type and suffix.

Output Formats

The Hugo documentation - Configure Output Formats - says isPlainText = true: “isPlainText uses Go’s plain text template parser for templates.” This probably means that Hugo just passes everything marked with PHP through the generator as text. So you really should know what you’re doing.

Next is baseName = “index”. Obviously, a Leaf Bundle with index.md is expected with this configuration. Leaf Bundle see - Page Bundles - in the Hugo documentation. For multilingual web pages, the primary language is stored in index.md and the other language in, for example, index.en.md. Multilingual web pages are taken into account by Hugo via baseName.

Setting the output format in Front Matter

Front Matter is used to add metadata to your content.
The Markdown file structure looks like this:

content
|__ php-test
|__ index.md
|__ index.en.md

File index.md:

---
title: "PHP Test"
date: 2024-05-25T20:50:28+02:00
lastupdate: ""
draft: false
slug:
translationKey: phptest
description: "PHP Configuration in Hugo testen"
author: "Administrator"
outputs: PHP
---

Russian version of PHP test.


Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.

And the English version of index.en.md:

---
title: "Test PHP"
date: 2024-05-25T20:50:28+02:00
lastupdate: ""
draft: false
slug:test-php
translationKey: phptest
description: "Test PHP configuration in Hugo"
author: "Administrator"
outputs: PHP
---


English version of PHP test.


Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.

[!info] To debug the page display in the Hugo development environment, you can temporarily replace the value outputs: PHP with outputs: html, but then the PHP code will not work and the html templates will be used. The article will further discuss the use of PHP templates, so you should return outputs: PHP and for development it is recommended to install a local PHP server.

Creating Templates for PHP

Now if you save the above data and access it in a browser, you will get a “Not Found” error page. For PHP, you first need to create the corresponding templates.

Our Hugo theme has a base template baseof.html. The template is used to define the header, title, menu bar, content block, and footer. The template is integrated with the content block single.html. For PHP to work in Hugo, these two templates need to be copied and renamed as *.php files in the same directory. Subsequently, the baseof.php file will not be modified, but the single.php below will have the modifications.

Then the layouts directory structure will look like this:

layouts
|__default
|__baseof.html
|__baseof.php
|__list.html
|__single.html
|__single.php

If you now call Hugo Generator in your browser while Hugo is running localhost:1313/php-test, you will get a white empty web page. But that’s okay, because Hugo doesn’t provide a PHP server.

The article «Using a PHP web server locally with Hugo» describes how you can install a local web server that provides PHP and connect it to Hugo. When the MAMP server is running, you can access the new website page in your browser localhost:8888/php-test. The web server changes the URL string when displayed to localhost:8888/php-test/index.php.

No lines of PHP have been written yet. Now you can start inserting PHP code.

Inserting PHP into the Single.php template.

The layouts/_default/single.php template currently looks like this:

{{ define "main" }}
<section class="container">
<h1>{{ .Title }}</h1>
{{ .Content }}
</section>
{{end}}

In {{ .Content }} the text “Lorem ipsum..” is pulled from index.md. PHP is now added below:

{{ define "main" }}
<section class="container">
<h1>{{ .Title }}</h1>
{{ .Content }}

<?php
echo 'Your browser: <br>' . $_SERVER['HTTP_USER_AGENT'];
echo '<br>Author: ' . '{{ .Params.author }}';
?>

</section>
{{end}}

If you visit the page again, your browser’s user agent will be displayed under the text “Lorem ipsum ..” Calling it in a different browser also creates a different user agent. The runtime dynamics are now present through PHP.

Also, and this is really cool, you can access the variables Front Matter from the Markdown content file - index.md. In the example above, the PHP code uses the front matter variable - author. Thanks for the tip - frjo - very useful.

Calling a PHP file in a single.php template

When PHP code is saved in a file, Hugo’s Front Matter variables will no longer be available. The single.php template will then look like this:

{{ define "main" }}
<section class="container">
<h1>{{ .Title }}</h1>

{{ readFile "static/php/php_test_01.php" | safeHTML }}
{{ .Content }}

</section>
{{end}}

The PHP code to execute is now located in the php_test_01.php file. The directory path is:

webpractica
|__content
|__static
|__php
|__php_test_01.php

The PHP code for php_test_01.php is shown below:

<?php
echo 'Your browser: <br>' . $_SERVER['HTTP_USER_AGENT'];
?>

Calling PHP in shortcode

Shortcodes in Hugo allow you to execute code in a Markdown file. This can also be used for PHP. In single.php, remove the call to readFile.

{{ define "main" }}
<section class="container">
<h1>{{ .Title }}</h1>
{{ .Content }}
</section>
{{end}}

Shortcode with filename php.html:

<div>
{{ .Inner | safeHTML }}
</div>

Using the shortcode in the content file index.ru.md:

---
title: "PHP Test"
date: 2024-05-25T20:50:28+02:00
lastupdate: ""
draft: false
slug:
translationKey: phptest
description: "PHP Configuration in Hugo testen"
author: "Administrator"
outputs: PHP


Russian version of PHP test.


Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.


{{< php >}}
<?php
echo 'Your browser: <br>' . $_SERVER['HTTP_USER_AGENT'];
?>
{{< /php >}}
Accessing Hugo variables and parameters from PHP code in a shortcode

Let’s create two auxiliary shortcodes. Shortcode layouts/shortcodes/param_page.html to get the value from Front Matter:

{{- with index .Page.Params (.Get 0 ) -}}
{{- . -}}
{{- end -}}

and the shortcode layouts/shortcodes/param_site.html to get the value from the settings file hugo.toml:

{{- with index .Page.Site.Params (.Get 0 ) -}}
{{- . -}}
{{- end -}}

Now all that’s left is to create a shortcode to which we’ll pass the PHP code itself, layouts/shortcodes/php.html:

<div {{ if ( .Get "class" ) }} class='{{ .Get "class" }}' {{ end }} {{ if ( .Get "title" ) }} title='{{ .Get "title" }}' {{ end }}>
{{ .Inner | safeHTML }}
</div>

Usage:

{{% php class="php" title="Demo PHP" %}}
<?php
$mem_php_title='{{% param_page "title" %}}';
echo 'Title: ' . '{{% param_page "title" %}}';
echo '<br>Authors: ' . '{{% param_page "author" %}}';
echo '<br>Copyright: ' . '{{% param_site "copyright" %}}';
?>
{{% /php %}}

Conclusion

Once you know how the basic PHP structure should be implemented in Hugo, the rest is relatively easy. The big advantage of Hugo is the security of a static website. Digging deeper into PHP introduces another potential security hole. For this reason, you should only use PHP in Hugo if there is absolutely no other way.

[!NOTE] Examples of using PHP with Hugo can be found here