Hello,

I know the safest way to write a pagination section with php is to use the http_build_query().
Like so:

$i = 0;
while($i<$total_pages)
{
    $i++;
    if($_GET['bool']=='null')
    {
        //Page Format: $_GET List.
        $array = array("tbl"=>"$tbl","col_1"=>"$col_1","bool"=>"$bool","input_1"=>"$input_1","lmt"=>"$limit","pg"=>"$i");
    }
    else
    {
        //Page Format: $_GET List.
        $array = array("tbl"=>"$tbl","col_1"=>"$col_1","col_2"=>"$col_2","bool"=>"$bool","input_1"=>"$input_1","input_2"=>"$input_2","lmt"=>"$limit","pg"=>"$i");
    }

    $serps_url = $_SERVER['PHP_SELF'].'?'.http_build_query($array);

    if($i==$page)
    {
        echo '<a href="' .htmlspecialchars($serps_url) .'">' ."<b>$i</b>" .'</a>';
    }
    else
    {
        echo '<a href="' .htmlspecialchars($serps_url) .'">' ."$i" .'</a>';
    }
}

I believe the above code is buggy because there is no need to use the htmlspecialchars() here.
Am I correct ?
Is the following code ok or not ?

$i = 0;
while($i<$total_pages)
{
    $i++;
    if($_GET['bool']=='null')
    {
        //Page Format: $_GET List.
        $array = array("tbl"=>"$tbl","col_1"=>"$col_1","bool"=>"$bool","input_1"=>"$input_1","lmt"=>"$limit","pg"=>"$i");
    }
    else
    {
        //Page Format: $_GET List.
        $array = array("tbl"=>"$tbl","col_1"=>"$col_1","col_2"=>"$col_2","bool"=>"$bool","input_1"=>"$input_1","input_2"=>"$input_2","lmt"=>"$limit","pg"=>"$i");
    }

    $serps_url = $_SERVER['PHP_SELF'].'?'.http_build_query($array);

    if($i==$page)
    {
        echo '<a href="' .$serps_url .'">' ."<b>$i</b>" .'</a>';
    }
    else
    {
        echo '<a href="' .$serps_url .'">' ."$i" .'</a>';
    }
}

Page Format 1: https://localhost/Work/buzz/Templates/Pagination_TEMPLATE.php?tbl=links&bool=null&col_1=domain&input_1=brute.com&lmt=1&pg=1

Page Format 2: https://localhost/Work/buzz/Templates/Pagination_TEMPLATE.php?tbl=links&bool=null&col_1=domain&col_2=email_domain&input_1=brute.com&input_2=brute.com&lmt=1&pg=1

Dani AI

Generated

Short answer: both patterns in this thread are valid—pick one consistent escaping strategy and stick to it. As explained, query values should be produced with http_build_query() (it URL‑encodes parameter values for you) and you must ensure the final string is safe for HTML output. This note fills a few practical gaps (encoding nuance, when to cast, and a small security caution).

Practical rules to follow:

  • Let http_build_query() handle query‑value encoding; do not urlencode() those values first (double‑encoding is a common pitfall).
  • Always normalize numeric parameters like the page index: cast with (int) or validate with a filter when reading and when you put the value into the params array.
  • rawurlencode() is for URL path segments, not query strings — http_build_query() is the correct tool for queries.

