Skip to content

Instantly share code, notes, and snippets.

@totten
Last active November 9, 2023 11:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save totten/22258d1776e269c793562bd4fe01a848 to your computer and use it in GitHub Desktop.
Save totten/22258d1776e269c793562bd4fe01a848 to your computer and use it in GitHub Desktop.
How to phase-in a new PHP class via mixin

How to phase-in a new PHP class via mixin

This briefly summarizes how to provide a new class-contract and backport/polyfill it via extension-mixins.

At time of writing, this technique has only been used in small/partial experiments.

Example use-case

  • You want to define a new class Civi\Foo\Bar as part of civicrm-core.
  • You want to use this class in several extensions.
  • You want to keep the current compatibility-level in those extensions (don't raise the core requirement)
  • You want some wiggle-room for fixing problems in Civi\Foo\Bar - i.e. it's possible (but unlikely) that you'll issue updates. (New versions should take precedence over old versions.)
  • So you put a copy of Civi\Foo\Bar into a mixin named foo-bar-polyfill@1.0.0.mixin.php.

Process

  1. Canonical implementation (preparing core)
    • Add a new class Civi\Foo\Bar to civicrm-core.
  2. Backport (polyfill) implementation (preparing to bundle Civi\Foo\Bar to run on older releases)
    • Create foo-bar-polyfill@1.0.0.mixin.php with some boilerplate and an implementation of the class.
      namespace Civi\Foo;
      return function() {
        if (class_exists('Civi\Foo\Bar')) return;
        class Bar { 
          // Copy in the full implementation
        }
      };
  3. Backport (polyfill) usage (for a specific extension)
    • Copy in foo-bar-polyfill@1.0.0.mixin.php. Update info.xml
  4. Maintenance (what if you need to patch Civi\Foo\Bar?)
    • If you only need the change to work on future versions of civicrm-core, then...
      • Update the canonical Civi\Foo\Bar. All done easypeasy.
    • If you need the change to apply on older version of civicrm-core, then...
      • There are options, but not so easypeasy.
      • Update the canonical Civi\Foo\Bar.
      • Update the backport foo-bar-polyfill. Raise the version number (eg v1.0.0 => v1.1.0).
      • Start using foo-bar-polyfill@1.1.0.mixin.php in extensions.
        • (This will take precedence over foo-bar-polyfill@1.0.0.mixin.php.)

Limitations

  • Only works if you're adding a new class.
  • Not amenable if you need to change a pre-existing class.
  • You should only make MINOR and PATCH updates to foo-bar-polyfill. MAJOR will be problematic.
  • You should only deploy on CiviCRM v5.45+. Older versions may kinda work but there's no guarantee of version-precedence.
    • (If extensions offer competing versions -- foo-bar-polyfill@1.1.0 and foo-bar-polyfill@1.0.0 -- then you want the newer version. This works on v5.45+.)
  • If the idea of backports is interesting, but these limitations suck too much, then maybe look at https://github.com/totten/pathload-poc/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment