Categories
HTML MVC PHP

Picture Uploads in PHP MVC Applications

Basic picture file uploads are handled on the client-side by a form and on the server-side by a script moving a cached temporary file to a permanent location while recording the appropriate path information to database. In this post, we will elaborate on each of these portions.

The HTML Form

For forms to upload files correctly, 2 requirements must be met: a named file input must be included in the form and the encoding type must be appropriately. We show below an example proper form:

  1. <form action='' method='post' enctype='multipart/form-data'>
  2.   <div class='form-group'>
  3.     <label>Picture:
  4.       <input type='file' name='newPicture' class='form-control' />
  5.     </label>
  6.   </div>
  7.   <div class='form-group'>
  8.     <label>Description:
  9.       <textarea name='description' class='form-control'></textarea>
  10.     </label>
  11.   </div>
  12.   <input type='submit' name='action' value='Add picture' class='btn btn-primary' />
  13.   <a href='/product/index' class='btn btn-secondary'>Cancel</a>
  14. </form>

First, we see on line 1 that the enctype attribute for the form is set to multipart/form-data. This encodes the submitted form data with different parts for different data, much like an email with attachments included. If the form enctype attribute to this value, the file is simply not received by the server application.

Second, we see on line 4 an input with the type attribute set to the value file. This input also has the attribute name set to newPicture so that we may refer to it by this name in the server script.

The submit URL for this form will be the same as its calling URL.

The PHP Script

Now, a server-side script must receive the form data, check the data and save the file as appropriate:

  1. public function addPicture($product_id){
  2.   if(isset($_FILES['newPicture']) 
  3.     && $_FILES['newPicture']['error'] == UPLOAD_ERR_OK){
  4.     $info = getimagesize($_FILES['newPicture']['tmp_name']);
  5.     $allowedTypes = [IMAGETYPE_JPEG=>'.jpg',
  6.                      IMAGETYPE_PNG=>'.png',
  7.                      IMAGETYPE_GIF=>'.gif'];//accept jpg, png, gif
  8.     if($info === false){ // no go
  9.       $this->view('product/addPicture', ['error'=>'Bad file format']);
  10.     }else if(!array_key_exists($info[2], $allowedTypes)){ // no go
  11.       $this->view('product/addPicture', 
  12.         ['error'=>'Not an accepted file type']);
  13.     }else{
  14.       //save the picture in the images folder
  15.       $path = getcwd().DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR;
  16.       $filename = uniqid().$allowedTypes[$info[2]];
  17.       move_uploaded_file($_FILES['newPicture']['tmp_name'], $path.$filename);
  18.  
  19.       $newPicture = $this->model('Picture');
  20.       $newPicture->product_id = $product_id;
  21.       $newPicture->filename = $filename;
  22.       $newPicture->description = $_POST['description'];
  23.       $newPicture->create();
  24.       header('location:/product/index');
  25.     }
  26.   }else{
  27.     $this->view('product/addPicture');
  28.   }
  29. }

This method is meant to accept files associated to products in a product catalog. Therefore, on line 1, we need to pass in the product_id foreign key value for the picture record to refer to the appropriate product record.

On lines 2 and 3, we verify that the picture is indeed sent (line 2) to the receiving script without any error (line 3). If not, the script branches to line 27 and presents the submission form.

On lines 4 and 8, we extract image file information (line 4) that will allow us to validate (on line 8) that the received file is indeed an image. If not, an error message is sent to the user on line 9.

At line 10, the image information (from line 4) is used again against an image type whitelist (produced on lines 5-7). If the file is not of the proper type, an error message is sent to the user, on lines 11-12.

Finally, if all checks are good, the script continues on to lines 15-24. Lines 15, 16, 17 handle saving the file to the filesystem as follows:

  1. Define the saving path in the OS, based on the current working directory. The file will be saved in the images folder under the server document root.
  2. Define the file name to a random string with the extension from the whitelist defined on lines 5-7.
  3. Move (save) the file from its temporary location to the images folder.

