GUID Lookup helper ohne Abhängigkeiten

Es wird mit zunehmendem Feature Creep (sorry für das böse Wort) leider erforderlich, auch für eigentlich triviale Vorgänge wie das Anlegen eines Ereignisses relativ sperrige GUIDs zur Hand zu haben. Für faule Menschen wie mich, die keine Lust haben, alleine zum Nachsehen dieser GUIDs ständig in die Doku zu schauen, habe ich mir ein kleines Hilfs-Skript für die __autoload.php geschrieben.

Nun wäre es natürlich kein Hexenwerk, einfach in der __autoload.php Konstanten zu definieren, aber das führt zu einer dauerhaften Abhängigkeit und meine Skripte funktionieren dann ohne weitere Anpassungen nur bei mir.

Also wollte ich eine Lösung, die für mich bequem ist, mir also das Nachsehen von unmöglich zu merkenden Strings erspart, aber zugleich ermöglicht, dass mein Code portabel bleibt.

Die Funktion mit dem Namen __GUID__ habe ich analog zu gewissen existierenden Pseudo-Konstanten wie __FUNCTION__ (gibt in php den Namen der aktuellen Funktion zurück) benannt. Dahinter ist in Klammern ein String-Literal in doppelten Anführungszeichen anzugeben, das entweder dem Namen eines Moduls, oder auch einer Aktion entspricht. Also beispielsweise

IPS_SetEventAction($event_id, __GUID__("Run Automation"), []);

oder auch

$ac_id = IPS_GetInstanceListByModuleID(__GUID__("Archive Control"))[0];

So weit, so gut - doch wie soll das jetzt portabel bleiben und nicht dauerhaft abhängig von der Definition eben dieser __GUID__-Funktion?

Der Clou ist, dass die Funktion sich den Skript-Code vornimmt und die Stellen, an denen sie aufgerufen wird, dort automatisch durch ein String-Literal mit der jeweiligen GUID, begleitet durch einen Kommentar, ersetzt. Dies geschieht einmal beim direkten Aufruf aus der Konsole und ist im Skriptfenster auch sofort sichtbar. Ab diesem Zeitpunkt wird die Funktion __GUID__ in diesem Skript nicht mehr referenziert und nicht mehr benötigt.

Aus den beiden oben gezeigten Beispielen wird also nach Klick auf den „Ausführen“ button automatisch folgendes:

IPS_SetEventAction($event_id, "{7938A5A2-0981-5FE0-BE6C-8AA610D654EB}" /* Action ID for "Run Automation" */, []);

bzw.

$ac_id = IPS_GetInstanceListByModuleID("{43192F0B-135B-4CE7-A0A7-1475603F3060}" /* Module ID of Archive Control */)[0];

Es folgt nun der Code der __GUID__-Funktion, welcher in die Datei __autoload.php einzutragen ist:

/* Use the function call __GUID__() in a script in order to obtain the GUID
   of a certain Module or Action. The function will make changes to the script itself
   upon first execution within the console, replacing the point of this function's
   invocation with a string constant containing the GUID and a comment describing
   what's referenced by the GUID.
   Thereby, the script using this function call remains entirely portable after its
   first execution, as this function is no longer required for it to run. Put this
   function in your __autoload.php to have it available whenever writing your own
   scripts.

   Parameter $name can bei either the name of a module (like "Archive Control") or
   the name of an action (like "Run Automation").
*/
function __GUID__($name) {
    $file_name = @IPS_GetScriptFile($_IPS['SELF']);
    if($file_name === false | $file_name == "") die("Auflösung von " . __FUNCTION__ . " fehlgeschlagen: Dateiname des Skripts nicht ermittelbar.");
    $lines = @file($file_name);
    if($lines === false) die("Auflösung von " . __FUNCTION__ . " fehlgeschlagen: Skript-Datei \"" . $file_name . "\" konnte nicht gelesen werden.");
    $module_guids = IPS_GetModuleList();
    $string_replacement = false;
    $result = false;
    foreach($module_guids as $guid) {
        $module = IPS_GetModule($guid);
        if($module["ModuleName"] == $name) {
            $string_replacement = "\"" . $guid . "\" /* Module ID of " . $name . " */";
            $result = $guid;
        }
    }
    if($string_replacement === false) {
        $actions = json_decode(IPS_GetActions(), true);
        $actions_matching = array();
        foreach($actions as $action) {
            if($action["caption"] == $name && count($action["form"]) == 0) $actions_matching[] = $action;
        }
        if(count($actions_matching) == 1) {
            $string_replacement = "\"" . $actions_matching[0]["id"] . "\" /* Action ID for \"" . $name . "\" */";
            $result = $actions_matching[0]["id"];
        } else if(count($actions_matching) > 1) {
            die("Auflösung von " . __FUNCTION__ . " fehlgeschlagen: Es existieren mehrere Aktionen mit dem Namen \"" . $name . "\" " . print_r($actions_matching, true));
        }
    }
    if($string_replacement === false) die("Auflösung von " . __FUNCTION__ . " fehlgeschlagen: GUID für \"" . $name . "\" nicht ermittelbar.");
    $string_to_replace = __FUNCTION__ . "(\"" . $name . "\")";
    $changes_made = false;
    foreach($lines as $index => $line) {
        if(strpos($line, $string_to_replace) !== false) {
            /*echo $string_to_replace . " found in line #" . ($index + 1) . "\n";
            echo "Old: " . $line;//*/
            $line = str_replace($string_to_replace, $string_replacement, $line);
            $lines[$index] = $line;
            //echo "New: " . $line;
            $changes_made = true;
        }
    }
    if($_IPS['SENDER'] == "Execute") {
        if(!$changes_made) die("Ersetzung von " . __FUNCTION__ . " fehlgeschlagen: Aufruf konnte im Skript-Code nicht gefunden werden. Bitte nur String-Literal in doppelten Anführungszeichen angeben!");
        IPS_SetScriptContent($_IPS['SELF'], implode('', $lines));
    }
    return $result;
} // __GUID__

Ein paar obligatorische Warnungen noch:

  • ich habe diese Funktion bei mir erfolgreich getestet und gehe davon aus, dass sie tut was sie soll und keinen Schaden anrichtet. Ich kann allerdings keinerlei Garantie übernehmen. Geht immer behutsam vor und macht Backups bevor ihr euch ins nächste Abenteuer stürzt. :wink:
  • die Funktion verändert das aufrufende Skript. Seid euch dessen bewusst. Wenn ihr bspw. aus irgendeinem Grunde den String __GUID__("Archive Control") (o.ä.) schon anderweitig verwendet, kommt es zu unerwünschtem Verhalten. Nein, ich halte das nicht für wahrscheinlich. Aber ich wollte es erwähnt haben.
1 „Gefällt mir“