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

Fatal error: Uncaught Error: Attempt to assign property "id" on null in phar:///usr/local/bin/terminus/src/Collections/TerminusCollection.php:95 #2254

Closed
labboy0276 opened this issue Nov 30, 2021 · 20 comments · May be fixed by #2268

Comments

@labboy0276
Copy link

labboy0276 commented Nov 30, 2021

Expected behavior

I am building out php8 support for lando / terminus and our pantheon recipe. I am using the 3.x beta1 version. I am trying to run any terminus command and have it work.

Actual behavior

Whenever I run any command (I ssh'd into lando appserver itself), I get this fun error:

Fatal error: Uncaught Error: Attempt to assign property "id" on null in phar:///usr/local/bin/terminus/src/Collections/TerminusCollection.php:95
Stack trace:
#0 phar:///usr/local/bin/terminus/src/Collections/TerminusCollection.php(81): Pantheon\Terminus\Collections\TerminusCollection->fetch()
#1 phar:///usr/local/bin/terminus/src/Hooks/Authorizer.php(32): Pantheon\Terminus\Collections\TerminusCollection->all()
#2 phar:///usr/local/bin/terminus/vendor/consolidation/annotated-command/src/Hooks/Dispatchers/InitializeHookDispatcher.php(47): Pantheon\Terminus\Hooks\Authorizer->ensureLogin(Object(Symfony\Component\Console\Input\ArgvInput), Object(Consolidation\AnnotatedCommand\AnnotationData))
#3 phar:///usr/local/bin/terminus/vendor/consolidation/annotated-command/src/Hooks/Dispatchers/InitializeHookDispatcher.php(36): Consolidation\AnnotatedCommand\Hooks\Dispatchers\InitializeHookDispatcher->doInitializeHook(Array, Object(Symfony\Component\Console\Input\ArgvInput), Object(Consolidation\AnnotatedCommand\AnnotationData))
#4 phar:///usr/local/bin/terminus/vendor/consolidation/annotated-command/src/Hooks/Dispatchers/InitializeHookDispatcher.php(29): Consolidation\AnnotatedCommand\Hooks\Dispatchers\InitializeHookDispatcher->callInitializeHook(Array, Object(Symfony\Component\Console\Input\ArgvInput), Object(Consolidation\AnnotatedCommand\AnnotationData))
#5 phar:///usr/local/bin/terminus/vendor/consolidation/annotated-command/src/CommandProcessor.php(145): Consolidation\AnnotatedCommand\Hooks\Dispatchers\InitializeHookDispatcher->initialize(Object(Symfony\Component\Console\Input\ArgvInput), Object(Consolidation\AnnotatedCommand\AnnotationData))
#6 phar:///usr/local/bin/terminus/vendor/consolidation/annotated-command/src/AnnotatedCommand.php(298): Consolidation\AnnotatedCommand\CommandProcessor->initializeHook(Object(Symfony\Component\Console\Input\ArgvInput), Array, Object(Consolidation\AnnotatedCommand\AnnotationData))
#7 phar:///usr/local/bin/terminus/vendor/symfony/console/Command/Command.php(225): Consolidation\AnnotatedCommand\AnnotatedCommand->initialize(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#8 phar:///usr/local/bin/terminus/vendor/symfony/console/Application.php(938): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#9 phar:///usr/local/bin/terminus/vendor/symfony/console/Application.php(266): Symfony\Component\Console\Application->doRunCommand(Object(Consolidation\AnnotatedCommand\AnnotatedCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 phar:///usr/local/bin/terminus/vendor/symfony/console/Application.php(142): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#11 phar:///usr/local/bin/terminus/vendor/consolidation/robo/src/Runner.php(282): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#12 phar:///usr/local/bin/terminus/src/Terminus.php(443): Robo\Runner->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput), Object(Robo\Application), Array)
#13 phar:///usr/local/bin/terminus/bin/terminus(61): Pantheon\Terminus\Terminus->run()
#14 /usr/local/bin/terminus(12): require('phar:///usr/loc...')

I have redone our Docker images for Lando, to build it from source as seen here: lando/pantheon#6, and I can tweak the code in Terminus on the TerminusCollection fetch method to:

    public function fetch()
    {
        foreach ($this->getData() as $id => $model_data) {
            if ($model_data === NULL) {
                continue;
            }

            if (!isset($model_data->id)) {
                $model_data->id = $id;
            }
            $this->add($model_data);
        }
        return $this;
    }

And will work well. Curious as to why we have a model data of NULL in php 8.0 only. I have tried this same setup with php 7.4 and it works just fine. This doesn't appear to be a lando specific issue.

Should I put in a PR to make this adjustment or is something else going on here?

Steps to reproduce the behavior

Use any drupal 9 recipe with php 8.0 and lando. Override your .lando.yml to use the edge php 8.0 image,

@greg-1-anderson
Copy link
Member

I have strong reservations about the proposed fix. I think we need to know why the model data is null before we decide on a solution. Are there simply spurious $id values that can be safely dropped, or might we have a situation where the model data for an id is lost and passed as NULL instead? We wouldn't want to simply hide dropped data problems, as that could make all Terminus commands behave unpredictably.

I have never observed this failure mode, though, so I am unsure what to suggest.

@labboy0276
Copy link
Author

Thanks for the quick response @greg-1-anderson . I can look around more, but I noticed the last 2 data items in the array are NULL:

  [128]=>
  object(stdClass)#4864 (13) {
    ["siteAndEnvLookupHook"]=>
    object(stdClass)#4860 (14) {
      ["schema"]=>
      int(4)
      ["class"]=>
      string(37) "Pantheon\Terminus\Hooks\SiteEnvLookup"
      ["method_name"]=>
      string(20) "siteAndEnvLookupHook"
      ["mtime"]=>
      int(1634594233)
      ["injected_classes"]=>
      array(0) {
      }
      ["name"]=>
      string(24) "site:and-env-lookup-hook"
      ["description"]=>
      string(351) "Determine the site and environment that this command should target. The Annotated Commands hook manager will call this function during the init phase of all commands. For those commands that have a parameter  'site_env' or 'site', this hook will attempt to fill in a default value for that parameter if the user did not provide it on the command line."
      ["help"]=>
      string(0) ""
      ["aliases"]=>
      array(0) {
      }
      ["annotations"]=>
      object(stdClass)#4861 (1) {
        ["hook"]=>
        string(6) "init *"
      }
      ["example_usages"]=>
      array(0) {
      }
      ["return_type"]=>
      NULL
      ["arguments"]=>
      object(stdClass)#4863 (1) {
        ["input"]=>
        object(stdClass)#4862 (1) {
          ["description"]=>
          string(0) ""
        }
      }
      ["options"]=>
      array(0) {
      }
    }
    ["getConfig"]=>
    object(stdClass)#4865 (5) {
      ["schema"]=>
      int(4)
      ["class"]=>
      string(37) "Pantheon\Terminus\Hooks\SiteEnvLookup"
      ["method_name"]=>
      string(9) "getConfig"
      ["mtime"]=>
      int(1634594233)
      ["injected_classes"]=>
      array(0) {
      }
    }
    ["setConfig"]=>
    object(stdClass)#4866 (5) {
      ["schema"]=>
      int(4)
      ["class"]=>
      string(37) "Pantheon\Terminus\Hooks\SiteEnvLookup"
      ["method_name"]=>
      string(9) "setConfig"
      ["mtime"]=>
      int(1633478205)
      ["injected_classes"]=>
      array(0) {
      }
    }
    ["setSites"]=>
    object(stdClass)#4867 (5) {
      ["schema"]=>
      int(4)
      ["class"]=>
      string(37) "Pantheon\Terminus\Hooks\SiteEnvLookup"
      ["method_name"]=>
      string(8) "setSites"
      ["mtime"]=>
      int(1634594233)
      ["injected_classes"]=>
      array(0) {
      }
    }
    ["sites"]=>
    object(stdClass)#4868 (5) {
      ["schema"]=>
      int(4)
      ["class"]=>
      string(37) "Pantheon\Terminus\Hooks\SiteEnvLookup"
      ["method_name"]=>
      string(5) "sites"
      ["mtime"]=>
      int(1634594233)
      ["injected_classes"]=>
      array(0) {
      }
    }
    ["getSite"]=>
    object(stdClass)#4869 (5) {
      ["schema"]=>
      int(4)
      ["class"]=>
      string(37) "Pantheon\Terminus\Hooks\SiteEnvLookup"
      ["method_name"]=>
      string(7) "getSite"
      ["mtime"]=>
      int(1634594233)
      ["injected_classes"]=>
      array(0) {
      }
    }
    ["getEnv"]=>
    object(stdClass)#4870 (5) {
      ["schema"]=>
      int(4)
      ["class"]=>
      string(37) "Pantheon\Terminus\Hooks\SiteEnvLookup"
      ["method_name"]=>
      string(6) "getEnv"
      ["mtime"]=>
      int(1634594233)
      ["injected_classes"]=>
      array(0) {
      }
    }
    ["getOptionalEnv"]=>
    object(stdClass)#4871 (5) {
      ["schema"]=>
      int(4)
      ["class"]=>
      string(37) "Pantheon\Terminus\Hooks\SiteEnvLookup"
      ["method_name"]=>
      string(14) "getOptionalEnv"
      ["mtime"]=>
      int(1634594233)
      ["injected_classes"]=>
      array(0) {
      }
    }
    ["requireSiteIsNotFrozen"]=>
    object(stdClass)#4872 (5) {
      ["schema"]=>
      int(4)
      ["class"]=>
      string(37) "Pantheon\Terminus\Hooks\SiteEnvLookup"
      ["method_name"]=>
      string(22) "requireSiteIsNotFrozen"
      ["mtime"]=>
      int(1634594233)
      ["injected_classes"]=>
      array(0) {
      }
    }
    ["getOptionalSiteEnv"]=>
    object(stdClass)#4873 (5) {
      ["schema"]=>
      int(4)
      ["class"]=>
      string(37) "Pantheon\Terminus\Hooks\SiteEnvLookup"
      ["method_name"]=>
      string(18) "getOptionalSiteEnv"
      ["mtime"]=>
      int(1634594233)
      ["injected_classes"]=>
      array(0) {
      }
    }
    ["getSiteEnv"]=>
    object(stdClass)#4874 (5) {
      ["schema"]=>
      int(4)
      ["class"]=>
      string(37) "Pantheon\Terminus\Hooks\SiteEnvLookup"
      ["method_name"]=>
      string(10) "getSiteEnv"
      ["mtime"]=>
      int(1634594233)
      ["injected_classes"]=>
      array(0) {
      }
    }
    ["getUnfrozenSiteEnv"]=>
    object(stdClass)#4875 (5) {
      ["schema"]=>
      int(4)
      ["class"]=>
      string(37) "Pantheon\Terminus\Hooks\SiteEnvLookup"
      ["method_name"]=>
      string(18) "getUnfrozenSiteEnv"
      ["mtime"]=>
      int(1634594233)
      ["injected_classes"]=>
      array(0) {
      }
    }
    ["id"]=>
    string(37) "Pantheon-Terminus-Hooks-SiteEnvLookup"
  }
  [129]=>
  NULL
  [130]=>
  NULL

I can dig around more to see maybe why the are not getting set properly or passed in correctly on instantiation of this class.

@labboy0276
Copy link
Author

labboy0276 commented Nov 30, 2021

When it is SavedTokens->getData(), the 2 keys the end up with NULL data are commands and tokens. They are the only single named keys for some reason. Which appear to be directories. Any insight on that @greg-1-anderson ?

@greg-1-anderson
Copy link
Member

Those extra keys are simply missing under PHP 7.4?

@labboy0276
Copy link
Author

labboy0276 commented Dec 2, 2021

Hey @greg-1-anderson so I was incorrect saying that under 7.4 and the 3.x version that it worked ok. The same issue persists and I believe I found out whats going on:

In the 2.x version of terminus, in the FileStore class get method, terminus is only using the file_exists function to check the directory items within the ~/.termimus/cache folder. So if there is a directory (which there is in 2.x and 7.4), it will still return a non-Null result.

While in the 3.x version of terminus FileStore class get method there is also a is_file function getting called as well. This in turn causes issues where there are directories in the cache folder, thus returning a NULL response and throwing off the TerminusCollection methods.

So, my solution would be to either:

  1. Use my code snippet above
  2. Remove the is_file from the FileStore class and adjust the logic there.
  3. Do more digging because it feels like the directory structure in ~/.terminus may be wrong?

