Große und komplexe Anwendungen mit Angular umsetzen

Angular-Anwendungen optimal dokumentieren
Keine Kommentare

An großen Softwareprojekten arbeiten in der Regel viele Entwickler in mehreren Teams über einen längeren Zeitraum. Daraus ergeben sich einige Herausforderungen, vor allem für die Angular-Dokumentation.

Es wird es immer wieder vorkommen, dass bestimmte Eigenschaften oder auch Eigenheiten einer Softwarelösung nicht allen Entwicklern in jeder Detailstufe bekannt sind. Auch können Entwickler zwischenzeitlich ausfallen oder neue Entwickler in ein Team aufgenommen werden, sei es durch Projektwechsel oder krankheitsbedingte Ausfälle. In all diesen Fällen ist es notwendig, dass Entwickler sich schnell und effizient in die bestehende Software einarbeiten können.

Auch wenn ein mit dem Projekt vertrauter Entwickler nach längerer Abwesenheitszeit wieder in ein Softwareprojekt einsteigt, wird er eine gewisse Zeit benötigen, um sich erneut in die Software einzufinden. Um andere Entwickler und auch sich selbst besser bei dieser (Wieder-)Einfindungsphase zu unterstützen, sollte daher jeder Entwickler parallel zur Software eine ausführliche Dokumentation anlegen und auch pflegen.

Sorgfältig dokumentieren

Eine aussagekräftige Dokumentation zeichnet sich in erster Linie dadurch aus, dass sie leicht auffindbar ist. Sie muss außerdem aktuell sein und zur jeweiligen Version der Software passen – schlimmer als keine Dokumentation zu besitzen, ist es, nur eine veraltete Dokumentation zur Verfügung zu haben. Der Entwicklungsprozess sollte daher neben dem Quellcode auch die zu erzeugende Dokumentation berücksichtigen. Das bedeutet einerseits, dass immer eine Liveversion der Dokumentation zur Entwicklungszeit in Form von Doc-Kommentaren zur Verfügung stehen sollte. So kann die Dokumentation von IDEs ausgewertet werden, um sie dem Entwickler zu gegebener Zeit anzuzeigen. Andererseits sollte eine Version der Dokumentation – sinnvollerweise die, die dem momentan in Produktion befindlichen Softwarestand entspricht – an zentraler Stelle, zum Beispiel in Form einer Webseite, bereitgestellt werden. In agilen Entwicklungsprozessen erweist es sich darüber hinaus als sinnvoll, die vollständige Dokumentation der Software in die „Definition of Done“ für einen Sprint aufzunehmen und gleichzeitig festzulegen, was „vollständig“ bedeutet. Softwaredokumentation lässt sich auf unterschiedliche Weise erzeugen, beispielsweise mithilfe von Wiki-Einträgen oder anderen externen Werkzeugen, oder als automatisch aus dem Code generiertes Dokument. Um die optimale Art von Dokumentation für ein Projekt zu bestimmen, sollte der Entwickler die gegebenen Rahmenbedingungen berücksichtigen.

In großen, agil vorgehenden Teams beispielsweise ändert sich der Code meist parallel auf verschiedenen Entwicklungszweigen. Entsprechend schnell und nebenläufig muss die Dokumentation hier aktualisierbar sein und den Zustand auf den verschiedenen Zweigen widerspiegeln. Mit Wiki-Systemen lässt sich das zum Beispiel nur schwer abbilden. Auch mit externen Tools erstellte Dokumente, etwa Word-Dokumente, schaffen hier keine Abhilfe. Allein das Zusammenführen von Dokumenten aus verschiedenen Entwicklungsständen oder Entwicklungszweigen ist in der Regel nur aufwendig von Hand, nicht jedoch automatisch, umsetzbar. Hier bietet sich daher eine Dokumentation nah am Artefakt, also der Software, an, deren Format das Versionsverwaltungssystem unterstützt. Eine Möglichkeit besteht darin, eine README-Datei anzulegen, die direkt mit dem Quellcode versioniert wird.

README-Datei

