How to create a file watcher with PHP and the Elementary Framework
Nana Axel
Posted on July 26, 2019
Introduction
Welcome everyone to this second How-To about the Elementary Framework (for those who have missed the first How-To, check it here).
In this article we will talk about the creation of a file watcher, a console app which will watch files in a directory and report to us any changes made to these files (deletion, creation, modification), written in the PHP language using FireFS, a part of my framework.
Creating a file watcher it's just a possibility of FireFS between much more others.
Prerequisites
Before start, make sure you have:
A PHP IDE or editor: You can download Visual Studio Code, it's a free and powerful code editor which can support many languages (including PHP).
Composer: Composer is a dependencies manager for PHP projects. You can download it at https://getcomposer.org/
Part 1 - Preparing the project
The first thing we have to do is to create our new project. Create a new folder with the name you want, and open a terminal into it.
FireFS is a composer package, so we need first to prepare our project to use composer. In the terminal you previuously opened, type:
composer init
This will initialize our project with composer support. Follow the initialization process to the end.
Now, in the same terminal, we can install FireFS with the command:
composer require elementaryframework/fire-fs
And wait the process finish, this will download the source code of the FireFS project and install it in your own. When the installation succeed, type again:
composer dump-autoload -o
This will generate an optimized autoloader file (useful for production). For a normal autoload file, just omit the -o switch.
Once FireFS is installed, we can now create the command line tool to watch our files. Create a file named watch inside your project folder and insert into it:
#!/usr/bin/env php
<?php// Require composer autoload filerequire"./vendor/autoload.php";// Print a message to the consoleprint"Hello world, this is a file watcher!";
What we are doing ?
We first require the autoloader file from composer
We print a message to the console output
To test if everything is working, just type in the terminal (while the working directory is the same as our project):
php watch
The console will print the text "Hello world, this is a file watcher!" before exit.
Note that the file name is watch (without any extension), and not watch.php
Part 2 - Our file watcher
Building a file watcher with FireFS is very easy, we just have to create a new FileSystemWatcher object, follow the build pattern and start it, easy, right?
The FireFS object
In FireFS, instances of the FireFS class represent a file system, and manage every files and folders in the given root directory. When creating a file watcher we have first to create a file system:
// We use the FireFS classuseElementaryFramework\FireFS\FireFS;// We create a new file system on the current directory$fs=newFireFS();
This will create a file system at the same path than our watch file (to change the root path, just define your prefered path as the first parameter of the FireFS class constructor).
The watched folder
Now we have to create the folder which will be watched. We can let our console app do it itself by using the great FireFS API:
// Check if the directory to watch existsif(!$fs->exists("files_to_watch")){// If not, create the directory$fs->mkdir("files_to_watch");}
This will create the directory files_to_watch only if it not exists at each run of our file watcher.
The IFileSystemListener object
Before enjoying our file watcher, we have to create a class implementing the IFileSystemListener interface. This class is used by the watcher to execute specific actions following the watch event (create, delete, update).
// We use the IFileSystemWatcher interfaceuseElementaryFramework\FireFS\Listener\IFileSystemListener;// We use the FileSystemEvent classuseElementaryFramework\FireFS\Events\FileSystemEvent;// Our listenerclassWatcherListenerimplementsIFileSystemListener{/**
* Action executed on any event.
* The returned boolean will define if the
* specific event handler (onCreated, onModified, onDeleted)
* have to be called after this call.
*/publicfunctiononAny(FileSystemEvent$event):bool{$eventType=$event->getEventType();$date=date("d/m/Y H:i:s");if($eventType===FileSystemEvent::EVENT_UNKNOWN){returntrue;}switch($eventType){caseFileSystemEvent::EVENT_CREATE:print"{$date} - [Created] {$event->getPath()}\n";break;caseFileSystemEvent::EVENT_MODIFY:print"{$date} - [Updated] {$event->getPath()}\n";break;caseFileSystemEvent::EVENT_DELETE:print"{$date} - [Deleted] {$event->getPath()}\n";break;}returnfalse;}/**
* Action executed when a file/folder is created.
*/publicfunctiononCreated(FileSystemEvent$event){}/**
* Action executed when a file/folder is updated.
*/publicfunctiononModified(FileSystemEvent$event){}/**
* Action executed when a file/folder is deleted.
*/publicfunctiononDeleted(FileSystemEvent$event){}}
The FileSystemWatcher object
Once we have a file system object and we know the directory (or the file) we want to watch, we can create the file watcher:
// We use the FileSystemWatcher classuseElementaryFramework\FireFS\Watcher\FileSystemWatcher;// Create the file watcher$watcher=newFileSystemWatcher($fs);
The next step is to configure our watcher. The configuration is done by calling some methods:
setListener(IFileSystemListener): Defines the IFileSystemListener instance used to execute specific actions on each files updates ;
setPath(string): Defines the path of directory/file to watch ;
setRecursive(bool): Defines if the watcher will watch for files recursively (works only if the watched entity is a directory) ;
addPattern(Regex): Add a files-to-watch pattern ;
addExcludePattern(Regex): Add a files-to-exclude pattern ;
setPattern(array): Set an array of files-to-watch patterns. This will overwrite any existing pattern ;
setExcludePattern(array): Set an array of files-to-exclude patterns. This will overwrite any existing pattern ;
setWatchInterval(int): Set the interval in milliseconds in which the watcher will process a watch event.
So we will build our watcher like this:
$watcher->setListener(newWatcherListener)->setRecursive(true)->setPath("./files_to_watch")->setWatchInterval(250)->build();// It's important to call build to validate the configuration
Important to know: The FileSystemWatcher class have some default exclude patterns, check them here
At this time, everything is set! Now the last thing to do is to start the watcher:
// Start the file watcher$watcher->start();
The complete watch file
We just have finished to create our file watcher, there is the complete file content:
#!/usr/bin/env php
<?php// Require composer autoloaderrequire_once__DIR__.'/vendor/autoload.php';// We use the FireFS classuseElementaryFramework\FireFS\FireFS;// We use the IFileSystemWatcher interfaceuseElementaryFramework\FireFS\Listener\IFileSystemListener;// We use the FileSystemEvent classuseElementaryFramework\FireFS\Events\FileSystemEvent;// We use the FileSystemWatcher classuseElementaryFramework\FireFS\Watcher\FileSystemWatcher;// We create a new file system on the current directory$fs=newFireFS();// Check if the directory to watch existsif(!$fs->exists("files_to_watch")){// If not, create the directory$fs->mkdir("files_to_watch");}// Our listenerclassWatcherListenerimplementsIFileSystemListener{/**
* Action executed on any event.
* The returned boolean will define if the
* specific event handler (onCreated, onModified, onDeleted)
* have to be called after this call.
*/publicfunctiononAny(FileSystemEvent$event):bool{$eventType=$event->getEventType();$date=date("d/m/Y H:i:s");if($eventType===FileSystemEvent::EVENT_UNKNOWN){returntrue;}switch($eventType){caseFileSystemEvent::EVENT_CREATE:print"{$date} - [Created] {$event->getPath()}\n";break;caseFileSystemEvent::EVENT_MODIFY:print"{$date} - [Updated] {$event->getPath()}\n";break;caseFileSystemEvent::EVENT_DELETE:print"{$date} - [Deleted] {$event->getPath()}\n";break;}returnfalse;}/**
* Action executed when a file/folder is created.
*/publicfunctiononCreated(FileSystemEvent$event){}/**
* Action executed when a file/folder is updated.
*/publicfunctiononModified(FileSystemEvent$event){}/**
* Action executed when a file/folder is deleted.
*/publicfunctiononDeleted(FileSystemEvent$event){}}// Create the file watcher$watcher=newFileSystemWatcher($fs);$watcher->setListener(newWatcherListener)->setPath("./files_to_watch")->setWatchInterval(250)->build();// It's important to call build to validate the configuration// Start the file watcher$watcher->start();
Now run this file using the command php watch, a new folder files_to_watch is directly created and the console wait for files changes. Try to create, delete or edit files inside the files_to_watch folder and you will be directly notified for these actions inside the console!
With this article, we have learn How to create a file watcher with PHP and the Elementary Framework. Using the FireFS module of the framework, we have seen steps by steps how it's very easy to implement a functionality like this. Now with the knowledge you have you can create more than a simple event reporting app, from crons on your webserver, to your own Jekyll PHP edition ;-), you have everything you need to go further.
What next?
Browse the FireFS source code on GitHub - and give a star if you like it - ;
FireFS is a library allowing you to write/read/delete files and folders of your file system, safely and easily.
It can be used for web applications as well for console applications, without any requirements.
Example
<?phpuseElementaryFramework\FireFS\FireFS;
// Create a new file system instance at the given path$fs = newFireFS("./app"); // /root/var/www/htdocs/app/// Check if the path "/root/var/www/htdocs/app/images/" existsif ($fs->exists("images")) {
// Change the working directory to the images folder$fs->setWorkingDirectory("./images");
// Create a new file in the working directory$fs->mkfile("./logo.png"); // /root/var/www/htdocs/app/images/logo.png// Read file from the file system root path$logo = $fs->read("logo.png"