PHP Code – How to create a Live Demo for free on 000webhost.com (outdated)

People often ask me, how i created the live demo at https://phpfm-demo.000webhostapp.com.
It resets automatically every hour, and shows a timer for the next reset.
Here i share the code, so if you have a project on GitHub and want to create a live demo, you can use it as base.
000webhostapp.com is a free webhosting service, and all you have to do is leave their banner on your demo.

demo-reset.php
This is the main script, it runs on my server crontab every hour.

<?php

    // Run on CRON every hour
    // 0 * * * * www-data wget -q --spider http://[URL TO DEMO-RESET] >/dev/null 2>&1

    // FTP functions

    function ftp_list($conn, $remote_path='.', &$output) {
        $entry_list = ftp_rawlist($conn, $remote_path);
        if (is_array($entry_list)) {
            $items = array();
            foreach ($entry_list as $entry) {
                $chunks = preg_split("/\s+/", $entry);
                list($item['rights'], $item['number'], $item['user'], $item['group'], $item['size'], $item['month'], $item['day'], $item['time']) = $chunks;
                $item['type'] = $chunks[0]{0} === 'd' ? 'directory' : 'file';
                array_splice($chunks, 0, 8); // Remove 8 itens separated by space
                $item['name'] = implode(" ", $chunks); // concat the rest, that is the name of the file
                if ($item['name'] == '.' || $item['name'] == '..') continue;
                $items[] = $item;
            }
            return $items;
        } else {
             $output .= "ftp_rawlist() error: ".$remote_path."<br>\n";
        }
        return false;
    }
    
    function ftp_total_delete($conn, $remote_path, &$output){
        $item_list = ftp_list($conn, $remote_path, $output);
        $ok = ($item_list !== false);
        if ($ok) {
            foreach ($item_list as $item) {
                $curr_remote_path = $remote_path;
                if ($curr_remote_path == '/') $curr_remote_path = '';
                $curr_remote_path = $curr_remote_path.'/'.$item['name'];
                if ($ok) {
                    if ($item['type'] == 'file') {
                        $ok = ftp_delete($conn, $curr_remote_path);
                        $output .= "FTP RM: ".$curr_remote_path." = ".($ok?'OK':'ERRO')."<br>\n";
                    } elseif ($item['type'] == 'directory') {
                        $ok = ftp_total_delete($conn, $curr_remote_path, $output);
                        if ($ok) {
                            $ok = ftp_rmdir($conn, $curr_remote_path);
                            $output .= "FTP RMDIR: ".$curr_remote_path." = ".($ok?'OK':'ERRO')."<br>\n";
                        }
                    }
                }
            }
        }
        return $ok;
    }
    
    function ftp_total_upload($conn, $local_path, $remote_path, &$output){
        $ok = true;
        $entry_list = scandir($local_path);
        foreach ($entry_list as $entry_name) {
            if ($entry_name == '.' || $entry_name == '..') continue;
            if ($ok) {
                $curr_local_path = $local_path.'/'.$entry_name;
                $curr_remote_path = $remote_path;
                if ($curr_remote_path == '/') $curr_remote_path = '';
                $curr_remote_path = $curr_remote_path.'/'.$entry_name;
                if (is_dir($curr_local_path)) {
                    $ok = ftp_mkdir($conn, $curr_remote_path);
                    $output .= "FTP MKDIR: ".$curr_remote_path." = ".($ok?'OK':'ERRO')."<br>\n";
                    if ($ok) {
                        $ok = ftp_total_upload($conn, $curr_local_path, $curr_remote_path, $output);
                    }
                } elseif (is_file($curr_local_path)) {
                    $ok = ftp_put($conn, $curr_remote_path, $curr_local_path, FTP_BINARY);
                    $output .= "FTP UPLOAD: ".$curr_local_path." => ".$curr_remote_path." = ".($ok?'OK':'ERRO')."<br>\n";
                }
            }
        }
        return $ok;
    }
    
    function ftp_total_download($conn, $remote_path, $local_path, &$output){
        $max_file_size = 15 * 1024 * 1024; // Mb em bytes
        $item_list = ftp_list($conn, $remote_path, $output);
        $ok = ($item_list !== false);
        if ($ok) {
            foreach ($item_list as $item) {
                $curr_local_path = $local_path.'/'.$item['name'];
                if (strpos($curr_local_path,'.php') !== false) $curr_local_path .= '.txt';
                $curr_remote_path = $remote_path;
                if ($curr_remote_path == '/') $curr_remote_path = '';
                $curr_remote_path = $curr_remote_path.'/'.$item['name'];
                if ($ok) {
                    if ($item['type'] == 'file') {
                        if ($item['size'] < $max_file_size){
                            $ok = ftp_get($conn, $curr_local_path, $curr_remote_path, FTP_BINARY);
                            $output .= "FTP GET: ".$curr_remote_path." => ".$curr_local_path." = ".($ok?'OK':'ERRO')."<br>\n";
                        } else {
                            $output .= "FTP GET IGNORED: ".$curr_remote_path." => ".$curr_local_path." = (".formatsize($item['size']).")<br>\n";
                        }
                    } elseif ($item['type'] == 'directory') {
                        if ($ok) {
                            if (!is_dir($curr_local_path)){
                                $ok = mkdir($curr_local_path);
                                $output .= "MKDIR: ".$curr_local_path." = ".($ok?'OK':'ERRO')."<br>\n";
                            }
                        }
                        $ok = ftp_total_download($conn, $curr_remote_path, $curr_local_path, $output);
                    }
                }
            }
        }
        return $ok;
    }
    
    // Format functions
    
    function formatsize($arg) {
        if ($arg>0){
            $j = 0;
            $ext = array(" bytes"," Kb"," Mb"," Gb"," Tb");
            while ($arg >= pow(1024,$j)) ++$j; {
                $arg = (round($arg/pow(1024,$j-1)*100)/100).($ext[$j-1]);
            }
            return $arg;
        } else return "0 Kb";
    }
    
    // Main Script

    function demo_reset(){

        $ftp_host = 'files.000webhost.com';
        $ftp_user = '[USER]';
        $ftp_pass = '[PASSWORD]';

        $ftp_root = '/storage/ssd4/595/8669595/public_html';

        $conn = ftp_connect($ftp_host,21,10);
        $ok = ($conn !== false);
        echo "FTP CONNECT: ".$ftp_host." = ".($ok?'OK':'ERRO')."<br>\n";

        if ($ok) {
            ftp_set_option($conn, FTP_TIMEOUT_SEC, 10);
            $ok = ftp_login($conn, $ftp_user, $ftp_pass);
            echo "FTP LOGIN: ".($ok?'OK':'ERRO')."<br>\n";
        }

        if ($ok) {
            echo "<br>\n";
            $ftp_root = '/';
            $output = '';
            $ok = ftp_total_delete($conn, $ftp_root, $output);
            echo "FTP TOTAL DELETE: ".$ftp_root." = ".($ok?'OK':'ERRO')."<br>\n";
            echo $output;
        }

        if ($ok) {
            echo "<br>\n";
            $local_root = './demo-reset/_docroot';
            $ftp_root = '/';
            $output = '';
            $ok = ftp_total_upload($conn, $local_root, $ftp_root, $output);
            echo "FTP TOTAL UPLOAD: ".$local_root." => ".$ftp_root." = ".($ok?'OK':'ERRO')."<br>\n";
            echo $output;
        }
        
        if ($ok) {
            echo "<br>\n";
            $ok = (ftp_chmod($conn, 0755, $ftp_root) !== false); // Returns the new file permissions on success or FALSE on error.
            echo "FTP CHMOD 0755 ".$ftp_root.": ".($ok?'OK':'ERRO')."<br>\n";
        }        

        if ($ok) {
            echo "<br>\n";
            $local_path = './demo-reset/php-get-time.txt';
            $remote_name = 'php-get-time-'.time().'.php';
            $remote_path = '/public_html/'.$remote_name;
            $remote_url = 'https://phpfm-demo.000webhostapp.com/'.$remote_name;
            if (is_file($local_path)) {
                $ok = ftp_put($conn, $remote_path, $local_path, FTP_BINARY);
                echo "FTP UPLOAD: ".$local_path." => ".$remote_path." = ".($ok?'OK':'ERRO')."<br>\n";
                if ($ok) {
                    $time = file_get_contents($remote_url);
                    $ok = ($time !== false);
                    echo "HTTP GET: ".$remote_url." = ".($ok?'OK':'ERRO')."<br>\n";
                    if ($ok) {
                        echo "TIME: ".$time."<br>\n";
                    }
                    $ok = ftp_delete($conn, $remote_path);
                    echo "FTP RM: ".$remote_path." = ".($ok?'OK':'ERRO')."<br>\n";
                }
            }
        }

        if ($ok) {
            echo "<br>\n";

            $local_root = './demo-reset/_docroot';

            if ($ok) {
                $remote_url = 'https://raw.githubusercontent.com/dulldusk/phpfm/master/index.php';
                $phpfm_src = file_get_contents($remote_url);
                $ok = ($phpfm_src !== false);
                echo "HTTP GET: ".$remote_url." = ".($ok?'OK':'ERRO')."<br>\n";
                if ($ok) {
                    $local_path = $local_root.'/phpfm.txt';
                    $remote_path = '/public_html/phpfm.php';
                    if (file_put_contents($local_path,$phpfm_src)) {
                        echo "SAVED AS: ".$local_path."<br>\n";
                        $ok = ftp_put($conn, $remote_path, $local_path, FTP_BINARY);
                        echo "FTP UPLOAD: ".$local_path." => ".$remote_path." = ".($ok?'OK':'ERRO')."<br>\n";
                        unlink($local_path);
                    }
                }
            }

            if ($ok) {
                $time = intval($time);
                if (!$time) $time = time();
                $index_str = file_get_contents('./demo-reset/index.txt');
                $index_str = str_replace('[START-TIME]',$time,$index_str);
                $local_path = $local_root.'/index.txt';
                $remote_path = '/public_html/index.php';
                if (file_put_contents($local_path,$index_str)) {
                    $ok = ftp_put($conn, $remote_path, $local_path, FTP_BINARY);
                    echo "FTP UPLOAD: ".$local_path." => ".$remote_path." = ".($ok?'OK':'ERRO')."<br>\n";
                    unlink($local_path);
                }
            }

        }

        ftp_close($conn);

        return $ok;
    }

    $GLOBALS['script_init_time'] = getmicrotime();
    @set_time_limit(60);

    $max_tries = 1;
    for($i=0;$i<$max_tries;$i++){
        if ($i>0) {
            sleep(2);
            echo "<br>\n";
        }
        $ok = demo_reset();
        echo "<br>\n";
        echo "<b>DEMO RESET</b>: ".($ok?'OK':'ERRO')."<br>\n";
        if ($ok) break;
    }

    echo "<br>\n";
    echo "page generated in ".number_format((getmicrotime()-$GLOBALS['script_init_time']), 3, '.', '')."s (limit ".ini_get("max_execution_time")."s) using ".formatsize(memory_get_usage())." (limit ".ini_get("memory_limit").")";

?>

demo-reset.php output:

FTP CONNECT: files.000webhost.com = OK
FTP LOGIN: OK

FTP TOTAL DELETE: / = OK
FTP RM: /public_html/LICENSE = OK
FTP RM: /public_html/README.md = OK
FTP RM: /public_html/index.php = OK
FTP RM: /public_html/phpfm.php = OK
FTP RMDIR: /public_html = OK

FTP TOTAL UPLOAD: ./demo-reset/_docroot => / = OK
FTP MKDIR: /public_html = OK
FTP UPLOAD: ./demo-reset/_docroot/public_html/LICENSE => /public_html/LICENSE = OK
FTP UPLOAD: ./demo-reset/_docroot/public_html/README.md => /public_html/README.md = OK

FTP UPLOAD: ./demo-reset/php-get-time.txt => /public_html/php-get-time-1587236205.php = OK
HTTP GET: https://phpfm-demo.000webhostapp.com/php-get-time-1587236205.php = OK
TIME: 1587236281
FTP RM: /public_html/php-get-time-1587236205.php = OK

HTTP GET: https://raw.githubusercontent.com/dulldusk/phpfm/master/index.php = OK
SAVED AS: ./demo-reset/_docroot/phpfm.txt
FTP UPLOAD: ./demo-reset/_docroot/phpfm.txt => /public_html/phpfm.php = OK
FTP UPLOAD: ./demo-reset/_docroot/index.txt => /public_html/index.php = OK

