Lokale WordPress-Installation

Oft möchte man an seiner WordPress-Seite etwas Neues ausprobieren, will aber den Betrieb der laufenden Seite nicht stören. Da macht sich dann eine lokale Installation sehr gut. Im folgenden will ich kurz beschreiben, was dazu nötig ist. Der Artikel bezieht sich auf Windows, unter anderen Systemen funktioniert es aber ähnlich.

Bestandsaufnahme

Um eine lokale Kopie einer WordPress-Seite einzurichten, brauchen wir folgendes:

  • PHP, MySQL und einen Webserver
  • WordPress sowie alle Plugins und Themes
  • die Datenbank und die hochgeladenen Dateien
  • Scripte um diese lokale Installation mit wenig Handarbeit auf den aktuellen Stand zu bringen

Insbesondere sollen auch Permalinks gehen und es sollten alle in Beiträgen enthaltenen Links noch funktionieren und nicht auf die Online-Version weiterleiten.

Schritt 1: XAMPP einrichten

Wie ihr PHP, Apache und MySQL auf euren Rechner bekommt, ist natürlich eure Sache. Da führen viele Wege zum Ziel, und eventuell nutzt ihr online ja nicht mal Apache sondern IIS oder NGINX. Für 9 von 10 Nutzern ist es aber sicher am einfachsten und passendsten, sich XAMPP zu installieren. Ernsthaft, zumindest unter Windows würde ich das der einzelnen Installation der Komponenten immer vorziehen!

Bitte die Installationshinweise ernst nehmen und XAMPP nicht wie es ist ans Netz hängen! Der Server ist zum Entwickeln gedacht, nicht zum Online-Betrieb von Webseiten!

Ok, PHP, Apache und MySQL sind also da. Folgende Dateien solltet ihr kennen, am besten legt ihr euch an passender Stelle eine Verknüpfung an, die ihr leicht wiederfindet:

  • C:\xampp\apache\conf\httpd.conf – Die ist die Konfigurationsdatei von Apache. Nach Änderungen Neustart nicht vergessen!
  • C:\xampp\php\php.ini – Dies ist die Konfigurationsdatei von PHP.
  • C:\xampp\apache\logs\error.log – Dies ist die Error-Logdatei des Apache. Fehler in der .htaccess werden hier z.B. geloggt.

Zuerst könnte man jetzt die php.ini richtig konfigurieren, was ich hier jetzt nicht im Detail beschreiben will. Wichtig ist meiner Meinung nach, das Errorlogging richtig einzustellen. Am besten PHP-Fehler in eine eigene Datei loggen lassen und diese Datei dann auch an leicht wiederzufindender Stelle verknüpfen.

Wichtig ist ebenfalls, den Apache für WordPress anzupassen. Dazu die httpd.conf öffnen und nach „IfModule alias_module“ suchen. In diesem Block das hier einfügen:

  Alias /meine-seite "D:/webapps/meine-seite-lokale-kopie"
  <Directory "D:/webapps/meine-seite-lokale-kopie">
    Options FollowSymLinks
    AllowOverride FileInfo
    Order allow,deny
    Allow from all
  </Directory>

Zur Erklärung: Normalerweise liefert XAMPP alles aus, was in C:\xampp\htdocs liegt. Das geht ohne weitere Konfiguration. Ich lege Projekte aber lieber auf D:\ ab, und das muss wie oben konfiguriert werden. Ihr sagt damit Apache, dass er den Aufruf http://localhost/meine-seite auf den Ordner D:/webapps/meine-seite-lokale-kopie mappen soll. Die beiden ersten Zeilen in dem Block sind wichtig, damit die Permalinks funktionieren. Lest jetzt am besten erst mal den Artikel zu Ende, Schritt 3 zeigt euch noch eine bessere Apache-Konfiguration, die obigen Schnipsel überflüssig macht.

Schritt 2: WordPress einrichten

Legt den eben konfigurierten Ordner an und kopiert alle Dateien der WordPress-Installation von eurem Webserver hinein. Dann braucht ihr natürlich noch eine Datenbank. Passt zuerst einmal die wp-config.php an. Datenbank-Server ist „localhost“, Name, Nutzer und Passwort könnt ihr so lassen oder ändern, wie ihr mögt. Die Datenbank müsst ihr lokal natürlich erst einmal anlegen. Dazu PhpMyAdmin oder ein Tool eurer Wahl öffnen und folgendes SQL ausführen (alle Daten natürlich entsprechend der wp-config.php ersetzen):

CREATE DATABASE IF NOT EXISTS meineseite;
ALTER DATABASE meineseite CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON meineseite.* TO 'meinuser'@'localhost' IDENTIFIED BY 'meinpasswort';
FLUSH PRIVILEGES;

Schritt 3: Permalinks einrichten und Datenbank importieren

Wir haben die Original-Datenbank noch nicht importiert, und das hat einen Grund. Die Datenbank enthält nämlich an mindestens zwei Stellen die Domain der Online-Version, was dazu führen würde, dass jeder Klick auf einen Link in der Seite uns zur Online-Version zurückführt. Das ist gefährlich, wenn man nicht aufpasst und im Glauben, auf der Testinstanz zu sein, an der Seite herumschraubt.

Die Adresse könnten wir jetzt einfach im Datenbank-Dump mit der localhost-Adresse ersetzen, das hat aber einen Nachteil: Manchmal steht die Adresse auch in serialisierten Strings, in denen die Länge mit codiert ist. Das geht kaputt wenn man einen String mit anderer Länge hineinschreibt. Ich habe deswegen mit einem kleinen Trick eine lokale Domain eingerichtet, unter der die lokale Seite aufgerufen wird. Die HOSTS-Datei öffnen (bei mir unter C:\Windows\System32\drivers\etc) und das hier ergänzen:

127.0.0.1    meine-seite.loc             # local version

Ich habe eine net-Domain, deswegen habe ich als lokales Kürzel „loc“ gewählt. Für „de“ müsstet ihr natürlich was anderes nehmen, z.B. „lc“. Hauptsache es gibt das nicht schon als echte Top-Level-Domain. Für Multisite-Installationen müsst ihr das analog auch für alle Sub-Domains machen. Ich würde dann für jede weitere Domain von 127.0.0.1 hochzählen (127.0.0.2 etc.).

Um über diese neue Domain die lokale Test-Installation aufrufen zu können, müsst ihr noch mal an die httpd.conf des Apache ran. Einfach das hier ganz unten einfügen (nur einmal pro Haupt-Domain, die Sub-Domains sind davon mit erfasst):

# meine-seite.net
<VirtualHost 127.0.0.1>
  ServerName meine-seite.loc
  ServerAlias *.meine-seite.loc
  DocumentRoot "D:/webapps/meine-seite-lokale-kopie"
  <Directory "D:/webapps/meine-seite-lokale-kopie">
    Options FollowSymLinks
    AllowOverride FileInfo
    Order allow,deny
    Allow from all
  </Directory>
</VirtualHost>

Wenn ihr das so einrichtet, könnt ihr euch die Konfiguration aus Schritt 1 auch sparen. Ich habe das nur mit aufgenommen, falls jemandem die lokale Domain zu viel Aufwand ist. Siehe auch das Update am Ende des Textes!

Dann einfach einen Datenbank-Dump von der Online-Version ziehen, z.B. per PhpMyAdmin oder über ein Backup-Plugin für WordPress, und in diesem Dump global die Original-Domain mit der „Fake-Domain“ ersetzen. Das ganze dann in die vorhin angelegte lokale Datenbank importieren. Für den Moment könnt ihr das manuell über PhpMyAdmin machen, Automatisierung kommt im nächsten Schritt.

So, an diesem Punkt sollte eine exakte Kopie eurer Webseite unter http://meine-seite.loc erreichbar sein. Geht als erstes auf Backend > Einstellungen > Permalinks und speichert die Permalinkstruktur neu ab. Das generiert die .htaccess-Datei neu, danach gehen auch alle Permalinks auf der Seite.

Schritt 4: Automatisierung

Wenn ihr eine Weile mit der Testinstanz gearbeitet habt, füllt sie sich mit Testinhalten, dafür fehlen die neueren echten Inhalte. Irgendwann kommt der Bedarf auf, die Testinstanz mit der Online-Version zu synchronisieren, um mit aktuellen Inhalten testen zu können. Das kann man natürlich wie oben beschrieben immer manuell machen, aber auf Dauer ist das nervig. Dafür habe ich mir ein kleines Script geschrieben.

Das Script selbst ist eine Batch-Datei, die nichts weiter tut als ein PHP-Script aufzurufen. Das ganze könnte man sicher besser mit Perl oder anderen Script-Sprachen lösen, und dann vor allem als eine Datei statt zwei umsetzen. Unter Windows war das aber die einfachste Lösung, ohne erst irgendwas installieren zu müssen. Das ganze ist nicht wirklich schön, aber es musste schnell gehen und funktioniert. 😉

Legt euch ein Verzeichnis für Scripts an und speichert das hier als import_dump.bat ab (Pfade ggf. anpassen):

@echo off
C:\xampp\php\php.exe import_dump.php

Und das hier als import_dump.php:

<?php

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'meineseite');

/** MySQL database username */
define('DB_USER', 'meinuser');

/** MySQL database password */
define('DB_PASSWORD', 'meinpasswort');


