Skip to content

Commit

Permalink
Merge pull request #4 from spatie/feature-retry-as-windows-admin
Browse files Browse the repository at this point in the history
Implement Windows retrying as administrator to update PHP ini
  • Loading branch information
freekmurze authored Mar 1, 2022
2 parents 1d51bba + 06b5040 commit a3aa5e6
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 3 deletions.
1 change: 1 addition & 0 deletions dump-phar-generator/box.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"output": "dump.phar",
"check-requirements": false,
"compactors": ["KevinGH\\Box\\Compactor\\PhpScoper"]
}
1 change: 1 addition & 0 deletions ray-phar-generator/box.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"output": "ray.phar",
"check-requirements": false,
"compactors": ["KevinGH\\Box\\Compactor\\PhpScoper"]
}
18 changes: 17 additions & 1 deletion src/Commands/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

class InstallCommand extends Command
{
use RetriesAsWindowsAdmin;

protected function configure()
{
$this
Expand All @@ -24,12 +26,26 @@ protected function execute(InputInterface $input, OutputInterface $output)

$output->writeln("Updating PHP ini: {$ini->getPath()}");

if (! $ini->update('auto_prepend_file', $this->getLoaderPath())) {
if ($ini->update('auto_prepend_file', $this->getLoaderPath())) {
$output->writeln('Successfully updated PHP ini. Global Ray has been installed.');

return 0;
}

if (! $this->shouldRetryAsWindowsAdmin($ini, $input)) {
$output->writeln('Unable to update PHP ini.');

return -1;
}

$output->writeln('Unable to update PHP ini. Access is denied.');

if (! $this->retryAsWindowsAdmin($ini, $input, $output)) {
$output->writeln('Failed updating PHP ini.');

return -1;
}

$output->writeln('Successfully updated PHP ini. Global Ray has been installed.');

return 0;
Expand Down
64 changes: 64 additions & 0 deletions src/Commands/RetriesAsWindowsAdmin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace Spatie\GlobalRay\Commands;

use Spatie\GlobalRay\Support\PhpIni;
use Spatie\GlobalRay\Support\Platform;
use Symfony\Component\Console\Input\Input;
use Symfony\Component\Console\Output\Output;
use Symfony\Component\Console\Question\ConfirmationQuestion;

trait RetriesAsWindowsAdmin
{
protected function shouldRetryAsWindowsAdmin(PhpIni $ini, Input $input): bool
{
return ! is_writeable($ini->getPath())
&& $input->isInteractive()
&& Platform::isWindowsNonAdminUser();
}

protected function retryAsWindowsAdmin(PhpIni $ini, Input $input, Output $output): bool
{
$question = new ConfirmationQuestion('Retry as Adminstrator?', false);

if (! $this->getHelper('question')->ask($input, $output, $question)) {
return false;
}

$tmpFile = tempnam(sys_get_temp_dir(), '');

file_put_contents($tmpFile, $ini->getContents());

$newIni = new PhpIni($tmpFile);

$newIni->update('auto_prepend_file', $this->getLoaderPath());

return $this->tryCopyAsWindowsAdmin($newIni->getPath(), $ini->getPath());
}

protected function tryCopyAsWindowsAdmin(string $source, string $destination): bool
{
$tmpFile = tempnam(sys_get_temp_dir(), '');
$script = $tmpFile.'.vbs';
rename($tmpFile, $script);

$source = str_replace('/', '\\', $source);
$destination = str_replace('/', '\\', $destination);

$vbs = <<<EOT
Set UAC = CreateObject("Shell.Application")
UAC.ShellExecute "cmd.exe", "/c copy /b /y ""$source"" ""$destination""", "", "runas", 0
Wscript.Sleep(300)
EOT;

file_put_contents($script, $vbs);
exec('"'.$script.'"');
@unlink($script);

if ($result = is_readable($destination)) {
@unlink($source);
}

return $result;
}
}
24 changes: 23 additions & 1 deletion src/Commands/UninstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

class UninstallCommand extends Command
{
use RetriesAsWindowsAdmin;

protected function configure()
{
$this
Expand All @@ -22,7 +24,27 @@ protected function execute(InputInterface $input, OutputInterface $output)
{
$ini = new PhpIni($input->getOption('ini'));

$ini->update('auto_prepend_file', null);
$output->writeln("Updating PHP ini: {$ini->getPath()}");

if ($ini->update('auto_prepend_file', null)) {
$output->writeln('Successfully updated PHP ini. Global Ray has been uninstalled.');

return 0;
}

if (! $this->shouldRetryAsWindowsAdmin($ini, $input)) {
$output->writeln('Unable to update PHP ini.');

return -1;
}

$output->writeln('Unable to update PHP ini. Access is denied.');

if (! $this->retryAsWindowsAdmin($ini, $input, $output)) {
$output->writeln('Failed updating PHP ini.');

return -1;
}

$output->writeln('Successfully updated PHP ini. Global Ray has been uninstalled.');

Expand Down
5 changes: 5 additions & 0 deletions src/Support/PhpIni.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ public function update(string $optionName, ?string $value): bool
return is_int(file_put_contents($this->path, $contents));
}

public function getContents()
{
return file_get_contents($this->path);
}

public function getPath()
{
return $this->path;
Expand Down
22 changes: 22 additions & 0 deletions src/Support/Platform.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Spatie\GlobalRay\Support;

class Platform
{
public static function isWindows(): bool
{
return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
}

public static function isWindowsNonAdminUser(): bool
{
if (! static::isWindows()) {
return false;
}

exec('fltmc.exe filters', $output, $exitCode);

return $exitCode !== 0;
}
}
3 changes: 2 additions & 1 deletion tests/Pest.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<?php

use Spatie\GlobalRay\Support\Platform;
use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Process\Process;

function executeGlobalRay(string $command, array $args = []): Process
{
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
if (Platform::isWindows()) {
$process = executeCommand(
array_merge(['php', 'global-ray', $command], $args),
realpath(__DIR__.'/../bin')
Expand Down

0 comments on commit a3aa5e6

Please sign in to comment.