Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 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-библиотека или отдельный сервис.

Путь одного вызова
  1. PHP создаёт new COM(ProgID) или получает уже активный объект
  2. Windows registry находит CLSID и запускает/подключает DLL либо EXE под учётной записью процесса
  3. PHP упаковывает аргументы в VARIANT/BSTR/SAFEARRAY и вызывает IDispatch
  4. COM-сервер выполняет работу; результат/HRESULT возвращается через ту же границу
  5. PHP преобразует безопасные значения или оставляет объект variant
Если нужноРазумный первый выборПочему
Старый локальный COM/OLE API на WindowsCOM/DOTNETКомпонент уже предоставляет IDispatch/type library.
Прочитать/создать XLSX без ExcelPhpSpreadsheet/Open XMLНет desktop-процесса, DCOM и интерактивных диалогов.
Вызвать современный .NET сервисHTTP/gRPC/CLI workerdotnet class не является универсальным .NET host.
Связать PHP с удалённым сервисомHTTP APIКонтракт переносим, наблюдаем и изолирован от desktop session.
Много тяжёлых Office-задачОчередь + отдельный Windows workerWeb-request не ждёт UI-приложение и может повторить job.

Установка и конфигурация: что должно совпасть до первой строки кода

Рабочая среда — это комплект: Windows + точная PHP-сборка + совместимый php_com_dotnet.dll + выбранный SAPI + зарегистрированный COM-сервер. Проверка только строки extension=... недостаточна.

Проверка среды от общего к конкретному
  1. PHP_OS_FAMILY === 'Windows' и нужная разрядность PHP
  2. extension_loaded('com_dotnet') в том же CLI, IIS FastCGI, Apache или отдельном Windows-worker
  3. Нужный ProgID зарегистрирован для этой разрядности и пользователя
  4. Безопасный smoke-test создаёт объект и вызывает read-only/in-memory метод
  5. Production worker имеет явные timeout, очередь, журнал HRESULT и cleanup

Локально эта ветка проверена отдельной PHP 8.5.11 NTS x64-сборкой; общий php.ini и WordPress не менялись. Команды диагностики находятся в следующем разделе.

Требования: Windows, регистрация компонента и граница .NET Framework

СлойМинимумКак проявляется отказ
PHPWindows build и загруженный php_com_dotnet.dllClass COM/DOTNET not found или extension_loaded=false.
COM componentProgID/CLSID/type library зарегистрированы для подходящей bitnessInvalid 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.

Собрать корзину в Scripting.Dictionary без OfficeСамостоятельный пример
<?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_dcom0INI_SYSTEM. Разрешает удалённый DCOM client. По умолчанию и в тестах выключено.
com.autoregister_typelib0INI_ALL. Автоматически регистрировать constants из typelib объекта, если это возможно.
com.autoregister_verbose0INI_ALL. Сообщать ошибки автообнаружения typelib через PHP error mechanism.
com.autoregister_casesensitive1INI_ALL. Историческая политика регистра; case-insensitive constants больше не поддерживаются современным PHP.
com.code_pageпусто → CP_ACPINI_ALL. Глобальная default codepage; переносимее передать codepage в constructor.
com.dotnet_versionv4.0.30319INI_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_SERVER1Код сервера — DLL внутри процесса PHP.
CLSCTX_INPROC_HANDLER2In-process handler для клиентской стороны COM.
CLSCTX_LOCAL_SERVER4COM-сервер — отдельный EXE на том же компьютере.
CLSCTX_REMOTE_SERVER16Сервер на другом компьютере через DCOM; в локальной среде запрещён.
CLSCTX_SERVER21Маска INPROC_SERVER | LOCAL_SERVER | REMOTE_SERVER.
CLSCTX_ALL23Все class contexts, включая INPROC_HANDLER.

Тип данных Automation: VT_*

