Blog

Käpt’n Hook und die Module

28.07.2023 // Renée Bäcker

Einige Kernfunktionen in Perl können nur schwer überschrieben werden – jedenfalls für die meisten von uns. Eine dieser Funktionen ist require. Um das Verhalten solcher Funktionen zu verändern oder auf die Ausführung der Funktionen reagieren zu können, wurde in Perl 5.38 der Spezialhash %{^HOOK} eingeführt.

In diesem Hash werden Codereferenzen gespeichert. Der Schlüsselname wird zusammengesetzt aus dem Namen der Funktion, zwei Unterstrichen _ und der Phase wann diese Codereferenz ausgeführt wird (derzeit gibt es nur die zwei Phasen before und after).

Im Beispiel von require können also Codereferenzen zu den Schlüsseln require__before und require__after gespeichert werden.

Beide Hooks bekommen als einzigen Parameter den Dateinamen übergeben, dabei wird der Paketname in einen Pfad umgewandelt (MIME::Base64 wird dabei zu MIME/Base64.pm umgewandelt).

Bei der Verwendung ist es wichtig, dass die Hooks vor dem Einbinden weiterer Module gesetzt werden. Daher muss man die entsprechenden Hooks in einem BEGIN{}-Block definieren:

#!/usr/bin/perl

use v5.38;

use strict;
use warnings;

BEGIN {
    %{^HOOK} = (
        require__before => sub ($path) {
            say "I try to require $path...";
        },
        require__after => sub ($path) {
            if ( $INC{$path} ) {
                # loading the module was successful
                say "welcome to the app, $path!";
            }
        }
    );
}   

use Mojo::File;

say "done...";

require__before wird dabei aufgerufen, bevor %INC geprüft, @INC durchsucht, INC-Hooks aufgerufen werden. require__after wird unabhängig vom Erfolg aufgerufen, aus diesem Grund wird in dem Beispielcode auch der Eintrag in %INC geprüft. Der before-Hook wird in der Reihenfolge der require-Aufrufe ausgeführt, der after-Hook in umgekehrter Reihenfolge.

Diese Hooks können gut zum Debugging genutzt werden, wenn man wissen möchte, welche Module eingebunden werden und wer diese Module einbindet:

use v5.38;

my %modules;
use Data::Dumper;

BEGIN {
    %{^HOOK} = (
        require__before => sub ($path) {
            my @info = caller(0);
            $modules{$path}->{$info[0]}++;
        },
    );
}

use OPM::Validate;
use Mojo::File;

print Dumper \%modules;
say "done...";



Permalink: