commit e9d2257d73f87e359dcb1f31135535fdfc02926b Author: Tim van den Boom Date: Fri Jan 2 20:52:43 2026 +0100 Initial commit diff --git a/actions/apply-settings.php b/actions/apply-settings.php new file mode 100644 index 0000000..b88206f --- /dev/null +++ b/actions/apply-settings.php @@ -0,0 +1,22 @@ + diff --git a/actions/bridge-editor.php b/actions/bridge-editor.php new file mode 100644 index 0000000..3cb07a1 --- /dev/null +++ b/actions/bridge-editor.php @@ -0,0 +1,131 @@ +exec(" DELETE FROM nodes WHERE required_by = '" . $bridge_name . "' and plug_id = '" . $plug . "'; "); + + foreach($nodes as $node) { + $from = $node[0]; + $to = $node[1]; + $db->exec(" INSERT INTO nodes (required_by, node_from, node_to, plug_id) VALUES ('" . $bridge_name . "', '" . $from . "', '" . $to . "', '" . $plug . "'); "); + } + + $message[0] = true; + $message[1] = "Die Änderungen für die Brücke $bridgeName wurden gespeichert!"; + $message = json_encode($message); + print ($message); + die(); + + + break; + + case "add": + + // Eingabedaten + $bridgeName = $bridgeName; + $nodes = json_decode($meta, true); + $name = $nodes["name"]; + $nodes = $nodes["pins"]; + + cleanPinsArray($nodes); // Dies stellt sicher, dass Javscript valide Werte bringt und die Datenbank nicht verunreinigt wird. + + // sicherstellen, dass Werte gesetzt sind + if (empty($nodes) || empty($name)) + { + $message[0] = false; + $message[1] = "Es wurden keine Änderungen gespeichert, da Parameter fehlten."; + $message = json_encode($message); + print ($message); + die(); + } + + + + $result = $db->query("SELECT COUNT(*) as anzahl FROM bridges WHERE id = '" . $name . "';"); + $row = $result->fetchArray(SQLITE3_ASSOC); + $number = $row['anzahl']; + + if($number > 0) { + $message[0] = false; + $message[1] = "Es gibt bereits eine Brücke, die " . $name . " heißt. Bitte einen anderen Namen wählen oder die andere Brücke " . $name . " löschen/bearbeiten. Stellen Sie zudem sicher, dass Sie den korrekten Steckertypen verwenden."; + $message = json_encode($message); + print ($message); + die(); + } + + $db->exec(" INSERT INTO bridges (plug_id, id) VALUES (" . $plug . ", '" . $name . "'); "); + + foreach($nodes as $node) { + $from = $node[0]; + $to = $node[1]; + $db->exec(" INSERT INTO nodes (required_by, node_from, node_to, plug_id) VALUES ('" . $name . "', '" . $from . "', '" . $to . "', '" . $plug . "'); "); + } + + $message[0] = true; + $message[1] = "Die Brücke $name wurde angelegt!"; + $message = json_encode($message); + print ($message); + die(); + break; + + case "remove": + + $bridge_name = $meta; + + $db->exec(" DELETE FROM nodes WHERE required_by = '" . $bridge_name . "' and plug_id = '" . $plug . "'; "); + $db->exec(" DELETE FROM bridges WHERE id = '" . $bridge_name . "' and plug_id = '" . $plug . "'; "); + + $message[0] = true; + $message[1] = "Die Brücke $bridge_name wurde nun gelöscht."; + $message = json_encode($message); + print ($message); + die(); + + break; + +} + diff --git a/actions/connection-test.php b/actions/connection-test.php new file mode 100644 index 0000000..8736d2f --- /dev/null +++ b/actions/connection-test.php @@ -0,0 +1,5 @@ + diff --git a/actions/extract-programs.php b/actions/extract-programs.php new file mode 100644 index 0000000..66a325c --- /dev/null +++ b/actions/extract-programs.php @@ -0,0 +1,1182 @@ + $item['bridges'], + // Die Program-ID in ein Array einzelner Zeichen aufteilen + "programID" => str_split($item['id']), + "programDescription" => $item['descr'] + ]; + + // 4. Die neue Struktur in einen JSON-String umwandeln und in einer Variable speichern + // JSON_UNESCAPED_UNICODE sorgt für die korrekte Darstellung von Umlauten. + $formattedJson = json_encode($outputData, JSON_UNESCAPED_UNICODE); + + $meta = $formattedJson; + + + echo $meta; + + + + +$meta = json_decode($meta, true); +$bridges = $meta["bridges"]; +$programID = $meta["programID"]; +$programDescription = $meta["programDescription"]; + + + + +$result = $db->query("SELECT COUNT(*) as anzahl FROM program_id_bits WHERE +position = 0 AND value = '" . $programID[0] . "' AND +position = 1 AND value = '" . $programID[1] . "' AND +position = 2 AND value = '" . $programID[2] . "' AND +position = 3 AND value = '" . $programID[3] . "' AND +position = 4 AND value = '" . $programID[4] . "' +;"); + +$row = $result->fetchArray(SQLITE3_ASSOC); +$number = $row['anzahl']; + +if($number > 0) { + $message[0] = false; + $message[1] = "Diese Programm-ID existiert unter dem aktuell gewählten Stecker bereits. Bitte anderen Namen wählen oder den Stecker wechseln."; + $message = json_encode($message); + print($message); + die(); +} + +$db->exec(" INSERT INTO programs (plug_id, program_description) VALUES ('" . $plug . "', '" . $programDescription . "'); "); + +$program_id = $db->lastInsertRowID(); +foreach($bridges as $bridge) { + $db->exec(" INSERT INTO program_bridge_relation (program_id, bridge_id, plug_id) VALUES ('" . $program_id . "', '" . $bridge . "', '" . $plug . "'); "); +} + +for($i=0; $i<=4; $i++) { + $db->exec(" INSERT INTO program_id_bits (value, position, program_id) VALUES ('" . $programID[$i] . "', '" . $i . "', '" . $program_id . "'); "); +} + + +} + + + diff --git a/actions/inventory.php b/actions/inventory.php new file mode 100644 index 0000000..580d246 --- /dev/null +++ b/actions/inventory.php @@ -0,0 +1,138 @@ +exec(" DELETE FROM measurement_nodes WHERE required_by = '" . $meta . "'; "); + $db->exec(" DELETE FROM measurement_program_id_bits WHERE measurement_id = '" . $meta . "'; "); + $db->exec(" DELETE FROM measurements WHERE id = '" . $meta . "'; "); + + + $message[0] = true; + $message[1] = "Die Messung wurde gelöscht."; + $message = json_encode($message); + print($message); + die(); + + + + break; + + + case "add": + + $meta = json_decode($meta, true); + + if(empty($meta["programCache"]) || empty($_SESSION["bridges"]) || empty($meta['name'])) { + $message[0] = false; + $message[1] = "Es gab einen Fehler beim Speichern!"; + $message = json_encode($message); + print($message); + die(); + } + + + $db->exec("INSERT INTO places (plug_id, name) VALUES (" . $plug . ", '" . $meta["name"]. "'); "); + + $newdata["plugID"] = $plug; + $newdata["place"] = $placeID; + $newdata["programID"] = $meta["programCache"]; + $bridges_original = $_SESSION["bridges"]; + $newdata["special"] = $meta["special"]; + $bridges_hidden = json_decode(urldecode($meta["bridges-hidden"])); + $newdata["timestamp"] = time(); + + + + // $bridges_result = $bridges_original - $bridges_hidden + $bridges_original; + $bridges_hidden; + + + + // Hilfsfunktion: vergleicht zwei Arrays inhaltlich, unabhängig von String/Int + function arrays_are_equal($a, $b) { + return ((string)$a[0] === (string)$b[0]) && ((string)$a[1] === (string)$b[1]); + } + + // Ergebnisarray initialisieren + $bridges_result = []; + + // Für jedes Originalelement prüfen, ob es in hidden vorkommt + foreach ($bridges_original as $orig) { + $found = false; + foreach ($bridges_hidden as $hidden) { + if (arrays_are_equal($orig, $hidden)) { + $found = true; + break; + } + } + if (!$found) { + $bridges_result[] = $orig; + } + } + + + $newdata["bridges"] = $bridges_result; + + + + + $db->exec("INSERT INTO measurements (place_name, comment, timestamp) VALUES ('" . $meta["name"] . "', '" . $meta["special"] . "', " . time() . ");"); + + $measurement_id = $db->lastInsertRowID(); + + $i = 0; + foreach($meta["programCache"] as $program_id_char) { + $db->exec("INSERT INTO measurement_program_id_bits (value, position, measurement_id) VALUES ('" . $program_id_char . "', '" . $i. "', '" . $measurement_id . "'); "); + $i++; + } + + foreach($newdata["bridges"] as $bridge) { + $db->exec("INSERT INTO measurement_nodes (required_by, node_from, node_to) VALUES (" . $measurement_id . ", '" . $bridge[0]. "', '" . $bridge[1] . "'); "); + } + + + + $message[0] = true; + $message[1] = "Die Messung wurde am Ort '" . $meta['name'] . "' gespeichert!"; + $message = json_encode($message); + print($message); + die(); + break; +} diff --git a/actions/program-editor.php b/actions/program-editor.php new file mode 100644 index 0000000..1cfc71b --- /dev/null +++ b/actions/program-editor.php @@ -0,0 +1,140 @@ +query("SELECT COUNT(*) as anzahl FROM program_id_bits WHERE + position = 0 AND value = '" . $programID[0] . "' AND + position = 1 AND value = '" . $programID[1] . "' AND + position = 2 AND value = '" . $programID[2] . "' AND + position = 3 AND value = '" . $programID[3] . "' AND + position = 4 AND value = '" . $programID[4] . "' + ;"); + + $row = $result->fetchArray(SQLITE3_ASSOC); + $number = $row['anzahl']; + + if($number > 0) { + $message[0] = false; + $message[1] = "Diese Programm-ID existiert unter dem aktuell gewählten Stecker bereits. Bitte anderen Namen wählen oder den Stecker wechseln."; + $message = json_encode($message); + print($message); + die(); + } + + $db->exec(" INSERT INTO programs (plug_id, program_description) VALUES ('" . $plug . "', '" . $programDescription . "'); "); + + $program_id = $db->lastInsertRowID(); + foreach($bridges as $bridge) { + $db->exec(" INSERT INTO program_bridge_relation (program_id, bridge_id, plug_id) VALUES ('" . $program_id . "', '" . $bridge . "', '" . $plug . "'); "); + } + + for($i=0; $i<=4; $i++) { + $db->exec(" INSERT INTO program_id_bits (value, position, program_id) VALUES ('" . $programID[$i] . "', '" . $i . "', '" . $program_id . "'); "); + } + + $message[0] = true; + $message[1] = 'Das Programm wurde als
+ ' . $programID[0] . '' . $programID[1] . '' . $programID[2] . '' . $programID[3] . '' . $programID[4] . ' + +
gespeichert!'; + $message = json_encode($message); + print($message); + die(); + + break; + case "edit": + + $meta = json_decode(urldecode($meta),true); + $bridges = $meta["bridges"]; + $programDescription = $meta["programDescription"]; + + if(empty($bridges) || empty($programDescription)) { + $message[0] = false; + $message[1] = "Es wurden keine Änderungen gespeichert."; + $message = json_encode($message); + print($message); + die(); + } + + $db->exec(" UPDATE programs set program_description = '" . $programDescription . "' WHERE id = '" . $programID . "' AND plug_id = '" . $plug . "'; "); + $db->exec(" DELETE FROM program_bridge_relation WHERE program_id = '" . $programID . "' and plug_id = '" . $plug . "'; "); + + foreach($bridges as $bridge) { + $db->exec(" INSERT INTO program_bridge_relation (program_id, bridge_id, plug_id) VALUES ('" . $programID . "', '" . $bridge . "', '" . $plug . "'); "); + } + + $message[0] = true; + $message[1] = 'Die Änderungen an
'; + + $program_identifiers = $db->query("SELECT * FROM program_id_bits WHERE program_id = '" . $programID . "';"); + + while ($program_identifier = $program_identifiers->fetchArray(SQLITE3_ASSOC)) { + $message[1] .= '' . $program_identifier["value"] . ''; + } + + $message[1] .= '
wurden gespeichert!'; + + $message = json_encode($message); + print($message); + die(); + + break; + case "remove": + $programID = $meta; + + // Überprüfen, ob die Struktur korrekt ist + if (!isset($meta)) { + $message[0] = false; + $message[1] = "Es gab einen Fehler bei der Datenübermittlung."; + $message = json_encode($message); + print($message); + die(); + } + + // Hier wichtig! Beim Löschvorgang muss die referenzielle Integrität gewahrt werden! Erst die Fremdschlüssel löschen, dann die Tabelle mit dem Primärschlüssel! + + $db->exec(" DELETE FROM program_bridge_relation WHERE program_id = '" . $meta . "' and plug_id = '" . $plug . "'; "); + $db->exec(" DELETE FROM program_id_bits WHERE program_id = '" . $meta . "'; "); + $db->exec(" DELETE FROM programs WHERE id = '" . $meta . "' and plug_id = '" . $plug . "'; "); + + + $message[0] = true; + $message[1] = "Das Programm wurde nun gelöscht."; + $message = json_encode($message); + print ($message); + die(); + + + break; + + +} diff --git a/actions/save-settings.php b/actions/save-settings.php new file mode 100644 index 0000000..6fd333c --- /dev/null +++ b/actions/save-settings.php @@ -0,0 +1,16 @@ + diff --git a/alt/actions/connection-test.php b/alt/actions/connection-test.php new file mode 100644 index 0000000..8736d2f --- /dev/null +++ b/alt/actions/connection-test.php @@ -0,0 +1,5 @@ + diff --git a/alt/index.php b/alt/index.php new file mode 100644 index 0000000..3d4275c --- /dev/null +++ b/alt/index.php @@ -0,0 +1,32 @@ + + + + + + + +
+ + + +
+ +
+
+ + diff --git a/alt/pagecontent/index.php b/alt/pagecontent/index.php new file mode 100644 index 0000000..9322141 --- /dev/null +++ b/alt/pagecontent/index.php @@ -0,0 +1,24 @@ + +
Federleiste prüfen
+
+

Automatische Erkennung

