web php

URL Parsing And Building

PHP applications often need to read URLs, preserve query parameters, build pagination links, validate redirect targets, and generate links safely.

Do not build URLs by casually concatenating untrusted strings. Use the standard parsing and encoding functions so spaces, ampersands, and special characters are represented correctly.

Parse URL parts

PHP example
<?php

declare(strict_types=1);

$url = 'https://example.com/products?page=2&search=php';
$parts = parse_url($url);

echo $parts['scheme'] . PHP_EOL;
echo $parts['host'] . PHP_EOL;
echo $parts['path'] . PHP_EOL;
echo $parts['query'] . PHP_EOL;

// Prints:
// https
// example.com
// /products
// page=2&search=php

parse_url() does not validate that a URL is safe to use. It only breaks the string into parts.

Parse query strings

Use parse_str() to turn a query string into an array.

PHP example
<?php

declare(strict_types=1);

parse_str('page=2&search=php+arrays', $query);

echo $query['page'] . PHP_EOL;
echo $query['search'] . PHP_EOL;

// Prints:
// 2
// php arrays

Treat parsed query values as untrusted input. Validate expected types and allowed values before using them.

Build query strings

Use http_build_query() when generating links.

PHP example
<?php

declare(strict_types=1);

$query = [
    'page' => 3,
    'search' => 'php arrays',
    'sort' => 'newest',
];

$url = '/products?' . http_build_query($query);

echo $url . PHP_EOL;

// Prints:
// /products?page=3&search=php+arrays&sort=newest

This is safer than writing '/products?page=' . $page . '&search=' . $search by hand.

Preserve filters while changing one value

Pagination often needs to keep the current filters while changing the page number.

PHP example
<?php

declare(strict_types=1);

/**
 * @param array<string, scalar|null> $currentQuery
 */
function pageUrl(string $path, array $currentQuery, int $page): string
{
    $query = array_merge($currentQuery, ['page' => $page]);

    return $path . '?' . http_build_query($query);
}

echo pageUrl('/products', ['search' => 'php', 'sort' => 'newest'], 4) . PHP_EOL;

// Prints:
// /products?search=php&sort=newest&page=4

Safe local redirects

Redirect targets are a common URL security issue. If you accept a return_to value from the request, do not blindly redirect to it. That creates an open redirect.

For local redirects, allow only paths that start with one / and do not start with //.

PHP example
<?php

declare(strict_types=1);

function safeLocalPath(string $target): string
{
    if (str_starts_with($target, '/') && !str_starts_with($target, '//')) {
        return $target;
    }

    return '/';
}

echo safeLocalPath('/dashboard') . PHP_EOL;
echo safeLocalPath('https://evil.example') . PHP_EOL;

// Prints:
// /dashboard
// /

Escape URLs in HTML

After building a URL, escape it before placing it in HTML.

PHP example
<?php

declare(strict_types=1);

$url = '/products?' . http_build_query(['search' => 'PHP & web']);
$safeUrl = htmlspecialchars($url, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');

echo '<a href="' . $safeUrl . '">Search</a>' . PHP_EOL;

// Prints:
// <a href="/products?search=PHP+%26+web">Search</a>

What to check in a project

Check that query strings are built with http_build_query() or framework URL helpers.

Check that redirect targets are constrained to local paths unless external redirects are explicitly allowed.

Check that URL parameters are validated after parsing. page=abc should not become a broken database query.

Check that generated URLs are escaped when rendered into HTML.

What you should be able to do

After this lesson, you should be able to parse URL parts, parse and build query strings, preserve filters while changing pagination, escape URLs in HTML, and avoid open redirects.

Practice

Task: Build Pagination URLs

Write a small PHP script that builds pagination URLs while preserving existing filters.

Requirements

  • Use declare(strict_types=1);.
  • Create a function that accepts a path, current query array, and target page.
  • Preserve existing query values.
  • Replace or add the page value.
  • Use http_build_query().
  • Include a normal filtered URL.
  • Include an edge case where the query is empty.
  • Print both URLs.

Check Your Work

Run the script and confirm spaces and ampersands are encoded correctly.

Show solution

This solution lets PHP handle URL encoding instead of concatenating query strings by hand.

PHP example
<?php

declare(strict_types=1);

/**
 * @param array<string, scalar|null> $currentQuery
 */
function paginationUrl(string $path, array $currentQuery, int $page): string
{
    $query = array_merge($currentQuery, ['page' => $page]);
    $queryString = http_build_query($query);

    return $queryString === '' ? $path : $path . '?' . $queryString;
}

echo paginationUrl('/products', ['search' => 'PHP & web', 'sort' => 'newest'], 2) . PHP_EOL;
echo paginationUrl('/products', [], 1) . PHP_EOL;

// Prints:
// /products?search=PHP+%26+web&sort=newest&page=2
// /products?page=1

When rendering this URL into an href, escape it with htmlspecialchars() as part of HTML output.

Why This Works

The filtered case proves existing values are preserved and encoded. The empty case proves the function still builds a valid page link.