PhpRiot
Become Zend Certified

Prepare for the ZCE exam using our quizzes (web or iPad/iPhone). More info...


When you're ready get 7.5% off your exam voucher using voucher CJQNOV23 at the Zend Store

Monitoring File Uploads using Ajax and PHP

Creating the PHP FileUploader Class

Now that we have an understanding of how to use upload monitoring with APC, we can create a new PHP class to handle file uploads and to return this upload information. In the coming sections we will write code to interface this class, but for now we will focus on the development of the class.

We are going to use PHP sessions to store upload information. The reason we do this is so the script will complete correctly even if APC upload monitoring is not enabled. When we develop the Ajax part of this solution, the status monitoring script will be continually polling the status script. Once the upload has completed we need this loop to finish. For now though, let's focus on developing the PHP class.

The code created in this section will be broken down by each method. The full FileUploader.php file can be downloaded from the article file list.

Initializing FileUploader

The first thing we do is to create the class constructor. Because this solution uses sessions, we call session_start() in the constructor. This isn't necessarily an ideal solution: your application may already initiate sessions but we just do it here so the code for this article is all self-contained.

Here is the code to define the class, as well as its constructor.

Listing 7 Initializing the FileUploader class (listing-7.php)
<?php
    class FileUploader
    {
        const SESSION_KEY = '__upload_status';
        const ID_KEY      = 'APC_UPLOAD_PROGRESS';
 
        public function __construct()
        {
            session_start();
 
            if (!array_key_exists(self::SESSION_KEY, $_SESSION)) {
                $_SESSION[self::SESSION_KEY] = array();
            }
        }
 
        // ... other code
    }
?>

We are going to store upload status session data as an array, with each element corresponding to a single file upload. We will store this data in a single element in the $_SESSION array. We define the name of this using the SESSION_KEY constant. Doing this using a constant allows us to easily change the name of this as required in the future without having to update all points in the code.

Additionally, we define the ID_KEY constant, which refers to the name of the form element that APC looks for in order to define an upload's ID. Once again this is unlikely to change but makes for cleaner code.

Checking for APC Upload Monitoring Support

The next step is to determine whether or not we have the ability to monitor the upload status. Doing this ensures that our code will work on all platforms regardless of whether APC is installed. For APC upload monitoring to work the following conditions must be met:

  • The APC module must be loaded
  • The apc_fetch() function must exist (which it should if APC is loaded, but we'll double check anyway).
  • APC must be enabled (upload monitoring is disabled if APC is disabled, although this wasn't the case in APC 3.0.13).
  • The apc.rfc1867 setting must be enabled.

In order to check each of these conditions we can use the following code:

Listing 8 Determining whether or not APC upload monitoring is enabled (listing-8.php)
<?php
    class FileUploader
    {
        // ... other code
 
        public static function CanGetUploadStatus()
        {
            if (!extension_loaded('apc'))
                return false;
 
            if (!function_exists('apc_fetch'))
                return false;
 
            return ini_get('apc.enabled') && ini_get('apc.rfc1867');
        }
 
        // ... other code
    }
?>

Retrieving the Upload Status for a File

Next we implement a method to retrieve the upload status for a given file ID. This method works first by checking for existing status data in the session. If there isn't, the data is initialized. Next the code calls apc_fetch() to retrieve the upload information as described in the previous section. The session is then updated and the data is returned.

This code as described is shown below. The comments describe exactly what is occurring in the getUploadStatus() method.

Listing 9 Retrieving the file upload data with getUploadStatus() (listing-9.php)
<?php
    class FileUploader
    {
        // ... other code
 
        public function getUploadStatus($id)
        {
            // sanitize the ID value
            $id = preg_replace('/[^a-z0-9]/i', '', $id);
            if (strlen($id) == 0)
                return;
 
            // ensure the uploaded status data exists in the session
            if (!array_key_exists($id, $_SESSION[self::SESSION_KEY])) {
                $_SESSION[self::SESSION_KEY][$id] = array(
                    'id'       => $id,
                    'finished' => false,
                    'percent'  => 0,
                    'total'    => 0,
                    'complete' => 0
                );
            }
 
            // retrieve the data from the session so it can be updated and returned
            $ret = $_SESSION[self::SESSION_KEY][$id];
 
            // if we can't retrieve the status or the upload has finished just return
            if (!self::CanGetUploadStatus() || $ret['finished'])
                return $ret;
 
            // retrieve the upload data from APC
            $status = apc_fetch('upload_' . $id);
 
            // false is returned if the data isn't found
            if ($status) {
                $ret['finished'] = (bool) $status['done'];
                $ret['total']    = $status['total'];
                $ret['complete'] = $status['current'];
 
                // calculate the completed percentage
                if ($ret['total'] > 0)
                    $ret['percent'] = $ret['complete'] / $ret['total'] * 100;
 
                // write the changed data back to the session
                $_SESSION[self::SESSION_KEY][$id] = $ret;
            }
 
            return $ret;
        }
 
        // ... other code
    }
?>

Handling File Uploads

To complete the FileUploader PHP class we will implement a method for actually handling the upload web form. Since this tutorial isn't focus specifically on how to handle file uploads the code has been somewhat simplified, but in essence this method will simply save an uploaded file to the specified location.

In addition to saving the file to the file system, this method will also update the upload status data in the session to indicate that the file upload has complete. If you refer to the getUploadStatus() method, there is a line of code that checks the finished array key. We use this to help the Ajax portion of our code know when to stop polling for the upload status.

Listing 10 Handling uploads with the upload() method (listing-10.php)
<?php
    class FileUploader
    {
        // ... other code
 
        public function upload($key, $path)
        {
            // ensure the given file has been uploaded
            if (!isset($_FILES[$key]) || !is_array($_FILES[$key]))
                return false;
 
            $file = $_FILES[$key];
            $id   = $_POST[self::ID_KEY];
 
            // only proceed if no errors have occurred
            if ($file['error'] != UPLOAD_ERR_OK)
                return false;
 
            // write the uploaded file to the filesystem
            $fullpath = sprintf('%s/%s', $path, basename($file['name']));
            if (!move_uploaded_file($file['tmp_name'], $fullpath))
                return false;
 
            // update the session data to indicate the upload has completed
            $size = filesize($fullpath);
 
            $_SESSION[self::SESSION_KEY][$id] = array(
                'id'       => $id,
                'finished' => true,
                'percent'  => 100,
                'total'    => $size,
                'complete' => $size
            );
 
            return true;
        }
    }
?>

In order to focus specifically on monitoring upload status, the error handling in this code has been somewhat simplified. Additionally, the code described here doesn’t deal with duplicate files or do any checking on the type of file.

This completes the FileUploader.php file. You can download the complete file from the article file listing.

In This Article


Additional Files