Skip to content

Instantly share code, notes, and snippets.

@johnstevenson
Created September 30, 2014 12:13
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/e03d91cdf9406e3e47f7 to your computer and use it in GitHub Desktop.
Save johnstevenson/e03d91cdf9406e3e47f7 to your computer and use it in GitHub Desktop.
Modify user environment variables in the Windows registry
<?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