Skip to content

Commit

Permalink
Fix GH-8750: Can not create VT_ERROR variant type
Browse files Browse the repository at this point in the history
We add support for creating `VT_ERROR` variants via `__construct()`,
and allow casting to int via `variant_cast()` and `variant_set_type()`.
We do not, however, allow type conversion by other means, to avoid
otherwise easily introduced type confusion.  VB(A) also only allows
explicit type conversion.

We also introduce `DISP_E_PARAMNOTFOUND` which might be the most
important `scode` for this purpose, since this allows to skip optional
parameters in method calls.

Closes GH-8886.
  • Loading branch information
cmb69 committed Jul 12, 2022
1 parent 6e24c16 commit 56804e3
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 5 deletions.
3 changes: 3 additions & 0 deletions NEWS
Expand Up @@ -10,6 +10,9 @@ PHP NEWS
json_encode(), serialize(), iconv_*(), mb_ereg*(), session_create_id(),
http_build_query(), strstr(), Reflection*::__toString(). (Arnaud)

- COM:
. Fixed bug GH-8750 (Can not create VT_ERROR variant type). (cmb)

- FPM:
. Added listen.setfib pool option to set route FIB on FreeBSD. (David Carlier)
. Added access.suppress_path pool option to filter access log entries.
Expand Down
1 change: 1 addition & 0 deletions UPGRADING
Expand Up @@ -264,6 +264,7 @@ PHP 8.2 UPGRADE NOTES
========================================

- COM_DOTNET:
. DISP_E_PARAMNOTFOUND
. LOCALE_NEUTRAL

- Curl:
Expand Down
2 changes: 2 additions & 0 deletions ext/com_dotnet/com_extension.c
Expand Up @@ -32,11 +32,13 @@
#define PHP_DISP_E_DIVBYZERO ((zend_long) (ULONG) DISP_E_DIVBYZERO)
#define PHP_DISP_E_OVERFLOW ((zend_long) (ULONG) DISP_E_OVERFLOW)
#define PHP_DISP_E_BADINDEX ((zend_long) (ULONG) DISP_E_BADINDEX)
#define PHP_DISP_E_PARAMNOTFOUND ((zend_long) (ULONG) DISP_E_PARAMNOTFOUND)
#define PHP_MK_E_UNAVAILABLE ((zend_long) (ULONG) MK_E_UNAVAILABLE)
#else
#define PHP_DISP_E_DIVBYZERO DISP_E_DIVBYZERO
#define PHP_DISP_E_OVERFLOW DISP_E_OVERFLOW
#define PHP_DISP_E_BADINDEX DISP_E_BADINDEX
#define PHP_DISP_E_PARAMNOTFOUND DISP_E_PARAMNOTFOUND
#define PHP_MK_E_UNAVAILABLE MK_E_UNAVAILABLE
#endif

Expand Down
5 changes: 5 additions & 0 deletions ext/com_dotnet/com_extension.stub.php
Expand Up @@ -264,6 +264,11 @@
* @cname PHP_DISP_E_BADINDEX
*/
const DISP_E_BADINDEX = UNKNOWN;
/**
* @var int
* @cname PHP_DISP_E_PARAMNOTFOUND
*/
const DISP_E_PARAMNOTFOUND = UNKNOWN;
/**
* @var int
* @cname PHP_MK_E_UNAVAILABLE
Expand Down
3 changes: 2 additions & 1 deletion ext/com_dotnet/com_extension_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 24 additions & 4 deletions ext/com_dotnet/com_variant.c
Expand Up @@ -92,7 +92,7 @@ static void safe_array_from_zval(VARIANT *v, zval *z, int codepage)
}
}

PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codepage)
static void php_com_variant_from_zval_ex(VARIANT *v, zval *z, int codepage, VARTYPE vt)
{
php_com_dotnet_object *obj;
zend_uchar ztype = IS_NULL;
Expand Down Expand Up @@ -145,6 +145,11 @@ PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codep
break;

case IS_LONG:
if (vt == VT_ERROR) {
V_VT(v) = VT_ERROR;
V_ERROR(v) = Z_LVAL_P(z);
break;
}
#if SIZEOF_ZEND_LONG == 4
V_VT(v) = VT_I4;
V_I4(v) = Z_LVAL_P(z);
Expand Down Expand Up @@ -172,6 +177,11 @@ PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codep
}
}

PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codepage)
{
php_com_variant_from_zval_ex(v, z, codepage, VT_EMPTY);
}

PHP_COM_DOTNET_API zend_result php_com_zval_from_variant(zval *z, VARIANT *v, int codepage)
{
OLECHAR *olestring = NULL;
Expand Down Expand Up @@ -448,7 +458,7 @@ PHP_METHOD(variant, __construct)
}

if (zvalue) {
php_com_variant_from_zval(&obj->v, zvalue, obj->code_page);
php_com_variant_from_zval_ex(&obj->v, zvalue, obj->code_page, vt);
}

/* Only perform conversion if variant not already of type passed */
Expand Down Expand Up @@ -1019,6 +1029,7 @@ PHP_FUNCTION(variant_set_type)
zval *zobj;
php_com_dotnet_object *obj;
/* VARTYPE == unsigned short */ zend_long vt;
VARIANT vtmp;
HRESULT res;

if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(),
Expand All @@ -1027,7 +1038,12 @@ PHP_FUNCTION(variant_set_type)
}
obj = CDNO_FETCH(zobj);

res = VariantChangeType(&obj->v, &obj->v, 0, (VARTYPE)vt);
if (V_VT(&obj->v) == VT_ERROR) {
VariantInit(&vtmp);
V_VT(&vtmp) = VT_I4;
V_I4(&vtmp) = V_ERROR(&obj->v);
}
res = VariantChangeType(&obj->v, V_VT(&obj->v) != VT_ERROR ? &obj->v : &vtmp, 0, (VARTYPE)vt);

if (SUCCEEDED(res)) {
if (vt != VT_DISPATCH && obj->typeinfo) {
Expand Down Expand Up @@ -1063,7 +1079,11 @@ PHP_FUNCTION(variant_cast)
obj = CDNO_FETCH(zobj);

VariantInit(&vres);
res = VariantChangeType(&vres, &obj->v, 0, (VARTYPE)vt);
if (V_VT(&obj->v) == VT_ERROR) {
V_VT(&vres) = VT_I4;
V_I4(&vres) = V_ERROR(&obj->v);
}
res = VariantChangeType(&vres, V_VT(&vres) == VT_EMPTY ? &obj->v : &vres, 0, (VARTYPE)vt);

if (SUCCEEDED(res)) {
php_com_wrap_variant(return_value, &vres, obj->code_page);
Expand Down
47 changes: 47 additions & 0 deletions ext/com_dotnet/tests/gh8750.phpt
@@ -0,0 +1,47 @@
--TEST--
Bug GH-8750 (Can not create VT_ERROR variant type)
--EXTENSIONS--
com_dotnet
--SKIPIF--
<?php
$provider = "Microsoft.ACE.OLEDB.12.0";
$filename = __DIR__ . "\\gh8750.mdb";
$catalog = new com("ADOX.Catalog");
try {
$catalog->Create("Provider=$provider;Data Source=$filename");
} catch (com_exception) {
die("skip $provider provider not available");
}
$catalog = null;
@unlink($filename);
?>
--FILE--
<?php
$filename = __DIR__ . "\\gh8750.mdb";

$catalog = new com("ADOX.Catalog");
$catalog->Create("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=$filename");
$catalog = null;

$db = new com("ADODB.Connection");
$db->ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=$filename";
$db->Mode = 1; // adModeRead
$db->Open();
// adSchemaProviderSpecific, *missing*, JET_SCHEMA_USERROSTER
$rs = $db->OpenSchema(-1, new variant(DISP_E_PARAMNOTFOUND, VT_ERROR), "{947bb102-5d43-11d1-bdbf-00c04fb92675}");
// manual counting since rs.RecordCount is -1 (not supported)
$i = 0;
while (!$rs->EOF) {
$rs->MoveNext();
$i++;
}
$rs->Close();
$db->Close();
var_dump($i);
?>
--EXPECT--
int(1)
--CLEAN--
<?php
unlink(__DIR__ . "/gh8750.mdb");
?>
36 changes: 36 additions & 0 deletions ext/com_dotnet/tests/gh8750a.phpt
@@ -0,0 +1,36 @@
--TEST--
Bug GH-8750 (Can not create VT_ERROR variant type) - cast and conversion
--EXTENSIONS--
com_dotnet
--FILE--
<?php
$error = new variant(DISP_E_PARAMNOTFOUND, VT_ERROR);

// explicit variant_cast() to int is supported if in range
echo variant_cast($error, VT_I4), PHP_EOL;

// however, explicit (int) casts are not supported
echo (int) $error, PHP_EOL;

// nor are implicit conversions
try {
echo 1 + $error, PHP_EOL;
} catch (TypeError $err) {
echo $err->getMessage(), PHP_EOL;
}

// we can retrieve the type
echo variant_get_type($error), PHP_EOL;

// and change it via variant_set_type()
variant_set_type($error, VT_I4);
echo variant_get_type($error), PHP_EOL;
?>
--EXPECTF--
-2147352572

Warning: Object of class variant could not be converted to int in %s on line %d
1
Unsupported operand types: int + variant
10
3

0 comments on commit 56804e3

Please sign in to comment.