The conventional approach
Generally, the safe play is to make a file like <myextension>/CRM/MyExtension/MyClass.php
:
<?php
class CRM_MyExtension_MyClass { ...}
(Be sure to match the capitalization of files and classes.)
Most documentation and tooling is based on CRM_*
conventions, and a handful of things in core (such as civicrm_payment_processor_type.class_name
) make quirky assumptions which only work with CRM_*
classes.
How it works: In civix-based extensions, the default hook_civicrm_config
adds your extension to the include_path
. The classloader CRM_Core_ClassLoader
searches the include_path
automatically for CRM_*
classes.
Critique: In older PHP projects, it was common to automatically link the class-loading with the include_path
, but in large codebases this can lead to a large number of file-system checks. Now-a-days many frameworks will reduce lookups by pre-generating some kind of map (namespaces<=>directories or classes<=>files).
The PSR-4 approach (Civi v4.7+)
PSR-4 is a more contemporary technique in which you explicitly map a directory to a namespace. Many PHP developers have been exposed to PSR-4 through composer.json
. Civi v4.7 adds CRM_Extension_ClassLoader
; with this, you can use info.xml
to declare similar mappings:
<extension key="..." type="module">
...
<classloader>
<psr4 prefix="My\Namespace\" path="src" />
</classloader>
...
</extension>
In this case, the file <myextension>/src/Foo.php
would look like:
<?php
namespace My\Namespace;
class Foo { ... }
In terms of lookup performance and caching, this should be better than CRM_Core_ClassLoader
. However, it doesn't have full parity with CRM_*
in terms of documentation / tooling / testing.
Custom class loader
You should be able to register your own class-loaders -- e.g. just call spl_autoload_register(...)
in your module's main PHP file (perhaps in hook_civicrm_config
). If you do this, I suggest following a convention like PSR-0 or PSR-4.
Compared to the PSR-4/Civi v4.7 approach above:
- Pro: Should be compatible with any version of Civi
- Con: Requires more lines of code
- Con: Has the same issue of negligible documentation / tooling / testing
PHPUnit Base Classes
There is an issue if you want to write a custom base-class for PHPUnit tests within an extension -- which is the topic of Does class autoloading not work in standalone phpunit testing?