КонстантаПроверенное значениеКак читать
VT_NULL1Automation NULL (не то же самое, что VT_EMPTY).
VT_EMPTY0Значения нет; размер полезных данных нулевой.
VT_UI117Беззнаковое 8-битное целое/байт.
VT_I116Знаковое 8-битное целое.
VT_UI218Беззнаковое 16-битное целое.
VT_I22Знаковое 16-битное целое.
VT_UI419Беззнаковое 32-битное целое.
VT_I43Знаковое 32-битное целое.
VT_R4432-битное IEEE floating point.
VT_R8564-битное IEEE floating point.
VT_BOOL11VARIANT_BOOL; true обычно хранится как -1, false как 0.
VT_ERROR10HRESULT/SCODE как значение VARIANT.
VT_CY6Currency: 64-битное целое, масштабированное на 10 000.
VT_DATE7Число дней от эпохи OLE Automation; это не Unix timestamp.
VT_BSTR8Unicode BSTR с длиной, управляемой Automation.
VT_DECIMAL14Automation DECIMAL.
VT_UNKNOWN13Указатель IUnknown.
VT_DISPATCH9Указатель IDispatch, через который PHP вызывает динамический API.
VT_VARIANT12Вложенный VARIANT; как флаг by-reference используется вместе с VT_BYREF.
VT_INT22Platform int Automation, в официальном описании эквивалент VT_I4.
VT_UINT23Platform unsigned int Automation, эквивалент VT_UI4.
VT_ARRAY8192Битовый флаг SAFEARRAY; объединяется OR с типом элементов.
VT_BYREF16384Битовый флаг ссылки на данные; объединяется OR с базовым VARTYPE.
VT_UI821Беззнаковое 64-битное целое; доступность зависит от x64.
VT_I820Знаковое 64-битное целое; доступность зависит от x64.

Кодовые страницы: CP_*

КонстантаПроверенное значениеКак читать
CP_ACP0Системная ANSI code page.
CP_MACCP2Macintosh code page.
CP_OEMCP1Системная OEM code page.
CP_UTF765000UTF-7; почти никогда не нужен для современного PHP-приложения.
CP_UTF865001UTF-8 — явный выбор для PHP-строк в UTF-8.
CP_SYMBOL42Symbol translations.
CP_THREAD_ACP3ANSI code page текущего thread.

Сравнение и locale

КонстантаПроверенное значениеКак читать
VARCMP_LT0left меньше right.
VARCMP_EQ1left равно right.
VARCMP_GT2left больше right.
VARCMP_NULL3Хотя бы один операнд NULL.
LOCALE_SYSTEM_DEFAULT2048Locale операционной системы.
LOCALE_NEUTRAL0Neutral locale; официальный manual советует обычно использовать system default.
NORM_IGNORECASE1Игнорировать регистр при variant_cmp.
NORM_IGNORENONSPACE2Игнорировать nonspacing characters.
NORM_IGNORESYMBOLS4Игнорировать символы.
NORM_IGNOREWIDTH131072Игнорировать различия ширины символов.
NORM_IGNOREKANATYPE65536Игнорировать различия Kana type.
NORM_IGNOREKASHIDAне определена в PHP 8.5.11 x64Игнорировать Arabic kashida; зависит от Windows library и отсутствует в проверенной сборке.

HRESULT для ветвления

КонстантаПроверенное значениеКак читать
DISP_E_DIVBYZERO2147614738HRESULT деления на ноль.
DISP_E_OVERFLOW2147614730HRESULT невозможного представления/overflow.
DISP_E_BADINDEX2147614731HRESULT отсутствующего индекса массива.
DISP_E_PARAMNOTFOUND2147614724HRESULT неизвестного parameter ID.
MK_E_UNAVAILABLE2147746275HRESULT: объект отсутствует в Running Object Table.
Как разобрать type 8204
  1. 8204 & VT_ARRAY не равно нулю → это SAFEARRAY
  2. 8204 & ~VT_ARRAY даёт 12 → элементы помечены VT_VARIANT
  3. Равенство $type === VT_VARIANT было бы ложным и потеряло бы флаг массива
Разобрать составной тип SAFEARRAYСамостоятельный пример
<?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 полезен оператору, но хрупок для программного сравнения.

Обработка отказа
  1. try охватывает минимальный внешний вызов
  2. catch com_exception получает HRESULT и внутреннее сообщение
  3. Приложение переводит известный код в свою политику: retry, fallback, invalid input или unavailable
  4. Журнал сохраняет operation/ProgID/HRESULT/duration; HTTP-пользователь получает безопасное доменное сообщение
  5. finally закрывает документ/соединение и отпускает ссылку на внешний процесс
Отличить отсутствие запущенного объекта от ошибки PHPСамостоятельный пример
<?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 Automationcom::__constructScripting.Dictionary, array-style Item, SAFEARRAY Keys.
XML через установленный MSXMLcom::__constructCOMPersistHelper::SaveToStream для bridge к PHP stream.
COM-visible коллекция .NET Frameworkdotnet::__constructПроверить IDispatch каждого method/property.
Точный VARTYPE для аргументаvariant::__constructvariant_get_type и variant_cast.
События Windows-компонентаcom_event_sinkcom_message_pump и bounded loop.
Понять неизвестное динамическое APIcom_print_typeinfocom_load_typelib + vendor docs.
Сохранить/загрузить через PHP streamCOMPersistHelper::SaveToStreamCOMPersistHelper::LoadFromStream.
Прочитать XML-заказ через MSXML без файлов и сетиСамостоятельный пример
<?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-visible коллекцию .NET FrameworkСамостоятельный пример
<?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.

Одна итерация
  1. COM-коллекция/SAFEARRAY сообщает следующий VARIANT
  2. Расширение преобразует безопасный scalar или оставляет variant/com
  3. PHP присваивает значение переменной foreach
  4. Цикл выполняет тело и просит следующий элемент до конца enumerator
Создать SAFEARRAY вариантов и пройти его через foreachСамостоятельный пример
<?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/несколько индексов.
Собрать корзину в Scripting.Dictionary без OfficeСамостоятельный пример
<?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 явно задаёт границу строк. Видимый результат — одна позиция корзины и обновлённое количество.

Разобрать составной тип SAFEARRAYСамостоятельный пример
<?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_nameProgID/CLSID/moniker. ProgID читабелен, CLSID точнее фиксирует класс.
server_namenull/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, если автоматического обнаружения недостаточно.
Собрать корзину в Scripting.Dictionary без OfficeСамостоятельный пример
<?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 остаются ограничениями.

Использовать COM-visible коллекцию .NET FrameworkСамостоятельный пример
<?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.

Увидеть границу: создать тип можно, вызвать его API — не всегдаСамостоятельный пример
<?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 Frameworkdotnet допустим с 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 к COM и обратно
  1. PHP scalar/array получает VARTYPE через явный constructor или автоматическое правило
  2. IDispatch получает VARIANT/SAFEARRAY/BSTR
  3. COM method возвращает VARIANT
  4. Без потери PHP получает scalar; иначе остаётся объект variant
  5. Код проверяет variant_get_type и делает осознанное cast/set_type
Передать COM точное 32-битное целоеСамостоятельный пример
<?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. Это нужно, когда сервер различает целые разных размеров.

Создать SAFEARRAY вариантов и пройти его через foreachСамостоятельный пример
<?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Практическая граница
IPersistFileGetCurFileName, LoadFromFile, SaveToFileCOM-компонент сам открывает путь; path/open_basedir false отделяйте от COM exception, а null-успех PHP 8.5 подтверждайте состоянием.
IPersistStreamGetMaxStreamSize, LoadFromStream, SaveToStreamPHP владеет stream; bound LoadFromStream возвращает true, unbound — загруженный dispatch object.
IPersistStreamInitInitNew и stream methodsПозволяет инициализировать новое состояние, если реализация это поддерживает.
Сохранить XML в php://temp и загрузить в новый 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 не пишет на диск.

Загрузить документ через IPersistFileСамостоятельный пример
<?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 нельзя.

Поймать E_NOTIMPL у MSXMLСамостоятельный пример
<?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.

Различить неизвестный ProgID и неактивный серверСамостоятельный пример
<?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.
0x80020005Automation 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.

Почему появляется proxy
  1. COM property возвращает SAFEARRAY с несколькими измерениями или требует несколько индексов
  2. Первый индекс ещё не выбирает scalar
  3. Расширение сохраняет COM array + уже выбранные индексы в com_safearray_proxy
  4. Следующий индекс завершает доступ; полученное значение снова проходит VARIANT conversion
Создать SAFEARRAY вариантов и пройти его через foreachСамостоятельный пример
<?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: строки, сравнение и даты

VARIANT: логика и биты

Persistence

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

com::__constructСоздаёт или связывает PHP с OLE Automation-объектом по ProgID/CLSID/moniker.

(PHP 4 > 4.1.0, PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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.
arrayCase-sensitive ключи Server, Username, Password, Domain, Flags. Username и Password указывают только вместе; credentials не помещают в код/лог. Официальный договор отдельно указывает: эта форма создания не управляется com.allow_dcom, даже если Server опущен.
FlagsOR-маска 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_nameProgID, CLSID или moniker установленного доверенного COM-компонента. Не принимайте значение напрямую от HTTP-клиента.
$server_namenull/пустое значение для локального стандартного сервера; имя удалённого сервера относится к 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-сервера.
Собрать корзину в Scripting.Dictionary без OfficeСамостоятельный пример
<?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 явно задаёт границу строк. Видимый результат — одна позиция корзины и обновлённое количество.

Прочитать XML-заказ через MSXML без файлов и сетиСамостоятельный пример
<?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.

(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8) · COM/DOTNET (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-сервера.
Использовать COM-visible коллекцию .NET FrameworkСамостоятельный пример
<?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.

Увидеть границу: создать тип можно, вызвать его API — не всегдаСамостоятельный пример
<?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 и кодовой страницей.

(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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.

ПараметрЧто передаём и зачем
$valuePHP 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-сервера.
Передать COM точное 32-битное целоеСамостоятельный пример
<?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. Это нужно, когда сервер различает целые разных размеров.

Создать SAFEARRAY вариантов и пройти его через foreachСамостоятельный пример
<?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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Связать helper с COM-объектом, который умеет сохраняться в streamСамостоятельный пример
<?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-интерфейсы реализует компонент.

Отделить успешное создание helper от отсутствующего 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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Обработать отсутствие IPersistFileСамостоятельный пример
<?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.

Получить имя у компонента с IPersistFileФрагмент или команда
// $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 для сохранения.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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 всегда запишет ноль байтов.

Поймать E_NOTIMPL у MSXMLСамостоятельный пример
<?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 сбросить объект в начальное состояние.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Проверить, что именно компонент сделал после InitNewСамостоятельный пример
<?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.

Не считать IDispatch доказательством поддержки InitNewСамостоятельный пример
<?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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Загрузить документ через IPersistFileСамостоятельный пример
<?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 нельзя.

Разделить запрет пути и отказ COM-интерфейсаСамостоятельный пример
<?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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Сохранить XML в php://temp и загрузить в новый 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 не пишет на диск.

Создать dispatch из 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 и управляет текущей файловой привязкой.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-компонента.
$remembertrue меняет текущий файл объекта; 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-сервера.
Сохранить копию через IPersistFileСамостоятельный пример
<?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; успех доказывают отсутствие исключения и реально созданный непустой файл.

Разделить запрет пути и отказ COM-интерфейсаСамостоятельный пример
<?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-компонента к выбранному пути.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Сохранить XML в php://temp и загрузить в новый 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 не пишет на диск.

Увидеть фактический результат SaveToStreamСамостоятельный пример
<?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-идентификаторов.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Создать идентификатор заказа и проверить форму GUIDСамостоятельный пример
<?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 случайный, поэтому проверяется полезное неизменное свойство — канонический формат с фигурными скобками.

Создать два разных correlation IDСамостоятельный пример
<?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-объекта.

(PHP 4 >= 4.2.0, PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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_objectPHP-объект с методами, названными как события выбранного 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-сервера.
Подключить обработчик к безопасному ADODB.Connection без открытия БДСамостоятельный пример
<?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.

Увидеть false при неверном dispinterfaceСамостоятельный пример
<?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 и не создаёт новый процесс.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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_idProgID или 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Самостоятельный пример
<?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, а не создаём новый объект.

Различить неизвестный ProgID и неактивный серверСамостоятельный пример
<?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-процессе.

(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Загрузить type library Scripting Runtime по файлуСамостоятельный пример
<?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 фактически игнорируется: нечувствительные к регистру константы больше не поддерживаются, поэтому пример использует значение по умолчанию без ложного обещания.

Обработать неизвестную type library без фатальной ошибкиСамостоятельный пример
<?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-обработчикам получить события.

(PHP 4 >= 4.2.0, PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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.

(PHP 4 >= 4.2.0, PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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_sinktrue просит описание 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-сервера.
Получить сигнатуры Scripting.Dictionary в буферСамостоятельный пример
<?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.

Попросить каркас outgoing eventsСамостоятельный пример
<?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-числа.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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.

ПараметрЧто передаём и зачем
$valuePHP 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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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
оба EMPTYInteger 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-сервера.
Сложить два числа AutomationСамостоятельный пример
<?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 над логическими значениями или битовыми масками.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-семантику.

leftrightофициальный результат
truetrue / false / nulltrue / false / null
falsetrue / false / nullfalse / false / false
nulltrue / false / nullnull / 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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Преобразовать BSTR с числом в VT_I4Самостоятельный пример
<?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-значения как строки.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Склеить две строки по правилам AutomationСамостоятельный пример
<?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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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_idWindows 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-сервера.
Сравнить два значения и прочитать VARCMP_*Самостоятельный пример
<?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-функций.

Сравнить строки и распознать сырой HRESULTСамостоятельный пример
<?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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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.

ПараметрЧто передаём и зачем
$timestampUnix 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-сервера.
Преобразовать Unix timestamp в VT_DATE и обратноСамостоятельный пример
<?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, поэтому пример проверяет тип и обратное преобразование.

Проверить нижнюю и верхнюю границы timestampСамостоятельный пример
<?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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Вернуть VT_DATE в Unix timestampСамостоятельный пример
<?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 и хранения в приложении.

Увидеть null при невозможном преобразованииСамостоятельный пример
<?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-деление с дробной частью.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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
хотя бы один NULLNULL
right EMPTY, left не EMPTYDISP_E_DIVBYZERO
left EMPTY, right не EMPTYDouble 0
оба EMPTYDISP_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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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Оставляет целую часть числа, двигаясь к нулю.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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. Выбирайте функцию по смыслу задачи, а не по названию «целая часть».

ПараметрЧто передаём и зачем
$valuePHP 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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Узнать точный VARTYPEСамостоятельный пример
<?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, а не вместо обычной модели данных приложения.

Разобрать составной тип SAFEARRAYСамостоятельный пример
<?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-деление.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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
хотя бы один NULLNULL
оба EMPTYDISP_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 (логическую импликацию).

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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 и обязан проверяться на типах, которые реально возвращает внешний компонент.

leftrightофициальный результат
truetrue / false / nulltrue / false / true
falsetrue / false / nulltrue / true / true
nulltrue / false / nulltrue / 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Возвращает целую часть с округлением вниз к минус бесконечности.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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.

ПараметрЧто передаём и зачем
$valuePHP 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-деления.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-значения.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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
хотя бы один NULLNULL
оба EMPTYempty 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-значение с противоположным знаком.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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.

ПараметрЧто передаём и зачем
$valuePHP 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Инвертирует логическое значение или битовую маску.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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 с подходящей инвертированной маской и фиксируют ширину типа.

ПараметрЧто передаём и зачем
$valuePHP 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; результат зависит от целочисленного представления.

Инвертировать VARIANT_BOOLСамостоятельный пример
<?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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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 и обязан проверяться на типах, которые реально возвращает внешний компонент.

leftrightофициальный результат
truetrue / false / nulltrue / true / true
falsetrue / false / nulltrue / false / null
nulltrue / false / nulltrue / 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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-число до заданного количества знаков.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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 и тесты границ.

ПараметрЧто передаём и зачем
$valuePHP 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-сервера.
Округлить variant до двух знаковСамостоятельный пример
<?php
declare(strict_types=1);

$rounded = variant_round(12.345, 2);
echo number_format((float) $rounded, 2, '.', ''), "\n";
Ожидаемый результат
12.34

Используется Automation-округление: проверенный результат 12.34 показывает округление половины к чётному. Пограничные значения проверяют на нужной версии Windows.

Подготовить сумму и проверить Automation failureСамостоятельный пример
<?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 на месте и ничего не возвращает.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-интерфейс.
$valuePHP 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-сервера.
Заменить значение существующего variantСамостоятельный пример
<?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.

Обновить out-подобный контейнерСамостоятельный пример
<?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 на месте.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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-сервера.
Преобразовать существующий variant в VT_I4Самостоятельный пример
<?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-правилам.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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
хотя бы один NULLNULL
оба EMPTYempty 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.

(PHP 5, PHP 7, PHP 8) · COM/DOTNET (Windows)

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 и обязан проверяться на типах, которые реально возвращает внешний компонент.

leftrightофициальный результат
truetrue / falsefalse / true
falsetrue / falsetrue / false
nullnullnull

Для 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.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.