psalm / psalm-plugin-symfony

Psalm Plugin for Symfony
MIT License
228 stars 53 forks source link

UnitEnum cannot be cast to string #272

Closed bakhtiyor closed 2 years ago

bakhtiyor commented 2 years ago

I have a variable declared in config/services.yaml

parameters:
    login_url: '%env(string:APP_FRONTEND_DOMAIN)%'

I am accessing it in my controller like this:

$loginUrl = (string) ($this->getParameter('login_url') ?? "");

Everything works fine, but psalm is giving following error:

ERROR: PossiblyInvalidCast - src/Controller/MyController.php:57:31 - UnitEnum cannot be cast to string (see https://psalm.dev/190)
$loginUrl = (string) ($this->getParameter('login_url') ?? "");

Any suggestions, how to fix it, please?

Duplicated from my stackoverflow question: https://stackoverflow.com/questions/73209831/unitenum-cannot-be-cast-to-string

seferov commented 2 years ago

@bakhtiyor thanks for submitting the issue.

Here is the related part on Symfony side:

    /**
     * Gets a service container parameter.
     *
     * @return array|bool|string|int|float|\UnitEnum|null
     *
     * @throws ParameterNotFoundException if the parameter is not defined
     */
    public function get(string $name);

According to the code, it means login_url can be array, bool, etc. including \UnitEnum which cannot be casted to string. Thus the error is correct actually.

On the other hand, I know that you specified the type on parameters with environment variable which should be string. To be able to infer the type of parameter, the plugin needs to analyze compiled container XML (assuming that you already configured it) which is currently missing.

For now, you can rewrite it to tackle the error:

$loginUrl = $this->getParameter('login_url');
$loginUrl = is_string($loginUrl) ? $loginUrl : '';
zmitic commented 2 years ago

You can do this instead:

class SomeController
{
    public function yourMethod(
        #[AutoWire('%env(string:APP_FRONTEND_DOMAIN)%')]
        private string $loginUrl,
    )
    {
        // do something with $loginUrl
    }
}

The better approach is this, although I didn't test it; should work anyway:

    public function yourMethod(
        #[AutoWire('%login_url%')]
        private string $loginUrl,
    )

Documentation here: https://symfony.com/blog/new-in-symfony-6-1-service-autowiring-attributes

bakhtiyor commented 2 years ago

@seferov thanks for your detail explanation and support.