✅ WEB- und WordPress-Nachrichten, Themen, Plugins. Hier teilen wir Tipps und beste Website-Lösungen.

Namespaces und automatisches Laden in WordPress

7

Letzte Woche habe ich meinen Vortrag auf dem WordCamp Atlanta über Namespaces und Autoloading gehalten. (Der vollständige Titel lautete Namespaces, Autoloading und Improving Plugin Architecture, aber das ist ein Bissen, nicht wahr?)

Aufgrund der Art des Vortrags habe ich mich entschieden, einen Beitrag zu schreiben, um den Beitrag zu begleiten, die Folien zu teilen und ein Beispiel-Plugin auf GitHub zu teilen, um den Vortrag zu unterstützen.

Also, wenn Sie anwesend waren, danke (!) und hier ist der Beitrag, den ich versprochen habe. Und für diejenigen unter Ihnen, die nicht teilgenommen haben, hoffe ich, dass dieser Beitrag trotzdem hilft, die Konzepte und Themen zu demonstrieren, die ich auf dem WordCamp besprochen habe.

Namensräume und automatisches Laden

Bevor ich über Namespaces und automatisches Laden in WordPress spreche, möchte ich darüber sprechen, weil es die Qualität Ihres Codes direkt beeinflussen kann und dies über Monate und Jahre hinweg tun kann.

Schließlich stellen sich nicht viele von uns Entwicklern bereits die Frage:

Wie können wir unseren Code besser machen, als er bereits ist?

Und viele von uns sind schlau genug, um zu wissen, was wir nicht wissen. Wir sind also damit konfrontiert, innerhalb der Einschränkungen zu arbeiten, die uns gegeben sind.

Manchmal haben wir Zeit, nach Möglichkeiten zu suchen, dies zu tun; In anderen Fällen müssen wir mit dem Wissen arbeiten, das wir haben. Und daran ist nichts auszusetzen.

Aber weil wir wissen, was wir nicht wissen, wissen wir, dass es Potenzial für mehr gibt.

Zuerst Ihr Code

Wenn es um Themen wie Namespaces und Autoloading im Zusammenhang mit WordPress geht, stoßen wir meines Erachtens oft auf gemischte Reaktionen.

Schließlich könnten wir über Dinge wie The Theme Customizer oder die REST-API oder etwas Lustigeres sprechen.

Ich meine, „Namespaces und automatisches Laden” klingt einfach nicht aufregend oder zukunftsweisend, wenn man es mit den neueren Funktionen und Technologien vergleicht, die verfügbar sind, oder?

Aber nein, sie sind nicht wirklich langweilig. Und durch diesen Beitrag und die begleitende Präsentation und den Quellcode werde ich Ihnen zeigen, warum und wie nicht.

Sie sind nicht langweilig

Ich denke, es ist fair zu sagen, dass Entwickler – zumindest ein Teil von uns oder ein Teil von ihnen, je nachdem, wie Sie sich selbst sehen – berüchtigt dafür sind, über Aspekte der Programmierung zu streiten.

„Trotzdem langweiliges Gespräch.”

Tatsächlich ist es nicht ungewöhnlich, jemanden darüber streiten zu hören, wie man eine for-Schleife am besten initialisiert und schreibt, die so leistungsfähig wie möglich ist, wenn man über einen kleinen Satz von Datenbanken iteriert, obwohl wir Quad-Core-Prozessoren und 16 GB RAM haben in unseren Desktop-Maschinen.

Wenn uns also etwas so Kleines so wichtig ist, dann interessiert uns sicherlich das Gesamtbild. Dinge wie:

  • Verbesserter Code
  • Bessere Organisation
  • Erhöhte Wartbarkeit
  • Einfacheres Debuggen
  • Mehr Geld verdienen (na ja, vielleicht).

Und Namespaces und Autoloading können direkt zu all dem führen (gut, ich kann nicht über Geld sprechen, aber es hat Potenzial).

Wenn ich die Rollen-Namespaces und den Ort zum automatischen Laden in all dem oben Genannten zusammenfassen würde, würde ich Folgendes sagen:

Namespaces und Autoloading führen zu verbessertem Code durch bessere Organisation, Unterteilung oder Modularisierung und engere Beziehungen durch ihre Konzepte.

Darüber hinaus erhöht dies die Wartbarkeit, da der Code in Paketen organisiert ist, was zu einer einfacheren Fehlersuche führen kann, wenn das Produkt wächst.

All dies kann zu Zeiteinsparungen oder einer besseren Zeitnutzung führen, was sich je nach Geschäftsmodell auf Ihr Endergebnis auswirken kann.

Aber das beschreibt noch nicht beides. Aber sicherlich klingen sie an diesem Punkt interessanter als bei ihrer ersten Einführung.

Also, was sind sie?

Bevor wir uns mit den Definitionen der einzelnen und den Rollen, die sie spielen, befassen, werfen wir einen Blick darauf, wie sich das Fehlen von Namespaces und das automatische Laden in WordPress negativ auf Ihre Erfahrung bei der Verwendung von Themen, Plugins, Add-Ons oder was auch immer Sie haben ausgewirkt hat.

Lassen Sie uns also einen Moment zurückgehen und jeden einzeln betrachten.

Namensräume

Stellen Sie sich vor, Sie haben ein Projekt geerbt und beginnen gleich mit der Arbeit daran. Angenommen, es ist ein WordPress-Plugin.

Sie installieren es; Sie gehen, um es zu aktivieren, und dann erhalten Sie mindestens eines davon:

  • Vielleicht sehen Sie diese unangenehme Organisationsnachricht oben im Browserfenster, die einen Stack-Trace anzeigt.
  • Möglicherweise sehen Sie eine Meldung, die auf einen Konflikt mit einem anderen vorhandenen Paket hinweist.
  • Oder vielleicht, wenn Sie versuchen, ein Plug-in zu aktivieren, wird die Seite aktualisiert, aber das Plug-in wird nicht aktiviert.
  • Vielleicht haben Sie sogar ein Code-Audit durchgeführt, und Sie sehen class_exists-Prüfungen in der gesamten Codebasis.

Einige oder alle der oben genannten Punkte können natürlich zu Problemen mit WordPress-Projekten beitragen. Aber Namespaces können die meiste Zeit wirklich viel davon beheben.

Das heißt, der Grund für diese Probleme ist, dass der Code, mit dem Sie arbeiten, Teil des globalen Namensraums ist (im Gegensatz zu seinem Namensraum) und PHP es nicht mag, wenn Klassen und Module denselben Namen haben .

Aber wenn Sie etwas benennen, geben Sie ihm seinen Bereich relativ zu sich selbst, der immer noch gut mit anderen Komponenten zusammenspielt, selbst wenn sie denselben Klassennamen haben.

Autoloader

Autoloader sind in mancher Hinsicht etwas weniger kompliziert. Denken Sie zuerst an den Code, den Sie schreiben oder den Code, mit dem Sie arbeiten – insbesondere im Kontext von WordPress-Plugins – und denken Sie dann darüber nach, wie oft Sie Folgendes schreiben oder sehen:

Und manchmal sehen Sie sie oben in der Datei, die das Plugin startet, und manchmal sehen Sie sie in der gesamten Codebasis verstreut.

Wenn sie alle in einer einzigen Datei enthalten sind, ist das nicht so schlimm, weil Sie zumindest wissen, wo sie sich befinden. Aber wenn sie überall verstreut sind, dann haben Sie keine Ahnung, wo eine Abhängigkeit in das System gebracht wird.

Das automatische Laden kann all dies lösen, indem die Abhängigkeiten bei Bedarf geladen werden (und für diejenigen, die daran interessiert sind, ist das automatische Laden schneller als das manuelle Einfügen).

Namensräume

Nach all dem sind wir bereit, über Namespaces und Autoloading zu sprechen. Aber Namensräume sind das grundlegende Konzept, also fangen wir dort an.

Aber nach all dem oben Genannten können Sie die Vorteile ihrer Verwendung erkennen. Vielleicht machen sie sogar Spaß, oder? Vielleicht?

Unabhängig davon brauchen wir eine Definition, an der wir arbeiten können, wenn wir dies für den Rest des Artikels durchsprechen.

Das PHP-Handbuch liefert die folgende Definition :

Namespaces sollen zwei Probleme lösen, auf die Autoren von Bibliotheken und Anwendungen stoßen, wenn sie wiederverwendbare Codeelemente wie Klassen oder Funktionen erstellen…

Und das ist nicht schlecht, aber es ist ziemlich lang, technisch und es könnte ein bisschen viel für diejenigen sein, die gerade erst anfangen. Vereinfachen wir es also für diesen Artikel ein wenig:

Etwas besser vielleicht?

Eine Möglichkeit, verwandte Klassen und Schnittstellen mit ähnlichem Zweck zu gruppieren.

Ich werde in diesem Vortrag nicht über Schnittstellen sprechen; Ich weiß jedoch, dass es objektorientierte Entwickler auf mittlerem Niveau gibt, die sie verwenden, also wollte ich sicherstellen, dass ich sie zumindest erwähne.

Ein praktisches Beispiel

Ich mag keine Programmierbeispiele, die keine realen oder praktischen Anwendungen geben. Oft werden uns Beispiele für Dinge gegeben, die wir nie kodifizieren würden.

Wie oft haben Sie einen objektorientierten Artikel gelesen und er enthält ein Beispiel für eine Tierklasse oder eine Autoklasse? Wir werden kein Auto programmieren.

Wir arbeiten viel eher mit Dateien. Wir werden uns also eine Reihe von Klassen ansehen, die für das Lesen und Schreiben von Dateien verantwortlich sind. Das heißt, wir waren gute objektorientierte Programmierer und haben unsere Klassen nach den Verantwortlichkeiten getrennt, die sie haben.

Und ja, Sie haben vielleicht Schnittstellen; Sie gehen jedoch über den Rahmen dieses Artikels hinaus und werden daher nicht aufgenommen.

Für unseren FileReader sehen die Grundlagen der Klasse also vielleicht so aus:

Namespaces und automatisches Laden in WordPress

Eine Klasse zum Lesen von Dateien.

Beachten Sie, dass die Funktion den Namen der Datei akzeptiert, die sie zum Lesen öffnen soll. Die Fehlerprüfung, wie es die Datei liest und was es zurückgibt, bleibt der Implementierung der Klasse überlassen.

Und für den FileWriter haben wir so etwas:

Namespaces und automatisches Laden in WordPress

Eine Klasse zum Schreiben von Dateien.

Andererseits akzeptiert diese Klasse die Informationen, die sie auf die Festplatte schreiben wird, und den Namen der Datei, in die sie geschrieben werden sollen.

Auch hier sind, wie im obigen Beispiel, keine Fehlerprüfungen, das Schreiben über eine Ressource, das Schließen der Ressourcen usw. enthalten.

Aber hier geht es nicht um die Arbeit mit Dateien. Stattdessen geht es darum, zu zeigen, wie Sie Ihren Code benennen, und diese beiden Beispiele sollen die Grundlage dafür bilden.

Hinweise zu Namensräumen

Es gibt einen Vorbehalt zu dem, was Sie in den Bildern des Beispielcodes oben sehen: Diese Klassen haben keinen Namensraum. Das heißt, sie befinden sich im globalen Namensraum, was sie reif für Konflikte mit anderen Klassen macht.

Betrachten Sie es so: Stellen Sie sich vor, Sie packen diesen Code in ein Plugin für jemand anderen, und dieser lädt dann ein anderes Plugin, das auch einen FileReader oder einen FileWriter hat. Da alles Teil desselben globalen Namensraums sein wird, werden Sie mit einem Konflikt konfrontiert.

Denken Sie daran:

Namensräume sind eine Möglichkeit, verwandte Klassen und Schnittstellen mit ähnlichem Zweck zu gruppieren.

Nehmen wir also die Klassen und benennen den Code.

Zuerst stellen wir einen Namensraum der obersten Ebene bereit, unter dem diese Klassen und alle anderen Klassen liegen werden; dann stellen wir ein Unterpaket (oder Unternamensraum oder Unterraum, wie ich sie genannt habe) bereit, in dem sich diese Dateiklassen befinden werden.

Das bedeutet, dass unser FileReader jetzt so aussieht:

Namespaces und automatisches Laden in WordPress

Eine Namespace-Klasse zum Lesen von Dateien.

Und unser FileWriter sieht nun so aus:

Namespaces und automatisches Laden in WordPress

Eine Namespace-Klasse zum Schreiben von Dateien.

Zunächst sehen Sie, dass die Verwendung von Namensräumen einfach ist: Sie verwenden einfach das Schlüsselwort namespace und deklarieren dann den Namensraum (der genauso gut WCATL sein könnte) ganz oben mit den folgenden Unterpaketen.

Dies führt jedoch zu anderen Themen – nämlich rund um die Dateiorganisation, Instanziierung und das automatische Laden – die es alle wert sind, behandelt zu werden.

Zur Dateiorganisation

An dieser Stelle ist es wichtig, ein Wort zur Dateiorganisation zu sagen. Je nachdem, mit wem Sie sprechen, werden Sie feststellen, dass einige Entwickler – Überraschung, Überraschung – eine Meinung darüber haben, wie Dateien organisiert werden sollten (und ich bin nicht anders).

Einerseits müssen Sie Ihre Dateien überhaupt nicht organisieren. Tatsächlich können Sie alles im Stammverzeichnis Ihres Projekts ablegen, die Informationen benennen und loslegen.

Namespaces und automatisches Laden in WordPress

Unorganisierte Dateien

Der obige Satz von Dateien ist für ein kleines Projekt, Sie können sich also vorstellen, wie viele Dateien für ein großes Projekt vorhanden wären.

Aber wenn Sie Ihre Dateien so organisiert haben, kann es etwas schwierig werden, einen Autoloader zu schreiben, weil ein Autoloader wissen muss, wo er die Dateien basierend auf ihrem Namensraum finden kann.

Hier kommen die Begriffe „logische Organisation” und „virtuelle Organisation” ins Spiel.

  • Die logische Organisation bezieht sich darauf, wie die Dateien auf der Festplatte organisiert sind, wie Sie es oben sehen. Sie befinden sich logischerweise im Root-Verzeichnis.
  • Virtuelle Organisation bezieht sich darauf, wie die Dateien in Bezug auf ihre Namespaces organisiert sind. Das bedeutet, dass es Verzeichnisse und Unterverzeichnisse gibt, die den Namensräumen, Unterpaketen usw. zugeordnet sind.

Wenn Sie also das obige Projekt, seine Namensräume, seine Unterpakete nehmen und sie dann sowohl logisch als auch virtuell organisieren würden, würde es ungefähr so ​​​​aussehen:

Namespaces und automatisches Laden in WordPress

Namespaces und automatisches Laden: Organisierte Dateien

Und obwohl Sie sich dafür entscheiden können, Ihre Dateien nach Belieben zu organisieren, bin ich ein Fan davon, sicherzustellen, dass es Parität zwischen den beiden gibt. Das heißt, ich möchte, dass meine logische und meine virtuelle Organisation übereinstimmen, wie Sie im Bild oben sehen.

Wenn ich mich mit dem automatischen Laden befasse, werden Sie sehen, warum dies wichtig ist.

Hinweise zu Namensräumen

Was passiert jedoch, wenn wir Klassen mit Namensräumen instanziieren müssen? Wenn Klassen keinen Namensraum haben, ist es einfach, das Schlüsselwort „new” zu verwenden.

Namespaces und automatisches Laden in WordPress

Instanziierung ohne Namespace.

Aber wir müssen eine Namespace-Klasse instanziieren, wir müssen noch einen Schritt weiter gehen und den vollständig qualifizierten Namen verwenden:

Namespaces und automatisches Laden in WordPress

Namespaces und Autoloading: Instanziierung mit einem Namespace.

Aber das wird umständlich, nicht wahr? Dieses spezielle Beispiel ist nicht so schlimm, aber stellen Sie sich vor, Sie würden an etwas mit mehr Unterpaketen arbeiten. Das würde ziemlich umständlich werden, oder?

Zu diesem Zweck können wir das sogenannte Aliasing verwenden. Es ist auch einfach. Wir können definieren, dass das Schlüsselwort „use” oben in der Datei verwendet wird, um auf den Namensraum zu verweisen, den wir als Alias ​​verwenden möchten, und dann das letzte Unterpaket als Teil des Alias ​​verwenden, um unsere Klasse zu instanziieren.

Klingt verwirrend, oder? Vielleicht hilft es, es in Aktion zu sehen:

Namespaces und automatisches Laden in WordPress

Aliasing-Namespaces.

Und das ist alles, was dazu gehört. Ja, Sie können beim Aliasing noch einen Schritt weiter gehen, aber damit gehe ich im Rahmen dieses Artikels nicht weiter.

Automatisches Laden

An dieser Stelle haben wir den Grundstein für das Autoloading gelegt. Ja, das Durcharbeiten von Namespaces kann eine Menge Arbeit sein, wenn Sie nicht daran gewöhnt sind; Es ist jedoch wichtig zu verstehen, da das automatische Laden ein wenig Arbeit erfordert, die unerwartet sein kann, wenn Sie noch nie damit vertraut gemacht wurden.

Unabhängig davon sind die wichtigsten Dinge, die Sie an dieser Stelle in Bezug auf Namespaces beachten sollten, folgende:

  1. Namensräume sind eine Möglichkeit, verwandte Klassen und Schnittstellen mit ähnlichem Zweck zu gruppieren.
  2. Erstellen Sie Parität durch Ihre Dateien und Namespaces, indem Sie sicherstellen, dass Ihre logische und virtuelle Organisation gleich sind.

Und jetzt ist es an der Zeit, sich mit Autoloading zu befassen.

Hinweise zum automatischen Laden

Schauen wir uns zunächst die Definition des automatischen Ladens an, wie sie im PHP-Handbuch bereitgestellt wird :

Die Funktion spl_autoload_register() registriert eine beliebige Anzahl von Autoloadern, sodass Klassen und Schnittstellen automatisch geladen werden können, wenn sie derzeit nicht definiert sind. Durch die Registrierung von Autoloadern erhält PHP eine letzte Chance, die Klasse oder Schnittstelle zu laden, bevor es mit einem Fehler fehlschlägt.

Es ist nicht schlecht. Es ist aber langwierig. Lassen Sie uns also, genau wie wir es bei Namespaces getan haben, eine kürzere Definition für diesen Artikel verwenden:

Eine Möglichkeit, Schnittstellen und Klassen automatisch zu laden, ohne Include- und Require-Anweisungen zu verwenden.

Auch hier werden wir in diesem Artikel keine Schnittstellen verwenden, obwohl einige Entwickler dies tun. Und das wird die Arbeitsdefinition für den Rest dieses Artikels liefern.

Ein praktisches Beispiel

Sobald Sie Ihre Dateien organisiert, benannt und geladen haben, ist es an der Zeit, genau das zu tun, richtig? Ich meine:

  1. Ihre Dateien sind organisiert,
  2. Sie sind bereit, sie zu laden

Es ist also an der Zeit, es automatisch zu tun, richtig? Aber da ist ein Fang. Das ganze „automatische Laden” von Dateien erfordert ein wenig Arbeit.

Schreiben eines Autoloaders

Das heißt, es ist automatisch, aber es erfordert noch etwas mehr Arbeit von unserer Seite. Bevor Sie mit diesen Schritten beginnen, ist es wichtig zu beachten:

  1. Es ist nicht vollständig automatisiert,
  2. wir müssen es schreiben.

So schön es auch wäre, den Code automatisch laden zu lassen, wir müssen einige Daten lesen, parsen und dann versuchen, die richtige Datei zu laden.

Aber vorausgesetzt, Sie schreiben es richtig und Ihren Namensraum und organisieren Ihre Dateien für jedes Projekt auf die gleiche Weise, können Sie Ihren Autoloader wiederverwenden. Das heißt, Sie schreiben es einmal und können es in anderen Projekten verwenden.

Schritte für einen Autoloader

Beim Schreiben eines Autoloaders sind nur wenige Schritte zu befolgen. Der Autoloader muss folgende Dateien beantworten können:

  1. Wo sind die Dateien?
  2. Wie heißen sie?
  3. Existiert die Datei?

Wenn alle oben genannten Punkte zutreffen (oder Sie alle mit „Ja” beantworten können), wird der Autoloader das tun, was er tun soll.

Wir werden uns gleich etwas Code ansehen, aber das Erste, was zu beachten ist, ist, dass dieser eine Funktion namens spl_autoload_register verwendet.

SPL bezieht sich auf die Standard-PHP-Bibliothek, und die Funktion akzeptiert eine Funktion als Argument, und diese Funktion akzeptiert den Namen der Klasse, die instanziiert werden soll. Es ist eher prozedural als objektorientiert, und ich werde gleich darauf eingehen, aber es ist wichtig, dass Sie beim Lesen dieses Codes daran denken.

Hier ist der erste Teil des Codes. Ich werde erklären, was es im Nachhinein tut:

Namespaces und automatisches Laden in WordPress

Autoloading, Teil 1 – Die Klasse finden

In diesem Teil des Codes erhält die Funktion den vollqualifizierten Namen der zu instanziierenden Klasse (z. B. „WCATLFileFileReader()”).

Als nächstes werden alle Teile des vollqualifizierten Namens in Teile zerlegt. Der Name der Klasse ist der letzte Index des Arrays, und ich entscheide mich dafür, meine Dateien als „class-filereader.php” zu benennen, damit die Funktion eine Variable, $class_file, erstellt, die auf den Namen der Datei verweist.

Aber wir sind noch nicht fertig. Wir müssen noch den vollständig qualifizierten Namen erhalten (dh wo sich die Datei auf der Festplatte befindet). Das könnte etwa so aussehen:

Namespaces und automatisches Laden in WordPress

Automatisches Laden, Teil 2 – Abrufen des vollständig qualifizierten Namens

An diesem Punkt bereiten wir eine Variable vor, $fully_qualified_path, die auf das Verzeichnis der obersten Ebene verweist.

Als nächstes durchläuft der Code alle Indizes des Arrays und erstellt einen Pfad für die Klassendatei. Sie können sich das so vorstellen, dass Sie einen String wie „wcatlfile” erstellen, den wir dann mit dem $class_file kombinieren.

Das bedeutet, dass der vollständig qualifizierte Pfad zu der Datei zu „wcatlfileclass-filereader.php” wird.

Und schließlich binden wir die Datei ein. Beachten Sie, dass dieser Code nicht überprüft, ob die Datei vorhanden ist. Obwohl ich es empfehle, wurde es aus Gründen der Länge weggelassen und weil wir in unserem Beispiel wissen, dass die Datei existiert.

Wenn die Datei nicht existiert, gibt es mehrere Möglichkeiten:

  1. Ausnahme auslösen,
  2. Ausnahme abfangen,
  3. Zeigen Sie eine eigene Fehlermeldung an,
  4. Oder eine andere Option, die ich in diesem Artikel in Betracht ziehen könnte.

Unabhängig davon sollten Sie in Ihrem Code defensiv sein, damit Sie sich auf den Fall vorbereiten können, dass eine Datei nicht vorhanden ist, und Sie den Fehler elegant behandeln können.

Beim automatischen Laden

Vor dem Abschluss ist es wichtig, Folgendes zu beachten:

  • Im gesamten Beispiel haben wir beim Namespacen des Codes objektorientierten Code verwendet. Schließlich handelt es sich um ein objektorientiertes Konzept.
  • Unser Autoloader ist in prozeduralem Code geschrieben. Was gibt?

Letztendlich hat das mit der Standard PHP Library zu tun. Sie können einen objektorientierten Autoloader schreiben, aber ich denke, das ist in vielen Fällen ein wenig übertrieben.

Der Prozess des Ladens einer Datei ist ein schrittweiser Prozess, daher ist das prozedurale Schreiben eine natürliche Anpassung.

Schließlich können sich andere dafür entscheiden, Tools wie Composer zu verwenden, um Abhängigkeiten einzubringen. Dies sind großartige Tools, und es gibt viele Vorteile, so etwas zu verwenden; Es geht jedoch über die Konzepte und Themen in diesem Artikel hinaus und sollte am besten für einen späteren Vortrag aufgehoben werden.

Ressourcen (und danke!)

Dies war einer der längsten Artikel, die ich für meine Website geschrieben habe.

Dies liegt zum Teil daran, dass es auf einem Vortrag für ein WordCamp basiert, und auch daran, dass ich sicherstellen möchte, dass ich eine solide Einführung und Grundlage biete, auf der Sie mit der Integration von Namespaces und dem automatischen Laden in Ihre WordPress-Plugins beginnen können.

Zusätzlich zu diesem Artikel habe ich auch die folgenden Ressourcen bereitgestellt:

Und damit hoffe ich, dass dies eine solide Einführung in Namespaces und Autoloading bietet und dass Sie damit beginnen können, dies mehr und mehr in Ihre Arbeit zu integrieren. Es kommt Ihrer Arbeit und anderen Entwicklern, die Ihre Arbeit möglicherweise ebenfalls verwenden, sehr zugute.

Aufnahmequelle: tommcfarlin.com

Diese Website verwendet Cookies, um Ihre Erfahrung zu verbessern. Wir gehen davon aus, dass Sie damit einverstanden sind, Sie können sich jedoch abmelden, wenn Sie möchten. Annehmen Weiterlesen