// from: http://stackoverflow.com/questions/2159059/string-replace-in-a-large-file-with-php
function replace_file($path, $string, $replace) {
  set_time_limit(0);
  $result = 0;
  if (is_file($path) === true) {
    $file = fopen($path, 'r');
    $temp = tempnam(dirname(__FILE__), 'tmp');
    if (is_resource($file) === true) {
      while (feof($file) === false) {
        file_put_contents($temp, str_replace($string, $replace, fgets($file)), FILE_APPEND);
      }
      fclose($file);
    }
    unlink($path);
    $result = rename($temp, $path);
  }
  return $result;
}


// find the database file
$files = glob(dirname(__FILE__) . '/db*.sql');
if (sizeof($files) == 0) {
  echo 'no SQL file found';
  return;
}
$file = str_replace('/', '\\', $files[0]);

// loop through the file contents and change stuff
$result = replace_file($file, array(
  'meine-seite.net',
  's:83:"/kunden/123456789/webseiten/meine-seite.net/wordpress/wp-content/blogs.dir/1/files/"',
  'ping_sites\', \'http://rpc.pingomatic.com/',
  '"cronjob_alert";i:1', // AntiVirus cron-job
  'webmaster@meine-seite.loc',
  'johannes@meine-seite.loc',
), array(
  'meine-seite.loc',
  's:64:"D:/webapps/meine-seite-lokale-kopie/wp-content/blogs.dir/1/files/"',
  'ping_sites\', \'',
  '"cronjob_alert";i:0',
  'webmaster@meine-seite.net',
  'johannes@meine-seite.net',
));
if ($result == 0) {
  echo 'result of search-and-replace: 0' . "\n";
}

// import dump into database
passthru('C:\xampp\mysql\bin\mysql.exe -u' . DB_USER . ' -p' . DB_PASSWORD . ' --default_character_set utf8 ' . DB_NAME . ' < ' . $file, $result);
echo 'result of db import: ' . $result;

Ganz oben sind der Einfachheit halber die Datenbank-Daten aus der wp-config.php eingefügt. Das Script erwartet einen Datenbank-Dump im gleichen Verzeichnis, der dem Namensschema „db*.sql“ folgt. Es wird die erste passende Datei genommen, also am besten die Datei nach dem Import löschen.

Gegen Ende des Scriptes ist aufgelistet, was mit was ersetzt wird. Hier müsst ihr natürlich eure Daten eintragen. Da wäre in erster Linie eure Domain zu ersetzen. Da an einigen Stellen Dateipfade serialisiert im Dump stehen, ersetze ich die auch. Hier müsstet ihr im Dump einfach mal nachschauen, wie das bei euch heißt („/kunden/123456789/webseiten/“ ist die Ordnerstruktur bei Domainfactory). Am Ende ersetze ich meine E-Mail-Adressen übrigens wieder zur Originalversion zurück, weil es mich mehrmals sehr verwirrt hat, dass der Gravatar für die .loc-Version nicht angezeigt wurde (geht ja auch schlecht).

Also: Script-Dateien speichern, Datenbank-Dump mit dem richtigen Namen in das gleiche Verzeichnis legen und das Script starten. Dann sollte eure Test-Datenbank durch den bereinigten Inhalt des DB-Dumps ersetzt werden. Alles, was ihr an der Datenbank der Testinstanz gemacht habt, ist dann natürlich weg!

Schritt 5: Versionskontrolle

Der letzte Schritt ist optional, aber wenn ihr längerfristig mit der Testinstanz arbeiten möchtet, macht es sich vielleicht gut, das ganze Projekt unter Versionskontrolle zu stellen. Das ist auch schon praktisch, wenn ihr alleine daran arbeitet. Wenn ihr z.B. gezwungen seid, ein Plugin zu patchen und nach einem Jahr gibt es da doch mal ein Upgrade, dann macht es sich super, wenn man im Logfile nachschlagen kann, ob und wenn ja was man geändert hatte.

Je nach Betriebssystem und euren sonstigen Ressourcen könnt ihr da wählen, was ihr möchtet (SVN, Git, Mercurial…). Ich habe mich für Git und den Hoster BitBucket entschieden. GitHub ist im OpenSource-Bereich verbreiteter, bietet aber nur 5 private Repositories an. Bei BitBucket könnt ihr dagegen unbeschränkt viele private Projekt ablegen (auf die dann also niemand sonst Zugriff hat). Natürlich könnt ihr das ganze auch einfach nur auf eurem lokalen Rechner unter Versionskontrolle stellen, aber wenn die Daten extern gespiegelt sind, sind sie auch von überall aus verfügbar und dienen im Notfall als Backup (angepasstes Theme, geänderte Plugins etc.).