+Verbinden Sie den zu messenden Stecker mit dem Gerät. Vermeiden Sie Bewegungen während der Messung. + +
Jetzt prüfen
+ + diff --git a/alt/pagecontent/start.php b/alt/pagecontent/start.php new file mode 100644 index 0000000..1e701f5 --- /dev/null +++ b/alt/pagecontent/start.php @@ -0,0 +1,339 @@ + "30", + "B" => "20", + "C" => "10", + "D" => "39", + "E" => "29", + "F" => "19", + "G" => "38", + "H" => "28", + "I" => "18", + "J" => "37", + "K" => "27", + "L" => "17", + "M" => "36", + "N" => "26", + "O" => "16", + "P" => "35", + "Q" => "25", + "R" => "15", + "S" => "34", + "T" => "24", + "U" => "14", + "V" => "33", + "W" => "23", + "X" => "13", + "Y" => "32", + "Z" => "22", + "AA" => "12", + "AB" => "31", + "AC" => "21", + "AD" => "11" +]; + +function translateArray($inputArray, $translationMap) { + return array_map(function($subArray) use ($translationMap) { + return array_map(function($item) use ($translationMap) { + return $translationMap[$item] ?? $item; // Falls kein Mapping existiert, bleibt das Original erhalten + }, $subArray); + }, $inputArray); +} + + + +flush(); + + +?> + + + +
+
+ Initialisieren +
+
+ Prüfung 1/2 +
+ +
+ Prüfung 2/2 +
+ +
+ + + + + + + +
+
+ Initialisieren +
+
+ Prüfung 1/2 +
+ +
+ Prüfung 2/2 +
+ +
+ + + + + + +
+
+ Initialisieren +
+
+ Prüfung 1/2 (x) +
+ +
+ Prüfung 2/2 +
+ +
+ + + + + + + +
Ergebnis
+
+
+

Gesamtergebnis

+
+

Einzelbrücken

+
+
+ +
+ + +
+ + + +
+
+ Initialisieren +
+
+ Prüfung 1/2 +
+ +
+ Prüfung 2/2 +
+
+

Es gab einen Fehler bei der Messung!
Messung 1 und Messung 2 lieferten keine übereinstimmenden Ergebnisse.
Bitte prüfen Sie, ob alle Steckverbindungen fest sitzen und vermeiden Sie Bewegungen, welche die Messung beeinträchtigen können.
Stellen Sie sicher, dass keine der Verbindungen gegen Erde kurzgeschlossen ist, da dies die Messung beeinträchtigt.
Erwägen Sie, eine erneute Messung zurchzuführen.

+
+

Ergebnis Messung 1:

+ + +

Ergebnis Messung 2:

+ + +
Zurück
+ + + + + diff --git a/alt/python/plug-check.py b/alt/python/plug-check.py new file mode 100644 index 0000000..4904e25 --- /dev/null +++ b/alt/python/plug-check.py @@ -0,0 +1,144 @@ +import smbus +import time +import json + +# Adressen der beiden MCP23017 Chips +AusgangRechts = 0x20 # Ausgang rechte Hälfte +AusgangLinks = 0x21 # Ausgang linke Hälfte +EingangRechts = 0x24 # Eingang rechte Hälfte +EingangLinks = 0x23 # Eingang linke Hälfte + +# Register-Adressen für den MCP23017 +IODIRA = 0x00 # Eingang / Ausgang A +IODIRB = 0x01 # Eingang / Ausgang B +GPIOA = 0x12 # GPIO A +GPIOB = 0x13 # GPIO B + +# Initialisiere den I2C Bus +bus = smbus.SMBus(1) + +RechtsA_ausgang = ["Z", "AA", "W", "AB", "AC", "AD", "AE", "AF"] +RechtsB_ausgang = ["Q", "U", "V", "T", "S", "R", "Y", "X"] + +RechtsA_eingang = ["R", "T", "V", "X", "Z", "AB", "AD", "AE"] +RechtsB_eingang = ["Q", "S", "W", "U", "AA", "Y", "AC", "AF"] + +LinksA_ausgang = ["C", "D", "E", "J", "F", "G", "H", "I"] +LinksB_ausgang = ["A", "B", "P", "O", "K", "L", "M", "N"] + +LinksA_eingang = ["B", "D", "F", "H", "J", "K", "M", "I"] +LinksB_eingang = ["A", "C", "E", "G", "L", "N", "O", "P"] + + +array = [] + +def default(): + adressen = [AusgangRechts, AusgangLinks, EingangRechts, EingangLinks] + + # Schleife für alle Registeradressen (0x00 bis 0x15) + for register in range(0x00, 0x16): + for adresse in adressen: + bus.write_byte_data(adresse, register, 0x00) + +# Konfiguriere alle Pins auf Chip 1 als Ausgang (A und B) +def configure_chip1_as_output(): + bus.write_byte_data(AusgangRechts, IODIRA, 0x00) # Setze alle Pins von A als Ausgang + bus.write_byte_data(AusgangRechts, IODIRB, 0x00) # Setze alle Pins von B als Ausgang + + bus.write_byte_data(AusgangLinks, IODIRA, 0x00) # Setze alle Pins von A als Ausgang + bus.write_byte_data(AusgangLinks, IODIRB, 0x00) # Setze alle Pins von B als Ausgang + +# Konfiguriere alle Pins auf Chip 2 als Eingang (A und B) +def configure_chip2_as_input(): + bus.write_byte_data(EingangRechts, IODIRA, 0xFF) # Setze alle Pins von A als Eingang + bus.write_byte_data(EingangRechts, IODIRB, 0xFF) # Setze alle Pins von B als Eingang + + bus.write_byte_data(EingangLinks, IODIRA, 0xFF) # Setze alle Pins von A als Eingang + bus.write_byte_data(EingangLinks, IODIRB, 0xFF) # Setze alle Pins von B als Eingang + + +# Hauptprogramm +def main(): + default() + time.sleep(0.5) + configure_chip1_as_output() + configure_chip2_as_input() + + # Teste alle Pins auf Chip 1 (A0-A7, B0-B7) + for pin in range(32): # 0 bis 31 + #print(f"Setze Pin {pin} auf HIGH auf Chip 1") + + + bus.write_byte_data(AusgangRechts, GPIOA, 0x00) + bus.write_byte_data(AusgangRechts, GPIOB, 0x00) + + bus.write_byte_data(AusgangLinks, GPIOA, 0x00) + bus.write_byte_data(AusgangLinks, GPIOB, 0x00) + + + if pin < 16: + # Setze den gewählten Pin auf HIGH + if pin < 8: # Pins A0-A7 + bus.write_byte_data(AusgangRechts, GPIOA, 1 << pin) + aktuellAn = RechtsA_ausgang[pin] + else: # Pins B0-B7 + bus.write_byte_data(AusgangRechts, GPIOB, 1 << (pin - 8)) + aktuellAn = RechtsB_ausgang[pin - 8] + else: + # Setze den gewählten Pin auf HIGH + if pin < 24: # Pins A0-A7 + bus.write_byte_data(AusgangLinks, GPIOA, 1 << pin - 16) + aktuellAn = LinksA_ausgang[pin - 16] + else: # Pins B0-B7 + bus.write_byte_data(AusgangLinks, GPIOB, 1 << (pin - 24)) + aktuellAn = LinksB_ausgang[pin - 24] + + #print("====================" + aktuellAn + "==========================") + + time.sleep(0.02) # Kurze Pause, damit die Änderung sichtbar wird + + Wert_Rechts_A = bus.read_byte_data(EingangRechts, GPIOA) + Wert_Links_A = bus.read_byte_data(EingangLinks, GPIOA) + + Wert_Rechts_B = bus.read_byte_data(EingangRechts, GPIOB) + Wert_Links_B = bus.read_byte_data(EingangLinks, GPIOB) + + + + for j in range(8): # Lese 4*8=32 Pins + bitmaske = 1 << j # Erstelle eine Maske: 1, 2, 4, 8, 16, ... + + Bit_Rechts_A = bool(Wert_Rechts_A & bitmaske) # Isoliere das entsprechende Bit + Bit_Links_A = bool(Wert_Links_A & bitmaske) # Isoliere das entsprechende Bit + Bit_Rechts_B = bool(Wert_Rechts_B & bitmaske) # Isoliere das entsprechende Bit + Bit_Links_B = bool(Wert_Links_B & bitmaske) # Isoliere das entsprechende Bit + + if Bit_Rechts_A == True: + if aktuellAn != RechtsA_eingang[j]: + array.append([aktuellAn ,RechtsA_eingang[j]]) + #print("Gefunden: " + RechtsA_eingang[j]) + + if Bit_Links_A == True: + if aktuellAn != LinksA_eingang[j]: + array.append([aktuellAn ,LinksA_eingang[j]]) + #print("Gefunden: " + LinksA_eingang[j]) + + if Bit_Rechts_B == True: + if aktuellAn != RechtsB_eingang[j]: + array.append([aktuellAn ,RechtsB_eingang[j]]) + #print("Gefunden: " + RechtsB_eingang[j]) + + if Bit_Links_B == True: + if aktuellAn != LinksB_eingang[j]: + array.append([aktuellAn ,LinksB_eingang[j]]) + #print("Gefunden: " + LinksB_eingang[j]) + + + + + json_output = json.dumps(array) + print(json_output) + +if __name__ == "__main__": + main() + diff --git a/alt/script.js b/alt/script.js new file mode 100644 index 0000000..83a1aae --- /dev/null +++ b/alt/script.js @@ -0,0 +1,117 @@ +function pageload(view, meta) { // Funktion zum Laden des Inhalts + + secondaryNavigation("close"); // Schließe das Stecker-Menü + page = window.location.hash.substr(1); // Hole die aufzurufende Seite über den URL-Hash (#xy) + + + $.ajax({ + url: "pagecontent/" + page + ".php?view=" + view + "&meta=" + meta, // Pfad der zu ladenden Seite aufbauen + success: function(result){ // Abfrage erfolgreich + $("#content").html(result); + $("div.navigation>div").removeClass("active"); // Alle Buttons inaktiv + $("#" + page).addClass("active"); // Button mit Klasse "page" wird aktiv geschaltet (im Menü) + }}); +} + + + + + +function checkConnection(last) { // Verbindunsstatus überwachen // Last ist der letzte gemessene Netzwerkzustand + $.ajax({ + url: "actions/connection-test.php?a=" + Date.now(), // verändere idr URL leicht, damit der Server nicht cached + timeout: 5000, // maximale Wartezeit sind 5 Sekunden; alles darüber wird als zu unzuverlässig angesehen + error: function(jqXHR, textStatus, errorThrown) { // Wenn ein Fehler bei der Anfrage auftritt (z.B. Netzwerkprobleme) + + $(".navigation-footer").html('
Keine Verbindung'); + console.error("Fehler bei der Anfrage:", textStatus, errorThrown); + + if (last != 1) { // letzter Zustand war "online" // Vermeiden von ständigen Nachrichten + message("Keine Verbindung"); // Werfe eine Nachricht + } + + setTimeout(function() { // nächste Prüfung in 5 Sekunden + checkConnection(1); // der Status offline wird mit übertragen + }, 5000); + }, + success: function(data) { // Verbindung iO + setTimeout(function() { // nächste Prüfung in 5 Sekunden + checkConnection(); + }, 5000); + + if (data == 'true') { // der Rückgabewert ist true, alles IO + $(".navigation-footer").html('
Verbunden'); + if (last == 1) { // war der letzte Status noch "offline", wird der Nutzer benachrichtigt + message("Verbindung wiederhergestellt!"); + } + } else { // außergewöhnlicher Fehler, aber möglich + message("Keine Verbindung"); // Verbindung dennoch unbrauchbar, wenn keine Daten geladen werden + $(".navigation-footer").html('
Keine Verbindung'); + } + } + }); +} + + +function secondaryNavigation(animation, content) { + + + + + if (animation == "open") { + + + $.get("pagecontent/plug-select.php", { + + + }, + function(data, status) { + if (status == "success") { + if (data != 'false') { + $("#navigation-secondary").css("left", "250px"); + $(".navigation-header").attr("onclick", "secondaryNavigation('close','plug-select')"); + setTimeout(function() { + $("#navigation-secondary").html(data); + console.log(data); + }, 200); + } else { + window.location.href = "#"; + + alert("Die Inhalte konnten nicht abgerufen werden. Vergewissern Sie sich, dass Sie mit dem WLAN-Netzwerk verbunden sind und laden Sie bei Bedarf die Seite neu."); + } + } + + }); + + + + } else { + $("#navigation-secondary").css("left", "-250px"); + $(".navigation-header").attr("onclick", "secondaryNavigation('open','plug-select')"); + } + +} + +function message(content) { + $("#message-box").html(content); + $("#message-box").css("display", "block"); + setTimeout(function() { + $("#message-box").css("transform", "translateY(0)"); + }, 50); + setTimeout(function() { + setTimeout(function() { + $("#message-box").css("display", "none"); + }, 500); + $("#message-box").css("transform", "translateY(calc(100% + 5px))"); + }, 3000); + +} + + + + +$(window).bind('beforeunload', function() { + $("#warning").css("display", "block"); + return 'Are you sure you want to leave?'; + +}); \ No newline at end of file diff --git a/alt/stecker.php b/alt/stecker.php new file mode 100644 index 0000000..2682fc0 --- /dev/null +++ b/alt/stecker.php @@ -0,0 +1,120 @@ +'; +?> + + + + + + + 30 + + 10 + + 31 + + 11 + + + + + + + + + + + + + + + + + + [70, 68], + 20 => [70, 44], + 30 => [70, 20], + 11 => [405, 68], + 12 => [370, 68], + 13 => [335, 68], + 14 => [300, 68], + 15 => [265, 68], + 16 => [230, 68], + 17 => [195, 68], + 18 => [160, 68], + 19 => [125, 68], + 21 => [405, 44], + 22 => [370, 44], + 23 => [335, 44], + 24 => [300, 44], + 25 => [265, 44], + 26 => [220, 44], + 27 => [185, 44], + 28 => [150, 44], + 29 => [115, 44], + 31 => [405, 20], + 32 => [370, 20], + 33 => [335, 20], + 34 => [300, 20], + 35 => [265, 20], + 36 => [230, 20], + 37 => [195, 20], + 38 => [160, 20], + 39 => [125, 20], + ]; + + foreach ($pin_coordinates as $coordinates) { + echo ''; + } + + if (empty($_GET['data'])) { + print ""; + die(); + } + +$inputArray = json_decode($_GET['data'], true); // JSON in ein PHP-Array umwandeln + + // Neues Array mit übersetzten Koordinaten + $translatedArray = array_map(function ($pair) use ($pin_coordinates) { + return [ + $pin_coordinates[$pair[0]], // Übersetze den ersten Wert + $pin_coordinates[$pair[1]], // Übersetze den zweiten Wert + ]; + }, $inputArray); + + $colors = ["#1a5fb4", "#26a269", "#e5a50a", "#c64600", "#a51d2d", "#613583", "#63452c", "#3d3846"]; + + // Schleife durch das übersetzte Array + $colorNumber = 0; + foreach ($translatedArray as $line) { + $point1 = $line[0]; // Erster Punkt [x, y] + $point2 = $line[1]; // Zweiter Punkt [x, y] + + $point1x = $point1[0] + 10; + $point1y = $point1[1] + 5; + $point2x = $point2[0] + 10; + $point2y = $point2[1] + 5; + + // Ausgabe eines Pfades (Linie zwischen zwei Punkten) + echo ''; + + $colorNumber++; + if ($colorNumber > 7) { + $colorNumber = 0; + } + } + ?> + \ No newline at end of file diff --git a/alt/style.css b/alt/style.css new file mode 100644 index 0000000..25069a4 --- /dev/null +++ b/alt/style.css @@ -0,0 +1,241 @@ +@font-face { + font-family: LexendDecaBold; + src: url(/vendor/fonts/LexendDeca-Bold.ttf); +} +@font-face { + font-family: LexendDecaMedium; + src: url(/vendor/fonts/LexendDeca-Medium.ttf); +} + body { + height: 100%; + + width: 100%; + padding: 20px; + margin: 0; + box-sizing: border-box; + font-family: LexendDecaBold; + font-weight: light; + color: #2e3436; + } + div.main { + width: 100%; + max-width: 900px; + height: auto; + border-radius: 15px; + overflow: hidden; + margin: 0 auto; + border: 5px solid #c3c3c3; + position: relative; + } + div.navigation { + width: 250px; + height: 600px; + background-color: #ebebeb; + float: left; + display: inline-block; + position: relative; + z-index: 501; + } + div.navigation.secondary { + position: absolute; + left: -250px; + top: 0; + overflow-y: auto; + z-index: 500; + transition-duration: .6s; + width: 500px; + backdrop-filter: blur(10px); + background-color: #ebebeb8c; + + } + div.navigation.secondary img { + width: 100px !important; + } + div.navigation>div, div.save-button { + padding: 5px; + margin: 10px; + line-height: 30px; + transition-duration: .4s; + cursor: pointer; + border-radius: 10px; + } + div.navigation>div:hover,div.navigation>div.active,div.navigation-footer,div.navigation-header,div.save-button:hover { + background-color: #d2d2d2; + } + div.navigation div.navigation-header { + text-align: center; + } + div.navigation div.navigation-header>div { + background-color: #fff; + border-radius: 0 0 7px 7px; + color: grey; + line-height: 20px; + font-size: 12px; + } + div.navigation>div>img:not(.infrago-logo),div.content div.save-button>img { + width: 30px; + max-height: 30px; + margin-right:20px; + vertical-align:middle; + filter: contrast(0.3); + } + div.navigation>div>img.infrago-logo { + width: 100%; + display:block; + margin-top: 15px; + } + div.content { + width: calc(100% - 250px); + float: right; + display: inline-block; + padding: 15px 20px; + height: 600px; + box-sizing: border-box; + overflow-y: auto; + text-align: center; + } + + div.content div.content-header { + text-align: center; + line-height: 30px; + padding: 0; + margin-bottom: 20px; + display: block; + font-size: 2em; + margin-block-start: 0.67em; + margin-block-end: 0.67em; + margin-inline-start: 0px; + margin-inline-end: 0px; + font-weight: bold; + unicode-bidi: isolate; + } + + div.content img { + width: 100%; + margin: 0 auto; + display: block; + } + div.navigation-footer { + position: absolute; + bottom:0; + width: calc(100% - 40px); + padding: 10px !important; + cursor: auto !important; + } + div.connection-indicator { + height: 15px; + width: 15px; + border-radius: 100%; + float: left; + vertical-align:middle; + margin: 7.5px; + } + div.connection-indicator.true { + background-color: #2ec27e; + } + div.connection-indicator.unknown { + background-color: #f6d32d; + } + div.connection-indicator.false { + background-color: #ed333b; + } + + + +div.save-button img { + display: inline-block; +} +div.save-button { + background-color: #ebebeb +} + +::-webkit-scrollbar { + width: 5px; +} + +/* Track */ +::-webkit-scrollbar-track { + background: #ebebeb; +} + +/* Handle */ +::-webkit-scrollbar-thumb, ::-webkit-scrollbar-thumb:hover { + background: #d2d2d2; +} + + +div.toggle-switch { + width: 100%; + height: 50px; + margin-bottom: 10px; + display: flex; +} +div.toggle-switch div { + display: inline-block; +} +div.toggle-switch div:nth-child(1), div.toggle-switch div:nth-child(3) { + width: 39%; +} +div.toggle-switch div:nth-child(2) { + width: 20%; + padding: 10px; +} + + +.switch { + position: relative; + display: inline-block; + width: 100%; + height: 34px; + +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; + border-radius: 25px; +} + +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; + border-radius: 100%; +} + +/* Rounded sliders */ +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} + +table.simple-devider th,table.simple-devider td { + padding: 15px; + +} +table.simple-devider * { + margin: 0; + vertical-align: top; +} diff --git a/alt/vendor/DB_InfraGo_logo_red_black_100px_rgb.svg b/alt/vendor/DB_InfraGo_logo_red_black_100px_rgb.svg new file mode 100644 index 0000000..484167f --- /dev/null +++ b/alt/vendor/DB_InfraGo_logo_red_black_100px_rgb.svg @@ -0,0 +1,77 @@ + \ No newline at end of file diff --git a/alt/vendor/fonts/LexendDeca-Bold.ttf b/alt/vendor/fonts/LexendDeca-Bold.ttf new file mode 100644 index 0000000..49c0bb7 Binary files /dev/null and b/alt/vendor/fonts/LexendDeca-Bold.ttf differ diff --git a/alt/vendor/fonts/LexendDeca-Medium.ttf b/alt/vendor/fonts/LexendDeca-Medium.ttf new file mode 100644 index 0000000..ffd9b9b Binary files /dev/null and b/alt/vendor/fonts/LexendDeca-Medium.ttf differ diff --git a/alt/vendor/icons/electric-plug-icon.svg b/alt/vendor/icons/electric-plug-icon.svg new file mode 100644 index 0000000..ca62634 --- /dev/null +++ b/alt/vendor/icons/electric-plug-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/alt/vendor/icons/fiber-cable-icon.svg b/alt/vendor/icons/fiber-cable-icon.svg new file mode 100644 index 0000000..aaacc4a --- /dev/null +++ b/alt/vendor/icons/fiber-cable-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/alt/vendor/icons/play.svg b/alt/vendor/icons/play.svg new file mode 100644 index 0000000..d40e7d6 --- /dev/null +++ b/alt/vendor/icons/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/alt/vendor/jquery.min.js b/alt/vendor/jquery.min.js new file mode 100644 index 0000000..a1c07fd --- /dev/null +++ b/alt/vendor/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0 diff --git a/db-test/index.php b/db-test/index.php new file mode 100755 index 0000000..8337367 --- /dev/null +++ b/db-test/index.php @@ -0,0 +1,97 @@ +exec('CREATE TABLE "bridges" ( + "plug_id" INTEGER, + "id" INTEGER UNIQUE, + PRIMARY KEY("id"), + FOREIGN KEY("plug_id") REFERENCES "plugs"("id") +)'); + +$db->exec('CREATE TABLE "measurement_nodes" ( + "required_by" INTEGER, + "node_from" INTEGER, + "node_to" INTEGER, + UNIQUE("required_by","node_from","node_to"), + FOREIGN KEY("required_by") REFERENCES "" +)'); + +$db->exec('CREATE TABLE "measurement_program_id_bits" ( + "value" TEXT, + "position" INTEGER, + "measurement_id" INTEGER, + UNIQUE("position","measurement_id"), + FOREIGN KEY("measurement_id") REFERENCES "" +)'); + +$db->exec('CREATE TABLE "measurements" ( + "id" INTEGER UNIQUE, + "place_name" TEXT, + "comment" TEXT, + "timestamp" INTEGER, + PRIMARY KEY("id" AUTOINCREMENT), + FOREIGN KEY("place_name") REFERENCES "" +)'); + +$db->exec('CREATE TABLE "nodes" ( + "plug_id" INTEGER, + "required_by" INTEGER, + "node_from" INTEGER, + "node_to" INTEGER, + UNIQUE("plug_id","required_by","node_from","node_to"), + FOREIGN KEY("required_by") REFERENCES "bridges"("id") +)'); + +$db->exec('CREATE TABLE "places" ( + "plug_id" INTEGER, + "name" TEXT UNIQUE, + PRIMARY KEY("name"), + FOREIGN KEY("plug_id") REFERENCES "plugs"("id") +)'); + +$db->exec('CREATE TABLE "plugs" ( + "id" INTEGER UNIQUE, + "name" TEXT, + "char_number" INTEGER, + PRIMARY KEY("id") +)'); + +$db->exec('CREATE TABLE "program_bridge_relation" ( + "program_id" INTEGER, + "bridge_id" INTEGER, + "plug_id" INTEGER, + UNIQUE("program_id","bridge_id","plug_id"), + FOREIGN KEY("bridge_id") REFERENCES "bridges"("id"), + FOREIGN KEY("plug_id") REFERENCES "plugs"("id"), + FOREIGN KEY("program_id") REFERENCES "programs"("id") +)'); + +$db->exec('CREATE TABLE "program_id_bits" ( + "value" TEXT, + "position" INTEGER, + "program_id" INTEGER, + UNIQUE("position","program_id"), + FOREIGN KEY("program_id") REFERENCES "programs"("id") +)'); + + +$db->exec('CREATE TABLE "programs" ( + "id" INTEGER UNIQUE, + "plug_id" INTEGER, + "program_description" TEXT, + PRIMARY KEY("id"), + FOREIGN KEY("plug_id") REFERENCES "plugs"("id") +)'); + +*/ + + +$db->exec('DELETE FROM "places" WHERE 1'); + + + +?> diff --git a/db-test/test.db b/db-test/test.db new file mode 100644 index 0000000..0b74b0f Binary files /dev/null and b/db-test/test.db differ diff --git a/errorcodes.php b/errorcodes.php new file mode 100644 index 0000000..6e0e3e8 --- /dev/null +++ b/errorcodes.php @@ -0,0 +1,2 @@ +

Fehlercodes:

+
GruppeBeschreibung

Eine einzelne Prüfung betreffend

00Fehlerfrei
01Brücke angeblich erkannt, vom redundanten Multiplexer jedoch nicht bestätigt (ungleichen Pin betreffend)
02Unterschiedliche Ergebnisse: Prüfer misst LOW, Redundanz misst HIGH
03Unterschiedliche Ergebnisse: Prüfer misst HIGH, Redundanz misst LOW
04Prüfspannung angeblich gesetzt, von der Redundanz jedoch nicht bestätigt

Die gesamte Prüfung betreffend

10Fehlerfrei
11Ein/mehrere Multiplexer nicht ansprechbar, i2c ordnungsgemäß gestartet
12i2c-Bus nicht startfähig, keine Kommunikation möglich
13Fehlerursache nicht weiter spezifizierbar
\ No newline at end of file diff --git a/functions.php b/functions.php new file mode 100644 index 0000000..2d3fcdf --- /dev/null +++ b/functions.php @@ -0,0 +1,198 @@ + Steckerpin, gilt für Programmsteckerprüfung) +$translationMap = [ + "0" => "18", + "1" => "19", + "2" => "20", + "3" => "21", + "4" => "22", + "5" => "23", + "6" => "24", + "7" => "25", + "8" => "10", + "9" => "11", + "10" => "12", + "11" => "13", + "12" => "14", + "13" => "15", + "14" => "16", + "15" => "17", + "16" => "34", + "17" => "35", + "18" => "36", + "19" => "37", + "20" => "38", + "21" => "39", + "22" => "40", + "23" => "41", + "24" => "26", + "25" => "27", + "26" => "28", + "27" => "29", + "28" => "30", + "29" => "31", + "30" => "32", + "31" => "33" +]; + +//Übsersetzungstabelle (Intern -> Steckerpin, gilt für Spurkabelprüfung) +$translationMap2 = [ + "0" => "25", + "1" => "24", + "2" => "23", + "3" => "22", + "4" => "21", + "5" => "20", + "6" => "19", + "7" => "18", + "8" => "17", + "9" => "16", + "10" => "15", + "11" => "14", + "12" => "13", + "13" => "12", + "14" => "11", + "15" => "10", + "16" => "41", + "17" => "40", + "18" => "39", + "19" => "38", + "20" => "37", + "21" => "36", + "22" => "35", + "23" => "34", + "24" => "33", + "25" => "32", + "26" => "31", + "27" => "30", + "28" => "29", + "29" => "28", + "30" => "27", + "31" => "26" +]; + +// Diese Funktion übersetzt die interne Pin-Bezeichnung in die des Herstellers gem. translationMap +// weil die Hardware der Spurkabelprüfer anders verschaltet ist, als die des Programmsetckerprüfers, wird nach zwei Übersetzungstabellen bei der Spurkabelprüfung gearbeitet +// nodes[0] wird gem Standardtabelle gehandhabt, nodes[1] nach translationMap2 - unsauber, aber nur durhc Software lösbar +function translateArray($inputArray, $translationMap, $translationMap2 = null) { + return array_map(function($subArray) use ($translationMap, $translationMap2) { + $map1 = $translationMap; + $map2 = $translationMap2 ?? $translationMap; // Wenn keine zweite Map, nimm die erste + return [ + $map1[$subArray[0]] ?? $subArray[0], + $map2[$subArray[1]] ?? $subArray[1] + ]; + }, $inputArray); +} + +function measurementsDiffer($bridges1, $bridges2) { + //Isoliere die Brücken + $bridges1 = array_map(function($entry) { + return $entry[0]; + }, $bridges1); + + $bridges2 = array_map(function($entry) { + return $entry[0]; + }, $bridges2); + + //Zeichne die Steckergrafik + $url1 = "../stecker.php?data=" . urlencode(json_encode(translateArray($bridges1, $translationMap))); + $url2 = "../stecker.php?data=" . urlencode(json_encode(translateArray($bridges2, $translationMap))); + + + + + ?> +

+ Es gab einen Fehler bei der Messung!
Messung 1 und Messung 2 lieferten keine übereinstimmenden Ergebnisse.
Bitte prüfen Sie, ob alle Steckverbindungen fest sitzen und vermeiden Sie Bewegungen, welche die Messung beeinträchtigen können.
Stellen Sie sicher, dass keine der Verbindungen gegen Erde kurzgeschlossen ist, da dies die Messung beeinträchtigt.
Erwägen Sie, eine erneute Messung durchzuführen. +

+
+

Ergebnis Messung 1:

+ + +

Ergebnis Messung 2:

+ + +
Zurück
+ + +

Eine oder mehrere Prüfungen versagten. Vollständige Fehlerausgabe:

+ + + + "; + } + ?> + +
Brücke vonnachAufgetretene Fehler" . $bridge[0][0] . "" . $bridge[0][1] . "" . $bridge[1] . "
+
+

Fehlercodes:

+ + i+1, letzter -> erster) + $completeEdges = []; + $numNodes = count($nodes); + for ($i = 0; $i < $numNodes; $i++) { + $from = $nodes[$i]; + $to = $nodes[($i + 1) % $numNodes]; // Kreis schließen + $completeEdges[] = [$from, $to]; + } + + return $completeEdges; +} + +$edges = [[0,1], [2,0]]; +$result = completeCycleEdges($edges); + +print_r($result); + + + + + + +*/ + + +?> diff --git a/index.php b/index.php new file mode 100644 index 0000000..9829b84 --- /dev/null +++ b/index.php @@ -0,0 +1,38 @@ + + + + + + + + +
+ + + +
+ + +
+
+ + diff --git a/pagecontent/bridge-editor.php b/pagecontent/bridge-editor.php new file mode 100755 index 0000000..bfd095f --- /dev/null +++ b/pagecontent/bridge-editor.php @@ -0,0 +1,410 @@ + +
Brücken-Editor - Neu anlegen
+
+ + + + + + + + +
'; // Hier folgt die eigentliche Bearbeitungs-Fläche + + + $pinTable_row = 0; + foreach ($nodes as $nodeItem) { // Anzahl an Gesamt-Brücken-Elementen (Dies ist Wichtig, da appliedPinRowNumber ein Funktionsparameter ist, damit JS weiß, an welcher Stelle ein neues Element mit welchem Index erstellt werden muss!) + $pinTable_row++; + } + $appliedPinRowNumber = $pinTable_row; + + // im Folgenden die Eingabefelder anzeigen (mit aufsteigender ID) + $pinTable_row = 0; + foreach ($nodes as $nodeItem) { + echo ' +
+ ' . $nodeItem[0] . ' + + ' . $nodeItem[1] . ' + +
'; + $pinTable_row++; + } + ?> + +
+ + + + +
Speichern
+ + +
Brücken-Editor - Bearbeiten
+
+ query(" + SELECT * FROM bridges WHERE plug_id = '" . $plug . "' AND id = '" . $meta . "'; + "); + $result = $result->fetchArray(SQLITE3_ASSOC); + + // Skelettstruktur: Die "Row" sorgt hier für das typische Layout, insbesodere für die Darstellung der Brückennummer + ?> +
+ + ' . $result["id"] . ' +
'); // Brückennummer + + $nodes_url = []; + + $nodes = $db->query(" + SELECT node_from,node_to + FROM nodes WHERE required_by = '". $result['id'] ."'; + "); + + while ($node = $nodes->fetchArray(SQLITE3_ASSOC)) { + $nodes_url[] = [$node['node_from'], $node['node_to']]; + } + + $url = '../stecker.php?data=' . urlencode(json_encode($nodes_url)); // URL parsen + $nodes = $nodes_url; // die Nodes speichern (hier stecken alle Daten drin) + + ?> +
+ + + + + + +
'; // Hier folgt die eigentliche Bearbeitungs-Fläche + + $pinTable_row = 0; + foreach ($nodes as $nodeItem) { // Anzahl an Gesamt-Brücken-Elementen (Dies ist Wichtig, da appliedPinRowNumber ein Funktionsparameter ist, damit JS weiß, an welcher Stelle ein neues Element mit welchem Index erstellt werden muss!) + $pinTable_row++; + } + $appliedPinRowNumber = $pinTable_row; + + // im Folgenden die Eingabefelder anzeigen (mit aufsteigender ID) + $pinTable_row = 0; + foreach ($nodes as $nodeItem) { + echo ' +
+ ' . $nodeItem[0] . ' + + ' . $nodeItem[1] . ' + +
'; + $pinTable_row++; + } + ?> + +
+ + +
Brücken-Editor - Übersicht
+
+ +
+
+ query("SELECT * FROM bridges WHERE plug_id = '" . $plug . "';"); + while ($row = $result->fetchArray(SQLITE3_ASSOC)) { + + ?> +
+ + ' . $row['id'] . ' +
'); + + $nodes_url = []; + + $nodes = $db->query(" + SELECT node_from,node_to + FROM nodes WHERE required_by = ". $row['id'] ."; + "); + + while ($node = $nodes->fetchArray(SQLITE3_ASSOC)) { + $nodes_url[] = [$node['node_from'], $node['node_to']]; + } + + $url = '../stecker.php?data=' . urlencode(json_encode($nodes_url)); // URL parsen + print(''); + print(' +
+ + +
+ '); + ?> +
+ query("SELECT COUNT(*) as anzahl FROM bridges WHERE plug_id = '" . $plug . "';"); + $row = $result->fetchArray(SQLITE3_ASSOC); + $number = $row['anzahl']; + + if($number <= 0) { + print("

Es sind keine Brücken für diesen Stecker gespeichert.
Eventuell muss ein anderer Stecker gewählt werden.

"); + } + ?> +
+ +
Spurkabel prüfen
+
+

Automatische Erkennung

+Verbinden Sie die Enden des Spurkabels mit dem Gerät. Vermeiden Sie Bewegungen während der Messung. + +
Jetzt prüfen
+ + diff --git a/pagecontent/database-search.php b/pagecontent/database-search.php new file mode 100755 index 0000000..da79194 --- /dev/null +++ b/pagecontent/database-search.php @@ -0,0 +1,322 @@ + +
+ query(" SELECT * FROM places WHERE name LIKE '%" . strtolower($meta) . "%' "); + + while ($row = $result->fetchArray(SQLITE3_ASSOC)) { + + // Wenn die suche leer ist, sollen auch keine Ergebnisse kommen (siehe Vorschläge ignorieren) + if($meta == "") { + return; + } + + ?> +
'); search('results');"> + +
+ +
+ Vorschläge ignorieren +
+ + +
+ query(" + + SELECT + m.id AS measurement_id, + m.comment AS comment, + m.timestamp AS timestamp, + m.place_name AS place_name, + b.value AS bit_value, + b.position AS bit_position + FROM + measurements m + JOIN places p ON p.name = m.place_name + JOIN measurement_program_id_bits b ON b.measurement_id = m.id + ORDER BY m.id + + "); + + /* + Ausgabe sieht z.B. so aus: + + | measurement_id | comment | timestamp | place_name | bit_value | bit_position | + | -------------- | ------- | --------- | ------------ | --------- | ------------ | + | 1 | xyz.. | 34535454 | Dudweiler Df | S | 0 | + | 1 | xyz.. | 34535454 | Dudweiler Df | - | 1 | + | 1 | xyz.. | 34535454 | Dudweiler Df | 4 | 2 | + | 1 | xyz.. | 34535454 | Dudweiler Df | - | 3 | + | 1 | xyz.. | 34535454 | Dudweiler Df | - | 4 | + | 7 | abc.. | 48548574 | Mannheim Hbf | A | 0 | + | 7 | abc.. | 48548574 | Mannheim Hbf | 6 | 1 | + | 7 | abc.. | 48548574 | Mannheim Hbf | 4 | 2 | + | 7 | abc.. | 48548574 | Mannheim Hbf | - | 3 | + | 7 | abc.. | 48548574 | Mannheim Hbf | S | 4 | + + */ + + // Die Datenbank wird als dreifach indexiertes Array gespeichert + $database_db = []; + while ($row = $result->fetchArray(SQLITE3_ASSOC)) { + $database_db[$row["place_name"]][$row["measurement_id"]]["program_id_bits"][$row["bit_position"]] = $row["bit_value"]; + $database_db[$row["place_name"]][$row["measurement_id"]]["comment"] = $row["comment"]; + $database_db[$row["place_name"]][$row["measurement_id"]]["timestamp"] = $row["timestamp"]; + } + + /* + + $database_db + └── $row["place_name"] + └── $row["measurement_id"] + ├── "program_id_bits" + │ └── $row["bit_position"] + │ └── $row["bit_value"] + ├── "program_id_string" + │ └── implode('', $bits) + ├── "comment" + │ └── $row["comment"] + └── "timestamp" + └── $row["timestamp"] + + */ + + // speichere die Bits als String, damit diese einfacher durchsuchbar sind + foreach ($database_db as $place => $entries) { + foreach ($entries as $index => $info) { + $bits = $info['program_id_bits']; + $database_db[$place][$index]['program_id_string'] = implode('', $bits); + } + } + + // durchsuche nach der Query im Programm-ID-String + foreach($database_db as $place_name => $measurements) { + $found = false; + foreach ($measurements as $measurement_id => $measurement) { + if(str_contains(strtolower($measurement["program_id_string"]), strtolower($meta))) { + $found = true; + } + } + if($found != true) { + continue; + } + ?> + +
+ + +        + +
+ $measurement) { + + if(!str_contains(strtolower($measurement["program_id_string"]), strtolower($meta))) { + continue; + } + + print(' +
+
+ '); + + + $i = 0; + foreach($measurement["program_id_bits"] as $programIDChar) { + echo '' . $programIDChar . ''; + $i++; + } + + print(' + +
+
+
+ ' . date("d.m.Y H:i:s", $measurement["timestamp"]) . ' +
'); + + ?> + +
+ +
+ + +
'); + + if(!empty($measurement["comment"])) { + + print(' +
+ '); + echo $measurement["comment"]; + print(' +
'); + } + } + ?> +
+
+ query(" + + SELECT + m.id AS measurement_id, + m.comment AS comment, + m.timestamp AS timestamp, + m.place_name AS place_name, + b.value AS bit_value, + b.position AS bit_position + FROM + measurements m + JOIN places p ON p.name = m.place_name + JOIN measurement_program_id_bits b ON b.measurement_id = m.id + ORDER BY m.id + + "); + + // Die Datenbank wird als dreifach indexiertes Array gespeichert + $database_db = []; + while ($row = $result->fetchArray(SQLITE3_ASSOC)) { + $database_db[$row["place_name"]][$row["measurement_id"]]["program_id_bits"][$row["bit_position"]] = $row["bit_value"]; + $database_db[$row["place_name"]][$row["measurement_id"]]["comment"] = $row["comment"]; + $database_db[$row["place_name"]][$row["measurement_id"]]["timestamp"] = $row["timestamp"]; + } + + /* + + $database_db + └── $row["place_name"] + └── $row["measurement_id"] + ├── "program_id_bits" + │ └── $row["bit_position"] + │ └── $row["bit_value"] + ├── "program_id_string" + │ └── implode('', $bits) + ├── "comment" + │ └── $row["comment"] + └── "timestamp" + └── $row["timestamp"] + + */ + + foreach($database_db as $place_name => $measurements) { + + + + ?> + +
+ [] + + + query(" + + SELECT + m.id AS measurement_id, + m.comment AS comment, + m.timestamp AS timestamp, + m.place_name AS place_name, + b.value AS bit_value, + b.position AS bit_position, + n.node_from AS node_from, + n.node_to AS node_to + FROM + measurements m + JOIN places p ON p.name = m.place_name + JOIN measurement_program_id_bits b ON b.measurement_id = m.id + JOIN measurement_nodes n ON n.required_by = m.id + +"); + +/* +Ausgabe sieht z.B. so aus: + +| measurement_id | comment | timestamp | place_name | bit_value | bit_position | node_from | node_to | +| -------------- | ------- | --------- | ------------ | --------- | ------------ | --------- | ------- | +| 1 | xyz.. | 34535454 | Dudweiler Df | S | 0 | 20 | 28 | +| 1 | xyz.. | 34535454 | Dudweiler Df | S | 0 | 28 | 30 | +| 1 | xyz.. | 34535454 | Dudweiler Df | - | 1 | 20 | 28 | +| 1 | xyz.. | 34535454 | Dudweiler Df | - | 1 | 28 | 30 | +| 1 | xyz.. | 34535454 | Dudweiler Df | 4 | 2 | 20 | 28 | +| 1 | xyz.. | 34535454 | Dudweiler Df | 4 | 2 | 28 | 30 | +| 1 | xyz.. | 34535454 | Dudweiler Df | - | 3 | 20 | 28 | +| 1 | xyz.. | 34535454 | Dudweiler Df | - | 3 | 28 | 30 | +| 1 | xyz.. | 34535454 | Dudweiler Df | - | 4 | 20 | 28 | +| 1 | xyz.. | 34535454 | Dudweiler Df | - | 4 | 28 | 30 | + +*/ + + +// Die Datenbank wird als dreifach indexiertes Array gespeichert +$database_db = []; +while ($row = $result->fetchArray(SQLITE3_ASSOC)) { + $id = $row["measurement_id"]; + + // Basisdaten setzen (überschreiben macht nichts) + $database_db[$id]["comment"] = $row["comment"]; + $database_db[$id]["timestamp"] = $row["timestamp"]; + $database_db[$id]["place_name"] = $row["place_name"]; + + // Programmbits: nur setzen, wenn nicht schon vorhanden + if (!isset($database_db[$id]["program_id_bits"][$row["bit_position"]])) { + $database_db[$id]["program_id_bits"][$row["bit_position"]] = $row["bit_value"]; + } + + // Nodes: nur hinzufügen, wenn noch nicht vorhanden + $node_pair = [$row["node_from"], $row["node_to"]]; + if ( + !isset($database_db[$id]["nodes"]) || + !in_array($node_pair, $database_db[$id]["nodes"]) + ) { + $database_db[$id]["nodes"][] = $node_pair; + } +} + + +// Ersetzen der $database_db[$id]["nodes"] zu einem String +$bitLength = 5; +$placeholder = '?'; +foreach ($database_db as $index => $row) { + $programIdBits = isset($row['program_id_bits']) ? $row['program_id_bits'] : []; + $programIdString = ''; + + // Baue ein Array mit fester Länge ($bitLength), fülle fehlende Positionen mit Platzhalter + for ($i = 0; $i < $bitLength; $i++) { + $programIdString .= isset($programIdBits[$i]) ? $programIdBits[$i] : $placeholder; + } + + $database_db[$index]['program_id_string'] = $programIdString; +} + + + + +// $list ist die CSV-Datei + +//$list[] = array("Gemessen am / um", "Stw-Name / Ort" ,"Erkannte Brücken (Pin-zu-Pin)", "(automatisch) ermittelte Programm-Nummer", "Freitext-Besonderheiten"); + + + +// gehe jeden Datenwankeintrag durch +foreach($database_db as $database_entry) { + // leere dne node_string zu Beginn + $node_string = ""; + // erstelle einen Brücken-String, der erweitert wird + foreach($database_entry["nodes"] as $node) { + $node_string .= $node[0] . "<->" . $node[1] . ", "; + } + + $imagePath = 'http://localhost/stecker.php?data=' . urlencode(json_encode($database_entry["nodes"])); // URL parsen + + // Schreibe ins Array + $list[] = array(date('d.m.Y H:i:s',$database_entry["timestamp"]), $database_entry["place_name"] ,$node_string, $database_entry["program_id_string"], $database_entry["comment"], $imagePath); +} + + + +function svgUrlToPngViaRsvg(string $svgUrl, string $outputPath): bool { + $svgData = file_get_contents($svgUrl); + if ($svgData === false) return false; + + $tempSvg = '../tmp/stecker_' . uniqid() . '.svg'; + file_put_contents($tempSvg, $svgData); + + + $cmd = escapeshellcmd("rsvg-convert -o " . escapeshellarg($outputPath) . " " . escapeshellarg($tempSvg)); + $result = shell_exec($cmd); + + unlink($tempSvg); + + return file_exists($outputPath); +} + + + + + +require '../vendor/composer/vendor/autoload.php'; + +use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\Writer\Xlsx; +use PhpOffice\PhpSpreadsheet\Worksheet\Drawing; + + + +// Neue Excel-Datei erzeugen +$spreadsheet = new Spreadsheet(); +$sheet = $spreadsheet->getActiveSheet(); + +// Spaltenüberschriften +$sheet->setCellValue('A1', 'Zeitstempel'); +$sheet->setCellValue('B1', 'Ort'); +$sheet->setCellValue('C1', 'Brücken'); +$sheet->setCellValue('D1', 'Programmbezeichnung'); +$sheet->setCellValue('E1', 'Notizen'); + +// Durch das Array iterieren und Daten + Bild einfügen +$row = 2; // Start in Zeile 2 +foreach ($list as $entry) { + [$timestamp,$place,$bridges,$program_number, $extra, $imagePath] = $entry; + + // Namen einfügen + $sheet->setCellValue("A$row", $timestamp); + $sheet->setCellValue("B$row", $place); + $sheet->setCellValue("C$row", $bridges); + $sheet->setCellValue("D$row", $program_number); + $sheet->setCellValue("E$row", $extra); + + + + + + $svgUrl = $imagePath; + $tmpPng = '../tmp/stecker_' . uniqid() . '.png'; + + // Bild einfügen, wenn Datei existiert + if (svgUrlToPngViaRsvg($svgUrl, $tmpPng)) { + $drawing = new Drawing(); + $drawing->setPath($tmpPng); + $drawing->setCoordinates("F$row"); + $drawing->setHeight(80); + $sheet->getRowDimension($row)->setRowHeight(80 * 0.75); + $drawing->setWorksheet($sheet); + } else { + $sheet->setCellValue("F$row", 'Bildkonvertierung fehlgeschlagen'); + } + + $row++; +} + +// Spaltenbreite anpassen +$sheet->getColumnDimension('A')->setAutoSize(true); +$sheet->getColumnDimension('B')->setWidth(20); // Platz fürs Bild + +// Datei speichern +$writer = new Xlsx($spreadsheet); +$writer->save('../temp.xlsx'); + +shell_exec("rm ../tmp/*"); + + +?> + +
Export - Fertig
+
+

+ Ihr Datenexport ist fertig vorbereitet. +

+
Herunterladen
+ + diff --git a/pagecontent/index.php b/pagecontent/index.php new file mode 100755 index 0000000..27462df --- /dev/null +++ b/pagecontent/index.php @@ -0,0 +1,15 @@ + +
Federleiste prüfen
+
+

Automatische Erkennung

+Verbinden Sie den zu messenden Stecker mit dem Gerät. Vermeiden Sie Bewegungen während der Messung. + +
Jetzt prüfen
+ + diff --git a/pagecontent/inventory.php b/pagecontent/inventory.php new file mode 100755 index 0000000..b9d11d1 --- /dev/null +++ b/pagecontent/inventory.php @@ -0,0 +1,18 @@ + + +
Bestand
+
+ +
+
+
+ +
+ +
+
+
+ diff --git a/pagecontent/plug-select.php b/pagecontent/plug-select.php new file mode 100755 index 0000000..fad6cef --- /dev/null +++ b/pagecontent/plug-select.php @@ -0,0 +1 @@ +Aktuell werden keine verschiedenen Steckerarten unterstützt. \ No newline at end of file diff --git a/pagecontent/program-editor.php b/pagecontent/program-editor.php new file mode 100755 index 0000000..d91e913 --- /dev/null +++ b/pagecontent/program-editor.php @@ -0,0 +1,230 @@ + +
Programm-Editor - Übersicht
+
+ +
+
+ query("SELECT * FROM programs WHERE plug_id = '" . $plug . "';"); + while ($row = $result->fetchArray(SQLITE3_ASSOC)) { + $results = true; + ?> +
+ + '); + // Stelle die einzelnen Programmziffern dar + $program_identifiers = $db->query("SELECT * FROM program_id_bits WHERE program_id = '" . $row['id'] . "';"); + + while ($program_identifier = $program_identifiers->fetchArray(SQLITE3_ASSOC)) { + echo '' . $program_identifier["value"] . ''; + } + print(' + +
'); + + // Die erforderlichen Brücken werden aus der Datenbank geholt (via program_bridge_relation) + $bridges_url = []; + $bridges = $db->query("SELECT * FROM program_bridge_relation WHERE program_id = '" . $row['id'] . "';"); + + while ($bridge = $bridges->fetchArray(SQLITE3_ASSOC)) { + $bridges_url[] = $bridge["bridge_id"]; + } + + $url = '../stecker.php?translate=true&data=' . urlencode(json_encode($bridges_url)); // URL parsen; Modus: Programmanzeige + + print(''); + // Aktionsmenü (mit hässlichem Inline-Code) + print(' +
+ + +
+ '); + ?> +
+ +
+ Es sind keine Programme für diesen Stecker gespeichert.
Eventuell muss ein anderer Stecker gewählt werden.

"); + } + + break; + + // Neues Hinzufügen eines Programms + case "add": + + $meta = json_decode(urldecode($meta), true); + ?> +
Programm-Editor - Neu anlegen
+ +
+

Zugehörige Brücken

+ Wählen Sie die Brücken, welche von diesem Programm gefordert werden + +
+ query("SELECT COUNT(*) as anzahl FROM bridges WHERE plug_id = '" . $plug . "';"); + $row = $result->fetchArray(SQLITE3_ASSOC); + $results_full = $row['anzahl']; + + $count = 0; + + // Holen der Ergebnisse + $result = $db->query("SELECT * FROM bridges WHERE plug_id = '" . $plug . "';"); + + // Zeigt ein horizonales Auswhl-Menü an, das alle Brücken als auswahl bietet + while ($row = $result->fetchArray(SQLITE3_ASSOC)) { + ?> + + +
+
+

Name für dieses Programm

+ + Hinweis: Der Name kann aus Sicherheitsgründen im Nachhinein nicht mehr geändert werden. Bei dieser Stellwerksart ist die ID fünfstellig
Ein '-' kennzeichnet ein "Don't-Care". Darüber hinaus sind nur Großbuchstaben und Zahlen zugelassen +
+
+ + + + + +
+
+

Beschreibung für dieses Programm

+ +
+
Speichern
+ + +
Programm-Editor - Bearbeiten
+ query("SELECT * FROM program_bridge_relation WHERE program_id = '" . $meta . "';"); + + while ($bridge = $bridges->fetchArray(SQLITE3_ASSOC)) { + + $bridges_url[] = $bridge["bridge_id"]; + + } + + $url = '../stecker.php?translate=true&data=' . urlencode(json_encode($bridges_url)); // URL parsen + + print(''); + + + + ?> + +
+

Zugehörige Brücken

+ Wählen Sie die Brücken, welche von diesem Programm gefordert werden + +
+ + query("SELECT COUNT(*) as anzahl FROM bridges WHERE plug_id = '" . $plug . "';"); + + $number_bridges = $number_bridges->fetchArray(SQLITE3_ASSOC); + $number_bridges = $number_bridges['anzahl']; + $number_joined = 0; + + // Hole zunächst ALLE Brücken aus der DB + $bridges = $db->query("SELECT * FROM bridges WHERE plug_id = '" . $plug . "';"); + + while ($bridge = $bridges->fetchArray(SQLITE3_ASSOC)) { + + // schaue nun, ob diese Brücke Teil des Programms ist + $bridges_contained = $db->query("SELECT COUNT(*) as anzahl FROM program_bridge_relation WHERE plug_id = '" . $plug . "' and bridge_id = '" . $bridge["id"] . "' and program_id = '" . $meta . "';"); + + $row = $bridges_contained->fetchArray(SQLITE3_ASSOC); + $number = $row['anzahl']; + + // Die Anzahl gibt Aufschlus darüber, ob Brücke von Programm gefordert ist -> wenn schon vorhanden, markiere diese Brücke schon vor ("checked"-Attribut) + ?> + + +
+
+

Name für dieses Programm

+ Darf auf Sicherheitsgründen nicht mehr geändert werden + +
+ query("SELECT * FROM program_id_bits WHERE program_id = '" . $meta . "' ORDER BY position;"); + while ($program_identifier = $program_identifiers->fetchArray(SQLITE3_ASSOC)) { + echo ''; + } + + // Vorausgefüllte Programmbezeichnung + $description = ($row = $db->querySingle("SELECT program_description FROM programs WHERE id = '$meta' and plug_id = '$plug'", true)) ? $row['program_description'] : null; + + ?> +
+
+

Beschreibung für dieses Programm

+ +
+
Speichern
+ + +

+ Die Prüfung konnte nicht abgeschlossen werden. Fehler +

+ + +
Selbstdiagnose - Ergebnisse
+
+ + + +
+

Detailansicht

+

Der Pin der jeweiligen Spalte wird eingeschaltet; Pins, welche einen Eingang registrieren, werden in der entsprechenden Zeile grün dargestellt.

+ + + "; + for ($j = 0; $j <= 32; $j++) { + $index = $j - 1; + if ($index == -1) { + echo ""; + } else { + echo ""; + } + } + echo ""; + + // Matrix + for ($i = 0; $i <= 31; $i++) { + echo ""; + echo ""; + + for ($j = 0; $j <= 31; $j++) { + $search = [$j, $i]; + if (in_array($search, $bridges)) { + echo ""; + } else { + echo ""; + } + } + echo ""; + } + ?> +
$index
$i-
+
+ Erklärung:
+ Jede Verbindung darf nur einmal aufgeführt sein. Gibt es Doppelungen, so besteht eine unzulässige Verbindung zu einem anderen Pin. + Kann sich ein Pin nicht selbst auslesen, so weist dies auf einen internen Kabelbruch oder ein defektes Bauteil hin. + Ist der Fehler spiegelsymmetrisch (z.B. 10-20 und 20-10), deutet dies auf einen Kurzschluss im System hin. + Tritt der Fehler nur einseitig auf, ist ein Bauteil defekt.

"); + ?> + +
+ Zurück +
+ + +
Selbstdiagnose
+ +

Sollten während Messungen Fehler aufgetreten sein, kann hier eine interne Diagnose durchgeführt werden. Dabei wird jeder "Pin" einmalig als Ausgang gesetzt und gleichzeitig der Wert ausgelesen. Nur, wenn der Pin selbst gelesen werden kann und keine unzulässigen Verbindungen zu anderen Pins bestehen, wird die Prüfung erfolgreich abgeschlossen.

+
+

Bitte trennen Sie zuvor alle Verbindungen, sodass sich das Gerät im Leerlauf befindet. Angeschlossene Kabel und Stecker verfälschen das Ergebnis!

+
+ +
+ Selbstdiagnose ausführen +
+ diff --git a/pagecontent/start-cable.php b/pagecontent/start-cable.php new file mode 100755 index 0000000..2659f19 --- /dev/null +++ b/pagecontent/start-cable.php @@ -0,0 +1,122 @@ + +

+ Die Prüfung konnte nicht abgeschlossen werden. Fehler +

+ 2. Fehlerfall +//es wäre schon sehr eigenartig, wenn verschiedene Fehlercodes nacheinander ausgespuckt würden (Wackelkontakt?) +if($bridges1 !== $bridges2) { + + measurementsDiffer($bridges1, $bridges2); + +} + +// Isoliere die Brücken und speichere sie als $bridges (hiermit wird später alles weitere verarbeitet) +$bridges = array_map(function($entry) { + return $entry[0]; +}, $bridges1); + +//Isoliere die Fehlercodes der ersten Messung; da Messungen identisch reicht erste Messung +$errors = array_filter($bridges1, function($entry) { + return $entry[1] !== 0; +}); + +//Wenn Fehlerspeicher gefüllt: Ausgabe und Abbruch +if (!empty($errors)) { + + measurementsContainErrors($bridges1); + +} + +// ---------- +//Ab hier wird die Logik geprüft, die Messung ist bis hierhin äußerlich in Ordnung +// ---------- + + +$bridges = translateArray($bridges, $translationMap, $translationMap2); + +$cleaned_output = []; +$errors = []; + +$ok = true; + +// iteriere durch die Messung und prüfe, ob stets gilt [[20,20],[22,22]] +//wenn nein: Kurzschluss ($errors) +foreach($bridges as $bridge) { + if($bridge[0] == $bridge[1]) { + $cleaned_output[] = $bridge[0]; + } + else { + $ok = false; + $errors[] = $bridge; + } +} + + +//Haupt-Steckergrafik +$url = 'stecker.php?mode=fill&data=' . urlencode(json_encode($cleaned_output)); + +?> + +
Ergebnis
+
+

Vollständigkeit

+

Die folgende Grafik zeigt Ihnen, auf welchen Adern eine durchgängige Verbindung erkannt wurde. Nur, wenn alle Pins grün gefüllt sind, ist das Spurkabel in Ordnung!

+ + + + + + +

Unzulässigkeiten

+

Die folgende Grafik zeigt Ihnen, auf welchen Adern eine Verbindung zu einer anderen Ader erkannt wurde. Ist die Grafik leer, sind alle Adern korrekt isoliert.

+ +

+ Es wurde mindestens ein Kurzschluss gefunden! +

+ + + + + +
Zurück
+ diff --git a/pagecontent/start.php b/pagecontent/start.php new file mode 100755 index 0000000..9ac01f7 --- /dev/null +++ b/pagecontent/start.php @@ -0,0 +1,625 @@ + +

+ Die Prüfung konnte nicht abgeschlossen werden. Fehler +

+ 2. Fehlerfall +//es wäre schon sehr eigenartig, wenn verschiedene Fehlercodes nacheinander ausgespuckt würden (Wackelkontakt?) +if($bridges1 !== $bridges2) { + + measurementsDiffer($bridges1, $bridges2); + +} + +// Isoliere die Brücken und speichere sie als $bridges (hiermit wird später alles weitere verarbeitet) +$bridges = array_map(function($entry) { + return $entry[0]; +}, $bridges1); + +//Isoliere die Fehlercodes der ersten Messung; da Messungen identisch reicht erste Messung +$errors = array_filter($bridges1, function($entry) { + return $entry[1] !== 0; +}); + +//Wenn Fehlerspeicher gefüllt: Ausgabe und Abbruch +if (!empty($errors)) { + + measurementsContainErrors($bridges1); + +} + +// ---------- +//Ab hier wird die Logik geprüft, die Messung ist bis hierhin äußerlich in Ordnung +// ---------- + + +//Hier wird jetzt geprüft, dass jede Messung bidirektional aufgenommen wurde, also 10<>20 UND 20<>10 +$doubles = []; // Array zur Verfolgung von Doppelungen +$errors = []; // Array für fehlende Doppelungen + +// Untersuche die einzelnen aufgenommenen Messpaare +foreach ($bridges as $pair) { + // Jedes Paar MUSS aus zwei Elementen bestehen + if (count($pair) !== 2) { + $errors[] = $pair; // speichere diesen fehlerhaften Eintrag + continue; + } + // Sortiere die Paare, um [23,14] und [14,23] gleich zu behandeln + sort($pair); + // Setze dieses Paar als "Index", um es gleich wieder zu finden + $key = implode("-", $pair); + // Prüfe, ob dieser Eintrag schon einmal da war + if (isset($doubles[$key])) { + $doubles[$key]++; // Verbindung erneut gefunden + } + else { + $doubles[$key] = 1; // Erstmaliges Auftreten + } +} +// Überprüfe, ob jede Verbindung genau zweimal vorhanden ist +foreach ($doubles as $key => $count) { + if ($count !== 2) { + $errors[] = $key; + } +} + +// Abbruch bei fehlenden Doppelungen +if (!empty($errors)) { + echo "Die Messung wurde zwar erfolgreich abgeschlossen, die Messergebnisse überzeugten jedoch nicht.
Es ist notwendig, dass Kabelbrücken aus beiden Richtungen erkannt werden.
Erwägen Sie, eine erneute Messung durchzuführen. Sollte der Fehler erneut auftreten, ist ein interner Defekt des Prüfgeräts wahrscheinlich.

Die folgende(n) Kabelbrücke(n) konnte(n) nur one-way gemessen werden:
"; + print_r($errors); + + ?> +
Zurück
+ + +
Ergebnis
+
+
+

Gesamtergebnis

+
+ +
+
+

Einzelbrücken

+
+
+
+ +
+ +
+

Identifizierte Brücken

+query(" + SELECT + b.id AS bridge_id, + n.node_from, + n.node_to + FROM + bridges b + JOIN + nodes n ON b.id = n.required_by + ORDER BY + b.id +"); + + +/* +Ausgabe sieht z.B. so aus: + +| bridge_id | node_from | node_to | +| --------- | --------- | ------- | +| 1 | 20 | 30 | +| 1 | 13 | 17 | +| 2 | 10 | 15 | + +*/ + +//die DB-ausgbe wird als indexiertes Array gespeichert, um einfacher durchsucht werden zu können +$bridges_db = []; +while ($row = $nodes->fetchArray(SQLITE3_ASSOC)) { + //Die Bridge-ID ist der Index, die geforderten Brücken werden im Standardformat eingefügt, z.B.: 1 => [[20,30],[13,17]] + $bridge_id = $row['bridge_id']; + $bridges_db[$bridge_id][] = [$row['node_from'], $row['node_to']]; +} + +//Vergleiche Datenbank mit gemessenen Werten +//iteriere durch alle Datenabnk-Brücken +foreach($bridges_db as $bridge_db => $required_nodes) { + $satisfied = true; + //iteriere durch alle von einer Brücke geforderten Node-Paare + foreach($required_nodes as $required_nodes_pairs) { + $innerMatch = 0; + // vergleiche die aktuelle Datenbank-Node mit allen gemessenen Nodes (+überkreuz) + for($i=0;$i<$measurement_result_number;$i++) { + if(($required_nodes_pairs[0] == $measurement_result[$i][0] && $required_nodes_pairs[1] == $measurement_result[$i][1] )||($required_nodes_pairs[1] == $measurement_result[$i][0] && $required_nodes_pairs[0] == $measurement_result[$i][1])){ + // innerhalb der Node-zu-Node-Verbidnung wurde die Bedingung erfüllt + $innerMatch = 1; + } + } + if($innerMatch != 1) { + // An dieser Stelle wird satisfied direkt negiert, da mind. eine Abhängigkeit, die in der Datenbank gefordert ist, nicht gefunden wurde + $satisfied = false; + } + } + // Kommen alle Nodes vor, wird die Brücke mit ihrem entsprechenden Namen dargestellt + if($satisfied == true) { + //Brücke vollständig enthalten -> speichern für später (Programm-Erkennung) + $identified_bridges[] = $bridge_db; + + //Erinnerung: es gilt bridge_db=>bridges_db + $url = '../stecker.php?data=' . urlencode(json_encode($bridges_db[$bridge_db])); // URL parsen + ?> +
+ ' . $bridge_db . '
'); + print(''); + ?> +
+ query(" + SELECT + p.id AS program_id, + p.program_description, + b.id AS bridge_id, + r.plug_id + FROM + program_bridge_relation r + JOIN programs p ON p.id = r.program_id + JOIN bridges b ON b.id = r.bridge_id + WHERE r.plug_id = '" . $plug . "' +"); + + +/* +Ausgabe sieht z.B. so aus: + +| program_id | program_description | bridge_id | +| ---------- | ------------------- | --------- | +| 1 | Weiche, die... | 1 | +| 2 | Gs, die... | 1 | +| 2 | Gs, die... | 10 | + +*/ + + +$bridges_db = []; +while ($row = $programs->fetchArray(SQLITE3_ASSOC)) { + // Gleiches vorgehen wie bei den Brücken: Programm-ID ist Index, Brücken sind Inhalt (2 => [1,10]) + $program_id = $row['program_id']; + $programs_db[$program_id][] = $row['bridge_id']; + $program_descriptions[$program_id] = $row['program_description']; +} + + +//Ist eine Stelle der Kennung des Programmsteckers zwischendrin doppelt beschrieben woren, wird idse Variable wahr +$double_program_identifier = false; + + +?> +
+

Identifizierte Programme

+
+ $required_bridges) { + + if(!empty($identified_bridges)) { //Abscicherung, sonst würde ein leeres Programm immer als vollständig enthalten markiert werden + $ok = 1; + } + else { + $ok = 0; + } + + // Iteriere durch alle geforderten Brücken + foreach ($required_bridges as $bridge_id) { + // sollte sich eine Programmbrücke nicht unter den identifizierten Messwerte befinden -> ok=0 + if (!in_array($bridge_id, $identified_bridges)) { + $ok = 0; + } + } + + // Programm ist mit allen Brücken vollständig vertreten + if($ok == 1) { + + ?> +
+
+ + query("SELECT * FROM program_id_bits WHERE program_id = '" . $program_db . "' ORDER BY position;"); + + while ($program_identifier = $program_identifiers->fetchArray(SQLITE3_ASSOC)) { + echo '' . $program_identifier["value"] . ''; + if($program_identifier["value"] != "" && $program_identifier["value"] != "-") { // wenn noch nicht anders beschrieben oder leer -> Einfügen in großen "Gesamtspeicher" für später + if($program_cache[$program_identifier["position"]] == "?") { + $program_cache[$program_identifier["position"]] = $program_identifier["value"]; + } + else { + // Wenn Stelle schon gesetzt war -> Warnung speichern + $double_program_identifier = true; + } + } + $i++; + } + ?> + +
+ '); + + ?> + +
+
+ + + +
+ +
+ +
+

Kennungs-Nummer

+
+
+
+ + ' . $cacheChar . ''; + if($cacheChar == "?") { + $ok = 0; + } + } + ?> + +
+
+
+ +

+ Wichtiger Hinweis: Es sind nicht alle Stellen der Kennungsnummer definiert. Dies deutet darauf hin, dass möglicherweise nicht alle Programme des Steckers korrekt erkannt wurden. Andererseits kann auch die Programm-Datenbank unvollständig sein, sodass eine Erkennung nicht möglich ist.
Betrachten Sie daher das Ergebnis mit Vorsicht! +

+ +

+ Wichtiger Hinweis: Im Laufe der Programmabgleiche wurde versucht, eine Stelle der Gesamtkennungsnummer zu übeschreiben, da diese bereits durch ein vorheriges Programm festgelegt wurde. Details dazu finden Sie unter "Indentifizierte Programme". Bitte gleichen Sie die Spalten ab, ob eine doppelte Stellendefinition vorliegt!
Betrachten Sie daher das Ergebnis mit Vorsicht! +

+ +
+

Identische Messungen

+
+ + query(" + SELECT + m.id AS measurement_id, + m.comment AS comment, + m.timestamp AS timestamp, + m.place_name AS place_name, + n.node_from AS node_from, + n.node_to AS node_to + FROM + measurements m + JOIN places p ON p.name = m.place_name + JOIN measurement_nodes n ON m.id = n.required_by + ORDER BY m.id + "); + + /* + Ausgabe sieht z.B. so aus: + + | measurement_id | comment | timestamp | place_name | node_from | node_to | + | -------------- | ------- | ---------- | -------------------- | --------- | ------- | + | 1 | abc | 1753357680 | Mannheim-Waldhof Wf | 20 | 26 | + | 1 | abc | 1753357680 | Mannheim-Waldhof Wf | 13 | 16 | + | 1 | abc | 1753357680 | Mannheim-Waldhof Wf | 30 | 35 | + | 2 | def | 1753357548 | Dudweiler Df | 30 | 35 | + | 2 | def | 1753357548 | Dudweiler Df | 10 | 12 | + + */ + + // Auch hier werden alle Messungen erstmal komplex indexiert (also zwei ineinander indexierte Arrays): measurement_id=>(nodes=>[[30,35],[10,12]], place_name, timestamp, comment) + $measurements_db = []; + while ($row = $measurement_nodes_db->fetchArray(SQLITE3_ASSOC)) { + $measurement_id = $row['measurement_id']; + $measurements_db[$measurement_id]['nodes'][] = [$row['node_from'],$row['node_to']]; + $measurements_db[$measurement_id]['place_name'] = $row['place_name']; + $measurements_db[$measurement_id]['timestamp'] = $row['timestamp']; + $measurements_db[$measurement_id]['comment'] = $row['comment']; + } + + //Indikator, dass alle Brücken gefunden wurden + $ok = 0; + + foreach ($measurements_db as $measurement_db) { + $satisfied = true; + + foreach ($measurement_result as $bridge_measure) { + $found = false; + + foreach ($measurement_db["nodes"] as $measurement_db_node) { + // Prüfe, ob Brücke identisch (+ überkreuz) + if ( + ($bridge_measure[0] == $measurement_db_node[0] && $bridge_measure[1] == $measurement_db_node[1]) || + ($bridge_measure[0] == $measurement_db_node[1] && $bridge_measure[1] == $measurement_db_node[0]) + ) { + $found = true; // Brücke gefunden + break; // Nächste Brücke prüfen + } + } + + if (!$found) { + // Diese Brücke nicht in DB gefunden -> Messung nicht erfüllt + $satisfied = false; + break; // weitere Brücken prüfen sinnlos + } + } + + if ($satisfied) { + $ok = 1; + + ?> +
+ + + + + + + + + + + +
+
+
+
+ + + " . date('d.m.Y H:i:s',$measurement_db["timestamp"]) . ""; + ?> + +
+
+
+
+
+ +
+ + +

Keine bekannt.

+ + +
+ +

Anpassen der Ergebnisse

+ + + + + + + + + + + + + + + + + + + +
+

Kennungs-Nr.

+
+
+ + + + + +
+
+

Sekundärbrücken abwählen

+
+
+ + + + + + + + + + +
+
+ +
+

Besonderheiten

+
+ +
+ +
+

Speichern unter...?

+ +
+ +
+ + + +
+
+

+ Die dargestellten Vorschläge befinden sich bereits in der Datenbank. Geben Sie einen Ort sein, der noch nicht in der Datenbank vorhanden ist, so wird dieser automatisch erstellt +

+ +
+ +
Zurück
+ + diff --git a/pagecontent/stecker.php b/pagecontent/stecker.php new file mode 100755 index 0000000..0dd4abe --- /dev/null +++ b/pagecontent/stecker.php @@ -0,0 +1,124 @@ +'; +?> + + + + + + + 30 + 10 + 31 + 11 + + + + + + + + + + + + + + + + + + + [70, 68], 20 => [70, 44], 30 => [70, 20], + 11 => [405, 68], 12 => [370, 68], 13 => [335, 68], 14 => [300, 68], + 15 => [265, 68], 16 => [230, 68], 17 => [195, 68], 18 => [160, 68], 19 => [125, 68], + 21 => [405, 44], 22 => [370, 44], 23 => [335, 44], 24 => [300, 44], + 25 => [265, 44], 26 => [220, 44], 27 => [185, 44], 28 => [150, 44], 29 => [115, 44], + 31 => [405, 20], 32 => [370, 20], 33 => [335, 20], 34 => [300, 20], + 35 => [265, 20], 36 => [230, 20], 37 => [195, 20], 38 => [160, 20], 39 => [125, 20], + ]; + + // Rechtecke einfärben, wenn "fill"-Modus aktiv ist + if ($_GET["mode"] == "fill") { + $data = $_GET["data"]; + $data = json_decode(urldecode($data), true); + + foreach ($pin_coordinates as $key => $coordinates) { + $fill = in_array($key, $data) ? "green" : "red"; + echo ''; + } + } else { + // Nur Umrisse + foreach ($pin_coordinates as $coordinates) { + echo ''; + } + } + + // SVG frühzeitig beenden, wenn kein Data vorhanden oder im fill-Modus + if (empty($_GET['data']) || $_GET["mode"] == "fill") { + print ""; + die(); + } + + // Brückenverbindungen übersetzen, falls aktiviert + if ($_GET["translate"] == "true") { + $db = new SQLite3('db-test/test.db'); + $data = json_decode($_GET["data"], true); + $plug = json_decode(file_get_contents("settings.json"), true)["plug"]; + $nodes_raw = []; + + foreach ($data as $bridge) { + $nodes = $db->query("SELECT node_from, node_to FROM nodes WHERE required_by = " . $bridge); + while ($node = $nodes->fetchArray(SQLITE3_ASSOC)) { + $nodes_raw[] = [$node['node_from'], $node['node_to']]; + } + } + $inputArray = $nodes_raw; + } else { + $inputArray = json_decode($_GET['data'], true); + } + + // Koordinaten übersetzen + $translatedArray = array_map(function ($pair) use ($pin_coordinates) { + return [ + $pin_coordinates[$pair[0]], + $pin_coordinates[$pair[1]], + ]; + }, $inputArray); + + $colors = ["#1a5fb4", "#26a269", "#e5a50a", "#c64600", "#a51d2d", "#613583", "#63452c", "#3d3846"]; + + // Linien ausgeben + $colorNumber = 0; + foreach ($translatedArray as $line) { + $point1 = $line[0]; + $point2 = $line[1]; + + $point1x = $point1[0] + 10; + $point1y = $point1[1] + 5; + $point2x = $point2[0] + 10; + $point2y = $point2[1] + 5; + + echo ''; + + $colorNumber = ($colorNumber + 1) % count($colors); + } + ?> + diff --git a/pagecontent/system.php b/pagecontent/system.php new file mode 100755 index 0000000..6c4fb15 --- /dev/null +++ b/pagecontent/system.php @@ -0,0 +1,139 @@ + +
System-Aktionen
+
+
Herunterfahren
+
Neu starten
+
Systeminfo
+
WLAN-/DHCP-Konfig anzeigen
+
Selbstdiagnose
+ + +
System-Aktionen: Sys-Info
+
+ +

Kernel-Version:

+ $output"; + ?> + +

Betriebssystem:

+ $output"; + ?> + +

System-Uptime:

+ $output"; + ?> + +

Letzter Boot:

+ $output"; + ?> + +

CPU-Infos:

+ $output"; + ?> + +

Arbeitsspeicher:

+ $output"; + ?> + +

Speicherplatz:

+ $output"; + ?> + +

Python-Version:

+ $output"; + ?> + +

Top 5 Prozesse nach CPU-Auslastung:

+ $output"; + ?> + +

CPU-Temperatur:

+ $output"; + ?> +

System-Uhrzeit (PHP):

+ " . strftime("%A, %e. %B %Y %H:%M:%S") . ""; + + + break; + case "wifi": + ?> +
System-Aktionen: WLAN/DHCP
+
+

Netzwerkverbindungen:

+ $output"; + ?> +

Verbundene Geräte:

+ $output"; + ?> +

Netzwerkschnittstellen:

+ $output"; + ?> +

Ifconfig:

+ $output"; + break; + case "reboot": + ?> +
System-Aktionen: Neustart
+
+ +

Das Prüfgerät wird nun neu gestartet. Die Verbindung wird in Kürze getrennt.

+

Stellen Sie sicher, dass Sie sich im Anschluss wieder mit dem Geräte-WLAN verbinden.

+ &1'); + echo "------- Fehlerprotokoll nach dieser Zeile -------
$output"; + break; + case "shutdown": + ?> +
System-Aktionen: Herunterfahren
+
+ +

Das Prüfgerät wird nun heruntergefahren. Die Verbindung wird in Kürze getrennt.

+

Auf Wiedersehen!

+ &1'); + echo "------- Fehlerprotokoll nach dieser Zeile -------
$output"; + break; +} diff --git a/pagecontent/temp.json b/pagecontent/temp.json new file mode 100755 index 0000000..e69de29 diff --git a/python/.plug-check.py.swp b/python/.plug-check.py.swp new file mode 100644 index 0000000..dee90ad Binary files /dev/null and b/python/.plug-check.py.swp differ diff --git a/python/.selfcheck.py.swp b/python/.selfcheck.py.swp new file mode 100644 index 0000000..7134580 Binary files /dev/null and b/python/.selfcheck.py.swp differ diff --git a/python/cable-check.py b/python/cable-check.py new file mode 100644 index 0000000..e30558f --- /dev/null +++ b/python/cable-check.py @@ -0,0 +1,230 @@ +import smbus +import time +import json +import sys + +pruefer1 = 0x20 +pruefer2 = 0x21 +observer1 = 0x22 +observer2 = 0x23 +spurkabel1 = 0x24 +spurkabel2 = 0x25 + +adressen = [pruefer1, pruefer2, observer1, observer2, spurkabel1, spurkabel2] + +IODIRA = 0x00 # Eingang / Ausgang A +IODIRB = 0x01 # Eingang / Ausgang B +GPPUA = 0x0C # Eingang / Ausgang A +GPPUB = 0x0D # Eingang / Ausgang B +GPIOA = 0x12 # GPIO A +GPIOB = 0x13 # GPIO B + +array = {} +logs = {} +data = [] + + +try: + # Initialisiere den I2C Bus + bus = smbus.SMBus(1) +except: + logs["errorcode"] = 12 + array["logs"] = logs + json_output = json.dumps(array) + print(json_output) + sys.exit(0) + +def default(): + + #Schleife für alle Registeradressen (0x00 bis 0x15) + for register in range(0x00, 0x16): + for adresse in adressen: + #print("Setze " + str(adresse) + " in Register " + str(register) + " auf Wert 0x00") + bus.write_byte_data(adresse, register, 0x00) + + + #print("Hallo! Es wird damit begonnen, alle Register zurpckzusetzen...") + #Aktiviere alle Pullups und setze alle als Eingang + time.sleep(0.05) + for adresse in adressen: + bus.write_byte_data(adresse, IODIRA, 0xFF) + bus.write_byte_data(adresse, GPPUA, 0xFF) + bus.write_byte_data(adresse, IODIRB, 0xFF) + bus.write_byte_data(adresse, GPPUB, 0xFF) + + #print("============= Reset erfolgreich! =============") + +def main(): + try: + default() + except: + logs["errorcode"] = 11 + return + for pin in range(32): # 0 bis 31 + aktuellAn = pin + + for adresse in adressen: + bus.write_byte_data(adresse, IODIRA, 0xFF) + bus.write_byte_data(adresse, GPPUA, 0xFF) + bus.write_byte_data(adresse, IODIRB, 0xFF) + bus.write_byte_data(adresse, GPPUB, 0xFF) + + time.sleep(0.05) + + if pin < 16: + # Setze den gewählten Pin auf HIGH + if pin < 8: # Pins A0-A7 + + wert = 0xFF & ~(1 << pin) # Alle Bits auf 1, außer das eine gewünschte auf 0 + + bus.write_byte_data(pruefer1, GPPUA, wert) + bus.write_byte_data(pruefer1, IODIRA, wert) + bus.write_byte_data(pruefer1, GPIOA, 0x00) + + else: # Pins B0-B7 + wert = 0xFF & ~(1 << pin - 8) # Alle Bits auf 1, außer das eine gewünschte auf 0 + + bus.write_byte_data(pruefer1, GPPUB, wert) + bus.write_byte_data(pruefer1, IODIRB, wert) + bus.write_byte_data(pruefer1, GPIOB, 0x00) + else: + # Setze den gewählten Pin auf HIGH + if pin < 24: # Pins A0-A7 + wert = 0xFF & ~(1 << pin - 16) # Alle Bits auf 1, außer das eine gewünschte auf 0 + + bus.write_byte_data(pruefer2, GPPUA, wert) + bus.write_byte_data(pruefer2, IODIRA, wert) + bus.write_byte_data(pruefer2, GPIOA, 0x00) + else: # Pins B0-B7 + wert = 0xFF & ~(1 << pin - 24) # Alle Bits auf 1, außer das eine gewünschte auf 0 + + bus.write_byte_data(pruefer2, GPPUB, wert) + bus.write_byte_data(pruefer2, IODIRB, wert) + bus.write_byte_data(pruefer2, GPIOB, 0x00) + + time.sleep(0.05) + + ##print(f"i2cget -y 1 0x20 0x12") + pruefer1_A_value = bus.read_byte_data(pruefer1, GPIOA) + pruefer1_B_value = bus.read_byte_data(pruefer1, GPIOB) + pruefer2_A_value = bus.read_byte_data(pruefer2, GPIOA) + pruefer2_B_value = bus.read_byte_data(pruefer2, GPIOB) + + observer1_A_value = bus.read_byte_data(observer1, GPIOA) + observer1_B_value = bus.read_byte_data(observer1, GPIOB) + observer2_A_value = bus.read_byte_data(observer2, GPIOA) + observer2_B_value = bus.read_byte_data(observer2, GPIOB) + + spurkabel1_A_value = bus.read_byte_data(spurkabel1, GPIOA) + spurkabel1_B_value = bus.read_byte_data(spurkabel1, GPIOB) + spurkabel2_A_value = bus.read_byte_data(spurkabel2, GPIOA) + spurkabel2_B_value = bus.read_byte_data(spurkabel2, GPIOB) + + #print(f"------>>>>Es konnte vom pruefer1_A_value der Wert {format(pruefer1_A_value, '08b')} festgetsellt werden") + #print(f"------>>>>Es konnte vom pruefer1_B_value der Wert {format(pruefer1_B_value, '08b')} festgetsellt werden") + #print(f"------>>>>Es konnte vom pruefer2_A_value der Wert {format(pruefer2_A_value, '08b')} festgetsellt werden") + #print(f"------>>>>Es konnte vom pruefer2_B_value der Wert {format(pruefer2_B_value, '08b')} festgetsellt werden") + + #print(f"------>>>>Es konnte vom observer1_A_value der Wert {format(observer1_A_value, '08b')} festgetsellt werden") + #print(f"------>>>>Es konnte vom observer1_B_value der Wert {format(observer1_B_value, '08b')} festgetsellt werden") + #print(f"------>>>>Es konnte vom observer2_A_value der Wert {format(observer2_A_value, '08b')} festgetsellt werden") + #print(f"------>>>>Es konnte vom observer2_B_value der Wert {format(observer2_B_value, '08b')} festgetsellt werden") + + #print(f"------>>>>Es konnte vom spurkabel1_A_value der Wert {format(spurkabel1_A_value, '08b')} festgetsellt werden") + #print(f"------>>>>Es konnte vom spurkabel1_B_value der Wert {format(spurkabel1_B_value, '08b')} festgetsellt werden") + #print(f"------>>>>Es konnte vom spurkabel2_A_value der Wert {format(spurkabel2_A_value, '08b')} festgetsellt werden") + #print(f"------>>>>Es konnte vom spurkabel2_B_value der Wert {format(spurkabel2_B_value, '08b')} festgetsellt werden") + + #print("===========================================================================") + + + for j in range(8): # Verarbeite 4*8=32 Pins + Jeweils 32 Observer- und Spurkabel-Daten + bitmaske = 1 << j # Erstelle eine Maske: 1, 2, 4, 8, 16, ... + + pruefer1_A_value_bit = bool(pruefer1_A_value & bitmaske) + pruefer1_B_value_bit = bool(pruefer1_B_value & bitmaske) + pruefer2_A_value_bit = bool(pruefer2_A_value & bitmaske) + pruefer2_B_value_bit = bool(pruefer2_B_value & bitmaske) + + observer1_A_value_bit = bool(observer1_A_value & bitmaske) + observer1_B_value_bit = bool(observer1_B_value & bitmaske) + observer2_A_value_bit = bool(observer2_A_value & bitmaske) + observer2_B_value_bit = bool(observer2_B_value & bitmaske) + + spurkabel1_A_value_bit = bool(spurkabel1_A_value & bitmaske) + spurkabel1_B_value_bit = bool(spurkabel1_B_value & bitmaske) + spurkabel2_A_value_bit = bool(spurkabel2_A_value & bitmaske) + spurkabel2_B_value_bit = bool(spurkabel2_B_value & bitmaske) + + + + i = j + + + + if spurkabel1_A_value_bit == False: + data.append([[pin ,i],0]) + + if pruefer1_A_value_bit == False and observer1_A_value_bit == True: + data.append([[pin ,i],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if pruefer1_A_value_bit == True and observer1_A_value_bit == False: + data.append([[pin ,i],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + + i = j + 8 + + + if spurkabel1_B_value_bit == False: + data.append([[pin ,i],0]) + + if pruefer1_B_value_bit == False and observer1_B_value_bit == True: + data.append([[pin ,i],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if pruefer1_B_value_bit == True and observer1_B_value_bit == False: + data.append([[pin ,i],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + + i = j + 16 + + + if spurkabel2_A_value_bit == False: + data.append([[pin ,i],0]) + + if pruefer2_A_value_bit == False and observer2_A_value_bit == True: + data.append([[pin ,i],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if pruefer2_A_value_bit == True and observer2_A_value_bit == False: + data.append([[pin ,i],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + + i = j + 24 + + if spurkabel2_B_value_bit == False: + data.append([[pin ,i],0]) + + if pruefer2_B_value_bit == False and observer2_B_value_bit == True: + data.append([[pin ,i],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if pruefer2_B_value_bit == True and observer2_B_value_bit == False: + data.append([[pin ,i],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + logs["errorcode"] = 10 + +if __name__ == "__main__": + try: + main() + except: + logs["errorcode"] = 13 + array["logs"] = logs + array["data"] = data + json_output = json.dumps(array) + print(json_output) + + + + diff --git a/python/observer-full.py b/python/observer-full.py new file mode 100644 index 0000000..2709889 --- /dev/null +++ b/python/observer-full.py @@ -0,0 +1,219 @@ +import smbus +import time +import json +import sys + +pruefer1 = 0x20 +pruefer2 = 0x21 +observer1 = 0x22 +observer2 = 0x23 + +adressen = [pruefer1, pruefer2, observer1, observer2] + +IODIRA = 0x00 # Eingang / Ausgang A +IODIRB = 0x01 # Eingang / Ausgang B +GPPUA = 0x0C # Eingang / Ausgang A +GPPUB = 0x0D # Eingang / Ausgang B +GPIOA = 0x12 # GPIO A +GPIOB = 0x13 # GPIO B + +array = {} +logs = {} +data = [] + + +try: + # Initialisiere den I2C Bus + bus = smbus.SMBus(1) +except: + logs["errorcode"] = 12 + array["logs"] = logs + json_output = json.dumps(array) + print(json_output) + sys.exit(0) + +def default(): + + #Schleife für alle Registeradressen (0x00 bis 0x15) + for register in range(0x00, 0x16): + for adresse in adressen: + #print("Setze " + str(adresse) + " in Register " + str(register) + " auf Wert 0x00") + bus.write_byte_data(adresse, register, 0x00) + + + #print("Hallo! Es wird damit begonnen, alle Register zurpckzusetzen...") + #Aktiviere alle Pullups und setze alle als Eingang + time.sleep(0.05) + for adresse in adressen: + bus.write_byte_data(adresse, IODIRA, 0xFF) + bus.write_byte_data(adresse, GPPUA, 0xFF) + bus.write_byte_data(adresse, IODIRB, 0xFF) + bus.write_byte_data(adresse, GPPUB, 0xFF) + + #print("============= Reset erfolgreich! =============") + +def main(): + try: + default() + except: + logs["errorcode"] = 11 + return + time.sleep(0.05) + for pin in range(32): # 0 bis 31 + aktuellAn = pin + + for adresse in adressen: + bus.write_byte_data(adresse, IODIRA, 0xFF) + bus.write_byte_data(adresse, GPPUA, 0xFF) + bus.write_byte_data(adresse, IODIRB, 0xFF) + bus.write_byte_data(adresse, GPPUB, 0xFF) + + time.sleep(0.05) + + if pin < 16: + # Setze den gewählten Pin auf HIGH + if pin < 8: # Pins A0-A7 + + wert = 0xFF & ~(1 << pin) # Alle Bits auf 1, außer das eine gewünschte auf 0 + + bus.write_byte_data(pruefer1, GPPUA, wert) + bus.write_byte_data(pruefer1, IODIRA, wert) + bus.write_byte_data(pruefer1, GPIOA, 0x00) + + else: # Pins B0-B7 + wert = 0xFF & ~(1 << pin - 8) # Alle Bits auf 1, außer das eine gewünschte auf 0 + + bus.write_byte_data(pruefer1, GPPUB, wert) + bus.write_byte_data(pruefer1, IODIRB, wert) + bus.write_byte_data(pruefer1, GPIOB, 0x00) + else: + # Setze den gewählten Pin auf HIGH + if pin < 24: # Pins A0-A7 + wert = 0xFF & ~(1 << pin - 16) # Alle Bits auf 1, außer das eine gewünschte auf 0 + + bus.write_byte_data(pruefer2, GPPUA, wert) + bus.write_byte_data(pruefer2, IODIRA, wert) + bus.write_byte_data(pruefer2, GPIOA, 0x00) + else: # Pins B0-B7 + wert = 0xFF & ~(1 << pin - 24) # Alle Bits auf 1, außer das eine gewünschte auf 0 + + bus.write_byte_data(pruefer2, GPPUB, wert) + bus.write_byte_data(pruefer2, IODIRB, wert) + bus.write_byte_data(pruefer2, GPIOB, 0x00) + + time.sleep(0.05) + + ##print(f"i2cget -y 1 0x20 0x12") + pruefer1_A_value = bus.read_byte_data(pruefer1, GPIOA) + pruefer1_B_value = bus.read_byte_data(pruefer1, GPIOB) + pruefer2_A_value = bus.read_byte_data(pruefer2, GPIOA) + pruefer2_B_value = bus.read_byte_data(pruefer2, GPIOB) + + observer1_A_value = bus.read_byte_data(observer1, GPIOA) + observer1_B_value = bus.read_byte_data(observer1, GPIOB) + observer2_A_value = bus.read_byte_data(observer2, GPIOA) + observer2_B_value = bus.read_byte_data(observer2, GPIOB) + + #print(f"------>>>>Es konnte vom Pruefer der Wert {format(Wert_pruefer, '08b')} festgetsellt werden") + #print(f"------>>>>Es konnte vom Observer der Wert {format(Wert_observer, '08b')} festgetsellt werden") + + + for j in range(8): # Verarbeite 4*8=32 Pins + Jeweils 32 Observer-Daten + bitmaske = 1 << j # Erstelle eine Maske: 1, 2, 4, 8, 16, ... + + pruefer1_A_value_bit = bool(pruefer1_A_value & bitmaske) + pruefer1_B_value_bit = bool(pruefer1_B_value & bitmaske) + pruefer2_A_value_bit = bool(pruefer2_A_value & bitmaske) + pruefer2_B_value_bit = bool(pruefer2_B_value & bitmaske) + + observer1_A_value_bit = bool(observer1_A_value & bitmaske) + observer1_B_value_bit = bool(observer1_B_value & bitmaske) + observer2_A_value_bit = bool(observer2_A_value & bitmaske) + observer2_B_value_bit = bool(observer2_B_value & bitmaske) + + + + i = j + + if pruefer1_A_value_bit == False and i != pin: + #print("Gefunden: " + str(pin) + " <> " + str(j)); + if observer1_A_value_bit == False: + #print("Dies wurde vom Observer bestätigt!"); + data.append([[pin ,i],0]) + else: + #print("Konnte NICHT bestätigen!"); + data.append([[pin ,i],1]) + if pruefer1_A_value_bit == False and observer1_A_value_bit == True: + data.append([[pin ,i],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if pruefer1_A_value_bit == True and observer1_A_value_bit == False: + data.append([[pin ,i],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + + i = j + 8 + + if pruefer1_B_value_bit == False and i != pin: + #print("Gefunden: " + str(pin) + " <> " + str(j)); + if observer1_B_value_bit == False: + #print("Dies wurde vom Observer bestätigt!"); + data.append([[pin ,i],0]) + else: + #print("Konnte NICHT bestätigen!"); + data.append([[pin ,i],1]) + if pruefer1_B_value_bit == False and observer1_B_value_bit == True: + data.append([[pin ,i],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if pruefer1_B_value_bit == True and observer1_B_value_bit == False: + data.append([[pin ,i],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + i = j + 16 + + if pruefer2_A_value_bit == False and i != pin: + #print("Gefunden: " + str(pin) + " <> " + str(j)); + if observer2_A_value_bit == False: + #print("Dies wurde vom Observer bestätigt!"); + data.append([[pin ,i],0]) + else: + #print("Konnte NICHT bestätigen!"); + data.append([[pin ,i],1]) + if pruefer2_A_value_bit == False and observer2_A_value_bit == True: + data.append([[pin ,i],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if pruefer2_A_value_bit == True and observer2_A_value_bit == False: + data.append([[pin ,i],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + i = j + 24 + + if pruefer2_B_value_bit == False and i != pin: + #print("Gefunden: " + str(pin) + " <> " + str(j)); + if observer2_B_value_bit == False: + #print("Dies wurde vom Observer bestätigt!"); + data.append([[pin ,i],0]) + else: + #print("Konnte NICHT bestätigen!"); + data.append([[pin ,i],1]) + if pruefer2_B_value_bit == False and observer2_B_value_bit == True: + data.append([[pin ,i],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if pruefer2_B_value_bit == True and observer2_B_value_bit == False: + data.append([[pin ,i],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + logs["errorcode"] = 10 + +if __name__ == "__main__": + try: + main() + except: + logs["errorcode"] = 13 + array["logs"] = logs + array["data"] = data + json_output = json.dumps(array) + print(json_output) + + + + diff --git a/python/observer.py b/python/observer.py new file mode 100644 index 0000000..534410e --- /dev/null +++ b/python/observer.py @@ -0,0 +1,127 @@ +import smbus +import time +import json +import sys + +pruefer = 0x20 +observer = 0x22 + + +IODIRA = 0x00 # Eingang / Ausgang A +IODIRB = 0x01 # Eingang / Ausgang B +GPPUA = 0x0C # Eingang / Ausgang A +GPPUB = 0x0D # Eingang / Ausgang B +GPIOA = 0x12 # GPIO A +GPIOB = 0x13 # GPIO B + +array = {} +logs = {} +data = [] + + +try: + # Initialisiere den I2C Bus + bus = smbus.SMBus(1) +except: + logs["errorcode"] = 12 + array["logs"] = logs + json_output = json.dumps(array) + print(json_output) + sys.exit(0) + + + + + +def default(): + #adressen = [pruefer] + + # Schleife für alle Registeradressen (0x00 bis 0x15) + #for register in range(0x00, 0x16): + # for adresse in adressen: + # time.sleep(0.05) + # #print("Setze " + str(adresse) + " in Register " + str(register) + " auf Wert 0x00") + # bus.write_byte_data(adresse, register, 0x00) + + + #print("Hallo! Es wird damit begonnen, alle Register zurpckzusetzen...") + time.sleep(0.05) + bus.write_byte_data(pruefer, IODIRA, 0xFF) + bus.write_byte_data(observer, IODIRA, 0xFF) + + bus.write_byte_data(pruefer, GPPUA, 0xFF) + bus.write_byte_data(observer, GPPUA, 0xFF) + #print("============= Reset erfolgreich! =============") + +def main(): + try: + default() + except: + logs["errorcode"] = 11 + return + time.sleep(0.05) + for pin in range(8): # 0 bis 7 + wert = 0xFF & ~(1 << pin) # Alle Bits auf 1, außer das eine gewünschte auf 0 + #print("======== Setze Pin " + str(pin) + " auf den Wert " + format(wert, '08b')); + + + bus.write_byte_data(pruefer, GPPUA, wert) + bus.write_byte_data(pruefer, IODIRA, wert) # Setze einzeln als Ausgang 111011111 usw... + bus.write_byte_data(pruefer, GPIOA, 0x00) # Deaktiviere alle pins (es reagiert sowieso nur der Ausgang, dh keine Bitmaske erforderlich) 00000000 usw.. + + time.sleep(0.05) + + ##print(f"i2cget -y 1 0x20 0x12") + Wert_pruefer = bus.read_byte_data(pruefer, GPIOA) + Wert_observer = bus.read_byte_data(observer, GPIOA) + + #print(f"------>>>>Es konnte vom Pruefer der Wert {format(Wert_pruefer, '08b')} festgetsellt werden") + #print(f"------>>>>Es konnte vom Observer der Wert {format(Wert_observer, '08b')} festgetsellt werden") + + ok = 0 + + for j in range(8): # Lese 4*8=32 Pins + bitmaske = 1 << j # Erstelle eine Maske: 1, 2, 4, 8, 16, ... + + Bit_pruefer = bool(Wert_pruefer & bitmaske) + Bit_observer = bool(Wert_observer & bitmaske) + + if Bit_observer == False: # Prüfe, ob aktueller Pin wirklich 0 sein darf + if j == pin: + ok = 1 + if Bit_pruefer == False and j != pin: + #print("Gefunden: " + str(pin) + " <> " + str(j)); + if Bit_observer == False: + #print("Dies wurde vom Observer bestätigt!"); + data.append([[pin ,j],0]) + else: + #print("Konnte NICHT bestätigen!"); + data.append([[pin ,j],1]) + if Bit_pruefer == False and Bit_observer == True: + data.append([[pin ,j],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if Bit_pruefer == True and Bit_observer == False: + data.append([[pin ,j],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + + + if ok != 1: + #print("Fehler beim Aufruf von Pin " + str(pin) + "! Es konnte nicht bestätigt werden, dass dieser Pin tatsächlich LOW ist!"); + data.append([[pin ,"???"],4]) + + logs["errorcode"] = 10 + +if __name__ == "__main__": + try: + main() + except: + logs["errorcode"] = 13 + array["logs"] = logs + array["data"] = data + json_output = json.dumps(array) + print(json_output) + + + + diff --git a/python/plug-check.py b/python/plug-check.py new file mode 100644 index 0000000..453ab60 --- /dev/null +++ b/python/plug-check.py @@ -0,0 +1,165 @@ +import smbus +import time +import json + +# Adressen der beiden MCP23017 Chips +AusgangRechts = 0x20 # Ausgang rechte Hälfte +AusgangLinks = 0x21 # Ausgang linke Hälfte +EingangRechts = 0x24 # Eingang rechte Hälfte +EingangLinks = 0x23 # Eingang linke Hälfte + +# Register-Adressen für den MCP23017 +IODIRA = 0x00 # Eingang / Ausgang A +IODIRB = 0x01 # Eingang / Ausgang B +GPIOA = 0x12 # GPIO A +GPIOB = 0x13 # GPIO B + +# Initialisiere den I2C Bus +bus = smbus.SMBus(1) + +RechtsA_ausgang = ["Z", "AA", "W", "AB", "AC", "AD", "AE", "AF"] +RechtsB_ausgang = ["Q", "U", "V", "T", "S", "R", "Y", "X"] + +RechtsA_eingang = ["R", "T", "V", "X", "Z", "AB", "AD", "AE"] +RechtsB_eingang = ["Q", "S", "W", "U", "AA", "Y", "AC", "AF"] + +LinksA_ausgang = ["C", "D", "E", "J", "F", "G", "H", "I"] +LinksB_ausgang = ["A", "B", "P", "O", "K", "L", "M", "N"] + +LinksA_eingang = ["B", "D", "F", "H", "J", "K", "M", "I"] +LinksB_eingang = ["A", "C", "E", "G", "L", "N", "O", "P"] + + +array = [] + +def default(): + adressen = [AusgangRechts, AusgangLinks, EingangRechts, EingangLinks] + + # Schleife für alle Registeradressen (0x00 bis 0x15) + for register in range(0x00, 0x16): + for adresse in adressen: + time.sleep(0.05) + print("Setze " + str(adresse) + " in Register " + str(register) + " auf Wert 0x00") + bus.write_byte_data(adresse, register, 0x00) + +# Konfiguriere alle Pins auf Chip 1 als Ausgang (A und B) +def configure_chip1_as_output(): + bus.write_byte_data(AusgangRechts, IODIRA, 0x00) # Setze alle Pins von A als Ausgang + bus.write_byte_data(AusgangRechts, IODIRB, 0x00) # Setze alle Pins von B als Ausgang + + bus.write_byte_data(AusgangLinks, IODIRA, 0x00) # Setze alle Pins von A als Ausgang + bus.write_byte_data(AusgangLinks, IODIRB, 0x00) # Setze alle Pins von B als Ausgang + +# Konfiguriere alle Pins auf Chip 2 als Eingang (A und B) +def configure_chip2_as_input(): + bus.write_byte_data(EingangRechts, IODIRA, 0xFF) # Setze alle Pins von A als Eingang + bus.write_byte_data(EingangRechts, IODIRB, 0xFF) # Setze alle Pins von B als Eingang + + bus.write_byte_data(EingangLinks, IODIRA, 0xFF) # Setze alle Pins von A als Eingang + bus.write_byte_data(EingangLinks, IODIRB, 0xFF) # Setze alle Pins von B als Eingang + + +# Hauptprogramm +def main(): + default() + time.sleep(0.05) + configure_chip1_as_output() + time.sleep(0.05) + configure_chip2_as_input() + + # Teste alle Pins auf Chip 1 (A0-A7, B0-B7) + for pin in range(32): # 0 bis 31 + #print(f"Setze Pin {pin} auf HIGH auf Chip 1") + + + + print("setze AusgangRechts GPIOA") + bus.write_byte_data(AusgangRechts, GPIOA, 0x00) + print("setze AusgangRechts GPIOB") + bus.write_byte_data(AusgangRechts, GPIOB, 0x00) + + print("setze AusgangLinks GPIOA") + bus.write_byte_data(AusgangLinks, GPIOA, 0x00) + print("setze AusgangLinks GPIOB") + bus.write_byte_data(AusgangLinks, GPIOB, 0x00) + + + if pin < 16: + # Setze den gewählten Pin auf HIGH + if pin < 8: # Pins A0-A7 + print("setze RechtsA_ausgang" + RechtsA_ausgang[pin]) + bus.write_byte_data(AusgangRechts, GPIOA, 1 << pin) + + aktuellAn = RechtsA_ausgang[pin] + else: # Pins B0-B7 + print("setze RechtsB_ausgang" + RechtsB_ausgang[pin-8]) + bus.write_byte_data(AusgangRechts, GPIOB, 1 << (pin - 8)) + aktuellAn = RechtsB_ausgang[pin - 8] + else: + # Setze den gewählten Pin auf HIGH + if pin < 24: # Pins A0-A7 + print("setze LinksA_ausgang" + LinksA_ausgang[pin-16]) + bus.write_byte_data(AusgangLinks, GPIOA, 1 << pin - 16) + aktuellAn = LinksA_ausgang[pin - 16] + else: # Pins B0-B7 + print("setze LinksB_ausgang" + LinksB_ausgang[pin-24]) + bus.write_byte_data(AusgangLinks, GPIOB, 1 << (pin - 24)) + aktuellAn = LinksB_ausgang[pin - 24] + + print("====================" + aktuellAn + "==========================") + + time.sleep(0.2) # Kurze Pause, damit die Änderung sichtbar wird + + print("Lese Wert_Rechts_A") + Wert_Rechts_A = bus.read_byte_data(EingangRechts, GPIOA) + print(Wert_Rechts_A) + print("Lese Wert_Links_A") + Wert_Links_A = bus.read_byte_data(EingangLinks, GPIOA) + print(Wert_Links_A) + + print("Lese Wert_Rechts_B") + Wert_Rechts_B = bus.read_byte_data(EingangRechts, GPIOB) + print(Wert_Rechts_B) + print("Lese Wert_Links_B") + Wert_Links_B = bus.read_byte_data(EingangLinks, GPIOB) + print(Wert_Links_B) + + + + for j in range(8): # Lese 4*8=32 Pins + bitmaske = 1 << j # Erstelle eine Maske: 1, 2, 4, 8, 16, ... + + Bit_Rechts_A = bool(Wert_Rechts_A & bitmaske) # Isoliere das entsprechende Bit + Bit_Links_A = bool(Wert_Links_A & bitmaske) # Isoliere das entsprechende Bit + Bit_Rechts_B = bool(Wert_Rechts_B & bitmaske) # Isoliere das entsprechende Bit + Bit_Links_B = bool(Wert_Links_B & bitmaske) # Isoliere das entsprechende Bit + + if Bit_Rechts_A == True: + if aktuellAn != RechtsA_eingang[j]: + array.append([aktuellAn ,RechtsA_eingang[j]]) + print("Gefunden: " + RechtsA_eingang[j]) + + if Bit_Links_A == True: + if aktuellAn != LinksA_eingang[j]: + array.append([aktuellAn ,LinksA_eingang[j]]) + print("Gefunden: " + LinksA_eingang[j]) + + if Bit_Rechts_B == True: + if aktuellAn != RechtsB_eingang[j]: + array.append([aktuellAn ,RechtsB_eingang[j]]) + print("Gefunden: " + RechtsB_eingang[j]) + + if Bit_Links_B == True: + if aktuellAn != LinksB_eingang[j]: + array.append([aktuellAn ,LinksB_eingang[j]]) + print("Gefunden: " + LinksB_eingang[j]) + + + + + json_output = json.dumps(array) + print(json_output) + +if __name__ == "__main__": + main() + diff --git a/python/plug-check_external-pulldown.py b/python/plug-check_external-pulldown.py new file mode 100644 index 0000000..5afed45 --- /dev/null +++ b/python/plug-check_external-pulldown.py @@ -0,0 +1,75 @@ +import smbus +import time +import json + +# Adressen der beiden MCP23017 Chips +MP = 0x20 # Ausgang rechte Hälfte + + +# Register-Adressen für den MCP23017 +IODIRA = 0x00 # Eingang / Ausgang A +IODIRB = 0x01 # Eingang / Ausgang B +GPIOA = 0x12 # GPIO A +GPIOB = 0x13 # GPIO B + +# Initialisiere den I2C Bus +bus = smbus.SMBus(1) + + +def default(): + # Schleife für alle Registeradressen (0x00 bis 0x15) + for register in range(0x00, 0x16): + bus.write_byte_data(MP, register, 0x00) + +# Konfiguriere alle Pins auf Chip 1 als Ausgang (A und B) +def configure_chip1_as_output(): + bus.write_byte_data(MP, IODIRA, 0x00) # Setze alle Pins von A als Ausgang + bus.write_byte_data(MP, IODIRB, 0x00) # Setze alle Pins von B als Ausgang + + + +# Hauptprogramm +def main(): + default() + time.sleep(0.5) + configure_chip1_as_output() + + # Teste alle Pins auf Chip 1 (A0-A7, B0-B7) + for pin in range(32): # 0 bis 31 + #print(f"Setze Pin {pin} auf HIGH auf Chip 1") + + + bus.write_byte_data(MP, GPIOA, 0x00) + + + if pin < 16: + # Setze den gewählten Pin auf HIGH + if pin < 8: # Pins A0- + + bus.write_byte_data(MP, IODIRA, ~(1 << pin)) + + bus.write_byte_data(MP, GPIOA, 1 << pin) + + + print("====================" + str(pin) + "==========================") + + time.sleep(0.2) # Kurze Pause, damit die Änderung sichtbar wird + + wert = bus.read_byte_data(MP, GPIOA) + + + + for j in range(8): # Lese 4*8=32 Pins + bitmaske = 1 << j # Erstelle eine Maske: 1, 2, 4, 8, 16, ... + + wert_bitweise = bool(wert & bitmaske) # Isoliere das entsprechende Bit + + + if wert_bitweise == True: + print("Gefunden: " + str(j)) + + + +if __name__ == "__main__": + main() + diff --git a/python/plug-check_internal-pullup.py b/python/plug-check_internal-pullup.py new file mode 100644 index 0000000..08441c4 --- /dev/null +++ b/python/plug-check_internal-pullup.py @@ -0,0 +1,53 @@ +import smbus +import time +import json + +# Adressen der beiden MCP23017 Chips +MP = 0x20 # Ausgang rechte Hälfte + + +# Register-Adressen für den MCP23017 +IODIRA = 0x00 # Eingang / Ausgang A +IODIRB = 0x01 # Eingang / Ausgang B +GPIOA = 0x12 # GPIO A +GPIOB = 0x13 # GPIO B + +PULLUPA = 0x0C +PULLUPB = 0x0D + +# Initialisiere den I2C Bus +bus = smbus.SMBus(1) + +# Hauptprogramm +def main(): + time.sleep(0.5) + + for pin in range(8): + + + bus.write_byte_data(MP, IODIRA, ~(1 << pin)) + time.sleep(0.02) + bus.write_byte_data(MP, PULLUPA, ~(1 << pin)) + time.sleep(0.02) + + bus.write_byte_data(MP, GPIOA, ~(1 << pin)) + time.sleep(0.02) + + + print("====================" + str(pin) + "==========================") + + wert = bus.read_byte_data(MP, GPIOA) + + for j in range(8): # Lese 4*8=32 Pins + bitmaske = 1 << j + + wert_bitweise = bool(wert & bitmaske) + + if wert_bitweise == False and j != pin: + print(str(j)) + + time.sleep(0.02) # Kurze Pause, damit die Änderung sichtbar wird + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/python/selfcheck.py b/python/selfcheck.py new file mode 100644 index 0000000..0c07a99 --- /dev/null +++ b/python/selfcheck.py @@ -0,0 +1,219 @@ +import smbus +import time +import json +import sys + +pruefer1 = 0x20 +pruefer2 = 0x21 +observer1 = 0x22 +observer2 = 0x23 + +adressen = [pruefer1, pruefer2, observer1, observer2] + +IODIRA = 0x00 # Eingang / Ausgang A +IODIRB = 0x01 # Eingang / Ausgang B +GPPUA = 0x0C # Eingang / Ausgang A +GPPUB = 0x0D # Eingang / Ausgang B +GPIOA = 0x12 # GPIO A +GPIOB = 0x13 # GPIO B + +array = {} +logs = {} +data = [] + + +try: + # Initialisiere den I2C Bus + bus = smbus.SMBus(1) +except: + logs["errorcode"] = 12 + array["logs"] = logs + json_output = json.dumps(array) + print(json_output) + sys.exit(0) + +def default(): + + #Schleife für alle Registeradressen (0x00 bis 0x15) + for register in range(0x00, 0x16): + for adresse in adressen: + #print("Setze " + str(adresse) + " in Register " + str(register) + " auf Wert 0x00") + bus.write_byte_data(adresse, register, 0x00) + + + #print("Hallo! Es wird damit begonnen, alle Register zurpckzusetzen...") + #Aktiviere alle Pullups und setze alle als Eingang + time.sleep(0.05) + for adresse in adressen: + bus.write_byte_data(adresse, IODIRA, 0xFF) + bus.write_byte_data(adresse, GPPUA, 0xFF) + bus.write_byte_data(adresse, IODIRB, 0xFF) + bus.write_byte_data(adresse, GPPUB, 0xFF) + + #print("============= Reset erfolgreich! =============") + +def main(): + try: + default() + except: + logs["errorcode"] = 11 + return + time.sleep(0.05) + for pin in range(32): # 0 bis 31 + aktuellAn = pin + + for adresse in adressen: + bus.write_byte_data(adresse, IODIRA, 0xFF) + bus.write_byte_data(adresse, GPPUA, 0xFF) + bus.write_byte_data(adresse, IODIRB, 0xFF) + bus.write_byte_data(adresse, GPPUB, 0xFF) + + time.sleep(0.05) + + if pin < 16: + # Setze den gewählten Pin auf HIGH + if pin < 8: # Pins A0-A7 + + wert = 0xFF & ~(1 << pin) # Alle Bits auf 1, außer das eine gewünschte auf 0 + + bus.write_byte_data(pruefer1, GPPUA, wert) + bus.write_byte_data(pruefer1, IODIRA, wert) + bus.write_byte_data(pruefer1, GPIOA, 0x00) + + else: # Pins B0-B7 + wert = 0xFF & ~(1 << pin - 8) # Alle Bits auf 1, außer das eine gewünschte auf 0 + + bus.write_byte_data(pruefer1, GPPUB, wert) + bus.write_byte_data(pruefer1, IODIRB, wert) + bus.write_byte_data(pruefer1, GPIOB, 0x00) + else: + # Setze den gewählten Pin auf HIGH + if pin < 24: # Pins A0-A7 + wert = 0xFF & ~(1 << pin - 16) # Alle Bits auf 1, außer das eine gewünschte auf 0 + + bus.write_byte_data(pruefer2, GPPUA, wert) + bus.write_byte_data(pruefer2, IODIRA, wert) + bus.write_byte_data(pruefer2, GPIOA, 0x00) + else: # Pins B0-B7 + wert = 0xFF & ~(1 << pin - 24) # Alle Bits auf 1, außer das eine gewünschte auf 0 + + bus.write_byte_data(pruefer2, GPPUB, wert) + bus.write_byte_data(pruefer2, IODIRB, wert) + bus.write_byte_data(pruefer2, GPIOB, 0x00) + + time.sleep(0.05) + + ##print(f"i2cget -y 1 0x20 0x12") + pruefer1_A_value = bus.read_byte_data(pruefer1, GPIOA) + pruefer1_B_value = bus.read_byte_data(pruefer1, GPIOB) + pruefer2_A_value = bus.read_byte_data(pruefer2, GPIOA) + pruefer2_B_value = bus.read_byte_data(pruefer2, GPIOB) + + observer1_A_value = bus.read_byte_data(observer1, GPIOA) + observer1_B_value = bus.read_byte_data(observer1, GPIOB) + observer2_A_value = bus.read_byte_data(observer2, GPIOA) + observer2_B_value = bus.read_byte_data(observer2, GPIOB) + + #print(f"------>>>>Es konnte vom Pruefer der Wert {format(Wert_pruefer, '08b')} festgetsellt werden") + #print(f"------>>>>Es konnte vom Observer der Wert {format(Wert_observer, '08b')} festgetsellt werden") + + + for j in range(8): # Verarbeite 4*8=32 Pins + Jeweils 32 Observer-Daten + bitmaske = 1 << j # Erstelle eine Maske: 1, 2, 4, 8, 16, ... + + pruefer1_A_value_bit = bool(pruefer1_A_value & bitmaske) + pruefer1_B_value_bit = bool(pruefer1_B_value & bitmaske) + pruefer2_A_value_bit = bool(pruefer2_A_value & bitmaske) + pruefer2_B_value_bit = bool(pruefer2_B_value & bitmaske) + + observer1_A_value_bit = bool(observer1_A_value & bitmaske) + observer1_B_value_bit = bool(observer1_B_value & bitmaske) + observer2_A_value_bit = bool(observer2_A_value & bitmaske) + observer2_B_value_bit = bool(observer2_B_value & bitmaske) + + + + i = j + + if pruefer1_A_value_bit == False: + #print("Gefunden: " + str(pin) + " <> " + str(j)); + if observer1_A_value_bit == False: + #print("Dies wurde vom Observer bestätigt!"); + data.append([[pin ,i],0]) + else: + #print("Konnte NICHT bestätigen!"); + data.append([[pin ,i],1]) + if pruefer1_A_value_bit == False and observer1_A_value_bit == True: + data.append([[pin ,i],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if pruefer1_A_value_bit == True and observer1_A_value_bit == False: + data.append([[pin ,i],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + + i = j + 8 + + if pruefer1_B_value_bit == False: + #print("Gefunden: " + str(pin) + " <> " + str(j)); + if observer1_B_value_bit == False: + #print("Dies wurde vom Observer bestätigt!"); + data.append([[pin ,i],0]) + else: + #print("Konnte NICHT bestätigen!"); + data.append([[pin ,i],1]) + if pruefer1_B_value_bit == False and observer1_B_value_bit == True: + data.append([[pin ,i],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if pruefer1_B_value_bit == True and observer1_B_value_bit == False: + data.append([[pin ,i],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + i = j + 16 + + if pruefer2_A_value_bit == False: + #print("Gefunden: " + str(pin) + " <> " + str(j)); + if observer2_A_value_bit == False: + #print("Dies wurde vom Observer bestätigt!"); + data.append([[pin ,i],0]) + else: + #print("Konnte NICHT bestätigen!"); + data.append([[pin ,i],1]) + if pruefer2_A_value_bit == False and observer2_A_value_bit == True: + data.append([[pin ,i],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if pruefer2_A_value_bit == True and observer2_A_value_bit == False: + data.append([[pin ,i],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + i = j + 24 + + if pruefer2_B_value_bit == False: + #print("Gefunden: " + str(pin) + " <> " + str(j)); + if observer2_B_value_bit == False: + #print("Dies wurde vom Observer bestätigt!"); + data.append([[pin ,i],0]) + else: + #print("Konnte NICHT bestätigen!"); + data.append([[pin ,i],1]) + if pruefer2_B_value_bit == False and observer2_B_value_bit == True: + data.append([[pin ,i],2]) + #print("Prüfer sagt NEIN, Observer sagt JA"); + if pruefer2_B_value_bit == True and observer2_B_value_bit == False: + data.append([[pin ,i],3]) + #print("Prüfer sagt JA, Observer sagt NEIN"); + + logs["errorcode"] = 10 + +if __name__ == "__main__": + try: + main() + except: + logs["errorcode"] = 13 + array["logs"] = logs + array["data"] = data + json_output = json.dumps(array) + print(json_output) + + + + diff --git a/python/test.py b/python/test.py new file mode 100644 index 0000000..08441c4 --- /dev/null +++ b/python/test.py @@ -0,0 +1,53 @@ +import smbus +import time +import json + +# Adressen der beiden MCP23017 Chips +MP = 0x20 # Ausgang rechte Hälfte + + +# Register-Adressen für den MCP23017 +IODIRA = 0x00 # Eingang / Ausgang A +IODIRB = 0x01 # Eingang / Ausgang B +GPIOA = 0x12 # GPIO A +GPIOB = 0x13 # GPIO B + +PULLUPA = 0x0C +PULLUPB = 0x0D + +# Initialisiere den I2C Bus +bus = smbus.SMBus(1) + +# Hauptprogramm +def main(): + time.sleep(0.5) + + for pin in range(8): + + + bus.write_byte_data(MP, IODIRA, ~(1 << pin)) + time.sleep(0.02) + bus.write_byte_data(MP, PULLUPA, ~(1 << pin)) + time.sleep(0.02) + + bus.write_byte_data(MP, GPIOA, ~(1 << pin)) + time.sleep(0.02) + + + print("====================" + str(pin) + "==========================") + + wert = bus.read_byte_data(MP, GPIOA) + + for j in range(8): # Lese 4*8=32 Pins + bitmaske = 1 << j + + wert_bitweise = bool(wert & bitmaske) + + if wert_bitweise == False and j != pin: + print(str(j)) + + time.sleep(0.02) # Kurze Pause, damit die Änderung sichtbar wird + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/reset.php b/reset.php new file mode 100644 index 0000000..436f64d --- /dev/null +++ b/reset.php @@ -0,0 +1,364 @@ + diff --git a/script.js b/script.js new file mode 100644 index 0000000..d0055b2 --- /dev/null +++ b/script.js @@ -0,0 +1,502 @@ +function distinctlyInput(input) { + // Wenn das aktuell gewählte Feld gefüllt wird + if ($('#distinctlyInput' + input).val().length >= 1) { + input++; // Fahre fort mit dem nächsten Feld + if ($('#distinctlyInput' + input).length) { + $('#distinctlyInput' + input).focus().select(); // Fokus auf das nächste Feld setzen + } + } + // Wenn das aktuell gewählte Feld geleert wird + else if ($('#distinctlyInput' + input).val().length === 0 && input > 1) { + input--; // Gehe zum vorherigen Feld + $('#distinctlyInput' + input).focus().select(); // Fokus auf das vorherige Feld setzen + } +} + + +function pageload(view, meta) { // Funktion zum Laden des Inhalts + + secondaryNavigation("close"); // Schließe das Stecker-Menü + page = window.location.hash.substr(1); // Hole die aufzurufende Seite über den URL-Hash (#xy) + + if(page.length === 0) { + + page = "index"; + + } + + var loadingTimer = setTimeout(function() { + $("#content").addClass("loading"); + message("Die Seite befindet sich im Aufbau..."); + }, 2000); // 2 Sekunden warten + + + $.ajax({ + url: "/pagecontent/" + page + ".php?view=" + view + "&meta=" + meta, // Pfad der zu ladenden Seite aufbauen + error: function(jqXHR, textStatus, errorThrown) { // Wenn ein Fehler bei der Anfrage auftritt (z.B. Netzwerkprobleme) + + message("Es ist ein unerwarteter Fehler aufgetreten. Der Seiteninhalt konnte nicht geladen werden!"); + + + }, + success: function(result){ // Abfrage erfolgreich + clearTimeout(loadingTimer); + $("#content").removeClass("loading"); + $("#content").html(result); + $("div.navigation>div").removeClass("active"); // Alle Buttons inaktiv + $("#" + page).addClass("active"); // Button mit Klasse "page" wird aktiv geschaltet (im Menü) + }}); +} + +function checkForReload(page) { + + page_hash = window.location.hash.substr(1); + + if(page_hash == page) { + pageload(); + } + +} + + + + + +function checkConnection(last) { // Verbindunsstatus überwachen // Last ist der letzte gemessene Netzwerkzustand + $.ajax({ + url: "/actions/connection-test.php?a=" + Date.now(), // verändere idr URL leicht, damit der Server nicht cached + timeout: 5000, // maximale Wartezeit sind 5 Sekunden; alles darüber wird als zu unzuverlässig angesehen + error: function(jqXHR, textStatus, errorThrown) { // Wenn ein Fehler bei der Anfrage auftritt (z.B. Netzwerkprobleme) + + $(".navigation-footer").html('
Keine Verbindung'); + console.error("Fehler bei der Anfrage:", textStatus, errorThrown); + + if (last != 1) { // letzter Zustand war "online" // Vermeiden von ständigen Nachrichten + message("Keine Verbindung"); // Werfe eine Nachricht + } + + setTimeout(function() { // nächste Prüfung in 5 Sekunden + checkConnection(1); // der Status offline wird mit übertragen + }, 5000); + }, + success: function(data) { // Verbindung iO + setTimeout(function() { // nächste Prüfung in 5 Sekunden + checkConnection(); + }, 5000); + + if (data == 'true') { // der Rückgabewert ist true, alles IO + $(".navigation-footer").html('
Verbunden'); + if (last == 1) { // war der letzte Status noch "offline", wird der Nutzer benachrichtigt + message("Verbindung wiederhergestellt!"); + } + } else { // außergewöhnlicher Fehler, aber möglich + message("Keine Verbindung"); // Verbindung dennoch unbrauchbar, wenn keine Daten geladen werden + $(".navigation-footer").html('
Keine Verbindung'); + } + } + }); +} + + + + +var pinRowNumber = 0; + + +function connectPins(startPoint, endPoint, origin) { + + + const encoded = $('#bridge-editor-img').attr("meta"); + const decoded = decodeURIComponent(encoded); + let myArray = JSON.parse(decoded); + + + console.log(myArray); + + length = myArray.length; + lengthNext = length + 1; + lengthPrev = length -1 ; + + + myArray[length] = []; + myArray[length][0] = startPoint; + myArray[length][1] = endPoint; + $(".pin-table").append('
' + startPoint + '' + endPoint + '
'); + + + + var myJsonStringURL = encodeURI(JSON.stringify(myArray)); + + + $('#bridge-editor-img').attr("meta", myJsonStringURL); + + + $('#bridge-editor-img').attr("style", "background-image: url('../stecker.php?data=" + myJsonStringURL + "')"); + + + $('#bridge-editor-edit').attr("value", pinRowNumber); + + + + +} + +function removePin(row) { + const encoded = $('#bridge-editor-img').attr("meta"); + const decoded = decodeURIComponent(encoded); + let myArray = JSON.parse(decoded); + + myArray[row] = []; + myArray[row][0] = ""; + myArray[row][1] = ""; + + $("#pinRow_" + row).hide(); + + var myJsonStringURL = encodeURI(JSON.stringify(myArray)); + + + $('#bridge-editor-img').attr("meta", myJsonStringURL); + + + $('#bridge-editor-img').attr("style", "background-image: url('../stecker.php?data=" + myJsonStringURL + "')"); + + + $('#bridge-editor-edit').attr("value", pinRowNumber); + + +} + + + +function programBridge(number) { // Checkboxen für die Auswahl der Brücken im Programm-Editor + + let myArray = []; // initialisiere Array + + index = 0; + + for (i = 0; i <= number; i++) { // iteriere durch alle Checkboxen + if ($('#programSelect_' + i).is(":checked")) { // wenn gesetzt + myArray[index] = $('#programSelect_' + i).val(); // Hänge die Brücken-ID dem Programm an + index++; + } + } + var myJsonString = JSON.stringify(myArray); // kreiere URL + + $('#program-editor-img').attr("src", "stecker.php?translate=true&data=" + myJsonString); // aktualisiere das Bild + console.log("OK"); + +} + +function excludeBridge(number) { // Checkboxen für die Auswahl der Brücken im Programm-Editor + + let myArray = []; // initialisiere Array + let myArraySubtrahend = []; // initialisiere Array + + index = 0; + indexSubtrahend = 0; + + for (i = 0; i < number; i++) { // iteriere durch alle Checkboxen + if ($('#programSelect_' + i).is(":checked")) { // wenn gesetzt + myArray[index] = JSON.parse($('#programSelect_' + i).val()); + index++; + } + else { + myArraySubtrahend[indexSubtrahend] = JSON.parse($('#programSelect_' + i).val()); + indexSubtrahend++; + } + } + var myJsonString = JSON.stringify(myArray); // kreiere URL + var myJsonStringSubtrahend = JSON.stringify(myArraySubtrahend); // kreiere URL + + + $('#result-editor-result').attr("src", "stecker.php?data=" + myJsonString); // aktualisiere das Bild + + + $('#database-bridges-hidden').attr("value", encodeURI(myJsonStringSubtrahend)); + + + console.log("OK"); + +} + + +function search(page,special) { // Datenbanksuche + + value = $('#database-search-term').val(); // Suchbegriff, wenn die Datenbank durchsucht wird + + if(page == 'results') { + if (special == "x") { // die Vorschläge werden ignoriert + value = ""; // Lösche Eingabe virtuell + } + } + + $.get("/pagecontent/database-search.php", { + meta: value, // Suchbegriff + view: page // je nachdem, von wo aus die Suche ausgelöst wurde + }, + function(data, status) { + if (status == "success") { + if(page == 'results') { // je nachdem, von wo aus die Suche ausgelöst wurde, anderen Container verändern + $("div.search-container div.results").html(data); + } + if(page == 'program') { + $("div.inventory-table").html(data); + } + + } + + }); + + +} + + +function save(page, action, meta) { // prinzipiell gleiche Funktion wie getcontent, jedoch mit Abfragen von JS-Werten + + suppress_page_reload = 0; + + if (page == "bridge-editor") { + + if (action == "edit" || action == "add") { + + number_entry = $('#bridge-editor-edit').val(); // von PHP fixierter Wert (Anzahl der zu Beginn vorhandenen Felder) + + let myArray = {}; // Initialisiere myArray als Objekt + + + const encoded = $('#bridge-editor-img').attr("meta"); + const decoded = decodeURIComponent(encoded); + myArray["pins"] = JSON.parse(decoded); + + if (action == "add") { // wird die Brücke neu angelegt, muss ein Name gewählt werden + myArray["name"] = prompt("Bitte noch einen Namen festlegen (nur Zahlen):"); + } + + + var myJsonString = JSON.stringify(myArray); // verpacke alle Pin-Brücken als JSON + + + meta = myJsonString; + + } + + } + + if (page == "program-editor") { + + if (action == "edit" || action == "add") { + + + + let myArray = {}; // Initialisiere als leeres, multidimensionales, indexiertes Array + myArray["bridges"] = []; // Initialisiere als leeres Array + + index = 0; + + for (i = 0; i <= meta; i++) { + if ($('#programSelect_' + i).is(":checked")) { + myArray["bridges"][index] = $('#programSelect_' + i).val(); + index++; + } + } + + + if (action == "add") { + myArray["programID"] = []; + for (i = 0; i <= 4; i++) { + j = i + 1; + myArray["programID"][i] = $('#distinctlyInput' + j).val().toUpperCase(); + } + } + + myArray["programDescription"] = $('#program-description').val(); + + + + console.log(myArray); + + var myJsonString = JSON.stringify(myArray); + console.log(myJsonString); // Zeigt die JSON-Ausgabe in der Konsole an + + meta = myJsonString; + + + + } + } + + if (page == "inventory" && action == "add") { + + + let myArray = {}; + myArray["name"] = $('#database-search-term').val(); + + myArray["bridges-hidden"] = $('#database-bridges-hidden').val(); + myArray["special"] = $('#database-special').val(); + + + myArray["programCache"] = []; + for (i = 0; i <= 4; i++) { + j = i + 1; + myArray["programCache"][i] = $('#distinctlyInput' + j).val().toUpperCase(); + } + + console.log(myArray); + + var myJsonString = JSON.stringify(myArray); + console.log(myJsonString); // Zeigt die JSON-Ausgabe in der Konsole an + + meta = myJsonString; + + suppress_page_reload = 1; + + + } + + console.log("/actions/" + page + ".php?action=" + action + "&meta=" + meta); + + $.get("/actions/" + page + ".php", { + action: action, + meta: meta + + }, + function(data, status) { + if (status == "success") { + data = JSON.parse(data); + if (data[0] == true) { + if (suppress_page_reload != 1) { + window.location.href = "#" + page; + pageload(); + } + + message(data[1]); + } else { + message(data[1]); + } + } + + }); + + + + +} + + +function secondaryNavigation(animation, content) { + + + + + if (animation == "open") { + + + $.get("/pagecontent/plug-select.php", { + + + }, + function(data, status) { + if (status == "success") { + if (data != 'false') { + $("#navigation-secondary").css("left", "250px"); + $(".navigation-header").attr("onclick", "secondaryNavigation('close','plug-select')"); + setTimeout(function() { + $("#navigation-secondary").html(data); + console.log(data); + }, 200); + } else { + window.location.href = "#"; + + alert("Die Inhalte konnten nicht abgerufen werden. Vergewissern Sie sich, dass Sie mit dem WLAN-Netzwerk verbunden sind und laden Sie bei Bedarf die Seite neu."); + } + } + + }); + + + + } else { + $("#navigation-secondary").css("left", "-250px"); + $(".navigation-header").attr("onclick", "secondaryNavigation('open','plug-select')"); + } + +} + +function changeSettings(type, value, meta) { + + $.get("/actions/save-settings.php", { + type: type, + value: value, + meta: meta + }, + function(data, status) { + if (status == "success") { + if (data == 'true') { + applySettings(type); + pageload(); + } else { + window.location.href = '#index'; + pageload(); + + alert("Die Inhalte konnten nicht abgerufen werden. Vergewissern Sie sich, dass Sie mit dem WLAN-Netzwerk verbunden sind und laden Sie bei Bedarf die Seite neu."); + } + } + + }); + +} + +function applySettings(type) { + + secondaryNavigation('close'); + + $.get("/actions/apply-settings.php", { + type: type + }, + function(data, status) { + if (status == "success") { + if (data != 'false') { + + if (type == "plug") { + + $(".navigation-header>div").html(data); + } + } else { + window.location.href = "#"; + + alert("Die Inhalte konnten nicht abgerufen werden. Vergewissern Sie sich, dass Sie mit dem WLAN-Netzwerk verbunden sind und laden Sie bei Bedarf die Seite neu."); + } + } + + }); + +} + + +function message(content) { + $("#message-box").html(content); + $("#message-box").css("display", "block"); + setTimeout(function() { + $("#message-box").css("transform", "translateY(0)"); + }, 50); + setTimeout(function() { + setTimeout(function() { + $("#message-box").css("display", "none"); + }, 500); + $("#message-box").css("transform", "translateY(calc(100% + 5px))"); + }, 3000); + +} + + + + +$(window).bind('beforeunload', function() { + $("#warning").css("display", "block"); + return 'Are you sure you want to leave?'; + +}); + + + + diff --git a/settings.json b/settings.json new file mode 100644 index 0000000..504e1d3 --- /dev/null +++ b/settings.json @@ -0,0 +1 @@ +{"plug":"0"} diff --git a/stecker.php b/stecker.php new file mode 100644 index 0000000..0dd4abe --- /dev/null +++ b/stecker.php @@ -0,0 +1,124 @@ +'; +?> + + + + + + + 30 + 10 + 31 + 11 + + + + + + + + + + + + + + + + + + + [70, 68], 20 => [70, 44], 30 => [70, 20], + 11 => [405, 68], 12 => [370, 68], 13 => [335, 68], 14 => [300, 68], + 15 => [265, 68], 16 => [230, 68], 17 => [195, 68], 18 => [160, 68], 19 => [125, 68], + 21 => [405, 44], 22 => [370, 44], 23 => [335, 44], 24 => [300, 44], + 25 => [265, 44], 26 => [220, 44], 27 => [185, 44], 28 => [150, 44], 29 => [115, 44], + 31 => [405, 20], 32 => [370, 20], 33 => [335, 20], 34 => [300, 20], + 35 => [265, 20], 36 => [230, 20], 37 => [195, 20], 38 => [160, 20], 39 => [125, 20], + ]; + + // Rechtecke einfärben, wenn "fill"-Modus aktiv ist + if ($_GET["mode"] == "fill") { + $data = $_GET["data"]; + $data = json_decode(urldecode($data), true); + + foreach ($pin_coordinates as $key => $coordinates) { + $fill = in_array($key, $data) ? "green" : "red"; + echo ''; + } + } else { + // Nur Umrisse + foreach ($pin_coordinates as $coordinates) { + echo ''; + } + } + + // SVG frühzeitig beenden, wenn kein Data vorhanden oder im fill-Modus + if (empty($_GET['data']) || $_GET["mode"] == "fill") { + print ""; + die(); + } + + // Brückenverbindungen übersetzen, falls aktiviert + if ($_GET["translate"] == "true") { + $db = new SQLite3('db-test/test.db'); + $data = json_decode($_GET["data"], true); + $plug = json_decode(file_get_contents("settings.json"), true)["plug"]; + $nodes_raw = []; + + foreach ($data as $bridge) { + $nodes = $db->query("SELECT node_from, node_to FROM nodes WHERE required_by = " . $bridge); + while ($node = $nodes->fetchArray(SQLITE3_ASSOC)) { + $nodes_raw[] = [$node['node_from'], $node['node_to']]; + } + } + $inputArray = $nodes_raw; + } else { + $inputArray = json_decode($_GET['data'], true); + } + + // Koordinaten übersetzen + $translatedArray = array_map(function ($pair) use ($pin_coordinates) { + return [ + $pin_coordinates[$pair[0]], + $pin_coordinates[$pair[1]], + ]; + }, $inputArray); + + $colors = ["#1a5fb4", "#26a269", "#e5a50a", "#c64600", "#a51d2d", "#613583", "#63452c", "#3d3846"]; + + // Linien ausgeben + $colorNumber = 0; + foreach ($translatedArray as $line) { + $point1 = $line[0]; + $point2 = $line[1]; + + $point1x = $point1[0] + 10; + $point1y = $point1[1] + 5; + $point2x = $point2[0] + 10; + $point2y = $point2[1] + 5; + + echo ''; + + $colorNumber = ($colorNumber + 1) % count($colors); + } + ?> + diff --git a/style3.css b/style3.css new file mode 100644 index 0000000..d5f428a --- /dev/null +++ b/style3.css @@ -0,0 +1,553 @@ +@font-face { + font-family: LexendDecaBold; + src: url(/vendor/fonts/LexendDeca-Bold.ttf); +} + @font-face { + font-family: LexendDecaMedium; + src: url(/vendor/fonts/LexendDeca-Medium.ttf); +} + body { + height: 100%; + width: 100%; + padding: 20px; + margin: 0; + box-sizing: border-box; + font-family: LexendDecaBold; + font-weight: light; + color: #2e3436; +} + div.main { + width: 100%; + max-width: 900px; + height: auto; + border-radius: 15px; + overflow: hidden; + margin: 0 auto; + border: 5px solid #c3c3c3; + position: relative; +} + div.navigation { + width: 250px; + height: 600px; + background-color: #ebebeb; + float: left; + display: inline-block; + position: relative; + z-index: 501; +} + div.navigation.secondary { + position: absolute; + left: -250px; + top: 0; + overflow-y: auto; + z-index: 500; + transition-duration: .6s; + width: 500px; + backdrop-filter: blur(10px); + background-color: #ebebeb8c; +} + div.navigation.secondary img { + width: 100px !important; +} + div.navigation>div, div.save-button { + padding: 5px; + margin: 10px; + line-height: 30px; + transition-duration: .4s; + cursor: pointer; + border-radius: 10px; +} + div.navigation>div:hover,div.navigation>div.active,div.navigation-footer,div.navigation-header,div.save-button:hover { + background-color: #d2d2d2; +} + div.navigation div.navigation-header { + text-align: center; +} + div.navigation div.navigation-header>div { + background-color: #fff; + border-radius: 0 0 7px 7px; + color: grey; + line-height: 20px; + font-size: 12px; +} + div.navigation>div>img:not(.infrago-logo),div.content div.save-button>img { + width: 30px; + max-height: 30px; + margin-right:20px; + vertical-align:middle; + filter: contrast(0.3); +} + div.navigation>div>img.infrago-logo { + width: 100%; + display:block; + margin-top: 15px; +} + div.content { + width: calc(100% - 250px); + float: right; + display: inline-block; + padding: 15px 20px; + height: 600px; + box-sizing: border-box; + overflow-y: auto; + text-align: center; +} +div.content>img.loader { + position: absolute; + height: 35%; + width: 35%; + left: 37.5%; + top: 37.5%; + display: none; +} + div#content { + transition-duration: .5s; + } + div#content.loading { + filter: blur(4px); + } + div#content.loading>img.loader { + display: block; + } + div.content div.content-header { + text-align: center; + line-height: 30px; + padding: 0; + margin-bottom: 20px; + display: block; + font-size: 2em; + margin-block-start: 0.67em; + margin-block-end: 0.67em; + margin-inline-start: 0px; + margin-inline-end: 0px; + font-weight: bold; + unicode-bidi: isolate; +} + div.content img { + width: 100%; + margin: 0 auto; + display: block; +} + div.navigation-footer { + position: absolute; + bottom:0; + width: calc(100% - 40px); + padding: 10px !important; + cursor: auto !important; +} + div.connection-indicator { + height: 15px; + width: 15px; + border-radius: 100%; + float: left; + vertical-align:middle; + margin: 7.5px; +} + div.connection-indicator.true { + background-color: #2ec27e; +} + div.connection-indicator.unknown { + background-color: #f6d32d; +} + div.connection-indicator.false { + background-color: #ed333b; +} + div.pin-table { + width: auto; + display: block; + margin: 0 auto; +} + div.pin-table img { + width: 30px; + transform: rotate(90deg); + padding-left: 17.5px; +} + div.pin-table div.row { + width: auto; + display: block; + height: 50px; +} + div.pin-table div.row * { + display: inline-block; +} + div.pin-table div.row input { + width: 100px; + border: 0; + background-color: #ebebeb; + padding: 15px; + font-family: inherit; + border-radius: 20px; + text-align: center; + height: 50px; +} + div.plug-table { + width: 100%; + margin-bottom: 50px; +} + div.row { + width: 100%; + height: 70px; + margin-bottom: 10px; +} + div.inventory-table>div.row { + height: 30px; + text-align: left; + display: block; + top: calc(50% - 27.5px); + position: relative; + width: auto; + background-color: #ebebeb; + margin: 10px; + padding: 5px; + border-radius: 25px; + height: auto; + line-height: 25px; +} + div.inventory-table>div.row div.plug-table { + margin-bottom: 0; +} + div.inventory-table>div.row div.plug-table div.label>span { + background-color: #d2d2d2; +} + div.inventory-table>div.row img,div.inventory-table>div.row img { + max-height: 100%; + width: auto; + display: inline-block; + filter: opacity(.5); + position: absolute; +} + div.inventory-table>div.row img.map,div.inventory-table>div.row img.down { + left: 10px; + top: 5px; + bottom: 5px; +} + div.inventory-table>div.row img.down { + left: unset; + cursor: pointer; + right: 10px; +} + div.inventory-table>div.row span { + margin-left: 4px; + line-height: 25px; + display: block; +} + div.label { + display: inline-block; + width: 20%; + margin: 0 auto; + text-align: center; + line-height: 100%; + float: left; + height: 70px; +} + div.label>span { + display: block; + top: calc(50% - 27.5px); + position:relative; + width: auto; + background-color: #ebebeb; + margin: 10px; + padding: 5px; + border-radius: 25px; + height: 25px; + line-height:25px; +} + div.label>span span { + padding: 2px; + background-color: #fff; + margin: 0px 3px; + border-radius: 100%; + display: block; + float: left; + width: 20px; + height: 20px; + line-height: 20px; + text-align: center; +} + img#bridge-editor-img { + display: inline-block; + width: 65%; + max-height:70px; +} + div.row div.options { + display: inline-block; + width: 15%; + height: 70px; + float: right; +} + div.row div.options img { + width: 40%; + padding: 3px; + box-sizing: border-box; + display: inline-block; + top: calc(50% - 25px); + position: relative; + filter: opacity(.5); + cursor: pointer; +} + div.action-menu { + position: absolute; + bottom: 10px; + right: 10px; + width: auto; + height: 50px; + z-index: 501; +} + div.action-menu img { + float: left; + width: 50px; + height: 50px; + background-color: #ffffff7a; + border-radius: 100%; + cursor: pointer; + filter: contrast(0.3); +} + div.distinctly-input { + display: flex; + justify-content: space-between; + gap: 10px; + width:70%; + margin: 0 auto; + margin-top: 20px; +} + .code-input, div.search-container input { + width: 50px; + height: 50px; + font-size: 24px; + text-align: center; + background-color: #ebebeb; + border-radius: 8px; + outline: none; + transition: border-color 0.3s ease; + border: none; +} + .code-input:focus { + border-color: #007bff; +} + textarea { + width: 100%; + max-width: 100%; + height: 200px; + max-height: 200px; + font-size: 14px; + text-align: center; + background-color: #ebebeb; + border-radius: 8px; + outline: none; + transition: border-color 0.3s ease; + border: none; +} + textarea:focus { + border-color: #007bff; +} + hr { + border: 3px solid #ebebeb; + margin: 20px 0; +} + div.search-container { + width: 100%; + height: 50px; + position: relative; +} + div.search-container input { + width: calc(100% - 60px); + border-radius: inherit 0px; + border-radius: 10px 0 0 10px; +} + div.search-container div.save-button { + width: 50px; + display: inline-block; + margin: 0; + height: 40px; + float: right; + text-align: center; + border-radius: 0 10px 10px 0 +} + div.search-container div.save-button img { + margin: 0; + margin-top: 4px; +} + div.search-container div.results { + position: absolute; + bottom: 50px; + left: 0; + right: 0; + background-color: #d2d2d2; + border-radius: 10px; +} + div.program-field { + padding: 20px; + margin-top: 20px; + border: 3px solid #ebebeb; + border-radius: 25px; +} + .checkbox-container { + position: relative; + display: inline-block; +} + .checkbox-container input[type="checkbox"] { + position: absolute; + opacity: 0; + pointer-events: none; +} + .checkbox-container .checkbox-text { + display: inline-block; + text-align: center; + cursor: pointer; + border-radius: 25px; + background-color: #ebebeb; + padding: 5px 15px; + border: 3px solid #ebebeb; +} + .checkbox-container input[type="checkbox"]:checked + .checkbox-text { + background-color: #d2d2d2; + border: 3px solid #2e3436; + color: #fff; +} + div.save-button img { + display: inline-block; +} + div.save-button { + background-color: #ebebeb +} + ::-webkit-scrollbar { + width: 5px; +} +/* Track */ + ::-webkit-scrollbar-track { + background: #ebebeb; +} +/* Handle */ + ::-webkit-scrollbar-thumb, ::-webkit-scrollbar-thumb:hover { + background: #d2d2d2; +} + div.toggle-switch { + width: 100%; + height: 50px; + margin-bottom: 10px; + display: flex; +} + div.toggle-switch div { + display: inline-block; +} + div.toggle-switch div:nth-child(1), div.toggle-switch div:nth-child(3) { + width: 39%; +} + div.toggle-switch div:nth-child(2) { + width: 20%; + padding: 10px; +} + .switch { + position: relative; + display: inline-block; + width: 100%; + height: 34px; +} + .switch input { + opacity: 0; + width: 0; + height: 0; +} + .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; + border-radius: 25px; +} + .slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; + border-radius: 100%; +} + input:checked + .slider:before { + -webkit-transform: translateX(86px); + -ms-transform: translateX(86px); + transform: translateX(86px); +} +/* Rounded sliders */ + .slider.round { + border-radius: 34px; +} + .slider.round:before { + border-radius: 50%; +} + table.simple-devider th,table.simple-devider td { + padding: 15px; +} + table.simple-devider * { + margin: 0; + vertical-align: top; +} + canvas { + cursor: crosshair; + background-repeat: no-repeat; +} + + @media only screen and (max-width: 1200px) { + div.main { + width: 100%; + height: 100%; + border: 0; + border-radius: 0; + max-width: unset; + } + body { + padding: 0; + padding-right: 0; + box-sizing: border-box; + } + div.content { + width: calc(100% - 250px); + height: 100%; + padding: 0; + } + div.navigation { + height: 100%; + overflow-y: scroll; + } + div.navigation-footer { + position: relative; + } + + } + + @media only screen and (max-width: 800px) { + + div.navigation { + width: 40px; + height: 100%; + } + div.navigation > div > span { + display: none; + } + div.navigation>div { + padding: 5px; + margin: 0; + } + + div.navigation.secondary { + left: -500px !important; + } + div.navigation-footer { + display: none; + } + div.content { + width: calc(100% - 40px); + height: 100%; + padding: 0; + } + +} diff --git a/temp.csv b/temp.csv new file mode 100644 index 0000000..fe181e8 --- /dev/null +++ b/temp.csv @@ -0,0 +1,8 @@ +"Gemessen am / um";"Stw-Name / Ort";"Erkannte Brücken (Pin-zu-Pin)";"(automatisch) ermittelte Programm-Nummer";Freitext-Besonderheiten +"24.07.2025 11:48:00";"Dudweiler Df";"20<->28, 30<->39, ";?????; +"24.07.2025 11:48:56";"Mannheim-Waldhof Wf (Mwf)";"20<->28, 30<->39, ";12345; +"24.07.2025 12:48:27";"Laupheim West Lf";"20<->28, 30<->39, ";S???1; +"24.07.2025 13:37:29";"Mannheim-Waldhof Wf (Mwf)";"20<->28, 30<->39, ";S???1; +"24.07.2025 14:28:33";"Dudweiler Df";"20<->28, 30<->39, ";S???1; +"28.07.2025 14:52:44";"Sinzig Sf";"19<->22, ";JAAAA; +"29.07.2025 05:57:38";"Mannheim-Waldhof Wf (Mwf)";"19<->22, ";JAAAA; diff --git a/temp.json b/temp.json new file mode 100644 index 0000000..ffba555 --- /dev/null +++ b/temp.json @@ -0,0 +1 @@ +{"plug":"0"} \ No newline at end of file diff --git a/temp.xlsx b/temp.xlsx new file mode 100644 index 0000000..00ae041 Binary files /dev/null and b/temp.xlsx differ diff --git a/vendor/DB_InfraGo_logo_red_black_100px_rgb.svg b/vendor/DB_InfraGo_logo_red_black_100px_rgb.svg new file mode 100644 index 0000000..484167f --- /dev/null +++ b/vendor/DB_InfraGo_logo_red_black_100px_rgb.svg @@ -0,0 +1,77 @@ + \ No newline at end of file diff --git a/vendor/composer/composer.json b/vendor/composer/composer.json new file mode 100644 index 0000000..4cc4a31 --- /dev/null +++ b/vendor/composer/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "phpoffice/phpspreadsheet": "^4.5" + } +} diff --git a/vendor/composer/composer.lock b/vendor/composer/composer.lock new file mode 100644 index 0000000..0889eef --- /dev/null +++ b/vendor/composer/composer.lock @@ -0,0 +1,600 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "218a60c9572277f09d6952f6a8761547", + "packages": [ + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, + { + "name": "maennchen/zipstream-php", + "version": "3.1.2", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-zlib": "*", + "php-64bit": "^8.2" + }, + "require-dev": { + "brianium/paratest": "^7.7", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.16", + "guzzlehttp/guzzle": "^7.5", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^11.0", + "vimeo/psalm": "^6.0" + }, + "suggest": { + "guzzlehttp/psr7": "^2.4", + "psr/http-message": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paul Duncan", + "email": "pabs@pablotron.org" + }, + { + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" + } + ], + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": [ + "stream", + "zip" + ], + "support": { + "issues": "https://github.com/maennchen/ZipStream-PHP/issues", + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.2" + }, + "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + } + ], + "time": "2025-01-27T12:07:53+00:00" + }, + { + "name": "markbaker/complex", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPComplex.git", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Complex\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@lange.demon.co.uk" + } + ], + "description": "PHP Class for working with complex numbers", + "homepage": "https://github.com/MarkBaker/PHPComplex", + "keywords": [ + "complex", + "mathematics" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPComplex/issues", + "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2" + }, + "time": "2022-12-06T16:21:08+00:00" + }, + { + "name": "markbaker/matrix", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPMatrix.git", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Matrix\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@demon-angel.eu" + } + ], + "description": "PHP Class for working with matrices", + "homepage": "https://github.com/MarkBaker/PHPMatrix", + "keywords": [ + "mathematics", + "matrix", + "vector" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPMatrix/issues", + "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1" + }, + "time": "2022-12-02T22:17:43+00:00" + }, + { + "name": "phpoffice/phpspreadsheet", + "version": "4.5.0", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "2ea9786632e6fac1aee601b6e426bcc723d8ce13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/2ea9786632e6fac1aee601b6e426bcc723d8ce13", + "reference": "2ea9786632e6fac1aee601b6e426bcc723d8ce13", + "shasum": "" + }, + "require": { + "composer/pcre": "^1||^2||^3", + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-gd": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "ext-zip": "*", + "ext-zlib": "*", + "maennchen/zipstream-php": "^2.1 || ^3.0", + "markbaker/complex": "^3.0", + "markbaker/matrix": "^3.0", + "php": "^8.1", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "dompdf/dompdf": "^2.0 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.2", + "mitoteam/jpgraph": "^10.3", + "mpdf/mpdf": "^8.1.1", + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.1 || ^2.0", + "phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0", + "phpstan/phpstan-phpunit": "^1.0 || ^2.0", + "phpunit/phpunit": "^10.5", + "squizlabs/php_codesniffer": "^3.7", + "tecnickcom/tcpdf": "^6.5" + }, + "suggest": { + "dompdf/dompdf": "Option for rendering PDF with PDF Writer", + "ext-intl": "PHP Internationalization Functions", + "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", + "mpdf/mpdf": "Option for rendering PDF with PDF Writer", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "https://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker", + "homepage": "https://markbakeruk.net" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net" + }, + { + "name": "Erik Tilt" + }, + { + "name": "Adrien Crivelli" + } + ], + "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", + "keywords": [ + "OpenXML", + "excel", + "gnumeric", + "ods", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "support": { + "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/4.5.0" + }, + "time": "2025-07-24T05:15:59+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/vendor/composer/composer.phar b/vendor/composer/composer.phar new file mode 100755 index 0000000..4f53d5a Binary files /dev/null and b/vendor/composer/composer.phar differ diff --git a/vendor/composer/vendor/autoload.php b/vendor/composer/vendor/autoload.php new file mode 100644 index 0000000..cb720d8 --- /dev/null +++ b/vendor/composer/vendor/autoload.php @@ -0,0 +1,22 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var string|null */ + private $vendorDir; + + // PSR-4 + /** + * @var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var list + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> + */ + private $prefixesPsr0 = array(); + /** + * @var list + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var array + */ + private $missingClasses = array(); + + /** @var string|null */ + private $apcuPrefix; + + /** + * @var array + */ + private static $registeredLoaders = array(); + + /** + * @param string|null $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return array> + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return list + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return list + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return array Array of classname => path + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + $includeFile = self::$includeFile; + $includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders keyed by their corresponding vendor directories. + * + * @return array + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } +} diff --git a/vendor/composer/vendor/composer/InstalledVersions.php b/vendor/composer/vendor/composer/InstalledVersions.php new file mode 100644 index 0000000..2052022 --- /dev/null +++ b/vendor/composer/vendor/composer/InstalledVersions.php @@ -0,0 +1,396 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to + * @internal + */ + private static $selfDir = null; + + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool + */ + private static $installedIsLocalDir; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; + } + + /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + $copiedLocalDir = false; + + if (self::$canGetVendors) { + $selfDir = self::getSelfDir(); + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; + } + } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; + } else { + self::$installed = array(); + } + } + + if (self::$installed !== array() && !$copiedLocalDir) { + $installed[] = self::$installed; + } + + return $installed; + } +} diff --git a/vendor/composer/vendor/composer/LICENSE b/vendor/composer/vendor/composer/LICENSE new file mode 100644 index 0000000..f27399a --- /dev/null +++ b/vendor/composer/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/composer/vendor/composer/autoload_classmap.php b/vendor/composer/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..0fb0a2c --- /dev/null +++ b/vendor/composer/vendor/composer/autoload_classmap.php @@ -0,0 +1,10 @@ + $vendorDir . '/composer/InstalledVersions.php', +); diff --git a/vendor/composer/vendor/composer/autoload_namespaces.php b/vendor/composer/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..15a2ff3 --- /dev/null +++ b/vendor/composer/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/maennchen/zipstream-php/src'), + 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), + 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), + 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'), + 'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'), + 'Matrix\\' => array($vendorDir . '/markbaker/matrix/classes/src'), + 'Composer\\Pcre\\' => array($vendorDir . '/composer/pcre/src'), + 'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'), +); diff --git a/vendor/composer/vendor/composer/autoload_real.php b/vendor/composer/vendor/composer/autoload_real.php new file mode 100644 index 0000000..1356510 --- /dev/null +++ b/vendor/composer/vendor/composer/autoload_real.php @@ -0,0 +1,38 @@ +register(true); + + return $loader; + } +} diff --git a/vendor/composer/vendor/composer/autoload_static.php b/vendor/composer/vendor/composer/autoload_static.php new file mode 100644 index 0000000..a5a71e3 --- /dev/null +++ b/vendor/composer/vendor/composer/autoload_static.php @@ -0,0 +1,81 @@ + + array ( + 'ZipStream\\' => 10, + ), + 'P' => + array ( + 'Psr\\SimpleCache\\' => 16, + 'Psr\\Http\\Message\\' => 17, + 'Psr\\Http\\Client\\' => 16, + 'PhpOffice\\PhpSpreadsheet\\' => 25, + ), + 'M' => + array ( + 'Matrix\\' => 7, + ), + 'C' => + array ( + 'Composer\\Pcre\\' => 14, + 'Complex\\' => 8, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'ZipStream\\' => + array ( + 0 => __DIR__ . '/..' . '/maennchen/zipstream-php/src', + ), + 'Psr\\SimpleCache\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/simple-cache/src', + ), + 'Psr\\Http\\Message\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/http-factory/src', + 1 => __DIR__ . '/..' . '/psr/http-message/src', + ), + 'Psr\\Http\\Client\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/http-client/src', + ), + 'PhpOffice\\PhpSpreadsheet\\' => + array ( + 0 => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet', + ), + 'Matrix\\' => + array ( + 0 => __DIR__ . '/..' . '/markbaker/matrix/classes/src', + ), + 'Composer\\Pcre\\' => + array ( + 0 => __DIR__ . '/..' . '/composer/pcre/src', + ), + 'Complex\\' => + array ( + 0 => __DIR__ . '/..' . '/markbaker/complex/classes/src', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit62e10e6c8df9afc192311b196c75bb33::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit62e10e6c8df9afc192311b196c75bb33::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit62e10e6c8df9afc192311b196c75bb33::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/vendor/composer/vendor/composer/installed.json b/vendor/composer/vendor/composer/installed.json new file mode 100644 index 0000000..c51d38e --- /dev/null +++ b/vendor/composer/vendor/composer/installed.json @@ -0,0 +1,614 @@ +{ + "packages": [ + { + "name": "composer/pcre", + "version": "3.3.2", + "version_normalized": "3.3.2.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "time": "2024-11-12T16:29:46+00:00", + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "install-path": "./pcre" + }, + { + "name": "maennchen/zipstream-php", + "version": "3.1.2", + "version_normalized": "3.1.2.0", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-zlib": "*", + "php-64bit": "^8.2" + }, + "require-dev": { + "brianium/paratest": "^7.7", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.16", + "guzzlehttp/guzzle": "^7.5", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^11.0", + "vimeo/psalm": "^6.0" + }, + "suggest": { + "guzzlehttp/psr7": "^2.4", + "psr/http-message": "^2.0" + }, + "time": "2025-01-27T12:07:53+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paul Duncan", + "email": "pabs@pablotron.org" + }, + { + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" + } + ], + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": [ + "stream", + "zip" + ], + "support": { + "issues": "https://github.com/maennchen/ZipStream-PHP/issues", + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.2" + }, + "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + } + ], + "install-path": "../maennchen/zipstream-php" + }, + { + "name": "markbaker/complex", + "version": "3.0.2", + "version_normalized": "3.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPComplex.git", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "time": "2022-12-06T16:21:08+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Complex\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@lange.demon.co.uk" + } + ], + "description": "PHP Class for working with complex numbers", + "homepage": "https://github.com/MarkBaker/PHPComplex", + "keywords": [ + "complex", + "mathematics" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPComplex/issues", + "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2" + }, + "install-path": "../markbaker/complex" + }, + { + "name": "markbaker/matrix", + "version": "3.0.1", + "version_normalized": "3.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPMatrix.git", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "time": "2022-12-02T22:17:43+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Matrix\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@demon-angel.eu" + } + ], + "description": "PHP Class for working with matrices", + "homepage": "https://github.com/MarkBaker/PHPMatrix", + "keywords": [ + "mathematics", + "matrix", + "vector" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPMatrix/issues", + "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1" + }, + "install-path": "../markbaker/matrix" + }, + { + "name": "phpoffice/phpspreadsheet", + "version": "4.5.0", + "version_normalized": "4.5.0.0", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "2ea9786632e6fac1aee601b6e426bcc723d8ce13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/2ea9786632e6fac1aee601b6e426bcc723d8ce13", + "reference": "2ea9786632e6fac1aee601b6e426bcc723d8ce13", + "shasum": "" + }, + "require": { + "composer/pcre": "^1||^2||^3", + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-gd": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "ext-zip": "*", + "ext-zlib": "*", + "maennchen/zipstream-php": "^2.1 || ^3.0", + "markbaker/complex": "^3.0", + "markbaker/matrix": "^3.0", + "php": "^8.1", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "dompdf/dompdf": "^2.0 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.2", + "mitoteam/jpgraph": "^10.3", + "mpdf/mpdf": "^8.1.1", + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.1 || ^2.0", + "phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0", + "phpstan/phpstan-phpunit": "^1.0 || ^2.0", + "phpunit/phpunit": "^10.5", + "squizlabs/php_codesniffer": "^3.7", + "tecnickcom/tcpdf": "^6.5" + }, + "suggest": { + "dompdf/dompdf": "Option for rendering PDF with PDF Writer", + "ext-intl": "PHP Internationalization Functions", + "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", + "mpdf/mpdf": "Option for rendering PDF with PDF Writer", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" + }, + "time": "2025-07-24T05:15:59+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "https://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker", + "homepage": "https://markbakeruk.net" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net" + }, + { + "name": "Erik Tilt" + }, + { + "name": "Adrien Crivelli" + } + ], + "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", + "keywords": [ + "OpenXML", + "excel", + "gnumeric", + "ods", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "support": { + "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/4.5.0" + }, + "install-path": "../phpoffice/phpspreadsheet" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "version_normalized": "1.0.3.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "time": "2023-09-23T14:17:50+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "install-path": "../psr/http-client" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "time": "2024-04-15T12:06:14+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "install-path": "../psr/http-factory" + }, + { + "name": "psr/http-message", + "version": "2.0", + "version_normalized": "2.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "time": "2023-04-04T09:54:51+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "install-path": "../psr/http-message" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "version_normalized": "3.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "time": "2021-10-29T13:26:27+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "install-path": "../psr/simple-cache" + } + ], + "dev": true, + "dev-package-names": [] +} diff --git a/vendor/composer/vendor/composer/installed.php b/vendor/composer/vendor/composer/installed.php new file mode 100644 index 0000000..b99e817 --- /dev/null +++ b/vendor/composer/vendor/composer/installed.php @@ -0,0 +1,104 @@ + array( + 'name' => '__root__', + 'pretty_version' => '1.0.0+no-version-set', + 'version' => '1.0.0.0', + 'reference' => null, + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => true, + ), + 'versions' => array( + '__root__' => array( + 'pretty_version' => '1.0.0+no-version-set', + 'version' => '1.0.0.0', + 'reference' => null, + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'composer/pcre' => array( + 'pretty_version' => '3.3.2', + 'version' => '3.3.2.0', + 'reference' => 'b2bed4734f0cc156ee1fe9c0da2550420d99a21e', + 'type' => 'library', + 'install_path' => __DIR__ . '/./pcre', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'maennchen/zipstream-php' => array( + 'pretty_version' => '3.1.2', + 'version' => '3.1.2.0', + 'reference' => 'aeadcf5c412332eb426c0f9b4485f6accba2a99f', + 'type' => 'library', + 'install_path' => __DIR__ . '/../maennchen/zipstream-php', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'markbaker/complex' => array( + 'pretty_version' => '3.0.2', + 'version' => '3.0.2.0', + 'reference' => '95c56caa1cf5c766ad6d65b6344b807c1e8405b9', + 'type' => 'library', + 'install_path' => __DIR__ . '/../markbaker/complex', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'markbaker/matrix' => array( + 'pretty_version' => '3.0.1', + 'version' => '3.0.1.0', + 'reference' => '728434227fe21be27ff6d86621a1b13107a2562c', + 'type' => 'library', + 'install_path' => __DIR__ . '/../markbaker/matrix', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'phpoffice/phpspreadsheet' => array( + 'pretty_version' => '4.5.0', + 'version' => '4.5.0.0', + 'reference' => '2ea9786632e6fac1aee601b6e426bcc723d8ce13', + 'type' => 'library', + 'install_path' => __DIR__ . '/../phpoffice/phpspreadsheet', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/http-client' => array( + 'pretty_version' => '1.0.3', + 'version' => '1.0.3.0', + 'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/http-client', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/http-factory' => array( + 'pretty_version' => '1.1.0', + 'version' => '1.1.0.0', + 'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/http-factory', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/http-message' => array( + 'pretty_version' => '2.0', + 'version' => '2.0.0.0', + 'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/http-message', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/simple-cache' => array( + 'pretty_version' => '3.0.0', + 'version' => '3.0.0.0', + 'reference' => '764e0b3939f5ca87cb904f570ef9be2d78a07865', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/simple-cache', + 'aliases' => array(), + 'dev_requirement' => false, + ), + ), +); diff --git a/vendor/composer/vendor/composer/pcre/LICENSE b/vendor/composer/vendor/composer/pcre/LICENSE new file mode 100644 index 0000000..c5a282f --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2021 Composer + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/composer/vendor/composer/pcre/README.md b/vendor/composer/vendor/composer/pcre/README.md new file mode 100644 index 0000000..4906514 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/README.md @@ -0,0 +1,189 @@ +composer/pcre +============= + +PCRE wrapping library that offers type-safe `preg_*` replacements. + +This library gives you a way to ensure `preg_*` functions do not fail silently, returning +unexpected `null`s that may not be handled. + +As of 3.0 this library enforces [`PREG_UNMATCHED_AS_NULL`](#preg_unmatched_as_null) usage +for all matching and replaceCallback functions, [read more below](#preg_unmatched_as_null) +to understand the implications. + +It thus makes it easier to work with static analysis tools like PHPStan or Psalm as it +simplifies and reduces the possible return values from all the `preg_*` functions which +are quite packed with edge cases. As of v2.2.0 / v3.2.0 the library also comes with a +[PHPStan extension](#phpstan-extension) for parsing regular expressions and giving you even better output types. + +This library is a thin wrapper around `preg_*` functions with [some limitations](#restrictions--limitations). +If you are looking for a richer API to handle regular expressions have a look at +[rawr/t-regx](https://packagist.org/packages/rawr/t-regx) instead. + +[![Continuous Integration](https://github.com/composer/pcre/workflows/Continuous%20Integration/badge.svg?branch=main)](https://github.com/composer/pcre/actions) + + +Installation +------------ + +Install the latest version with: + +```bash +$ composer require composer/pcre +``` + + +Requirements +------------ + +* PHP 7.4.0 is required for 3.x versions +* PHP 7.2.0 is required for 2.x versions +* PHP 5.3.2 is required for 1.x versions + + +Basic usage +----------- + +Instead of: + +```php +if (preg_match('{fo+}', $string, $matches)) { ... } +if (preg_match('{fo+}', $string, $matches, PREG_OFFSET_CAPTURE)) { ... } +if (preg_match_all('{fo+}', $string, $matches)) { ... } +$newString = preg_replace('{fo+}', 'bar', $string); +$newString = preg_replace_callback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string); +$newString = preg_replace_callback_array(['{fo+}' => fn ($match) => strtoupper($match[0])], $string); +$filtered = preg_grep('{[a-z]}', $elements); +$array = preg_split('{[a-z]+}', $string); +``` + +You can now call these on the `Preg` class: + +```php +use Composer\Pcre\Preg; + +if (Preg::match('{fo+}', $string, $matches)) { ... } +if (Preg::matchWithOffsets('{fo+}', $string, $matches)) { ... } +if (Preg::matchAll('{fo+}', $string, $matches)) { ... } +$newString = Preg::replace('{fo+}', 'bar', $string); +$newString = Preg::replaceCallback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string); +$newString = Preg::replaceCallbackArray(['{fo+}' => fn ($match) => strtoupper($match[0])], $string); +$filtered = Preg::grep('{[a-z]}', $elements); +$array = Preg::split('{[a-z]+}', $string); +``` + +The main difference is if anything fails to match/replace/.., it will throw a `Composer\Pcre\PcreException` +instead of returning `null` (or false in some cases), so you can now use the return values safely relying on +the fact that they can only be strings (for replace), ints (for match) or arrays (for grep/split). + +Additionally the `Preg` class provides match methods that return `bool` rather than `int`, for stricter type safety +when the number of pattern matches is not useful: + +```php +use Composer\Pcre\Preg; + +if (Preg::isMatch('{fo+}', $string, $matches)) // bool +if (Preg::isMatchAll('{fo+}', $string, $matches)) // bool +``` + +Finally the `Preg` class provides a few `*StrictGroups` method variants that ensure match groups +are always present and thus non-nullable, making it easier to write type-safe code: + +```php +use Composer\Pcre\Preg; + +// $matches is guaranteed to be an array of strings, if a subpattern does not match and produces a null it will throw +if (Preg::matchStrictGroups('{fo+}', $string, $matches)) +if (Preg::matchAllStrictGroups('{fo+}', $string, $matches)) +``` + +**Note:** This is generally safe to use as long as you do not have optional subpatterns (i.e. `(something)?` +or `(something)*` or branches with a `|` that result in some groups not being matched at all). +A subpattern that can match an empty string like `(.*)` is **not** optional, it will be present as an +empty string in the matches. A non-matching subpattern, even if optional like `(?:foo)?` will anyway not be present in +matches so it is also not a problem to use these with `*StrictGroups` methods. + +If you would prefer a slightly more verbose usage, replacing by-ref arguments by result objects, you can use the `Regex` class: + +```php +use Composer\Pcre\Regex; + +// this is useful when you are just interested in knowing if something matched +// as it returns a bool instead of int(1/0) for match +$bool = Regex::isMatch('{fo+}', $string); + +$result = Regex::match('{fo+}', $string); +if ($result->matched) { something($result->matches); } + +$result = Regex::matchWithOffsets('{fo+}', $string); +if ($result->matched) { something($result->matches); } + +$result = Regex::matchAll('{fo+}', $string); +if ($result->matched && $result->count > 3) { something($result->matches); } + +$newString = Regex::replace('{fo+}', 'bar', $string)->result; +$newString = Regex::replaceCallback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string)->result; +$newString = Regex::replaceCallbackArray(['{fo+}' => fn ($match) => strtoupper($match[0])], $string)->result; +``` + +Note that `preg_grep` and `preg_split` are only callable via the `Preg` class as they do not have +complex return types warranting a specific result object. + +See the [MatchResult](src/MatchResult.php), [MatchWithOffsetsResult](src/MatchWithOffsetsResult.php), [MatchAllResult](src/MatchAllResult.php), +[MatchAllWithOffsetsResult](src/MatchAllWithOffsetsResult.php), and [ReplaceResult](src/ReplaceResult.php) class sources for more details. + +Restrictions / Limitations +-------------------------- + +Due to type safety requirements a few restrictions are in place. + +- matching using `PREG_OFFSET_CAPTURE` is made available via `matchWithOffsets` and `matchAllWithOffsets`. + You cannot pass the flag to `match`/`matchAll`. +- `Preg::split` will also reject `PREG_SPLIT_OFFSET_CAPTURE` and you should use `splitWithOffsets` + instead. +- `matchAll` rejects `PREG_SET_ORDER` as it also changes the shape of the returned matches. There + is no alternative provided as you can fairly easily code around it. +- `preg_filter` is not supported as it has a rather crazy API, most likely you should rather + use `Preg::grep` in combination with some loop and `Preg::replace`. +- `replace`, `replaceCallback` and `replaceCallbackArray` do not support an array `$subject`, + only simple strings. +- As of 2.0, the library always uses `PREG_UNMATCHED_AS_NULL` for matching, which offers [much + saner/more predictable results](#preg_unmatched_as_null). As of 3.0 the flag is also set for + `replaceCallback` and `replaceCallbackArray`. + +#### PREG_UNMATCHED_AS_NULL + +As of 2.0, this library always uses PREG_UNMATCHED_AS_NULL for all `match*` and `isMatch*` +functions. As of 3.0 it is also done for `replaceCallback` and `replaceCallbackArray`. + +This means your matches will always contain all matching groups, either as null if unmatched +or as string if it matched. + +The advantages in clarity and predictability are clearer if you compare the two outputs of +running this with and without PREG_UNMATCHED_AS_NULL in $flags: + +```php +preg_match('/(a)(b)*(c)(d)*/', 'ac', $matches, $flags); +``` + +| no flag | PREG_UNMATCHED_AS_NULL | +| --- | --- | +| array (size=4) | array (size=5) | +| 0 => string 'ac' (length=2) | 0 => string 'ac' (length=2) | +| 1 => string 'a' (length=1) | 1 => string 'a' (length=1) | +| 2 => string '' (length=0) | 2 => null | +| 3 => string 'c' (length=1) | 3 => string 'c' (length=1) | +| | 4 => null | +| group 2 (any unmatched group preceding one that matched) is set to `''`. You cannot tell if it matched an empty string or did not match at all | group 2 is `null` when unmatched and a string if it matched, easy to check for | +| group 4 (any optional group without a matching one following) is missing altogether. So you have to check with `isset()`, but really you want `isset($m[4]) && $m[4] !== ''` for safety unless you are very careful to check that a non-optional group follows it | group 4 is always set, and null in this case as there was no match, easy to check for with `$m[4] !== null` | + +PHPStan Extension +----------------- + +To use the PHPStan extension if you do not use `phpstan/extension-installer` you can include `vendor/composer/pcre/extension.neon` in your PHPStan config. + +The extension provides much better type information for $matches as well as regex validation where possible. + +License +------- + +composer/pcre is licensed under the MIT License, see the LICENSE file for details. diff --git a/vendor/composer/vendor/composer/pcre/composer.json b/vendor/composer/vendor/composer/pcre/composer.json new file mode 100644 index 0000000..d3a7e67 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/composer.json @@ -0,0 +1,54 @@ +{ + "name": "composer/pcre", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "type": "library", + "license": "MIT", + "keywords": [ + "pcre", + "regex", + "preg", + "regular expression" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8 || ^9", + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Composer\\Pcre\\": "tests" + } + }, + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "scripts": { + "test": "@php vendor/bin/phpunit", + "phpstan": "@php phpstan analyse" + } +} diff --git a/vendor/composer/vendor/composer/pcre/extension.neon b/vendor/composer/vendor/composer/pcre/extension.neon new file mode 100644 index 0000000..b9cea11 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/extension.neon @@ -0,0 +1,22 @@ +# composer/pcre PHPStan extensions +# +# These can be reused by third party packages by including 'vendor/composer/pcre/extension.neon' +# in your phpstan config + +services: + - + class: Composer\Pcre\PHPStan\PregMatchParameterOutTypeExtension + tags: + - phpstan.staticMethodParameterOutTypeExtension + - + class: Composer\Pcre\PHPStan\PregMatchTypeSpecifyingExtension + tags: + - phpstan.typeSpecifier.staticMethodTypeSpecifyingExtension + - + class: Composer\Pcre\PHPStan\PregReplaceCallbackClosureTypeExtension + tags: + - phpstan.staticMethodParameterClosureTypeExtension + +rules: + - Composer\Pcre\PHPStan\UnsafeStrictGroupsCallRule + - Composer\Pcre\PHPStan\InvalidRegexPatternRule diff --git a/vendor/composer/vendor/composer/pcre/src/MatchAllResult.php b/vendor/composer/vendor/composer/pcre/src/MatchAllResult.php new file mode 100644 index 0000000..b22b52d --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/MatchAllResult.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchAllResult +{ + /** + * An array of match group => list of matched strings + * + * @readonly + * @var array> + */ + public $matches; + + /** + * @readonly + * @var 0|positive-int + */ + public $count; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array> $matches + */ + public function __construct(int $count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + $this->count = $count; + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/MatchAllStrictGroupsResult.php b/vendor/composer/vendor/composer/pcre/src/MatchAllStrictGroupsResult.php new file mode 100644 index 0000000..b7ec397 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/MatchAllStrictGroupsResult.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchAllStrictGroupsResult +{ + /** + * An array of match group => list of matched strings + * + * @readonly + * @var array> + */ + public $matches; + + /** + * @readonly + * @var 0|positive-int + */ + public $count; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array> $matches + */ + public function __construct(int $count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + $this->count = $count; + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/MatchAllWithOffsetsResult.php b/vendor/composer/vendor/composer/pcre/src/MatchAllWithOffsetsResult.php new file mode 100644 index 0000000..032a02c --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/MatchAllWithOffsetsResult.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchAllWithOffsetsResult +{ + /** + * An array of match group => list of matches, every match being a pair of string matched + offset in bytes (or -1 if no match) + * + * @readonly + * @var array> + * @phpstan-var array}>> + */ + public $matches; + + /** + * @readonly + * @var 0|positive-int + */ + public $count; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array> $matches + * @phpstan-param array}>> $matches + */ + public function __construct(int $count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + $this->count = $count; + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/MatchResult.php b/vendor/composer/vendor/composer/pcre/src/MatchResult.php new file mode 100644 index 0000000..e951a5e --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/MatchResult.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchResult +{ + /** + * An array of match group => string matched + * + * @readonly + * @var array + */ + public $matches; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array $matches + */ + public function __construct(int $count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/MatchStrictGroupsResult.php b/vendor/composer/vendor/composer/pcre/src/MatchStrictGroupsResult.php new file mode 100644 index 0000000..126ee62 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/MatchStrictGroupsResult.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchStrictGroupsResult +{ + /** + * An array of match group => string matched + * + * @readonly + * @var array + */ + public $matches; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array $matches + */ + public function __construct(int $count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/MatchWithOffsetsResult.php b/vendor/composer/vendor/composer/pcre/src/MatchWithOffsetsResult.php new file mode 100644 index 0000000..ba4d4bc --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/MatchWithOffsetsResult.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class MatchWithOffsetsResult +{ + /** + * An array of match group => pair of string matched + offset in bytes (or -1 if no match) + * + * @readonly + * @var array + * @phpstan-var array}> + */ + public $matches; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + * @param array $matches + * @phpstan-param array}> $matches + */ + public function __construct(int $count, array $matches) + { + $this->matches = $matches; + $this->matched = (bool) $count; + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/PHPStan/InvalidRegexPatternRule.php b/vendor/composer/vendor/composer/pcre/src/PHPStan/InvalidRegexPatternRule.php new file mode 100644 index 0000000..8a05fb2 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/PHPStan/InvalidRegexPatternRule.php @@ -0,0 +1,142 @@ + + */ +class InvalidRegexPatternRule implements Rule +{ + public function getNodeType(): string + { + return StaticCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $patterns = $this->extractPatterns($node, $scope); + + $errors = []; + foreach ($patterns as $pattern) { + $errorMessage = $this->validatePattern($pattern); + if ($errorMessage === null) { + continue; + } + + $errors[] = RuleErrorBuilder::message(sprintf('Regex pattern is invalid: %s', $errorMessage))->identifier('regexp.pattern')->build(); + } + + return $errors; + } + + /** + * @return string[] + */ + private function extractPatterns(StaticCall $node, Scope $scope): array + { + if (!$node->class instanceof FullyQualified) { + return []; + } + $isRegex = $node->class->toString() === Regex::class; + $isPreg = $node->class->toString() === Preg::class; + if (!$isRegex && !$isPreg) { + return []; + } + if (!$node->name instanceof Node\Identifier || !Preg::isMatch('{^(match|isMatch|grep|replace|split)}', $node->name->name)) { + return []; + } + + $functionName = $node->name->name; + if (!isset($node->getArgs()[0])) { + return []; + } + + $patternNode = $node->getArgs()[0]->value; + $patternType = $scope->getType($patternNode); + + $patternStrings = []; + + foreach ($patternType->getConstantStrings() as $constantStringType) { + if ($functionName === 'replaceCallbackArray') { + continue; + } + + $patternStrings[] = $constantStringType->getValue(); + } + + foreach ($patternType->getConstantArrays() as $constantArrayType) { + if ( + in_array($functionName, [ + 'replace', + 'replaceCallback', + ], true) + ) { + foreach ($constantArrayType->getValueTypes() as $arrayKeyType) { + foreach ($arrayKeyType->getConstantStrings() as $constantString) { + $patternStrings[] = $constantString->getValue(); + } + } + } + + if ($functionName !== 'replaceCallbackArray') { + continue; + } + + foreach ($constantArrayType->getKeyTypes() as $arrayKeyType) { + foreach ($arrayKeyType->getConstantStrings() as $constantString) { + $patternStrings[] = $constantString->getValue(); + } + } + } + + return $patternStrings; + } + + private function validatePattern(string $pattern): ?string + { + try { + $msg = null; + $prev = set_error_handler(function (int $severity, string $message, string $file) use (&$msg): bool { + $msg = preg_replace("#^preg_match(_all)?\\(.*?\\): #", '', $message); + + return true; + }); + + if ($pattern === '') { + return 'Empty string is not a valid regular expression'; + } + + Preg::match($pattern, ''); + if ($msg !== null) { + return $msg; + } + } catch (PcreException $e) { + if ($e->getCode() === PREG_INTERNAL_ERROR && $msg !== null) { + return $msg; + } + + return preg_replace('{.*? failed executing ".*": }', '', $e->getMessage()); + } finally { + restore_error_handler(); + } + + return null; + } + +} diff --git a/vendor/composer/vendor/composer/pcre/src/PHPStan/PregMatchFlags.php b/vendor/composer/vendor/composer/pcre/src/PHPStan/PregMatchFlags.php new file mode 100644 index 0000000..aa30ab3 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/PHPStan/PregMatchFlags.php @@ -0,0 +1,70 @@ +getType($flagsArg->value); + + $constantScalars = $flagsType->getConstantScalarValues(); + if ($constantScalars === []) { + return null; + } + + $internalFlagsTypes = []; + foreach ($flagsType->getConstantScalarValues() as $constantScalarValue) { + if (!is_int($constantScalarValue)) { + return null; + } + + $internalFlagsTypes[] = new ConstantIntegerType($constantScalarValue | PREG_UNMATCHED_AS_NULL); + } + return TypeCombinator::union(...$internalFlagsTypes); + } + + static public function removeNullFromMatches(Type $matchesType): Type + { + return TypeTraverser::map($matchesType, static function (Type $type, callable $traverse): Type { + if ($type instanceof UnionType || $type instanceof IntersectionType) { + return $traverse($type); + } + + if ($type instanceof ConstantArrayType) { + return new ConstantArrayType( + $type->getKeyTypes(), + array_map(static function (Type $valueType) use ($traverse): Type { + return $traverse($valueType); + }, $type->getValueTypes()), + $type->getNextAutoIndexes(), + [], + $type->isList() + ); + } + + if ($type instanceof ArrayType) { + return new ArrayType($type->getKeyType(), $traverse($type->getItemType())); + } + + return TypeCombinator::removeNull($type); + }); + } + +} diff --git a/vendor/composer/vendor/composer/pcre/src/PHPStan/PregMatchParameterOutTypeExtension.php b/vendor/composer/vendor/composer/pcre/src/PHPStan/PregMatchParameterOutTypeExtension.php new file mode 100644 index 0000000..e0d6020 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/PHPStan/PregMatchParameterOutTypeExtension.php @@ -0,0 +1,65 @@ +regexShapeMatcher = $regexShapeMatcher; + } + + public function isStaticMethodSupported(MethodReflection $methodReflection, ParameterReflection $parameter): bool + { + return + $methodReflection->getDeclaringClass()->getName() === Preg::class + && in_array($methodReflection->getName(), [ + 'match', 'isMatch', 'matchStrictGroups', 'isMatchStrictGroups', + 'matchAll', 'isMatchAll', 'matchAllStrictGroups', 'isMatchAllStrictGroups' + ], true) + && $parameter->getName() === 'matches'; + } + + public function getParameterOutTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, ParameterReflection $parameter, Scope $scope): ?Type + { + $args = $methodCall->getArgs(); + $patternArg = $args[0] ?? null; + $matchesArg = $args[2] ?? null; + $flagsArg = $args[3] ?? null; + + if ( + $patternArg === null || $matchesArg === null + ) { + return null; + } + + $flagsType = PregMatchFlags::getType($flagsArg, $scope); + if ($flagsType === null) { + return null; + } + + if (stripos($methodReflection->getName(), 'matchAll') !== false) { + return $this->regexShapeMatcher->matchAllExpr($patternArg->value, $flagsType, TrinaryLogic::createMaybe(), $scope); + } + + return $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createMaybe(), $scope); + } + +} diff --git a/vendor/composer/vendor/composer/pcre/src/PHPStan/PregMatchTypeSpecifyingExtension.php b/vendor/composer/vendor/composer/pcre/src/PHPStan/PregMatchTypeSpecifyingExtension.php new file mode 100644 index 0000000..3db0ce0 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/PHPStan/PregMatchTypeSpecifyingExtension.php @@ -0,0 +1,119 @@ +regexShapeMatcher = $regexShapeMatcher; + } + + public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void + { + $this->typeSpecifier = $typeSpecifier; + } + + public function getClass(): string + { + return Preg::class; + } + + public function isStaticMethodSupported(MethodReflection $methodReflection, StaticCall $node, TypeSpecifierContext $context): bool + { + return in_array($methodReflection->getName(), [ + 'match', 'isMatch', 'matchStrictGroups', 'isMatchStrictGroups', + 'matchAll', 'isMatchAll', 'matchAllStrictGroups', 'isMatchAllStrictGroups' + ], true) + && !$context->null(); + } + + public function specifyTypes(MethodReflection $methodReflection, StaticCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes + { + $args = $node->getArgs(); + $patternArg = $args[0] ?? null; + $matchesArg = $args[2] ?? null; + $flagsArg = $args[3] ?? null; + + if ( + $patternArg === null || $matchesArg === null + ) { + return new SpecifiedTypes(); + } + + $flagsType = PregMatchFlags::getType($flagsArg, $scope); + if ($flagsType === null) { + return new SpecifiedTypes(); + } + + if (stripos($methodReflection->getName(), 'matchAll') !== false) { + $matchedType = $this->regexShapeMatcher->matchAllExpr($patternArg->value, $flagsType, TrinaryLogic::createFromBoolean($context->true()), $scope); + } else { + $matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createFromBoolean($context->true()), $scope); + } + + if ($matchedType === null) { + return new SpecifiedTypes(); + } + + if ( + in_array($methodReflection->getName(), ['matchStrictGroups', 'isMatchStrictGroups', 'matchAllStrictGroups', 'isMatchAllStrictGroups'], true) + ) { + $matchedType = PregMatchFlags::removeNullFromMatches($matchedType); + } + + $overwrite = false; + if ($context->false()) { + $overwrite = true; + $context = $context->negate(); + } + + // @phpstan-ignore function.alreadyNarrowedType + if (method_exists('PHPStan\Analyser\SpecifiedTypes', 'setRootExpr')) { + $typeSpecifier = $this->typeSpecifier->create( + $matchesArg->value, + $matchedType, + $context, + $scope + )->setRootExpr($node); + + return $overwrite ? $typeSpecifier->setAlwaysOverwriteTypes() : $typeSpecifier; + } + + // @phpstan-ignore arguments.count + return $this->typeSpecifier->create( + $matchesArg->value, + $matchedType, + $context, + // @phpstan-ignore argument.type + $overwrite, + $scope, + $node + ); + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/PHPStan/PregReplaceCallbackClosureTypeExtension.php b/vendor/composer/vendor/composer/pcre/src/PHPStan/PregReplaceCallbackClosureTypeExtension.php new file mode 100644 index 0000000..7b95367 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/PHPStan/PregReplaceCallbackClosureTypeExtension.php @@ -0,0 +1,91 @@ +regexShapeMatcher = $regexShapeMatcher; + } + + public function isStaticMethodSupported(MethodReflection $methodReflection, ParameterReflection $parameter): bool + { + return in_array($methodReflection->getDeclaringClass()->getName(), [Preg::class, Regex::class], true) + && in_array($methodReflection->getName(), ['replaceCallback', 'replaceCallbackStrictGroups'], true) + && $parameter->getName() === 'replacement'; + } + + public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, ParameterReflection $parameter, Scope $scope): ?Type + { + $args = $methodCall->getArgs(); + $patternArg = $args[0] ?? null; + $flagsArg = $args[5] ?? null; + + if ( + $patternArg === null + ) { + return null; + } + + $flagsType = PregMatchFlags::getType($flagsArg, $scope); + + $matchesType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createYes(), $scope); + if ($matchesType === null) { + return null; + } + + if ($methodReflection->getName() === 'replaceCallbackStrictGroups' && count($matchesType->getConstantArrays()) === 1) { + $matchesType = $matchesType->getConstantArrays()[0]; + $matchesType = new ConstantArrayType( + $matchesType->getKeyTypes(), + array_map(static function (Type $valueType): Type { + if (count($valueType->getConstantArrays()) === 1) { + $valueTypeArray = $valueType->getConstantArrays()[0]; + return new ConstantArrayType( + $valueTypeArray->getKeyTypes(), + array_map(static function (Type $valueType): Type { + return TypeCombinator::removeNull($valueType); + }, $valueTypeArray->getValueTypes()), + $valueTypeArray->getNextAutoIndexes(), + [], + $valueTypeArray->isList() + ); + } + return TypeCombinator::removeNull($valueType); + }, $matchesType->getValueTypes()), + $matchesType->getNextAutoIndexes(), + [], + $matchesType->isList() + ); + } + + return new ClosureType( + [ + new NativeParameterReflection($parameter->getName(), $parameter->isOptional(), $matchesType, $parameter->passedByReference(), $parameter->isVariadic(), $parameter->getDefaultValue()), + ], + new StringType() + ); + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/PHPStan/UnsafeStrictGroupsCallRule.php b/vendor/composer/vendor/composer/pcre/src/PHPStan/UnsafeStrictGroupsCallRule.php new file mode 100644 index 0000000..5bced50 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/PHPStan/UnsafeStrictGroupsCallRule.php @@ -0,0 +1,112 @@ + + */ +final class UnsafeStrictGroupsCallRule implements Rule +{ + /** + * @var RegexArrayShapeMatcher + */ + private $regexShapeMatcher; + + public function __construct(RegexArrayShapeMatcher $regexShapeMatcher) + { + $this->regexShapeMatcher = $regexShapeMatcher; + } + + public function getNodeType(): string + { + return StaticCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->class instanceof FullyQualified) { + return []; + } + $isRegex = $node->class->toString() === Regex::class; + $isPreg = $node->class->toString() === Preg::class; + if (!$isRegex && !$isPreg) { + return []; + } + if (!$node->name instanceof Node\Identifier || !in_array($node->name->name, ['matchStrictGroups', 'isMatchStrictGroups', 'matchAllStrictGroups', 'isMatchAllStrictGroups'], true)) { + return []; + } + + $args = $node->getArgs(); + if (!isset($args[0])) { + return []; + } + + $patternArg = $args[0] ?? null; + if ($isPreg) { + if (!isset($args[2])) { // no matches set, skip as the matches won't be used anyway + return []; + } + $flagsArg = $args[3] ?? null; + } else { + $flagsArg = $args[2] ?? null; + } + + if ($patternArg === null) { + return []; + } + + $flagsType = PregMatchFlags::getType($flagsArg, $scope); + if ($flagsType === null) { + return []; + } + + $matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createYes(), $scope); + if ($matchedType === null) { + return [ + RuleErrorBuilder::message(sprintf('The %s call is potentially unsafe as $matches\' type could not be inferred.', $node->name->name)) + ->identifier('composerPcre.maybeUnsafeStrictGroups') + ->build(), + ]; + } + + if (count($matchedType->getConstantArrays()) === 1) { + $matchedType = $matchedType->getConstantArrays()[0]; + $nullableGroups = []; + foreach ($matchedType->getValueTypes() as $index => $type) { + if (TypeCombinator::containsNull($type)) { + $nullableGroups[] = $matchedType->getKeyTypes()[$index]->getValue(); + } + } + + if (\count($nullableGroups) > 0) { + return [ + RuleErrorBuilder::message(sprintf( + 'The %s call is unsafe as match group%s "%s" %s optional and may be null.', + $node->name->name, + \count($nullableGroups) > 1 ? 's' : '', + implode('", "', $nullableGroups), + \count($nullableGroups) > 1 ? 'are' : 'is' + ))->identifier('composerPcre.unsafeStrictGroups')->build(), + ]; + } + } + + return []; + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/PcreException.php b/vendor/composer/vendor/composer/pcre/src/PcreException.php new file mode 100644 index 0000000..23d9327 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/PcreException.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +class PcreException extends \RuntimeException +{ + /** + * @param string $function + * @param string|string[] $pattern + * @return self + */ + public static function fromFunction($function, $pattern) + { + $code = preg_last_error(); + + if (is_array($pattern)) { + $pattern = implode(', ', $pattern); + } + + return new PcreException($function.'(): failed executing "'.$pattern.'": '.self::pcreLastErrorMessage($code), $code); + } + + /** + * @param int $code + * @return string + */ + private static function pcreLastErrorMessage($code) + { + if (function_exists('preg_last_error_msg')) { + return preg_last_error_msg(); + } + + $constants = get_defined_constants(true); + if (!isset($constants['pcre']) || !is_array($constants['pcre'])) { + return 'UNDEFINED_ERROR'; + } + + foreach ($constants['pcre'] as $const => $val) { + if ($val === $code && substr($const, -6) === '_ERROR') { + return $const; + } + } + + return 'UNDEFINED_ERROR'; + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/Preg.php b/vendor/composer/vendor/composer/pcre/src/Preg.php new file mode 100644 index 0000000..400abbf --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/Preg.php @@ -0,0 +1,430 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +class Preg +{ + /** @internal */ + public const ARRAY_MSG = '$subject as an array is not supported. You can use \'foreach\' instead.'; + /** @internal */ + public const INVALID_TYPE_MSG = '$subject must be a string, %s given.'; + + /** + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @return 0|1 + * + * @param-out array $matches + */ + public static function match(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int + { + self::checkOffsetCapture($flags, 'matchWithOffsets'); + + $result = preg_match($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL, $offset); + if ($result === false) { + throw PcreException::fromFunction('preg_match', $pattern); + } + + return $result; + } + + /** + * Variant of `match()` which outputs non-null matches (or throws) + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @return 0|1 + * @throws UnexpectedNullMatchException + * + * @param-out array $matches + */ + public static function matchStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int + { + $result = self::match($pattern, $subject, $matchesInternal, $flags, $offset); + $matches = self::enforceNonNullMatches($pattern, $matchesInternal, 'match'); + + return $result; + } + + /** + * Runs preg_match with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL and PREG_OFFSET_CAPTURE are always set, no other flags are supported + * @return 0|1 + * + * @param-out array}> $matches + */ + public static function matchWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): int + { + $result = preg_match($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, $offset); + if ($result === false) { + throw PcreException::fromFunction('preg_match', $pattern); + } + + return $result; + } + + /** + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @return 0|positive-int + * + * @param-out array> $matches + */ + public static function matchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int + { + self::checkOffsetCapture($flags, 'matchAllWithOffsets'); + self::checkSetOrder($flags); + + $result = preg_match_all($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL, $offset); + if (!is_int($result)) { // PHP < 8 may return null, 8+ returns int|false + throw PcreException::fromFunction('preg_match_all', $pattern); + } + + return $result; + } + + /** + * Variant of `match()` which outputs non-null matches (or throws) + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @return 0|positive-int + * @throws UnexpectedNullMatchException + * + * @param-out array> $matches + */ + public static function matchAllStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int + { + $result = self::matchAll($pattern, $subject, $matchesInternal, $flags, $offset); + $matches = self::enforceNonNullMatchAll($pattern, $matchesInternal, 'matchAll'); + + return $result; + } + + /** + * Runs preg_match_all with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported + * @return 0|positive-int + * + * @param-out array}>> $matches + */ + public static function matchAllWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): int + { + self::checkSetOrder($flags); + + $result = preg_match_all($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, $offset); + if (!is_int($result)) { // PHP < 8 may return null, 8+ returns int|false + throw PcreException::fromFunction('preg_match_all', $pattern); + } + + return $result; + } + + /** + * @param string|string[] $pattern + * @param string|string[] $replacement + * @param string $subject + * @param int $count Set by method + * + * @param-out int<0, max> $count + */ + public static function replace($pattern, $replacement, $subject, int $limit = -1, ?int &$count = null): string + { + if (!is_scalar($subject)) { + if (is_array($subject)) { + throw new \InvalidArgumentException(static::ARRAY_MSG); + } + + throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject))); + } + + $result = preg_replace($pattern, $replacement, $subject, $limit, $count); + if ($result === null) { + throw PcreException::fromFunction('preg_replace', $pattern); + } + + return $result; + } + + /** + * @param string|string[] $pattern + * @param ($flags is PREG_OFFSET_CAPTURE ? (callable(array}>): string) : callable(array): string) $replacement + * @param string $subject + * @param int $count Set by method + * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + * + * @param-out int<0, max> $count + */ + public static function replaceCallback($pattern, callable $replacement, $subject, int $limit = -1, ?int &$count = null, int $flags = 0): string + { + if (!is_scalar($subject)) { + if (is_array($subject)) { + throw new \InvalidArgumentException(static::ARRAY_MSG); + } + + throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject))); + } + + $result = preg_replace_callback($pattern, $replacement, $subject, $limit, $count, $flags | PREG_UNMATCHED_AS_NULL); + if ($result === null) { + throw PcreException::fromFunction('preg_replace_callback', $pattern); + } + + return $result; + } + + /** + * Variant of `replaceCallback()` which outputs non-null matches (or throws) + * + * @param string $pattern + * @param ($flags is PREG_OFFSET_CAPTURE ? (callable(array}>): string) : callable(array): string) $replacement + * @param string $subject + * @param int $count Set by method + * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + * + * @param-out int<0, max> $count + */ + public static function replaceCallbackStrictGroups(string $pattern, callable $replacement, $subject, int $limit = -1, ?int &$count = null, int $flags = 0): string + { + return self::replaceCallback($pattern, function (array $matches) use ($pattern, $replacement) { + return $replacement(self::enforceNonNullMatches($pattern, $matches, 'replaceCallback')); + }, $subject, $limit, $count, $flags); + } + + /** + * @param ($flags is PREG_OFFSET_CAPTURE ? (array}>): string>) : array): string>) $pattern + * @param string $subject + * @param int $count Set by method + * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + * + * @param-out int<0, max> $count + */ + public static function replaceCallbackArray(array $pattern, $subject, int $limit = -1, ?int &$count = null, int $flags = 0): string + { + if (!is_scalar($subject)) { + if (is_array($subject)) { + throw new \InvalidArgumentException(static::ARRAY_MSG); + } + + throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject))); + } + + $result = preg_replace_callback_array($pattern, $subject, $limit, $count, $flags | PREG_UNMATCHED_AS_NULL); + if ($result === null) { + $pattern = array_keys($pattern); + throw PcreException::fromFunction('preg_replace_callback_array', $pattern); + } + + return $result; + } + + /** + * @param int-mask $flags PREG_SPLIT_NO_EMPTY or PREG_SPLIT_DELIM_CAPTURE + * @return list + */ + public static function split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array + { + if (($flags & PREG_SPLIT_OFFSET_CAPTURE) !== 0) { + throw new \InvalidArgumentException('PREG_SPLIT_OFFSET_CAPTURE is not supported as it changes the type of $matches, use splitWithOffsets() instead'); + } + + $result = preg_split($pattern, $subject, $limit, $flags); + if ($result === false) { + throw PcreException::fromFunction('preg_split', $pattern); + } + + return $result; + } + + /** + * @param int-mask $flags PREG_SPLIT_NO_EMPTY or PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_OFFSET_CAPTURE is always set + * @return list + * @phpstan-return list}> + */ + public static function splitWithOffsets(string $pattern, string $subject, int $limit = -1, int $flags = 0): array + { + $result = preg_split($pattern, $subject, $limit, $flags | PREG_SPLIT_OFFSET_CAPTURE); + if ($result === false) { + throw PcreException::fromFunction('preg_split', $pattern); + } + + return $result; + } + + /** + * @template T of string|\Stringable + * @param string $pattern + * @param array $array + * @param int-mask $flags PREG_GREP_INVERT + * @return array + */ + public static function grep(string $pattern, array $array, int $flags = 0): array + { + $result = preg_grep($pattern, $array, $flags); + if ($result === false) { + throw PcreException::fromFunction('preg_grep', $pattern); + } + + return $result; + } + + /** + * Variant of match() which returns a bool instead of int + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * + * @param-out array $matches + */ + public static function isMatch(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool + { + return (bool) static::match($pattern, $subject, $matches, $flags, $offset); + } + + /** + * Variant of `isMatch()` which outputs non-null matches (or throws) + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @throws UnexpectedNullMatchException + * + * @param-out array $matches + */ + public static function isMatchStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool + { + return (bool) self::matchStrictGroups($pattern, $subject, $matches, $flags, $offset); + } + + /** + * Variant of matchAll() which returns a bool instead of int + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * + * @param-out array> $matches + */ + public static function isMatchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool + { + return (bool) static::matchAll($pattern, $subject, $matches, $flags, $offset); + } + + /** + * Variant of `isMatchAll()` which outputs non-null matches (or throws) + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * + * @param-out array> $matches + */ + public static function isMatchAllStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool + { + return (bool) self::matchAllStrictGroups($pattern, $subject, $matches, $flags, $offset); + } + + /** + * Variant of matchWithOffsets() which returns a bool instead of int + * + * Runs preg_match with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * + * @param-out array}> $matches + */ + public static function isMatchWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): bool + { + return (bool) static::matchWithOffsets($pattern, $subject, $matches, $flags, $offset); + } + + /** + * Variant of matchAllWithOffsets() which returns a bool instead of int + * + * Runs preg_match_all with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param array $matches Set by method + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * + * @param-out array}>> $matches + */ + public static function isMatchAllWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): bool + { + return (bool) static::matchAllWithOffsets($pattern, $subject, $matches, $flags, $offset); + } + + private static function checkOffsetCapture(int $flags, string $useFunctionName): void + { + if (($flags & PREG_OFFSET_CAPTURE) !== 0) { + throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the type of $matches, use ' . $useFunctionName . '() instead'); + } + } + + private static function checkSetOrder(int $flags): void + { + if (($flags & PREG_SET_ORDER) !== 0) { + throw new \InvalidArgumentException('PREG_SET_ORDER is not supported as it changes the type of $matches'); + } + } + + /** + * @param array $matches + * @return array + * @throws UnexpectedNullMatchException + */ + private static function enforceNonNullMatches(string $pattern, array $matches, string $variantMethod) + { + foreach ($matches as $group => $match) { + if (is_string($match) || (is_array($match) && is_string($match[0]))) { + continue; + } + + throw new UnexpectedNullMatchException('Pattern "'.$pattern.'" had an unexpected unmatched group "'.$group.'", make sure the pattern always matches or use '.$variantMethod.'() instead.'); + } + + /** @var array */ + return $matches; + } + + /** + * @param array> $matches + * @return array> + * @throws UnexpectedNullMatchException + */ + private static function enforceNonNullMatchAll(string $pattern, array $matches, string $variantMethod) + { + foreach ($matches as $group => $groupMatches) { + foreach ($groupMatches as $match) { + if (null === $match) { + throw new UnexpectedNullMatchException('Pattern "'.$pattern.'" had an unexpected unmatched group "'.$group.'", make sure the pattern always matches or use '.$variantMethod.'() instead.'); + } + } + } + + /** @var array> */ + return $matches; + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/Regex.php b/vendor/composer/vendor/composer/pcre/src/Regex.php new file mode 100644 index 0000000..038cf06 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/Regex.php @@ -0,0 +1,176 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +class Regex +{ + /** + * @param non-empty-string $pattern + */ + public static function isMatch(string $pattern, string $subject, int $offset = 0): bool + { + return (bool) Preg::match($pattern, $subject, $matches, 0, $offset); + } + + /** + * @param non-empty-string $pattern + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + */ + public static function match(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchResult + { + self::checkOffsetCapture($flags, 'matchWithOffsets'); + + $count = Preg::match($pattern, $subject, $matches, $flags, $offset); + + return new MatchResult($count, $matches); + } + + /** + * Variant of `match()` which returns non-null matches (or throws) + * + * @param non-empty-string $pattern + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @throws UnexpectedNullMatchException + */ + public static function matchStrictGroups(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchStrictGroupsResult + { + // @phpstan-ignore composerPcre.maybeUnsafeStrictGroups + $count = Preg::matchStrictGroups($pattern, $subject, $matches, $flags, $offset); + + return new MatchStrictGroupsResult($count, $matches); + } + + /** + * Runs preg_match with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param int-mask $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported + */ + public static function matchWithOffsets(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchWithOffsetsResult + { + $count = Preg::matchWithOffsets($pattern, $subject, $matches, $flags, $offset); + + return new MatchWithOffsetsResult($count, $matches); + } + + /** + * @param non-empty-string $pattern + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + */ + public static function matchAll(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllResult + { + self::checkOffsetCapture($flags, 'matchAllWithOffsets'); + self::checkSetOrder($flags); + + $count = Preg::matchAll($pattern, $subject, $matches, $flags, $offset); + + return new MatchAllResult($count, $matches); + } + + /** + * Variant of `matchAll()` which returns non-null matches (or throws) + * + * @param non-empty-string $pattern + * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported + * @throws UnexpectedNullMatchException + */ + public static function matchAllStrictGroups(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllStrictGroupsResult + { + self::checkOffsetCapture($flags, 'matchAllWithOffsets'); + self::checkSetOrder($flags); + + // @phpstan-ignore composerPcre.maybeUnsafeStrictGroups + $count = Preg::matchAllStrictGroups($pattern, $subject, $matches, $flags, $offset); + + return new MatchAllStrictGroupsResult($count, $matches); + } + + /** + * Runs preg_match_all with PREG_OFFSET_CAPTURE + * + * @param non-empty-string $pattern + * @param int-mask $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported + */ + public static function matchAllWithOffsets(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllWithOffsetsResult + { + self::checkSetOrder($flags); + + $count = Preg::matchAllWithOffsets($pattern, $subject, $matches, $flags, $offset); + + return new MatchAllWithOffsetsResult($count, $matches); + } + /** + * @param string|string[] $pattern + * @param string|string[] $replacement + * @param string $subject + */ + public static function replace($pattern, $replacement, $subject, int $limit = -1): ReplaceResult + { + $result = Preg::replace($pattern, $replacement, $subject, $limit, $count); + + return new ReplaceResult($count, $result); + } + + /** + * @param string|string[] $pattern + * @param ($flags is PREG_OFFSET_CAPTURE ? (callable(array}>): string) : callable(array): string) $replacement + * @param string $subject + * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + */ + public static function replaceCallback($pattern, callable $replacement, $subject, int $limit = -1, int $flags = 0): ReplaceResult + { + $result = Preg::replaceCallback($pattern, $replacement, $subject, $limit, $count, $flags); + + return new ReplaceResult($count, $result); + } + + /** + * Variant of `replaceCallback()` which outputs non-null matches (or throws) + * + * @param string $pattern + * @param ($flags is PREG_OFFSET_CAPTURE ? (callable(array}>): string) : callable(array): string) $replacement + * @param string $subject + * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + */ + public static function replaceCallbackStrictGroups($pattern, callable $replacement, $subject, int $limit = -1, int $flags = 0): ReplaceResult + { + $result = Preg::replaceCallbackStrictGroups($pattern, $replacement, $subject, $limit, $count, $flags); + + return new ReplaceResult($count, $result); + } + + /** + * @param ($flags is PREG_OFFSET_CAPTURE ? (array}>): string>) : array): string>) $pattern + * @param string $subject + * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set + */ + public static function replaceCallbackArray(array $pattern, $subject, int $limit = -1, int $flags = 0): ReplaceResult + { + $result = Preg::replaceCallbackArray($pattern, $subject, $limit, $count, $flags); + + return new ReplaceResult($count, $result); + } + + private static function checkOffsetCapture(int $flags, string $useFunctionName): void + { + if (($flags & PREG_OFFSET_CAPTURE) !== 0) { + throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the return type, use '.$useFunctionName.'() instead'); + } + } + + private static function checkSetOrder(int $flags): void + { + if (($flags & PREG_SET_ORDER) !== 0) { + throw new \InvalidArgumentException('PREG_SET_ORDER is not supported as it changes the return type'); + } + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/ReplaceResult.php b/vendor/composer/vendor/composer/pcre/src/ReplaceResult.php new file mode 100644 index 0000000..3384771 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/ReplaceResult.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +final class ReplaceResult +{ + /** + * @readonly + * @var string + */ + public $result; + + /** + * @readonly + * @var 0|positive-int + */ + public $count; + + /** + * @readonly + * @var bool + */ + public $matched; + + /** + * @param 0|positive-int $count + */ + public function __construct(int $count, string $result) + { + $this->count = $count; + $this->matched = (bool) $count; + $this->result = $result; + } +} diff --git a/vendor/composer/vendor/composer/pcre/src/UnexpectedNullMatchException.php b/vendor/composer/vendor/composer/pcre/src/UnexpectedNullMatchException.php new file mode 100644 index 0000000..f123828 --- /dev/null +++ b/vendor/composer/vendor/composer/pcre/src/UnexpectedNullMatchException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Pcre; + +class UnexpectedNullMatchException extends PcreException +{ + public static function fromFunction($function, $pattern) + { + throw new \LogicException('fromFunction should not be called on '.self::class.', use '.PcreException::class); + } +} diff --git a/vendor/composer/vendor/composer/platform_check.php b/vendor/composer/vendor/composer/platform_check.php new file mode 100644 index 0000000..8748439 --- /dev/null +++ b/vendor/composer/vendor/composer/platform_check.php @@ -0,0 +1,29 @@ += 80200)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.0". You are running ' . PHP_VERSION . '.'; +} + +if (PHP_INT_SIZE !== 8) { + $issues[] = 'Your Composer dependencies require a 64-bit build of PHP.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + throw new \RuntimeException( + 'Composer detected issues in your platform: ' . implode(' ', $issues) + ); +} diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.editorconfig b/vendor/composer/vendor/maennchen/zipstream-php/.editorconfig new file mode 100644 index 0000000..f7cd914 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.editorconfig @@ -0,0 +1,22 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.{yml,md,xml}] +indent_style = space +indent_size = 2 + +[*.{rst,php}] +indent_style = space +indent_size = 4 + +[composer.json] +indent_style = space +indent_size = 2 + +[composer.lock] +indent_style = space +indent_size = 4 diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.gitattributes b/vendor/composer/vendor/maennchen/zipstream-php/.gitattributes new file mode 100644 index 0000000..e058ebd --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.gitattributes @@ -0,0 +1,6 @@ +.gitignore text eol=lf +.gitattributes text eol=lf +*.md text eol=lf +*.php text eol=lf +*.yml text eol=lf +*.xml text eol=lf diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/CODE_OF_CONDUCT.md b/vendor/composer/vendor/maennchen/zipstream-php/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..9d75b87 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +jonatan@maennchen.ch. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][mozilla coc]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][faq]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/CONTRIBUTING.md b/vendor/composer/vendor/maennchen/zipstream-php/.github/CONTRIBUTING.md new file mode 100644 index 0000000..d8caee0 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/CONTRIBUTING.md @@ -0,0 +1,139 @@ +# Contributing to ZipStream-PHP + +## Welcome! + +We look forward to your contributions! Here are some examples how you can +contribute: + +- [Report a bug](https://github.com/maennchen/ZipStream-PHP/issues/new?labels=bug&template=BUG.md) +- [Propose a new feature](https://github.com/maennchen/ZipStream-PHP/issues/new?labels=enhancement&template=FEATURE.md) +- [Send a pull request](https://github.com/maennchen/ZipStream-PHP/pulls) + +## We have a Code of Conduct + +Please note that this project is released with a +[Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this +project you agree to abide by its terms. + +## Any contributions you make will be under the MIT License + +When you submit code changes, your submissions are understood to be under the +same [MIT License](https://github.com/maennchen/ZipStream-PHP/blob/main/LICENSE) +that covers the project. By contributing to this project, you agree that your +contributions will be licensed under its MIT License. + +## Write bug reports with detail, background, and sample code + +In your bug report, please provide the following: + +- A quick summary and/or background +- Steps to reproduce + - Be specific! + - Give sample code if you can. +- What you expected would happen +- What actually happens +- Notes (possibly including why you think this might be happening, or stuff you +- tried that didn't work) + +Please do not report a bug for a version of ZIPStream-PHP that is no longer +supported (`< 3.0.0`). Please do not report a bug if you are using a version of +PHP that is not supported by the version of ZipStream-PHP you are using. + +Please post code and output as text +([using proper markup](https://guides.github.com/features/mastering-markdown/)). +Do not post screenshots of code or output. + +Please include the output of `composer info | sort`. + +## Workflow for Pull Requests + +1. Fork the repository. +2. Create your branch from `main` if you plan to implement new functionality or + change existing code significantly; create your branch from the oldest branch + that is affected by the bug if you plan to fix a bug. +3. Implement your change and add tests for it. +4. Ensure the test suite passes. +5. Ensure the code complies with our coding guidelines (see below). +6. Send that pull request! + +Please make sure you have +[set up your user name and email address](https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup) +for use with Git. Strings such as `silly nick name ` look really +stupid in the commit history of a project. + +We encourage you to +[sign your Git commits with your GPG key](https://docs.github.com/en/github/authenticating-to-github/signing-commits). + +Pull requests for new features must be based on the `main` branch. + +We are trying to keep backwards compatibility breaks in ZipStream-PHP to a +minimum. Please take this into account when proposing changes. + +Due to time constraints, we are not always able to respond as quickly as we +would like. Please do not take delays personal and feel free to remind us if you +feel that we forgot to respond. + +## Coding Guidelines + +This project comes with a configuration file (located at `/psalm.yml` in the +repository) that you can use to perform static analysis (with a focus on type +checking): + +```bash +$ .composer run test:lint +``` + +This project comes with a configuration file (located at +`/.php-cs-fixer.dist.php` in the repository) that you can use to (re)format your +source code for compliance with this project's coding guidelines: + +```bash +$ composer run format +``` + +Please understand that we will not accept a pull request when its changes +violate this project's coding guidelines. + +## Using ZipStream-PHP from a Git checkout + +The following commands can be used to perform the initial checkout of +ZipStream-PHP: + +```bash +$ git clone git@github.com:maennchen/ZipStream-PHP.git + +$ cd ZipStream-PHP +``` + +Install ZipStream-PHP's dependencies using [Composer](https://getcomposer.org/): + +```bash +$ composer install +$ composer run install:tools # Install phpDocumentor using phive +``` + +## Running ZipStream-PHP's test suite + +After following the steps shown above, ZipStream-PHP's test suite is run like +this: + +```bash +$ composer run test:unit +``` + +There's some slow tests in the test suite that test the handling of big files in +the archives. To skip them use the following command instead: + +```bash +$ composer run test:unit:fast +``` + +## Generating ZipStream-PHP Documentation + +To generate the documentation for the library, run: + +```bash +$ composer run docs:generate +``` + +The guide documentation pages can be found in the `/guides/` directory. diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/FUNDING.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/FUNDING.yml new file mode 100644 index 0000000..5a46127 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/FUNDING.yml @@ -0,0 +1 @@ +github: maennchen diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/BUG.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/BUG.yml new file mode 100644 index 0000000..0eb8cc7 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/BUG.yml @@ -0,0 +1,71 @@ +name: 🐞 Bug Report +description: Something is broken? +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + - Create a discussion instead if you are looking for support: + https://github.com/maennchen/ZipStream-PHP/discussions + - type: input + id: version + attributes: + label: ZipStream-PHP version + placeholder: x.y.z + validations: + required: true + - type: input + id: php-version + attributes: + label: PHP version + placeholder: x.y.z + validations: + required: true + - type: checkboxes + id: constraints + attributes: + label: Constraints for Bug Report + options: + - label: | + I'm using a version of ZipStream that is currently supported: + https://github.com/maennchen/ZipStream-PHP#version-support + required: true + - label: | + I'm using a version of PHP that has active support: + https://www.php.net/supported-versions.php + required: true + - label: | + I'm using a version of PHP that is compatible with your used + ZipStream version. + required: true + - label: | + I'm using the latest release of the used ZipStream major version. + required: true + - type: textarea + id: summary + attributes: + label: Summary + description: Provide a summary describing the problem you are experiencing. + validations: + required: true + - type: textarea + id: current-behaviour + attributes: + label: Current behavior + description: What is the current (buggy) behavior? + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: How to reproduce + description: Provide steps to reproduce the bug. + validations: + required: true + - type: textarea + id: expected-behaviour + attributes: + label: Expected behavior + description: What was the expected (correct) behavior? + validations: + required: true diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/FEATURE.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/FEATURE.yml new file mode 100644 index 0000000..e5dec63 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/FEATURE.yml @@ -0,0 +1,11 @@ +name: 🎉 Feature Request +description: You have a neat idea that should be implemented? +labels: ["enhancement"] +body: + - type: textarea + id: description + attributes: + label: Description + description: Provide a summary of the feature you would like to see implemented. + validations: + required: true diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE.md b/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..6892c57 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,6 @@ +Please go the the `Preview` tab and select the appropriate sub-template: + +* [🐞 Failing Test](?expand=1&template=FAILING_TEST.md) +* [🐞 Bug Fix](?expand=1&template=FIX.md) +* [⚙ Improvement](?expand=1&template=IMPROVEMENT.md) +* [🎉 New Feature](?expand=1&template=NEW_FEATURE.md) diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FAILING_TEST.md b/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FAILING_TEST.md new file mode 100644 index 0000000..24603cb --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FAILING_TEST.md @@ -0,0 +1,13 @@ + + + diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FIX.md b/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FIX.md new file mode 100644 index 0000000..77f65a0 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FIX.md @@ -0,0 +1,13 @@ + + + diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/IMPROVEMENT.md b/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/IMPROVEMENT.md new file mode 100644 index 0000000..3ac8e31 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/IMPROVEMENT.md @@ -0,0 +1,9 @@ + + + diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/NEW_FEATURE.md b/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/NEW_FEATURE.md new file mode 100644 index 0000000..ca53939 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/NEW_FEATURE.md @@ -0,0 +1,9 @@ + + + diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/SECURITY.md b/vendor/composer/vendor/maennchen/zipstream-php/.github/SECURITY.md new file mode 100644 index 0000000..3046c31 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/SECURITY.md @@ -0,0 +1,22 @@ +# Security Policy + +[![OpenSSF Vulnerability Disclosure](https://img.shields.io/badge/OpenSSF-Vulnerability_Disclosure-green)](https://github.com/ossf/oss-vulnerability-guide/blob/main/finder-guide.md) +[![GitHub Report](https://img.shields.io/badge/GitHub-Security_Advisories-blue)](https://github.com/maennchen/ZipStream-PHP/security/advisories/new) +[![Email Report](https://img.shields.io/badge/Email-jonatan%40maennchen.ch-blue)](mailto:jonatan@maennchen.ch) + +This repository follows the +[OpenSSF Vulnerability Disclosure guide](https://github.com/ossf/oss-vulnerability-guide/tree/main). +You can learn more about it in the +[Finders Guide](https://github.com/ossf/oss-vulnerability-guide/blob/main/finder-guide.md). + +Please report vulnerabilities via the +[GitHub Security Vulnerability Reporting](https://github.com/maennchen/ZipStream-PHP/security/advisories/new) +or via email to [`jonatan@maennchen.ch`](mailto:jonatan@maennchen.ch) if this does +not work for you. + +Our vulnerability management team will respond within 3 working days of your +report. If the issue is confirmed as a vulnerability, we will open a Security +Advisory. This project follows a 90 day disclosure timeline. + +If you have questions about reporting security issues, email the vulnerability +management team: [`jonatan@maennchen.ch`](mailto:jonatan@maennchen.ch) diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/dependabot.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/dependabot.yml new file mode 100644 index 0000000..9d20742 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/dependabot.yml @@ -0,0 +1,15 @@ +version: 2 +updates: + - package-ecosystem: "composer" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + groups: + github-actions: + applies-to: version-updates + patterns: + - "*" diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/scorecard.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/scorecard.yml new file mode 100644 index 0000000..219fc0b --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/scorecard.yml @@ -0,0 +1,14 @@ +annotations: + - checks: + - fuzzing + reasons: + - reason: not-applicable # PHP is memory safe + - checks: + - packaging + reasons: + - reason: not-supported # Using Composer + - checks: + - signed-releases + reasons: + - reason: not-applicable # Releases are distributed via Composer + diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/branch_main.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/branch_main.yml new file mode 100644 index 0000000..15ff278 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/branch_main.yml @@ -0,0 +1,24 @@ +on: + push: + branches: + - "main" + +name: "Main Branch" + +permissions: + contents: read + +jobs: + test: + name: "Test" + + permissions: + contents: read + security-events: write + + uses: ./.github/workflows/part_test.yml + + docs: + name: "Docs" + + uses: ./.github/workflows/part_docs.yml diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/part_dependabot.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/part_dependabot.yml new file mode 100644 index 0000000..20a13a2 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/part_dependabot.yml @@ -0,0 +1,30 @@ +on: + workflow_call: {} + +name: "Dependabot" + +permissions: + contents: read + +jobs: + automerge_dependabot: + name: "Automerge PRs" + + runs-on: ubuntu-latest + + permissions: + pull-requests: write + contents: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - uses: fastify/github-action-merge-dependabot@c3bde0759d4f24db16f7b250b2122bc2df57e817 # v3.11.0 + with: + github-token: ${{ github.token }} + use-github-auto-merge: true + # Major Updates need to be merged manually + target: minor diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/part_docs.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/part_docs.yml new file mode 100644 index 0000000..9b779eb --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/part_docs.yml @@ -0,0 +1,51 @@ +on: + workflow_call: {} + +name: "Documentation" + +permissions: + contents: read + +jobs: + generate: + name: "Generate" + + runs-on: ubuntu-latest + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: SetUp PHP + id: setup-php + uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2 + with: + php-version: "8.3" + tools: phive + - name: Cache Tools + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + id: cache + with: + path: ~/.phive + key: tools-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-${{ hashFiles('**/phars.xml') }} + restore-keys: | + tools-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}- + tools-${{ steps.setup-php.outputs.php-version }}- + tools- + - name: Install Tools + run: composer run install:tools + - name: Generate Docs + run: composer run docs:generate + - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: docs + path: docs + - name: Package for GitHub Pages + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 + with: + path: docs + diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/part_release.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/part_release.yml new file mode 100644 index 0000000..112d72a --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/part_release.yml @@ -0,0 +1,94 @@ +on: + workflow_call: + inputs: + releaseName: + required: true + type: string + stable: + required: false + type: boolean + default: false + +name: "Release" + +permissions: + contents: read + +jobs: + create: + name: Create Release + + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Create prerelease + if: ${{ !inputs.stable }} + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + gh release create \ + --repo ${{ github.repository }} \ + --title ${{ inputs.releaseName }} \ + --prerelease \ + --generate-notes \ + ${{ inputs.releaseName }} + + - name: Create release + if: ${{ inputs.stable }} + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + gh release create \ + --repo ${{ github.repository }} \ + --title ${{ inputs.releaseName }} \ + --generate-notes \ + ${{ inputs.releaseName }} + + upload_release: + name: "Upload" + + needs: ["create"] + + runs-on: ubuntu-latest + + permissions: + id-token: write + contents: write + attestations: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: docs + path: docs + - run: | + tar -czvf docs.tar.gz docs + - name: "Attest Documentation" + id: attestation + uses: actions/attest-build-provenance@520d128f165991a6c774bcb264f323e3d70747f4 # v2.2.0 + with: + subject-path: "docs.tar.gz" + - name: Copy Attestation + run: cp "$ATTESTATION" docs.tar.gz.sigstore + env: + ATTESTATION: "${{ steps.attestation.outputs.bundle-path }}" + - name: Upload + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + gh release upload --clobber "${{ github.ref_name }}" \ + docs.tar.gz docs.tar.gz.sigstore diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/part_test.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/part_test.yml new file mode 100644 index 0000000..d4f8180 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/part_test.yml @@ -0,0 +1,181 @@ +on: + workflow_call: + +name: "Test" + +permissions: + contents: read + +jobs: + phpunit: + name: PHPUnit (PHP ${{ matrix.php }} on ${{ matrix.os }}) + + runs-on: ${{ matrix.os }} + + continue-on-error: ${{ matrix.experimental }} + + strategy: + fail-fast: false + matrix: + php: ["8.2", "8.3", "8.4"] + os: [ubuntu-latest] + experimental: [false] + include: + - php: nightly + os: ubuntu-latest + experimental: true + - php: "8.4" + os: windows-latest + experimental: false + - php: "8.4" + os: macos-latest + experimental: false + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: SetUp PHP + id: setup-php + uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2 + with: + php-version: "${{ matrix.php }}" + tools: phpunit + coverage: xdebug + extensions: xdebug,zip + - name: Get composer cache directory + id: composer-cache-common + if: "${{ runner.os != 'Windows' }}" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - name: Get composer cache directory + id: composer-cache-windows + if: "${{ runner.os == 'Windows' }}" + run: echo "dir=$(composer config cache-files-dir)" >> $env:GITHUB_OUTPUT + - name: Cache Deps + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + id: cache + with: + path: ${{ steps.composer-cache-common.outputs.dir }}${{ steps.composer-cache-windows.outputs.dir }} + key: deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer- + deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}- + deps-${{ steps.setup-php.outputs.php-version }}- + deps- + - name: Install Deps + if: matrix.php != 'nightly' + run: composer install --prefer-dist + - name: Install Deps (ignore PHP requirement) + if: matrix.php == 'nightly' + run: composer install --prefer-dist --ignore-platform-req=php+ + - name: Run PHPUnit + run: composer run test:unit:cov + - name: Upload coverage results to Coveralls + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_PARALLEL: true + COVERALLS_FLAG_NAME: ${{ runner.os }}-${{ steps.setup-php.outputs.php-version }} + run: composer run coverage:report + continue-on-error: ${{ matrix.experimental }} + + mark_coverage_done: + needs: ["phpunit"] + + runs-on: ubuntu-latest + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Coveralls Finished + uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6 + with: + github-token: ${{ secrets.github_token }} + parallel-finished: true + + psalm: + name: Run Psalm + + runs-on: "ubuntu-latest" + + permissions: + security-events: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: SetUp PHP + id: setup-php + uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2 + with: + php-version: "8.3" + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - name: Cache Deps + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + id: cache + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer- + deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}- + deps-${{ steps.setup-php.outputs.php-version }}- + deps- + - name: Install Deps + run: composer install --prefer-dist + - name: Run Psalm + run: composer run test:lint -- --report=results.sarif + - name: "Upload SARIF" + uses: github/codeql-action/upload-sarif@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3 + with: + sarif_file: results.sarif + + php-cs: + name: Run PHP-CS + + runs-on: "ubuntu-latest" + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: SetUp PHP + id: setup-php + uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2 + with: + php-version: "8.3" + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - name: Cache Deps + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + id: cache + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer- + deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}- + deps-${{ steps.setup-php.outputs.php-version }}- + deps- + - name: Install Deps + run: composer install --prefer-dist + - name: Run PHP-CS + run: composer run test:formatted diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/pr.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/pr.yml new file mode 100644 index 0000000..d21f398 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/pr.yml @@ -0,0 +1,50 @@ +on: + pull_request: + branches: + - "*" + workflow_dispatch: {} + +name: "Pull Request" + +permissions: + contents: read + +jobs: + test: + name: "Test" + + permissions: + contents: read + security-events: write + + uses: ./.github/workflows/part_test.yml + + docs: + name: "Docs" + + uses: ./.github/workflows/part_docs.yml + + dependabot: + name: "Dependabot" + + if: ${{ github.actor == 'dependabot[bot]'}} + + permissions: + pull-requests: write + contents: write + + uses: ./.github/workflows/part_dependabot.yml + + dependency-review: + name: Dependency Review + runs-on: ubuntu-latest + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: 'Checkout Repository' + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: 'Dependency Review' + uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0 diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/scorecard.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/scorecard.yml new file mode 100644 index 0000000..c1d08a2 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/scorecard.yml @@ -0,0 +1,78 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '28 11 * * 3' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: "Checkout code" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard (optional). + # Commenting out will disable upload of results to your repo's Code Scanning dashboard + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5 + with: + sarif_file: results.sarif diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/tag-beta.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/tag-beta.yml new file mode 100644 index 0000000..b339945 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/tag-beta.yml @@ -0,0 +1,29 @@ +on: + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+" + +name: "Beta Tag" + +permissions: + contents: read + +jobs: + docs: + name: "Docs" + + uses: ./.github/workflows/part_docs.yml + + release: + name: "Release" + + needs: ["docs"] + + permissions: + id-token: write + contents: write + attestations: write + + uses: ./.github/workflows/part_release.yml + with: + releaseName: "${{ github.ref_name }}" diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/tag-stable.yml b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/tag-stable.yml new file mode 100644 index 0000000..dfc1438 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.github/workflows/tag-stable.yml @@ -0,0 +1,55 @@ +on: + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+" + +name: "Stable Tag" + +permissions: + contents: read + +jobs: + docs: + name: "Docs" + + uses: ./.github/workflows/part_docs.yml + + release: + name: "Release" + + needs: ["docs"] + + permissions: + id-token: write + contents: write + attestations: write + + uses: ./.github/workflows/part_release.yml + with: + releaseName: "${{ github.ref_name }}" + stable: true + + deploy_pages: + name: "Deploy to GitHub Pages" + + needs: ["release", "docs"] + + runs-on: ubuntu-latest + + permissions: + pages: write + id-token: write + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Harden Runner + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + with: + egress-policy: audit + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.gitignore b/vendor/composer/vendor/maennchen/zipstream-php/.gitignore new file mode 100644 index 0000000..e52a498 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.gitignore @@ -0,0 +1,12 @@ +/composer.lock +/cov +/coverage.clover.xml +/docs +.idea +/.php-cs-fixer.cache +/.phpdoc/cache +/.phpunit.result.cache +/phpunit.xml +/.phpunit.cache +/tools +/vendor diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.phive/phars.xml b/vendor/composer/vendor/maennchen/zipstream-php/.phive/phars.xml new file mode 100644 index 0000000..c958402 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.phive/phars.xml @@ -0,0 +1,4 @@ + + + + diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php b/vendor/composer/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php new file mode 100644 index 0000000..9d47c38 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php @@ -0,0 +1,73 @@ + + * @copyright 2022 Nicolas CARPi + * @see https://github.com/maennchen/ZipStream-PHP + * @license MIT + * @package maennchen/ZipStream-PHP + */ + +use PhpCsFixer\Config; +use PhpCsFixer\Finder; +use PhpCsFixer\Runner; + +$finder = Finder::create() + ->exclude('.github') + ->exclude('.phpdoc') + ->exclude('docs') + ->exclude('tools') + ->exclude('vendor') + ->in(__DIR__); + +$config = new Config(); +return $config->setRules([ + '@PER' => true, + '@PER:risky' => true, + '@PHP83Migration' => true, + '@PHP84Migration' => true, + '@PHPUnit84Migration:risky' => true, + 'array_syntax' => ['syntax' => 'short'], + 'class_attributes_separation' => true, + 'declare_strict_types' => true, + 'dir_constant' => true, + 'is_null' => true, + 'no_homoglyph_names' => true, + 'no_null_property_initialization' => true, + 'no_php4_constructor' => true, + 'no_unused_imports' => true, + 'no_useless_else' => true, + 'non_printable_character' => true, + 'ordered_imports' => true, + 'ordered_class_elements' => true, + 'php_unit_construct' => true, + 'pow_to_exponentiation' => true, + 'psr_autoloading' => true, + 'random_api_migration' => true, + 'return_assignment' => true, + 'self_accessor' => true, + 'semicolon_after_instruction' => true, + 'short_scalar_cast' => true, + 'simplified_null_return' => true, + 'single_class_element_per_statement' => true, + 'single_line_comment_style' => true, + 'single_quote' => true, + 'space_after_semicolon' => true, + 'standardize_not_equals' => true, + 'strict_param' => true, + 'ternary_operator_spaces' => true, + 'trailing_comma_in_multiline' => true, + 'trim_array_spaces' => true, + 'unary_operator_spaces' => true, + 'global_namespace_import' => [ + 'import_classes' => true, + 'import_functions' => true, + 'import_constants' => true, + ], + ]) + ->setFinder($finder) + ->setRiskyAllowed(true) + ->setParallelConfig(Runner\Parallel\ParallelConfigFactory::detect()); diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig b/vendor/composer/vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig new file mode 100644 index 0000000..2a70c0a --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig @@ -0,0 +1,15 @@ +{% extends 'layout.html.twig' %} + +{% set topMenu = { + "menu": [ + { "name": "Guides", "url": "https://maennchen.dev/ZipStream-PHP/guide/index.html"}, + { "name": "API", "url": "https://maennchen.dev/ZipStream-PHP/classes/ZipStream-ZipStream.html"}, + { "name": "Issues", "url": "https://github.com/maennchen/ZipStream-PHP/issues"}, + ], + "social": [ + { "iconClass": "fab fa-github", "url": "https://github.com/maennchen/ZipStream-PHP"}, + { "iconClass": "fas fa-envelope-open-text", "url": "https://github.com/maennchen/ZipStream-PHP/discussions"}, + { "iconClass": "fas fa-money-bill", "url": "https://github.com/sponsors/maennchen"}, + ] +} +%} \ No newline at end of file diff --git a/vendor/composer/vendor/maennchen/zipstream-php/.tool-versions b/vendor/composer/vendor/maennchen/zipstream-php/.tool-versions new file mode 100644 index 0000000..150c1ee --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/.tool-versions @@ -0,0 +1 @@ +php 8.4.3 diff --git a/vendor/composer/vendor/maennchen/zipstream-php/LICENSE b/vendor/composer/vendor/maennchen/zipstream-php/LICENSE new file mode 100644 index 0000000..ebe7fe2 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/LICENSE @@ -0,0 +1,24 @@ +MIT License + +Copyright (C) 2007-2009 Paul Duncan +Copyright (C) 2014 Jonatan Männchen +Copyright (C) 2014 Jesse G. Donat +Copyright (C) 2018 Nicolas CARPi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/composer/vendor/maennchen/zipstream-php/README.md b/vendor/composer/vendor/maennchen/zipstream-php/README.md new file mode 100644 index 0000000..1e6d679 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/README.md @@ -0,0 +1,157 @@ +# ZipStream-PHP + +[![Main Branch](https://github.com/maennchen/ZipStream-PHP/actions/workflows/branch_main.yml/badge.svg)](https://github.com/maennchen/ZipStream-PHP/actions/workflows/branch_main.yml) +[![Coverage Status](https://coveralls.io/repos/github/maennchen/ZipStream-PHP/badge.svg?branch=main)](https://coveralls.io/github/maennchen/ZipStream-PHP?branch=main) +[![Latest Stable Version](https://poser.pugx.org/maennchen/zipstream-php/v/stable)](https://packagist.org/packages/maennchen/zipstream-php) +[![Total Downloads](https://poser.pugx.org/maennchen/zipstream-php/downloads)](https://packagist.org/packages/maennchen/zipstream-php) +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9524/badge)](https://www.bestpractices.dev/projects/9524) +[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/maennchen/ZipStream-PHP/badge)](https://scorecard.dev/viewer/?uri=github.com/maennchen/ZipStream-PHP) + +## Unstable Branch + +The `main` branch is not stable. Please see the +[releases](https://github.com/maennchen/ZipStream-PHP/releases) for a stable +version. + +## Overview + +A fast and simple streaming zip file downloader for PHP. Using this library will +save you from having to write the Zip to disk. You can directly send it to the +user, which is much faster. It can work with S3 buckets or any PSR7 Stream. + +Please see the [LICENSE](LICENSE) file for licensing and warranty information. + +## Installation + +Simply add a dependency on maennchen/zipstream-php to your project's +`composer.json` file if you use Composer to manage the dependencies of your +project. Use following command to add the package to your project's dependencies: + +```bash +composer require maennchen/zipstream-php +``` + +## Usage + +For detailed instructions, please check the +[Documentation](https://maennchen.github.io/ZipStream-PHP/). + +```php +// Autoload the dependencies +require 'vendor/autoload.php'; + +// create a new zipstream object +$zip = new ZipStream\ZipStream( + outputName: 'example.zip', + + // enable output of HTTP headers + sendHttpHeaders: true, +); + +// create a file named 'hello.txt' +$zip->addFile( + fileName: 'hello.txt', + data: 'This is the contents of hello.txt', +); + +// add a file named 'some_image.jpg' from a local file 'path/to/image.jpg' +$zip->addFileFromPath( + fileName: 'some_image.jpg', + path: 'path/to/image.jpg', +); + +// finish the zip stream +$zip->finish(); +``` + +## Upgrade to version 3.1.2 + +- Minimum PHP Version: `8.2` + +## Upgrade to version 3.0.0 + +### General + +- Minimum PHP Version: `8.1` +- Only 64bit Architecture is supported. +- The class `ZipStream\Option\Method` has been replaced with the enum + `ZipStream\CompressionMethod`. +- Most clases have been flagged as `@internal` and should not be used from the + outside. + If you're using internal resources to extend this library, please open an + issue so that a clean interface can be added & published. + The externally available classes & enums are: + - `ZipStream\CompressionMethod` + - `ZipStream\Exception*` + - `ZipStream\ZipStream` + +### Archive Options + +- The class `ZipStream\Option\Archive` has been replaced in favor of named + arguments in the `ZipStream\ZipStream` constuctor. +- The archive options `largeFileSize` & `largeFileMethod` has been removed. If + you want different `compressionMethods` based on the file size, you'll have to + implement this yourself. +- The archive option `httpHeaderCallback` changed the type from `callable` to + `Closure`. +- The archive option `zeroHeader` has been replaced with the option + `defaultEnableZeroHeader` and can be overridden for every file. Its default + value changed from `false` to `true`. +- The archive option `statFiles` was removed since the library no longer checks + filesizes this way. +- The archive option `deflateLevel` has been replaced with the option + `defaultDeflateLevel` and can be overridden for every file. +- The first argument (`name`) of the `ZipStream\ZipStream` constuctor has been + replaced with the named argument `outputName`. +- Headers are now also sent if the `outputName` is empty. If you do not want to + automatically send http headers, set `sendHttpHeaders` to `false`. + +### File Options + +- The class `ZipStream\Option\File` has been replaced in favor of named + arguments in the `ZipStream\ZipStream->addFile*` functions. +- The file option `method` has been renamed to `compressionMethod`. +- The file option `time` has been renamed to `lastModificationDateTime`. +- The file option `size` has been renamed to `maxSize`. + +## Upgrade to version 2.0.0 + +https://github.com/maennchen/ZipStream-PHP/tree/2.0.0#upgrade-to-version-200 + +## Upgrade to version 1.0.0 + +https://github.com/maennchen/ZipStream-PHP/tree/2.0.0#upgrade-to-version-100 + +## Contributing + +ZipStream-PHP is a collaborative project. Please take a look at the +[.github/CONTRIBUTING.md](.github/CONTRIBUTING.md) file. + +## Version Support + +Versions are supported according to the table below. + +Please do not open any pull requests contradicting the current version support +status. + +Careful: Always check the `README` on `main` for up-to-date information. + +| Version | New Features | Bugfixes | Security | +|---------|--------------|----------|----------| +| *3* | ✓ | ✓ | ✓ | +| *2* | ✗ | ✗ | ✓ | +| *1* | ✗ | ✗ | ✗ | +| *0* | ✗ | ✗ | ✗ | + +This library aligns itself with the PHP core support. New features and bugfixes +will only target PHP versions according to their current status. + +See: https://www.php.net/supported-versions.php + +## About the Authors + +- Paul Duncan - https://pablotron.org/ +- Jonatan Männchen - https://maennchen.dev +- Jesse G. Donat - https://donatstudios.com +- Nicolas CARPi - https://www.deltablot.com +- Nik Barham - https://www.brokencube.co.uk diff --git a/vendor/composer/vendor/maennchen/zipstream-php/composer.json b/vendor/composer/vendor/maennchen/zipstream-php/composer.json new file mode 100644 index 0000000..6ecd503 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/composer.json @@ -0,0 +1,93 @@ +{ + "name": "maennchen/zipstream-php", + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": ["zip", "stream"], + "type": "library", + "license": "MIT", + "authors": [{ + "name": "Paul Duncan", + "email": "pabs@pablotron.org" + }, + { + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" + } + ], + "require": { + "php-64bit": "^8.2", + "ext-mbstring": "*", + "ext-zlib": "*" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "guzzlehttp/guzzle": "^7.5", + "ext-zip": "*", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.5", + "friendsofphp/php-cs-fixer": "^3.16", + "vimeo/psalm": "^6.0", + "brianium/paratest": "^7.7" + }, + "suggest": { + "psr/http-message": "^2.0", + "guzzlehttp/psr7": "^2.4" + }, + "scripts": { + "format": "php-cs-fixer fix", + "test": [ + "@test:unit", + "@test:formatted", + "@test:lint" + ], + "test:unit:setup-cov": "@putenv XDEBUG_MODE=coverage", + "test:unit": "paratest --functional", + "test:unit:cov": ["@test:unit:setup-cov", "@test:unit --coverage-clover=coverage.clover.xml --coverage-html cov"], + "test:unit:slow": "@test:unit --group slow", + "test:unit:slow:cov": ["@test:unit:setup-cov", "@test:unit --coverage-clover=coverage.clover.xml --coverage-html cov --group slow"], + "test:unit:fast": "@test:unit --exclude-group slow", + "test:unit:fast:cov": ["@test:unit:setup-cov", "@test:unit --coverage-clover=coverage.clover.xml --coverage-html cov --exclude-group slow"], + "test:formatted": "@format --dry-run --stop-on-violation --using-cache=no", + "test:lint": "psalm --stats --show-info=true --find-unused-psalm-suppress", + "coverage:report": "php-coveralls --coverage_clover=coverage.clover.xml --json_path=coveralls-upload.json --insecure", + "install:tools": "phive install --trust-gpg-keys 0x67F861C3D889C656 --trust-gpg-keys 0x8AC0BAA79732DD42", + "docs:generate": "tools/phpdocumentor --sourcecode" + }, + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { "ZipStream\\Test\\": "test/" } + }, + "archive": { + "exclude": [ + "/composer.lock", + "/docs", + "/.gitattributes", + "/.github", + "/.gitignore", + "/guides", + "/.phive", + "/.php-cs-fixer.cache", + "/.php-cs-fixer.dist.php", + "/.phpdoc", + "/phpdoc.dist.xml", + "/.phpunit.result.cache", + "/phpunit.xml.dist", + "/psalm.xml", + "/test", + "/tools", + "/.tool-versions", + "/vendor" + ] + } +} diff --git a/vendor/composer/vendor/maennchen/zipstream-php/guides/ContentLength.rst b/vendor/composer/vendor/maennchen/zipstream-php/guides/ContentLength.rst new file mode 100644 index 0000000..21fea34 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/guides/ContentLength.rst @@ -0,0 +1,47 @@ +Adding Content-Length header +============= + +Adding a ``Content-Length`` header for ``ZipStream`` can be achieved by +using the options ``SIMULATION_STRICT`` or ``SIMULATION_LAX`` in the +``operationMode`` parameter. + +In the ``SIMULATION_STRICT`` mode, ``ZipStream`` will not allow to calculate the +size based on reading the whole file. ``SIMULATION_LAX`` will read the whole +file if neccessary. + +``SIMULATION_STRICT`` is therefore useful to make sure that the size can be +calculated efficiently. + +.. code-block:: php + use ZipStream\OperationMode; + use ZipStream\ZipStream; + + $zip = new ZipStream( + operationMode: OperationMode::SIMULATE_STRICT, // or SIMULATE_LAX + defaultEnableZeroHeader: false, + sendHttpHeaders: true, + outputStream: $stream, + ); + + // Normally add files + $zip->addFile('sample.txt', 'Sample String Data'); + + // Use addFileFromCallback and exactSize if you want to defer opening of + // the file resource + $zip->addFileFromCallback( + 'sample.txt', + exactSize: 18, + callback: function () { + return fopen('...'); + } + ); + + // Read resulting file size + $size = $zip->finish(); + + // Tell it to the browser + header('Content-Length: '. $size); + + // Execute the Simulation and stream the actual zip to the client + $zip->executeSimulation(); + diff --git a/vendor/composer/vendor/maennchen/zipstream-php/guides/FlySystem.rst b/vendor/composer/vendor/maennchen/zipstream-php/guides/FlySystem.rst new file mode 100644 index 0000000..4e6c6fb --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/guides/FlySystem.rst @@ -0,0 +1,34 @@ +Usage with FlySystem +=============== + +For saving or uploading the generated zip, you can use the +`Flysystem `_ package, and its many +adapters. + +For that you will need to provide another stream than the ``php://output`` +default one, and pass it to Flysystem ``putStream`` method. + +.. code-block:: php + + // Open Stream only once for read and write since it's a memory stream and + // the content is lost when closing the stream / opening another one + $tempStream = fopen('php://memory', 'w+'); + + // Create Zip Archive + $zipStream = new ZipStream( + outputStream: $tempStream, + outputName: 'test.zip', + ); + $zipStream->addFile('test.txt', 'text'); + $zipStream->finish(); + + // Store File + // (see Flysystem documentation, and all its framework integration) + // Can be any adapter (AWS, Google, Ftp, etc.) + $adapter = new Local(__DIR__.'/path/to/folder'); + $filesystem = new Filesystem($adapter); + + $filesystem->writeStream('test.zip', $tempStream) + + // Close Stream + fclose($tempStream); diff --git a/vendor/composer/vendor/maennchen/zipstream-php/guides/Nginx.rst b/vendor/composer/vendor/maennchen/zipstream-php/guides/Nginx.rst new file mode 100644 index 0000000..c53d300 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/guides/Nginx.rst @@ -0,0 +1,16 @@ +Usage with nginx +============= + +If you are using nginx as a webserver, it will try to buffer the response. +So you'll want to disable this with a custom header: + +.. code-block:: php + header('X-Accel-Buffering: no'); + # or with the Response class from Symfony + $response->headers->set('X-Accel-Buffering', 'no'); + +Alternatively, you can tweak the +`fastcgi cache parameters `_ +within nginx config. + +See `original issue `_. \ No newline at end of file diff --git a/vendor/composer/vendor/maennchen/zipstream-php/guides/Options.rst b/vendor/composer/vendor/maennchen/zipstream-php/guides/Options.rst new file mode 100644 index 0000000..5e92e94 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/guides/Options.rst @@ -0,0 +1,66 @@ +Available options +=============== + +Here is the full list of options available to you. You can also have a look at +``src/ZipStream.php`` file. + +.. code-block:: php + + use ZipStream\ZipStream; + + require_once 'vendor/autoload.php'; + + $zip = new ZipStream( + // Define output stream + // (argument is eiter a resource or implementing + // `Psr\Http\Message\StreamInterface`) + // + // Setup with `psr/http-message` & `guzzlehttp/psr7` dependencies + // required when using `Psr\Http\Message\StreamInterface`. + outputStream: $filePointer, + + // Set the deflate level (default is 6; use -1 to disable it) + defaultDeflateLevel: 6, + + // Add a comment to the zip file + comment: 'This is a comment.', + + // Send http headers (default is true) + sendHttpHeaders: false, + + // HTTP Content-Disposition. + // Defaults to 'attachment', where FILENAME is the specified filename. + // Note that this does nothing if you are not sending HTTP headers. + contentDisposition: 'attachment', + + // Output Name for HTTP Content-Disposition + // Defaults to no name + outputName: "example.zip", + + // HTTP Content-Type. + // Defaults to 'application/x-zip'. + // Note that this does nothing if you are not sending HTTP headers. + contentType: 'application/x-zip', + + // Set the function called for setting headers. + // Default is the `header()` of PHP + httpHeaderCallback: header(...), + + // Enable streaming files with single read where general purpose bit 3 + // indicates local file header contain zero values in crc and size + // fields, these appear only after file contents in data descriptor + // block. + // Set to true if your input stream is remote + // (used with addFileFromStream()). + // Default is false. + defaultEnableZeroHeader: false, + + // Enable zip64 extension, allowing very large archives + // (> 4Gb or file count > 64k) + // Default is true + enableZip64: true, + + // Flush output buffer after every write + // Default is false + flushOutput: true, + ); diff --git a/vendor/composer/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst b/vendor/composer/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst new file mode 100644 index 0000000..22af71d --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst @@ -0,0 +1,21 @@ +Usage with PSR 7 Streams +=============== + +PSR-7 streams are `standardized streams `_. + +ZipStream-PHP supports working with these streams with the function +``addFileFromPsr7Stream``. + +For all parameters of the function see the API documentation. + +Example +--------------- + +.. code-block:: php + + $stream = $response->getBody(); + // add a file named 'streamfile.txt' from the content of the stream + $zip->addFileFromPsr7Stream( + fileName: 'streamfile.txt', + stream: $stream, + ); diff --git a/vendor/composer/vendor/maennchen/zipstream-php/guides/StreamOutput.rst b/vendor/composer/vendor/maennchen/zipstream-php/guides/StreamOutput.rst new file mode 100644 index 0000000..9f3165b --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/guides/StreamOutput.rst @@ -0,0 +1,39 @@ +Stream Output +=============== + +Stream to S3 Bucket +--------------- + +.. code-block:: php + + use Aws\S3\S3Client; + use Aws\Credentials\CredentialProvider; + use ZipStream\ZipStream; + + $bucket = 'your bucket name'; + $client = new S3Client([ + 'region' => 'your region', + 'version' => 'latest', + 'bucketName' => $bucket, + 'credentials' => CredentialProvider::defaultProvider(), + ]); + $client->registerStreamWrapper(); + + $zipFile = fopen("s3://$bucket/example.zip", 'w'); + + $zip = new ZipStream( + enableZip64: false, + outputStream: $zipFile, + ); + + $zip->addFile( + fileName: 'file1.txt', + data: 'File1 data', + ); + $zip->addFile( + fileName: 'file2.txt', + data: 'File2 data', + ); + $zip->finish(); + + fclose($zipFile); diff --git a/vendor/composer/vendor/maennchen/zipstream-php/guides/Symfony.rst b/vendor/composer/vendor/maennchen/zipstream-php/guides/Symfony.rst new file mode 100644 index 0000000..902552c --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/guides/Symfony.rst @@ -0,0 +1,130 @@ +Usage with Symfony +=============== + +Overview for using ZipStream in Symfony +-------- + +Using ZipStream in Symfony requires use of Symfony's ``StreamedResponse`` when +used in controller actions. + +Wrap your call to the relevant ``ZipStream`` stream method (i.e. ``addFile``, +``addFileFromPath``, ``addFileFromStream``) in Symfony's ``StreamedResponse`` +function passing in any required arguments for your use case. + +Using Symfony's ``StreamedResponse`` will allow Symfony to stream output from +ZipStream correctly to users' browsers and avoid a corrupted final zip landing +on the users' end. + +Example for using ``ZipStream`` in a controller action to zip stream files +stored in an AWS S3 bucket by key: + +.. code-block:: php + + use Symfony\Component\HttpFoundation\StreamedResponse; + use Aws\S3\S3Client; + use ZipStream; + + //... + + /** + * @Route("/zipstream", name="zipstream") + */ + public function zipStreamAction() + { + // sample test file on s3 + $s3keys = array( + "ziptestfolder/file1.txt" + ); + + $s3Client = $this->get('app.amazon.s3'); //s3client service + $s3Client->registerStreamWrapper(); //required + + // using StreamedResponse to wrap ZipStream functionality + // for files on AWS s3. + $response = new StreamedResponse(function() use($s3keys, $s3Client) + { + // Define suitable options for ZipStream Archive. + // this is needed to prevent issues with truncated zip files + //initialise zipstream with output zip filename and options. + $zip = new ZipStream\ZipStream( + outputName: 'test.zip', + defaultEnableZeroHeader: true, + contentType: 'application/octet-stream', + ); + + //loop keys - useful for multiple files + foreach ($s3keys as $key) { + // Get the file name in S3 key so we can save it to the zip + //file using the same name. + $fileName = basename($key); + + // concatenate s3path. + // replace with your bucket name or get from parameters file. + $bucket = 'bucketname'; + $s3path = "s3://" . $bucket . "/" . $key; + + //addFileFromStream + if ($streamRead = fopen($s3path, 'r')) { + $zip->addFileFromStream( + fileName: $fileName, + stream: $streamRead, + ); + } else { + die('Could not open stream for reading'); + } + } + + $zip->finish(); + + }); + + return $response; + } + +In the above example, files on AWS S3 are being streamed from S3 to the Symfon +application via ``fopen`` call when the s3Client has ``registerStreamWrapper`` +applied. This stream is then passed to ``ZipStream`` via the +``addFileFromStream`` function, which ZipStream then streams as a zip to the +client browser via Symfony's ``StreamedResponse``. No Zip is created server +side, which makes this approach a more efficient solution for streaming zips to +the client browser especially for larger files. + +For the above use case you will need to have installed +`aws/aws-sdk-php-symfony `_ to +support accessing S3 objects in your Symfony web application. This is not +required for locally stored files on you server you intend to stream via +``ZipStream``. + +See official Symfony documentation for details on +`Symfony's StreamedResponse `_ +``Symfony\Component\HttpFoundation\StreamedResponse``. + +Note from `S3 documentation `_: + + Streams opened in "r" mode only allow data to be read from the stream, and + are not seekable by default. This is so that data can be downloaded from + Amazon S3 in a truly streaming manner, where previously read bytes do not + need to be buffered into memory. If you need a stream to be seekable, you + can pass seekable into the stream context options of a function. + +Make sure to configure your S3 context correctly! + +Uploading a file +-------- + +You need to add correct permissions +(see `#120 `_) + +**example code** + + +.. code-block:: php + + $path = "s3://{$adapter->getBucket()}/{$this->getArchivePath()}"; + + // the important bit + $outputContext = stream_context_create([ + 's3' => ['ACL' => 'public-read'], + ]); + + fopen($path, 'w', null, $outputContext); diff --git a/vendor/composer/vendor/maennchen/zipstream-php/guides/Varnish.rst b/vendor/composer/vendor/maennchen/zipstream-php/guides/Varnish.rst new file mode 100644 index 0000000..952d287 --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/guides/Varnish.rst @@ -0,0 +1,22 @@ +Usage with Varnish +============= + +Serving a big zip with varnish in between can cause random stream close. +This can be solved by adding attached code to the vcl file. + +To avoid the problem, add the following to your varnish config file: + +.. code-block:: + sub vcl_recv { + # Varnish can’t intercept the discussion anymore + # helps for streaming big zips + if (req.url ~ "\.(tar|gz|zip|7z|exe)$") { + return (pipe); + } + } + # Varnish can’t intercept the discussion anymore + # helps for streaming big zips + sub vcl_pipe { + set bereq.http.connection = "close"; + return (pipe); + } diff --git a/vendor/composer/vendor/maennchen/zipstream-php/guides/index.rst b/vendor/composer/vendor/maennchen/zipstream-php/guides/index.rst new file mode 100644 index 0000000..48f465a --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/guides/index.rst @@ -0,0 +1,126 @@ +ZipStream PHP +============= + +A fast and simple streaming zip file downloader for PHP. Using this library will +save you from having to write the Zip to disk. You can directly send it to the +user, which is much faster. It can work with S3 buckets or any PSR7 Stream. + +.. toctree:: + + index + Symfony + Options + StreamOutput + FlySystem + PSR7Streams + Nginx + Varnish + ContentLength + +Installation +--------------- + +Simply add a dependency on ``maennchen/zipstream-php`` to your project's +``composer.json`` file if you use Composer to manage the dependencies of your +project. Use following command to add the package to your project's +dependencies: + +.. code-block:: sh + composer require maennchen/zipstream-php + +If you want to use``addFileFromPsr7Stream``` +(``Psr\Http\Message\StreamInterface``) or use a stream instead of a +``resource`` as ``outputStream``, the following dependencies must be installed +as well: + +.. code-block:: sh + composer require psr/http-message guzzlehttp/psr7 + +If ``composer install`` yields the following error, your installation is missing +the `mbstring extension `_, +either `install it `_ +or run the follwoing command: + +.. code-block:: + Your requirements could not be resolved to an installable set of packages. + + Problem 1 + - Root composer.json requires PHP extension ext-mbstring * but it is + missing from your system. Install or enable PHP's mbstrings extension. + +.. code-block:: sh + composer require symfony/polyfill-mbstring + +Usage Intro +--------------- + +Here's a simple example: + +.. code-block:: php + + // Autoload the dependencies + require 'vendor/autoload.php'; + + // create a new zipstream object + $zip = new ZipStream\ZipStream( + outputName: 'example.zip', + + // enable output of HTTP headers + sendHttpHeaders: true, + ); + + // create a file named 'hello.txt' + $zip->addFile( + fileName: 'hello.txt', + data: 'This is the contents of hello.txt', + ); + + // add a file named 'some_image.jpg' from a local file 'path/to/image.jpg' + $zip->addFileFromPath( + fileName: 'some_image.jpg', + path: 'path/to/image.jpg', + ); + + // add a file named 'goodbye.txt' from an open stream resource + $filePointer = tmpfile(); + fwrite($filePointer, 'The quick brown fox jumped over the lazy dog.'); + rewind($filePointer); + $zip->addFileFromStream( + fileName: 'goodbye.txt', + stream: $filePointer, + ); + fclose($filePointer); + + // add a file named 'streamfile.txt' from the body of a `guzzle` response + // Setup with `psr/http-message` & `guzzlehttp/psr7` dependencies required. + $zip->addFileFromPsr7Stream( + fileName: 'streamfile.txt', + stream: $response->getBody(), + ); + + // finish the zip stream + $zip->finish(); + +You can also add comments, modify file timestamps, and customize (or +disable) the HTTP headers. It is also possible to specify the storage method +when adding files, the current default storage method is ``DEFLATE`` +i.e files are stored with Compression mode 0x08. + +Known Issues +--------------- + +The native Mac OS archive extraction tool prior to macOS 10.15 might not open +archives in some conditions. A workaround is to disable the Zip64 feature with +the option ``enableZip64: false``. This limits the archive to 4 Gb and 64k files +but will allow users on macOS 10.14 and below to open them without issue. +See `#116 `_. + +The linux ``unzip`` utility might not handle properly unicode characters. +It is recommended to extract with another tool like +`7-zip `_. +See `#146 `_. + +It is the responsability of the client code to make sure that files are not +saved with the same path, as it is not possible for the library to figure it out +while streaming a zip. +See `#154 `_. diff --git a/vendor/composer/vendor/maennchen/zipstream-php/phpdoc.dist.xml b/vendor/composer/vendor/maennchen/zipstream-php/phpdoc.dist.xml new file mode 100644 index 0000000..b98fe1c --- /dev/null +++ b/vendor/composer/vendor/maennchen/zipstream-php/phpdoc.dist.xml @@ -0,0 +1,39 @@ + + + 💾 ZipStream-PHP + + docs + + + latest + + + src + + api + + + php + + public + ZipStream + true + + + + guides + + guide + + + +