WebsSockets with kirby?

Hi
I cannot find anything about WebSockets in kirby.
I need some kind of notification to all users when a datatable has changed.
Maybe there is another way to solve this in kirby?

Kind regards
Alain

1 Like

Kirby doesn’t offer any help with creating or using websockets. I guess this is because in environments where php is used, websockets aren’t normally supported (think about shared hostings - they can’t allow your php process to open new ports). And probably also because that would be something utterly specific for a content management system to offer.

Kirby though is very extensible, so if you are in an environment that supports websockets (that would be a dedicated server or a VM) you could set something up with other php libraries and notify users about the changes by configuring a page update hook (https://getkirby.com/docs/reference/plugins/hooks/page-update-after).

If you aren’t in an environment supporting websockets (shared hosting) you could either let the clients “poll” every n seconds for updates by making an API call, or integrate a third party solution like a firebase realtime database. Clients would connect to that instead. When something happens, your kirby hook would post an update to firebase and firebase would then broadcast that to all connected clients.

Thanks for your advice.
What about Server Send Events?

Do you think this could work as well?

I’m not at all familiar with the eventsource api (honestly I’ve never used it and it’s kinda the first time I look into it). But it looks like some formalized way of doing “ajax long polling”. This means you still have a long running php process for each open browser tab and I don’t know how happy hosting providers are about this.
Another problem I see with PHP doing this, could be that you still need to get the events into that eventsource. Since in PHP every request is isolated from the others.
I mean that when someone updates a page, the kirby hook is run in the process that updated that page; there’s no clear way I can think of to get that “event” into the other process that produces the eventsource stream for the browser. I guess that could be done for example with shared memory blocks (probably not installed in shared hostings), or something like named pipes, but that kinda gets complicated; at least for me.

I tried implementing something using websockets myself as well. Its a bit tricky, but you can manage your own script by calling it in a separate process.

See this question for the code-snippet. I asked there if there is a way to do post-request / post-route processing, but unfortunately I think the external process is the only way to make things work without editing the Kirby-installation, or without .htaccess magic, as far as I can figure out.

Keep in mind though that every other aspect of the websocket stuff (or Server-sent-events for your second suggestion) you need to implement yourself (either by third-party implementation, or a custom one).

@Rasteiner Thanks again for your explanations.

Everytime when the hook gets envoked, the “Server-Sent Event Server”, for example sse_server-php, should get the new data. I’m thinking about a global variable which both, the hook and der Server-Sent Server can read and write.
To me this does not sound like something unsolvable, but I’m not experianced enough as well :blush:

Does anybody have idea how to solve this?

Edit 06.11.20:
I tried this:

/site/config/config.php

return [
'hooks' => [
'page.update:after' => function ($newPage, $oldPage) {
    global $message;
    $message = 'we have a change';
}
]
];

/site/config/sse_manager.php

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

global $message;
static $message='';

echo "data: Servermessage: {$message}\n\n";
flush();

Javascript in the Template (before the closing Body-Tag)

var source = new EventSource("/site/config/sse_manger.php");
source.onmessage = function(event) {
    console.log(event.data);
};

The output in the console is always empty ($message is always an empty string).
The Hook works, I have tested it.
The SSE Stream works as well.

I guess the problem is in my lack of understanding the basics of php.
But maybe some can guide me to the right direction?

Edit 2 on 06.11.20
I saw in the documentation, that I can handle hooks with “options”.
For testing purpose I did put the following in sse_manager.php

$kirby->option('someOtherSetting');

And in config.php I added:

'someOtherSetting' => 'Hello message'

But $kirby->option('someOtherSetting'); is not available in sse_manager.php (Error: Uncaught Error: Call to a member function option() on null in sse_manager.php).

How can I access to that kiry option helper?

I’m sorry if my questions are to dumb for you :slight_smile:

A PHP process can’t access variables of other processes. And as a simplification, think that each request is a new process. It would be like one program wanting to read a variable in the memory of another program - that’s normally not how it works.

So, no matter how much you declare a variable as global, it doesn’t span over different processes.
If you want to communicate between processes, you need to get some kind of “inter process communication” happening - which is not a trivial argument - especially on shared hosting environments where providers typically want to isolate as much as possible each request (they obviously don’t want any risk of a website interfering with other websites).

In not shared hostings, you would, for example, set up something like a redis server that offers you a pub/sub service. In pure php, I honestly am not sure how I would proceed…
Probably ignore everything and simply poll for updates from the client, like every minute or so (don’t know how fast you need those notifications).

You can’t. Or actually you could, but it wouldn’t help you.
Even if you load kirby (by including kirby/bootstrap.php), the hooks you register in that process / request would never be executed. That’s simply not how PHP works.
You need to think about PHP scripts as processes that exist only for the moment they generate a response for a web request. They are very volatile programs: a webserver (like apache) starts them when it receives a request “that should be handled by php”. Immediately they generate a response and return that to the webserver. Then, they immediately exit. Therefore a “hook” can only be executed in the context of the request that caused it.
Even if two processes happen to be started in the same moment, they’re still isolated from each other.

Not dumb, the answer is just complicated :slight_smile:

@Resteiner
Thanks alot for clearing the dust in my brain a bit :slight_smile:

That means, index.php is the first and only one process that gets invoked when I visit a kirby website? And every other php File which is included somehow withing index.php is isolated in that same process?

As you suggested, I could just permanently reload the data from the server, but in my case it should be relatively fast, like every second or so. I don’t know how many users a server can handle when pulling data every seconde. What do you think?

Maybe I should also check out your first suggestion with firebase. But it sounds to me like a too long detour and to prone to errors for something simple like update notifications.

Just another question of pure curiousity:
How do you realize a chat application on a LAMP Stack, woudn’t you encounter the same problems?

exactly :slight_smile:

It really depends on the nature of the data you wanna people to be updated about. Is it “big”?
If it’s “small”, you could think to write that data (when it changes) to a simple text file somewhere on the server and people would constantly reload that textfile. That text file would be served directly by the webserver, without starting up PHP and kirby, and the load would therefore be relatively light.
When the client notices a change between a loaded value and the previous one, it shows a notification.
I honestly don’t know what would be worse for a server: having 1000 people constantly loading the same 12bytes textfile, or having 1000 people connected each to their own long running PHP process (that would be the case of doing it with server sent events).

Yes you would. Realistically, you probably wouldn’t want to write a popular chat app on a LAMP stack. At least not that kind of LAMP stack you get in a shared hosting environment.
More generally speaking, “LAMP” could also mean: Linux (well, of course), Apache (as reverse proxy), MySQL (for permanent storage), PHP (as webserver, for example with something like AMPHP), but this kind of stack can’t work in shared hostings.

if you wanna go down a rabbit hole, you could try to set up some kind of IPC (inter process communication) with something like named pipes. The php function involved would be “posix_mkfifo”, maybe check if it’s available on your hosting as some of them probably disable it, and you need to have the posix extension enabled.
Or you could try and do something with simple files, like: the hook writes a file, the event source reads the file. You probably would want to familiarize with the stream functions (make it so that the event source fgets and “waits” for content to be written to the file).
You’ll notice it gets complicated pretty soon :wink: