Глава 99 · От первого объекта до production worker
COM и .NET в PHP на Windows: полный справочник Automation
Полная официальная ветка COM/DOTNET: установка, VARIANT/SAFEARRAY, события, type libraries, persistence, HRESULT и все 43 функции/метода с безопасными Office-free примерами.
COM/OLE Automation: когда PHP действительно нужен мост к Windows
Задача COM-расширения — не «запустить всё Windows из PHP», а вызвать установленный компонент, который отдаёт совместимый Automation-интерфейс. Практический выигрыш появляется у локального Windows-worker: прочитать старый XML/ADO-источник, вызвать оборудование или корпоративный компонент, заполнить документ, принять событие. Для обычного HTTP/API, CSV/XLSX без Office или переносимого приложения чаще проще нативная PHP-библиотека или отдельный сервис.
- PHP создаёт
new COM(ProgID)или получает уже активный объект - Windows registry находит CLSID и запускает/подключает DLL либо EXE под учётной записью процесса
- PHP упаковывает аргументы в VARIANT/BSTR/SAFEARRAY и вызывает IDispatch
- COM-сервер выполняет работу; результат/HRESULT возвращается через ту же границу
- PHP преобразует безопасные значения или оставляет объект
variant
| Если нужно | Разумный первый выбор | Почему |
|---|---|---|
| Старый локальный COM/OLE API на Windows | COM/DOTNET | Компонент уже предоставляет IDispatch/type library. |
| Прочитать/создать XLSX без Excel | PhpSpreadsheet/Open XML | Нет desktop-процесса, DCOM и интерактивных диалогов. |
| Вызвать современный .NET сервис | HTTP/gRPC/CLI worker | dotnet class не является универсальным .NET host. |
| Связать PHP с удалённым сервисом | HTTP API | Контракт переносим, наблюдаем и изолирован от desktop session. |
| Много тяжёлых Office-задач | Очередь + отдельный Windows worker | Web-request не ждёт UI-приложение и может повторить job. |
Установка и конфигурация: что должно совпасть до первой строки кода
Рабочая среда — это комплект: Windows + точная PHP-сборка + совместимый php_com_dotnet.dll + выбранный SAPI + зарегистрированный COM-сервер. Проверка только строки extension=... недостаточна.
PHP_OS_FAMILY === 'Windows'и нужная разрядность PHPextension_loaded('com_dotnet')в том же CLI, IIS FastCGI, Apache или отдельном Windows-worker- Нужный ProgID зарегистрирован для этой разрядности и пользователя
- Безопасный smoke-test создаёт объект и вызывает read-only/in-memory метод
- Production worker имеет явные timeout, очередь, журнал HRESULT и cleanup
Локально эта ветка проверена отдельной PHP 8.5.11 NTS x64-сборкой; общий php.ini и WordPress не менялись. Команды диагностики находятся в следующем разделе.
Требования: Windows, регистрация компонента и граница .NET Framework
| Слой | Минимум | Как проявляется отказ |
|---|---|---|
| PHP | Windows build и загруженный php_com_dotnet.dll | Class COM/DOTNET not found или extension_loaded=false. |
| COM component | ProgID/CLSID/type library зарегистрированы для подходящей bitness | Invalid class string, Class not registered, 0x80040154/0x800401F3. |
| Учётная запись | Право launch/access/file/network ресурса; service account веб-сервера проверяется отдельно от интерактивного пользователя | Access denied или метод виден, но действие не выполняется. |
| Desktop/session | Для UI-приложений доступна ожидаемая интерактивная среда | Диалог висит невидимо; automation timeout. |
| DOTNET | .NET Framework runtime и COM-visible/IDispatch тип | Тип создаётся, но метод даёт E_NOINTERFACE или assembly не грузится. |
Современная команда dotnet --info описывает .NET SDK/runtime, но класс PHP DOTNET использует Windows .NET Framework COM interop. Это разные границы. Нельзя считать любую DLL из проекта .NET 6/8 автоматически доступной.
Установка php_com_dotnet.dll без изменения системного PHP
Официальная Windows-сборка PHP содержит DLL расширения. В отдельном ini указывают её каталог и включают модуль. Для текущей разработки создана собственная runtime-папка и проверен SHA-256 архива с официальной страницы Downloads.
<?php
declare(strict_types=1);
$items = new COM('Scripting.Dictionary', null, CP_UTF8);
$items->CompareMode = 1; // TextCompare: ключи без учёта регистра
$items->Add('SKU-104', 2);
$items->Item['sku-104'] = 3;
echo "positions: {$items->Count}\n";
echo "SKU-104: {$items->Item['SKU-104']}\n";
Ожидаемый результат
positions: 1
SKU-104: 3
ProgID создаёт локальный Windows-компонент; параметр CP_UTF8 явно задаёт границу строк. Видимый результат — одна позиция корзины и обновлённое количество.
| Команда/строка | Что доказывает |
|---|---|
php --ini | Какой ini читает именно этот процесс. |
php -m | Есть ли com_dotnet среди загруженных modules. |
php -r "var_dump(extension_loaded('com_dotnet'));" | Минимальная проверка текущего SAPI. |
extension=php_com_dotnet.dll | Подключение DLL; путь/версия/архитектура обязаны совпасть с PHP. |
Все директивы COM: безопасность, codepage, typelib и .NET runtime
| Директива | Локальное проверенное значение | Где меняется и зачем |
|---|---|---|
com.allow_dcom | 0 | INI_SYSTEM. Разрешает удалённый DCOM client. По умолчанию и в тестах выключено. |
com.autoregister_typelib | 0 | INI_ALL. Автоматически регистрировать constants из typelib объекта, если это возможно. |
com.autoregister_verbose | 0 | INI_ALL. Сообщать ошибки автообнаружения typelib через PHP error mechanism. |
com.autoregister_casesensitive | 1 | INI_ALL. Историческая политика регистра; case-insensitive constants больше не поддерживаются современным PHP. |
com.code_page | пусто → CP_ACP | INI_ALL. Глобальная default codepage; переносимее передать codepage в constructor. |
com.dotnet_version | v4.0.30319 | INI_SYSTEM, доступна с PHP 8. Первые три части .NET Framework version с префиксом v; именно она включает поддержку .NET Framework 4+ в dotnet. |
com.typelib_file | пусто | INI_SYSTEM. Файл со списком typelib, по одной на строку, загружаемых при старте процесса как com_load_typelib; исторические суффиксы #cis/#case_insensitive просили нечувствительные к регистру constants. |
До PHP 8 класс dotnet не поддерживал .NET Framework 4+ напрямую; assembly, зарегистрированную regasm.exe, можно было создавать через обычный COM. В PHP 8+ dotnet использует com.dotnet_version. Параметр constructor локальнее глобальной codepage: new COM('Scripting.Dictionary', null, CP_UTF8) сразу показывает границу строк. Для web-worker не включайте DCOM «чтобы исчезла ошибка».
Предопределённые константы: полный набор и как читать составные типы
Константа — не магическое число. CLSCTX выбирает место выполнения, VT_* описывает упаковку значения, CP_* — преобразование текста, VARCMP/NORM — сравнение, HRESULT — причину отказа. Значения ниже сняты с PHP 8.5.11 x64; условная NORM_IGNOREKASHIDA оставлена как официальный узел и честно помечена отсутствующей.
Где живёт COM-сервер: CLSCTX_*
| Константа | Проверенное значение | Как читать |
|---|---|---|
CLSCTX_INPROC_SERVER | 1 | Код сервера — DLL внутри процесса PHP. |
CLSCTX_INPROC_HANDLER | 2 | In-process handler для клиентской стороны COM. |
CLSCTX_LOCAL_SERVER | 4 | COM-сервер — отдельный EXE на том же компьютере. |
CLSCTX_REMOTE_SERVER | 16 | Сервер на другом компьютере через DCOM; в локальной среде запрещён. |
CLSCTX_SERVER | 21 | Маска INPROC_SERVER | LOCAL_SERVER | REMOTE_SERVER. |
CLSCTX_ALL | 23 | Все class contexts, включая INPROC_HANDLER. |
Тип данных Automation: VT_*
| Константа | Проверенное значение | Как читать |
|---|---|---|
VT_NULL | 1 | Automation NULL (не то же самое, что VT_EMPTY). |
VT_EMPTY | 0 | Значения нет; размер полезных данных нулевой. |
VT_UI1 | 17 | Беззнаковое 8-битное целое/байт. |
VT_I1 | 16 | Знаковое 8-битное целое. |
VT_UI2 | 18 | Беззнаковое 16-битное целое. |
VT_I2 | 2 | Знаковое 16-битное целое. |
VT_UI4 | 19 | Беззнаковое 32-битное целое. |
VT_I4 | 3 | Знаковое 32-битное целое. |
VT_R4 | 4 | 32-битное IEEE floating point. |
VT_R8 | 5 | 64-битное IEEE floating point. |
VT_BOOL | 11 | VARIANT_BOOL; true обычно хранится как -1, false как 0. |
VT_ERROR | 10 | HRESULT/SCODE как значение VARIANT. |
VT_CY | 6 | Currency: 64-битное целое, масштабированное на 10 000. |
VT_DATE | 7 | Число дней от эпохи OLE Automation; это не Unix timestamp. |
VT_BSTR | 8 | Unicode BSTR с длиной, управляемой Automation. |
VT_DECIMAL | 14 | Automation DECIMAL. |
VT_UNKNOWN | 13 | Указатель IUnknown. |
VT_DISPATCH | 9 | Указатель IDispatch, через который PHP вызывает динамический API. |
VT_VARIANT | 12 | Вложенный VARIANT; как флаг by-reference используется вместе с VT_BYREF. |
VT_INT | 22 | Platform int Automation, в официальном описании эквивалент VT_I4. |
VT_UINT | 23 | Platform unsigned int Automation, эквивалент VT_UI4. |
VT_ARRAY | 8192 | Битовый флаг SAFEARRAY; объединяется OR с типом элементов. |
VT_BYREF | 16384 | Битовый флаг ссылки на данные; объединяется OR с базовым VARTYPE. |
VT_UI8 | 21 | Беззнаковое 64-битное целое; доступность зависит от x64. |
VT_I8 | 20 | Знаковое 64-битное целое; доступность зависит от x64. |
Кодовые страницы: CP_*
| Константа | Проверенное значение | Как читать |
|---|---|---|
CP_ACP | 0 | Системная ANSI code page. |
CP_MACCP | 2 | Macintosh code page. |
CP_OEMCP | 1 | Системная OEM code page. |
CP_UTF7 | 65000 | UTF-7; почти никогда не нужен для современного PHP-приложения. |
CP_UTF8 | 65001 | UTF-8 — явный выбор для PHP-строк в UTF-8. |
CP_SYMBOL | 42 | Symbol translations. |
CP_THREAD_ACP | 3 | ANSI code page текущего thread. |
Сравнение и locale
| Константа | Проверенное значение | Как читать |
|---|---|---|
VARCMP_LT | 0 | left меньше right. |
VARCMP_EQ | 1 | left равно right. |
VARCMP_GT | 2 | left больше right. |
VARCMP_NULL | 3 | Хотя бы один операнд NULL. |
LOCALE_SYSTEM_DEFAULT | 2048 | Locale операционной системы. |
LOCALE_NEUTRAL | 0 | Neutral locale; официальный manual советует обычно использовать system default. |
NORM_IGNORECASE | 1 | Игнорировать регистр при variant_cmp. |
NORM_IGNORENONSPACE | 2 | Игнорировать nonspacing characters. |
NORM_IGNORESYMBOLS | 4 | Игнорировать символы. |
NORM_IGNOREWIDTH | 131072 | Игнорировать различия ширины символов. |
NORM_IGNOREKANATYPE | 65536 | Игнорировать различия Kana type. |
NORM_IGNOREKASHIDA | не определена в PHP 8.5.11 x64 | Игнорировать Arabic kashida; зависит от Windows library и отсутствует в проверенной сборке. |
HRESULT для ветвления
| Константа | Проверенное значение | Как читать |
|---|---|---|
DISP_E_DIVBYZERO | 2147614738 | HRESULT деления на ноль. |
DISP_E_OVERFLOW | 2147614730 | HRESULT невозможного представления/overflow. |
DISP_E_BADINDEX | 2147614731 | HRESULT отсутствующего индекса массива. |
DISP_E_PARAMNOTFOUND | 2147614724 | HRESULT неизвестного parameter ID. |
MK_E_UNAVAILABLE | 2147746275 | HRESULT: объект отсутствует в Running Object Table. |
8204 & VT_ARRAYне равно нулю → это SAFEARRAY8204 & ~VT_ARRAYдаёт 12 → элементы помечены VT_VARIANT- Равенство
$type === VT_VARIANTбыло бы ложным и потеряло бы флаг массива
<?php
declare(strict_types=1);
$values = new variant([1, 2, 3], VT_ARRAY | VT_VARIANT);
$type = variant_get_type($values);
echo (($type & VT_ARRAY) !== 0) ? "array\n" : "scalar\n";
echo 'element marker: ', $type & ~VT_ARRAY, "\n";
Ожидаемый результат
array
element marker: 12
VT_ARRAY и VT_BYREF — флаги. Равенство одному VT_* не работает для составного типа; применяют битовую маску.
com_exception и HRESULT: ветвиться по коду, журналировать контекст
Потенциально фатальная ошибка COM превращается в com_exception. Унаследованное getCode() содержит HRESULT. Его удобно печатать как беззнаковый hex: $error->getCode() & 0xFFFFFFFF. Локализованный message полезен оператору, но хрупок для программного сравнения.
- try охватывает минимальный внешний вызов
- catch com_exception получает HRESULT и внутреннее сообщение
- Приложение переводит известный код в свою политику: retry, fallback, invalid input или unavailable
- Журнал сохраняет operation/ProgID/HRESULT/duration; HTTP-пользователь получает безопасное доменное сообщение
- finally закрывает документ/соединение и отпускает ссылку на внешний процесс
<?php
declare(strict_types=1);
try {
com_get_active_object('Scripting.Dictionary');
echo "active\n";
} catch (com_exception $error) {
printf("not active: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
not active: 0x800401E3
Scripting.Dictionary не регистрируется в Running Object Table. Получаем штатный HRESULT MK_E_UNAVAILABLE, а не создаём новый объект.
<?php
declare(strict_types=1);
try {
variant_div(10, 0);
echo "unexpected\n";
} catch (com_exception $error) {
printf("division failed: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
division failed: 0x80020012
Automation сообщает DISP_E_DIVBYZERO через com_exception; знаменатель внешних данных проверяют до вызова.
Практическая карта: от локального объекта до событий и persistence
| Задача | Начать с | Следующий слой |
|---|---|---|
| Хранилище key/value из Windows Automation | com::__construct | Scripting.Dictionary, array-style Item, SAFEARRAY Keys. |
| XML через установленный MSXML | com::__construct | COMPersistHelper::SaveToStream для bridge к PHP stream. |
| COM-visible коллекция .NET Framework | dotnet::__construct | Проверить IDispatch каждого method/property. |
| Точный VARTYPE для аргумента | variant::__construct | variant_get_type и variant_cast. |
| События Windows-компонента | com_event_sink | com_message_pump и bounded loop. |
| Понять неизвестное динамическое API | com_print_typeinfo | com_load_typelib + vendor docs. |
| Сохранить/загрузить через PHP stream | COMPersistHelper::SaveToStream | COMPersistHelper::LoadFromStream. |
<?php
declare(strict_types=1);
$xml = new COM('MSXML2.DOMDocument.6.0', null, CP_UTF8);
$xml->async = false;
$loaded = $xml->loadXML('<order id="104"><total>1490</total></order>');
echo 'loaded: ', $loaded ? 'yes' : 'no', "\n";
echo 'order: ', $xml->documentElement->getAttribute('id'), "\n";
echo 'total: ', $xml->selectSingleNode('/order/total')->text, "\n";
Ожидаемый результат
loaded: yes
order: 104
total: 1490
Второй COM-объект решает другую задачу: разбирает XML и возвращает свойства/методы через IDispatch. Данные встроены, поэтому пример не обращается к сети.
<?php
declare(strict_types=1);
$queue = new DOTNET('mscorlib', 'System.Collections.ArrayList', CP_UTF8);
$queue->Add('order-104');
$queue->Add('order-105');
echo "count: {$queue->Count}\n";
echo "first: {$queue->Item(0)}\n";
Ожидаемый результат
count: 2
first: order-104
DOTNET загружает класс из сборки .NET Framework. ArrayList видим через COM и позволяет показать реальный вызов методов без пользовательской DLL.
Рабочий сервис не размазывает COM по контроллерам. Один adapter владеет ProgID/codepage/lifecycle, переводит HRESULT в доменные ошибки и возвращает PHP DTO. Так его можно заменить fixture в тесте и вынести в отдельный Windows worker.
For Each: как PHP foreach проходит IEnumVARIANT/SAFEARRAY
В VB/ASP конструкция For Each соответствует PHP foreach, если COM-объект отдаёт стандартный IEnumVARIANT. SAFEARRAY хранит границы, поэтому также итерируется. Но ключ PHP не обязан быть бизнес-ключом COM-коллекции: в Scripting.Dictionary foreach отдаёт последовательность keys.
- COM-коллекция/SAFEARRAY сообщает следующий VARIANT
- Расширение преобразует безопасный scalar или оставляет variant/com
- PHP присваивает значение переменной foreach
- Цикл выполняет тело и просит следующий элемент до конца enumerator
<?php
declare(strict_types=1);
$codes = new variant(['A-104', 'B-205', 'C-306'], VT_ARRAY | VT_VARIANT);
echo 'type: ', variant_get_type($codes), "\n";
foreach ($codes as $code) {
echo $code, "\n";
}
Ожидаемый результат
type: 8204
A-104
B-205
C-306
Флаг VT_ARRAY объединяется с типом элементов. SAFEARRAY знает границы, поэтому доступен foreach; обычный PHP array и COM SAFEARRAY остаются разными представлениями.
SAFEARRAY и array-style properties: скобки не превращают COM в PHP array
У COM есть два похожих внешне механизма. SAFEARRAY — настоящий Automation-массив с размерностями/границами. Indexed property — property getter/setter, которому PHP передаёт индекс через квадратные скобки. У Scripting.Dictionary выражение $dict->Item['SKU'] вызывает параметризованное свойство Item, а $dict->Keys() возвращает SAFEARRAY.
| Запись | Что происходит | Ловушка |
|---|---|---|
$dict->Item['SKU'] | Вызов indexed property | Это не PHP array offset и не проверяется Reflection. |
$dict->Keys() | COM method возвращает SAFEARRAY | Элементы/нижняя граница определены сервером. |
new variant($phpArray, VT_ARRAY|VT_VARIANT) | Создание Automation SAFEARRAY | Не всякая требуемая SAFEARRAY(BSTR)** сигнатура совпадёт с этим layout/byref. |
foreach ($safeArray as $value) | Итерация по границам SAFEARRAY | Многомерный array может потребовать proxy/несколько индексов. |
<?php
declare(strict_types=1);
$items = new COM('Scripting.Dictionary', null, CP_UTF8);
$items->CompareMode = 1; // TextCompare: ключи без учёта регистра
$items->Add('SKU-104', 2);
$items->Item['sku-104'] = 3;
echo "positions: {$items->Count}\n";
echo "SKU-104: {$items->Item['SKU-104']}\n";
Ожидаемый результат
positions: 1
SKU-104: 3
ProgID создаёт локальный Windows-компонент; параметр CP_UTF8 явно задаёт границу строк. Видимый результат — одна позиция корзины и обновлённое количество.
<?php
declare(strict_types=1);
$values = new variant([1, 2, 3], VT_ARRAY | VT_VARIANT);
$type = variant_get_type($values);
echo (($type & VT_ARRAY) !== 0) ? "array\n" : "scalar\n";
echo 'element marker: ', $type & ~VT_ARRAY, "\n";
Ожидаемый результат
array
element marker: 12
VT_ARRAY и VT_BYREF — флаги. Равенство одному VT_* не работает для составного типа; применяют битовую маску.
Класс com: динамический объект, методы которого PHP заранее не видит
Класс com наследует variant, но содержит в Reflection только constructor. Остальные обращения перегружены: имя property/method и аргументы уходят в IDispatch выбранного объекта. Поэтому IDE не может вывести полный API только из класса PHP; нужны type library, vendor docs, com_print_typeinfo или локальный stub.
| Часть constructor | Смысл |
|---|---|
| module_name | ProgID/CLSID/moniker. ProgID читабелен, CLSID точнее фиксирует класс. |
| server_name | null/local; удалённое имя требует com.allow_dcom и инфраструктуры DCOM. В array-форме default Flags: CLSCTX_SERVER без Server, CLSCTX_REMOTE_SERVER с Server. |
| codepage | Граница PHP string ↔ Unicode BSTR; предпочтителен явный CP_UTF8 при UTF-8-коде. |
| typelib | Явная type library, если автоматического обнаружения недостаточно. |
<?php
declare(strict_types=1);
$items = new COM('Scripting.Dictionary', null, CP_UTF8);
$items->CompareMode = 1; // TextCompare: ключи без учёта регистра
$items->Add('SKU-104', 2);
$items->Item['sku-104'] = 3;
echo "positions: {$items->Count}\n";
echo "SKU-104: {$items->Item['SKU-104']}\n";
Ожидаемый результат
positions: 1
SKU-104: 3
ProgID создаёт локальный Windows-компонент; параметр CP_UTF8 явно задаёт границу строк. Видимый результат — одна позиция корзины и обновлённое количество.
Класс dotnet: .NET Framework через COM interop, а не универсальный вызов любой DLL
dotnet тоже наследует variant. Constructor принимает assembly name, полное datatype name и codepage. До PHP 8 он не поддерживал .NET Framework 4+ напрямую; regasm.exe позволял создать зарегистрированный класс через COM. В PHP 8+ runtime для dotnet выбирает INI_SYSTEM-директива com.dotnet_version. Static/generic API и тип без IDispatch остаются ограничениями.
<?php
declare(strict_types=1);
$queue = new DOTNET('mscorlib', 'System.Collections.ArrayList', CP_UTF8);
$queue->Add('order-104');
$queue->Add('order-105');
echo "count: {$queue->Count}\n";
echo "first: {$queue->Item(0)}\n";
Ожидаемый результат
count: 2
first: order-104
DOTNET загружает класс из сборки .NET Framework. ArrayList видим через COM и позволяет показать реальный вызов методов без пользовательской DLL.
<?php
declare(strict_types=1);
$builder = new DOTNET('mscorlib', 'System.Text.StringBuilder');
try {
$builder->Append('order-104');
echo "callable\n";
} catch (com_exception $error) {
printf("not dispatchable: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
not dispatchable: 0x80004002
Официальное ограничение проявляется явно: объект может создаться, но класс без подходящего IDispatch не обязан отдавать методы PHP. Это не ошибка синтаксиса PHP.
| Ситуация | Выбор |
|---|---|
| Небольшой проверенный COM-visible type из .NET Framework | dotnet допустим с fixture и точной runtime policy. |
| Современная .NET 6/8 business library | Отдельный dotnet service/CLI с JSON/gRPC либо COM-visible adapter, написанный специально. |
| Static/generic API | Обёртка instance facade на стороне .NET или другой IPC; прямой DOTNET не обещает доступ. |
Класс variant: контейнер «значение + VARTYPE», а не обычная PHP-переменная
PHP zval уже хранит свой тип, но COM требует Automation VARTYPE с отдельными правилами чисел, дат, BSTR, NULL/EMPTY, массивов и ссылок. Обычно расширение преобразует scalar автоматически. Явный variant нужен, когда сервер различает VT_I4/VT_BSTR/VT_DATE, ожидает SAFEARRAY или возвращает значение без безопасного прямого PHP-соответствия.
- PHP scalar/array получает VARTYPE через явный constructor или автоматическое правило
- IDispatch получает VARIANT/SAFEARRAY/BSTR
- COM method возвращает VARIANT
- Без потери PHP получает scalar; иначе остаётся объект variant
- Код проверяет variant_get_type и делает осознанное cast/set_type
<?php
declare(strict_types=1);
$quantity = new variant(12, VT_I4);
echo 'type: ', variant_get_type($quantity), "\n";
echo 'value: ', (int) $quantity, "\n";
Ожидаемый результат
type: 3
value: 12
VARIANT хранит не только значение 12, но и договор VT_I4. Это нужно, когда сервер различает целые разных размеров.
<?php
declare(strict_types=1);
$codes = new variant(['A-104', 'B-205', 'C-306'], VT_ARRAY | VT_VARIANT);
echo 'type: ', variant_get_type($codes), "\n";
foreach ($codes as $code) {
echo $code, "\n";
}
Ожидаемый результат
type: 8204
A-104
B-205
C-306
Флаг VT_ARRAY объединяется с типом элементов. SAFEARRAY знает границы, поэтому доступен foreach; обычный PHP array и COM SAFEARRAY остаются разными представлениями.
COMPersistHelper: мост IPersist и PHP streams с проверкой реального interface
Helper не добавляет persistence любому COM-объекту. Он вызывает интерфейсы, которые уже реализует компонент: IPersistFile для file, IPersistStream для stream, IPersistStreamInit для нового/stream состояния. Constructor принимает IDispatch object, поэтому отсутствие нужного IPersist проявляется только на конкретном method.
| Interface | Методы helper | Практическая граница |
|---|---|---|
| IPersistFile | GetCurFileName, LoadFromFile, SaveToFile | COM-компонент сам открывает путь; path/open_basedir false отделяйте от COM exception, а null-успех PHP 8.5 подтверждайте состоянием. |
| IPersistStream | GetMaxStreamSize, LoadFromStream, SaveToStream | PHP владеет stream; bound LoadFromStream возвращает true, unbound — загруженный dispatch object. |
| IPersistStreamInit | InitNew и stream methods | Позволяет инициализировать новое состояние, если реализация это поддерживает. |
<?php
declare(strict_types=1);
$source = new COM('MSXML2.DOMDocument.6.0');
$source->async = false;
$source->loadXML('<order id="104"><total>1490</total></order>');
$stream = fopen('php://temp', 'w+b');
(new COMPersistHelper($source))->SaveToStream($stream);
rewind($stream);
$copy = new COM('MSXML2.DOMDocument.6.0');
$copy->async = false;
$loaded = (new COMPersistHelper($copy))->LoadFromStream($stream);
echo 'bound return: ', get_debug_type($loaded), ' ', $loaded === true ? 'true' : 'other', "\n";
echo 'total: ', $copy->selectSingleNode('/order/total')->text, "\n";
Ожидаемый результат
bound return: bool true
total: 1490
Путь данных виден полностью: COM-объект → PHP stream → уже связанный COM-объект. Для bound helper реализация возвращает true; временный stream не пишет на диск.
<?php
declare(strict_types=1);
$path = __DIR__ . '/persist-demo.htm';
try {
$source = new COM('htmlfile');
$source->write('<html><body><p>saved value</p></body></html>');
$source->close();
(new COMPersistHelper($source))->SaveToFile($path, false);
$copy = new COM('htmlfile');
$result = (new COMPersistHelper($copy))->LoadFromFile($path, 0);
echo 'return: ', get_debug_type($result), "\n";
echo 'loaded file: ', basename(str_replace('\\', '/', (string) $copy->URL)), "\n";
} finally {
if (is_file($path)) {
unlink($path);
}
}
Ожидаемый результат
return: null
loaded file: persist-demo.htm
Успех подтверждается отсутствием исключения и наблюдаемой файловой привязкой объекта. В PHP 8.5 успешный вызов вернул null, хотя официальный stub обещает bool: проверять его truthiness нельзя.
<?php
declare(strict_types=1);
$xml = new COM('MSXML2.DOMDocument.6.0');
$helper = new COMPersistHelper($xml);
try {
echo $helper->GetMaxStreamSize(), "\n";
} catch (com_exception $error) {
printf("size unavailable: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
size unavailable: 0x80004001
Наличие IPersistStream ещё не гарантирует, что компонент реализовал GetSizeMax. Практический резерв — записать в php://temp и измерить ftell/strlen.
Полный набор методов разобран в справочнике: COMPersistHelper::__construct, COMPersistHelper::GetCurFileName, COMPersistHelper::GetMaxStreamSize, COMPersistHelper::InitNew, COMPersistHelper::LoadFromFile, COMPersistHelper::LoadFromStream, COMPersistHelper::SaveToFile, COMPersistHelper::SaveToStream.
com_exception: где находится HRESULT и как не зависеть от текста Windows
com_exception — final subclass стандартного Exception. Отдельных методов у класса нет: используются getCode(), getMessage(), getPrevious(), stack trace. Code соответствует HRESULT и является более устойчивой основой для policy, чем локализованный message.
<?php
declare(strict_types=1);
try {
com_get_active_object('No.Such.AutomationServer', CP_UTF8);
echo "active\n";
} catch (com_exception $error) {
printf("invalid ProgID: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
invalid ProgID: 0x800401F3
Неизвестный ProgID даёт другой HRESULT. В журнале полезно хранить код и собственный контекст, не показывая внутренние пути пользователю.
| Код | Смысл в примерах | Действие приложения |
|---|---|---|
| 0x800401E3 / MK_E_UNAVAILABLE | Объект не активен в Running Object Table | Предложить запустить worker/object либо выбрать new COM по политике. |
| 0x800401F3 | Неверный ProgID | Ошибка конфигурации/установки, не retry. |
| 0x80004002 / E_NOINTERFACE | Нет нужного IDispatch/IPersist interface | Выбрать другой API/adapter. |
| 0x80020005 | Automation type mismatch | Проверить VARTYPE, вход и locale; не повторять без изменения. |
| 0x80020012 / DISP_E_DIVBYZERO | Деление на ноль | Проверить входные данные. |
com_safearray_proxy: внутренний proxy для сложного доступа к SAFEARRAY
Класс final, не имеет публичных методов/constructor в Reflection и не предназначен для ручного создания. Расширение может вернуть proxy, когда требуется продолжить индексирование многомерного SAFEARRAY или параметризованного array-style property. Работают квадратные скобки и фактические границы COM-массива, а не API обычного PHP array.
- COM property возвращает SAFEARRAY с несколькими измерениями или требует несколько индексов
- Первый индекс ещё не выбирает scalar
- Расширение сохраняет COM array + уже выбранные индексы в com_safearray_proxy
- Следующий индекс завершает доступ; полученное значение снова проходит VARIANT conversion
<?php
declare(strict_types=1);
$codes = new variant(['A-104', 'B-205', 'C-306'], VT_ARRAY | VT_VARIANT);
echo 'type: ', variant_get_type($codes), "\n";
foreach ($codes as $code) {
echo $code, "\n";
}
Ожидаемый результат
type: 8204
A-104
B-205
C-306
Флаг VT_ARRAY объединяется с типом элементов. SAFEARRAY знает границы, поэтому доступен foreach; обычный PHP array и COM SAFEARRAY остаются разными представлениями.
Полный справочник COM/DOTNET по категориям
Дерево ниже содержит все 43 refentry официальной ветки, а не только функции из учебных примеров. В каждой карточке есть точная сигнатура, параметры, return/failure, два разных сценария и связанные инструменты.
Создание и исследование COM
События
.NET Framework
VARIANT: создание, тип и преобразование
VARIANT: числа
variant_absvariant_addvariant_subvariant_mulvariant_divvariant_idivvariant_modvariant_powvariant_negvariant_fixvariant_intvariant_round
VARIANT: строки, сравнение и даты
VARIANT: логика и биты
Persistence
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
com::__constructСоздаёт или связывает PHP с OLE Automation-объектом по ProgID/CLSID/moniker.
public function com::__construct(string $module_name, array|string|null $server_name = null, int $codepage = CP_ACP, string $typelib = "")Создаёт или связывает PHP с OLE Automation-объектом по ProgID/CLSID/moniker.
Первый аргумент может быть ProgID (Scripting.Dictionary), CLSID либо moniker. Moniker похож на URL-схему: он одновременно называет ресурс и способ его загрузки (официальные примеры — путь к документу и LDAP:). Windows ищет регистрацию класса и создаёт DLL/EXE-сервер или связывает ресурс. Это означает реальный внешний lifecycle: constructor может запустить процесс, открыть документ или вернуть ошибку ещё до первого method call.
Форма $server_name | Что означает |
|---|---|
null или строка | Стандартный server либо DCOM host. Для ненулевого удалённого имени требуется com.allow_dcom=1. |
array | Case-sensitive ключи Server, Username, Password, Domain, Flags. Username и Password указывают только вместе; credentials не помещают в код/лог. Официальный договор отдельно указывает: эта форма создания не управляется com.allow_dcom, даже если Server опущен. |
| Flags | OR-маска CLSCTX_INPROC_SERVER, INPROC_HANDLER, LOCAL_SERVER, REMOTE_SERVER, SERVER или ALL. Если Server опущен, default — CLSCTX_SERVER; если Server задан, default — CLSCTX_REMOTE_SERVER. |
$codepage управляет преобразованием PHP string ↔ Unicode BSTR и наследуется объектами/variants, возвращёнными этим COM-объектом. Для UTF-8-кода лучше передать CP_UTF8 явно и проверить кириллицу. $typelib позволяет связать type library при создании; пустая строка оставляет обычное обнаружение.
| Параметр | Что передаём и зачем |
|---|---|
$module_name | ProgID, CLSID или moniker установленного доверенного COM-компонента. Не принимайте значение напрямую от HTTP-клиента. |
$server_name | null/пустое значение для локального стандартного сервера; имя удалённого сервера относится к DCOM и требует отдельной политики/разрешения. По умолчанию: null. |
$codepage | Кодовая страница преобразования PHP string ↔ BSTR. Для UTF-8-исходника обычно передают CP_UTF8 явно. По умолчанию: CP_ACP. |
$typelib | Имя/GUID/path type library, которую следует связать с создаваемым объектом; пустая строка оставляет автоматическое обнаружение. По умолчанию: "". |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | public function com::__construct(string $module_name, array|string|null $server_name = null, int $codepage = CP_ACP, string $typelib = "") |
| Результат | Новый объект класса com (он наследуется от variant); доступные свойства/методы определяет сам Automation-сервер, а не Reflection PHP. |
| Ошибки и особые исходы | Отсутствующий ProgID/interface/permission или ошибка компонента дают com_exception с HRESULT; неправильные PHP-типы дают TypeError/ValueError. |
| Версии | (PHP 4 > 4.1.0, PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$items = new COM('Scripting.Dictionary', null, CP_UTF8);
$items->CompareMode = 1; // TextCompare: ключи без учёта регистра
$items->Add('SKU-104', 2);
$items->Item['sku-104'] = 3;
echo "positions: {$items->Count}\n";
echo "SKU-104: {$items->Item['SKU-104']}\n";
Ожидаемый результат
positions: 1
SKU-104: 3
ProgID создаёт локальный Windows-компонент; параметр CP_UTF8 явно задаёт границу строк. Видимый результат — одна позиция корзины и обновлённое количество.
<?php
declare(strict_types=1);
$xml = new COM('MSXML2.DOMDocument.6.0', null, CP_UTF8);
$xml->async = false;
$loaded = $xml->loadXML('<order id="104"><total>1490</total></order>');
echo 'loaded: ', $loaded ? 'yes' : 'no', "\n";
echo 'order: ', $xml->documentElement->getAttribute('id'), "\n";
echo 'total: ', $xml->selectSingleNode('/order/total')->text, "\n";
Ожидаемый результат
loaded: yes
order: 104
total: 1490
Второй COM-объект решает другую задачу: разбирает XML и возвращает свойства/методы через IDispatch. Данные встроены, поэтому пример не обращается к сети.
Рядом: com_print_typeinfo, com_load_typelib, com_get_active_object.
Вернуться к карте задач или полному дереву COM.
dotnet::__constructСоздаёт COM-visible класс из .NET Framework assembly через interop-слой Windows.
public function dotnet::__construct(string $assembly_name, string $datatype_name, int $codepage = CP_ACP)Создаёт COM-visible класс из .NET Framework assembly через interop-слой Windows.
Это Windows COM interop для совместимых типов .NET Framework: assembly загружается по $assembly_name, затем создаётся класс с полным $datatype_name. Возможность создать объект и возможность вызвать конкретный method — разные проверки: generic/static API, перегрузки или тип без подходящего IDispatch могут дать E_NOINTERFACE.
До PHP 8 класс dotnet не поддерживал .NET Framework 4 и новее; зарегистрированную через regasm.exe assembly можно было создавать как обычный COM-объект. Начиная с PHP 8 версию CLR для dotnet выбирает INI_SYSTEM-директива com.dotnet_version. Для нового .NET 6/8 приложения обычно делают отдельный HTTP/CLI worker.
| Параметр | Что передаём и зачем |
|---|---|
$assembly_name | Имя .NET Framework assembly, например mscorlib, либо полное strong name согласно среде. |
$datatype_name | Полное имя COM-visible .NET-типа; generic/static классы и типы без IDispatch имеют ограничения. |
$codepage | Кодовая страница преобразования PHP string ↔ BSTR. Для UTF-8-исходника обычно передают CP_UTF8 явно. По умолчанию: CP_ACP. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | public function dotnet::__construct(string $assembly_name, string $datatype_name, int $codepage = CP_ACP) |
| Результат | Новый dotnet-объект. Создание ещё не доказывает, что тип отдаёт вызываемые методы через IDispatch. |
| Ошибки и особые исходы | Отсутствующий ProgID/interface/permission или ошибка компонента дают com_exception с HRESULT; неправильные PHP-типы дают TypeError/ValueError. |
| Версии | (PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$queue = new DOTNET('mscorlib', 'System.Collections.ArrayList', CP_UTF8);
$queue->Add('order-104');
$queue->Add('order-105');
echo "count: {$queue->Count}\n";
echo "first: {$queue->Item(0)}\n";
Ожидаемый результат
count: 2
first: order-104
DOTNET загружает класс из сборки .NET Framework. ArrayList видим через COM и позволяет показать реальный вызов методов без пользовательской DLL.
<?php
declare(strict_types=1);
$builder = new DOTNET('mscorlib', 'System.Text.StringBuilder');
try {
$builder->Append('order-104');
echo "callable\n";
} catch (com_exception $error) {
printf("not dispatchable: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
not dispatchable: 0x80004002
Официальное ограничение проявляется явно: объект может создаться, но класс без подходящего IDispatch не обязан отдавать методы PHP. Это не ошибка синтаксиса PHP.
Рядом: com::__construct, variant_get_type.
Вернуться к карте задач или полному дереву COM.
variant::__constructСоздаёт VARIANT с явным значением, VARTYPE и кодовой страницей.
public function variant::__construct(mixed $value = null, int $type = VT_EMPTY, int $codepage = CP_ACP)Создаёт VARIANT с явным значением, VARTYPE и кодовой страницей.
VARIANT — конверт на границе Automation: внутри одновременно лежат значение и VARTYPE. Если $value опущен либо равен null, официальный договор создаёт VT_EMPTY. Явный $type нужен, когда сервер различает строку «42», число 42, VT_DATE, VT_ARRAY или BYREF.
PHP способен автоматически обнаруживать COM-параметры по ссылке; вручную оборачивать каждый аргумент в variant не нужно. Явную оболочку вводят там, где это требует type library или доказанный failure. $codepage относится к преобразованию PHP string в Unicode.
| Параметр | Что передаём и зачем |
|---|---|
$value | PHP scalar/null либо variant/COM/DOTNET по правилам OLE Automation. Допустимый смысл зависит от операции. По умолчанию: null. |
$type | Одна VT_* константа либо составная маска (например VT_ARRAY | VT_VARIANT), поддерживаемая данной операцией. По умолчанию: VT_EMPTY. |
$codepage | Кодовая страница преобразования PHP string ↔ BSTR. Для UTF-8-исходника обычно передают CP_UTF8 явно. По умолчанию: CP_ACP. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | public function variant::__construct(mixed $value = null, int $type = VT_EMPTY, int $codepage = CP_ACP) |
| Результат | Новый variant. При value=null/отсутствующем value создаётся VT_EMPTY согласно официальному договору. |
| Ошибки и особые исходы | Отсутствующий ProgID/interface/permission или ошибка компонента дают com_exception с HRESULT; неправильные PHP-типы дают TypeError/ValueError. |
| Версии | (PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$quantity = new variant(12, VT_I4);
echo 'type: ', variant_get_type($quantity), "\n";
echo 'value: ', (int) $quantity, "\n";
Ожидаемый результат
type: 3
value: 12
VARIANT хранит не только значение 12, но и договор VT_I4. Это нужно, когда сервер различает целые разных размеров.
<?php
declare(strict_types=1);
$codes = new variant(['A-104', 'B-205', 'C-306'], VT_ARRAY | VT_VARIANT);
echo 'type: ', variant_get_type($codes), "\n";
foreach ($codes as $code) {
echo $code, "\n";
}
Ожидаемый результат
type: 8204
A-104
B-205
C-306
Флаг VT_ARRAY объединяется с типом элементов. SAFEARRAY знает границы, поэтому доступен foreach; обычный PHP array и COM SAFEARRAY остаются разными представлениями.
Рядом: variant_get_type, variant_cast, variant_set_type.
Вернуться к карте задач или полному дереву COM.
COMPersistHelper::__constructСвязывает helper с COM-объектом для вызовов IPersistFile/IPersistStream/IPersistStreamInit.
public function COMPersistHelper::__construct(?variant $variant = null)Связывает helper с COM-объектом для вызовов IPersistFile/IPersistStream/IPersistStreamInit.
Helper принимает COM/variant с IDispatch, но constructor не доказывает наличие IPersistFile, IPersistStream или IPersistStreamInit. Поддержка выясняется на конкретном методе. null имеет практический смысл, когда объект должен появиться из stream через LoadFromStream; прочие calls без связанного объекта закончатся ошибкой interface.
Такой слой полезен для форматов, которые сам компонент умеет сохранять. Он не превращает произвольный объект в сериализуемый и не заменяет ваш договор формата/версии.
| Параметр | Что передаём и зачем |
|---|---|
$variant | Экземпляр variant/com/dotnet. Для persistence объект должен дополнительно реализовать нужный IPersist-интерфейс. По умолчанию: null. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | public function COMPersistHelper::__construct(?variant $variant = null) |
| Результат | Новый final helper; constructor не проверяет поддержку каждого IPersist-метода заранее. |
| Ошибки и особые исходы | Отсутствующий ProgID/interface/permission или ошибка компонента дают com_exception с HRESULT; неправильные PHP-типы дают TypeError/ValueError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$xml = new COM('MSXML2.DOMDocument.6.0');
$xml->async = false;
$xml->loadXML('<order id="104"/>');
$helper = new COMPersistHelper($xml);
echo get_class($helper), "\n";
echo $helper->InitNew() ? "initialized\n" : "not initialized\n";
Ожидаемый результат
COMPersistHelper
initialized
Конструктор только связывает helper и COM-объект. Успех отдельных методов зависит от того, какие IPersist-интерфейсы реализует компонент.
<?php
declare(strict_types=1);
$dictionary = new COM('Scripting.Dictionary');
$helper = new COMPersistHelper($dictionary);
try {
$helper->InitNew();
echo "IPersistStreamInit available\n";
} catch (com_exception $error) {
printf("IPersistStreamInit unavailable: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
IPersistStreamInit unavailable: 0x80004002
Scripting.Dictionary поддерживает IDispatch, поэтому constructor принимает объект, но не реализует нужный persistence-интерфейс. Проверять надо операцию, а не только создание helper.
Рядом: COMPersistHelper::SaveToStream, COMPersistHelper::LoadFromStream.
Вернуться к карте задач или полному дереву COM.
COMPersistHelper::GetCurFileNameЗапрашивает текущий файл у объекта с IPersistFile.
public function COMPersistHelper::GetCurFileName(): string|falseЗапрашивает текущий файл у объекта с IPersistFile.
Метод делегирует IPersistFile::GetCurFile(). Он отвечает о текущей файловой привязке самого компонента, а не о последнем пути, который PHP случайно использовал. Для объекта без IPersistFile будет com_exception; для поддерживаемого interface договор допускает string либо false.
Путь может раскрывать профиль пользователя или сетевой ресурс, поэтому не возвращайте его в публичный HTTP-ответ. Если задача требует только сохранения bytes, stream-интерфейс обычно проще и безопаснее.
| Параметр | Что передаём и зачем |
|---|---|
| Параметров нет | Вызов не принимает аргументы; среда и состояние COM описаны ниже. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | public function COMPersistHelper::GetCurFileName(): string|false |
| Результат | string с текущим файлом либо false по договору; отсутствие IPersistFile/ошибка вызова даёт com_exception. |
| Ошибки и особые исходы | Отсутствующий ProgID/interface/permission или ошибка компонента дают com_exception с HRESULT; неправильные PHP-типы дают TypeError/ValueError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$xml = new COM('MSXML2.DOMDocument.6.0');
$helper = new COMPersistHelper($xml);
try {
echo $helper->GetCurFileName(), "\n";
} catch (com_exception $error) {
printf("no IPersistFile: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
no IPersistFile: 0x80004002
MSXML умеет сохраняться в stream, но не обязан предоставлять IPersistFile. HRESULT позволяет выбрать другой путь — SaveToStream.
// $document — уже созданный доверенный COM-объект с IPersistFile.
$helper = new COMPersistHelper($document);
$filename = $helper->GetCurFileName();
echo $filename === false ? "no current file\n" : basename($filename) . "\n";
Это контекстный фрагмент: конкретный ProgID, лицензия и формат определяются установленным приложением. false и исключение — разные исходы.
Рядом: COMPersistHelper::LoadFromFile, COMPersistHelper::SaveToFile.
Вернуться к карте задач или полному дереву COM.
COMPersistHelper::GetMaxStreamSizeПросит компонент оценить максимальный размер stream для сохранения.
public function COMPersistHelper::GetMaxStreamSize(): intПросит компонент оценить максимальный размер stream для сохранения.
Метод вызывает IPersistStream::GetSizeMax() через IPersistStream/IPersistStreamInit. Это оценка количества bytes для сохранения, а не гарантия точного размера. Ноль — допустимый ответ конкретного компонента; E_NOTIMPL означает, что объект сохраняется, но предварительную оценку не предоставляет.
Не выделяйте неограниченную память только по этой оценке. В php-src 64-битный ULARGE_INTEGER приводится к zend_long с TODO о корректной 64-bit обработке: на 32-bit PHP большая оценка может усечься/переполниться. Практический запасной путь — ограниченный php://temp, SaveToStream, измерение фактических bytes и лимит приложения.
| Параметр | Что передаём и зачем |
|---|---|
| Параметров нет | Вызов не принимает аргументы; среда и состояние COM описаны ниже. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | public function COMPersistHelper::GetMaxStreamSize(): int |
| Результат | Оценка в байтах как int; 0 допустим. В php-src ULARGE_INTEGER приводится к zend_long с TODO о корректной 64-bit обработке: на 32-bit PHP большая оценка может не представиться надёжно. E_NOTIMPL/отсутствующий интерфейс дают com_exception. |
| Ошибки и особые исходы | Отсутствующий ProgID/interface/permission или ошибка компонента дают com_exception с HRESULT; неправильные PHP-типы дают TypeError/ValueError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$document = new COM('htmlfile');
$helper = new COMPersistHelper($document);
echo 'estimated bytes: ', $helper->GetMaxStreamSize(), "\n";
Ожидаемый результат
estimated bytes: 0
Компонент htmlfile на проверенной Windows возвращает 0: это оценка реализации, а не обещание, что SaveToStream всегда запишет ноль байтов.
<?php
declare(strict_types=1);
$xml = new COM('MSXML2.DOMDocument.6.0');
$helper = new COMPersistHelper($xml);
try {
echo $helper->GetMaxStreamSize(), "\n";
} catch (com_exception $error) {
printf("size unavailable: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
size unavailable: 0x80004001
Наличие IPersistStream ещё не гарантирует, что компонент реализовал GetSizeMax. Практический резерв — записать в php://temp и измерить ftell/strlen.
Рядом: COMPersistHelper::SaveToStream.
Вернуться к карте задач или полному дереву COM.
COMPersistHelper::InitNewПросит IPersistStreamInit сбросить объект в начальное состояние.
public function COMPersistHelper::InitNew(): boolПросит IPersistStreamInit сбросить объект в начальное состояние.
Вызов просит IPersistStreamInit инициализировать новое состояние. true сообщает об успешном COM-call, но смысл «default» определяет компонент. Проверенный MSXML вернул true и сохранил старый root — поэтому после вызова проверяют наблюдаемое состояние, а при необходимости создают новый объект.
Объект только с IPersistStream или обычным IDispatch этого method не поддерживает и даст com_exception.
| Параметр | Что передаём и зачем |
|---|---|
| Параметров нет | Вызов не принимает аргументы; среда и состояние COM описаны ниже. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | public function COMPersistHelper::InitNew(): bool |
| Результат | true/false от компонента; отсутствие IPersistStreamInit либо ошибка COM дают com_exception. |
| Ошибки и особые исходы | Отсутствующий ProgID/interface/permission или ошибка компонента дают com_exception с HRESULT; неправильные PHP-типы дают TypeError/ValueError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$xml = new COM('MSXML2.DOMDocument.6.0');
$xml->loadXML('<old/>');
$helper = new COMPersistHelper($xml);
echo $helper->InitNew() ? "initialized\n" : "failed\n";
echo $xml->documentElement === null ? "empty\n" : "has root\n";
Ожидаемый результат
initialized
has root
InitNew вернул true, но проверенный MSXML 6.0 оставил старую корневую ноду. Булев результат подтверждает вызов интерфейса, а не желаемое бизнес-состояние: проверяйте объект после вызова или создавайте новый DOM.
<?php
declare(strict_types=1);
$helper = new COMPersistHelper(new COM('Scripting.Dictionary'));
try {
$helper->InitNew();
echo "initialized\n";
} catch (com_exception $error) {
printf("unsupported: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
unsupported: 0x80004002
COM-объект вызывается через IDispatch, а InitNew требует другого интерфейса. Ошибка обрабатывается в точке границы.
Рядом: COMPersistHelper::LoadFromStream.
Вернуться к карте задач или полному дереву COM.
COMPersistHelper::LoadFromFileЗагружает состояние объекта из файла через IPersistFile.
public function COMPersistHelper::LoadFromFile(string $filename, int $flags = 0): boolЗагружает состояние объекта из файла через IPersistFile.
Компонент сам открывает $filename через IPersistFile. $flags берутся из Windows STGM; ноль просит обычные default permissions, а реализация вправе выбрать более строгий режим. Передавайте нормализованный путь только внутри allowlist-каталога.
Официальная сигнатура обещает bool, но PHP 8.5 после успешного IPersistFile::Load возвращает null: truthiness ошибочно объявит успех неуспехом. Успех — вызов без исключения плюс наблюдаемое состояние объекта. expand_filepath/open_basedir могут вернуть false с warning ещё до COM; допустимый путь с отказом компонента даёт com_exception. Office-free runnable использует встроенный htmlfile и удаляет fixture.
| Параметр | Что передаём и зачем |
|---|---|
$filename | Путь к файлу для IPersistFile. Нормализуйте и ограничивайте его каталогом приложения; поддержка зависит от COM-компонента. |
$flags | Для variant_cmp — NORM_* флаги сравнения; для LoadFromFile — STGM-флаги. Смысл определяется сигнатурой текущей карточки. По умолчанию: 0. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | public function COMPersistHelper::LoadFromFile(string $filename, int $flags = 0): bool |
| Результат | Официальная сигнатура обещает bool, но PHP 8.5 при успешном IPersistFile::Load возвращает null. Успех подтверждают отсутствием исключения и наблюдаемым состоянием объекта; false относится к отказу expand_filepath/open_basedir до COM. |
| Ошибки и особые исходы | До COM expand_filepath/open_basedir могут выдать warning и вернуть false. После допустимого пути отказ IPersistFile/компонента даёт com_exception. Успешный PHP 8.5 call возвращает null, поэтому нельзя проверять truthiness; проверяйте исключение и наблюдаемый файл/объект. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$path = __DIR__ . '/persist-demo.htm';
try {
$source = new COM('htmlfile');
$source->write('<html><body><p>saved value</p></body></html>');
$source->close();
(new COMPersistHelper($source))->SaveToFile($path, false);
$copy = new COM('htmlfile');
$result = (new COMPersistHelper($copy))->LoadFromFile($path, 0);
echo 'return: ', get_debug_type($result), "\n";
echo 'loaded file: ', basename(str_replace('\\', '/', (string) $copy->URL)), "\n";
} finally {
if (is_file($path)) {
unlink($path);
}
}
Ожидаемый результат
return: null
loaded file: persist-demo.htm
Успех подтверждается отсутствием исключения и наблюдаемой файловой привязкой объекта. В PHP 8.5 успешный вызов вернул null, хотя официальный stub обещает bool: проверять его truthiness нельзя.
<?php
declare(strict_types=1);
ini_set('open_basedir', __DIR__);
$outside = dirname(__DIR__) . '/blocked-persist.htm';
$warningSeen = false;
set_error_handler(function () use (&$warningSeen): bool {
$warningSeen = true;
return true;
});
try {
$blocked = (new COMPersistHelper(new COM('htmlfile')))->LoadFromFile($outside, 0);
} finally {
restore_error_handler();
}
echo 'path blocked: ', $warningSeen && $blocked === false ? 'false' : 'unexpected', "\n";
try {
(new COMPersistHelper(new COM('MSXML2.DOMDocument.6.0')))
->LoadFromFile(__DIR__ . '/allowed.xml', 0);
echo "unexpected\n";
} catch (com_exception $error) {
printf("COM failure: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
path blocked: false
COM failure: 0x80004002
expand_filepath/open_basedir останавливают вызов до COM и возвращают false с PHP warning. Если путь уже допустим, отсутствие IPersistFile или отказ компонента выражается com_exception с HRESULT.
Рядом: COMPersistHelper::SaveToFile, COMPersistHelper::LoadFromStream.
Вернуться к карте задач или полному дереву COM.
COMPersistHelper::LoadFromStreamЗагружает состояние объекта из PHP stream через IPersistStream/IPersistStreamInit.
public function COMPersistHelper::LoadFromStream(resource $stream): boolЗагружает состояние объекта из PHP stream через IPersistStream/IPersistStreamInit.
Метод читает ранее сохранённое состояние из открытого PHP stream resource. Позиция принадлежит PHP: после записи обычно требуется rewind($stream). У helper со связанным объектом успех возвращает true. У new COMPersistHelper(null) OleLoadFromStream создаёт и возвращает dispatch object, несмотря на bool в официальном stub.
Для unbound-варианта stream должен содержать CLSID и persistence payload, а компонент обязан реализовывать подходящий IPersistStream. Невалидные bytes дают com_exception; результат нельзя без проверки считать bool.
| Параметр | Что передаём и зачем |
|---|---|
$stream | Открытый PHP stream resource. Позиция чтения/записи важна: перед LoadFromStream обычно вызывают rewind(). |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | public function COMPersistHelper::LoadFromStream(resource $stream): bool |
| Результат | Для helper со связанным объектом успешный вызов возвращает true. Для new COMPersistHelper(null) OleLoadFromStream возвращает загруженный dispatch object, несмотря на документированный bool; stream читается с текущей позиции. |
| Ошибки и особые исходы | Повреждённый stream или неподдерживаемый interface дают com_exception. Успех возвращает true для bound object либо загруженный dispatch object для helper, созданного с null; документированный bool не охватывает второй runtime-исход. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$source = new COM('MSXML2.DOMDocument.6.0');
$source->async = false;
$source->loadXML('<order id="104"><total>1490</total></order>');
$stream = fopen('php://temp', 'w+b');
(new COMPersistHelper($source))->SaveToStream($stream);
rewind($stream);
$copy = new COM('MSXML2.DOMDocument.6.0');
$copy->async = false;
$loaded = (new COMPersistHelper($copy))->LoadFromStream($stream);
echo 'bound return: ', get_debug_type($loaded), ' ', $loaded === true ? 'true' : 'other', "\n";
echo 'total: ', $copy->selectSingleNode('/order/total')->text, "\n";
Ожидаемый результат
bound return: bool true
total: 1490
Путь данных виден полностью: COM-объект → PHP stream → уже связанный COM-объект. Для bound helper реализация возвращает true; временный stream не пишет на диск.
<?php
declare(strict_types=1);
$source = new COM('MSXML2.DOMDocument.6.0');
$source->async = false;
$source->loadXML('<order id="104"/>');
$payloadStream = fopen('php://temp', 'w+b');
(new COMPersistHelper($source))->SaveToStream($payloadStream);
rewind($payloadStream);
// OleLoadFromStream ожидает CLSID перед состоянием IPersistStream.
$objectStream = fopen('php://temp', 'w+b');
fwrite($objectStream, pack('H*', '056AD98892F1D411A65F0040963251E5'));
stream_copy_to_stream($payloadStream, $objectStream);
rewind($objectStream);
$loaded = (new COMPersistHelper(null))->LoadFromStream($objectStream);
echo 'unbound return: ', get_debug_type($loaded), "\n";
echo 'order: ', $loaded->documentElement->getAttribute('id'), "\n";
$invalid = fopen('php://temp', 'w+b');
fwrite($invalid, '<order>');
rewind($invalid);
$target = new COM('MSXML2.DOMDocument.6.0');
$target->async = false;
try {
(new COMPersistHelper($target))->LoadFromStream($invalid);
echo "loaded\n";
} catch (com_exception $error) {
printf("invalid persisted data: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
unbound return: variant
order: 104
invalid persisted data: 0xC00CE553
У helper без связанного объекта OleLoadFromStream возвращает загруженный dispatch object, хотя stub документирует bool; у bound helper результатом остаётся true. Ошибку формата сообщает компонент, поэтому код фиксирует HRESULT.
Рядом: COMPersistHelper::SaveToStream, COMPersistHelper::InitNew.
Вернуться к карте задач или полному дереву COM.
COMPersistHelper::SaveToFileСохраняет объект в файл через IPersistFile и управляет текущей файловой привязкой.
public function COMPersistHelper::SaveToFile(?string $filename, bool $remember = true): boolСохраняет объект в файл через IPersistFile и управляет текущей файловой привязкой.
IPersistFile сохраняет копию состояния. При $remember=true путь становится текущим working file компонента и dirty flag должен очиститься; при false это «Save a Copy As». Семантика nullable filename зависит от реализации — новый код передаёт явный нормализованный path.
PHP 8.5 при успешном IPersistFile::Save возвращает null вопреки bool в stub. Успех подтверждают отсутствием исключения и реально созданным/изменённым файлом. expand_filepath/open_basedir могут вернуть false до COM; COM-отказ даёт исключение и мог оставить частичный файл, поэтому используйте временный path, finally и контролируемую замену.
| Параметр | Что передаём и зачем |
|---|---|
$filename | Путь к файлу для IPersistFile. Нормализуйте и ограничивайте его каталогом приложения; поддержка зависит от COM-компонента. |
$remember | true меняет текущий файл объекта; false означает Save a Copy As и не должен менять текущую привязку. По умолчанию: true. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | public function COMPersistHelper::SaveToFile(?string $filename, bool $remember = true): bool |
| Результат | Официальная сигнатура обещает bool, но PHP 8.5 при успешном IPersistFile::Save возвращает null. Успех подтверждают отсутствием исключения и наблюдаемым файлом/состоянием; false относится к отказу expand_filepath/open_basedir до COM. |
| Ошибки и особые исходы | До COM expand_filepath/open_basedir могут выдать warning и вернуть false. После допустимого пути отказ IPersistFile/компонента даёт com_exception. Успешный PHP 8.5 call возвращает null, поэтому нельзя проверять truthiness; проверяйте исключение и наблюдаемый файл/объект. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$target = __DIR__ . '/persist-copy.htm';
try {
$document = new COM('htmlfile');
$document->write('<html><body><p>report copy</p></body></html>');
$document->close();
$result = (new COMPersistHelper($document))->SaveToFile($target, false);
echo 'return: ', get_debug_type($result), "\n";
echo 'file created: ', is_file($target) ? 'yes' : 'no', "\n";
echo 'payload bytes: ', is_file($target) && filesize($target) > 0 ? 'yes' : 'no', "\n";
} finally {
if (is_file($target)) {
unlink($target);
}
}
Ожидаемый результат
return: null
file created: yes
payload bytes: yes
remember=false выполняет Save a Copy As. В PHP 8.5 успешный вызов вернул null вопреки bool в stub; успех доказывают отсутствие исключения и реально созданный непустой файл.
<?php
declare(strict_types=1);
ini_set('open_basedir', __DIR__);
$outside = dirname(__DIR__) . '/blocked-persist.htm';
$warningSeen = false;
set_error_handler(function () use (&$warningSeen): bool {
$warningSeen = true;
return true;
});
try {
$blocked = (new COMPersistHelper(new COM('htmlfile')))->SaveToFile($outside, false);
} finally {
restore_error_handler();
}
echo 'path blocked: ', $warningSeen && $blocked === false ? 'false' : 'unexpected', "\n";
$target = __DIR__ . '/allowed-persist.xml';
try {
(new COMPersistHelper(new COM('MSXML2.DOMDocument.6.0')))->SaveToFile($target, true);
echo "unexpected\n";
} catch (com_exception $error) {
printf("COM failure: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
} finally {
if (is_file($target)) {
unlink($target);
}
}
Ожидаемый результат
path blocked: false
COM failure: 0x80004002
Путь, который не проходит expand/open_basedir, даёт false до вызова COM. Допустимый путь доходит до компонента; отсутствие IPersistFile даёт com_exception, а возможный частичный файл удаляется в finally.
Рядом: COMPersistHelper::GetCurFileName, COMPersistHelper::SaveToStream.
Вернуться к карте задач или полному дереву COM.
COMPersistHelper::SaveToStreamСохраняет объект в PHP stream без прямого доступа COM-компонента к выбранному пути.
public function COMPersistHelper::SaveToStream(resource $stream): boolСохраняет объект в PHP stream без прямого доступа COM-компонента к выбранному пути.
Метод просит IPersistStream/IPersistStreamInit записать состояние в PHP stream. После успешного call текущая позиция обычно находится в конце; для чтения нужны rewind() и проверка фактической длины. Stream удобен для checksum, лимита и atomic storage на стороне приложения.
Наличие GetMaxStreamSize не обязательно: реальный SaveToStream может работать и при E_NOTIMPL оценки. Закрытие stream остаётся обязанностью PHP-кода.
| Параметр | Что передаём и зачем |
|---|---|
$stream | Открытый PHP stream resource. Позиция чтения/записи важна: перед LoadFromStream обычно вызывают rewind(). |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | public function COMPersistHelper::SaveToStream(resource $stream): bool |
| Результат | true/false; bytes появляются в PHP stream с текущей позиции записи. |
| Ошибки и особые исходы | Отсутствующий ProgID/interface/permission или ошибка компонента дают com_exception с HRESULT; неправильные PHP-типы дают TypeError/ValueError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$source = new COM('MSXML2.DOMDocument.6.0');
$source->async = false;
$source->loadXML('<order id="104"><total>1490</total></order>');
$stream = fopen('php://temp', 'w+b');
(new COMPersistHelper($source))->SaveToStream($stream);
rewind($stream);
$copy = new COM('MSXML2.DOMDocument.6.0');
$copy->async = false;
$loaded = (new COMPersistHelper($copy))->LoadFromStream($stream);
echo 'bound return: ', get_debug_type($loaded), ' ', $loaded === true ? 'true' : 'other', "\n";
echo 'total: ', $copy->selectSingleNode('/order/total')->text, "\n";
Ожидаемый результат
bound return: bool true
total: 1490
Путь данных виден полностью: COM-объект → PHP stream → уже связанный COM-объект. Для bound helper реализация возвращает true; временный stream не пишет на диск.
<?php
declare(strict_types=1);
$xml = new COM('MSXML2.DOMDocument.6.0');
$xml->async = false;
$xml->loadXML('<order id="104"/>');
$stream = fopen('php://temp', 'w+b');
$saved = (new COMPersistHelper($xml))->SaveToStream($stream);
rewind($stream);
$payload = stream_get_contents($stream);
echo 'saved: ', $saved ? 'yes' : 'no', "\n";
echo 'payload: ', $payload, "\n";
Ожидаемый результат
saved: yes
payload: <order id="104"/>
Вместо абстрактного true читатель видит байты, которые реально появились в PHP stream.
Рядом: COMPersistHelper::LoadFromStream, COMPersistHelper::GetMaxStreamSize.
Вернуться к карте задач или полному дереву COM.
com_create_guidГенерирует Windows GUID в фигурных скобках для correlation ID и COM-идентификаторов.
function com_create_guid(): string|falseГенерирует Windows GUID в фигурных скобках для correlation ID и COM-идентификаторов.
Windows создаёт GUID в Microsoft-формате с фигурными скобками. Он подходит как технический correlation/object identifier, но не является секретом, токеном доступа или доказательством личности. На системный отказ функция возвращает false, поэтому результат проверяют перед сохранением.
Для кросс-платформенного UUID официальный manual указывает PECL uuid_create(); выбор формата включают в контракт БД/API, а не маскируют строковыми заменами в каждом месте.
| Параметр | Что передаём и зачем |
|---|---|
| Параметров нет | Вызов не принимает аргументы; среда и состояние COM описаны ниже. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function com_create_guid(): string|false |
| Результат | GUID как string в фигурных скобках либо false при системной ошибке. |
| Ошибки и особые исходы | Системный отказ возвращает false; формат/тип результата проверяйте до сохранения. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$guid = com_create_guid();
if ($guid === false) {
throw new RuntimeException('GUID generation failed');
}
echo preg_match('/^\{[0-9A-F]{8}(?:-[0-9A-F]{4}){3}-[0-9A-F]{12}\}$/i', $guid)
? "valid GUID\n" : "invalid GUID\n";
Ожидаемый результат
valid GUID
Сам GUID случайный, поэтому проверяется полезное неизменное свойство — канонический формат с фигурными скобками.
<?php
declare(strict_types=1);
$first = com_create_guid();
$second = com_create_guid();
echo is_string($first) && is_string($second) ? "two strings\n" : "failure\n";
echo $first !== $second ? "different\n" : "collision\n";
Ожидаемый результат
two strings
different
Correlation ID связывает журнал, очередь и ответ сервиса. Уникальность двух значений показывает назначение, не обещая математически невозможную абсолютную гарантию.
Вернуться к карте задач или полному дереву COM.
com_event_sinkСоединяет outgoing events COM-объекта с методами PHP-объекта.
function com_event_sink(variant $variant, object $sink_object, array|string|null $sink_interface = null): boolСоединяет outgoing events COM-объекта с методами PHP-объекта.
Sink-object содержит PHP-методы с именами событий outgoing dispinterface. Если $sink_interface=null, расширение пытается выбрать default source interface type library; параметр явно nullable начиная с PHP 8. Явное имя/GUID снимает неоднозначность. com_print_typeinfo(..., display_sink: true) помогает построить skeleton, но сигнатуры сверяют с документацией компонента.
После подключения события приходят только пока живы COM-object и sink и код качает message queue. Это долгоживущий worker, а не обычный короткий HTTP-request. До PHP 8 вызов exit() из handler мог повесить PHP; безопаснее handler ставит флаг/бросает контролируемое исключение, а главный loop завершает lifecycle.
| Параметр | Что передаём и зачем |
|---|---|
$variant | Экземпляр variant/com/dotnet. Для persistence объект должен дополнительно реализовать нужный IPersist-интерфейс. |
$sink_object | PHP-объект с методами, названными как события выбранного dispinterface. |
$sink_interface | Имя/GUID outgoing dispinterface или null для попытки выбрать default source interface. По умолчанию: null. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function com_event_sink(variant $variant, object $sink_object, array|string|null $sink_interface = null): bool |
| Результат | true при успешном подключении или false, если interface не найден/не подключён; некоторые COM-ошибки выражаются исключением. |
| Ошибки и особые исходы | false является штатным исходом и должен проверяться. Некорректный PHP-тип даёт TypeError; COM-отказ может дать com_exception. |
| Версии | (PHP 4 >= 4.2.0, PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
final class ConnectionEvents {
public array $seen = [];
public function ConnectComplete(...$arguments): void {
$this->seen[] = 'ConnectComplete';
}
}
$connection = new COM('ADODB.Connection');
$sink = new ConnectionEvents();
$connected = com_event_sink($connection, $sink, 'ConnectionEvents');
echo $connected ? "sink connected\n" : "sink unavailable\n";
Ожидаемый результат
sink connected
Здесь проверяется только соединение dispinterface с PHP-объектом: база и сеть не открываются. Имена методов sink должны совпадать с событиями type library.
<?php
declare(strict_types=1);
final class EmptySink {}
$connection = new COM('ADODB.Connection');
$connected = com_event_sink($connection, new EmptySink(), 'NoSuchEvents');
echo $connected ? "connected\n" : "interface not found\n";
Ожидаемый результат
interface not found
Не каждый отказ выражается исключением: неверное имя outgoing interface возвращает false, поэтому результат нужно проверять.
Рядом: com_message_pump, com_print_typeinfo.
Вернуться к карте задач или полному дереву COM.
com_get_active_objectПолучает уже работающий объект из Running Object Table и не создаёт новый процесс.
function com_get_active_object(string $prog_id, ?int $codepage = null): variantПолучает уже работающий объект из Running Object Table и не создаёт новый процесс.
Функция обращается к Windows Running Object Table. Она никогда не запускает отсутствующий процесс: уже зарегистрированный object возвращается как variant, отсутствие обычно даёт MK_E_UNAVAILABLE. Параметр $codepage стал nullable в PHP 8. Неверный ProgID и «правильный, но сейчас не запущен» — разные конфигурационные ветки.
У desktop-приложений один активный экземпляр может не выдерживать нескольких web-клиентов. Используйте отдельного worker/очередь и явно решите, разрешён ли fallback на new COM(...); молчаливое создание меняет смысл операции.
| Параметр | Что передаём и зачем |
|---|---|
$prog_id | ProgID или CLSID объекта, который уже зарегистрирован в Running Object Table. Функция ничего не запускает. |
$codepage | Кодовая страница преобразования PHP string ↔ BSTR. Для UTF-8-исходника обычно передают CP_UTF8 явно. По умолчанию: null. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function com_get_active_object(string $prog_id, ?int $codepage = null): variant |
| Результат | variant/com handle на уже активный объект; при отсутствии обычно com_exception с MK_E_UNAVAILABLE. |
| Ошибки и особые исходы | Отсутствующий ProgID/interface/permission или ошибка компонента дают com_exception с HRESULT; неправильные PHP-типы дают TypeError/ValueError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
try {
com_get_active_object('Scripting.Dictionary');
echo "active\n";
} catch (com_exception $error) {
printf("not active: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
not active: 0x800401E3
Scripting.Dictionary не регистрируется в Running Object Table. Получаем штатный HRESULT MK_E_UNAVAILABLE, а не создаём новый объект.
<?php
declare(strict_types=1);
try {
com_get_active_object('No.Such.AutomationServer', CP_UTF8);
echo "active\n";
} catch (com_exception $error) {
printf("invalid ProgID: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
invalid ProgID: 0x800401F3
Неизвестный ProgID даёт другой HRESULT. В журнале полезно хранить код и собственный контекст, не показывая внутренние пути пользователю.
Рядом: com::__construct.
Вернуться к карте задач или полному дереву COM.
com_load_typelibЗагружает type library и регистрирует её константы в текущем PHP-процессе.
function com_load_typelib(string $typelib, bool $case_insensitive = true): boolЗагружает type library и регистрирует её константы в текущем PHP-процессе.
Type library можно указать path к TLB/EXE/DLL, GUID с version либо human-readable name. Разрешение идёт именно в таком порядке; поиск по имени самый дорогой, потому что перебирает registry. Успех регистрирует constants в текущем PHP process как define().
com.typelib_file эффективнее для предзагрузки, com.autoregister_typelib пытается загрузить library при создании COM-object, но зависит от предоставленных interfaces. Исторически $case_insensitive описан как обратный одноимённому аргументу define(). В PHP 8.5 case-insensitive constants не поддержаны: в проверенном вызове второй аргумент не понадобился. В long-lived worker фиксируйте GUID/version и избегайте конфликтов имён.
| Параметр | Что передаём и зачем |
|---|---|
$typelib | Имя/GUID/path type library, которую следует связать с создаваемым объектом; пустая строка оставляет автоматическое обнаружение. |
$case_insensitive | Исторический параметр регистрации констант. В PHP 8.5 нечувствительные к регистру константы не поддерживаются, аргумент фактически игнорируется с предупреждением при явном несовместимом значении. По умолчанию: true. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function com_load_typelib(string $typelib, bool $case_insensitive = true): bool |
| Результат | true при загрузке/регистрации либо false, если library не найдена/не загружена. |
| Ошибки и особые исходы | false является штатным исходом и должен проверяться. Некорректный PHP-тип даёт TypeError; COM-отказ может дать com_exception. |
| Версии | (PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$loaded = com_load_typelib('scrrun.dll');
echo $loaded ? "typelib loaded\n" : "typelib missing\n";
Ожидаемый результат
typelib loaded
Файл встроен в Windows и не запускает Office. В PHP 8.5 параметр case_insensitive фактически игнорируется: нечувствительные к регистру константы больше не поддерживаются, поэтому пример использует значение по умолчанию без ложного обещания.
<?php
declare(strict_types=1);
$loaded = com_load_typelib('No Such Automation Library');
echo $loaded ? "loaded\n" : "not found\n";
Ожидаемый результат
not found
Поиск по имени может обходить registry и дороже прямого пути/GUID. Невалидное имя возвращает false в проверенной среде.
Рядом: com_print_typeinfo.
Вернуться к карте задач или полному дереву COM.
com_message_pumpОбрабатывает Windows COM messages и даёт sink-обработчикам получить события.
function com_message_pump(int $timeout_milliseconds = 0): boolОбрабатывает Windows COM messages и даёт sink-обработчикам получить события.
COM apartments доставляют calls/events через Windows message queue. Значение 0 не ждёт: pending messages обрабатываются, иначе сразу false. Положительный timeout ограничивает один wait; true значит, что были обработаны message(s), false — timeout без обработки.
Принимайте только небольшой timeout >= 0. PHP парсит signed zend_long, а C-код передаёт его Windows как DWORD: отрицательное число может стать огромным ожиданием. Loop обязан иметь отдельный monotonic deadline, cancellation flag и cleanup; один большой timeout и бесконечный web-request недопустимы.
| Параметр | Что передаём и зачем |
|---|---|
$timeout_milliseconds | Неотрицательная и небольшая граница одного ожидания COM-message в миллисекундах. 0 только проверяет очередь. Повторный pump дополнительно ограничивают внешним deadline. По умолчанию: 0. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function com_message_pump(int $timeout_milliseconds = 0): bool |
| Результат | true, если message обработан, иначе false после пустой очереди/timeout. |
| Ошибки и особые исходы | false после пустой очереди/timeout является штатным исходом. timeout должен быть >= 0 и малым: signed zend_long приводится в C к DWORD, поэтому отрицательное число способно стать огромным ожиданием. Повторный loop ограничивают внешним deadline/cancellation. |
| Версии | (PHP 4 >= 4.2.0, PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$handled = com_message_pump(0);
echo $handled ? "event handled\n" : "queue empty\n";
Ожидаемый результат
queue empty
Нулевой timeout не блокирует скрипт. false означает, что за этот вызов сообщение не было обработано.
<?php
declare(strict_types=1);
$timeoutMs = 5;
if ($timeoutMs < 0 || $timeoutMs > 50) {
throw new InvalidArgumentException('timeout must be between 0 and 50 ms');
}
$handled = com_message_pump($timeoutMs);
echo $handled ? "event handled\n" : "timeout\n";
Ожидаемый результат
timeout
Проверяйте timeout до вызова: C-код принимает signed zend_long, но передаёт его Windows как DWORD, поэтому отрицательное значение может превратиться в огромное ожидание. Повторные вызовы дополнительно ограничивают внешним deadline и флагом отмены.
Рядом: com_event_sink.
Вернуться к карте задач или полному дереву COM.
com_print_typeinfoПечатает PHP-подобное описание dispatch/sink interface для исследования динамического API.
function com_print_typeinfo(variant|string $variant, ?string $dispatch_interface = null, bool $display_sink = false): boolПечатает PHP-подобное описание dispatch/sink interface для исследования динамического API.
Первый аргумент — COM-object либо имя type library по тем же правилам, что у com_load_typelib. $dispatch_interface выбирает IDispatch-descendant, $display_sink=true просит соответствующий outgoing interface для event sink. Функция печатает declaration в stdout и отдельно возвращает bool.
Для инструмента разработки перехватывайте вывод через output buffering и превращайте его в локальные stubs. Dump не гарантирует overload rules, ownership, thread model или business contract; его нельзя без фильтра выдавать посетителю.
| Параметр | Что передаём и зачем |
|---|---|
$variant | Экземпляр variant/com/dotnet. Для persistence объект должен дополнительно реализовать нужный IPersist-интерфейс. |
$dispatch_interface | Необязательное имя interface, которое надо распечатать вместо default dispatch interface. По умолчанию: null. |
$display_sink | true просит описание outgoing events, useful для подготовки методов com_event_sink. По умолчанию: false. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function com_print_typeinfo(variant|string $variant, ?string $dispatch_interface = null, bool $display_sink = false): bool |
| Результат | true/false и побочный вывод declaration в stdout; используйте output buffering, если нужен string. |
| Ошибки и особые исходы | false является штатным исходом и должен проверяться. Некорректный PHP-тип даёт TypeError; COM-отказ может дать com_exception. |
| Версии | (PHP 4 >= 4.2.0, PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$dictionary = new COM('Scripting.Dictionary');
ob_start();
$printed = com_print_typeinfo($dictionary);
$declaration = ob_get_clean();
echo $printed ? "typeinfo printed\n" : "typeinfo unavailable\n";
echo str_contains($declaration, 'function Add') ? "has Add\n" : "no Add\n";
echo str_contains($declaration, 'function Exists') ? "has Exists\n" : "no Exists\n";
Ожидаемый результат
typeinfo printed
has Add
has Exists
Функция печатает PHP-подобный каркас. Output buffering превращает его в строку для локального изучения; это не полноценный типобезопасный stub.
<?php
declare(strict_types=1);
$connection = new COM('ADODB.Connection');
ob_start();
$printed = com_print_typeinfo($connection, 'ConnectionEvents', true);
$declaration = ob_get_clean();
echo $printed ? "sink typeinfo printed\n" : "sink typeinfo unavailable\n";
echo str_contains($declaration, 'function ConnectComplete') ? "has ConnectComplete\n" : "no ConnectComplete\n";
Ожидаемый результат
sink typeinfo printed
has ConnectComplete
display_sink=true исследует события, которые COM-сервер посылает PHP. Это помогает назвать методы объекта для com_event_sink.
Рядом: com_event_sink, com_load_typelib.
Вернуться к карте задач или полному дереву COM.
variant_absВозвращает абсолютную величину Automation-числа.
function variant_abs(mixed $value): variantВозвращает абсолютную величину Automation-числа.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
Возвращается новый variant с модулем числа. Исходный object не меняется; для смены знака существует variant_neg. Непредставимый тип/default property даёт com_exception.
| Параметр | Что передаём и зачем |
|---|---|
$value | PHP scalar/null либо variant/COM/DOTNET по правилам OLE Automation. Допустимый смысл зависит от операции. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_abs(mixed $value): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$value = variant_abs(-17);
echo (int) $value, "\n";
Ожидаемый результат
17
Модуль нужен для абсолютной величины расхождения независимо от знака.
<?php
declare(strict_types=1);
$actual = 97;
$target = 100;
$delta = variant_abs(variant_sub($actual, $target));
echo (int) $delta <= 5 ? "within tolerance\n" : "reject\n";
Ожидаемый результат
within tolerance
Комбинация variant_sub и variant_abs показывает реальную проверку измерения.
Рядом: variant_neg, variant_sub.
Вернуться к карте задач или полному дереву COM.
variant_addСкладывает два значения по правилам VarAdd, включая особое поведение строк/null/empty.
function variant_add(mixed $left, mixed $right): variantСкладывает два значения по правилам VarAdd, включая особое поведение строк/null/empty.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
| Операнды | Официальное правило VarAdd |
|---|---|
| две строки | Конкатенация |
| число и numeric string / string и character | Сложение после Automation-приведения |
| два числа | Сложение |
| хотя бы один NULL | Результат NULL |
| оба EMPTY | Integer subtype |
Из-за перегруженного смысла для текста лучше variant_cat, а для бизнес-чисел — явные типы. Порядок и VARTYPE проверяют до операции.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_add(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$sum = variant_add(1200, 290);
echo (int) $sum, "\n";
Ожидаемый результат
1490
Функция применяет правила OLE Automation, а возвращает новый variant.
<?php
declare(strict_types=1);
$goods = new variant(2500, VT_I4);
$total = variant_add($goods, 490);
echo "total: " . (int) $total . "\n";
Ожидаемый результат
total: 2990
Сценарий сохраняет COM-тип входа и явно преобразует результат для PHP-вывода.
Рядом: variant_sub, variant_cat.
Вернуться к карте задач или полному дереву COM.
variant_andВыполняет Automation AND над логическими значениями или битовыми масками.
function variant_and(mixed $left, mixed $right): variantВыполняет Automation AND над логическими значениями или битовыми масками.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
Это битовый AND, не PHP &&: он вычисляет оба значения и сохраняет Automation-семантику.
| left | right | официальный результат |
|---|---|---|
| true | true / false / null | true / false / null |
| false | true / false / null | false / false / false |
| null | true / false / null | null / false / null |
Для integer операция выбирает bits, установленные в обеих masks.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_and(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$flags = variant_and(6, 3);
echo (int) $flags, "\n";
Ожидаемый результат
2
6 (110) AND 3 (011) оставляет общий бит 2.
<?php
declare(strict_types=1);
$permissions = 0b1010;
$exportFlag = 0b0010;
$matched = variant_and($permissions, $exportFlag);
echo (int) $matched !== 0 ? "export allowed\n" : "denied\n";
Ожидаемый результат
export allowed
Битовые флаги встречаются в COM API и старых Windows-протоколах.
Рядом: variant_or, variant_xor, variant_not.
Вернуться к карте задач или полному дереву COM.
variant_castСоздаёт новый variant, приведённый к указанному VARTYPE.
function variant_cast(variant $variant, int $type): variantСоздаёт новый variant, приведённый к указанному VARTYPE.
Функция копирует исходный variant, затем вызывает Automation VariantChangeType для копии. Оригинал остаётся прежним. $type — VT_* constant; успешное преобразование не означает, что locale-dependent текст был однозначным.
Если требуется заменить существующий container, используется variant_set_type. Для безопасного adapter обычно лучше cast в локальной переменной, проверить VARTYPE/value и только потом передавать наружу.
| Параметр | Что передаём и зачем |
|---|---|
$variant | Экземпляр variant/com/dotnet. Для persistence объект должен дополнительно реализовать нужный IPersist-интерфейс. |
$type | Одна VT_* константа либо составная маска (например VT_ARRAY | VT_VARIANT), поддерживаемая данной операцией. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_cast(variant $variant, int $type): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$text = new variant('42', VT_BSTR);
$number = variant_cast($text, VT_I4);
echo variant_get_type($text), ' -> ', variant_get_type($number), "\n";
echo (int) $number, "\n";
Ожидаемый результат
8 -> 3
42
variant_cast создаёт новый variant; исходный BSTR остаётся BSTR.
<?php
declare(strict_types=1);
try {
variant_cast(new variant('twelve', VT_BSTR), VT_I4);
echo "converted\n";
} catch (com_exception $error) {
printf("conversion failed: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
conversion failed: 0x80020005
Некорректный текст даёт com_exception; внешние данные нельзя считать числом только из-за ожидаемого типа API.
Рядом: variant_set_type, variant_get_type.
Вернуться к карте задач или полному дереву COM.
variant_catЯвно конкатенирует два Automation-значения как строки.
function variant_cat(mixed $left, mixed $right): variantЯвно конкатенирует два Automation-значения как строки.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
Операция явно соответствует Automation-конкатенации (left . right) и возвращает новый variant. Она снимает двусмысленность variant_add, где две строки соединяются, а numeric string может участвовать в сложении. Для внешних идентификаторов сначала нормализуйте VARTYPE/encoding.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_cat(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$label = variant_cat('Order ', '104');
echo (string) $label, "\n";
Ожидаемый результат
Order 104
variant_cat всегда выражает намерение конкатенации, даже когда строка выглядит числом.
<?php
declare(strict_types=1);
$prefix = new variant('001', VT_BSTR);
$code = variant_cat($prefix, '-A');
echo (string) $code, "\n";
Ожидаемый результат
001-A
Начальные нули сохраняются, потому что операция строковая.
Рядом: variant_add.
Вернуться к карте задач или полному дереву COM.
variant_cmpСравнивает два scalar Automation-значения с выбранными locale/flags.
function variant_cmp(mixed $left, mixed $right, int $locale_id = LOCALE_SYSTEM_DEFAULT, int $flags = 0): intСравнивает два scalar Automation-значения с выбранными locale/flags.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
Сравниваются только scalar values, не arrays и не variant records. При успехе результат — VARCMP_LT/EQ/GT/NULL, а не привычные -1/0/1. Реализация возвращает raw int VarCmp: Automation failure способен прийти как HRESULT вне этих четырёх констант без обязательного com_exception. Для strings $locale_id задаёт collation; flags OR-комбинируют NORM_*.
NORM_IGNOREKASHIDA присутствует в официальном описании, но не определён в проверенной PHP 8.5.11 x64-сборке. Перед использованием условной constant проверяйте defined().
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
$locale_id | Windows LCID для сравнения строк. LOCALE_SYSTEM_DEFAULT связывает результат с настройками ОС. По умолчанию: LOCALE_SYSTEM_DEFAULT. |
$flags | Для variant_cmp — NORM_* флаги сравнения; для LoadFromFile — STGM-флаги. Смысл определяется сигнатурой текущей карточки. По умолчанию: 0. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_cmp(mixed $left, mixed $right, int $locale_id = LOCALE_SYSTEM_DEFAULT, int $flags = 0): int |
| Результат | Raw int из Windows VarCmp: при успехе одна из VARCMP_LT/EQ/GT/NULL, а при Automation failure возможен HRESULT вне этих констант без обязательного com_exception. |
| Ошибки и особые исходы | Функция возвращает raw int VarCmp. Сначала проверяйте VARCMP_LT/EQ/GT/NULL; любое иное значение может быть HRESULT Automation failure, даже если com_exception не возник. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$result = variant_cmp(10, 20);
echo $result === VARCMP_LT ? "left is smaller\n" : "other\n";
Ожидаемый результат
left is smaller
Результат — одна из констант VARCMP, а не произвольное отрицательное число как у некоторых PHP comparator-функций.
<?php
declare(strict_types=1);
$result = variant_cmp('sku-104', 'SKU-104', LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE);
echo $result === VARCMP_EQ ? "same code\n" : "different\n";
$raw = variant_cmp(
new variant([1], VT_ARRAY | VT_VARIANT),
new variant([1], VT_ARRAY | VT_VARIANT),
);
$known = [VARCMP_LT, VARCMP_EQ, VARCMP_GT, VARCMP_NULL];
echo in_array($raw, $known, true)
? "known comparison\n"
: sprintf("raw HRESULT: 0x%08X\n", $raw & 0xFFFFFFFF);
Ожидаемый результат
same code
raw HRESULT: 0x80020005
Locale и flags влияют на строковое сравнение. Реализация возвращает raw int из VarCmp: при неподдерживаемом SAFEARRAY это HRESULT 0x80020005 вне VARCMP_* и без обязательного com_exception, поэтому результат сначала сверяют с четырьмя допустимыми константами.
Рядом: variant_get_type.
Вернуться к карте задач или полному дереву COM.
variant_date_from_timestampПреобразует Unix timestamp в VT_DATE.
function variant_date_from_timestamp(int $timestamp): variantПреобразует Unix timestamp в VT_DATE.
Unix timestamp — секунды от 1970 UTC, а VT_DATE — число дней Automation. Функция создаёт новый VT_DATE для COM-call. Отрицательное значение даёт ValueError; для значения после поддерживаемого предела диагностика PHP указывает 23:59:59 31 декабря 3000 UTC.
Пограничный round-trip в целевой Windows/runtime обязателен: преобразование проходит через localtime и Windows date conversion. Для календарной даты отдельно фиксируют timezone и смысл instant/local time.
| Параметр | Что передаём и зачем |
|---|---|
$timestamp | Unix timestamp в секундах. Преобразование учитывает локальную временную границу Windows/OLE Automation. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_date_from_timestamp(int $timestamp): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Отрицательный timestamp и значение после поддерживаемого Windows-предела (диагностика PHP: 23:59:59 31.12.3000 UTC) дают ValueError; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$timestamp = 1704067200;
$date = variant_date_from_timestamp($timestamp);
echo 'type: ', variant_get_type($date), "\n";
echo 'roundtrip: ', variant_date_to_timestamp($date), "\n";
Ожидаемый результат
type: 7
roundtrip: 1704067200
Строковое представление зависит от Windows locale/timezone, поэтому пример проверяет тип и обратное преобразование.
<?php
declare(strict_types=1);
$lastDocumentedSecond = 32535215999; // 3000-12-31 23:59:59 UTC
$date = variant_date_from_timestamp($lastDocumentedSecond);
echo 'upper boundary: ', variant_date_to_timestamp($date), "\n";
foreach ([-1 => 'negative', PHP_INT_MAX => 'after 3000'] as $timestamp => $label) {
try {
variant_date_from_timestamp($timestamp);
echo $label, " accepted\n";
} catch (ValueError) {
echo $label, " rejected\n";
}
}
Ожидаемый результат
upper boundary: 32535215999
negative rejected
after 3000 rejected
Негативное значение и timestamp далеко после 31 декабря 3000 года дают ValueError. Пограничный допустимый instant проверяется round-trip; сообщение верхней ошибки формулирует официальный предел как 23:59:59 31.12.3000 UTC.
Рядом: variant_date_to_timestamp.
Вернуться к карте задач или полному дереву COM.
variant_date_to_timestampПреобразует date-compatible VARIANT в Unix timestamp или null.
function variant_date_to_timestamp(variant $variant): ?intПреобразует date-compatible VARIANT в Unix timestamp или null.
Функция принимает date-compatible variant и возвращает Unix int либо null. «Similar» type может быть неявно приведён: проверенная VT_BSTR строка 2024-01-01 успешно превратилась по locale/timezone Windows, а SAFEARRAY при невозможном VariantChangeType вернул null без исключения.
Такой строковый успех не является переносимым parser contract. В adapter явно разбирайте текст, используйте variant_date_from_timestamp и всегда проверяйте null до DateTime/БД; com_exception не является обязательной веткой conversion failure.
| Параметр | Что передаём и зачем |
|---|---|
$variant | Экземпляр variant/com/dotnet. Для persistence объект должен дополнительно реализовать нужный IPersist-интерфейс. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_date_to_timestamp(variant $variant): ?int |
| Результат | Unix timestamp как int либо null, если VariantChangeType/SystemTime conversion не удалась; Automation failure здесь не обязан стать com_exception. |
| Ошибки и особые исходы | Невозможное Automation-преобразование может вернуть null без исключения; проверяйте результат перед DateTime/БД. Неправильный PHP-тип даёт TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$date = variant_date_from_timestamp(946684800);
$timestamp = variant_date_to_timestamp($date);
echo $timestamp, "\n";
echo gmdate('Y-m-d', $timestamp), "\n";
Ожидаемый результат
946684800
2000-01-01
Код получает обычный int, пригодный для DateTimeImmutable и хранения в приложении.
<?php
declare(strict_types=1);
$unsupported = new variant([1], VT_ARRAY | VT_VARIANT);
$timestamp = variant_date_to_timestamp($unsupported);
echo 'conversion failure: ', get_debug_type($timestamp), "\n";
Ожидаемый результат
conversion failure: null
VariantChangeType не может превратить SAFEARRAY в VT_DATE. Реализация возвращает null без обязательного com_exception, поэтому результат проверяют до передачи в DateTimeImmutable или БД.
Рядом: variant_date_from_timestamp.
Вернуться к карте задач или полному дереву COM.
variant_divВыполняет обычное Automation-деление с дробной частью.
function variant_div(mixed $left, mixed $right): variantВыполняет обычное Automation-деление с дробной частью.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
| Случай | Результат VarDiv |
|---|---|
| числа или преобразуемые string/date/character/bool | Деление, обычно Double |
| хотя бы один NULL | NULL |
| right EMPTY, left не EMPTY | DISP_E_DIVBYZERO |
| left EMPTY, right не EMPTY | Double 0 |
| оба EMPTY | DISP_E_OVERFLOW |
Делитель из внешних данных проверяют до call. Для целой части и остатка используются variant_idiv/variant_mod.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_div(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$average = variant_div(15, 4);
echo (float) $average, "\n";
Ожидаемый результат
3.75
variant_div сохраняет дробную часть в отличие от variant_idiv.
<?php
declare(strict_types=1);
try {
variant_div(10, 0);
echo "unexpected\n";
} catch (com_exception $error) {
printf("division failed: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
division failed: 0x80020012
Automation сообщает DISP_E_DIVBYZERO через com_exception; знаменатель внешних данных проверяют до вызова.
Рядом: variant_idiv, variant_mod.
Вернуться к карте задач или полному дереву COM.
variant_eqvВыполняет логическую/битовую эквивалентность EQV.
function variant_eqv(mixed $left, mixed $right): variantВыполняет логическую/битовую эквивалентность EQV.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
EQV — битовая эквивалентность: для bool true, когда состояния совпадают; для bit masks возвращает инверсию XOR по ширине Automation-типа. Это не PHP identity и не сравнение объектов. Для обычных bool выражение $left === $right часто понятнее; EQV нужен на реальной VARIANT-границе.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_eqv(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$same = variant_eqv(true, true);
echo (bool) $same ? "equivalent\n" : "different\n";
Ожидаемый результат
equivalent
EQV истинно, когда оба логических значения совпадают.
<?php
declare(strict_types=1);
$paid = true;
$reserved = false;
$consistent = variant_eqv($paid, $reserved);
echo (bool) $consistent ? "same state\n" : "investigate\n";
Ожидаемый результат
investigate
Пример показывает логику согласованности двух подсистем; обычный PHP === часто читается проще, если VARIANT не требуется.
Рядом: variant_imp, variant_xor.
Вернуться к карте задач или полному дереву COM.
variant_fixОставляет целую часть числа, двигаясь к нулю.
function variant_fix(mixed $value): variantОставляет целую часть числа, двигаясь к нулю.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
Fix отбрасывает дробную часть по направлению к нулю: -7.8 → -7. Для положительных значений совпадает с Int; для отрицательных variant_int идёт вниз и вернёт -8. Выбирайте функцию по смыслу задачи, а не по названию «целая часть».
| Параметр | Что передаём и зачем |
|---|---|
$value | PHP scalar/null либо variant/COM/DOTNET по правилам OLE Automation. Допустимый смысл зависит от операции. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_fix(mixed $value): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$value = variant_fix(-7.8);
echo (int) $value, "\n";
Ожидаемый результат
-7
Для отрицательного числа Fix идёт к нулю: -7, а не -8.
<?php
declare(strict_types=1);
$ratio = variant_div(-78, 10);
echo (int) variant_fix($ratio), "\n";
Ожидаемый результат
-7
С отрицательными значениями поведение отличается от variant_int; выбор должен отражать предметную задачу.
Рядом: variant_int, variant_round.
Вернуться к карте задач или полному дереву COM.
variant_get_typeВозвращает VARTYPE, включая составные флаги VT_ARRAY/VT_BYREF.
function variant_get_type(variant $variant): intВозвращает VARTYPE, включая составные флаги VT_ARRAY/VT_BYREF.
Возвращаемый int сравнивают с VT_* constants. COM и DOTNET обычно дают VT_DISPATCH, потому что оба класса наследуются от variant. У array/by-reference код составной: проверяют bits VT_ARRAY/VT_BYREF и base type, а не только равенство одной constant.
Функция отвечает о Automation-container, но не валидирует business shape, размер SAFEARRAY или доступность методов IDispatch.
| Параметр | Что передаём и зачем |
|---|---|
$variant | Экземпляр variant/com/dotnet. Для persistence объект должен дополнительно реализовать нужный IPersist-интерфейс. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_get_type(variant $variant): int |
| Результат | int VARTYPE; для SAFEARRAY/BYREF это битовая комбинация, а не один простой код. |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$value = new variant('order-104', VT_BSTR);
echo variant_get_type($value), "\n";
echo variant_get_type($value) === VT_BSTR ? "text\n" : "other\n";
Ожидаемый результат
8
text
Код типа нужен на границе с неоднозначным COM API, а не вместо обычной модели данных приложения.
<?php
declare(strict_types=1);
$values = new variant([1, 2, 3], VT_ARRAY | VT_VARIANT);
$type = variant_get_type($values);
echo (($type & VT_ARRAY) !== 0) ? "array\n" : "scalar\n";
echo 'element marker: ', $type & ~VT_ARRAY, "\n";
Ожидаемый результат
array
element marker: 12
VT_ARRAY и VT_BYREF — флаги. Равенство одному VT_* не работает для составного типа; применяют битовую маску.
Рядом: variant_cast, variant_set_type.
Вернуться к карте задач или полному дереву COM.
variant_idivВыполняет целочисленное Automation-деление.
function variant_idiv(mixed $left, mixed $right): variantВыполняет целочисленное Automation-деление.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
| Случай | Официальное правило VarIdiv |
|---|---|
| string/date/character/bool либо числа | Приведение к integer, затем integer division |
| хотя бы один NULL | NULL |
| оба EMPTY | DISP_E_DIVBYZERO |
Это не замена проверки делителя и не обязательно то же самое, что PHP intdiv() для string/date/bool variants. Остаток вычисляется variant_mod.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_idiv(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$boxes = variant_idiv(17, 5);
echo (int) $boxes, "\n";
Ожидаемый результат
3
17 единиц дают три полные коробки по пять.
<?php
declare(strict_types=1);
$units = 17;
$size = 5;
$boxes = variant_idiv($units, $size);
$rest = variant_mod($units, $size);
echo "boxes: " . (int) $boxes . ", rest: " . (int) $rest . "\n";
Ожидаемый результат
boxes: 3, rest: 2
variant_idiv и variant_mod образуют практическую пару.
Рядом: variant_div, variant_mod.
Вернуться к карте задач или полному дереву COM.
variant_impВыполняет Automation IMP (логическую импликацию).
function variant_imp(mixed $left, mixed $right): variantВыполняет Automation IMP (логическую импликацию).
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
| left | right | официальный результат |
|---|---|---|
| true | true / false / null | true / false / true |
| false | true / false / null | true / true / true |
| null | true / false / null | true / null / null |
IMP означает Automation-импликацию. Для обычного policy-кода лучше дать условиям предметные имена и использовать прозрачное !$left || $right, если VARIANT-совместимость не нужна.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_imp(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$result = variant_imp(true, false);
echo (bool) $result ? "true\n" : "false\n";
Ожидаемый результат
false
IMP ложно только когда условие истинно, а следствие ложно.
<?php
declare(strict_types=1);
$paid = true;
$confirmed = false;
$valid = variant_imp($paid, $confirmed);
echo (bool) $valid ? "rule holds\n" : "rule broken\n";
Ожидаемый результат
rule broken
Для обычного PHP выражение !$paid || $confirmed понятнее; variant_imp нужен при сохранении Automation-семантики.
Рядом: variant_eqv.
Вернуться к карте задач или полному дереву COM.
variant_intВозвращает целую часть с округлением вниз к минус бесконечности.
function variant_int(mixed $value): variantВозвращает целую часть с округлением вниз к минус бесконечности.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
Int округляет вниз к минус бесконечности: -7.8 → -8. Для положительного числа это целая часть. Если задача означает «отбросить дробь к нулю», нужен variant_fix; если «до заданных знаков» — variant_round.
| Параметр | Что передаём и зачем |
|---|---|
$value | PHP scalar/null либо variant/COM/DOTNET по правилам OLE Automation. Допустимый смысл зависит от операции. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_int(mixed $value): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$value = variant_int(-7.2);
echo (int) $value, "\n";
Ожидаемый результат
-8
Int идёт к минус бесконечности и даёт -8; Fix дал бы -7.
<?php
declare(strict_types=1);
$price = -0.2;
$bucket = variant_int($price);
echo (int) $bucket, "\n";
Ожидаемый результат
-1
Разница на отрицательных значениях важна для координат и интервалов; для денег обычно используют целые минимальные единицы.
Рядом: variant_fix, variant_round.
Вернуться к карте задач или полному дереву COM.
variant_modВозвращает остаток от Automation-деления.
function variant_mod(mixed $left, mixed $right): variantВозвращает остаток от Automation-деления.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
Возвращает остаток Automation-деления. Используют вместе с variant_idiv для разбиения количества на полные группы и остаток. Знак, coercion strings/bools и overflow относятся к Automation, поэтому внешние operands сначала приводят/проверяют; нулевой divisor даёт com_exception.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_mod(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$rest = variant_mod(17, 5);
echo (int) $rest, "\n";
Ожидаемый результат
2
Остаток показывает неполную коробку после целого деления.
<?php
declare(strict_types=1);
$queue = variant_mod(104, 3);
echo 'queue: ', (int) $queue, "\n";
Ожидаемый результат
queue: 2
Остаток используют для циклического распределения, если такая политика действительно допустима.
Рядом: variant_idiv.
Вернуться к карте задач или полному дереву COM.
variant_mulУмножает два Automation-значения.
function variant_mul(mixed $left, mixed $right): variantУмножает два Automation-значения.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
| Случай | Официальное правило VarMul |
|---|---|
| string/date/character/bool либо числа | Automation multiplication |
| хотя бы один NULL | NULL |
| оба EMPTY | empty string |
Официальная страница отдельно утверждает преобразование bool «-1 для false, 0 для true»; это расходится с обычным представлением VARIANT_BOOL и требует проверки конкретного runtime. Поэтому на bool не строят финансовую арифметику: передают явный numeric VARTYPE.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_mul(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$subtotal = variant_mul(490, 3);
echo (int) $subtotal, "\n";
Ожидаемый результат
1470
Новый variant содержит 1470.
<?php
declare(strict_types=1);
$price = new variant(2500, VT_I4);
$discounted = variant_mul($price, 0.9);
echo number_format((float) $discounted, 2, '.', ''), "\n";
Ожидаемый результат
2250.00
Для реальных денег точность VT_R8 может быть недостаточна; храните копейки целым либо используйте decimal-договор COM.
Рядом: variant_div, variant_pow.
Вернуться к карте задач или полному дереву COM.
variant_negВозвращает Automation-значение с противоположным знаком.
function variant_neg(mixed $value): variantВозвращает Automation-значение с противоположным знаком.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
Возвращает новый variant с противоположным числовым знаком; исходный variant остаётся прежним. Несмотря на старое официальное краткое описание «logical negation», это арифметический VarNeg и отличается от bitwise variant_not.
| Параметр | Что передаём и зачем |
|---|---|
$value | PHP scalar/null либо variant/COM/DOTNET по правилам OLE Automation. Допустимый смысл зависит от операции. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_neg(mixed $value): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$debt = variant_neg(1250);
echo (int) $debt, "\n";
Ожидаемый результат
-1250
variant_neg возвращает новый variant и не меняет исходный аргумент.
<?php
declare(strict_types=1);
$charge = new variant(490, VT_I4);
$reversal = variant_neg($charge);
echo "charge: " . (int) $charge . ", reversal: " . (int) $reversal . "\n";
Ожидаемый результат
charge: 490, reversal: -490
Отмена операции представлена отдельным значением; исходная сумма остаётся положительной.
Рядом: variant_abs.
Вернуться к карте задач или полному дереву COM.
variant_notИнвертирует логическое значение или битовую маску.
function variant_not(mixed $value): variantИнвертирует логическое значение или битовую маску.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
NOT инвертирует каждый bit Automation-значения; для bool меняет true/false, для integer создаёт complement, NULL остаётся NULL. Это не отрицание объекта и не проверка «пусто ли». Чтобы убрать конкретные flags, обычно применяют AND с подходящей инвертированной маской и фиксируют ширину типа.
| Параметр | Что передаём и зачем |
|---|---|
$value | PHP scalar/null либо variant/COM/DOTNET по правилам OLE Automation. Допустимый смысл зависит от операции. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_not(mixed $value): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$inverted = variant_not(5);
echo (int) $inverted, "\n";
Ожидаемый результат
-6
Automation применяет побитовое NOT; результат зависит от целочисленного представления.
<?php
declare(strict_types=1);
$enabled = new variant(true, VT_BOOL);
$disabled = variant_not($enabled);
echo (bool) $disabled ? "on\n" : "off\n";
Ожидаемый результат
off
Для bool операция читается как логическое отрицание; тип входа лучше задавать явно.
Рядом: variant_and, variant_or.
Вернуться к карте задач или полному дереву COM.
variant_orОбъединяет логические значения или битовые флаги операцией OR.
function variant_or(mixed $left, mixed $right): variantОбъединяет логические значения или битовые флаги операцией OR.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
| left | right | официальный результат |
|---|---|---|
| true | true / false / null | true / true / true |
| false | true / false / null | true / false / null |
| null | true / false / null | true / null / null |
Это не short-circuit ||; оба operand уже являются значениями. Для integer OR объединяет bits обеих masks.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_or(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$flags = variant_or(0b0100, 0b0010);
echo (int) $flags, "\n";
Ожидаемый результат
6
OR включает все биты, присутствующие хотя бы в одной маске.
<?php
declare(strict_types=1);
$current = 0b1000;
$read = 0b0001;
$updated = variant_or($current, $read);
echo decbin((int) $updated), "\n";
Ожидаемый результат
1001
Десятичное значение переводится в двоичную строку только для объяснения.
Рядом: variant_and, variant_xor.
Вернуться к карте задач или полному дереву COM.
variant_powВозводит левое Automation-число в степень right.
function variant_pow(mixed $left, mixed $right): variantВозводит левое Automation-число в степень right.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
Возводит left в степень right через Automation VarPow. Дробные/отрицательные степени и большие результаты могут дать floating result, overflow или com_exception. Для денег сначала определяют математическую модель, затем отдельную стратегию округления.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_pow(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$areaFactor = variant_pow(3, 2);
echo (int) $areaFactor, "\n";
Ожидаемый результат
9
3 во второй степени даёт 9.
<?php
declare(strict_types=1);
$factor = variant_pow(1.1, 3);
echo number_format((float) $factor, 3, '.', ''), "\n";
Ожидаемый результат
1.331
Плавающее вычисление подходит для модели; финансовое округление требует отдельной политики.
Рядом: variant_mul.
Вернуться к карте задач или полному дереву COM.
variant_roundОкругляет Automation-число до заданного количества знаков.
function variant_round(mixed $value, int $decimals): ?variantОкругляет Automation-число до заданного количества знаков.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
Возвращает variant при успехе либо null на Automation failure без обязательного com_exception и не меняет исходное значение. На проверенной Windows 12.345 до двух знаков стало 12.34: это Automation/banker's rounding, а не обещание «всегда вверх при 5». Проверяйте null до числового cast; для invoice/tax policy используйте согласованный decimal algorithm и тесты границ.
| Параметр | Что передаём и зачем |
|---|---|
$value | PHP scalar/null либо variant/COM/DOTNET по правилам OLE Automation. Допустимый смысл зависит от операции. |
$decimals | Количество знаков для Automation-округления. Проверяйте отрицательные значения и банковское округление на целевой среде. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_round(mixed $value, int $decimals): ?variant |
| Результат | variant при успехе либо null при Automation failure без обязательного com_exception. В проверенной Windows применяется Automation/banker's rounding: 12.345 до двух знаков стало 12.34. |
| Ошибки и особые исходы | Неподдерживаемый Automation-тип может вернуть null без исключения; неправильный PHP-тип по-прежнему даёт TypeError. Проверяйте результат перед приведением к числу. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$rounded = variant_round(12.345, 2);
echo number_format((float) $rounded, 2, '.', ''), "\n";
Ожидаемый результат
12.34
Используется Automation-округление: проверенный результат 12.34 показывает округление половины к чётному. Пограничные значения проверяют на нужной версии Windows.
<?php
declare(strict_types=1);
$raw = variant_div(1000, 3);
$display = variant_round($raw, 2);
echo number_format((float) $display, 2, '.', ''), "\n";
$unsupported = variant_round(new variant([1], VT_ARRAY | VT_VARIANT), 2);
echo 'unsupported: ', get_debug_type($unsupported), "\n";
Ожидаемый результат
333.33
unsupported: null
Округление для вывода не заменяет стратегию денег. При неподдерживаемом SAFEARRAY VarRound завершается ошибкой Automation, но PHP-обёртка возвращает null без обязательного com_exception; это отдельная проверяемая ветка.
Рядом: variant_fix, variant_int.
Вернуться к карте задач или полному дереву COM.
variant_setМеняет значение существующего variant на месте и ничего не возвращает.
function variant_set(variant $variant, mixed $value): voidМеняет значение существующего variant на месте и ничего не возвращает.
Функция преобразует $value и заменяет содержимое существующего variant: новый object не создаётся, старое COM-value освобождается, return отсутствует. Все references на этот object увидят mutation. Это полезно для подготовленного out/in-out container, но неожиданно для кода, ожидающего immutable value.
После failure не угадывайте состояние container; создайте новый variant либо проверьте type/value. Для новой копии с другим типом используйте variant_cast.
| Параметр | Что передаём и зачем |
|---|---|
$variant | Экземпляр variant/com/dotnet. Для persistence объект должен дополнительно реализовать нужный IPersist-интерфейс. |
$value | PHP scalar/null либо variant/COM/DOTNET по правилам OLE Automation. Допустимый смысл зависит от операции. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_set(variant $variant, mixed $value): void |
| Результат | void; изменяется переданный объект variant. |
| Ошибки и особые исходы | Невозможное Automation-преобразование выбрасывает com_exception; неправильный PHP-тип — TypeError. Операция мутирует объект, поэтому после ошибки состояние не угадывают. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$status = new variant('draft', VT_BSTR);
variant_set($status, 'sent');
echo (string) $status, "\n";
echo variant_get_type($status), "\n";
Ожидаемый результат
sent
8
variant_set меняет объект на месте и возвращает void; тип при совместимом значении остаётся BSTR.
<?php
declare(strict_types=1);
$count = new variant(0, VT_I4);
variant_set($count, 7);
echo "processed: " . (int) $count . "\n";
Ожидаемый результат
processed: 7
Такая мутация встречается при подготовке параметра, который COM-метод изменит по ссылке.
Рядом: variant_set_type, variant_cast.
Вернуться к карте задач или полному дереву COM.
variant_set_typeМеняет VARTYPE и значение существующего variant на месте.
function variant_set_type(variant $variant, int $type): voidМеняет VARTYPE и значение существующего variant на месте.
Это in-place версия variant_cast: существующий container меняет VARTYPE и значение через Automation conversion, return — void. Другие references видят изменение. Непреобразуемый текст/overflow дают com_exception; только успешный return позволяет читать новое значение.
В adapter безопаснее преобразовать копию variant_cast, проверить результат и лишь затем заменить shared state, если mutation действительно нужна.
| Параметр | Что передаём и зачем |
|---|---|
$variant | Экземпляр variant/com/dotnet. Для persistence объект должен дополнительно реализовать нужный IPersist-интерфейс. |
$type | Одна VT_* константа либо составная маска (например VT_ARRAY | VT_VARIANT), поддерживаемая данной операцией. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_set_type(variant $variant, int $type): void |
| Результат | void; изменяется переданный объект variant, при невозможном преобразовании — com_exception. |
| Ошибки и особые исходы | Невозможное Automation-преобразование выбрасывает com_exception; неправильный PHP-тип — TypeError. Операция мутирует объект, поэтому после ошибки состояние не угадывают. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$value = new variant('42', VT_BSTR);
variant_set_type($value, VT_I4);
echo variant_get_type($value), "\n";
echo (int) $value, "\n";
Ожидаемый результат
3
42
В отличие от variant_cast функция меняет тот же объект и возвращает void.
<?php
declare(strict_types=1);
$value = new variant('forty-two', VT_BSTR);
try {
variant_set_type($value, VT_I4);
echo "converted\n";
} catch (com_exception $error) {
echo "still type: " . variant_get_type($value) . "\n";
printf("failed: 0x%08X\n", $error->getCode() & 0xFFFFFFFF);
}
Ожидаемый результат
still type: 8
failed: 0x80020005
После ошибки нельзя предполагать новый тип; код явно проверяет состояние.
Рядом: variant_cast, variant_get_type.
Вернуться к карте задач или полному дереву COM.
variant_subВычитает right из left по Automation-правилам.
function variant_sub(mixed $left, mixed $right): variantВычитает right из left по Automation-правилам.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
| Случай | Официальное правило VarSub |
|---|---|
| strings/characters/numbers, которые Automation может привести | Subtraction |
| хотя бы один NULL | NULL |
| оба EMPTY | empty string |
Порядок operands критичен: остаток/дефицит удобно отличать по знаку результата.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_sub(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$available = variant_sub(12, 5);
echo (int) $available, "\n";
Ожидаемый результат
7
Результат — новый variant со значением 7.
<?php
declare(strict_types=1);
$stock = new variant(3, VT_I4);
$requested = 5;
$after = variant_sub($stock, $requested);
echo (int) $after < 0 ? "shortage: " . abs((int) $after) . "\n" : "enough\n";
Ожидаемый результат
shortage: 2
Отрицательный остаток превращается в понятный бизнес-результат.
Рядом: variant_add, variant_abs.
Вернуться к карте задач или полному дереву COM.
variant_xorОставляет различающиеся логические/битовые позиции операцией XOR.
function variant_xor(mixed $left, mixed $right): variantОставляет различающиеся логические/битовые позиции операцией XOR.
Операция принимает PHP int, float, string, bool, null либо COM/VARIANT/DOTNET. Scalar сначала преобразуется в VARIANT по правилам конструктора; у COM/DOTNET берётся default property. Поэтому результат может отличаться от одноимённого оператора PHP и обязан проверяться на типах, которые реально возвращает внешний компонент.
| left | right | официальный результат |
|---|---|---|
| true | true / false | false / true |
| false | true / false | true / false |
| null | null | null |
Для masks XOR оставляет различающиеся bits и может переключить flag повторным применением. Для «включить» flag используйте OR, потому что XOR при повторе выключит его.
| Параметр | Что передаём и зачем |
|---|---|
$left | Левый операнд: PHP scalar/null или variant/COM/DOTNET, приводимый по Automation-правилам. |
$right | Правый операнд; порядок существенен для вычитания, деления, сравнения, IMP и конкатенации. |
| Часть договора | Что получает программа |
|---|---|
| Сигнатура | function variant_xor(mixed $left, mixed $right): variant |
| Результат | Новый variant с результатом Automation-операции. PHP scalar получают явным приведением; точный VARTYPE проверяют variant_get_type(). |
| Ошибки и особые исходы | Несовместимые VARTYPE, overflow, деление на ноль и иные Automation-ошибки дают com_exception с HRESULT; неправильный PHP-тип — TypeError. |
| Версии | (PHP 5, PHP 7, PHP 8); проверено на PHP 8.5.11 NTS x64. |
| Платформа | Только Windows; extension php_com_dotnet.dll; реальный объект дополнительно зависит от установленного/зарегистрированного COM-сервера. |
<?php
declare(strict_types=1);
$difference = variant_xor(0b1100, 0b1010);
echo decbin((int) $difference), "\n";
Ожидаемый результат
110
XOR показывает позиции, в которых маски отличаются.
<?php
declare(strict_types=1);
$flags = 0b1000;
$verbose = 0b0010;
$toggled = variant_xor($flags, $verbose);
echo decbin((int) $toggled), "\n";
Ожидаемый результат
1010
Повторный XOR тем же флагом вернёт исходное состояние; для явного включения используют OR.
Рядом: variant_and, variant_or.
Вернуться к карте задач или полному дереву COM.
Источники и обсуждения
- PHP Manual: COM and .Net (Windows)
- PHP Manual: Installing/Configuring
- PHP Manual: Requirements
- PHP Manual: Installation
- PHP Manual: Runtime Configuration
- PHP Manual: Predefined Constants
- PHP Manual: Errors and error handling
- PHP Manual: Examples
- PHP Manual: For Each
- PHP Manual: Arrays and Array-style COM properties
- PHP Manual: The com class
- PHP Manual: com::__construct
- PHP Manual: The dotnet class
- PHP Manual: dotnet::__construct
- PHP Manual: variant class
- PHP Manual: variant::__construct
- PHP Manual: The COMPersistHelper class
- PHP Manual: COMPersistHelper::__construct
- PHP Manual: COMPersistHelper::GetCurFileName
- PHP Manual: COMPersistHelper::GetMaxStreamSize
- PHP Manual: COMPersistHelper::InitNew
- PHP Manual: COMPersistHelper::LoadFromFile
- PHP Manual: COMPersistHelper::LoadFromStream
- PHP Manual: COMPersistHelper::SaveToFile
- PHP Manual: COMPersistHelper::SaveToStream
- PHP Manual: The com_exception class
- PHP Manual: The com_safearray_proxy class
- PHP Manual: COM Functions
- PHP Manual: com_create_guid
- PHP Manual: com_event_sink
- PHP Manual: com_get_active_object
- PHP Manual: com_load_typelib
- PHP Manual: com_message_pump
- PHP Manual: com_print_typeinfo
- PHP Manual: variant_abs
- PHP Manual: variant_add
- PHP Manual: variant_and
- PHP Manual: variant_cast
- PHP Manual: variant_cat
- PHP Manual: variant_cmp
- PHP Manual: variant_date_from_timestamp
- PHP Manual: variant_date_to_timestamp
- PHP Manual: variant_div
- PHP Manual: variant_eqv
- PHP Manual: variant_fix
- PHP Manual: variant_get_type
- PHP Manual: variant_idiv
- PHP Manual: variant_imp
- PHP Manual: variant_int
- PHP Manual: variant_mod
- PHP Manual: variant_mul
- PHP Manual: variant_neg
- PHP Manual: variant_not
- PHP Manual: variant_or
- PHP Manual: variant_pow
- PHP Manual: variant_round
- PHP Manual: variant_set
- PHP Manual: variant_set_type
- PHP Manual: variant_sub
- PHP Manual: variant_xor