Adding Custom Code to Packages

This documentation is for Concrete CMS version 8 and above. For parts of this documentation in version 5.7, please refer to this document.

Packages will often need to include custom PHP code for some of their functionality. For example, when adding our custom event to the on_start() method, it will be smarter for us to include this functionality in a separate custom script, rather than directly in the on_start() method. So instead of this:

public function on_start()
{
    // Lots of code that we do on every page load.
}

Instead, let's add that to a separate code library. Let's say our company is FooCorp, and our product deals with the management of widgets. With that in mind, we can create a namespace for our add-on's custom code:

FooCorp\WidgetManager

and make our first library class Bootstrapper, which will take care of all these tasks, whatever they may be.

public function on_start()
{
    $startup = new \FooCorp\WidgetManager\Bootstrapper();
    $startup->boot();
}

That's great, but we have a few problems. First, where do we put this class so that Concrete will automatically find it. Do we have to do something like

require_once $pkg->getPackagePath() . '/src/Bootstrapper.php';

from within our controller.php? Nope! We can use PHP autoloading to find the class for us. Concrete's package system makes this easy to do. Just add this to your package controller:

protected $pkgAutoloaderRegistries = array(
    'src/FooCorp/WidgetManager' => '\FooCorp\WidgetManager'
);

This will register FooCorp\WidgetManager as a PSR-4 autoloader prefix, and point it at the src/FooCorp/WidgetManager directory inside your package's root directory. Any classes found in there, at any level of hierarchy will automatically be loaded, assuming their namespaces and class names match (with case sensitivity) the proper location in the filesystem. Just make sure that the \FooCorp\WidgetManager\Bootstrapper class can be found at packages/widget_manager/src/FooCorp/WidgetManager/Bootstrapper.php, and you'll be in business.

Legacy Support: Concrete\Package\WidgetManager\Src

You may have noticed there's nothing in here about an automatic namespace like Concrete\Package\WidgetManager\Src. In 5.7.5.10 and earlier, this namespace was automatically created, and mapped to src/packages/widget_manager/src. In version 8 this legacy support is still enabled, provided your package meets the following criteria:

  • You do not use $pkgAutoloaderRegistries (if you do register custom autoloaders using the method above, the legacy namespace will not be enabled.)
  • Your package requires the version of Concrete below 8.0. If your package requires Concrete 8.0 or greater, it cannot use the legacy namespace.
  • You have not overridden the $pkgEnableLegacyNamespace protected variable and turned the legacy namespace off.

If these are all true, you have access to the legacy namespace, but you should consider not using it. You can easily autoload code without using this automatic namespace, and its guaranteed to work for longer.

Extending Core Classes in your Package

Certain core Concrete objects can be delivered in packages, but have class names that are automatically generated by Concrete. For example, let's say we have a package with the handle 'spam_stopper', that installs an anti-spam plugin with the handle 'akismet.'. Concrete automatically generates a class name based on the package handle, and the type of class:

Concrete\Package\SpamStopper\Antispam\AkismetController

Any class generated in this way will automatically attempt to load its package from

packages/spam_stopper/src/Concrete/Antispam/AkismetController

How do I know which items can be extended in this way?

These should be documented in the Developer Documentation; if you don't see an entry, look in the Concrete source for the method overrideable_core_class(). It's a hint that the item being instantiated will be loaded from application/ first, before trying the core.