Thoughts?

@heddn
Copy link

heddn commented Dec 8, 2021

I can easily reproduce this issue in a github action.

env:
  COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist --optimize-autoloader"
  PHP_EXTENSIONS: sodium, json, pdo_sqlite, gd, xml, iconv, intl, ctype, mbstring
  PHP_VERSION: 8.0

build-deploy:
    name: Build and deploy
    runs-on: ubuntu-latest

    steps:
      - name: Setup cache PHP extensions
        id: extcache
        uses: shivammathur/cache-extensions@v1
        with:
          php-version: ${{ env.PHP_VERSION }}
          extensions: ${{ env.PHP_EXTENSIONS }}
          key: php-ext-cache-v1
      - name: Cache PHP extensions
        uses: actions/cache@v2
        with:
          path: ${{ steps.extcache.outputs.dir }}
          key: ${{ steps.extcache.outputs.key }}
          restore-keys: ${{ steps.extcache.outputs.key }}
      - name: Installing PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ env.PHP_VERSION }}
          extensions: ${{ env.PHP_EXTENSIONS }}
          coverage: none
          tools: composer:v2
          ini-values: sendmail_path=/bin/true
      - name: Set Composer Cache Directory
        id: composer-cache
        run: |
          echo "::set-output name=dir::$(composer config cache-files-dir)"
      - uses: actions/cache@v2
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: composer-${{ hashFiles('**/composer.lock', '**/composer.patches.json') }}
          restore-keys: |
            composer-
      - name: Installing Terminus
        run: |
          set -e
          curl -L https://github.com/pantheon-systems/terminus/releases/download/3.0.0-beta1/terminus --output terminus
          chmod +x terminus
          mv terminus /usr/local/bin/terminus
          terminus auth:login --machine-token=${{ secrets.PANTHEON_MACHINE_TOKEN }}
          terminus self:plugin:install terminus-secrets-plugin