DEMO RESET: OK

page generated in 2.024s (limit 60s) using 706.48 Kb (limit 50M)

php-get-time.txt
A simple php script to get the current time at the 000webhost server. It is uploaded, and removed afterwards.

<?php
    echo time();
?>

index.txt
This is the HTML file, that is updated by demo-reset.php with the 000webhost server current time, and uploaded as index.php, creating the frameset with the counter for the demo.

<?php
    header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
    header("Cache-Control: post-check=0, pre-check=0", false);
    header("Pragma: no-cache");
    header("Content-Type: text/html; charset=UTF-8");
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "//www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="//www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="shortcut icon" href="phpfm.php?action=99&filename=favicon.ico" type="image/x-icon">
<title>PHP File Manager - Demo</title>
<style type="text/css">
    html {
        width: 100%;
        margin-left: 0 !important;
    }
    body {
        font-family : Arial;
        font-size: 18px;
        font-weight : normal;
        color: #000000;
        background-color: #EEEEEE;
    }
    a {
        font-family : Arial;
        font-size : 18px;
        text-decoration: none;
        color: #000000;
        cursor: pointer;
    }
    a:link {
        color: #000000;
    }
    a:visited {
        color: #000000;
    }
    a:hover {
        color: #0A77F7;
    }
    a:active {
        color: #000000;
    }
    #sfdownload {
        position: absolute;
        top: 0;
        left: 0;
        padding-top: 8px;
        padding-left: 10px;
    }
    #timerInfo {
        position: absolute;
        top: 0;
        left: 290px;
        padding-top: 12px;
        padding-left: 10px;
        font-size: 18px;
    }
    #paypalDiv {
        position: absolute;
        top: 0;
        left: 1020px;
        padding-top: 4px;
        padding-left: 10px;
    }
    #paypalBut {
        height: 56px;
        border: none;
        cursor: pointer;
    }
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<?php
    if (isset($_REQUEST['top'])){
        echo '
        <body>
            <div id="sfdownload">
                <a target="_blank" href="https://sourceforge.net/projects/phpfm/files/latest/download"><img alt="Download PHP File Manager" src="https://a.fsdn.com/con/app/sf-download-button" width=276 height=48 srcset="https://a.fsdn.com/con/app/sf-download-button?button_size=2x 2x"></a>
            </div>
            <div id="timerInfo">
                <b><a target="_blank" href="https://github.com/dulldusk/phpfm/archive/master.zip" target="_blank">PHP File Manager Demo - Github Latest</a></b>
                <br \>
                Feel free to mess around! This is a public demo installation<span id="time">, and will reset itself every hour.</span>
            </div>
            <div id="paypalDiv">
                <a target="_blank" href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=dulldusk@gmail.com&item_name='.urlencode('Donation for phpFileManager').'&item_number='.urlencode('Help me create new features. Any amount is well received!').'&amount=0%2e00&currency_code=USD">
                    <img id="paypalBut" src="//www.dulldusk.com/paypal.png" title="Donate with PayPal">
                </a>
            </div>
            <script LANGUAGE="JavaScript">
            <!--
                function startTimer(duration, display) {
                    var start = Date.now();
                    var diff, minutes, seconds, timerIntervalID;
                    function timer() {
                        diff = duration - (((Date.now() - start) / 1000) | 0);
                        if (diff >= 0) {
                            // does the same job as parseInt truncates the float
                            minutes = (diff / 60) | 0;
                            seconds = (diff % 60) | 0;
                            minutes_str = minutes < 10 ? "0" + minutes : minutes;
                            seconds_str = seconds < 10 ? "0" + seconds : seconds;
                            display.textContent = ", and will reset itself in " + minutes_str + ":" + seconds_str + ".";
                            if (diff <= 0) {
                                start = start + 1000; // add one second so that the count down starts at the full duration
                            }
                            if (minutes == 0 && seconds == 0){
                                clearInterval(timerIntervalID);
                                setTimeout(function(){
                                    window.top.document.location.href = "https://phpfm-demo.000webhostapp.com";
                                },1000);
                            }
                        } else {
                            clearInterval(timerIntervalID);
                            //display.textContent = "00:00";
                            display.textContent = ", and will reset itself every hour.";
                        }
                        $(\'div:has(a:has(img[alt="www.000webhost.com"]))\').remove();
                    };
                    timer();
                    timerIntervalID = setInterval(timer, 1000);
                }
                var server_time_on_demo_reset = [START-TIME];
                var server_time_now = '.time().';
                window.onload = function () {
                    var durationMinutes = 60;
                    var durationSeconds = (durationMinutes * 60) - (server_time_now - server_time_on_demo_reset) + 15;
                    var display = document.querySelector(\'#time\');
                    startTimer(durationSeconds, display);
                };
            //-->
            </script>
        </body>
        ';
    } else {
        echo '
        <frameset rows="64,*" framespacing="0" frameborder="1">
            <frame src="index.php?top" name=frametop border="0" marginwidth="0" marginheight="0" scrolling="no">
            <frame src="phpfm.php" name=framephpfm border="0" marginwidth="0" marginheight="0">
        </frameset>
        ';
    }
?>
</html>

Hope you like it!