Eine README-Datei ist im Wesentlichen eine einfache Textdatei, die das Softwareprojekt und wichtige Informationen zur Installation und/oder Nutzung des Projekts kurz zusammenfasst. Außerdem kann der Entwickler darin zusätzliche (Meta-)Informationen ablegen, die von einer automatischen Dokumentation nicht miterfasst werden können. Dazu gehören etwa eine Projektbeschreibung, (grafische) Verwendungsbeispiele für das Projekt, Set-up-Schritte, Ansprechpartner oder Informationen, wie man sich am Projekt beteiligen kann. Heutzutage kommen in vielen Projekten oft einfache Auszeichnungssprachen wie Markdown zum Schreiben der README-Datei zum Einsatz. Der Vorteil besteht darin, dass sie dem Dokument eine einheitliche Struktur verleihen, die sowohl leicht zu schreiben und lesen als auch gut durch Programme und Versionsverwaltungen zu verarbeiten ist. Plattformen wie Bitbucket, GitHub oder GitLab können die README-Dateien dann verarbeitet und als eine Einstiegsdokumentation auf der Hauptseite des jeweiligen Projekts darstellen.

Automatische Dokumentation

Eine sinnvolle Option ist es, die Dokumentation automatisch aus dem Code generieren zu lassen. Auch wenn sich einfache Textformate wie Markdown oder Asciidoc ebenso leicht zusammenführen lassen wie Quellcode, bietet die automatische Erzeugung den Vorteil, dass die Dokumentation sich aus dem Quellcode selbst ergibt und sich damit zum Teil von selbst erzeugt. Auf diese Weise befindet sich die Dokumentation automatisch auf demselben Stand wie der jeweilige Quellcode. Weiterhin ist der Aufwand zur Erstellung und Pflege gering.

Automatisches Generieren von Dokumentationen unterteilt sich dabei meist in zwei Teilaspekte. Zum einen wird der Quellcode statisch analysiert, um grundlegende Informationen wie Rückgabetypen und Übergabeparameter daraus zu extrahieren. Zum anderen werden im Code vorhandene Doc-Kommentare analysiert und verarbeitet. Mithilfe dieser Kommentare lässt sich die Dokumentation zusätzliche anreichern.

Im JavaScript-Umfeld ist JSDoc ein weit verbreitetes Tool zur automatischen Dokumentationsgenerierung. Wer Dokumentation für ein Angular-Projekt erzeugt, sollte zudem die Besonderheiten im Hinterkopf behalten, die sich etwa bei Angular-Komponenten aus der Interaktion von JavaScript-Code und HTML-Markup im Browser ergeben.

JSDoc, ESDoc, TypeDoc

Um aus Quellcode automatisch Dokumentationen zu generieren, gibt es verschiedene (sprachspezifische) Werkzeuge. Je nachdem, welches Ziel bei einem Projekt verfolgt wird und welche Technologien dabei eingesetzt werden, sollte die Dokumentation mit einem entsprechenden Tool erzeugt werden. Arbeitet man beispielsweise an einer sehr browsernahen Bibliothek, so ist JSDoc eine sinnvolle Wahl.

Bei einer etwas allgemeineren Bibliothek, die möglicherweise auch schon aktuellere JavaScript-Sprachfeatures nutzt, ist ESDoc eine gute Wahl. Die Library RxJS für reaktive Programmierung mit JavaScript nutzt zum Beispiel ESDoc, um die Dokumentation zu generieren. Handelt es sich bei dem zu dokumentierenden Projekt um ein reines TypeScript-Projekt, nutzt man sinnvollerweise das dafür ausgelegte TypeDoc.

Compodoc

Compodoc ist ein Werkzeug, das zusätzlich zu den sprachlichen Eigenheiten von TypeScript die Besonderheiten von Angular berücksichtigt, beispielsweise die Angular-eigenen Dekoratoren. Somit kann Compodoc eine spezifische, auf Angular-Projekte angepasste Dokumentation generieren. Konkret werden beim Erzeugen der Dokumentation die Metadaten und Modulstruktur des Projekts mit Imports, Exports, Declarations, Providers usw. ausgewertet und diese Zusammenhänge anschließend in einer visuellen Darstellung ausgegeben. Eine Übersicht über die Compodoc-Syntax findet sich im Kasten „Überblick Compodoc-Syntax“.

Überblick Compodoc-Syntax

Compodoc nutzt den TypeScript-Parser, um die Dokumentation zu generieren. Daher sollten die Doc-Kommentare mit JSDoc-Syntax geschrieben werden, um zu Compodoc kompatibel zu sein. Die allgemeine Syntax eines JSDoc-Kommentars ist dabei /** * Supported comment */.

Tags

Tags können gewöhnliche Doc-Kommentare mit zusätzlichen Metainformationen anreichern. Mit dem Tag @returns kann beispielsweise der Rückgabetyp mit einer näheren Beschreibung oder kurzen Erklärung versehen werden. Da der TypeScript-Compiler bisher nicht alle JSDoc-Tags unterstützt, können mit Compodoc die Tags @returns, @param, @example und @link genutzt werden:

@returns wird zur genaueren semantischen Beschreibung des Rückgabewerts genutzt. Beispiel:

/**
 * Generates random values.
 * @returns An Array of random length, filled with random values
 */ 
generateRandomValues(): Array<number> {...}

@param wird zur genaueren semantischen Beschreibung eines Übergabeparameters genutzt. Das erste Argument nach @param ist dabei der Parametername, danach folgt die Beschreibung. Beispiel:

/**
 * Parses a string and returns an array of all
 * the numbers contained within the string
 * @param value The string to be analyzed
 * @returns An Array of all the numbers contained in value
 */
parseValue(value: string): Array<number> {...}

@example wird vor allem genutzt, um (Verwendungs-)Beispiele für Komponenten/Direktiven/Pipes geben zu können. Der Doc-Kommentar wird dafür vor den jeweiligen Decorator geschrieben. Das Tag lässt sich auch im Zusammenhang mit Class-Membern verwendet, um passende Beispiele anzugeben. Beispiel:

/**
 * @example * <app-visitor-list
 * [visitors]="visitors">
 * </app-visitor-list>
 */

Auch bei der Verwendung von Tags kann man auf die Markdown-Syntax zurückgreifen, etwa um Beispiele mit Codeausschnitten anzureichern. Die Markdown-Syntax für Codeblöcke sind drei einleitende Backticks („`), den Abschluss bilden ebenfalls drei Backticks, TypeScript gibt die Art des Syntax-Highlightings an. Beispiel mit Markdown:

/**
 * @example
 * ```typescript
 * let visitors = this.visitorService.getAll();
 * ```
 */

@link zum Erzeugen von Querverweisen sowohl innerhalb des Projekts als auch auf äußere Ressourcen. Zum Verlinken von Inhalten innerhalb des Projekts reicht es dabei, den Klassennamen der jeweiligen Komponente (oder Pipe, Direktive etc.) anzugeben. Sowohl für interne als auch externe Links stehen dabei jeweils drei verschiedene Syntaxen zur Verfügung, um die Links zu erzeugen. Beispiel für externe Links:

/** 
 *  External Links:
 *
 *  [Google]{@link http://www.google.com}
 *  {@link http://www.apple.com|Apple}
 *  {@link https://github.com GitHub}
 */

Beispiel für interne Links:

/**
 *  * Internal Links:
 * 
 *  {@link VisitorListComponent} 
 *  [VisitorList]{@link VisitorListComponent} 
 *  {@link SkillListComponent|VisitorList} 
 */

Die Installation in ein bestehendes Angular-Projekt ist dabei denkbar einfach. Mit npm i –save-dev @compodoc/compodoc wird die Abhängigkeit in die package.json eingetragen und das Compodoc-Kommando installiert. Nach der Installation ist es sinnvoll, die Compodoc-Befehle als npm-Skripte im jeweiligen Projekt zu hinterlegen und auch später in den Build-Prozess zu integrieren. Beispielsweise kann der Entwickler folgende Befehle in den scripts-Bereich der package.json des Projekts aufnehmen:

"scripts": { "docs": "compodoc -p src/tsconfig.app.json --hideGenerator", "docs:serve": "npm run docs:build -- -s", "docs:watch": "npm run docs:build -- -s -w", ... 
}

Der Befehl docs lässt nun Compodoc die Dokumentation generieren. Mit den Kommandozeilenargumenten -p src/tsconfig.app.json lässt sich Compodoc die Konfiguration mitgeben, die auch für den Bau des eigentlichen Angular-Projekts verwendet wird. Anhand dieser Konfiguration analysiert Compodoc das Projekt, um daraus die Dokumentation zu generieren. Der Parameter –hideGenerator blendet das Compodoc-Logo in der generierten Dokumentation aus. Die weiteren Skripte fügen Parameter hinzu, um die generierte Dokumentation per Liveserver zur Verfügung zu stellen (-s) und bei Änderungen der Quelldateien im Dateisystem direkt eine aktualisierte Version der Dokumentation generieren zu lassen (-w).

Mit der Option –theme [theme] lässt sich die optische Darstellung der generierten Dokumentation beeinflussen. Möglich sind die Optionen laravel, original, postmark, readthedocs, stripe, vagrant. Falls die Option nicht verwendet wird, ist der Default gitbook. Es ist auch möglich, ein eigenes Theme zu schreiben, das dann mit der Option –extTheme [file] angegeben werden kann. Im folgenden Beispiel wird das Default-Theme verwendet.

IT Security Camp 2021

Interview mit Christian Schneider zum Thema „DevSecOps“

DevSecOps ist, bezogen auf Security-Checks, die logische Fortführung der Automatisierung im DevOps-Sinne

IT-Security Experte, Christian Schneider

Christian ist als freiberuflicher Whitehat Hacker, Trainer und Security-Coach tätig. Als Softwareentwickler mit mittlerweile 20 Jahren Erfahrung, fand er 2005 seinen Themenschwerpunkt im Bereich IT-Security.

Die von Compodoc generierte Dokumentation teilt sich in mehrere Abschnitte auf, die durch das Navigationsmenü – im Default-Theme auf der linken Seite – erreichbar sind. Unter anderem ist dort, sofern vorhanden, die README-Datei des Projekts zu finden, die analog zu Plattformen wie GitHub als kurze Zusammenfassung des Projekts dargestellt wird. Neben der README-Datei existiert zudem noch der Menüpunkt Overview, der einen Überblick über das gesamte Projekt liefert. Zur schnellen Orientierung dient ein Graph, der die Modulstruktur der Software visualisiert (Abb. 1).

Abb. 1: Übersicht über die Modulstruktur

Abb. 1: Übersicht über die Modulstruktur

Documentation Coverage

Über das Hauptmenü sind die weiteren Bestandteile des Projekts erreichbar: (Sub-)Module, Komponenten, Direktiven, Services (Injectables), Pipes, Routen und allgemeine TypeScript-Klassen. Über den Unterpunkt DOCUMENTATION COVERAGE kann man sich die Abdeckung des Codes mit Dokumentation/Doc-Kommentaren ansehen. Dies kann eine sinnvolle Metrik für die „Definition of Done“ des Projekts liefern. Gleichzeitig ist die Coverage auch hilfreich, um in einem Projekt mit insgesamt hoher Dokumentationsabdeckung einzelne Klassen auszumachen, die noch wenig oder unzureichend dokumentiert sind. In der Übersicht wird eine hohe Dokumentationsabdeckung mit grün, eine geringe Dokumentationsabdeckung mit gelb, und eine unzureichende Dokumentationsabdeckung pro Datei mit rot gekennzeichnet.

Dokumentation von Modulen

Wählt der Entwickler im Navigationsmenü den Menüpunkt Module aus, erhält er eine grafische Übersicht der einzelnen Module, die auch die innere Struktur der Module visualisiert. Wie in Abbildung 2 zu sehen, werden die Bestandteile eines Moduls unterschiedlich farblich dargestellt: Module als solche in grün, Deklarationen (also Komponenten, Direktiven und Pipes) in gelb, Providers (z. B. Services) in orange und exportierte Bestandteile eines Moduls in rot. Einen Spezialfall verkörpern blau markierte Einträge. Bei ihnen handelt es sich um die Bootstrap-Komponenten, also die Root-Komponenten einer Anwendung.

Abb. 2: Detailliertere Übersicht über den (inneren) strukturellen Aufbau der Module

Abb. 2: Detailliertere Übersicht über den (inneren) strukturellen Aufbau der Module

Damit ist es möglich, sich für jedes Modul einen schnellen Überblick zu verschaffen, welche Komponenten, Direktiven und Pipes deklariert, welche anderen Module importiert, welche Services bereitgestellt und welche Teile des Moduls exportiert werden. Hilfreich ist dies vor allem dann, wenn der zugrunde liegende Quellcode gerade nicht zur Verfügung steht. Selbst wenn der Quellcode zugänglich ist, zum Beispiel über Plattformen wie GitHub, kann es sich als praktischer erweisen, sich der generierten Dokumentation zu bedienen, als sich durch eine umfangreiche Library durcharbeiten zu müssen, um die gesuchte Quelldatei zu finden.

Will der Entwickler aus der Modulübersicht ein einzelnes Modul auswählen, um es näher zu analysieren, gelangt er mit dem BROWSE-Knopf in eine Detailansicht des Moduls. Diese Ansicht enthält neben der Übersichtsgrafik die Beschreibung des Moduls, einen Link zu dessen Quellcode (zur Source-Ansicht später mehr) und Links zu Declarations, Providers, Imports und sonstigen Informationen des Moduls.

Dokumentation allgemeiner Klassen

Neben Modulen sind Services (Injectables) ein weiterer wichtiger Teil von Angular-Anwendungen. Sie bilden mit ihren Methoden die Businesslogik von Anwendungen ab. Die Dokumentation der Services und ihrer Methoden ist daher besonders wichtig. Ein Beispiel für die Dokumentation eines Service ist in Abbildung 3 zu sehen. Compodoc ermöglicht für alle TypeScript-Klassen eine Unterscheidung der Methoden in private , public und protected anhand von verschiedenen Symbolen. Statische Methoden werden auf diese Weise ebenso kenntlich gemacht.

Abb. 3: Dokumentation eines Service

Abb. 3: Dokumentation eines Service

Eine weitere Art von TypeScript-Klassen, die in Angular eine wichtige Rolle spielen, sind die sogenannten Pipes. Pipes nehmen Input-Werte aus einem Modellobjekt entgegen und verwandeln die Daten in einen formatierten Output-String. Der Output-String wird dann im Template angezeigt. In Abbildung 4 ist die Dokumentation für eine Pipe exemplarisch gezeigt. Betrachtet man die Beschreibung genau, so sieht man, dass Textstellen wie „Usage“ und „Syntax“ besonders hervorgehoben sind. Das wird durch eine besondere (Markdown-ähnliche) Syntax innerhalb der Doc-Kommentare der Klasse ermöglicht.

Abb. 4: Dokumentation einer Pipe mit formatiertem Text („Usage“, „Example“)

Abb. 4: Dokumentation einer Pipe mit formatiertem Text („Usage“, „Example“)

Quellcodeansicht

Wählt man in der oberen Menüleiste den Tab SOURCE aus, gelangt man in die Quellcodeansicht der Pipe (Abb. 5). In dieser Ansicht wird der komplette Quellcode für die entsprechende Datei angezeigt, sodass alle Einzelheiten bzw. alle Implementierungsdetails der jeweiligen Klasse einsehbar sind. Auch die Markdown-ähnliche Syntax, mit der die Doc-Kommentare formatiert werden können, ist hier dargestellt.

Abb. 5: Compodoc-Quellcodeansicht am Beispiel einer Angular-Pipe

Abb. 5: Compodoc-Quellcodeansicht am Beispiel einer Angular-Pipe

Eine andere wesentliche Methode, um Doc-Kommentare zu formatieren, ist die Verwendung von Tags. Mit Tags ist es möglich, detailliertere Kontextinformationen zur Dokumentation hinzuzufügen. Beispielsweise enthält die Dokumentation des Service aus Abbildung 3 einen Link zur Dokumentation der Klasse Todo. Ein solcher Link kann mit einem @link-Tag erzeugt werden. Der Quellcode, der die Verwendung des Link-Tags zeigt, ist in Abbildung 6 dargestellt. Abbildung 7 zeigt die Verwendung des @returns-Tags, mit dessen Hilfe sich der Rückgabewert einer Methode (semantisch) näher beschreiben lässt.

Abb. 6: Um Hyperlinks in der Dokumentation zu erzeugen, wird das „@link“-Tag verwendet

Abb. 6: Um Hyperlinks in der Dokumentation zu erzeugen, wird das „@link“-Tag verwendet

Abb. 7: Das „@returns“-Tag wird verwendet, um explizit den Rückgabewert einer Methode zu beschreiben

Abb. 7: Das „@returns“-Tag wird verwendet, um explizit den Rückgabewert einer Methode zu beschreiben

Die Quellcodeansicht ist vor allem dann hilfreich, wenn der eigentliche Quellcode einer Library nicht einsehbar ist, sondern lediglich die vorkompilierte und konkatenierte Version zur Verfügung steht. Diese Form ist typisch, wenn Libraries per npm verteilt werden. Selbst wenn der eigentliche Quellcode vorliegt, kann es angenehmer sein, schnell in der Dokumentation zu schauen, als sich durch den Quellcode und die Struktur einer Library zu suchen. Das trifft vor allem dann zu, wenn die Struktur der Library einem selbst unbekannt ist, was eher der Regelfall sein dürfte.

Dokumentation von Komponenten

Einer der wichtigsten Bestandteile des Angular-Frameworks sind Komponenten. Im Gegensatz zu den anderen Bestandteilen einer Angular-Anwendung haben Komponenten eine zusätzliche Eigenschaft: Ihnen ist ein Template zugeordnet. Templates liefern das nötige HTML, um später zu einer grafischen Repräsentation der Komponente zur Laufzeit zu gelangen. Neben einer allgemeinen Beschreibung und eventuellen Klassenattributen werden auch die Metadaten der Komponente angezeigt.

In diesem Beispiel etwa werden in der Metadatensektion des Komponentenselektors die Style-Dateien und die Templatedatei angegeben. Neben diesen Informationen kann man sich – wie bei den anderen Klassen auch – den Quellcode der Komponente anzeigen lassen, indem man auf den Reiter Source klickt. Das Template ist eine eigene (HTML-)Datei außerhalb der TypeScript-Sources. Daher bietet Compodoc mit dem Reiter TEMPLATE die Möglichkeit, sich dieses in einer separaten Quellcodeansicht anzuschauen.

Neben dieser Quellcodeansicht für das Template ist es auch hilfreich, sich einen visuellen Überblick über die Baumstruktur des Templates zu verschaffen. Dazu wird das Template zunächst von Compodoc analysiert. Basierend auf dieser Analyse wird ein Diagramm gezeichnet, das die lokale DOM-Struktur abbildet. Dieses Diagramm ist dann unter dem Reiter DOM TREE sichtbar. Das Diagramm kann zwischen reinen HTML-Elementen (blau), Komponenten (rot) und HTML-Elementen mit Direktiven (orange) unterscheiden.

Die Grafik ist dabei immer folgendermaßen aufgebaut: Von links nach rechts werden die Elemente/Komponenten in der Reihenfolge aufgeführt, wie sie im Template definiert werden. Von oben nach unten werden die Elemente/Komponenten entsprechend ihrer Verschachtelungstiefe eingezeichnet: Befindet sich die Komponente/das Element im Template auf der obersten Ebene – quasi auf der Root-Ebene der Komponente – wird es ganz oben eingezeichnet, ansonsten entsprechend seiner Verschachtelungstiefe. Durch Pfeile wird die Zugehörigkeit der Elemente gekennzeichnet: Da sich die beiden <a>-Elemente beide innerhalb des <p>-Elements befinden, gehören die <a>-Elemente zu <p>, daher gehen die Pfeile von <p> nach <a>.

Fazit

Eine ausführliche Dokumentation lässt sich durch den Einsatz unterschiedlicher Mittel erzielen. In diesem Artikel wurde die README-Datei in Kombination mit Compodoc als Werkzeug zum Generieren von Dokumentation dargestellt. Weitere Bestandteile einer sorgfältig gepflegten Dokumentation von Softwareprojekten sind die API-Dokumentation – beispielsweise mit OpenAPI/Swagger – und die Systemdokumentation, zum Beispiel mit UML-Deployment-Diagrammen. Je nach Umfang der zu entwickelnden Systeme sollte das richtige Mittel – oder eine Kombination mehrerer Mittel – eingesetzt werden. Beispielsweise ist es sehr sinnvoll, zusätzlich zu der automatisch durch Compodoc generierten Dokumentation eine README-Datei anzulegen.

PHP Magazin

Entwickler MagazinDieser Artikel ist im PHP Magazin erschienen. Das PHP Magazin deckt ein breites Spektrum an Themen ab, die für die erfolgreiche Webentwicklung unerlässlich sind.

Natürlich können Sie das PHP Magazin über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. In unserem Kiosk ist das PHP Magazin weiterhin im Print-Abonnement erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Abonnieren
Benachrichtige mich bei
guest
0 Comments
Inline Feedbacks
View all comments
X
- Gib Deinen Standort ein -
- or -