# Advanced usage of helpers

## Registering Helpers

`Laminas\View\Renderer\PhpRenderer` composes a *plugin manager* for managing
helpers, specifically an instance of `Laminas\View\HelperPluginManager`, which
extends `Laminas\ServiceManager\AbstractPluginManager`, which is itself an
extension of `Laminas\ServiceManager\ServiceManager`.  `HelperPluginManager` is a
specialized service manager, so you can register a helper/plugin like any other
service (see the [Service Manager documentation](https://docs.laminas.dev/laminas-servicemanager/configuring-the-service-manager/)
for more information).

Programmatically, this is done as follows:

```php
use MyModule\View\Helper\LowerCase;

// $view is an instance of PhpRenderer
$pluginManager = $view->getHelperPluginManager();

// Register an alias:
$pluginManager->setAlias('lowercase', LowerCase::class);

// Register a factory:
$pluginManager->setFactory(LowerCase::class, function () {
   $lowercaseHelper = new LowerCase();

   // ...do some configuration or dependency injection...

   return $lowercaseHelper;
});
```

Within an MVC application, you will typically pass a map of plugins to the class
via your configuration.

```php
use MyModule\View\Helper;
use Laminas\ServiceManager\Factory\InvokableFactory;

// From within a configuration file
return [
   'view_helpers' => [
        'aliases' => [
            'lowercase' => Helper\LowerCase::class,
            'uppercase' => Helper\UpperCase::class,
        ],
        'factories' => [
            LowerCase::class => InvokableFactory::class,
            UpperCase::class => InvokableFactory::class,
        ],
    ],
];
```

If your module class implements `Laminas\ModuleManager\Feature\ViewHelperProviderInterface`,
or just the method `getViewHelperConfig()`, you could also do the following
(it's the same as the previous example).

```php
namespace MyModule;

class Module
{
    public function getViewHelperConfig()
    {
        return [
            'aliases' => [
                'lowercase' => Helper\LowerCase::class,
                'uppercase' => Helper\UpperCase::class,
            ],
            'factories' => [
                LowerCase::class => InvokableFactory::class,
                UpperCase::class => InvokableFactory::class,
            ],
        ];
    }
}
```

The two latter examples can be done in each module that needs to register
helpers with the `PhpRenderer`; however, be aware that another module can
register helpers with the same name, so order of modules can impact which helper
class will actually be registered!

## Writing Custom Helpers

Writing custom helpers is easy. We recommend extending
`Laminas\View\Helper\AbstractHelper`, but at the minimum, you need only implement
the `Laminas\View\Helper\HelperInterface` interface:

```php
namespace Laminas\View\Helper;

use Laminas\View\Renderer\RendererInterface as Renderer;

interface HelperInterface
{
    /**
     * Set the View object
     *
     * @param  Renderer $view
     * @return HelperInterface
     */
    public function setView(Renderer $view);

    /**
     * Get the View object
     *
     * @return Renderer
     */
    public function getView();
}
```

If you want your helper to be capable of being invoked as if it were a method call of the
`PhpRenderer`, you should also implement an `__invoke()` method within your helper.

As previously noted, we recommend extending `Laminas\View\Helper\AbstractHelper`, as it implements the
methods defined in `HelperInterface`, giving you a headstart in your development.

> ### Invokable helpers
>
> Starting with version 2.7.0, helpers no longer need to be instances of
> `HelperInterface`, but can be *any* PHP callable. We recommend writing helpers
> as invokable classes (classes implementing `__invoke()`.

Once you have defined your helper class, make sure you can autoload it, and then
register it with the [plugin manager](#registering-helpers).

Here is an example helper, which we're titling "SpecialPurpose"

```php
namespace MyModule\View\Helper;

use Laminas\View\Helper\AbstractHelper;

class SpecialPurpose extends AbstractHelper
{
    protected $count = 0;

    public function __invoke()
    {
        $this->count++;
        $output = sprintf("I have seen 'The Jerk' %d time(s).", $this->count);
        return htmlspecialchars($output, ENT_QUOTES, 'UTF-8');
    }
}
```

Then assume that we [register it with the plugin manager](#registering-helpers)
by the name "specialPurpose".

Within a view script, you can call the `SpecialPurpose` helper as many times as
you like; it will be instantiated once, and then it persists for the life of
that `PhpRenderer` instance.

```php
// remember, in a view script, $this refers to the Laminas\View\Renderer\PhpRenderer instance.
echo $this->specialPurpose();
echo $this->specialPurpose();
echo $this->specialPurpose();
```

The output would look something like this:

```php
I have seen 'The Jerk' 1 time(s).
I have seen 'The Jerk' 2 time(s).
I have seen 'The Jerk' 3 time(s).
```

Sometimes you will need access to the calling `PhpRenderer` object; for
instance, if you need to use the registered encoding, or want to render another
view script as part of your helper. This is why we define the `setView()` and
`getView()` methods. As an example, we could rewrite the `SpecialPurpose` helper
as follows to take advantage of the `EscapeHtml` helper:

```php
namespace MyModule\View\Helper;

use Laminas\View\Helper\AbstractHelper;

class SpecialPurpose extends AbstractHelper
{
    protected $count = 0;

    public function __invoke()
    {
        $this->count++;
        $output  = sprintf("I have seen 'The Jerk' %d time(s).", $this->count);
        $escaper = $this->getView()->plugin('escapehtml');
        return $escaper($output);
    }
}
```

> ### Accessing the view or other helpers in callables
>
> As noted earlier, starting in version 2.7.0, you may use any PHP callable as a
> helper. If you do, however, how can you access the renderer or other plugins?
>
> The answer is: dependency injection.
>
> If you write your helper as a class, you can accept dependencies via the
> constructor or other setter methods. Create a factory that pulls those
> dependencies and injects them.
>
> As an example, if we need the `escapeHtml()` helper, we could write our helper
> as follows:
>
> ```php
> namespace MyModule\View\Helper;
>
> use Laminas\View\Helper\EscapeHtml;
>
> class SpecialPurpose
> {
>     private $count = 0;
>
>     private $escaper;
>
>     public function __construct(EscapeHtml $escaper)
>     {
>         $this->escaper = $escaper;
>     }
>
>     public function __invoke()
>     {
>         $this->count++;
>         $output  = sprintf("I have seen 'The Jerk' %d time(s).", $this->count);
>         $escaper = $this->escaper;
>         return $escaper($output);
>     }
> }
> ```
>
> Then we would write a factory like the following:
>
> ```php
> use Laminas\ServiceManager\AbstractPluginManager;
>
> class SpecialPurposeFactory
> {
>     public function __invoke($container)
>     {
>         if (! $container instanceof AbstractPluginManager) {
>             // laminas-servicemanager v3. v2 passes the helper manager directly.
>             $container = $container->get('ViewHelperManager');
>         }
>
>         return new SpecialPurpose($container->get('escapeHtml'));
>     }
> }
> ```
>
> If access to the view were required, we'd pass the `PhpRenderer` service
> instead.

## Registering Concrete Helpers

Sometimes it is convenient to instantiate a view helper, and then register it
with the renderer.  This can be done by injecting it directly into the plugin
manager.

```php
// $view is a PhpRenderer instance

$helper = new MyModule\View\Helper\LowerCase;
// ...do some configuration or dependency injection...

$view->getHelperPluginManager()->setService('lowercase', $helper);
```

The plugin manager will validate the helper/plugin, and if the validation
passes, the helper/plugin will be registered.
