Self-updating PHP script

Now that we have thoroughly covered self-updating bash scripts, let’s have a look at the PHP counterpart:

 #!/usr/bin/php
<?php
define( "SELF", basename( __FILE__ ) );
define( "INVNAME", $argv[ 0 ] );

// The base location from where to retrieve new versions of this script
$UPDATE_BASE = "http://typo3scripts.googlecode.com/svn/trunk";

/**
 * Update check
 */
function updateCheck() {
  $_contentVersions = file_get_contents( $UPDATE_BASE . "/versions" );
  $_contentSelf     = split( "\n", file_get_contents( INVNAME ), 2 );
  $_sumSelf         = md5( $_contentSelf[ 1 ] );
  
  $_isListed = preg_match( "/^" . SELF . " (?P<sum>[0-9a-zA-Z]{32})/ms", $_contentVersions, $_sumLatest );
  if( !$_isListed ) {
    file_put_contents( "php://stderr", "No update information is available for '" . SELF . "'.\n" );
    file_put_contents( "php://stderr", "Please check the project home page http://code.google.com/p/typo3scripts/.\n" );
    
  } else if( $_sumSelf != $_sumLatest[ 1 ] ) {
    file_put_contents( "php://stderr", "NOTE: New version available!\n" );
  }
}

/**
 * Self-update
 */
function runSelfUpdate() {
  echo "Performing self-update...\n";

  $_tempFileName = INVNAME . ".tmp";
  
  // Download new version
  echo "Downloading latest version...";
  global $UPDATE_BASE;
  $_fileContents = @file_get_contents( $UPDATE_BASE . "/" . SELF );
  if( strlen( $_fileContents ) <= 0 ) {
    echo "Failed: Error while trying to download new version!\n";
    echo "File requested: " . $UPDATE_BASE . "/" . SELF . "\n";
    exit( 1 );
  }
  $_payload = split( "\n", $_fileContents, 2 );
  echo "Done.\n";
  
  // Restore shebang
  $_selfContent = split( "\n", file_get_contents( INVNAME ), 2 );
  $_interpreter = $_selfContent[ 0 ];
  file_put_contents( $_tempFileName, $_interpreter . "\n" );
  file_put_contents( $_tempFileName, $_payload[ 1 ], FILE_APPEND );
  
  // Copy over modes from old version
  $_octalMode = fileperms( INVNAME );
  if( FALSE == chmod( $_tempFileName, $_octalMode ) ) {
    echo "Failed: Error while trying to set mode on $_tempFileName.\n";
    exit( 1 );
  }
  
  // Spawn update script
  $_name = INVNAME;
  $_updateScript = <<<EOS
#!/bin/bash
# Overwrite old file with new
if mv "$_tempFileName" "$_name"; then
  echo "Done."
  echo "Update complete."
  rm -- $0
else
  echo "Failed!"
fi
EOS;
  file_put_contents( "updateScript.sh", $_updateScript );

  echo "Inserting update process...";
    file_put_contents( "updateScript.sh", $_updateScript );
  chmod( "updateScript.sh", 0700 );

  if( function_exists( "pcntl_exec" ) ) {
    pcntl_exec( "/bin/bash", array( "./updateScript.sh" ) );
    
  } else if( function_exists( "passthru" ) ) {
    die( passthru( "./updateScript.sh" ) );
    
  } else {
    die( "Please execute ./updateScript.sh now." );
  }
}
?>

You might notice that it’s pretty much a line-by-line translation of the bash version. That was intentional due to maintenance concerns within the project this was developed for. So, it might not be the optimal solution, but it works at least just as well as the bash version.

Leave a Reply

You must be logged in to post a comment.