Subversion Setup · 13 min read · Jan 19, 2026

Einrichten eines modularen Subversion-Repositorys für PHP-gesteuerte Websites

Einrichten eines modularen Subversion-Repositorys für PHP-gesteuerte Websites

Willem Bogaerts - Kratz Business Solutions

Zusammenfassung

Das Teilen von Code zwischen Projekten ist mit Subversion immer noch keine triviale Angelegenheit. Besonders wenn Sie mit SourceSafe vertraut sind, werden Sie feststellen, dass Subversion es schwierig macht, Code zu teilen. Subversion scheint wirklich großartig darin zu sein, ein Versionschaos zu erzeugen und gut darin, eines zu lösen, aber der Grund, warum ich eine Quellcodekontrolle benötige, ist, um zu verhindern, dass ein solches Chaos entsteht. Hier kann Subversion erheblich verbessert werden, aber es ist nicht unmöglich. Dieses Howto wird eine Verzeichnisstruktur demonstrieren, die den Subversion-Teilungsmechanismus sowie andere Probleme berücksichtigt, die Repositories mit sich bringen.

Konvention in diesem Howto

Sie werden an vielen Stellen sehen. Ersetzen Sie dies durch das Wurzelverzeichnis Ihres Repositorys. Das Wurzelverzeichnis Ihres Repositorys ist eine URL, die normalerweise mit https://, file:/// oder svn:// beginnt.
Es wird davon ausgegangen, dass Sie wissen, was Subversion ist, dass Sie seine grundlegende Verwendung kennen und dass Sie ein Repository haben oder erstellen können.
Dieses Howto gibt Ihnen nicht “den besten” Weg, ein Repository zu organisieren, da dies von Ihren Bedürfnissen abhängt. Es soll helfen, indem es einige dieser Entscheidungen zeigt und wie sie die Struktur des Repositorys beeinflussen.

Subversions Art des Code-Sharings

Verzeichnisse in einer Arbeitskopie können Links zu anderen Repositories enthalten, indem eine svn:externals-Eigenschaft definiert wird. Dies führt dazu, dass das verlinkte Repository-Verzeichnis in Ihrer Arbeitskopie enthalten ist, es jedoch nicht Teil des Projekts selbst wird. Das bedeutet, dass Sie das Verzeichnis mit allen Dateien in Ihrer Arbeitskopie sehen, jedoch nicht im zentralen Repository. Sie können die Eigenschaft nur im zentralen Repository sehen.

Es gibt einige Nachteile dieser Art des Teilens:

  • Sie können nur absolute URLs für die Links angeben, sodass eine nahtlose Migration eines Repositorys nahezu unmöglich ist. Selbst das Wechseln von Protokollen (z. B. von svn:// zu https://) führt zu defekten Arbeitskopien und vielen Problemen.
  • Sie können keine Dateien verlinken, nur Verzeichnisse. Dies hat erhebliche Auswirkungen auf die Organisation des Codes.

PHP-Verzeichnisprobleme

Es gibt einige Dinge, die man beachten sollte, wenn man mit Verzeichnissen auf einer Website arbeitet, insbesondere mit PHP. Aus Sicherheitsgründen möchten wir nicht alle Quellen in ein Verzeichnis legen, das von einem Browser zugänglich ist. Die einzigen Dateien, die wir dort ablegen, sind Dateien, die von einem Browser aufgerufen werden müssen. Ich nenne diese Dateien “laufende” Dateien im Gegensatz zu “definierenden” Dateien, die nur Klassendefinitionen oder Funktionsdefinitionen enthalten. Laufenden Code und definierenden Code in einer Datei zu mischen, ist im Allgemeinen keine gute Idee.

Auch aus Sicherheitsgründen haben viele Websites einen eingeschränkten Bereich, der Unit-Tests, Fehlerprotokollseiten oder sogar eine vollständige Backoffice-Website enthält. Dieser eingeschränkte Bereich ist normalerweise durch das Webserver-Passwort geschützt.

Ein Projekt enthält also Verzeichnisse mit definierenden Dateien und ein Webstammverzeichnis (häufig www/ oder htdocs/), das laufende Dateien und optional einen eingeschränkten Bereich enthält.

PHP und relative Verzeichnisse

Leider hat PHP eine sehr kontraintuitive Art, den Speicherort einer eingebundenen Datei zu bestimmen. Die Befehle zum Einfügen einer Datei funktionieren alle in Bezug auf die zuerst aufgerufene Datei und nicht in Bezug auf die aktuelle Datei. Um die Sache noch schlimmer zu machen, wird der Speicherort der aktuellen Datei verwendet, wenn der ursprüngliche Speicherort nicht zu einer vorhandenen Datei führt.

Das klingt kompliziert, und das ist es auch, also hier ein Beispiel:

Angenommen, Sie rufen eine Seite “index.php” auf. Diese Seite bindet eine Seite “library/functions.php” ein. Diese bindet wiederum “settings.php” ein. Sie würden vermuten, dass “settings.php” im Bibliotheksverzeichnis gesucht wird, aber das ist nicht der Fall. Es wird im selben Verzeichnis wie “index.php” gesucht!

Wie oben gesagt, sucht PHP weiterhin im erwarteten Verzeichnis, wenn die Datei nicht gefunden wird. Alles scheint also zu funktionieren, wie Sie es erwarten, bis Sie auf Dateien mit demselben Namen in verschiedenen Verzeichnissen stoßen. Dann haben Sie wirklich Schwierigkeiten herauszufinden, warum PHP “plötzlich” die Datei aus dem falschen Verzeichnis auswählt.

Das bedeutet, dass Sie eine andere Datei mit relativen Pfaden nicht sicher einfügen können. Wir müssen sie mit Code wie:

require_once(dirname(__FILE__) . '/library/functions.php');

absolut machen.

Vergessen Sie nicht das ‘/‘ am Anfang des relativen Pfades, da die dirname-Funktion Pfade ohne abschließende Schrägstriche zurückgibt.

Organisation unseres Repositorys

Es gibt einige Dinge, die für den Code des Projekts zu berücksichtigen sind. Für die Entwicklung ist es praktisch, das gesamte Projekt als Ganzes auszuchecken. Aber für einen Live-Server ist dies möglicherweise nicht das, was Sie möchten. Möglicherweise möchten Sie Datenbankcode (SQL-Skripte) in einem Verzeichnis auschecken, das weit entfernt von den Webverzeichnissen liegt, vielleicht sogar auf einem anderen Server. Es kann Teile des Projekts geben, die Sie überhaupt nicht auf einem Server haben möchten, die jedoch in der Entwicklung benötigt werden, wie Dokumentationsdateien.

Darüber hinaus möchten wir einen zentralen Ort, um den gemeinsamen Code zu speichern. Theoretisch könnten wir Code von einem anderen Projekt “ausleihen”, aber es wäre wirklich schwierig zu verfolgen, welche Projekte von welchen anderen Projekten abhängen. Stattdessen werden wir jeden Standardcode an einen zentralen Ort verschieben.

Wurzelverzeichnisse

Der zentrale Ort, der allen Standardcode enthält, wird “/standard/“ sein. Dieses Verzeichnis wird natürlich in die Standardbibliotheken unterteilt. Projekte werden im Ordner “/projects/“ sein, der nach Kunde und Projekt unterteilt werden kann, zum Beispiel. Code von außen, wie heruntergeladene Bibliotheken wie PHPMailer oder FPDF, wird in “/external/“ abgelegt.

Zweige und Tags

Es ist gute Praxis in einem Subversion-Repository, Ihren Code in einem Branch namens “trunk” zu halten und bei Bedarf andere Branches auf derselben Ebene zu erstellen. Selbst wenn Sie noch keine Branches möchten, erstellen Sie ein Verzeichnis “trunk” direkt unter einem Projekt. Trunk ist der aktive Branch.

Denken Sie darüber nach. Wenn wir auf den trunk einer Standardbibliothek verlinken, verlinken wir auf den aktiven Branch. Das bedeutet, dass alle Fehlerkorrekturen in der verlinkten Bibliothek aktualisiert werden, wann immer wir eine Arbeitskopie aktualisieren. Aber wenn wir einen Fehler einführen, wird dies auch in unsere Arbeitskopien aktualisiert. Sogar die auf einem Live-Webserver! Möglicherweise möchten Sie stattdessen auf einen mehr oder weniger stabilen Branch verlinken, aber das Beheben von Fehlern erfordert etwas mehr Aufwand.

Welchen Link Sie auch wählen, es ist gut zu wissen, dass Sie später immer wechseln können.

Was ist in einer Komponente oder einem Projekt

Es gibt viele Dinge, die wir in ein Repository einfügen möchten, und wir möchten nicht alles am selben Ort auf unserem Webserver haben. Einige Dinge sollten am besten überhaupt nicht auf einem Webserver abgelegt werden oder können auf einem anderen Server ausgecheckt werden, wie ein Datenbankserver.

Beachten Sie, dass die Einrichtung einer Arbeitskopie auf einem Entwicklungsrechner von der auf einem Server abweicht. Auf einem Entwicklungsrechner haben Sie wahrscheinlich ein zentrales Wurzelverzeichnis für alle Ihre Projekte. Dieses Verzeichnis wird dann so konfiguriert, dass es über Ihren lokalen Webserver zugänglich ist, sodass Sie Ihren Server nicht für jedes Projekt, an dem Sie arbeiten, neu konfigurieren müssen. Auf einem Live-Server müssen die Dinge ohnehin konfiguriert werden, und Sicherheitsüberlegungen führen dazu, dass wir nur die Dateien auschecken, die benötigt werden, und nichts weiter. Zu Beginn erstellen wir die folgenden Verzeichnisse in jeder Komponente (falls erforderlich):

dokumentation/Kundeninput, Datenbank- und Objektschemata usw. Es ist nicht nötig, dies auf einem Webserver abzulegen, aber es ist sehr nützlich für Entwickler.sql/Datenbankerstellungs-, Aktualisierungs- und Konvertierungsskripte. Für den Checkout auf dem Datenbankserver.code/Der tatsächliche Anwendungscode.test/Unit-Tests

Diese Verzeichnisse erscheinen grob gleich in den Projekten, mit dem Unterschied, dass Unit-Tests ausführbar sein sollten und daher Teil des Codebereichs sind. Wenn Sie nicht möchten, dass Unit-Tests auf einem Live-Server vorhanden sind, können Sie sie getrennt halten. Für Projekte wird meine Einrichtung sein:

dokumentation/Wie oben. Enthält auch Verweise auf verwendete Komponenten.datenbank/Enthält Datenbankerstellungsskripte und Verweise aus

sql

Verzeichnissen von verwendeten Komponenten.web/Der definierende Anwendungscode.web/htdocs/Das Stammverzeichnis mit dem tatsächlichen laufenden Anwendungscode, HTML-Seiten und anderem Webinhalt, wie Stylesheets und Bildern.web/htdocs/restricted/Eingeschränkter Bereich.web/test/Unit-Tests. Enthält Verweise aus den Tests der verwendeten Komponenten.selenium/Funktionale Tests (siehe

http://selenium.openqa.org/

).

Laufender und definierender Code (erneut)

Wir könnten laufenden Code und definierenden Code in separate Verzeichnisse legen, sodass wir den laufenden Code in ein separates Verzeichnis innerhalb des Webstamms auschecken könnten. Dies bedeutet jedoch, dass das Unterverzeichnis, das diesen Code enthält, Teil der URL wird (wie www.example.com/restricted/errorhandling/viewerrorlog.php), und das ist möglicherweise nicht das, was Sie möchten (vielleicht möchten Sie www.example.com/restricted/viewerrorlog.php). Es gibt eine Lösung dafür. Wir können die laufenden Dateien an einem nicht zugänglichen Ort ablegen und eine Art Proxy an einem zugänglichen Ort platzieren. Dieser Proxy ist eine PHP-Datei mit nichts anderem als einer Include- oder Require-Anweisung, die auf die Datei im nicht zugänglichen Ort verweist:

/htdocs/restricted/viewerrorlog.php aufgerufen wird
// Es ist ein Proxy, der auf /errorhandling/viewerrorlog.php verweist, das nicht direkt von einem Browser aufgerufen werden kann.
// (weil es außerhalb des Webstamms liegt)
require(dirname(__FILE__) . '/../../errorhandling/viewerrorlog.php');
?>

Das Verzeichnis errorhandling wird dann aus der Standardbibliothek geteilt und enthält “definierende” Klassendateien zur Fehlerbehandlung und eine “laufende” Datei, um sie anzuzeigen.

Maschinenabhängige Einstellungen

Einstellungsdateien sind in einem Repository etwas ungewöhnlich. Sie möchten sie in einem Repository haben, aber Sie möchten nicht, dass sie automatisch aktualisiert werden. Ich verwende eine Zwischenmethode für meine Einstellungsdateien: Ich erstelle eine Datei namens settings_example.php im Einstellungsverzeichnis. Diese Datei enthält alle Einstellungen mit Kommentaren, wie sie für verschiedene Maschinen festgelegt werden. Sie enthält auch einen Kommentar, der besagt, dass Sie sie in eine Datei namens “settings.php” kopieren müssen.

Ich verweise nur auf settings.php von anderen Dateien aus und setze eine svn:ignore-Eigenschaft mit dem Wert “settings.php” im Einstellungsverzeichnis. Das bedeutet, dass eine Arbeitskopie nicht “out of the box” funktioniert, aber das wird sie ohnehin nicht tun, aufgrund der Abhängigkeit von maschinenabhängigen Einstellungen. Die svn:ignore-Eigenschaft verhindert, dass Einstellungen von verschiedenen Maschinen Ihre überschreiben, wenn Sie ein Update durchführen.

In die Praxis umsetzen

Ein Beispiel. Angenommen, wir haben ein Projekt, das E-Mails senden und PDF-Dateien erstellen muss. Wir haben beschlossen, dass wir die PHPMailer von PEAR und die FPDF-Bibliotheken verwenden. Um spezielle Einschränkungen in der PHP-Konfiguration zu vermeiden, laden wir einfach die PHPMailer herunter und verwenden nicht die Methode “pear install”, um sie zu installieren.

Außerdem wird dieses Projekt eine standardisierte Fehlerbehandlung und eine standardisierte Datenbankklasse haben. Der Einfachheit halber nehme ich an, dass Sie mit einem leeren Repository beginnen.

Erstellen der benötigten Verzeichnisse

Zuerst erstellen Sie die oben beschriebenen Wurzelverzeichnisse: /external/, /standard/ und /projects/. Ich empfehle, ein grafisches Frontend für Subversion zu verwenden, wie TortoiseSVN oder RapidSVN.

Wir werden uns zuerst mit den externen Bibliotheken befassen. Laden Sie PHPMailer und FPDF herunter und entpacken Sie sie und importieren Sie sie in /external/phpmailer/trunk/ und /external/fpdf/trunk/. Wenn Sie möchten, können Sie von beiden “firstdownload”-Zweige erstellen.

Als nächstes erstellen wir ein Projekt, an dem wir arbeiten können. Dieses Projekt ist für den Kunden “CustomerInc” und das Projekt heißt “SamplePrj”. Ich bin mir sicher, dass Sie bessere Namen für Ihre Projekte finden können. Also erstellen wir den vollständigen Pfad von /projects/CustomerInc/SamplePrj/trunk/. Der “trunk” ist der Hauptbranch: Sie arbeiten möglicherweise in anderen (Entwicklungs-)Branches, aber diese werden schließlich in den trunk zusammengeführt. Wenn Sie am trunk arbeiten (und warum sollten Sie nicht, wenn es ein neues Projekt ist), ist dieses Verzeichnis das Verzeichnis, das als Arbeitskopie ausgecheckt werden soll. Aber warten wir damit. Unter dem trunk erstellen wir “Zweck”-Verzeichnisse für Webcode (php, html usw.), Datenbankcode und Dokumentation.
Unter dem Webverzeichnis habe ich ein Webstammverzeichnis (htdocs) und ein Verzeichnis für generische projektspezifische PHP-Dateien erstellt.

Erstellen der Freigaben

Bis jetzt habe ich alles direkt im Repository gemacht. Aber um Code zu teilen, müssen wir zuerst eine Arbeitskopie erstellen. Lassen Sie uns das tun.

Da unser lokaler Webserver auf allen unseren Projektcode zugreifen können muss, mache ich meine Arbeitskopie in einem Wurzelordner meines Dateisystems (/projects//) oder auf der Festplatte (C:\projects\\). Andernfalls muss der Webserver Zugriffsrechte im Home-Verzeichnis aller Benutzer haben, die Subversion verwenden. Durch die Verwendung eines Wurzelordners können Sie sicherstellen, dass der Webserver auf alle zugreifen kann, und Sie können sogar Verzeichnisse für verschiedene Benutzer trennen. Für unixbasierte Systeme sollten alle Subversion-Benutzer Mitglieder der Gruppe Ihres Webservers sein, und das gesamte Projektverzeichnis sollte mindestens gruppenlesbar sein. Zumindest das Webzeug, das ist.

In unserer Arbeitskopie haben wir das Verzeichnis web, in dem sich das htdocs-Verzeichnis befindet und wo wir die Links zu FPDF und PHPMailer haben möchten. Um diese Links zu erstellen, fügen Sie dem Webverzeichnis eine Subversion-Eigenschaft svn:external mit folgendem Wert hinzu:

fpdf /external/fpdf/trunk/code
phpmailer /external/phpmailer/trunk/code

(ja, der Wert besteht aus 2 Zeilen) und aktualisieren Sie dann Ihre Arbeitskopie. Sie sollten jetzt zwei zusätzliche Verzeichnisse mit Ihrem Update erhalten, wie in diesem Screenshot einer Arbeitskopie unseres Projekts gezeigt.

Hinweis: Ich habe die Links zum trunk erstellt. Sie können stattdessen auf einen Branch verlinken oder auf eine bestimmte Revisionsnummer verlinken. Ich habe nur externe Verweise auf die Codeabschnitte erstellt. Sie möchten wahrscheinlich auch externe Verweise auf die Dokumentation der externen Bibliotheken erstellen.

Nützlichen Code in die Standardbibliothek verschieben

Wenn Ihre Projekte reifen, entwickeln Sie immer mehr Code, der in anderen Projekten nützlich wäre. In unserem Fall nehmen wir an, dass wir ein Fehlerbehandlungspaket und eine Datenbankklasse entwickelt haben. Der erste Schritt besteht natürlich darin, alle Projektabhängigkeiten aus diesem Code zu entfernen. Legen Sie den zu teilenden Code (nennen wir ihn ein Paket) in ein separates Verzeichnis, da er in einem separaten Verzeichnis sein muss, wenn er aus der Standardbibliothek zurückgeteilt wird. Stellen Sie also sicher, dass der generische Fehlerbehandlungscode in einem Verzeichnis errorhandling und die Datenbankklasse in einem Verzeichnis database, beide direkt unter dem Webverzeichnis, abgelegt sind.

Sie können die svn mkdir- und die svn move-Befehle oder einen grafischen Subversion-Client verwenden, um das Paket in Ihrem Projekt nach /standard//trunk/code zu verschieben. Vergessen Sie nicht, Ihre Entwicklungsarbeitskopie nach diesem Schritt zu aktualisieren (Sie werden sehen, dass das Paketverzeichnis verschwindet), fügen Sie dann zwei Zeilen zur svn:external-Eigenschaft hinzu (in der Form /standard//trunk/code), aktualisieren Sie (um das Verzeichnis jetzt aus der Standardbibliothek zurückzubekommen) und bestätigen Sie die Änderung der Eigenschaft. Die meisten Probleme liegen im Aktualisieren Ihrer Arbeitskopien, insbesondere wenn diese Arbeitskopie eine Live-Website ist, für die Sie so wenig Ausfallzeit wie möglich wünschen.
Wenn Sie Ihre Arbeitskopien nach jeder Änderung auf dem Server aktualisieren, kann nichts schiefgehen. Das Problem tritt auf, wenn Sie eine Arbeitskopie haben, die das Paket noch am ursprünglichen Projektstandort hat und Sie auf einen Zustand aktualisieren möchten, in dem dasselbe Verzeichnis jetzt aus einem svn:externals-Verweis stammt. Wenn Sie das versuchen, erhalten Sie einen Fehler, der besagt, dass das Zielverzeichnis für diesen externen Verweis bereits existiert, sodass der externe Verweis nicht erstellt werden kann. Dies wird leicht gelöst, indem Sie dieses Verzeichnis manuell entfernen, bevor Sie diese Arbeitskopie aktualisieren.

Bewegen Sie alle Unit-Tests, Dokumentationen, SQL-Skripte usw. auf ähnliche Weise in die Standardbibliothek.

Aktualisierung des Live-Servers

Wenn mehr als eine Person Zugriff auf den Web- oder Datenbankserver hat, ist es besser, ein Konto für alle zu verwenden. Dies stellt sicher, dass die .svn-Verzeichnisse nicht unter Konflikten von Kontoeinstellungen und Benutzerrechten leiden. Darüber hinaus können Sie diesem Konto Schreibberechtigungen im Repository verweigern, sodass ein kompromittierter Webserver nicht einfach schädliche Inhalte einpflegen kann.

Auf einem Linux-Webserver sind die folgenden Rechte sinnvoll: Lesen und Schreiben für den Benutzer, der die “live”-Arbeitskopie aktualisiert, Leserechte für die Gruppe (die die Gruppe des Webservers ist) und keine Rechte für andere. Wenn Sie das SGID-Bit auf den Verzeichnissen setzen, sind alle hinzugefügten Dateien auch für den Webserver zugänglich. Sie können das SGID-Bit mit chmod g+s setzen. Erwägen Sie, die umask auf 0027 zu setzen, um die Berechtigungen für hinzugefügte Dateien ebenfalls richtig zu setzen. Ein frisch hinzugefügtes htdocs-Verzeichnis könnte dann so aussehen:

drwxr-s--- 9 webdev abyssd 4096 2007-09-08 12:42 restricted/  
-rw-r----- 1 webdev abyssd 441 2007-09-08 12:42 index.php  
-rw-r----- 1 webdev abyssd 2954 2007-09-08 12:42 main.css

Modularer Datenbankcode

Wir haben bereits besprochen, wie Includes in PHP funktionieren, aber wie binden wir Dateien in SQL ein? Wir müssen in der Lage sein, verschiedene Dateien aus verschiedenen Verzeichnissen in SQL-Code einzufügen, da wir unser Repository auf diese Weise organisiert haben. Wenn wir PHP-Code teilen, sollten wir auch den entsprechenden SQL-Code teilen. Ich habe ein kleines PHP- und ein kleines Python-Skript geschrieben, um das zu tun. Sie finden sie unter http://www.w-p.dds.nl/sqlincludeparser_php.txt (PHP) und http://www.w-p.dds.nl/sqlincludeparser_py.txt (Python). Diese Skripte ermöglichen es Ihnen, Include-Anweisungen in einer Basisdatei in folgender Form zu definieren:

CREATE DATABASE IF NOT EXISTS someDatabase;
USE someDatabase;
-- @include(errorhandling/errortables.sql) # (re)creates tables used by the errorhandling package
DROP TABLE IF EXISTS someTable;
CREATE TABLE someTable
      (someID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
...etc.

Wenn Sie jede eingebundene Datei und die Basisdatei wiederholbar machen, können Sie sie verwenden, um immer wieder zu einem sauberen Zustand zu gelangen, was sehr nützlich für Tests und Entwicklungen sein kann. Um es zu verwenden, leiten Sie einfach die Ausgabe meines Skripts an den SQL-Befehlszeilenclient weiter:

sqlincludeparser.py  | mysql -u  -p []
Share: X/Twitter LinkedIn

Erhalte neue Beiträge in deinem Posteingang.

Kein Spam. Jederzeit abmelden.