Skip to content

CIVIPLMMSR-623: Fix entity type registration on CiviCRM 5.75#23

Merged
erawat merged 1 commit intomasterfrom
CIVIPLMMSR-623/fix-entity-registration-civicrm-5.75
Apr 21, 2026
Merged

CIVIPLMMSR-623: Fix entity type registration on CiviCRM 5.75#23
erawat merged 1 commit intomasterfrom
CIVIPLMMSR-623/fix-entity-registration-civicrm-5.75

Conversation

@erawat
Copy link
Copy Markdown
Member

@erawat erawat commented Apr 21, 2026

Overview

Fixes the extension failing to enable on CiviCRM 5.75 with a TypeError. The entity types (PaymentAttempt, PaymentProcessorCustomer, PaymentWebhook) were not being registered during installation due to a mixin timing issue.

Before

Enabling the extension on CiviCRM 5.75 throws:

TypeError: Civi\Schema\EntityRepository::getEntity(): Argument #1 ($entityName) must be of type string, null given

The previous backport (#21) added mixin/polyfill.php but the polyfill is gated on !class_exists('CRM_Extension_MixInfo'). Since MixInfo exists since CiviCRM 5.45, the polyfill never runs on 5.75.

After

The extension installs and enables successfully on both CiviCRM 5.75 and 6.4.

Technical Details

CiviCRM's MixinScanner only processes installed extensions. During this extension's own installation, the entity-types-php@2 mixin listener is never loaded, so entity types are not registered in EntityRepository. When CRM_Core_DAO_Base::getEntityDefinition() runs, getEntityNameForClass() returns null causing the TypeError.

Two changes fix this:

  1. Bundle mixin/entity-types-php@2.0.0.mixin.php — copied from the Stripe extension, matching the standard CiviCRM pattern (both Stripe and GoCardless bundle their entity-types mixin). This ensures the MixinScanner finds the mixin in the extension's own directory.

  2. Add direct hook_civicrm_entityTypes implementation in paymentprocessingcore.php — registers entity types by reading schema/*.entityType.php directly, bypassing the mixin timing issue. When both this hook and the mixin fire (normal operation), the result is identical (same data written to the same keys). On CiviCRM 6.4+ this is a harmless no-op.

Core overrides

No core overrides.

Comments

  • The paymentprocessingcore.civix.php file was intentionally not modified as it is auto-generated by civix.
  • Double entity type registration is harmless — both paths write to $entityTypes[$entity['name']] with identical data, so the later write simply overwrites with the same content.

The extension fails to enable on CiviCRM 5.75 with a TypeError because
entity types are not registered during installation. The MixinScanner
only processes installed extensions, so the entity-types-php@2 mixin
listener is never loaded for this extension during its own install.

- Bundle entity-types-php@2.0.0.mixin.php (copied from Stripe, matching
  the standard CiviCRM pattern for extensions with custom entities)
- Add direct hook_civicrm_entityTypes implementation as a fallback that
  registers entity types from schema/*.entityType.php regardless of
  mixin timing. When both fire, the result is identical (same keys).

On CiviCRM 6.4+ this is a harmless no-op.
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical installation failure on CiviCRM 5.75 caused by a timing issue where entity types were not being registered. By bundling the required mixin and implementing a direct hook for entity type registration, the extension now correctly registers its entities during the installation process, ensuring compatibility across CiviCRM versions.

Highlights

  • Mixin Integration: Bundled the entity-types-php@2.0.0 mixin to ensure proper registration of entity types during extension installation.
  • Direct Hook Implementation: Added a direct implementation of hook_civicrm_entityTypes in paymentprocessingcore.php to bypass timing issues during installation on CiviCRM 5.75.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new mixin for auto-registering entity declarations and implements a direct hook in paymentprocessingcore.php to resolve a timing issue during extension installation on CiviCRM 5.75. The review feedback suggests adding an array type hint to the hook parameter for PHPStan level 9 compliance, improving the handling of glob() results to avoid iterating over a [false] array on failure, and adding validation for entity data within the mixin to ensure consistency and robustness.

Comment thread paymentprocessingcore.php
*
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes
*/
function paymentprocessingcore_civicrm_entityTypes(&$entityTypes): void {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add the array type hint to the $entityTypes parameter. This ensures compliance with PHPStan level 9 requirements for proper typing and improves code clarity by explicitly defining the expected type for the reference.

function paymentprocessingcore_civicrm_entityTypes(array &$entityTypes): void {
References
  1. PHPStan level 9 compliance (proper types, no mixed where avoidable) (link)

Comment thread paymentprocessingcore.php
Comment on lines +54 to +55
$files = (array) glob($schemaDir . '/*.entityType.php');
foreach ($files as $file) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Casting the result of glob() directly to (array) is problematic because glob() returns false on failure. In PHP, (array) false results in [false], which causes the loop to execute once with an invalid filename, leading to a warning during the include call. Using the null coalescing operator ?: [] is a safer way to handle potential failures.

  foreach (glob($schemaDir . '/*.entityType.php') ?: [] as $file) {

Comment on lines +32 to +37
$files = (array) glob($mixInfo->getPath('schema/*.entityType.php'));
foreach ($files as $file) {
$entity = include $file;
$entity['module'] = $mixInfo->longName;
$e->entityTypes[$entity['name']] = $entity;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The glob() result should be handled safely to avoid issues if it returns false. Additionally, adding validation for the $entity variable (ensuring it is an array and contains a name) improves robustness and maintains consistency with the implementation in paymentprocessingcore.php. This prevents potential errors if a file in the schema directory is malformed or does not return the expected array.

    foreach (glob($mixInfo->getPath('schema/*.entityType.php')) ?: [] as $file) {
      $entity = include $file;
      if (is_array($entity) && !empty($entity['name'])) {
        $entity['module'] = $mixInfo->longName;
        $e->entityTypes[$entity['name']] = $entity;
      }
    }
References
  1. Prefer manual loops with explicit type checks to ensure compliance with strict static analysis and maintain consistency with established codebase patterns.

@erawat
Copy link
Copy Markdown
Member Author

erawat commented Apr 21, 2026

Response to Gemini review comments

1. Add array type hint to $entityTypes (paymentprocessingcore.php:49)
Declining — CiviCRM's hook_civicrm_entityTypes is defined without a type hint on the parameter, and all other hooks in this file (civicrm_permission, civicrm_container, etc.) follow the same convention. Adding array would deviate from CiviCRM's hook contract. PHPStan won't flag this because the CiviCRM hook stubs don't declare array either.

2. Use ?: [] instead of (array) for glob() (paymentprocessingcore.php:54-55)
Technically correct that glob() can return false, but (array) glob(...) is the exact pattern used in CiviCRM core's own entity-types-php@2/mixin.php and in the Stripe extension. The is_dir() guard on line 51 prevents the main false return scenario. Changing this would deviate from the established CiviCRM convention for no practical benefit.

3. Add validation in the mixin file (mixin/entity-types-php@2.0.0.mixin.php:32-37)
This file is a verbatim copy from CiviCRM core / Stripe. We intentionally keep it identical to upstream so it can be updated without merge conflicts. Any robustness improvements should go through the CiviCRM core project, not as local modifications here.

@erawat erawat merged commit b0b6e93 into master Apr 21, 2026
3 checks passed
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 this pull request may close these issues.

2 participants