Skip to content

Instantly share code, notes, and snippets.

@johnstevenson
Last active July 4, 2020 13:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnstevenson/1236ffc261b2156bbd9c9e7f2059ca68 to your computer and use it in GitHub Desktop.
Save johnstevenson/1236ffc261b2156bbd9c9e7f2059ca68 to your computer and use it in GitHub Desktop.
Composer self-update as admin
<?php
/**
* This demo shows a method for elevating to an Admin on Windows and running
* a command in cmd.exe, from a PHP script. It demonstrates how Composer can
* self-update to an Admin location.
*
* Note that it creates composer-phar.txt file in the current directory.
*/
$localFilename = ''; // the location where composer.phar is to be installed
$newFilename = ''; // the location of the downloaded (or rollback) composer.phar
$interactive = true; // assume we are interactive
echo "Upgrading to version '3.0.0' (stable channel).", PHP_EOL;
// Assume rename($newFilename, $localFilename) fails
if (!$interactive) {
return 1;
}
$cygwin = preg_match('/cygwin/i', php_uname());
if (!$cygwin && !defined('PHP_WINDOWS_VERSION_BUILD')) {
return 1;
}
// See if we are already an admin
exec('cmd.exe /c fltmc.exe filters', $output, $exitCode);
if ($exitCode === 0) {
return 1;
}
// Set up dummy files
$localFilename = __DIR__.DIRECTORY_SEPARATOR.'composer-phar.txt';
$newFilename = @tempnam(sys_get_temp_dir(), '');
file_put_contents($newFilename, sprintf('composer phar content %s', microtime()));
echo sprintf('Unable to write to the "%s" directory. Access is denied.', dirname($localFilename)), PHP_EOL;
$question = ' Complete this operation with Administrator priviledges [yes]? ';
$answer = readline($question);
$cancelledMsg = 'Operation cancelled. Please run the self-update command as an Administrator.'.PHP_EOL;
if (!preg_match('/^y/i', $answer)) {
echo $cancelledMsg;
return 1;
}
$hashFunc = function($filename) {
return file_exists($filename) ? hash_file('sha3-384', $filename) : '';
};
$newFilenameHash = $hashFunc($newFilename);
$tmpFile = @tempnam(sys_get_temp_dir(), '');
$script = $tmpFile.'.ps1';
rename($tmpFile, $script);
// Format the file names for cmd.exe
if ($cygwin) {
$cygwinLocalFilename = $localFilename;
$localFilename = exec(sprintf("cygpath -w '%s'", $localFilename));
$newFilename = exec(sprintf("cygpath -w '%s'", $newFilename));
} else {
// cmd.exe move seems to be fussy about backslashes
$localFilename = str_replace('/', '\\', $localFilename);
$newFilename = str_replace('/', '\\', $newFilename);
}
$ps1 = <<<EOT
\$exitCode = 0
try {
# Use an elevated cmd.exe to move new filename to local filename
Start-Process -FilePath "\$env:comspec" -ArgumentList "/c move /y `"$newFilename`" `"$localFilename`"" -Verb RunAs -WindowStyle Hidden
} catch {
\$exitCode = 2
}
exit \$exitCode
EOT;
file_put_contents($script, $ps1);
if ($cygwin) {
$winPath = exec(sprintf("cygpath -w '%s'", $tmpFile));
$script = $winPath.'.ps1';
}
$output = null;
$command = sprintf('powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File "%s"', $script);
exec($command, $output, $exitCode);
@unlink($script);
if ($exitCode === 0) {
// see if the file was moved
usleep(300000);
$localFilename = $cygwin ? $cygwinLocalFilename : $localFilename;
$hash = $hashFunc($localFilename);
if ($hash && $hash === $newFilenameHash) {
echo 'Operation succeeded.'.PHP_EOL;
return 0;
} else {
echo 'Operation failed (file not written). Please run the self-update command as an Administrator.'.PHP_EOL;
return 1;
}
} elseif ($exitCode === 2) {
echo $cancelledMsg;
return 1;
} else {
echo 'Operation failed (unabled to run Powershell script). Please run the self-update command as an Administrator.'.PHP_EOL;
if ($output) {
echo 'Error: ', implode(PHP_EOL, $output), PHP_EOL;
}
return 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment