phphtmlspecialchars

htmlspecialchars won't accept 'variable variables' as an argument


I'm through reading a book: "PHP and MySQL ® Web Development 4th edition" by Luke Welling & Laura Thomson. On page 49. author suggest additional esoteric (for me at least) way to deal with form. Namely, if names in html form have same values which are distinct just by number incremented by one, we could use 'variable variables' type of variable in echo statment to dynamicaly extract values from The form...:

Here is my html:

<form action="processorder.php" method="post">
  <table style="border: 0px;">
    <tr style="background: #cccccc;">
      <td style="width: 150px; text-align: center;">Item</td>
      <td style="width: 15px; text-align: center;">Quantity</td>
    </tr>
    <tr>
      <td>Tires</td>
      <td><input type="text" name="name1" size="3" maxlength="3" /></td>
    </tr>
    <tr>
      <td>Oil</td>
      <td><input type="text" name="name2" size="3" maxlength="3" /></td> 
    </tr>
    <tr>
      <td>Spark Plugs</td>
      <td><input type="text" name="name3" size="3" maxlength="3" /></td>
    </tr>
    <tr>
      <td colspan="2" style="text-align: center;"><input type="submit" value="Submit Order" /></td>
    </tr>
  </table>
  <tr>
    <td>How did you find Bob's?</td>
    <td>
      <select name="find">
        <option value = "a">I'm a regular customer</option>
        <option value = "b">TV adversting</option>
        <option value = "c">Phone directory</option>
        <option value = "d">Word of mouth</option>
      </select>
    </td>
  </tr>
</form>

Here is my .php file:

<!DOCTYPE html>
<html>
  <head>
    <title>Bob's Auto Parts - Order Results</title>
  </head>
  <body>
    <h1>Bob's Auto Parts</h1>
    <h2>Order Results</h2>
    <?php
      //create short variable names
      
      $tireqty  = $_POST['name1'];
      $oilqty   = $_POST['name2'];
      $sparkqty = $_POST['name3'];
      
      for ($i=1; $i <= 3; $i++){
        $temp = "name$i";
        echo htmlspecialchars($$temp).'<br />'; // or whatever processing we want to do
      }
      
      echo "<p>Order processed at ";
      echo date('H:i, jS F Y');// (string as an argument)
      echo "</p>";
      
      // or: 
      // echo "<p>Order processed at " . date('H:i, jS F Y') . "</p>";
      // (avoiding multiple echo commands)
      
      
      echo "<p>Your order is as follows: </p>";
      echo htmlspecialchars($tireqty)." tires<br />";
      echo htmlspecialchars($oilqty)." bottles of oil<br />";
      echo htmlspecialchars($sparkqty)." spark plugs<br />";
      
      $totalqty = 0;
      $totalqty = $tireqty + $oilqty + $sparkqty;
      echo "<p>Items ordered " . $totalqty . "<br />";
      $totalamount = 0.00;
      
      define('TIREPRICE', 100);
      define('OILPRICE', 10);
      define('SPARKPRICE', 4);
      $totalamount = $tireqty * TIREPRICE + $oilqty * OILPRICE + $sparkqty * SPARKPRICE;
      echo "Subtotal: $:" . number_format($totalamount, 2) . "<br />";
      
      $taxrate = 0.10;
      $totalamount = $totalamount * (1 + $taxrate);
      echo "Total including taxes is: $" . number_format($totalamount, 2) . "</p>";
      
      // echo 'isset($tireqty): '.isset($tireqty).'<br />';
      // echo 'isset($nothere): '.isset($nothere).'<br />';
      // echo 'empty($tireqty): '.empty($tireqty).'<br />';
      // echo 'empty($nothere): '.empty($nothere).'<br />';
      
      
      
      
    ?>
    
  </body>
</html>

Here is where my problem is:

  for ($i=1; $i <= 3; $i++){
    $temp = "name$i";
    echo htmlspecialchars($$temp).'<br />'; 
  }

Script doesn't react nor accept parameter to be some other kind then simple variable. Even an editor (np++) doesn't accept second "$" sign.


Solution

  • This actually has nothing to do with the htmlspecialchars() function. When you write this in your loop:

    $temp = "name$i"
    

    You're creating a string with the value name1. When you then try to use it with a 'variable variable', like this:

    $$temp
    

    The code is looking for a variable called $name1, which does not exist.

    All you really need to do to fix your original code is generate the key name you want to use from the $_POST array:

    for ($i = 1; $i <= 3; $i++) {
        echo htmlspecialchars($_POST["name$i"]) . '<br />';
    }
    

    However, this is a somewhat inelegant solution anyway.


    A Better Solution

    You could start by changing your input names, so they all come into the $_POST array as their own numerically indexed array.

    <tr>
        <td>Tyres</td>
        <td><input type="text" name="name[]" size="3" maxlength="3" /></td>
    </tr>
    <tr>
        <td>Oil</td>
        <td><input type="text" name="name[]" size="3" maxlength="3" /></td> 
    </tr>
    <tr>
        <td>Spark Plugs</td>
        <td><input type="text" name="name[]" size="3" maxlength="3" /></td>
    </tr>
    

    You can then loop through $_POST['name'] and echo those values easily:

    foreach ($_POST['name'] as $val) {
        echo htmlspecialchars($val) . '<br>';
    }
    

    Are either of these things an ideal solution? No, not really. You know you need a form field for tyres, oil and spark plugs, so trying to invoke a pattern where all the inputs are called name makes no sense in the first place. You would generally do this when the form fields all belong to one entity, or if you don't know how many rows there will be when the form is submitted.

    What is $_POST['name2'] or $_POST['name'][1]? Who knows. You have to read through your code and find the form to double check which field that belongs to.

    Code should be semantic and easily understood by anyone reading it (including yourself, when you look back at your own code in the future).

    I would rename the form inputs properly:

    <tr>
        <td>Tyres</td>
        <td><input type="text" name="tyres" size="3" maxlength="3" /></td>
    </tr>
    <tr>
        <td>Oil</td>
        <td><input type="text" name="oil" size="3" maxlength="3" /></td> 
    </tr>
    <tr>
        <td>Spark Plugs</td>
        <td><input type="text" name="spark_plugs" size="3" maxlength="3" /></td>
    </tr>
    

    And then everything becomes much more straightforward and understandable. In your code you have three values available. You know what they are, and what the names mean.

    echo htmlspecialchars($_POST['tyres']) . '<br>';
    echo htmlspecialchars($_POST['oil']) . '<br>';
    echo htmlspecialchars($_POST['spark_plugs']) . '<br>';
    

    If you then want to use fancy loops and things with those, there's much I could go further into, but it's generally undesirable to over complicate everything. You end up producing more bugs, making your code more difficult for everyone to interpret, and making the process of debugging your code more time consuming.