Skip to content
GitLab
Projects Groups Topics Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in
  • CiviCRM Core CiviCRM Core
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Graph
    • Compare revisions
  • Issues 1,180
    • Issues 1,180
    • List
    • Boards
    • Service Desk
    • Milestones
  • Deployments
    • Deployments
    • Releases
  • Wiki
    • Wiki
  • Activity
  • Graph
  • Create a new issue
  • Commits
  • Issue Boards
Collapse sidebar
  • DevelopmentDevelopment
  • CiviCRM CoreCiviCRM Core
  • Issues
  • #4055
Closed
Open
Issue created Dec 29, 2022 by kainuk@kainuk

Extension cannot found its own classes during install when opcache is enabled

I start with the following configuration:

  • PHP 8.1.7 Using the docker image FROM php:8.1.7-apache-buster
  • OPCache enabled standard configuration
  • Buildkit CiviCRM install for Drupal 9 see below:
civibuild create patch \
  --type drupal9-demo \
  --civi-ver master \
  --url http://patch.kainuk \
  --cms-ver 9.5

Now, when I enable the Action Provider extension (just from the extension screen), the following error is thrown.

Error: Class "Civi\ActionProvider\Symfony\Component\DependencyInjection\DefinitionAdapter" not found in action_provider_civicrm_container() (line 15 of /buildkit/build/patch/web/sites/default/files/civicrm/ext/action-provider/action_provider.php)

cv flush lets the problem disappear, and for the Action Provider that is no problem because only code is installed (it does not change the database). However, other extensions can have more problems because the installation is interrupted, and possible incomplete.

The problem is, however, reproducible. When I disable the extension, and uninstall it, the error reappears after installing.

I did some duty time in the debugger and found the following code in the extension class loader (CRM_Extension_ClassLoader)

$file = $this->getCacheFile();
    if (file_exists($file)) {
      $this->loader = require $file;
    }
    else {
      $this->loader = $this->buildClassLoader();
      $ser = serialize($this->loader);
      file_put_contents($file,
        sprintf("<?php\nreturn unserialize(%s);", var_export($ser, 1))
      );
    }

$this->buildClassLoader() is an expensive procedure, so after calculation the result is saved for reuse. When a new extension is installed, the saved file is removed because it does not contain the new extension configuration. A new classloader with all the extension stuff is calculated and an updated file is written.

For loading the class, the PHP construct require is used. This construct was designed for loading PHP code. The assumption is that PHP code does not change much, and OPCache used this assumption, to store the file in memory. The risk is here is that not the newly written configuration is used, but an old one from memory. On my configuration, I observed with the PHP debugger, that when the hook_civicrm_container the old classloader is loaded with require. Just put a breakpoint before the $this->loader = require $file line.

When the OPCache is disabled, the problem does not appear.

Edited Dec 29, 2022 by kainuk
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Assignee
Assign to
Time tracking