So, we have an in-house content management system (CMS) that we tend to use on client sites. It has many features like that of WordPress, Drupal, etc. that make it work REALLY well for the needs of all of our clients. We give them the ability to control page content, add blogs and news feeds, contact forms, etc. while still adding in those behind the scenes features like caching systems, link structure control, SEO control, image uploading, etc. With the amount of sites on this CMS, it was starting to become difficult to keep them all actively updated, as we would have to login to their site, upload new files, synchronize the database, and then insert any new  site settings that were implemented.

All of the big content management systems have auto update systems. Zen Cart, Drupal, WordPress, Joomla, etc. all have this built in as a core function and now we do too. We have simply added a link on our administrator menu that says, 'System Update'. When clicked, the site checks our server for a list of available updates and downloads the next update that is needed for the CMS. Quick and painless, right? Well, here's the code:

Just a note: BACK UP YOUR FILES before you attempt any of this. This is a FREE script I'm handing out. No promises that it will work with your server or your CMS. It will have to be reconfigured a tad to work with anything you might be working with.

<h1>DYNAMIC UPDATE SYSTEM</h1>
<?
ini_set('max_execution_time',60);

 

//Check for an update. We have a simple file that has a new release version on each line. (1.00, 1.02, 1.03, etc.)
$getVersions = file_get_contents('http://your-server.com/CMS-UPDATE-PACKAGES/current-release-versions.php') or die ('ERROR');
if ($getVersions != '')
{
    //If we managed to access that file, then lets break up those release versions into an array.
    echo '<p>CURRENT VERSION: '.get_siteInfo('CMS-Version').'</p>';
    echo '<p>Reading Current Releases List</p>';
    $versionList = explode("n", $getVersions);    
    foreach ($versionList as $aV)
    {
        if ( $aV > get_siteInfo('CMS-Version')) {
            echo '<p>New Update Found: v'.$aV.'</p>';
            $found = true;
           
            //Download The File If We Do Not Have It
            if ( !is_file(  $_ENV['site']['files']['includes-dir'].'/UPDATES/MMD-CMS-'.$aV.'.zip' )) {
                echo '<p>Downloading New Update</p>';
                $newUpdate = file_get_contents('http://your-server.com/CMS-UPDATE-PACKAGES/MMD-CMS-'.$aV.'.zip');
                if ( !is_dir( $_ENV['site']['files']['includes-dir'].'/UPDATES/' ) ) mkdir ( $_ENV['site']['files']['includes-dir'].'/UPDATES/' );
                $dlHandler = fopen($_ENV['site']['files']['includes-dir'].'/UPDATES/MMD-CMS-'.$aV.'.zip', 'w');
                if ( !fwrite($dlHandler, $newUpdate) ) { echo '<p>Could not save new update. Operation aborted.</p>'; exit(); }
                fclose($dlHandler);
                echo '<p>Update Downloaded And Saved</p>';
            } else echo '<p>Update already downloaded.</p>';    
           
            if ($_GET['doUpdate'] == true) {
                //Open The File And Do Stuff
                $zipHandle = zip_open($_ENV['site']['files']['includes-dir'].'/UPDATES/MMD-CMS-'.$aV.'.zip');
                echo '<ul>';
                while ($aF = zip_read($zipHandle) )
                {
                    $thisFileName = zip_entry_name($aF);
                    $thisFileDir = dirname($thisFileName);
                   
                    //Continue if its not a file
                    if ( substr($thisFileName,-1,1) == '/') continue;
                   
    
                    //Make the directory if we need to...
                    if ( !is_dir ( $_ENV['site']['files']['server-root'].'/'.$thisFileDir ) )
                    {
                         mkdir ( $_ENV['site']['files']['server-root'].'/'.$thisFileDir );
                         echo '<li>Created Directory '.$thisFileDir.'</li>';
                    }
                   
                    //Overwrite the file
                    if ( !is_dir($_ENV['site']['files']['server-root'].'/'.$thisFileName) ) {
                        echo '<li>'.$thisFileName.'...........';
                        $contents = zip_entry_read($aF, zip_entry_filesize($aF));
                        $contents = str_replace("rn", "n", $contents);
                        $updateThis = '';
                       
                        //If we need to run commands, then do it.
                        if ( $thisFileName == 'upgrade.php' )
                        {
                            $upgradeExec = fopen ('upgrade.php','w');
                            fwrite($upgradeExec, $contents);
                            fclose($upgradeExec);
                            include ('upgrade.php');
                            unlink('upgrade.php');
                            echo' EXECUTED</li>';
                        }
                        else
                        {
                            $updateThis = fopen($_ENV['site']['files']['server-root'].'/'.$thisFileName, 'w');
                            fwrite($updateThis, $contents);
                            fclose($updateThis);
                            unset($contents);
                            echo' UPDATED</li>';
                        }
                    }
                }
                echo '</ul>';
                $updated = TRUE;
            }
            else echo '<p>Update ready. <a href="?doUpdate=true">&raquo; Install Now?</a></p>';
            break;
        }
    }
    
    if ($updated == true)
    {
        set_setting('site','CMS',$aV);
        echo '<p class="success">&raquo; CMS Updated to v'.$aV.'</p>';
    }
    else if ($found != true) echo '<p>&raquo; No update is available.</p>';

    
}
else echo '<p>Could not find latest realeases.</p>';

I'll walk you through the main key parts of this file to make things a bit simpler when it's time to use it yourself. Essentially, what we are doing is checking the server for a list of current release versions. This a simple text file with a new line for each release version. (1.00, 1.02, 1.03, etc.) We take this list and turn it into an array and compare it to whatever version we currently have installed. Our CMS stores everything in a variable for us to access, so calling get_setting('site','CMS-Version); returns the numerical version of the CMS we are currently running .

$getVersions = file_get_contents('http://your-server.com/CMS-UPDATE-PACKAGES/current-release-versions.php') or die ('ERROR');
if ($getVersions != '')
{
    //If we managed to access that file, then lets break up those release versions into an array.
    echo '<p>CURRENT VERSION: '.get_siteInfo('CMS-Version').'</p>';
    echo '<p>Reading Current Releases List</p>';
    $versionList = explode("n", $getVersions);    
    foreach ($versionList as $aV)
    {
        if ( $aV > get_siteInfo('CMS-Version')) {
            echo '<p>New Update Found: v'.$aV.'</p>';

We loop through our list of new releases until we find one newer than the current release. When a new release is uploaded to the server, it is uploaded as a ZIP file with the same naming format as the previous - MMD-CMS-[VERSIONNUMBER].zip. We can use PHP to download this file from our update server and save it to a temporary folder. We give the user the option to either update now or later. Since the file is saved, there will be no need to download it again. Of course, we check to make sure our temporary folder exists before trying to save the file there.

Looking at the code below, we first check to make sure the file has not been downloaded already. If it has, then great - let's continue. Otherwise, let's download it. We use file_get_contents to download the file. Once downloaded, we create the file to store it if it doesn't exist. Using fopen and fwrite, we can create the file and then toss this newly uploaed information inside. Close our fwrite, and unset our downloaded file - the user will now get a choice to install now or later.

//Download The File If We Do Not Have It
            if ( !is_file(  $_ENV['site']['files']['includes-dir'].'/UPDATES/MMD-CMS-'.$aV.'.zip' )) {
                echo '<p>Downloading New Update</p>';
                $newUpdate = file_get_contents('http://your-server.com/CMS-UPDATE-PACKAGES/MMD-CMS-'.$aV.'.zip');
                if ( !is_dir( $_ENV['site']['files']['includes-dir'].'/UPDATES/' ) ) mkdir ( $_ENV['site']['files']['includes-dir'].'/UPDATES/' );
                $dlHandler = fopen($_ENV['site']['files']['includes-dir'].'/UPDATES/MMD-CMS-'.$aV.'.zip', 'w');
                if ( !fwrite($dlHandler, $newUpdate) ) { echo '<p>Could not save new update. Operation aborted.</p>'; exit(); }
                fclose($dlHandler);
                echo '<p>Update Downloaded And Saved</p>';
                unset($newUpdate);
            } else echo '<p>Update already downloaded.</p>';

At this point our file is downloaded, and we toss a link out to our user to see if they want to install now.

            if ($_GET['doUpdate'] == true) {
                    //Open The File And Do Stuff           
             }
            else echo '<p>Update ready. <a href="?doUpdate=true">&raquo; Install Now?</a></p>';
            break;

We use break; to stop our foreach loop that is looking at our update list. We don't want to download a bunch of updates at once.

When they click that 'Install Now' link, we go ahead come back to this page. Since we have our download check up above, we don't need to worry about the script re-downloading the file. Now, we open our download and start to utilize the contents of the zip. zip_open was not readily available on our server, so we went ahead and recompiled apache and PHP to support it - that's another lesson in itself. Most servers will have it available. zip_open(); takes your zip file and opens it. Utilizing a while() loop, we use a mysql-like approach with the file. We loop through all of it's contents and place them on the server.

zip_entry_name() retrieves the filename of the file that we are currently looking at with zip_read. If our entry name ends with a slash, we skip and goto the next file. A note: zip_entry_name() will give the filename AND directory structure for the file, for example:

/support/includes/functions.php

We will use this to our advantage.

Sometimes when we make updates, we add new folders. For example, our caching plugin added a new folder to hold temporary files. Part of our script is to make sure all of our folders exist, so we go ahead and make sure all of our files have an accompanying dir. Using dirname() with zip_entry_name, and combining it with $_SERVER['DOCUMENT_ROOT'], we can make sure we have all relative folder trees. On our CMS, we store our DOCUMENT_ROOT in an $_ENV variable for instances where we are installed in a subfolder.

                //Open The File And Do Stuff
                $zipHandle = zip_open($_ENV['site']['files']['includes-dir'].'/UPDATES/MMD-CMS-'.$aV.'.zip');
                echo '<ul>';
                while ($aF = zip_read($zipHandle) )
                {
                    $thisFileName = zip_entry_name($aF);
                    $thisFileDir = dirname($thisFileName);
                   
                    //Continue if its not a file
                    if ( substr($thisFileName,-1,1) == '/') continue;
                   
    
                    //Make the directory if we need to...
                    if ( !is_dir ( $_ENV['site']['files']['server-root'].'/'.$thisFileDir ) )
                    {
                         mkdir ( $_ENV['site']['files']['server-root'].'/'.$thisFileDir );
                         echo '<li>Created Directory '.$thisFileDir.'</li>';
                    }

After we have made sure our directory exists, we simply place the file we are currently looking at. zip_entry_read() pulls the contents of the file. You need to use this with zip_entry_filesize, which returns the size of the file, or you will end up with only the first few lines of your files. For one reason or another, our unzipped files all ended up with erratic line spacing. We use str_replace to remove this spacing and turn out files back to normal form.

              
                    //Overwrite the file
                    if ( !is_dir($_ENV['site']['files']['server-root'].'/'.$thisFileName) ) {
                        echo '<li>'.$thisFileName.'...........';
                        $contents = zip_entry_read($aF, zip_entry_filesize($aF));
                        $contents = str_replace("rn", "n", $contents);
                        $updateThis = '';

At this point we are actually going to place the file. We have included an upgrade.php file that contains PHP code to execute when running an update. This may include database changes, setting changes, etc. or might not exist at all. If our current file name is 'upgrade.php', we grab the contents, write it to our current directory, include it to run any code inside, then delete the file. No big deal ,right?

                        //If we need to run commands, then do it.
                        if ( $thisFileName == 'upgrade.php' )
                        {
                            $upgradeExec = fopen ('upgrade.php','w');
                            fwrite($upgradeExec, $contents);
                            fclose($upgradeExec);
                            include ('upgrade.php');
                            unlink('upgrade.php');
                            echo' EXECUTED</li>';
                        }

If our file is NOT 'upgrade.php', we go ahead and overwrite or create the file that already exists. fwrite(); will overwrite an existing file, or create if it doesn't exist. Since our zip file contains all of our files in the proper directory format, it makes this REALLY easy for us. Again, DOCUMENT_ROOT + our file name (which includes the zip's document path). Open the file, place the zip's contents inside, and close your fwrite.

                        else
                        {
                            $updateThis = fopen($_ENV['site']['files']['server-root'].'/'.$thisFileName, 'w');
                            fwrite($updateThis, $contents);
                            fclose($updateThis);
                            unset($contents);
                            echo' UPDATED</li>';
                        }

At this point our file loop will go through all of our files and do what it needs to do with them. The loop ends and we let the user know. Set a variable to tell the script that we updated, and the code at the bottom of the script will let the user know. Our CMS function, set_setting() will update the database of our new version. Since we stopped our foreach loop, $aV will hold the number of our latest version.

                       }
                    }
                }
                echo '</ul>';
                $updated = TRUE;
    if ($updated == true)
    {
        set_setting('site','CMS',$aV);
        echo '<p class="success">&raquo; CMS Updated to v'.$aV.'</p>';
    }
    else if ($found != true) echo '<p>&raquo; No update is available.</p>';

I managed to make this script in about two hours. I hope I saved somebody some time with it. You can download the source files below with an example of a simple update I might put up. Make sure you format these files to work with your system.

Download Source Files

» Download Source Files

Screenshots

Update Image In Admin Menu

Our Admin Menu With Update Link

Update Screen After Download

Updater Showing New Install Available

Update Screen During Install

Update Install Output

Update Screen After Install

Update Check After Install