psalm icon indicating copy to clipboard operation
psalm copied to clipboard

UnresolvableInclude when using variable - second

Open jcvignoli opened this issue 1 year ago • 7 comments

Psalm can't follow the include and returns "UnresolvableInclude: Cannot resolve the given expression to a file path"

# file Settings.php
define ('ROOT', './src' );
class Settings {
   public const FOLDERS_PATH = [ 'templates' => ROOT . '/templates' ]
}

# file Other.php
$file_with_functions = Settings::FOLDERS_PATH['templates'] . '/myfile.php';
require_once $file_with_functions;

But this follows the include with no error:

# file Settings.php
define ('ROOT', './src' );

# file Other.php
$file_with_functions = ROOT . '/templates/myfile.php';
require_once $file_with_functions;

As a result, Psalm report that the functions I defined in my $file_with_functions file are not found...

jcvignoli avatar Feb 06 '24 16:02 jcvignoli

Hey @jcvignoli, can you reproduce the issue on https://psalm.dev? These will be used as phpunit tests when implementing the feature or fixing this bug.

psalm-github-bot[bot] avatar Feb 06 '24 16:02 psalm-github-bot[bot]

Another variation: https://psalm.dev/r/f05f18c046 (note the type of after_concat element)

Funny thing though, if you uncomment 'after' => self::AFTER - it works as expected: https://psalm.dev/r/003ab2965b

weirdan avatar Feb 06 '24 22:02 weirdan

I found these snippets:

https://psalm.dev/r/f05f18c046
<?php

class C {
    public const BEFORE = 'long time ago';
    public const ARR = [
        'before_concat' => self::BEFORE . ' in a galaxy far away',
        'after_concat' => self::AFTER . ' after the Sun explodes',
        // 'after' => self::AFTER,
    ];
    public const AFTER = 'in a distant future';
}
$_d = C::ARR;
/** @psalm-trace $_d */;
Psalm output (using commit 4b2c698):

INFO: Trace - 13:24 - $_d: array{after_concat: string, before_concat: 'long time ago in a galaxy far away'}
https://psalm.dev/r/003ab2965b
<?php

class C {
    public const BEFORE = 'long time ago';
    public const ARR = [
        'before_concat' => self::BEFORE . ' in a galaxy far away',
        'after_concat' => self::AFTER . ' after the Sun explodes',
        'after' => self::AFTER,
    ];
    public const AFTER = 'in a distant future';
}
$_d = C::ARR;
/** @psalm-trace $_d */;
Psalm output (using commit 4b2c698):

INFO: Trace - 13:24 - $_d: array{after: 'in a distant future', after_concat: 'in a distant future after the Sun explodes', before_concat: 'long time ago in a galaxy far away'}

psalm-github-bot[bot] avatar Feb 06 '24 22:02 psalm-github-bot[bot]

So the simplest test case is: https://psalm.dev/r/7fa1a03bd7

This is not a bug per se, as the inferred type is correct, just not as precise as it could be.

weirdan avatar Feb 07 '24 00:02 weirdan

I found these snippets:

https://psalm.dev/r/7fa1a03bd7
<?php
class A {
    const C = ['c is ' . self::B];
    const B = 'b';
}
$_ = A::C;
/** @psalm-check-type-exact $_ = list{'c is b'} */;
Psalm output (using commit 4b2c698):

ERROR: CheckType - 7:51 - Checked variable $_ = list{'c is b'} does not match $_ = list{string}

psalm-github-bot[bot] avatar Feb 07 '24 00:02 psalm-github-bot[bot]

@jcvignoli in your second example, how does Settings.php get loaded? In the first example autoload could load it (because the Settings class is referenced), but it's not immediately clear what's going on in the case you say is working.

weirdan avatar Feb 07 '24 02:02 weirdan

@weirdan I have an autoload file loading the class using spl_autoload_register(). Actually, the define ('ROOT', './src' ); is not in Settings.php, but in a different config file called before the autoload. I tried to simplify as much as possible, but the file Others.php is a class with a method, which basically does this:

protected function method( string $column ): array {
	require_once Settings::FOLDERS_PATH['templates'] . '/myfile.php';
	/** @psalm-suppress UndefinedFunction -- it actually exists! */
	$replacement_list = returnSelectedText( 'title' );
	return $replacement_list[ $column ];
}

Since myfile.php is not found (psalm throws a warning), it doesn't find the function returnSelectedText() in myfile.php. Should I replace Settings::FOLDERS_PATH['templates'] . '/myfile.php'; with ROOT . '/templates/myfile.php'; in the method, the function is found by psalm. Settings::FOLDERS_PATH['templates'] is a dirty class including various paths defined as follows:

class Settings {
	public const MAIN = [
		'ASSETS_PATH'          => ROOT . '/assets',
	];
	public const FOLDERS_PATH  = [
		'pics' => self::MAIN['ASSETS_PATH'] . '/pics',
		'js'   => self::MAIN['ASSETS_PATH'] . '/js',
		'css'  => self::MAIN['ASSETS_PATH'] . '/css',
		'templates'  => ROOT . '/templates',
		'class'  => ROOT . '/class',
	];
}

Hope it helps to grasp what's going on.

jcvignoli avatar Feb 07 '24 02:02 jcvignoli