Display Desktop Notifications in PHP CLI Applications
Tags: laravel, webdev, php
Published: 2024-01-30 11:50:41

## Introduction

There may be times when you're building a long-running script in PHP and you want to notify the user when the script has finished running. This could be to let them know that the script has finished successfully or to let them know that there was an error.

I've wanted to do this a few times after creating some long-running Artisan commands in my Laravel projects.

A great (and really cool!) way to do this is to use desktop notifications.

In this article, we're going to take a look at how to use the "JoliNotif" (`jolicode/jolinotif`) package to display desktop notifications from your PHP CLI applications. I'm mainly a Laravel developer, so the examples will be based around triggering the notifications from Laravel Artisan commands, but the concepts should be easy enough to apply to our PHP CLI applications.

## What is JoliNotif?

[JoliNotif](https://github.com/jolicode/JoliNotif) is a cross-platform package that allows you to display desktop notifications from your PHP applications running on Windows, macOS, and Linux.

It was built and is maintained by [JoliCode](https://github.com/jolicode). At the time of writing this article, it has over 5.2 million downloads and 1.3k stars on GitHub.

It's a great package and is super easy to use!

A few years ago, I built a small Laravel package as a proof of concept called "[Laravel Executor](https://github.com/ash-jc-allen/laravel-executor)". It was a package that allowed you to build scripts using PHP and then execute them from an Artisan command. The idea behind it was that you could build a deployment script or other utilities with it. It was mainly built as a fun little project for me to practice my web development skills and to [learn more about building Laravel packages](https://ashallendesign.co.uk/blog/5-lessons-i-learned-from-building-my-own-laravel-package).

I used JoliNotif in Laravel Executor so that developers could add notifications to their Artisan commands. I thought it was a cool little feature that added that extra bit of polish to the package.

If you're building a package, CLI application, or Laravel Artisan command, you might want also want to add desktop notifications to it.

So let's get stuck in and take a look at how to use it!

## Install the Package

To get started with using `jolicode/jolinotif`, we'll need to install it into our project using Composer. We can do this by running the following command in our project root:

```text
composer require jolicode/jolinotif
```

The package should now be installed and ready to use!

## Create a Basic Notification

To create a notification, we first need to create a new `Joli\JoliNotif\Notification` class instance. This is the object that holds the data about the notification you want to display to the user.

After we've created the new instance, we want to pass it to a "Notifier" which is responsible for displaying the notification to the user. We'll cover notifiers in a bit more detail later in this article. You can access the notifier using the `NotifierFactory::create()` method.

To send a basic notification, we can do the following:

```php
use Joli\JoliNotif\Notification;
use Joli\JoliNotif\NotifierFactory;

$notification = (new Notification())
    ->setBody('The long script has finished!')
    ->setTitle('My Awesome PHP CLI App!');
    
NotifierFactory::create()->send($notification);
```

Running the following code above would result in a desktop notification being displayed to the user that looks something like this:

![Basic desktop notification](https://ashallendesign.ams3.cdn.digitaloceanspaces.com/public/blog/163/desktop-notification.png)

It's worth noting that the body of the notification (set using the `setBody` method) is a required field. If you don't set it, then an exception will be thrown when trying to display the notification.

To give some context as to how you might want to use this in a Laravel Artisan command, let's look at a quick example. We'll imagine that we've built a long-running Artisan command that does some processing and we want to notify the user when it's finished. Our command might look something like this:

```php
declare(strict_types=1);

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Joli\JoliNotif\Notification;
use Joli\JoliNotif\NotifierFactory;

final class DesktopNotification extends Command
{
    protected $signature = 'app:desktop-notification';

    protected $description = 'Run a long script';

    public function handle(): int
    {
        // Run the long script here...

        $this->sendDesktopNotification();

        return self::SUCCESS;
    }

    private function sendDesktopNotification(): void
    {
        $notification = (new Notification())
            ->setBody('The long script has finished!')
            ->setTitle('My Awesome PHP CLI App!');

        NotifierFactory::create()->send($notification);
    }
}
```

## Customise the Notification

Depending on the notifier that's being used, you can customise the notification in a number of ways.

### Change the Subtitle

Some notifiers allow you to add a subtitle to the notification. This is a small piece of text that's displayed underneath the title of the notification. To add a subtitle to the notification, you can use the `addOption` method on the notification object like so:

```php
$notification = (new Notification())
    ->setBody('The long script has finished!')
    ->setTitle('My Awesome PHP CLI App!')
    ->addOption('subtitle', 'This is a subtitle');

NotifierFactory::create()->send($notification);
```

Running the above code on a macOS machine would result in a notification that looks something like this:

![Basic notification with subtitle](https://ashallendesign.ams3.cdn.digitaloceanspaces.com/public/blog/163/desktop-notification-with-subtitle.png)

It's worth noting that if the notifier doesn't support subtitles, then it will just be ignored and not displayed to the user.

### Change the Sound

Depending on the notifier that's being used, you can also change the sound that's played when the notification is displayed to the user. To do this, you can use the `addOption` method on the notification object like so:

```php
$notification = (new Notification())
    ->setBody('The long script has finished!')
    ->setTitle('My Awesome PHP CLI App!')
    ->addOption('sound', 'Funk');

NotifierFactory::create()->send($notification);
```

The GitHub repo lists the following sounds that are available to use on a macOS machine: Basso, Frog, Hero, Pop, Submarine, Blow, Funk, Morse, Purr, Tink, Bottle, Glass, Ping, Sosumi.

However, this is a non-exhaustive list, so you might want to do some investigating to see which sounds are available on the OS that you're intending the application to run on.

### Change the Icon

Some (but not many) of the available notifiers allow you to change the icon of the notification. This is great for adding a bit of extra branding to your notifications if they're going to be running on another user's machine.

To change the icon of the notification, you can use the `setIcon` method on the notification object like so:

```php
$notification = (new Notification())
    ->setBody('The long script has finished!')
    ->setTitle('My Awesome PHP CLI App!')
    ->setIcon('/absolute/path/to/my/icon.png');

NotifierFactory::create()->send($notification);
```

As we can see in the code example above, we've passed the icon's absolute path to the `setIcon` method.

If you attempt to set an icon and the machine's notifier doesn't support it, then it will just be ignored and not displayed to the user.

## Notifiers

As we've briefly mentioned, JoliNotif uses the concept of "notifiers" to display the notifications on the user's desktop. These are the underlying notification systems that are available on the device that can be used to trigger desktop notifications. By default, it will attempt to resolve a notifier based on the operating system that the application is running on.

As we've mentioned with the customisation options above, due to the small differences between the notifiers, it means that there may be some features that are available on some operating systems but not on others.

If a notifier can't be found that the package can use and is supported by the OS, then no errors will be thrown. Instead, the notification will just be ignored and not displayed to the user. Depending on the context of what the notification was used for, this might not be an issue.

### Available Notifiers

At the time of writing this article, JoliNotif supports the following notifiers:

- **Linux**
  - **KDialogNotifier** - Supports: Body, Title
  - **NotifySendNotifier** - Supports: Body, Title, Icon
- **macOS**
  - **GrowlNotifyNotifier** - Supports: Body, Title, Icon
  - **TerminalNotifierNotifier** - Supports: Body, Title
  - **AppleScriptNotifier** - Supports: Body, Title
- **Windows**
    - **SnoreToastNotifier** - Supports: Body, Title, Icon
    - **ToasterNotifier** - Supports: Body, Title, Icon
    - **NotifuNotifier** - Supports: Body, Title, Icon

### Manually Specifying a Notifier

There may be times when you want to manually specify a notifier (or group of notifiers) to attempt to use. This could be due to you wanting to use a specific feature that's only available on a certain notifier.

To do this, you can pass your desired notifier in an array to the `NotifierFactory::create()` method as the first argument. For example, if you wanted to use the `TerminalNotifier` you could do the following:

```php
NotifierFactory::create([new TerminalNotifierNotifier()])->send($notification);
```

In this case, JoliNotif will attempt to use the `TerminalNotifier` notifier to display the notification. If it can't find it, or if it's not supported by the OS, then the notification will be ignored. Otherwise, it will be displayed to the user.

### Creating Your Own Notifier

You may want to create your own notifier to use with JoliNotif. For example, you may want to use a notifier that's not currently supported by the package. Although this isn't something I've needed to do myself, it's still good to know that it's possible.

To create your own notifier, you'll need to create a class that implements the package's `Joli\JoliNotif\Notifier` interface. If you're unsure about what interfaces are, you might want to check out my "[Using Interfaces to Write Better PHP Code](https://ashallendesign.co.uk/blog/using-interfaces-to-write-better-php-code)" article. The interface looks like so:

```php
namespace Joli\JoliNotif;

interface Notifier
{
    public const PRIORITY_LOW = 0;
    public const PRIORITY_MEDIUM = 50;
    public const PRIORITY_HIGH = 100;

    /**
     * This method is called to check whether the notifier can be used on the
     * current system or not.
     */
    public function isSupported(): bool;

    /**
     * The supported notifier with the higher priority will be preferred.
     */
    public function getPriority(): int;

    /**
     * Send the given notification.
     *
     * @throws Exception\InvalidNotificationException if the notification is invalid
     */
    public function send(Notification $notification): bool;
}
```

So you may have a class that looks something like this:

```php
namespace App\Notifiers;

use Joli\JoliNotif\Notification;
use Joli\JoliNotif\Notifier;

final class MyCustomNotifier implements Notifier
{
    public function isSupported(): bool
    {
        // Check if the notifier is supported on the current system...
    }

    public function getPriority(): int
    {
        // Return the priority of the notifier...
    }

    public function send(Notification $notification): bool
    {
        // Send the notification...
    }
}
```

Once you've configured your new notifier, you can then pass it to the `NotifierFactory::create()` method as we did in the previous section like so:

```php
NotifierFactory::create([new MyCustomNotifier()])->send($notification);
```

If your new `MyCustomNotifier` class is supported by the current system, then your notifier's `send` method will be called and the notification will be displayed to the user.

## Other Types of Notifications

In this article, we've covered how to display desktop notifications from your PHP CLI applications. But there may be times that you want to notify your users in other ways, such as sending SMS messages, emails, broadcasts using WebSockets, or even WhatsApp messages.

Of course, the use case for using these types of notifications is a little different to using desktop notifications. But if you're interested in learning more about these types of notifications, you might want to check out some of my other articles:

👉 [Send an SMS in Laravel Using Vonage (Previously Nexmo)](https://ashallendesign.co.uk/blog/send-an-sms-in-laravel-using-vonage-previously-nexmo)

👉 [Send WhatsApp Messages in Laravel With Vonage's Native SDK](https://ashallendesign.co.uk/blog/send-whatsapp-messages-in-laravel-with-vonage-s-native-sdk)

👉 [A Guide to Using Websockets in Laravel](https://ashallendesign.co.uk/blog/a-guide-to-using-websockets-in-laravel)

## Conclusion

Hopefully, this article has given a quick insight into how you can use the `jolicode/jolinotif` package to display desktop notifications from your PHP CLI applications and Laravel Artisan commands.

If you enjoyed reading this post, I'd love to hear about it. Likewise, if you have any feedback to improve the future ones, I'd also love to hear that too.

You might also be interested in checking out my 220+ page ebook "[Battle Ready Laravel](https://battle-ready-laravel.com)" which covers similar topics in more depth.

Or, you might want to check out my other 440+ ebook "[Consuming APIs in Laravel](https://consuming-apis-in-laravel.com)" which teaches you how to use Laravel to consume APIs from other services.

Keep on building awesome stuff! 🚀