web php

HTML, CSS, And JavaScript Integration

PHP often renders the first HTML response. CSS controls presentation. JavaScript adds client-side behaviour. A PHP developer does not need to be a frontend specialist to work safely here, but they do need to understand how server-rendered data crosses into HTML, CSS, and JavaScript.

The key rule is context: a value printed into HTML must be escaped for HTML, and a value passed into JavaScript should be JSON encoded.

Render HTML and pass small JS data safely

PHP example
<?php

declare(strict_types=1);

$pageTitle = 'Course progress';
$progress = ['completed' => 7, 'total' => 12];

$title = htmlspecialchars($pageTitle, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$progressJson = json_encode(
    $progress,
    JSON_THROW_ON_ERROR | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
);

echo "<h1>{$title}</h1>" . PHP_EOL;
echo "<script>const progress = {$progressJson};</script>" . PHP_EOL;

// Prints:
// <h1>Course progress</h1>
// <script>const progress = {"completed":7,"total":12};</script>

Do not build JavaScript strings by manually concatenating quotes and user data. JSON encoding knows how to represent strings, numbers, booleans, arrays, and objects safely for JavaScript.

A server-rendered page can link static CSS and JavaScript assets.

PHP example
<?php

declare(strict_types=1);

$assetVersion = '2026-05-28';

echo '<link rel="stylesheet" href="/assets/app.css?v=' . rawurlencode($assetVersion) . '">' . PHP_EOL;
echo '<script src="/assets/app.js?v=' . rawurlencode($assetVersion) . '" defer></script>' . PHP_EOL;

// Prints:
// <link rel="stylesheet" href="/assets/app.css?v=2026-05-28">
// <script src="/assets/app.js?v=2026-05-28" defer></script>

defer lets the browser download the script without blocking HTML parsing, then run it after the document has been parsed. It is a good default for many server-rendered pages.

The query string version is a simple cache-busting technique. Larger projects often use a build tool that creates hashed filenames instead.

Data attributes are useful for small values

For small pieces of data tied to an element, data-* attributes are often simpler than a script block.

PHP example
<?php

declare(strict_types=1);

function escapeHtml(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

$userId = '42';
$displayName = 'Asha <Admin>';

echo '<button data-user-id="' . escapeHtml($userId) . '">'
    . escapeHtml($displayName)
    . '</button>' . PHP_EOL;

// Prints:
// <button data-user-id="42">Asha &lt;Admin&gt;</button>

JavaScript can read element.dataset.userId. Keep data attributes small and simple. Do not dump large application state into HTML attributes.

Progressive enhancement

Progressive enhancement means the server-rendered page still does the important job without JavaScript, while JavaScript improves the experience when available.

A delete button might be a normal POST form first. JavaScript can later add a confirmation modal or AJAX request.

PHP example
<?php

declare(strict_types=1);

$id = 42;

echo '<form method="post" action="/tasks/' . $id . '/delete">'
    . '<button type="submit" data-confirm="Delete this task?">Delete</button>'
    . '</form>' . PHP_EOL;

// Prints:
// <form method="post" action="/tasks/42/delete"><button type="submit" data-confirm="Delete this task?">Delete</button></form>

The server route must still validate authorization and CSRF tokens. JavaScript is not a security boundary.

Keep CSS concerns in CSS

PHP should choose semantic classes and state attributes, not generate lots of inline styles.

PHP example
<?php

declare(strict_types=1);

$status = 'overdue';
$allowedStatuses = ['paid', 'pending', 'overdue'];

$class = in_array($status, $allowedStatuses, true) ? 'invoice--' . $status : 'invoice--pending';

echo '<span class="invoice ' . $class . '">Overdue</span>' . PHP_EOL;

// Prints:
// <span class="invoice invoice--overdue">Overdue</span>

The CSS file can decide what .invoice--overdue looks like.

What to check in a project

Check that server values printed into HTML are escaped with the right helper or template engine syntax.

Check that data passed to JavaScript uses JSON encoding, not hand-built JavaScript.

Check that linked scripts use defer or an intentional loading strategy.

Check that JavaScript enhancements do not replace server-side validation, authorization, or CSRF protection.

Check that PHP is not generating unnecessary inline CSS when a class or data attribute would be cleaner.

What you should be able to do

After this lesson, you should be able to render safe HTML, link CSS and JavaScript assets, pass PHP data into JavaScript safely, use data attributes for small values, and explain why JavaScript enhances but does not secure a PHP application.

Practice

Task: Render A Page Shell

Write a small PHP script that renders a page shell with HTML, linked assets, and safe JavaScript data.

Requirements

  • Use declare(strict_types=1);.
  • Escape the page title for HTML.
  • Link a CSS file.
  • Link a JavaScript file with defer.
  • Pass a small PHP array into JavaScript using json_encode().
  • Include one value that contains characters like < or & to prove escaping.
  • Print the rendered output.

Check Your Work

Run the script and confirm that HTML text is escaped while the JavaScript data is valid JSON.

Show solution

This solution uses different output strategies for HTML and JavaScript.

PHP example
<?php

declare(strict_types=1);

function escapeHtml(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

$title = 'Course <Progress>';
$assetVersion = '2026-05-28';
$progress = ['completed' => 7, 'total' => 12, 'label' => 'PHP & web'];

$progressJson = json_encode(
    $progress,
    JSON_THROW_ON_ERROR | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
);

echo '<!doctype html>' . PHP_EOL;
echo '<title>' . escapeHtml($title) . '</title>' . PHP_EOL;
echo '<link rel="stylesheet" href="/assets/app.css?v=' . rawurlencode($assetVersion) . '">' . PHP_EOL;
echo '<script src="/assets/app.js?v=' . rawurlencode($assetVersion) . '" defer></script>' . PHP_EOL;
echo '<h1>' . escapeHtml($title) . '</h1>' . PHP_EOL;
echo '<script>window.progress = ' . $progressJson . ';</script>' . PHP_EOL;

// Prints:
// <!doctype html>
// <title>Course &lt;Progress&gt;</title>
// <link rel="stylesheet" href="/assets/app.css?v=2026-05-28">
// <script src="/assets/app.js?v=2026-05-28" defer></script>
// <h1>Course &lt;Progress&gt;</h1>
// <script>window.progress = {"completed":7,"total":12,"label":"PHP \u0026 web"};</script>

The title is escaped for HTML because it is rendered into <title> and <h1>. The progress data is JSON encoded because it is consumed by JavaScript.

Why This Works

The example uses the right escaping for each context. It also links CSS and JavaScript as assets instead of mixing all behaviour into the PHP file.