I was not able to reproduce this issue in ddev using php 8. But ddev has terminus 2 installed by default and I have to overwrite with terminus 3. I think that is the difference. Github action fails on terminus auth:login.

@heddn
Copy link

heddn commented Dec 8, 2021

Actually, while the final error message is the same, my stack trace is a little different:

 PHP Fatal error:  Uncaught Error: Attempt to assign property "id" on null in phar:///usr/local/bin/terminus/src/Collections/TerminusCollection.php:95
Stack trace:
#0 phar:///usr/local/bin/terminus/src/Collections/TerminusCollection.php(81): Pantheon\Terminus\Collections\TerminusCollection->fetch()
#1 phar:///usr/local/bin/terminus/src/Collections/TerminusCollection.php(137): Pantheon\Terminus\Collections\TerminusCollection->all()
#2 phar:///usr/local/bin/terminus/src/Commands/Auth/LoginCommand.php(38): Pantheon\Terminus\Collections\TerminusCollection->get()
#3 [internal function]: Pantheon\Terminus\Commands\Auth\LoginCommand->logIn()
#4 phar:///usr/local/bin/terminus/vendor/consolidation/annotated-command/src/CommandProcessor.php(257): call_user_func_array()
#5 phar:///usr/local/bin/terminus/vendor/consolidation/annotated-command/src/CommandProcessor.php(212): Consolidation\AnnotatedCommand\CommandProcessor->runCommandCallback()
#6 phar:///usr/local/bin/terminus/vendor/consolidation/annotated-command/src/CommandProcessor.php(176): Consolidation\AnnotatedCommand\CommandProcessor->validateRunAndAlter()
#7 phar:///usr/local/bin/terminus/vendor/consolidation/annotated-command/src/AnnotatedCommand.php(313): Consolidation\AnnotatedCommand\CommandProcessor->process()
#8 phar:///usr/local/bin/terminus/vendor/symfony/console/Command/Command.php(259): Consolidation\AnnotatedCommand\AnnotatedCommand->execute()
#9 phar:///usr/local/bin/terminus/vendor/symfony/console/Application.php(938): Symfony\Component\Console\Command\Command->run()
#10 phar:///usr/local/bin/terminus/vendor/symfony/console/Application.php(266): Symfony\Component\Console\Application->doRunCommand()
#11 phar:///usr/local/bin/terminus/vendor/symfony/console/Application.php(142): Symfony\Component\Console\Application->doRun()
#12 phar:///usr/local/bin/terminus/vendor/consolidation/robo/src/Runner.php(282): Symfony\Component\Console\Application->run()
#13 phar:///usr/local/bin/terminus/src/Terminus.php(443): Robo\Runner->run()
#14 phar:///usr/local/bin/terminus/bin/terminus(61): Pantheon\Terminus\Terminus->run()
#15 /usr/local/bin/terminus(12): require('...')
#16 {main}
  thrown in phar:///usr/local/bin/terminus/src/Collections/TerminusCollection.php on line 95

@heddn
Copy link

heddn commented Dec 8, 2021

As a temporary work around (it didn't work) I tried doing mkdir -p ~/.terminus/cache/tokens ~/.terminus/cache/commands. And all other combinations of pre-seeding the parent or subsets of the folders and even touch of the token file itself. The secrets plugin installs fine. It is only the login that is failing.

@greg-1-anderson
Copy link
Member

I can't reproduce this locally. Does anyone know specifically what folders to create where to see this?

I don't like the existing fixes, which mask symptoms rather than fixing the problem. If the actual problem is coming out of e.g. SavedTokens::getData(), then any filtering / skipping should happen there.

@labboy0276
Copy link
Author

One of the folders that get created on our lando setups is created in the bin here it looks like: https://github.com/pantheon-systems/terminus/blob/3.x/bin/terminus#L65

@greg-1-anderson
Copy link
Member

greg-1-anderson commented Dec 8, 2021

Yes, but I have that directory in my ~/.terminus folder, and I can't reproduce this problem when using PHP 8.0.

@labboy0276
Copy link
Author

Not sure then, I can reproduce it with every D9 / php8 site I tried on Pantheon. We had to do a work around so we could ship out the php 8 containers for now on our pantheon recipe.

@heddn
Copy link

heddn commented Dec 8, 2021

#2268 I switch from try/catch (/Exception $e) to try/catch (/Throwable $t). Obviously we are expecting some errors because we were doing a try/catch. It looks like PHP 8.0 is just a little different in how it throws up problems and throwable seems to be a more failsafe solution.

@greg-1-anderson
Copy link
Member

Yeah, I commented on that PR just now. I'd feel more comfortable if we knew how to reproduce the problem before selecting a fix.

@heddn
Copy link

heddn commented Dec 8, 2021

#2231 has resolved the issue for us and was released in 3.0.0. This could probably be closed duplicate.

Now I'm facing #2270

@greg-1-anderson
Copy link
Member

#2231 has resolved the issue for us and was released in 3.0.0. This could probably be closed duplicate.

Maybe this is why I can't reproduce! #2270 should be resolved with another PR.

@labboy0276
Copy link
Author

We have been building for source this whole time as you can see here: https://github.com/lando/pantheon/blob/main/recipes/pantheon/8.0-fpm/Dockerfile#L25

So #2231 has been merged in for a couple months now.

@labboy0276
Copy link
Author

I will keep digging in the near future as I don't think the issues @heddn was having is the same as we are having.

@labboy0276
Copy link
Author

I stand corrected @greg-1-anderson. I updated our docker containers to use the 3.0.1 phar and it works now. So I was wrong and all is well now. Thank you @heddn as well.

@johnbburg
Copy link

johnbburg commented Jul 20, 2022

Apologies for commenting on a closed ticket. I just wanted to say that I am seeing the error:

PHP Fatal error:  Uncaught Error: Attempt to assign property "id" on string in phar:///home/jbrandenburg/terminus/terminus/src/Collections/TerminusCollection.php:95
Stack trace:

When running the terminus site:list command

Which appears to coincide with the status page detecting "Our monitoring has detected elevated error rates for the Dashboard and Terminus commands. These may manifest as slow page loads, failed logins, or failures with Terminus commands." A colleague of mine is reporting the same error with the same command.

I am using:

Terminus 3.0.7
PHP 8.1.7 (cli) (built: Jun 10 2022 12:22:48) (NTS)

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

Successfully merging a pull request may close this issue.

4 participants