Lines 19-24 record the data as follows:

  1. Get an instance of the Picture class.
  2. Populate the product_id foreign key from the method parameter.
  3. Populate the filename, since other computers don’t have access to the full path structure. We will use this filename later to display these pictures.
  4. Populate the description from the form data.
  5. Invoke Picture::create() to save the Picture record to the database.
  6. Redirect the client side to the Product index.

Viewing the Pictures

In such an application, we simply call up all the pictures in a product detail user story with a controller method as follows:

  1. public function detail($product_id){
  2.   $theProduct = $this->model('Product')->find($product_id);
  3.   $thePictures = $this->model('Picture')->getForProduct($product_id);
  4.   $theProduct->pictures = $thePictures;
  5.   $this->view('product/detail', $theProduct);
  6. }

By setting the pictures to be part of the product, we are able to pass all the data as a single object to then output the picture URLs as in the following view code:

  1. <?php
  2. foreach($data->pictures as $picture){
  3.   echo "<img src='/images/$picture->filename' style='max-width:100px;' />";
  4.   echo "<a href='/product/deletePicture/$picture->picture_id' class='btn btn-danger'>Delete picture</a>";
  5. }
  6. ?>

On line 3, we output an image element that will refer to the faved file in the images folder under the Web server document root. On line 4, we also include a delete link.

Deleting the Picture

Mistakes happen and things change, so it makes sense to be able to delete pictures from our Web application. The hyperlink form the above view calls the deletePicture method from our product controller, defined as follows:

  1. public function deletePicture($picture_id){
  2.   $thePicture = $this->model('Picture')->find($picture_id);
  3.   unlink(getcwd().DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.$thePicture->filename);
  4.   $thePicture->delete();
  5.   header('location:/product/detail/'.$thePicture->product_id);
  6. }

The process is simply to find the Picture record using its primary key value, delete the associated file with the unlink instruction, and delete the picture record before redirecting back the appropriate page (here we go back to the product details.

The Picture Model and Database

To support all these operations, we need a Picture model similar to the following:

  1.  
  2. class Picture extends Model{
  3.   var $product_id;
  4.   var $filename;
  5.   var $description;
  6.  
  7.   public function getForProduct($product_id){
  8.     $SQL = 'SELECT * FROM Picture where product_id = :product_id';
  9.     $stmt = self::$_connection->prepare($SQL);
  10.     $stmt->execute(['product_id'=>$product_id]);
  11.     $stmt->setFetchMode(PDO::FETCH_CLASS, 'Picture');
  12.     return $stmt->fetchAll();
  13.   }
  14.  
  15.   public function create(){
  16.     $SQL = 'INSERT INTO Picture(product_id,filename,description) VALUE(:product_id,:filename,:description)';
  17.     $stmt = self::$_connection->prepare($SQL);
  18.     $stmt->execute(['product_id'=>$this->product_id,'filename'=>$this->filename,'description'=>$this->description]);
  19.     return $stmt->rowCount();
  20.   }
  21.  
  22.   public function find($picture_id){
  23.     $SQL = 'SELECT * FROM Picture WHERE picture_id = :picture_id';
  24.     $stmt = self::$_connection->prepare($SQL);
  25.     $stmt->execute(['picture_id'=>$picture_id]);
  26.     $stmt->setFetchMode(PDO::FETCH_CLASS, 'Picture');
  27.     return $stmt->fetch();
  28.   }
  29.  
  30.   public function update(){
  31.     $SQL = 'UPDATE Picture SET filename = :filename,description = :description WHERE picture_id = :picture_id';
  32.     $stmt = self::$_connection->prepare($SQL);
  33.     $stmt->execute(['filename'=>$this->filename,'description'=>$this->description,'picture_id'=>$this->picture_id]);
  34.     return $stmt->rowCount();
  35.   }
  36.  
  37.   public function delete(){
  38.     $SQL = 'DELETE FROM Picture WHERE picture_id = :picture_id';
  39.     $stmt = self::$_connection->prepare($SQL);
  40.     $stmt->execute(['picture_id'=>$this->picture_id]);
  41.     return $stmt->rowCount();
  42.   }
  43. }

This model performs operations on a Picture table with the following fields:

  • picture_id: the automatically incremented integer primary key
  • product_id: the foreign key to the product table
  • filename: a string type to hold the short filename
  • description: a text type field to hold long descriptions.

Conclusion

These general guidelines are also applicable to records that are child records of a master-detail relationship. You could change the upload types by modifying the upload whitelist and replacing getimagesize with other validation mechanisms.

Categories
HTML

Improving your Views

So far, we have user very minimalistic views to prove that our application works. However, if we ever want to roll out a product that will be used by anyone, it has to provide a feeling of being well-built. And if it looks bad, it does not feel well-built.

Let’s start using Bootstrap to make our views look better.

Using Bootstrap

We will start by proposing a base template for views. When you create a new view, it should automatically contain this material in order to provide the wanted look and feel:

  1. <!doctype html>
  2. <html lang="en">
  3.   <head>
  4.     <!-- Required meta tags -->
  5.     <meta charset="utf-8">
  6.     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  7.  
  8.     <!-- Bootstrap CSS -->
  9.     <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
  10.  
  11.     <title>TITLE</title>
  12.   </head>
  13.   <body>
  14.     <div class='container'>
  15.       <h1>HEADING</h1>
  16.  
  17.       <!-- Optional JavaScript -->
  18.       <!-- jQuery first, then Popper.js, then Bootstrap JS -->
  19.       <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
  20.       <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
  21.       <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
  22.     </div>
  23.   </body>
  24. </html>

Buttons, submit inputs and hyperlinks

All we need to do now in integrate the proper classes on the proper elements to make them look better. The first category is that of buttons (see https://getbootstrap.com/docs/4.0/components/buttons/):

  1. <button type="button" class="btn btn-primary">Primary</button>
  2. <button type="button" class="btn btn-secondary">Secondary</button>
  3. <button type="button" class="btn btn-success">Success</button>
  4. <button type="button" class="btn btn-danger">Danger</button>
  5. <button type="button" class="btn btn-warning">Warning</button>
  6. <button type="button" class="btn btn-info">Info</button>
  7. <button type="button" class="btn btn-light">Light</button>
  8. <button type="button" class="btn btn-dark">Dark</button>
  9.  
  10. <button type="button" class="btn btn-link">Link</button>

The class='btn btn-BUTTONTYPE' attribute definition is what will give the button appearance to any submit input, button, or hyperlink that you want. Therefore, the following all have a button appearance:

  1. <a href="CSTutoring.ca" class="btn btn-primary">CSTutoring.ca</a>
  2. <button type="button" class="btn btn-secondary">Secondary</button>
  3. <input type="submit" class="btn btn-success" value="Submit" />

Form Inputs

For forms to look adequate for a serious application, there are few modifications to make (see https://getbootstrap.com/docs/4.0/components/forms/):

  1. <div class="form-group">
  2.     <label for="exampleInputEmail1">Email address</label>
  3.     <input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email">
  4.     <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
  5.   </div>

Two changes are essential: 1) apply class="form-group" to a div element containing the label and input elements and 2) apply class="form-control" to the input elements (except type submit that you will format as a button, as above).

Tables

Bootstrap can really help with the appearance of tables (see https://getbootstrap.com/docs/4.4/content/tables/). My preferred type is the striped table as below:

  1. <table class="table table-striped">
  2. ...
  3. </table>

Custom CSS and JS

In the PHP MVC application structure proposed and used in this series of posts, it is possible to use custom CSS and JS, referred through script and link elements of your views. You can simply create css and js folders under the Web server document root. The current .htaccess configuration is made to allow access to all the contained resources.

  • htdocs: contains index.php and the folders below
    • app: contains all models, views and controllers as well as core files.
    • css: add your custom CSS
    • js: add your custom JavaScript

Conclusion

For data-centric applications, it doesn’t take that much to make the application look acceptable. If anything, at least take the time to apply a Bootstrap theme on your application.

Categories
HTML MVC PHP

Deeper in Models

In this post, we will explore more database transactions in model classes with the previous Item class example. We will also create views that can support these operations.

SQL DML (Data Modification Language) are these operations that we must implement in our model classes. They comprise all CRUD (Create, Read, Update, Delete) operations, i.e., respectively INSERT, SELECT, UPDATE, and DELETE.

Create

We add a create method to the Item model class as follows:

public function create(){
  $SQL = 'INSERT INTO Item(name) VALUES (:name)';
  $stmt = self::$_connection->prepare($SQL);
  $stmt->execute(['name'=>$this->name]);
  return self::$_connection->lastInsertId();
}

In the above, we first define the SQL command we will use, with a named placeholder, :name. We then get PDO to prepare the statement; the use of prepared statements has many advantages including reusability and SQL-injection prevention (this happens later).

Next, we run the prepared statement with the execute method. The parameter is an array ([...]) containing a dictionary of values for this statement, i.e., if there is a :name placeholder in the SQL, then there must be a name key-value pair in this dictionary. The execute method will take all this data and sanitize it, preventing any special character that could be used to perform SQL injection. In other words, if you are using prepared statements in all your models such that all data is passed into the statements through the execute method, then you have been diligent in preventing SQL injection.

The final step in the create method is to return the primary key value assigned to the created record. This value is fetched by PDO’s lastInsertId() method.

We now focus on the view used to create this record. We create a view, /app/view/home/create.php that will be used for data entry by the user, as follows:

<html>
  <head><title>Create an Item</title></head>
  <body>
    <h1>Create an Item</h1>
    <form action='' method='post'>
      <label>Name:<input type='text' name='name' /></label><br />
      <input type='submit name='action' value='Create' /> <a href='/home/index'>Cancel</a>
    </form>
  </body>
</html> 

The form is submitted to the same URL from which it was called. This way, we can write the instructions to call the view and to add the record in the same controller method in HomeController:

public function create(){
  if(isset($_POST['action'])){
    $newItem = $this->model('Item');
    $newItem->name = $_POST['name'];
    $newItem->create();
    header('location:/Home/index');
  }else{
    $this->view('home/create');
  }
}

The create method is written in two branches, the first if the submit button named action is pressed. We instantiate an Item object and initialize its name attribute (its only attribute) before calling the create() method to store it. Finally, we send the browser headers to redirect to the /Home/index URL for the application, which will display the list of items. The user can also navigate back to the list of items by blicking Cancel.

Read

We now focus on a detail-view feature for the records. Bear in mind that this is especially useful in cases where records hold many fields. We first add a find(…) method to the model class:

public function find($item_id){
  $SQL = 'SELECT * FROM Item WHERE item_id = :item_id';
  $stmt = self::$_connection->prepare($SQL);
  $stmt->execute(['item_id'=>$item_id]);
  $stmt->setFetchMode(PDO::FETCH_CLASS, 'Item');
  return $stmt->fetch();
}

In contrast with the get() method we wrote in An Introduction to Models, the SQL includes a WHERE clause to choose the record based on the primary key value and this value is passed in the call to the execute() method. Finally, the is returned after a call to fetch() such that the single matching record is returned as an object, without being placed in an array.

To display the record, we build a detail view, /app/views/home/detail.php which should include all record fields in the display:

<html>
  <head><title>Item Details</title></head>
  <body>
    <h1>Item Details</h1>
    <dl>
      <dt>Name:</dt>
      <dd><?=$data->name ?></dd>
    </dl>
<a href='/home/index'>Back to list</a>
  </body>
</html> 

Above, we are assuming the single record is passed to the view and that the name attribute is displayed by the <?=expression ?> tags which are shorthand for <?php echo expression; ?>.

We complete this part with method detail in HomeController as follows:

public function detail($item_id){
  $theItem = $this->model('Item')->find($item_id);
  $this->view('home/detail', $theItem);
}

On the first line, we invoke the method to find the wanted record and store it in $theItem, which is then passed to the newly-created view on the following line.

Update

Another important part of a data-centric Web application is the ability to modify data. In a MySQL database, this is done through UPDATE SQL statements, as in the following update method added to the Item model.

public function update(){
  $SQL = 'UPDATE Item 
          SET name = :name 
          WHERE item_id = :item_id';
  $stmt = self::$_connection->prepare($SQL);
  $stmt->execute(['name'=>$this->name,
                  'item_id'=>$this->item_id]);
  return $stmt->rowCount();
}

First we write a proper SQL UPDATE statement with named placeholders where the data is meant to go. Then we prepare the statement and execute it with the needed data, from the object. Finally, we return the rowCount as feedback about how many rows were modified.

The view to support this process must allow data entry from the user AND have the data entry fields prepopulated with the existing record field values. We add the view /app/views/home/edit.php as follows:

<html>
  <head><title>Edit an item</title></head>
  <body>
    <h1>Edit an item</h1>
    <form action='' method='post'>
      <label>Name:<input type='text' name='name'
           value='<?= $data->name ?>' /></label><br />
      <input type='submit name='action' value='Save' /> <a href='/home/index'>Cancel</a>
    </form>
  </body>
</html> 

For the view to work properly, it must receive the record as its $data parameter. We put all this together in the HomeController edit method, as follows:

public function edit($item_id){
  $theItem = $this->model('Item')->find($item_id);
  if(isset($_POST['action'])){
    $theItem->name = $_POST['name'];
    $theItem->update();
    header('location:/Home/index');
  }else{
    $this->view('home/edit', $theItem);
  }
}

This method is very similar to create, in that it handles displaying a form, processes the submitted data, and redirects back to the list of all items. The distinctions are that edit operates on an existing record and thus must find and present it in the view prior to calling for updates with the user input. Naturally, if a user clicks the Cancel hyperlink, they navigate back to the index method, so this case need not be addressed here.

Delete

To complete the basic data management for the Item table, we must have the ability to delete records. We will implement the Item model class delete method as follows:

public function delete(){
  $SQL = 'DELETE FROM Item WHERE item_id = :item_id';
  $stmt = self::$_connection->prepare($SQL);
  $stmt->execute(['item_id'=>$this->item_id]);
  return $stmt->rowCount();
}

It is easy to also implement this method to take the item_id as a parameter and forego using the actual object attributes.

The pattern to process this request is familiar by now, but in a nutshell: define the DELETE statement with an :item_id placeholder; prepare the statement; execute the statement with the data to replace the placeholders; and return the number of rows affected by the change.

To make this process go smoothly, there are two possibilities: with or without a deletion confirmation view. We write a deletion confirmation view /app/views/home/delete.php as follows:

<html>
  <head><title>Delete an item</title></head>
  <body>
    <h1>Delete an item</h1>
    <form action='' method='post'>
      <label>Name:<input type='text' name='name'
        value='<?= $data->name ?>' disabled />
        </label><br />
      <input type='submit name='action' value='Delete' /> <a href='/home/index'>Cancel</a>
    </form>
  </body>
</html> 

So the user will click on the Delete button to confirm deletion and the Cancel link to cancel the deletion.

To complete the deletion implementation, we add the delete method to the HomeController as follows:

public function delete($item_id){
  $theItem = $this->model('Item')->find($item_id);
  if(isset($_POST['action'])){
    $theItem->delete();
    header('location:/Home/index');
  }else{
    $this->view('home/delete', $theItem);
  }
}

In the above, we only handle the cases when the method is called with or without the action button being clicked. These cases happen upon submitting the form and navigating to delete, respectively.

Tying it All Together

The only aspect missing from this application is the set of hyperlinks that allow navigation. Since all views have links back to /home/index and since all methods redirect to /home/index as well, the links to go to access create, detail, update, and delete methods must be implemented in the view for the index method of the HomeController class; we modify it as follows:

<html>
<head><title>Item List</title></head>
<body>
<h1>List of items</h1>
<a href='/home/create/'>Add an item</a>
<table>
<tr><th>Name</th><th>Actions</th></tr>
<?php
  foreach($data['items'] as $item){
    echo "<tr><td>$item->name</td><td>
      <a href='/home/detail/$item->item_id'>Details</a>
      <a href='/home/edit/$item->item_id'>Modify</a>
      <a href='/home/delete/$item->item_id'>Delete</a>
      </td></tr>";
  }
?>
</table>
</body>
</html>

Notice the hyperlinks all lead to URLs which will be used to invoke controller methods with their parameters. The controller methods will, in turn, call the appropriate views and process the responses to finally redirect as appropriate.

Categories
HTML

Creating HTML Input Forms

HTML Forms are a great vehicle to send user input to scripts.

Starting and Ending HTML Forms

You can create HTML forms by using the HTML <form> and </form> tags, e.g., <form action=”myscript.php” method=”POST”> … </form>

  • myscript.php will start running on the server only once the form data is sent to the server.
  • the data will be sent using the POST data stream (not part of the URL).

Form Buttons

You can create submit and reset buttons by placing the following within <form> & </form> tags.

<input type=”submit” name=”action” value=”Submit”>

<input type=”reset” value=”Erase and Restart”>

  • The submit button will be labeled “Submit”.
  • The reset button will be labeled “Erase and Restart”. This type of button is not highly recommended…

Text Input Boxes

Text input boxes are form elements for a single line of text input, e.g.,

Name: <input type=”text” size=”15″ maxlength=”20″ name=”fname”>

  • Use the name specified to identify the form element in the receiving program.
  • Will be 15 characters wide accepting a maximum of 20 characters.
  • Will set a variable named fname with value of whatever the end-users enter.

Password Boxes

Password boxes similar to text boxes except the actual input is hidden.

<input type=”password” size=”15″ maxlength=”20″ name=”password”>

Warning: password boxes are not secure

  • When the user submits the form, any data input is sent without encryption, just like any other HTML form field.
  • Someone with network access could read the password being transferred. This is why HTTPS is a nice thing to have.
  • Moreover, using the GET method to send a password would make it visible as part of the URL in the HTTP request. One must use POST as the submission method.

Text Areas

The following creates a text area containing 4 rows and 50 columns prefilled with “Comments…”.

<textarea rows=”4″ cols=”50″ name=”Comments”>Comments…</textarea>

Text areas have closing tags. Any text here will appear as default text in text area.

Radio Buttons

Radio buttons are small circles that can select by clicking them with a mouse. Only one within a group can be selected at once. The value that will be sent to the form-processing program is that of the selected item.



<input type=”radio” name=”contact” value=”Yes” checked>

<input type=”radio” name=”contact” value=”No” >

  • The name argument must be the same for all radio buttons operating together. Since both radio buttons above have the same name, they will operate together.
  • The value argument sets the variable value that will be available to the form-processing script.
  • The “Yes” item above will be pre-checked when the form is viewed.

Check Boxes

Check boxes are small boxes on a form that create a check mark when the user clicks them. The following item will be pre-checked when the form is viewed:

<input type=”checkbox” name=”Bicycle” value=”Yes”> Bicycle

<input type=”checkbox” name=”Car” value=”Yes”> Car

<input type=”checkbox” name=”Plane” value=”Yes”> Plane

  • Each check box sets a different variable name when selected.
  • The values will be sent to the form-processing program only if the checkbox is checked.
  • The above example creates independent check boxes; each checkbox can be selected and will set a value for a different variable name.

We may want to create a set of check boxes that use the same name argument.

<input type=”checkbox” name=”travel” value=”Car” checked> Car?

<input type=”checkbox” name=”travel” value=”Bike”> Bicycle?

<input type=”checkbox” name=”travel” value=”Horse”> Horse?

<input type=”checkbox” name=”travel” value=”None”> None of the above?

Since each checkbox element has the same name, multiple values can be set for the same variable name. The value received by the form-processing script would be a comma-separated list of all items checked. Use explode(…) to convert the list to an array.

NOTE:

<input type=”checkbox” name=”travel[]” value=”Car” checked> Car?

<input type=”checkbox” name=”travel[]” value=”Bike”> Bicycle?

<input type=”checkbox” name=”travel[]” value=”Horse”> Horse?

<input type=”checkbox” name=”travel[]” value=”None”> None of the above?

For this example, the value received by the form-processing script would be an array of all items checked.

Selection Lists

Creates a box with a scrolling list of one or more items that user can highlight and select.

<select name=”Accommodations” size=2 multiple>

<option> A fine hotel </option>

<option selected> A cheap motel! </option>

<option> A tent in the parking lot </option>

<option> Just give me a sleeping bag </option>

</select>

Here, the text of the selected options will be sent to the receiving script.

  • This HTML code creates four options formatted in a scrolling list.
  • Only two of these options are displayed at the same time, and the user can select more than one option.
  • Multiple selections are sent to the form-processing script as a comma-separated list.