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
<!DOCTYPE html>
<html>
<body>
<?php
$siteName = 'Let Codes';
echo "Welcome to {$siteName}!";
?>
</body>
</html><!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
<ul>
<li>Name: <?= htmlspecialchars($user['name']) ?></li>
<li>Role: <?= htmlspecialchars($user['role']) ?></li>
<li>Joined: <?= $user['joinedAt']->format('Y-m-d') ?></li>
</ul>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.
Checking the setting on a given server
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 $items = ['Apples', 'Bread', 'Milk']; ?>
<ul>
<?php foreach ($items as $item): ?>
<li><?= htmlspecialchars($item) ?></li>
<?php endforeach; ?>
</ul><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
class Config
{
public const APP_NAME = 'Let Codes';
}
// File ends here. No "?>" below this line.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 ofif/foreachblocks, 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.