Ich habe mir also Git für Windows installiert und im Root-Verzeichnis des Projekts ein neues Repository erzeugt. Nach der gut verständlichen Anleitung von BitBucket habe ich das dann mit BitBucket synchronisiert. Der Windows-Git-Client ist sehr spartanisch, aber um Änderungen zu committen und ab und an zu BitBucket zu pushen reicht es aus.

Fazit

Mit oben vorgestellten Maßnahmen haben wir nun eine lokale Kopie der Seite. Wenn es nun daran geht, ein neues Plugin zu integrieren oder ein WordPress-Update durchzuführen, kann man das ganz in Ruhe lokal ausprobieren. Erst wenn alles funktioniert, werden die Änderungen auch online eingespielt/durchgeführt. Das ganze System ist noch nicht perfekt, weil man z.B. Einstellungen online manuell wiederholen muss. Es ist aber allemal besser, als an einer Live-Seite rumzuschrauben.

Ich hoffe, der Artikel ist verständlich und hilft dem ein oder anderen weiter. Falls ich an bestimmten Stellen zu schnell war, einfach kommentieren und ich gehe mehr ins Detail.

Update Mai 2012: Gerade merke ich, dass man nicht ohne weiteres mehrere VirtualHosts für 127.0.0.1 definieren kann. Man landet dann immer beim zuerst definierten. Dazu sollte man mal im Unterordner C:\xampp\apache\conf\extra die Datei httpd-vhosts.conf öffnen. Zum einen sollten hier eigentlich die VirtualHosts rein anstatt direkt in die httpd.conf, zum anderen ist hier die Direktive „NameVirtualHost *:80″ einzukommentieren („#“ entfernen davor). Dann sollten auch mehrere VirtualHosts mit der gleichen IP, aber verschiedenen Namen klappen.

4 Gedanken zu „Lokale WordPress-Installation

  1. Hi! Herzlichen Dank für diese Informationen!
    Habe mir deinen Beitrag durchgelesen und komme irgendwie nicht ganz mit.
    Zudem vermisse ich vermisse hier leider eine Detaillierte beschreibung deines Search and Replace aufbaus wohin damit, wie online einsetzten etc…
    Ich will nicht sagen dass es schwer formuliert ist aber ganz verständlich ist es auch nicht. Meine frage an dich wäre, ob ich dieses script auch einsetzten kann um die veränderten links in meiner Datenbank (online) wieder herzustellen und wie das generell funitioniert.
    Herzlichen Dank nochmal und weiter alles gute! René

  2. Ja, ich schreibe manchmal etwas umständlich. In diesem Fall ist mir aber nicht ganz klar, was Dir unklar ist. 😉 Das Script zum Anpassen und Importieren der Datenbank kannst Du ablegen, wo Du möchtest. Die Datenbank-Zugangsdaten sind hineinkopiert, so dass nichts Externes gebraucht wird.

    Der Artikel beschreibt an sich nur, wie man schnell eine Kopie der Online-Seite in die lokale Test-Instanz bekommt. Der umgekehrte Weg ist nicht zu empfehlen, aber technisch natürlich möglich. Dann solltest Du aber sicher sein, dass in der Zwischenzeit online keine Kommentare eingegangen sind. Und ein separates Backup der Datenbank schadet auch nicht, nur falls beim Importieren doch etwas schiefgeht.

    Ganz grob sieht das Export-Script so aus, dass es zuerst die lokale DB exportiert über mysqldump. Die zu exportierenden Tabellen reiche ich mit Leerzeichen getrennt rein, da es mir nur um einige wenige Tabellen geht. Danach passiert auf dem Dump-File die gleiche Ersetzung wie beim Import, nur andersherum.

    Bitte gerne nachfragen, wenn noch etwas unklar ist oder Du Interesse an dem Export-Script hast. Mir fehlt gerade die Zeit, mehr dazu zu schreiben, aber ich kann es ggf. gerne noch online stellen.

  3. Hallo
    Ist es eigentlich möglich auf einer lokalen XAMPP gleichzeitig Typo3 und WordPress Homepages zu entwickeln?

    Gruss, Roger

  4. Ja, wieso sollte das nicht gehen? Ich beschreibe oben ja gerade ein Setup, mit dem Du beliebig viele Seiten auf einem Rechner bearbeiten kannst. Du kannst die Sachen aber auch einfach in Unterordner packen, genauso wie auf dem Server ja auch.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Bitte beachte die Kommentarregeln: 1) Kein Spam, und bitte höflich bleiben. 2) Ins Namensfeld gehört ein Name. Gerne ein Pseudonym, aber bitte keine Keywords. 3) Keine kommerziellen Links, außer es hat Bezug zum Beitrag. mehr Details...

So, noch mal kurz drüber schauen und dann nichts wie ab damit. Vielen Dank fürs Kommentieren! :-)