Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uncaught UnexpectedValueException: $storage should not be null #11280

Open
gijs-blanken opened this issue Feb 13, 2025 · 5 comments
Open

Uncaught UnexpectedValueException: $storage should not be null #11280

gijs-blanken opened this issue Feb 13, 2025 · 5 comments

Comments

@gijs-blanken
Copy link

Hi everyone,

I'm running Psalm 6.5.1 and I'm running into an issue that was not present on the 5.20.0 branch when we were on PHP 8.3 (we are now on 8.4). I noticed a commit which supposedly fixed this error (#11232). But unfortunately it persists for me. I also tested the analyses with threads=1 and scan-threads=1 but to no avail. I'm not quite sure what extra context to add, please let me know!

Thanks in advance 😅

Uncaught UnexpectedValueException: $storage should not be null for App\Facades\Defuse::__callstatic in /www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Methods.php:1154
Stack trace:
#0 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php(528): Psalm\Internal\Codebase\Methods->getStorage(Object(Psalm\Internal\MethodIdentifier))
#1 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php(204): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer::handleNamedCall(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(PhpParser\Node\Identifier), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), Array, 'App\\Facades\\Def...', false, false, NULL)
#2 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php(230): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), Object(Psalm\Type\Atomic\TNamedObject), false, false, false, false, NULL)
#3 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(217): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), NULL)
#4 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(92): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), false, NULL, NULL, NULL, false)
#5 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php(237): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), false, NULL, NULL, NULL)
#6 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php(180): Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentsAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Array, Array, 'array_map', true, Object(Psalm\Context), Object(Psalm\Internal\Type\TemplateResult))
#7 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(318): Psalm\Internal\Analyzer\Statements\Expression\Call\FunctionCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\FuncCall), Object(Psalm\Context), Object(Psalm\Internal\Type\TemplateResult))
#8 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(92): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\FuncCall), Object(Psalm\Context), false, NULL, NULL, NULL, false)
#9 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php(161): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\FuncCall), Object(Psalm\Context))
#10 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(539): Psalm\Internal\Analyzer\Statements\ReturnAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Return_), Object(Psalm\Context))
#11 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(191): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Return_), Object(Psalm\Context), Object(Psalm\Context))
#12 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(508): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), Object(Psalm\Context), true)
#13 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1823): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), Object(Psalm\Context))
#14 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(447): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeClassMethod(Object(PhpParser\Node\Stmt\ClassMethod), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Internal\Analyzer\ClassAnalyzer), Object(Psalm\Context), Object(Psalm\Context))
#15 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(197): Psalm\Internal\Analyzer\ClassAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Context))
#16 /www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(1488): Psalm\Internal\Analyzer\FileAnalyzer->analyze()
#17 /www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(436): Psalm\Internal\Codebase\Analyzer::analysisWorker(Object(Psalm\Config), Object(Psalm\Progress\DebugProgress), '/www/app/Http/C...')
#18 /www/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(245): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1)
#19 /www/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(521): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1, false, true)
#20 /www/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php(392): Psalm\Internal\Analyzer\ProjectAnalyzer->check('/www/dev', true)
#21 /www/vendor/vimeo/psalm/psalm(9): Psalm\Internal\Cli\Psalm::run(Array)
#22 /www/vendor/bin/psalm(119): include('/www/vendor/vim...')
#23 {main}
(Psalm 6.5.1@3f17a6b24a2dbe543e21408c2b19108cf6a355ef crashed due to an uncaught Throwable)
Copy link

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

@danog
Copy link
Collaborator

danog commented Feb 13, 2025

Please manually search the entire codebases for classes named Defuse in the App\Facades namespace; it is possible you have two or more copies (including in the vendor folder).

If there is only one copy, please send the class here, removing private code as needed (reducing the code as long as the error is still longer reproducible)

@gijs-blanken
Copy link
Author

I searched for duplicates of the Defuse class in a App\Facades namespace. Unfortunately I didn't find any duplicates. I'll try and provide as much context now. This concerns a Laravel 11 project.

In config/app.php I have a class aliases pointing to \App\Facades\Defuse.php

'aliases' => [
       'Defuse' => \App\Facades\Defuse::class,
    ],

That point to the following class

<?php

namespace App\Facades;

use Illuminate\Support\Facades\Facade;

/**
 * @method static encryptFile($path, $path1, $decryptAccountKey)
 * @method static decrypt($value, ?\Defuse\Crypto\Key $key = null)
 */
class Defuse extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'DefuseEncryption';
    }
}

In my app\Providers\ServiceProvider.php

$this->app->singleton('DefuseEncryption', function ($_app) {
        return new EncryptionHelper();
});

I also have a DefuseEncryption.php trait class

<?php

namespace App\Traits;

use App\Facades\Defuse;
use Defuse\Crypto\Key as DefuseKey;
use Exception;

use function in_array;

/**
 *  @psalm-require-extends \Illuminate\Database\Eloquent\Model
 */
trait DefuseEncryption
{
    /**
     * Attempt decryption of object
     */
    public function decrypt(?DefuseKey $key = null): void
    {
        foreach ($this->encrypted_fields as $field) {
            try {
                $this->attributes[$field] = Defuse::decrypt($this->attributes[$field], $key);
            } catch (Exception) {
            }
        }
    }

    /**
     * Attempt encryption of object
     */
    public function encrypt(?DefuseKey $_key = null): void
    {
    }

    /**
     *  @psalm-assert-if-true value-of<self::$encrypted_fields> $fieldName
     *  @param non-empty-string $fieldName
     */
    public function isEncryptedField(string $fieldName): bool
    {
        return in_array($fieldName, $this->encrypted_fields);
    }
}

And the $storage should not be null error seems to originated from this section of code (found using --debug-by-line)

use App\Facades\Defuse;

    private function encryptCredentials(array $credentials): array
    {
        return array_map(Defuse::forceEncrypt(...), $credentials);
    }

Hopefully this helps pin-point this issue. I should mention that all of this code has been in the codebase for quite some time.

Thanks in advance @danog

@gijs-blanken
Copy link
Author

I managed to solve the error by adding the following annotations the App\Facades\Defuse.php file:

<?php

namespace App\Facades;

use Illuminate\Support\Facades\Facade;

/**
 * @method static bool encryptFile(string $inputFilename, string $outputFilename, ?\Defuse\Crypto\Key $key = null)
 * @method static bool decryptFile(string $inputFilename, string $outputFilename, ?\Defuse\Crypto\Key $key = null, ?int $accountId = null)
 * @method static string forceEncrypt(?string $message = null, ?\Defuse\Crypto\Key $key = null)
 * @method static string decrypt(?string $ciphertext, ?\Defuse\Crypto\Key $key = null)
 * @method static \Defuse\Crypto\Key decryptAccountKey(\App\Account $account)
 */
class Defuse extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'DefuseEncryption';
    }
}

@danog
Copy link
Collaborator

danog commented Feb 14, 2025

Reopening, will attempt to fix the exception directly

@danog danog reopened this Feb 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants