Home > Developer Guide > Framework > Mixins
Mixins: Create a new mixin
???+ tldr "About this document"
This page presents ___how to develop a mixin___. It assumes you have read about [___the general concepts___](index.md).
Tutorial
Let's create a mixin called hello-world
with version 1.0.0
. It will
announce "Hello world".
First, enable hello-world
in info.xml
:
<extension type="module">
<mixins>
<mixin>menu-xml@1.0.0</mixin>
<mixin>mgd-php@1.0.0</mixin>
+ <mixin>hello-world@1.0.0</mixin>
</mixins>
</extension>
Next, create the mixin PHP file:
mkdir 'mixin/hello-world@1/'
nano 'mixin/hello-world@1/mixin.php'
This file will need some annotations:
<?php
/**
* @mixinName hello-world
* @mixinVersion 1.0.0
*/
Finally, we define a function. This function receives information about your extension.
It can use that information to do something pleasant, like say "Hello".
return function (CRM_Extension_MixInfo $mixInfo, CRM_Extension_BootCache $bootCache) {
printf("Hello world. I am extension \"%s\"!\n", $mixInfo->longName);
};
The mixin function runs very early -- and very frequently. Calling printf()
here would be too much.
Instead, we should be more selective -- we should use CiviCRM hooks to find the opportune moment. For example, hook_civicrm_alterContent allows you to append content to the HTML page-output.
return function (CRM_Extension_MixInfo $mixInfo, CRM_Extension_BootCache $bootCache) {
Civi::dispatcher()->addListener('hook_civicrm_alterContent', function ($event) use ($mixInfo) {
if (!$mixInfo->isActive()) {
return;
}
$event->content .= sprintf("<div>Hello world. I am extension \"%s\"!\n", $mixInfo->longName);
});
};
Finally, to activate the new mixin, you will need to clear system caches.
cv flush
FAQ
??? question "What is $mixInfo
?"
A mixin like `hello-world` or `menu-xml` can be enabled on many different extensions.
The `$mixInfo` tells you about the name, location, and status of the extension.
For more information, see [CRM_Extension_MixInfo](https://github.com/civicrm/civicrm-core/blob/master/CRM/Extension/MixInfo.php).
Note: The class `CRM_Extension_MixInfo` was added in CiviCRM 5.45. If you are using the polyfill to run
older versions of CiviCRM, then it will provide a similar object - but the class-name may differ. Use relaxed type-hints.
??? question "What is $bootCache
?"
Many mixins perform a *scan* over the extension source-code. Depending on the specific scan, this may be fast or slow.
The boot-cache is optimized for storing the scan-results during boot.
Of course, like any cache, there is a trade-off between performance and complexity. I would only suggest it if (1) the scan-process is expensive
and (2) the scan-data needs to be used frequently.
For more information, see [CRM_Extension_BootCache](https://github.com/civicrm/civicrm-core/blob/master/CRM/Extension/BootCache.php).
Note: The class `CRM_Extension_BootCache` was added in CiviCRM 5.45. If you are using the polyfill to run
older versions of CiviCRM, then it will provide a similar object - but the class-name may differ. Use relaxed type-hints.
??? question "Why does it return
an anonymous function?"
Mixins are designed to be multi-version safe. This notation allows the system to load multiple versions
of the same mixin -- without any conflicts over function-names or class-names.
??? question "Can I define named classes or named functions?"
Sort of.
Remember that mixins are multi-version safe. Specifically, the system must be allowed to load *major* versions in parallel. As long as
the major versions don't conflict, it should work.
For example, to prevent a conflict between `hello-world@1.0.0` and `hello-world@2.0.0`, you could use versioned-namespaces:
```php
// FILE: mixin/hello-world@1.0.0.php
namespace HelloWorldV1; // <== Notice "V1"
class Greeter {
}
return function($mixInfo, $bootCache) {
$g = new Greeter();
};
```
```php
// FILE: mixin/hello-world@2.0.0.php
namespace HelloWorldV2; // <== Notice "V2"
class Greeter {
}
return function($mixInfo, $bootCache) {
$g = new Greeter();
};
```
Another approach is to refrain from *ever* issuing a major update. [This gist](https://gist.github.com/totten/22258d1776e269c793562bd4fe01a848)
outlines such a scenario.
However, this has only been tested lightly. At time of writing, [standard mixins](standard.md) do not use named-classes or named-functions.