Created
September 30, 2014 12:13
-
-
Save johnstevenson/e03d91cdf9406e3e47f7 to your computer and use it in GitHub Desktop.
Modify user environment variables in the Windows registry
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* Classes to modify User environment variables in the Windows registry | |
* | |
* Warning: | |
* Use at your own discretion and with care. It would be quite easy to | |
* make a system unstable by adding or modifying certain values. | |
* | |
* Ensure that this code works for you by testing it first (create a | |
* Restore Point or backup your HKCU\Environment key beforehand). | |
* | |
* Usage: | |
* $registry = new WindowsRegistry(); | |
* $env = new WindowsUserEnvironment($registry); | |
* | |
* $env->addUserVariable('MYVAR', '%SystemRoot%\\somewhere'); | |
* $env->removeUserVariable('MYVAR'); | |
* | |
* $env->addUserPath('%AppData%/somewhere'); | |
* $env->removeUserPath('%AppData%\\somewhere'); | |
*/ | |
class WindowsUserEnvironment | |
{ | |
protected $key = 'HKCU\\Environment'; | |
protected $registry = null; | |
public function __construct($registry) | |
{ | |
$this->registry = $registry; | |
} | |
public function addUserVariable($name, $value) | |
{ | |
if ('path' === strtolower($name)) { | |
return $this->addUserPath($value); | |
} else { | |
return $this->addValue($name, $value, false); | |
} | |
} | |
public function addUserPath($path) | |
{ | |
if (!$value = $this->getNewPath($path, true)) { | |
// the path already contains $path | |
return true; | |
} else { | |
return $this->addValue('Path', $value, true); | |
} | |
} | |
public function removeUserVariable($name) | |
{ | |
if ('path' === strtolower($name)) { | |
throw new \InvalidArgumentException('You cannot delete the user path'); | |
} else { | |
return $this->registry->delete($this->key, $name); | |
} | |
} | |
public function removeUserPath($path) | |
{ | |
if (!$value = $this->getNewPath($path, false)) { | |
if (null === $value) { | |
// the path does not contain $path | |
return true; | |
} else { | |
// the truncated path is empty | |
return $this->registry->delete($this->key, 'Path'); | |
} | |
} else { | |
// add the truncated path to the registry | |
return $this->addValue('Path', $value, true); | |
} | |
} | |
protected function escape($value) | |
{ | |
if (!$len = strlen($value)) { | |
return $value; | |
} | |
$s = '%' === $value[0] ? '^%' : $value[0]; | |
for ($i = 1; $i < $len; $i++) { | |
if ('%' === $value[$i]) { | |
if ('^' !== $value[$i - 1]) { | |
$s .= '^'; | |
} | |
} | |
$s .= $value[$i]; | |
} | |
return $s; | |
} | |
protected function quoteValue($value, &$type) { | |
$value = $this->escape($value); | |
if (false !== strpos($value, '^%')) { | |
$type = 'REG_EXPAND_SZ'; | |
$quote = '^"'; | |
} else { | |
$type = 'REG_SZ'; | |
$quote = '"'; | |
} | |
return $quote.$value.$quote; | |
} | |
protected function checkPath($path) | |
{ | |
if (!$path || false !== strpos($path, ';')) { | |
throw new \InvalidArgumentException('Invalid path value'); | |
} | |
} | |
protected function normalizePath($path) | |
{ | |
$path = str_replace('/', '\\', trim($path)); | |
return rtrim($path, '\\'); | |
} | |
protected function getUserPaths() | |
{ | |
$result = array(); | |
if ($this->registry->read('HKCU\\Environment', 'Path', $path)) { | |
$paths = explode(';', $path); | |
$result = array_filter($paths, function($item) { | |
return $item; | |
}); | |
} | |
return $result; | |
} | |
protected function getNewPath($value, $add) | |
{ | |
$value = $this->normalizePath($value); | |
$this->checkPath($value); | |
$paths = $this->getUserPaths(); | |
$newPaths = array(); | |
$test = strtolower($value); | |
$found = false; | |
foreach($paths as $item) { | |
if ($test === strtolower($this->normalizePath($item))) { | |
$found = true; | |
if ($add) { | |
return; | |
} else { | |
continue; | |
} | |
} | |
$newPaths[] = $item; | |
} | |
if ($add) { | |
$newPaths[] = $value; | |
} elseif (!$found) { | |
return; | |
} | |
return join(';', $newPaths); | |
} | |
protected function addValue($name, $value, $isPath) | |
{ | |
$value = $this->quoteValue($value, $type); | |
$type = $isPath ? 'REG_EXPAND_SZ' : $type; | |
return $this->registry->write($this->key, $name, $type, $value); | |
} | |
} | |
class WindowsRegistry | |
{ | |
protected function call($cmd, &$output) | |
{ | |
if (!defined('PHP_WINDOWS_VERSION_BUILD')) { | |
throw new \BadMethodCallException('Not supported on this OS'); | |
} | |
exec($cmd . ' 2>&1', $output, $status); | |
return 0 === $status; | |
} | |
/** | |
* Reads a value from the registry | |
* | |
* Returns True if a value is read from registry, putting the value | |
* in $value. | |
* | |
* $key is in the form of ROOTKEY\SubKey name where ROOTKEY is one of: | |
* HKLM, HKCU, HKCR, HKU, HKCC | |
* | |
* $raw affects how the data is returned for the following values, with | |
* the default format shown first: | |
* | |
* REG_DWORD, REG_QWORD | |
* integer, or hexadecimal if raw=true | |
* | |
* REG_BINARY | |
* string, or hexadecimal if raw=true | |
* | |
* REG_MULTI_SZ | |
* array, or string if raw=true (formatted 'sssss\0sssss\0sssss') | |
* | |
* @param String $key The parent key of the value | |
* @param String $name The name of the value | |
* @param mixed $value The returned value | |
* @param Bool $raw Whether to format certain values | |
* @return Bool Whether the value was found | |
*/ | |
public function read($key, $name, &$value, $raw = false) | |
{ | |
$result = false; | |
$value = null; | |
$cmd = 'REG QUERY "'.$key.'" /v "'.$name.'"'; | |
if (!$this->call($cmd, $output)) { | |
return $result; | |
} | |
$line = implode('', $output); | |
if ($pos = stripos($line, $name)) { | |
$line = trim(substr($line, $pos + strlen($name))); | |
$parts = preg_split('/\s+/', $line, 2); | |
if (count($parts) === 2) { | |
$type = strtoupper($parts[0]); | |
$value = $parts[1]; | |
if ('REG_DWORD' === $type || 'REG_QWORD' === $type) { | |
$value = $raw ? $value : intval($value, 16); | |
} elseif ('REG_BINARY' === $type) { | |
$value = $raw ? $value : @pack("H*" , $value); | |
} elseif ('REG_MULTI_SZ' === $type) { | |
$value = $raw ? $value : explode('\0', $value); | |
} | |
} | |
$result = true; | |
} | |
return $result; | |
} | |
public function write($key, $name, $type, $data) | |
{ | |
// Important to use the /f switch or we will invoke a prompt if the value exists | |
$cmd = 'REG ADD "'.$key.'" /v "'.$name.'" /t "'.$type.'" /d '.$data.' /f'; | |
return $this->call($cmd, $dummy); | |
} | |
public function delete($key, $name) | |
{ | |
// Important to use the /f switch or we will invoke a prompt | |
$cmd = 'REG DELETE "'.$key.'" /v "'.$name.'" /f'; | |
return $this->call($cmd, $dummy); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment