Skip to content

False type_mismatch_argument for several PHP resource-to-object migrated handles #164

@alexchexes

Description

@alexchexes

PHPantom version

phpantom_lsp 0.8.0-dirty

Installation method

Pre-built binary from GitHub Releases

Operating system

Windows x86_64

Editor

VS Code

Bug description

PHPantom applies PHP 8.1+ parameter types for several migrated extension handles, but keeps the old pre-migration return types for the corresponding factory/open functions.

Expected: after checking for false, the handle should be treated as the PHP 8.1+ object type.

Actual: PHPantom reports type_mismatch_argument, usually with resource|false as the argument type.

Verified affected cases:

  • finfo_open() -> finfo_file() / finfo_close()
  • imap_open() -> imap_search() / imap_close()
  • ftp_connect() -> ftp_pwd() / ftp_close()
  • ldap_connect() -> ldap_bind() / ldap_close()
  • pg_connect() -> pg_query() / pg_close()
  • pg_query() -> pg_fetch_assoc() / pg_free_result()
  • pspell_new() -> pspell_check()

Steps to reproduce

  1. Use PHP 8.1 or newer in .phpantom.toml.
  2. Create a PHP file with:
<?php

function check_finfo(): void
{
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    if ($finfo === false) {
        throw new RuntimeException('finfo_open failed');
    }

    finfo_file($finfo, __FILE__);
    finfo_close($finfo);
}

function check_imap(): void
{
    $imap = imap_open('{imap.example.com:993/imap/ssl}INBOX', 'user@example.com', 'password');
    if ($imap === false) {
        throw new RuntimeException('imap_open failed');
    }

    imap_search($imap, 'UNSEEN SINCE 2023-01-10');
    imap_close($imap);
}

function check_ftp(): void
{
    $ftp = ftp_connect('ftp.example.com');
    if ($ftp === false) {
        throw new RuntimeException('ftp_connect failed');
    }

    ftp_pwd($ftp);
    ftp_close($ftp);
}

function check_ldap(): void
{
    $ldap = ldap_connect('ldap://ldap.example.com');
    if ($ldap === false) {
        throw new RuntimeException('ldap_connect failed');
    }

    ldap_bind($ldap);
    ldap_close($ldap);
}

function check_pgsql(): void
{
    $connection = pg_connect('host=localhost dbname=test');
    if ($connection === false) {
        throw new RuntimeException('pg_connect failed');
    }

    pg_query($connection, 'select 1');
    pg_close($connection);
}

function check_pgsql_result(): void
{
    $connection = pg_connect('host=localhost dbname=test');
    if ($connection === false) {
        throw new RuntimeException('pg_connect failed');
    }

    $result = pg_query($connection, 'select 1');
    if ($result === false) {
        throw new RuntimeException('pg_query failed');
    }

    pg_fetch_assoc($result);
    pg_free_result($result);
}

function check_pspell(): void
{
    $dictionary = pspell_new('en');
    if ($dictionary === false) {
        throw new RuntimeException('pspell_new failed');
    }

    pspell_check($dictionary, 'word');
}
  1. Run diagnostics.

PHPantom reports:

Argument 1 ($finfo) expects finfo, got resource|false
Argument 1 ($imap) expects IMAP\Connection, got resource|false
Argument 1 ($ftp) expects FTP\Connection, got resource|false
Argument 1 ($ldap) expects LDAP\Connection, got resource|false
Argument 1 ($connection) expects PgSql\Connection, got resource|false
Argument 1 ($connection) expects PgSql\Connection|null, got resource|false
Argument 1 ($result) expects PgSql\Result, got resource|false
Argument 1 ($dictionary) expects PSpell\Dictionary, got int|false

Error output or panic trace


.phpantom.toml

[php]
version = "8.4"

Additional context

This looks like version-aware stub handling is applied to callee parameter types, but not consistently applied to the return types of the corresponding handle-creation functions.

I also checked similar short examples for curl_init(), imagecreatetruecolor(), xml_parser_create(), and socket_create() in the same repro file. Those did not trigger this mismatch.

Binary source

Originally observed in the VS Code extension v0.5.0. Reproduced with the extension-cached GitHub Releases binary

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions