Fingerprinting and code execution when something changes¶
Castor provides a fingerprint
functionality to execute tasks only if something
changed. To detect whether something has changed, Castor uses a fingerprint that
can be anything like a string content, the hash of a file or a group of files
(based on content or modification time), etc.
The fingerprint()
function¶
You can use the fingerprint()
function to conditionally execute a callback if
the given fingerprint has changed.
use Castor\Attribute\AsTask;
use function Castor\fingerprint;
use function Castor\io;
#[AsTask(description: 'Execute a callback only if the fingerprint has changed')]
function task_with_a_fingerprint(): void
{
fingerprint(
callback: function () {
io()->writeln('Cool, no fingerprint! Executing...');
},
id: 'my-fingerprint',
fingerprint: "my fingerprint",
);
}
Note
You can use the $force
parameter of the fingerprint()
function to force
the execution of the callback even if the fingerprint has not changed.
Note
By default the fingerprint is scoped to the current project, but you can use
the $global
parameter to make it shared across all projects.
The hasher()
function¶
Most of the time, you will want your fingerprint hash to be based on the content
of a file, to scope it to a specific task or something else. To help you compute
this hash, Castor provides a hasher()
function. It returns an instance of
Castor\Helper\HasherHelper
with various helper methods:
write()
: Writes a hash of a specific (string) value.writeFile()
: Writes a hash of a file content or its modification time.writeWithFinder()
: Writes a hash of a group of files obtained through Finder.writeGlob()
: Writes a hash of a group of files obtained via a glob pattern.writeTaskName()
: Writes the name of the current task.writeTaskArgs()
: Writes arguments passed to the current task.writeTask()
: Writes a combination of the current task name and arguments.finish()
: Finalizes the hash operation, returning a string of the hash.
The methods writeFile()
, writeWithFinder()
and writeGlob()
accept a second
parameter $strategy
to specify on with criteria the hash should be based on.
This parameter is a Castor\Fingerprint\FileHashStrategy
enum that contains two
values:
Content
will make the hash dependent on the file's contentMTimes
will make the hash depend on the file's last modification time. This is the default strategy.
Example usage:
use Castor\Attribute\AsTask;
use Castor\Fingerprint\FileHashStrategy;
use function Castor\fingerprint;
use function Castor\hasher;
#[AsTask(description: 'Execute a callback only if the fingerprint has changed')]
function task_with_a_fingerprint(): void
{
fingerprint(
callback: function () {
io()->writeln('Executing the callback because my-file.json has changed.');
},
id: 'task-id',
fingerprint: hasher()->writeFile('my-file.json', FileHashStrategy::Content)->finish(),
);
}
The fingerprint_exists()
and fingerprint_save()
functions¶
If you want more control over the fingerprint behaviour, you can use the
fingerprint_exists()
and fingerprint_save()
functions to conditionally
execute your code:
use Castor\Attribute\AsTask;
use Castor\Fingerprint\FileHashStrategy;
use function Castor\finder;
use function Castor\fingerprint_exists;
use function Castor\fingerprint_save;
use function Castor\hasher;
#[AsTask(description: 'Check if the fingerprint has changed before executing some code')]
function task_with_some_fingerprint(): void
{
if (!fingerprint_exists('task-id', my_fingerprint_check())) {
io()->writeln('Executing some code because fingerprint has changed.');
fingerprint_save('task-id', my_fingerprint_check());
}
}
function my_fingerprint_check(): string
{
return hasher()
->writeWithFinder(
finder()
->in(__DIR__)
->name('*.json')
->files(),
FileHashStrategy::Content
)
->finish();
}