<?php
/**
 * File Upload Progress Information using PHP and APC
 *
 * This code is trying to mimic the code published by Rasmus Lerdorf at
 * http://progphp.com/progress.phps
 * 
 * APC extension is not part of the standard PHP distribution for
 * installation and configuration please consult the PHP Manual at
 * http://de.php.net/manual/en/book.apc.php
 * or any Websites that provide this information. For instance IBM have a
 * very decent tutorial about this at
 * http://www.ibm.com/developerworks/library/os-php-v525/index.html        
 *   
 * The code from Rasmus Lerdorf are using Yahoo User Interface Framework.
 * This code is trying not to use any Framework implementation, for the sake
 * of simplicity and to fully understand the functionality of the APC extension 
 * for developing File Upload Progress Information. 
 * 
 * I said Progress Information in fact, because i do not create any Progress
 * Bar (again) for the sake of simplicity. Example code about how to create a 
 * Progress Bar can be found in other websites or tutorials.     
 *      
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted. Feel free to modify it as you please.
 * 
 * If you have any suggestion, feel free to contact me via the email below,
 * or via the contact form on my blog: 
 * http://arthur.purnama.de/2007/12/contact.html   
 *
 * $Author: Arthur Purnama (purnama@programmer.net) $
 * $Date: 2008-07-15 14:23:00 -0400 (Tue, 15 Jul 2008) $
 *  
 */
 
/**
 * Because we are using the same PHP file for uploading the file and
 * checking the file progress, we need this if-else statement to
 * differentiate it. If the request method is a post, then consider it coming
 * from the upload form, otherwise it coming from the XMLHttpRequest. At this
 * moment it will check whether a GET parameter named "progress_key" is defined
 * otherwise it will be considered as an error.
 */
if($_SERVER['REQUEST_METHOD']=='POST') {
  
//implement your upload code here
  //e.g. @move_uploaded_file($filename, $destination)
  
exit;
} else if(isset(
$_GET['progress_key'])) {
  
//with apc_fetch() function return the information about the upload progress
  //as an array. Please consult the PHP Manual at
  //http://de.php.net/manual/en/apc.configuration.php
  //for more information about the array keys and the values.
  //consider the use of ini_get() function to get the default prefix value
  //defined in php.ini
  
$status apc_fetch(ini_get('apc.rfc1867_prefix').$_GET['progress_key']);
  echo 
json_encode($status);
  exit;
}else{
  
//you can implement some code to display error here
}
?>
<html>
<head>
<script type="text/javascript">
/**
 * This is a simple function to create a XMLHttpRequest instance
 *
 */  
function createRequestObject() 
{
    var requestObject;
    requestObject = false;
    if( window.ActiveXObject ){
        for( var i = 5; i; i-- ){
            try{
               if( i == 2 ){
                requestObject = new ActiveXObject( "Microsoft.XMLHTTP" );    
            }else{        
                requestObject = new ActiveXObject( "Msxml2.XMLHTTP." + i + ".0" );
            }
            break;
          }catch( excNotLoadable ){                        
                requestObject = false;
          }
        }
    }else{
        requestObject = new XMLHttpRequest();
    }
    return requestObject;
}
/**
 * Instantiate an XMLHttpRequest Object and pass it to http variable
 *
 */ 
var http = createRequestObject();

/**
 * This function submit the form given in function parameter and wait 100ms
 * before running the updateProgress() function. 
 * 
 * You need to wait until the file reached the server before beginning to read 
 * the progress information, otherwise the apc_fetch() function will return a 
 * boolean false. Please consider to give it a longer wait interval 
 * e.g. 500-2000 in productive environment.       
 *
 */  
function postForm(formName){
  document.getElementById(formName).submit();
  setTimeout('updateProgress()', 100);
}

/**
 * This function will make a HTTP GET Request to the server for this file,
 * with a GET parameter "progress_key". The value of the Get parameter is
 * gained from the hidden input field that have "progress_key" as an ID.  
 *
 * If the server responding with status 200, then the JSON response text will be
 * generated as object with the javascript eval() function. 
 * 
 * if "done" attribute is not defined in JSON, then the progress will be 
 * considered as not finished. In this case we will compute the percentage
 * of the progress and display it on the "div" container that have 
 * "progress_win"  as ID. After that, we wait for 500ms and call the
 * updateProgress() function again.
 * 
 * This step will be executed until the JSON response text have "done" attribute
 * in it. If its the case, then the percentage will be forced to display "100%".
 * The loop will burden the server because of this loop request. Please consider
 * a longer time intervall too at this point.        
 *  
 */   
function updateProgress(){ 
  progress_key = document.getElementById('progress_key').value;
  http.open("GET", 'upload_progress.php?progress_key='+progress_key, true);
    http.onreadystatechange = function () {
        if (http.readyState == 4) {
            if (http.status == 200) {
              the_object = eval("(" + http.responseText + ")");            
              if(!the_object.done){
                result = Math.round((the_object.current/the_object.total) * 100);
                document.getElementById('progress_win').innerHTML = result +'%';
                setTimeout("updateProgress()",500);
              }else{
                document.getElementById('progress_win').innerHTML = '100%';
              }
            }
        }
    }
  http.send(null);
}

</script>
</head>
<body>
 <!--  
   To prevent the page to be reloaded if the submit button is clicked,
   a target attribute is defined for the form, targeting the hidden iframe.
   In this case, if the submit button is clicked, the form will be posted
   to the server from the iframe.
 -->
 Try to upload a file with 200k or more of size.
 <form target="hiddenframe" enctype="multipart/form-data" id="upload_form" 
       action="upload_progress.php" 
       onsubmit="postForm('upload_form');" method="POST">
   <!--  
     This hidden field with APC_UPLOAD_PROGRESS is a must if you want your
     code to be functioning correctly. a unique id as a value need to given
     to prevent the system to read progress from another thread.
   -->
  <input type="hidden" name="APC_UPLOAD_PROGRESS" id="progress_key" 
         value="<?php echo uniqid()?>" />
  <input type="file" id="test_file" name="test_file"/><br/>
  <input type="submit" value="Upload!"/>
 </form>
 <div id="progress_win"> 
 </div> 
 <iframe name="hiddenframe" style="display: none;" >Loading...</iframe>
</body>
</html>