Skip to content

Instantly share code, notes, and snippets.

@dixyes
Last active May 6, 2022 02:40
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 dixyes/feec7e0794da940bd9e6497434b59c83 to your computer and use it in GitHub Desktop.
Save dixyes/feec7e0794da940bd9e6497434b59c83 to your computer and use it in GitHub Desktop.
php FFI registry utilities
<?php
/**
* Win32 api utitlities class
* needs to be initialized with W32api::init()
*/
final class W32api
{
private const BASIC_CDEFS = <<<'CDEF'
typedef uint16_t WORD, *PWORD, *LPWORD;
typedef uint32_t DWORD, *PDWORD, *LPDWORD;
typedef uint64_t SIZE_T;
typedef uint8_t BYTE, *LPBYTE;
typedef long LONG;
typedef unsigned long ULONG;
typedef unsigned int UINT;
typedef short SHORT;
typedef long LSTATUS, LRESULT;
typedef uint16_t LANGID;
typedef DWORD ACCESS_MASK;
typedef ACCESS_MASK REGSAM;
typedef void* LPVOID;
typedef void* HANDLE;
typedef HANDLE* PHANDLE;
typedef HANDLE HKEY, *PHKEY;
typedef int32_t BOOL, *LPBOOL;
typedef int16_t wchar_t;
typedef const wchar_t *LPCWSTR;
typedef wchar_t WCHAR, *LPWSTR;
typedef const char *LPCSTR;
typedef char CHAR, *LPSTR;
CDEF;
public const KERNEL32_CDEFS = <<<'CDEF'
int MultiByteToWideChar(
UINT CodePage,
DWORD dwFlags,
char *lpMultiByteStr,
int cbMultiByte,
LPWSTR lpWideCharStr,
int cchWideChar
);
int WideCharToMultiByte(
UINT CodePage,
DWORD dwFlags,
LPCWSTR lpWideCharStr,
int cchWideChar,
LPSTR lpMultiByteStr,
int cbMultiByte,
LPCSTR lpDefaultChar,
LPBOOL lpUsedDefaultChar
);
BOOL CloseHandle(
HANDLE hObject
);
LSTATUS RegCreateKeyExA(
HKEY hKey,
LPCSTR lpSubKey,
DWORD Reserved,
LPSTR lpClass,
DWORD dwOptions,
REGSAM samDesired,
const /*LPSECURITY_ATTRIBUTES*/ void* lpSecurityAttributes,
PHKEY phkResult,
LPDWORD lpdwDisposition
);
LSTATUS RegOpenKeyExA(
HKEY hKey,
LPCSTR lpSubKey,
DWORD ulOptions,
REGSAM samDesired,
PHKEY phkResult
);
LSTATUS RegCloseKey(
HKEY hKey
);
LSTATUS RegQueryValueExA(
HKEY hKey,
LPCSTR lpValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData
);
LSTATUS RegSetValueExA(
HKEY hKey,
LPCSTR lpValueName,
DWORD Reserved,
DWORD dwType,
LPVOID lpData,
DWORD cbData
);
LSTATUS RegDeleteTreeA(
HKEY hKey,
LPCSTR lpSubKey
);
LSTATUS RegEnumKeyExA(
HKEY hKey,
DWORD dwIndex,
LPSTR lpName,
LPDWORD lpcchName,
LPDWORD lpReserved,
LPSTR lpClass,
LPDWORD lpcchClass,
LPVOID lpftLastWriteTime
);
LSTATUS RegEnumValueA(
HKEY hKey,
DWORD dwIndex,
LPSTR lpValueName,
LPDWORD lpcchValueName,
LPDWORD lpReserved,
LPDWORD lpType,
char* lpData,
LPDWORD lpcbData
);
DWORD GetLastError();
CDEF;
/** @removable */
private function __construct()
{
throw new Exception('this utility class should not be instance');
}
/**
* make ffi objects by cdef with basic cdefs
*
* @param string $cdef cdef used
* @param string $dll dll to find symbols
* @return \FFI
*/
public static function make(string $cdef, string $dll): FFI
{
return \FFI::cdef(self::BASIC_CDEFS . $cdef, $dll);
}
/**
* kernel32 wrapper
*
* @var Kernel32API
*/
public static \FFI $kernel32;
/**
* HKEY_CLASSES_ROOT handle
*/
public static \FFI\CData $hkcr;
/**
* HKEY_CURRENT_USER handle
*/
public static \FFI\CData $hkcu;
/**
* HKEY_LOCAL_MACHINE handle
*/
public static \FFI\CData $hklm;
/**
* HKEY_USERS handle
*/
public static \FFI\CData $hku;
/**
* HKEY_CURRENT_CONFIG handle
*/
public static \FFI\CData $hkcc;
public static function init(): void
{
/* @phpstan-ignore-next-line */
self::$kernel32 = self::make(self::KERNEL32_CDEFS, 'kernel32.dll');
foreach ([
"hkcr" => "\x00\x00\x00\x80" /* 0x80000000 HKEY_CLASSES_ROOT */,
"hkcu" => "\x01\x00\x00\x80" /* 0x80000001 HKEY_CURRENT_USER */,
"hklm" => "\x02\x00\x00\x80" /* 0x80000002 HKEY_LOCAL_MACHINE */,
"hku" => "\x03\x00\x00\x80" /* 0x80000003 HKEY_USERS */,
"hkcc" => "\x04\x00\x00\x80" /* 0x80000004 HKEY_CURRENT_CONFIG */,
] as $name => $data) {
/* @phpstan-ignore-next-line */
$hkey = self::$kernel32->new('HKEY', false, true);
/* @removable */
if ($hkey === null) {
throw new Exception('failed create ffi object');
}
\FFI::memcpy(\FFI::addr($hkey), $data, 4);
static::${$name} = $hkey;
}
}
}
class RegistryValue extends stdClass implements Stringable
{
const REG_SZ = 1;
const REG_EXPAND_SZ = 2;
const REG_BINARY = 3;
const REG_MULTI_SZ = 7;
const REG_DWORD = 4;
const REG_QWORD = 11;
public function __construct(
public ?int $type,
public string|int|bool|array $data,
) {
if ($this->type === null) {
switch (true) {
case is_bool($data):
case is_int($data):
$this->type = static::REG_DWORD;
break;
case is_string($data):
$this->type = static::REG_SZ;
break;
case is_array($data):
$this->type = static::REG_MULTI_SZ;
break;
default:
throw new Exception('not implemented');
}
}
// validate
switch ($this->type) {
case static::REG_SZ:
case static::REG_EXPAND_SZ:
case static::REG_BINARY:
if (!is_string($this->data)) {
goto typeerror;
}
break;
case static::REG_MULTI_SZ:
if (!is_array($this->data)) {
goto typeerror;
}
foreach ($this->data as $v) {
if (!is_string($v)) {
throw new Exception('bad multi sz');
}
}
break;
case static::REG_DWORD:
case static::REG_QWORD:
switch (true) {
case is_int($this->data):
break;
case is_bool($this->data):
$this->data = $this->data ? 1 : 0;
break;
case is_string($this->data):
if ($this->type === static::REG_QWORD && strlen($this->data) === 8) {
break;
}
throw new Exception("bad dword/qword value \"{$this->data}\"");
default:
goto typeerror;
}
break;
default:
throw new Exception('not implemented');
}
return;
typeerror:
if ($data instanceof \FFI\CData) {
return;
}
throw new TypeError("cannot use {$this->data} for type {$this->type}");
}
/**
* make cdata for setting reg value
*
* @return array{0: \FFI\CData|str, 1: int}
*/
public function makeCData(): array
{
if ($this->data instanceof \FFI\CData) {
return [$this->data, \FFI::sizeof($this->data)];
}
switch ($this->type) {
case static::REG_SZ:
case static::REG_EXPAND_SZ:
case static::REG_BINARY:
$cvalue = $this->data;
$clen = strlen($this->data);
break;
case static::REG_MULTI_SZ:
$data = array_map(fn ($str) => $str . "\0", $this->data);
$cvalue = implode('', $data) . "\0";
$clen = strlen($cvalue);
break;
case static::REG_DWORD:
$clen = 4;
$cvalue = \FFI::new('char[4]');
$cintvalue = \FFI::new('uint32_t');
$cintvalue->cdata = $this->data;
\FFI::memcpy($cvalue, $cintvalue, 4);
break;
case static::REG_QWORD:
$clen = 8;
if (is_string($this->data)) {
$cvalue = \FFI::new('char[8]');
\FFI::memcpy($cvalue, $this->data, 8);
break;
}
$cvalue = \FFI::new('char[8]');
$cintvalue = \FFI::new('uint64_t');
$cintvalue->cdata = $this->data;
\FFI::memcpy($cvalue, $cintvalue, 8);
break;
default:
throw new Exception('not implemented');
}
return [$cvalue, $clen];
}
public function toNumber(): int
{
switch ($this->type) {
case static::REG_DWORD:
if ($this->data instanceof \FFI\CData) {
return $this->data->cdata;
}
return $this->data;
case static::REG_QWORD:
if ($this->data instanceof \FFI\CData || is_string($this->data)) {
$uint64 = \FFI::new('uint64_t');
\FFI::memcpy($uint64, $this->data, 8);
return $uint64->cdata;
}
return $this->data;
default:
throw new Exception('not implemented');
}
}
public function __toString(): string
{
$data = $this->data;
switch ($this->type) {
case static::REG_DWORD:
case static::REG_QWORD:
$data = $this->toNumber();
break;
case static::REG_BINARY:
case static::REG_EXPAND_SZ:
case static::REG_SZ:
if ($data instanceof \FFI\CData) {
$data = \FFI::string($data);
}
break;
default:
throw new Exception('not implemented');
}
return (string)$data;
}
public static function typeToName(int $type): string
{
// I want enum...
switch ($type) {
case static::REG_SZ:
return 'REG_SZ';
case static::REG_EXPAND_SZ:
return 'REG_EXPAND_SZ';
case static::REG_BINARY:
return 'REG_BINARY';
case static::REG_MULTI_SZ:
return 'REG_MULTI_SZ';
case static::REG_DWORD:
return 'REG_DWORD';
case static::REG_QWORD:
return 'REG_QWORD';
default:
throw new Exception('not implemented');
}
}
}
/**
* registry key class, use arrayaccess apis to curd values
*
* @implements \ArrayAccess<string|null,int|string|null>
*/
class RegistryKey implements ArrayAccess, IteratorAggregate
{
/**
* readonly permission
*/
public const KEY_READ = 0x20019;
/**
* readable and writable permission
*/
public const KEY_WRITE = 0x20006;
final private function __construct(
private FFI\CData $hkey,
private string $key,
private int $permission,
) {
}
public function __destruct()
{
W32api::$kernel32->RegCloseKey($this->hkey);
\FFI::free(\FFI::addr($this->hkey));
}
public function __toString(): string
{
return 'RegistryKey<' . $this->key . '>';
}
public function __debugInfo()
{
/**
* ffi may try to read memory address which handle value refered to, then segfault
*/
return ['hkey' => '<redacted>', 'key' => $this->key];
}
/**
* create a registry key, can also open existing key
*
* @param string $key key name
* @param null|FFI\CData|self $root root key, default is HKLM
* @param int|null $permission permission, default is rw
* @return static|null created/opened regkey
*/
public static function create(string $key, null|\FFI\CData|self $root = null, ?int $permission = null): ?static
{
$hkey = W32api::$kernel32->new('HKEY', false, true);
/* @removable */
if ($hkey === null) {
throw new Exception('failed create ffi object');
}
$hkey_root = $root instanceof \FFI\CData ? $root : ($root ? $root->hkey : W32api::$hklm);
$full_key = $root instanceof static ? static::joinPath($root->key, $key) : $key;
$permission = $permission ?? static::KEY_WRITE;
$ret = W32api::$kernel32->RegCreateKeyExA(
$hkey_root, // hkey
$key, // subkey
0, // reserved
null, // class
0, // options
$permission, // sam
null, // sec attr
\FFI::addr($hkey), // pointer to hkey
null // dispos
);
if ($ret !== 0) {
\FFI::free(\FFI::addr($hkey));
throw new Exception("failed create key \"{$full_key}\": {$ret}");
}
return new static($hkey, $key, $permission);
}
/**
* open exist key
*
* @param string $key key path
* @param null|FFI\CData|self $root root key, default is HKLM
* @param int|null $permission permission, default is ro
*/
public static function open(string $key, null|\FFI\CData|self $root = null, ?int $permission = null): ?static
{
$hkey = W32api::$kernel32->new('HKEY', false, true);
/* @removable */
if ($hkey === null) {
throw new Exception('failed create ffi object');
}
$hkey_root = $root instanceof \FFI\CData ? $root : ($root ? $root->hkey : W32api::$hklm);
$full_key = $root instanceof static ? static::joinPath($root->key, $key) : $key;
$permission = $permission ?? static::KEY_READ;
$ret = W32api::$kernel32->RegOpenKeyExA(
$hkey_root, // hkey
$key, // subkey
0, // options
$permission, // sam
\FFI::addr($hkey) // pointer to hkey
);
if ($ret !== 0) {
\FFI::free(\FFI::addr($hkey));
if ($ret/* ERROR_FILE_NOT_FOUND */ !== 2) {
throw new Exception("failed open key \"{$full_key}\": {$ret}");
}
return null;
}
return new static($hkey, $full_key, $permission);
}
/**
* create sub key
*
* @param string $key key path
* @param int|null $permission permission, default is rw
*/
public function createSubKey(string $key, ?int $permission = null): ?static
{
return static::create($key, $this->hkey, $permission);
}
/**
* open sub exist key
*
* @param string $key key path
* @param int|null $permission permission, default is ro
*/
public function openSubKey(string $key, ?int $permission = null): ?static
{
return static::open($key, $this->hkey, $permission);
}
/**
* join reg path
*/
public static function joinPath(string $a, string $b): string
{
return rtrim($a, '\\') . '\\' . ltrim($b, '\\');
}
/**
* enum keys under a key
*
* @return Iterable<string>
*/
public function enumrateSubKeys(): Iterable
{
$keyName = \FFI::new('char[4096]');
$keyNameLen = W32API::$kernel32->new('DWORD', false, true);
$ret = 0;
$index = 0;
while ($ret != 259/*ERROR_NO_MORE_ITEMS*/) {
$keyNameLen->cdata = \FFI::sizeof($keyName);
$ret = W32api::$kernel32->RegEnumKeyExA(
$this->hkey, // hkey
$index++, // index
$keyName, // out: key name
\FFI::addr($keyNameLen), // in, out: key name size
null,
null, // out: key class
null, // in, out: size of key class
null, // in, out: last written
);
if ($ret == 259/*ERROR_NO_MORE_ITEMS*/) {
break;
}
if ($ret !== 0) {
\FFI::free($keyNameLen);
throw new Exception("failed enumurate key \"{$this->key}\" values: {$ret}");
}
$name = \FFI::string($keyName, $keyNameLen->cdata);
yield $name;
}
\FFI::free($keyNameLen);
}
/**
* prepare buffer cdata from RegEnumValueA or RegQueryValueExA
*/
private static function prepareCData(int $type, int $size): \FFI\CData
{
switch ($type) {
case RegistryValue::REG_SZ:
case RegistryValue::REG_EXPAND_SZ:
case RegistryValue::REG_BINARY:
case RegistryValue::REG_MULTI_SZ:
$cval = \FFI::new("char[{$size}]", false, true);
break;
case RegistryValue::REG_DWORD:
case RegistryValue::REG_QWORD:
$cval = \FFI::new('uint64_t[1]', false, true);
break;
default:
throw new Exception('not implemented type ' . $type);
}
return $cval;
}
/**
* parse cdata from RegEnumValueA or RegQueryValueExA
*/
private static function parseCData(int $type, \FFI\CData $cval, int $size, bool $useRegistryValue): mixed
{
switch ($type) {
case RegistryValue::REG_SZ:
case RegistryValue::REG_EXPAND_SZ:
// omit endding '\0'
$size--;
case RegistryValue::REG_BINARY:
$retval = \FFI::string($cval, $size);
break;
case RegistryValue::REG_MULTI_SZ:
if ($size <= 1) {
$retval = [];
break;
}
$data = \FFI::string($cval, $size - 1);
$retval = explode("\0", $data);
array_pop($retval);
reset($retval);
break;
case RegistryValue::REG_DWORD:
$retval = $cval[0];
break;
case RegistryValue::REG_QWORD:
return new RegistryValue(RegistryValue::REG_QWORD, \FFI::string($cval, 8));
default:
throw new Exception('not implemented');
}
if ($useRegistryValue) {
$retval = new RegistryValue($type, $retval);
}
return $retval;
}
/**
* enum values under a key
*
* @return Iterable<string, mixed>
* @phpstan-return ($useRegistryValue is true ? Iterable<string, RegistryValue> : Iterable<string, mixed>)
*/
public function enumrateValues(bool $useRegistryValue = false): Iterable
{
$valueName = \FFI::new("char[16384]");
$valueNameLen = W32API::$kernel32->new('DWORD');
$valueDataLen = W32API::$kernel32->new('DWORD');
$valueType = W32API::$kernel32->new('DWORD');
$ret = 0;
$index = 0;
while ($ret != 259/*ERROR_NO_MORE_ITEMS*/) {
// get data len
$valueNameLen->cdata = \FFI::sizeof($valueName);
$ret = W32api::$kernel32->RegEnumValueA(
$this->hkey, // hkey
$index, // index
$valueName, // out: value name
\FFI::addr($valueNameLen), // in, out: value name size
null,
\FFI::addr($valueType), // out: value type
null, // out: value data
\FFI::addr($valueDataLen), // in, out: size of value data
);
if ($ret == 259/*ERROR_NO_MORE_ITEMS*/) {
break;
}
if ($ret !== 0) {
throw new Exception("failed enumurate key \"{$this->key}\" size: {$ret}");
}
// get data
$valueNameLen->cdata = \FFI::sizeof($valueName);
$valueData = static::prepareCData($valueType->cdata, $valueDataLen->cdata);
$ret = W32api::$kernel32->RegEnumValueA(
$this->hkey, // hkey
$index++, // index
$valueName, // out: value name
\FFI::addr($valueNameLen), // in, out: value name size
null,
\FFI::addr($valueType), // out: value type
\FFI::cast('char*', $valueData), // out: value data
\FFI::addr($valueDataLen), // in, out: size of value data
);
if ($ret == 259/*ERROR_NO_MORE_ITEMS*/) {
break;
}
if ($ret !== 0) {
throw new Exception("failed enumurate key \"{$this->key}\" values: {$ret}");
}
if ($valueNameLen->cdata === 0) {
$name = null;
} else {
$name = \FFI::string($valueName, $valueNameLen->cdata);
}
$type = $valueType->cdata;
$data = static::parseCData($type, $valueData, $valueDataLen->cdata, $useRegistryValue);
yield $name => $data;
}
}
/**
* get value under a key
*
* @param string|null $valueName value name, null for "Default" value
*/
public function getValue(?string $valueName, bool $useRegistryValue = false): mixed
{
$retval = null;
try {
$type = W32api::$kernel32->new('DWORD', false, true);
/* @removable */
if ($type === null) {
throw new Exception('failed create ffi object');
}
$size = W32api::$kernel32->new('DWORD', false, true);
/* @removable */
if ($size === null) {
throw new Exception('failed create ffi object');
}
$ret = W32api::$kernel32->RegQueryValueExA($this->hkey, $valueName, null, \FFI::addr($type), null, \FFI::addr($size));
if ($ret !== 0) {
if ($ret/* ERROR_FILE_NOT_FOUND */ !== 2) {
echo "failed read \"{$valueName}\" type and size: {$ret}" . PHP_EOL;
}
return null;
}
//var_dump($type->cdata, $size->cdata);
/**
* @var int $sizeInt
*/
$sizeInt = $size->cdata;
$cval = static::prepareCData($type->cdata, $sizeInt);
/* @removable */
if ($cval === null) {
throw new Exception('failed create ffi object');
}
$ret = W32api::$kernel32->RegQueryValueExA($this->hkey, $valueName, null, null, \FFI::cast('uint8_t*', $cval), \FFI::addr($size));
if ($ret !== 0) {
echo "failed read \"{$valueName}\" value: {$ret}" . PHP_EOL;
return null;
}
$retval = static::parseCData($type->cdata, $cval, $size->cdata, $useRegistryValue);
} finally {
if (isset($type)) {
\FFI::free($type);
}
if (isset($size)) {
\FFI::free($size);
}
if (isset($cval)) {
\FFI::free($cval);
}
}
return $retval;
}
/**
* deleta a reg key
*
* @param string $key key path
* @param CData|null $root root key, default is HKLM
* @return bool true is success, otherwise false
*/
public static function delete(string $key, ?FFI\CData $root = null): bool
{
$ret = W32api::$kernel32->RegDeleteTreeA($root ?? W32api::$hklm, $key);
if ($ret !== 0 && $ret/* ERROR_FILE_NOT_FOUND */ !== 2) {
throw new Exception("failed delete key \"{$key}\": {$ret}");
}
return true;
}
public function offsetExists(mixed $key): bool
{
if (!is_string($key)) {
throw new Exception("bad offset type: {$key}");
}
return $this->offsetGet($key) !== null;
}
/**
* @param string $key
*/
public function offsetGet(mixed $key): mixed
{
return $this->getValue($key);
}
public function offsetSet(mixed $key, mixed $value): void
{
if (!$value instanceof RegistryValue) {
$value = new RegistryValue(type: null, data: $value);
}
$type = $value->type;
[$cvalue, $clen] = $value->makeCData();
$ret = W32api::$kernel32->RegSetValueExA($this->hkey, $key, 0, $type, $cvalue, $clen);
if ($ret !== 0) {
throw new Exception("failed set \"{$key}\" for {$this}: $ret");
}
}
public function offsetUnset(mixed $offset): void
{
throw new Exception('not implemented');
}
public function getIterator(): Traversable
{
return $this->enumrateValues();
}
}
W32API::init();
$ceshiKey = RegistryKey::create(
key: 'ceshi',
root: W32api::$hkcu,
permission: RegistryKey::KEY_WRITE | RegistryKey::KEY_READ
);
function myassert($thing, $msg = null)
{
if (!$thing) {
throw new Exception('failed assert' . ($msg ? ': ' . $msg : ''));
}
}
function myassertSame($a, $b)
{
if ($a !== $b) {
var_dump($a, $b);
myassert(false, "$a is not $b");
}
}
function myassertNotSame($a, $b)
{
if ($a === $b) {
var_dump($a, $b);
myassert(false, "$a is $b");
}
}
function matchformat($str, $format)
{
$matches = sscanf(trim($str), trim($format));
foreach ($matches as $v) {
myassert($v !== null);
}
}
var_dump($ceshiKey);
ob_start();
var_dump($ceshiKey);
$dumped = ob_get_clean();
matchformat($dumped, '
object(RegistryKey)#%d (%d) {
["hkey"]=>
string(%d) "<redacted>"
["key"]=>
string(%d) "ceshi"
}');
print("$ceshiKey\n");
myassertSame("$ceshiKey", "RegistryKey<ceshi>");
$ceshiROKey = RegistryKey::open('ceshi', W32api::$hkcu);
myassert($ceshiROKey);
// tests setting value on "default" or a named value via literal values
foreach ([null, 'ceshiValue'] as $valueName) {
$ceshiKey[$valueName] = 0xcafebabe;
myassertSame($ceshiROKey[$valueName], 0xcafebabe);
$ceshiKey[$valueName] = 'cafebabe';
myassertSame($ceshiROKey[$valueName], 'cafebabe');
$ceshiKey[$valueName] = ['cafe', 'babe'];
myassertSame($ceshiROKey[$valueName], ['cafe', 'babe']);
$ceshiKey[$valueName] = false;
myassertSame($ceshiROKey[$valueName], 0);
$ceshiKey[$valueName] = true;
myassertSame($ceshiROKey[$valueName], 1);
}
// tests setting value on "default" or a named value via RegistryValue object
foreach ([null, 'ceshiValue'] as $valueName) {
// bool as dword
$ceshiKey[$valueName] = new RegistryValue(null, false);
myassertSame($ceshiKey[$valueName], 0);
myassertSame($ceshiROKey[$valueName], 0);
$ceshiKey[$valueName] = new RegistryValue(RegistryValue::REG_DWORD, false);
myassertSame($ceshiKey[$valueName], 0);
myassertSame($ceshiROKey[$valueName], 0);
$ceshiKey[$valueName] = new RegistryValue(null, true);
myassertSame($ceshiKey[$valueName], 1);
myassertSame($ceshiROKey[$valueName], 1);
$ceshiKey[$valueName] = new RegistryValue(RegistryValue::REG_DWORD, true);
myassertSame($ceshiKey[$valueName], 1);
myassertSame($ceshiROKey[$valueName], 1);
// guess dword
$ceshiKey[$valueName] = new RegistryValue(null, 0xcafebabe);
myassertSame($ceshiKey[$valueName], 0xcafebabe);
myassertSame($ceshiROKey[$valueName], 0xcafebabe);
// manually set dword
$ceshiKey[$valueName] = new RegistryValue(RegistryValue::REG_DWORD, 0xcafebabe);
myassertSame($ceshiKey[$valueName], 0xcafebabe);
myassertSame($ceshiROKey[$valueName], 0xcafebabe);
// manually set 32bit num int as qword, read as int
$ceshiKey[$valueName] = new RegistryValue(RegistryValue::REG_QWORD, 0xcafebabe);
myassert($ceshiROKey[$valueName] instanceof RegistryValue);
myassertSame($ceshiROKey[$valueName]->type, RegistryValue::REG_QWORD);
myassertSame($ceshiROKey[$valueName]->toNumber(), 0xcafebabe);
// manually set 63bit num int as qword, read as int
$ceshiKey[$valueName] = new RegistryValue(RegistryValue::REG_QWORD, 0x6463626134333231);
myassert($ceshiROKey[$valueName] instanceof RegistryValue);
myassertSame($ceshiROKey[$valueName]->type, RegistryValue::REG_QWORD);
myassertSame($ceshiROKey[$valueName]->toNumber(), 0x6463626134333231);
// manually set 64bit num int as qword, read as int
$ceshiKey[$valueName] = new RegistryValue(RegistryValue::REG_QWORD, -0x6463626134333231);
myassert($ceshiROKey[$valueName] instanceof RegistryValue);
myassertSame($ceshiROKey[$valueName]->type, RegistryValue::REG_QWORD);
myassertSame($ceshiROKey[$valueName]->toNumber(), -0x6463626134333231);
// manually set 8byte qword, read as int
$ceshiKey[$valueName] = new RegistryValue(RegistryValue::REG_QWORD, "\xef\xbe\xad\xde\xbe\xba\xfe\xca");
myassert($ceshiROKey[$valueName] instanceof RegistryValue);
myassertSame($ceshiROKey[$valueName]->type, RegistryValue::REG_QWORD);
myassertSame($ceshiROKey[$valueName]->toNumber(), -0x3501454121524111);
// guess sz
$ceshiKey[$valueName] = new RegistryValue(null, 'cafebabe');
myassertSame($ceshiROKey[$valueName], 'cafebabe');
// manually set sz
$ceshiKey[$valueName] = new RegistryValue(RegistryValue::REG_SZ, 'cafebabe');
myassertSame($ceshiROKey[$valueName], 'cafebabe');
// manually set binary
$ceshiKey[$valueName] = new RegistryValue(RegistryValue::REG_SZ, "\0cafebabe\0\1\2");
myassertSame($ceshiROKey[$valueName], "\0cafebabe\0\1\2");
// manually set expandable
$ceshiKey[$valueName] = new RegistryValue(RegistryValue::REG_EXPAND_SZ, "cafebabe");
myassertSame($ceshiROKey[$valueName], "cafebabe");
// guess multi sz
$ceshiKey[$valueName] = new RegistryValue(null, ['cafe', 'babe']);
myassertSame($ceshiROKey[$valueName], ['cafe', 'babe']);
// manually set multi sz
$ceshiKey[$valueName] = new RegistryValue(RegistryValue::REG_MULTI_SZ, ['cafe', 'babe']);
myassertSame($ceshiROKey[$valueName], ['cafe', 'babe']);
}
$ceshiKey[null] = 'unsetted!!!';
$ceshiKey['ceshiValue'] = false;
$ceshiKey['ceshiDWORD'] = 0x1234;
$ceshiKey['ceshiSZ'] = 'hello php';
// emurate values by enumrateValues
$existValues = [
null => new RegistryValue(RegistryValue::REG_SZ, 'unsetted!!!'),
'ceshiValue' => new RegistryValue(RegistryValue::REG_DWORD, 0),
'ceshiDWORD' => new RegistryValue(RegistryValue::REG_DWORD, 0x1234),
'ceshiSZ' => new RegistryValue(RegistryValue::REG_SZ, 'hello php'),
];
foreach ($ceshiKey->enumrateValues(true) as $name => $value) {
$valueAssertion = $existValues[$name];
myassertSame($valueAssertion->type, $value->type);
myassertSame("$valueAssertion", "$value");
unset($existValues[$name]);
}
myassertSame($existValues, []);
// emurate values by iterator
$existValues = [
null => new RegistryValue(RegistryValue::REG_SZ, 'unsetted!!!'),
'ceshiValue' => new RegistryValue(RegistryValue::REG_DWORD, 0),
'ceshiDWORD' => new RegistryValue(RegistryValue::REG_DWORD, 0x1234),
'ceshiSZ' => new RegistryValue(RegistryValue::REG_SZ, 'hello php'),
];
foreach ($ceshiKey as $name => $value) {
$valueAssertion = $existValues[$name];
myassertSame("$valueAssertion", "$value");
unset($existValues[$name]);
}
myassertSame($existValues, []);
// TODO: remove value
//unset($ceshiKey['ceshiValue']);
//unset($ceshiKey['ceshiDWORD']);
//unset($ceshiKey['ceshiSZ']);
//myassertSame($ceshiROKey['ceshiValue'], null);
//myassertSame($ceshiROKey['ceshiDWORD'], null);
//myassertSame($ceshiROKey['ceshiSZ'], null);
// create new key
$subKey = $ceshiKey->createSubKey('subKey');
$subKey2 = $ceshiKey->createSubKey('subKey2');
// emurate keys
$existKeys = ['subKey', 'subKey2'];
foreach ($ceshiKey->enumrateSubKeys() as $keyName) {
$key = array_search($keyName, $existKeys);
myassertNotSame($key, false);
unset($existKeys[$key]);
}
myassertSame($existKeys, []);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment