PHPPHP Tags & Embedding in HTML

PHP Tags & Embedding in HTML

PHP was built from day one to live inside HTML files. The parser treats everything outside a PHP tag as plain text to print verbatim, and everything between an opening and closing tag as code to execute. Knowing exactly which tag styles exist, which ones are safe to use in 2026, and how the parser switches in and out of "PHP mode" is foundational — get it wrong and you either leak raw PHP source to the browser or break a template that mixes markup with logic.

The standard tag: <?php ?>

The canonical, always-available way to open a block of PHP is <?php and the matching close is ?>. This pair works in every PHP installation, in every configuration, with no ini setting required — which is why it is the only style you should use for code that has to run reliably on servers you do not control.

index.php

PHP
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;body&gt;

&lt;?php
    $siteName = 'Let Codes';
    echo "Welcome to {$siteName}!";
?&gt;

&lt;/body&gt;
&lt;/html&gt;
<!DOCTYPE html>
<html>
<body>

Welcome to Let Codes!

</body>
</html>

Notice what actually happens: the whitespace and newlines inside the <?php ... ?> block are part of the code, not the output. Anything before the opening tag and after the closing tag is streamed to the browser exactly as written, byte for byte — PHP does not "understand" HTML at all, it just skips over it.

The short echo tag: <?= ?>

Printing a value is such a common operation inside templates that PHP has a dedicated shorthand for it. <?= $value ?> is exactly equivalent to <?php echo $value; ?>. Unlike the old "short open tag" (covered below), the short echo tag is always enabled, regardless of any php.ini setting, since PHP 5.4. This makes it the best tool for sprinkling dynamic values into otherwise static markup.

profile.php — short echo tags in a template

PHP
&lt;ul&gt;
    &lt;li&gt;Name: &lt;?= htmlspecialchars($user['name']) ?&gt;&lt;/li&gt;
    &lt;li&gt;Role: &lt;?= htmlspecialchars($user['role']) ?&gt;&lt;/li&gt;
    &lt;li&gt;Joined: &lt;?= $user['joinedAt']->format('Y-m-d') ?&gt;&lt;/li&gt;
&lt;/ul&gt;

A <?= ?> block can hold any expression, not just a bare variable — a function call, a ternary, string concatenation, method chaining. What it cannot hold is a full statement list; it evaluates one expression and echoes the result, so <?= $a; $b; ?> is a syntax error, not "print $a, then run $b."

The short open tag: <? ?> — deprecated, avoid it

Long before the short echo tag existed, some installations enabled a bare <? as a synonym for <?php, controlled by the short_open_tag ini directive. It still appears in old codebases and old tutorials, which is exactly why you need to recognize it — and avoid writing it yourself.

Do not use bare short open tags
`short_open_tag` defaults to **off** in modern PHP and many hosts disable it entirely for security and portability reasons. Code that relies on `<? ?>` will silently print as raw text instead of executing on any server where the setting is off — there is no warning or error, the PHP just never runs. Because the setting is also ambiguous with XML's `<?xml ... ?>` processing instruction (both start with `<?`), mixing short tags with XML content used to cause real parsing conflicts. Always write `<?php` in full.

Checking the setting on a given server

Bash
php -i | grep short_open_tag
# short_open_tag => Off => Off      (the common, safe default)

There is no good reason to turn short_open_tag on for new work. Every editor, linter, and framework in the modern PHP ecosystem assumes <?php and <?=; writing <? ?> only buys you four fewer keystrokes at the cost of code that breaks the moment it touches a stricter host.

Switching in and out of PHP mode repeatedly

Because the tag pair is just a mode switch, a single file can hop between HTML and PHP as many times as needed. This is the pattern classic PHP templates use instead of building one giant string of markup inside echo calls.

list.php — control structures wrapping HTML

PHP
&lt;?php $items = ['Apples', 'Bread', 'Milk']; ?&gt;

&lt;ul&gt;
&lt;?php foreach ($items as $item): ?&gt;
    &lt;li&gt;&lt;?= htmlspecialchars($item) ?&gt;&lt;/li&gt;
&lt;?php endforeach; ?&gt;
&lt;/ul&gt;
<ul>
    <li>Apples</li>
    <li>Bread</li>
    <li>Milk</li>
</ul>

The foreach (...): ... endforeach; alternative syntax exists specifically for this style of template — it reads better than curly braces once HTML is interleaved between the opening and closing of the loop. The same alternative syntax is available for if, while, for, and switch.

Closing tag omission in pure-PHP files

When a file contains only PHP — a class definition, a config file, an include with no trailing HTML — the closing ?> is conventionally left off entirely. This is not laziness; it prevents a specific, hard-to-diagnose bug.

src/Config.php — no closing tag

PHP
&lt;?php

class Config
{
    public const APP_NAME = 'Let Codes';
}

// File ends here. No "?&gt;" below this line.
Why the closing tag is dangerous in pure-PHP files
Anything after a final `?>`, including a single trailing space or newline character, is treated as literal output. If that file is later `require`d or `include`d — say, before a `header()` call or a session cookie is set — PHP will have already sent that one whitespace byte to the browser. That counts as output, which triggers the dreaded "headers already sent" error and can silently break redirects, cookies, and file downloads. Omitting the closing tag makes the mistake impossible: there is nothing after the last line of code to leak.
When embedding still makes sense — and when it does not
  • Good fit: simple, mostly-static templates where a handful of values or a loop need to be injected into markup — landing pages, email templates, legacy view files.

  • Poor fit: anything with non-trivial branching logic. Once a template needs more than a couple of if/foreach blocks, mixing tag-switching with real logic gets hard to read and test — reach for a templating engine (e.g. Twig, Blade) or at least separate the logic into a controller/service and keep the view file to output only.

  • Never: writing business logic — database queries, validation, authorization checks — directly inside a tag-switching HTML template. Keep that in PHP-only files and pass already-prepared data into the view.

Tags are not statements
`<?php` and `?>` are not PHP statements themselves — they are instructions to the parser about which language it is currently reading. This is why `<?php ?><?php ?>` back to back is legal and does nothing: it is just "enter PHP mode, immediately leave, enter again, immediately leave."
Tip
Configure your editor or linter to flag any `<?` that is not immediately followed by `php` or `=`. Catching an accidental short open tag at write-time is far cheaper than debugging why a snippet of "PHP" showed up as literal text on a production server that has `short_open_tag` disabled.