Encoding and escaping nuance:

  • There are two equivalent ways to produce valid HTML links: either generate a query string with & and escape the whole URL when you print it into an attribute (recommended), or tell http_build_query() to emit &amp; and then print the result without further escaping. Do NOT do both — that’s what causes &amp;amp; in your output. When escaping, use htmlspecialchars(..., ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') so quotes and malformed bytes are handled safely.

Security and robustness:

  • Don’t assume $_SERVER['PHP_SELF'] is safe to echo raw; prefer $_SERVER['SCRIPT_NAME'], a hardcoded script path, or always escape it before output. Sanitize incoming $_GET values (use casts or filter_input) before trust or use in queries.

Quick checklist:

  • Use http_build_query() for query values; don’t pre-encode them.
  • Cast pg to integer when building and validating.
  • Choose one escaping approach (escape at output OR emit HTML-ready &amp;) and avoid double-encoding.
  • Escape the final URL with htmlspecialchars(..., ENT_QUOTES, 'UTF-8') if you output raw &.
  • Avoid echoing unescaped $_SERVER['PHP_SELF'].

Recommended Answers

All 12 Replies

No. The other thread is not related to http_build_query() function even though it is related to PAGINATION SECTION like this one.

Shall I stick to my first code in my original post and discard the second one ?

Yes, you should. The first one correctly converts & to &amp;.

Both of these are equally valid:

// Version 1 converts & to &amp; with htmlspecialchars()
$serps_url = $_SERVER['PHP_SELF'].'?'.http_build_query($array);
echo '<a href="' .htmlspecialchars($serps_url) .'">' ."<b>$i</b>" .'</a>';

// Version 2 converts & to &amp; with http_build_query()
$serps_url = $_SERVER['PHP_SELF'].'?'.http_build_query($array, '', '&amp;');
echo '<a href="' . $serps_url .'">' ."<b>$i</b>" .'</a>';

and this is not valid HTML:

// This produces HTML that includes & where it should be &amp;
$serps_url = $_SERVER['PHP_SELF'].'?'.http_build_query($array);
echo '<a href="' .$serps_url .'">' ."<b>$i</b>" .'</a>';

Your answer cannot get any clearer than that!
Thank You!

commented: Thank you! +34

I have created Pagination Section Templates.
Here they are:

TEMPLATE 1

$i = 0;
while($i<$total_pages)
{
    $i++;
    if($bool=='and' || $bool=='or')
    {
        $serps_url = $_SERVER['PHP_SELF'].'?'.'tbl='.urlencode($tbl).'&'.'col_1='.urlencode($col_1).'&'.'col_2='.urlencode($col_2).'&'.'bool='.$bool.'&'.'input_1='.urlencode($input_1).'&'.'input_2='.urlencode($input_2).'&'.'lmt='.intval($limit).'&'.'pg='.intval($i);
    }
    else
    {
        $serps_url = $_SERVER['PHP_SELF'].'?'.'tbl='.urlencode($tbl).'&'.'col_1='.urlencode($col_1).'&'.'bool='.urlencode($bool).'&'.'input_1='.urlencode($input_1).'&'.'lmt='.intval($limit).'&'.'pg='.intval($i);
    }
    if($i==$page)
    {
        echo '<a href="' .htmlspecialchars($serps_url) .'">' ."<b>$i</b>" .'</a>'; //Need to add htmlspecialchars(), to convert '&' to '&amp', when echoing link here.
    }
    else
    {
        echo '<a href="' .htmlspecialchars($serps_url) .'">' ."$i" .'</a>'; //Need to add htmlspecialchars(), to convert '&' to '&amp', when echoing link here.
    }
}

TEMPLATE 2

$i = 0;
while($i<$total_pages)
{
    $i++;
    if($_GET['bool']=='null')
    {
        //Page Format: $_GET List.
        $array = array("tbl"=>"$tbl","col_1"=>"$col_1","bool"=>"$bool","input_1"=>"$input_1","lmt"=>"$limit","pg"=>"$i");
    }
    else
    {
        //Page Format: $_GET List.
        $array = array("tbl"=>"$tbl","col_1"=>"$col_1","col_2"=>"$col_2","bool"=>"$bool","input_1"=>"$input_1","input_2"=>"$input_2","lmt"=>"$limit","pg"=>"$i");
    }

    $serps_url = $_SERVER['PHP_SELF'].'?'.http_build_query($array); //Did not convert '&' to '&amp' and so need to add htmlspecialchars() when echoing link.

    if($i==$page)
    {
        echo '<a href="' .htmlspecialchars($serps_url) .'">' ."<b>$i</b>" .'</a>'; //Need to add htmlspecialchars(), to convert '&' to '&amp', when echoing link here.
    }
    else
    {
        echo '<a href="' .htmlspecialchars($serps_url) .'">' ."$i" .'</a>'; //Need to add htmlspecialchars(), to convert '&' to '&amp', when echoing link here.
    }
}

TEMPLATE 3

$i = 0;
while($i<$total_pages)
{
    $i++;
    if($_GET['bool']=='null')
    {
        //Page Format: $_GET List.
        $array = array("tbl"=>"$tbl","col_1"=>"$col_1","bool"=>"$bool","input_1"=>"$input_1","lmt"=>"$limit","pg"=>"$i");
    }
    else
    {
        //Page Format: $_GET List.
        $array = array("tbl"=>"$tbl","col_1"=>"$col_1","col_2"=>"$col_2","bool"=>"$bool","input_1"=>"$input_1","input_2"=>"$input_2","lmt"=>"$limit","pg"=>"$i");
    }

    $serps_url = $_SERVER['PHP_SELF'].'?'.http_build_query($array,'','&amp'); //Did convert '&' to '&amp' and so NO need to add htmlspecialchars() when echoing link.

    if($i==$page)
    {
        echo '<a href="' .$serps_url .'">' ."<b>$i</b>" .'</a>'; //No need to add htmlspecialchars(), to convert '&' to '&amp', when echoing link here.
    }
    else
    {
        echo '<a href="' .$serps_url .'">' ."$i" .'</a>'; //No need to add htmlspecialchars(), to convert '&' to '&amp', when echoing link here.
    }
}

TEMPLATE 4

$i = 0;
while($i<$total_pages)
{
    $i++;
    if($_GET['bool']=='null')
    {
        //Page Format: $_GET List.
        $array = array("tbl"=>"$tbl","col_1"=>urlencode("$col_1"),"bool"=>urlencode("$bool"),"input_1"=>urlencode("$input_1"),"lmt"=>urlencode("$limit"),"pg"=>intval("$i"));
    }
    else
    {
        //Page Format: $_GET List.
        $array = array("tbl"=>"$tbl","col_1"=>urlencode("$col_1"),"col_2"=>urlencode("$col_2"),"bool"=>urlencode("$bool"),"input_1"=>urlencode("$input_1"),"input_2"=>urlencode("$input_2"),"lmt"=>urlencode("$limit"),"pg"=>intval("$i"));
    }

    $serps_url = $_SERVER['PHP_SELF'].'?'.http_build_query($array);

    if($i==$page)
    {
        echo '<a href="' .htmlspecialchars($serps_url) .'">' ."<b>$i</b>" .'</a>';
    }
    else
    {
        echo '<a href="' .htmlspecialchars($serps_url) .'">' ."$i" .'</a>';
    }
}

NOTE: I have a big question on TEMPLATE 4. I remember you said if I use http_build_query() then no need to use urlencode() or raw_urlencode() as http_build_query() will auto do them. But what-about INT_VAL() ? I am discarding urlencode() and raw_urlencode() from above and I believe I should discard the INT_VAL() too. Correct ? Or should I leave it ? What is the correct thing to do ?

I remember you said if I use http_build_query() then no need to use urlencode() or raw_urlencode() as http_build_query() will auto do them.

Yes, when I look at the official PHP documentation for http_build_query() I can see that it says that the description of this function is that it "Generates a URL-encoded query string from the associative (or indexed) array provided." Therefore, there is no need to run urlencode() since it does it for me.

rawurlencode() is a whole different matter. Remember, http_build_query() only operates on query strings. rawurlencode() is used on the URL path. You still need to use rawurlencode() if the URL path you want to use is not already URL encoded. In your case, the URL path is set to $_SERVER['PHP_SELF'] which is already properly URL encoded, so there's no need to use it.

You will still want to use intval() to make sure that the 'pg' passed in is an integer. What if someone visits the URL ?pg=blah when you are expecting pg to be an integer?

Things are clear now.
Thank you very much.
So, by default, php encodes $_SERVER['PHP_SELF'].
Correct ?

The purpose of urlencode and rawurlencode is to translate weird characters that have no place being in URLs into a form that is URL-friendly, so that freeform strings can be properly transported within a valid-formatted URL without the URL breaking.

For example, is not a valid URL.

When you use user input (that can literally be absolutely anything) to dynamically build your URL, there’s a chance of the end user entering wacky data that makes no sense in the context of a properly formatted URL. So that’s why we encode the bits and segments of the URL that have the possibility to be “weird”, to ensure that our URL looks and functions like a valid URL. These urlencode functions “translate” weird characters and symbols that have no business being in a URL into encoded versions that represent the characters but in a way that is allowed in URL strings.

$_SERVER[PHP_SELF] is the URL of the current page, according to php. If PHP is telling us it’s the URL representing the page we are on, it goes without question that it’s a valid URL. There’s no need to encode any invalid characters because we won’t find any invalid characters in a valid URL.

,

I do not know why this was working last night:

$serps_url = $_SERVER['PHP_SELF'].'?'.http_build_query($array,'','&amp');

Was not working tonight. After an hrs of test, I spotted my error:

$serps_url = $_SERVER['PHP_SELF'].'?'.http_build_query($array,'','&amp;');

Now it is working.
I now check back on your feed-back above and see you had the semi colon. Do not know why I missed it.
Bothering to mention my mistake here incase any newbie falls prey to the same mistake. Got to lookout for that semi colon in such situations.

No need to reply.

Question solved by Proprietor: Dani.

I'll mark this question as solved. Glad you got it working.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.