Letztens habe ich über ein auf PHP Arrays basierendes Mehrsprachigkeitssystem bei Webseiten gesprochen.
Einer der Nachteile bei diesem System ist, dass jeglicher Webseiteninhalt in die Seite ge‘

echo

‚t wird. Möchte man jetzt interaktive PHP Inhalte auf die Seite bringen, wird es kompliziert.

Denn durch

echo

ausgegebene Inhalte werden einfach nur wiedergegeben, nicht noch einmal verarbeitet. Das führt dazu, dass durch

echo

ausgegebene PHP Codezeilen einfach ignoriert bzw. auskommentiert (Chrome) werden.

Beispiel:

<?php
$string='Dies <?php echo("ist ein"); ?> Test!'; 
echo($string);
?>

Ausgegeben wird:
Dies Test!

Der PHP Code wurde ignoriert.

Lösung:
Ich habe eine Funktion geschrieben, mit der PHP Code selbst durch ein

echo

noch funktioniert.
Der Funktion wird ein Text übergeben, der sowohl einfach nur ausgegeben werden soll, als auch PHP Code enthält, der ausgeführt werden soll. Die Funktion teilt einfachen Text und PHP Code auf, gibt den Text normal aus und führt den PHP Code aus.

Code

Code anzeigen

<?php
$arr['a']='dies <em>ist <?php echo("ein");?> test</em> und gleich 
<?php 
$test="<b>noch ein"; 
$mark="!!";
echo($test." test".$mark); 
?></b>
<hr />
ja ja, so geht das';

function printcontent($page) {
	if(strpos($page,"<?php")!==false) {
		$temp = split("(<\?php|\?>)",$page);
		for($i = 0; $temp[$i]; $i++) {
			if(strrpos($temp[$i],";")>=strlen($temp[$i])-3) {
				echo(eval($temp[$i]));
			} else { 
				echo($temp[$i]); 
			}
		}
	}
}
?>

<html>
<body>
<?php printcontent($arr['a']); ?>
</body></html>

Usage:

<?php printcontent($string); ?>

Wobei $string eine Zeichenkette ist, die einen

< ?php ... ?>

Part enthält. Wichtig ist, dass ihr tatsächlich mit

< ?php

beginnt, nicht nur

< ?

und dass das

?>

spätestens 3 Zeichen nach dem letzten Semikolon der letzten PHP Zeile kommt. So wird jeder PHP Part erkannt.
Setzt ihr also vor dem abschließendem

?>

5 Absätze oder Leerzeichen schlägt die korrekte Interpretation fehl.

Demo

Demo

Download

echo-php-code [.zip]

Changelog


v1.1: Performance mit schneller strpos() Vorabfrage erhöht. Regex kommt jetzt nur noch zum Einsatz wenn im String überhaupt ein < ?php gefunden wird.

Ziel: Aus einem Batch Script E-Mails verschicken können ohne vorher Programme installieren oder konfigurieren zu müssen.
Extra: 1-Zeiler

Lösung:
Aus dem Script einen PHP Webmailer fernsteuern. Wenn die Ansprüche nicht zu hoch sind ist diese Lösung in 2, 3 Minuten einsatzbereit und absolut ausreichend.
Einzige Voraussetzung ist hierbei natürlich ein PHP fähiger Webserver. Auf den (Windows) Clients sind keine weiteren Anforderungen gegeben.

Voraussetzung: PHP fähiger Webserver für den Mailer und eine Internetverbindung am Client

Scriptbeispiel:

start "iexplore" "http://php.webserver.de/webmailer.php?submitcode4f3d2s1a&contactname=Testserver 110&subject=Backup fehlgeschlagen&email=edv@iqb.hu-berlin.de&message=Das NAS Backup ist fehlgeschlagen&mailto=hannes.schurig@online.de"

PHP Mailer Code:

Code anzeigenDen Code könnt ihr bequem mit den Links/Rechts Pfeiltasten horizontal bewegen.

<?php
$sendMailFeedback = "";
 
if (isset($_GET['submitcode44gf902jd284'])) {
 
    if (trim($_GET['contactname']) == '') { 
			$sendMailFeedback = "no contact name"; 
		} else { 
			$name = trim($_GET['contactname']); 
		}
 
    if (trim($_GET['subject']) == '') { 
			$sendMailFeedback = "no subject";
		} else {
      $realsubject = trim($_GET['subject']);
      $subject = 'PHP Mailer: "'.$realsubject.'"'; 
    }
 
    if (trim($_GET['email']) == '')  { 
			$sendMailFeedback = "no email";
		} else if (!preg_match("/[a-z0-9._+-]*@[a-z0-9._+-]+/", trim($_GET['email']))) { 
			$sendMailFeedback = "no valid email"; 
		} else { 
			$email = trim($_GET['email']); 
		}
 
    if (trim($_GET['message']) == '') { 
			$sendMailFeedback = "no message"; 
		} else {
        if (function_exists('stripslashes')) { 
					$comments = stripslashes(trim($_GET['message'])); 
				} else { 
					$comments = trim($_GET['message']); 
				} 
		}
 
    if ($sendMailFeedback=="") {
        $emailTo = $_GET['mailto'];
        $datum = date("d.m.Y",$timestamp);
        $uhrzeit = date("H:i",$timestamp);
        $body = "\n###############\nPHP Mailer\n###############\n\nVersendet am $datum um $uhrzeit Uhr\nAn: $emailTo \nVon: $name \nEmail: $email \nBetreff: $realsubject \n\nText:\n$comments";
        $headers = 'From: PHP Mailer <'.$emailTo.'>' . "\r\n" . 'Reply-To: ' . $email;
 
        mail($emailTo, $subject, $body, $headers);
        $sendMailFeedback = "success";
    }
} else {
	$sendMailFeedback = "submitcode missing or wrong";
}
echo $sendMailFeedback;
?>

Erläuterung:
Das Batch Script startet den Internet Explorer (bzw. den als Standard eingestellten Browser) mit einer URL als Parameter, die dann also aufgerufen wird.
Da der Prozess

iexplore

angesprochen wird sollte es ein Windows PC mit installiertem Internet Explorer sein. Computer ohne IE oder mit anderem OS müssen den Pfad zu einem Browser einsetzen, der URLs per Parameter annimmt und aufruft.

Die URL ruft das PHP Script auf, das ihr vorher auf einen Webserver geparkt habt.
Mein erster Parameter ist so etwas wie ein Passwort. Damit nicht jeder im Web diesen Webmailer einfach so gebrauchen kann wird im PHP Code ganz am Anfang mit

if(isset($_GET['submitcode4f3d2s1a'])) {

überprüft, ob ein bestimmter Parameter gesetzt ist, den am besten nur ihr wisst und auch anpassen solltet. Ohne diesen Key funktioniert der Webmailer nicht, gibt ihn also nur bedingt weiter.
Alle weiteren Parameter sind beliebig anpassbar, müssen natürlich den GET Abfragen der PHP entsprechen.

Diese Variante ist also recht flexibel. Ihr könnt den Mailer beliebig anpassen, abspecken, aufrüsten usw.

Beispielnutzung: automatisierte Server Backup Batch Lösung

Letzte Woche habe ich die Mehrsprachigkeit einer Webseite in PHP mit einigen zusätzlichen Extras und Debuginformationen veröffentlicht. Ich zeige jetzt nochmal eine gekürzte Produktivversion ohne Schnick-schnack und Kommentare.

Code

Code anzeigenDen Code könnt ihr bequem mit den Links/Rechts Pfeiltasten horizontal bewegen.

<?php
/*
   ###################
    PHP Multilanguage (minified version)
   ###################
   Desc: This page detects your browserlanguage, cookie or chosen language with PHP and displays the right content.
   Author: Hannes Schurig
   Date: 20.06.2011
   Edited: 20.06.2011 (v1.5)
   based on 1.4 of the long version
   1.5: * Code minified
   1.6: + header(Location) URL correction, no more .php?lang=?? after choosing lang
   URL (min version): http://hannes-schurig.de/21/06/2011/php-mehrsprachigkeitssystem-minified/
   URL (long version): http://hannes-schurig.de/17/06/2011/php-mehrsprachigkeitssystem-mit-einigen-extras/
 */

$lang = detectlang();
function langanalyse($new_lang) {
	switch($new_lang) {
		case ("de"): case ("en"): 
			$lang = $new_lang; 
			setcookie("lang", $lang, time()+31536000); break;
		case ("delete"):
			setcookie("lang", $lang, time()-3600); break;
		default:
			$lang = "de"; 
			setcookie("lang", $lang, time()+31536000); break;
	}
	return $lang;
}
function detectlang() {
	//https Erkennung
	$s = empty($_SERVER["HTTPS"]) ? '' : ($_SERVER["HTTPS"] == "on") ? "s" : "";
	if(isset($_GET["lang"])) { 
		$lang = langanalyse($_GET["lang"]);
		header('Location: http'.$s.'://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME']); 
	}
	elseif(isset($_COOKIE["lang"])) { $lang = langanalyse($_COOKIE["lang"]); }
	elseif(isset($_POST["lang"])) { 
		$lang = langanalyse($_POST["lang"]);
		header('Location: http'.$s.'://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME']);
	}
	else {
		$cutstring = explode("-", $_SERVER["HTTP_ACCEPT_LANGUAGE"]);
		$detectlang = $cutstring[0];
		$lang = langanalyse($detectlang);
	}
	return $lang;
}

echo('
<a href="'.$_SERVER['SCRIPT_NAME'].'?lang=de">De</a>  
<a href="'.$_SERVER['SCRIPT_NAME'].'?lang=en">En</a>  
<a href="'.$_SERVER['SCRIPT_NAME'].'?lang=delete">Del</a>
');

// PHP Array mit den eigentlichen Inhalten der erlaubten Sprachen
$content["de"]["text"]="Hallo, dies ist nur ein Test";
$content["en"]["text"]="Hello, this is just a test";
$content["de"]["menu"]="
<ul>
 <li>Autos</li>
 <li>Frauen</li>
 <li>Alkohol</li>
</ul>";
$content["en"]["menu"]="
<ul>
 <li>Sex</li>
 <li>Drugs</li>
 <li>Rock'n'Roll</li>
</ul>";
$content["de"]["info"]="Zu Ihrer Information:<br/>
News 1: bla<br/>
News 2: bla<br/>
Test";
$content["en"]["info"]="For Your Information:<br/>
News 1: Foo<br/>
News 2: Bar<br/>
foobar";

?>
<html>
 <head>
  <title>Mehrsprachigkeit mit PHP, Array und COOKIES</title>
  <style type="text/css">
  div {
    margin: 20px;
	padding: 15px;
	float: left; }
  #langdebug {
    border: 1px solid black;
    font-size: 0.8em; }
  </style>
 </head>
<body>
 
 <div id="menu">
  <? echo($content[$lang]["menu"]); ?>
 </div>
 <div id="text">
  <? echo($content[$lang]["text"]); ?>
 </div>
 <div id="info">
  <? echo($content[$lang]["info"]); ?>
 </div>

</body>
</html>

Der eigentliche Codeteil (ohne Kommentare, Array und Sprachwahllinks) ist also nur noch 20 Zeilen lang aber trotzdem noch gut lesbar.

Demo

Demo Link

Download

min [.zip]

long version: Link

Heute früh direkt bemerkt: Google hat eine kleine Designänderung auf der Mainpage:

Modern, einfach, Google 🙂

Auch an der Suchseite und den Suchergebnissen wurde gearbeitet:

Die linke Sidebar hat sich verändert: Abstände, Linien(farben) und Positionen der Elemente wurden überarbeitet. Die Icons links sind ebenfalls neu und farblos; darüber lässt sich streiten finde ich, Google ist bei mir grundsätzlich eher bunt. Der ganze Seitenheader ist ein wenig neu arrangiert und designed. Die Suchergebnisse haben jetzt angenehmere Abstände (wodurch aber weniger Ergebnisse ins Sichtfeld passen), das Grün der Links ist (meiner Meinung nach) angenehmer und die Links stehen jetzt direkt unter dem Seitentitel.

Das wären so die auffälligsten Änderungen. Momentan sehe ich das neue Google Design nur im Chrome, im Firefox ist das alte Design noch aktiv. Ich kann also noch sehr gut Vorher-Nachher-Bilder zeigen 🙂

Ich möchte hier den grundlegenden Aufbau einer mehrsprachigen Internetseite zeigen. Das Grundgerüst wird mit HTML, CSS und PHP realisiert.

Features

  • Browser-Spracherkennung
  • Sprachüberprüfung
  • Sprachspeicherung mit Cookies
  • Cookie-Löschfunktion
  • Anzeige der Cookie-Lebensdauer
  • Abfangen unbekannter Sprachen, Setzen einer Ausweichsprache
  • Debuginformationen in einer Box


Funktionsweise

  • Beim ersten Besuch der Zielseite startet die automatische Spracherkennung. Das System prüft den Browser auf mitgegebene Sprachinformationen im HTTP Header und nimmt die bevorzugte Sprache des Browsers als Seitensprache (falls Informationen mitgegeben und Sprache verfügbar).
  • Der Benutzer kann manuell durch Anklicken von Länderfahnen/Links eine andere verfügbare Sprache wählen.
  • Wird eine verfügbare Sprache vom Browser erkannt, vom Benutzer angeklickt oder anderweitig gewählt (z.B. manuelle URL Eingabe) so wird ein Cookie erstellt (falls möglich), in dem die Sprache gespeichert wird. Der Besucher wird bei seinem nächsten Webseitenbesuch also gleich die gewünschte Sprache sehen.
  • Die Cookielaufzeit beträgt 1 Jahr. Bei jeder Aktion auf der Zielseite wird die Lebensdauer wieder auf 1 Jahr gesetzt. Der Tod des Cookies wird in der Debugbox ausgegeben. Durch einen Klick des Benutzers auf einen Button/Link lässt sich das Cookie löschen.
  • Wird die Sprache (durch Browsererkennung, Wahl oder Eingabe) nicht erkannt oder ist für die erkannte Sprache kein übersetzter Inhalt vorhanden so wird eine Ausweichsprache (Standardsprache) gesetzt.
Code

Code anzeigenDen Code könnt ihr bequem mit den Links/Rechts Pfeiltasten horizontal bewegen.

<?php
/*
   ######################
    PHP Multilanguage
   ######################
   Desc: This page detects your browserlanguage, cookie or chosen language with PHP and displays the right content.
   Author: Hannes Schurig
   Date: 15.06.2011
   Edited: 16.06.2011 (v1.4)
   Changelog:
   1.1: komplett umgeschrieben... Funktion langanalyse geschrieben, gekürzt, optimiert
   1.2: + Debugmeldung (echo Ausgabe) bei unbekannter Sprache, mehr Kommentare
   1.3: + Cookie löschen Feature
   1.4: + Cookie Lebensdaueranzeige, Lebensdaueroptimierung (exakt 1 Jahr), besseres Deutsch in der Debugbox
   1.5: * Code minified
   1.6: + header(Location) URL correction, no more .php?lang=?? after choosing lang (only in minified version, link below)
   ###################
    minified version
   ###################
   !URL minified productive version: http://hannes-schurig.de/21/06/2011/php-mehrsprachigkeitssystem-minified/
 */

$lang = detectlang();
// Spracherkennung
// GET abfragen; wird geliefert wenn ein Sprachlink geklickt wird
if(isset($_GET["lang"])) { 
	$lang = langanalyse($_GET["lang"],"get");
}
// POST abfragen; wird in meinem Beispiel nicht benutzt
elseif(isset($_POST["lang"])) {
	$lang = langanalyse($_POST["lang"],"post");
}
// Cookie abfragen; ist ab dem 2. Besuch gesetzt (wenn Cookies aktiviert sind)
elseif(isset($_COOKIE["lang"])) {
	$lang = langanalyse($_COOKIE["lang"],"cookie");
}
// keine Sprache gefunden; Browsersprache erkennen, die Sprache mit der höchsten Priorität auswählen
else {
	$cutstring = explode("-", $_SERVER["HTTP_ACCEPT_LANGUAGE"]);
	$detectlang = $cutstring[0];
	$lang = langanalyse($detectlang,"browser");
}
// Überprüfe ob für die Sprache verfügbar ist
function langanalyse($new_lang, $methode) {
	switch($new_lang) {
		// die erlaubten Sprachen hier hintereinander auflisten
		case ("de"): 
		case ("en"): 
			$lang = $new_lang; 
			setcookie("lang", $lang, time()+31536000);
			break;
		// Cookie löschen Funktion als Pseudo-Sprache
		case ("delete"):
			setcookie("lang", $lang, time()-3600); 
			break;
		// für die erkannte Sprache sind keine Übersetzungen vorhanden oder die Sprache ist gänzlich unbekannt
		default:
			$unknown_lang = 1;
			// die Standardsprache:
			$lang = "en"; 
			setcookie("lang", $lang, time()+31536000);
	}
	// Methode erkennen, Debugbox öffnen und Informationen ausgeben
	switch($methode) {
		case ("get"): echo('<div id="langdebug">Methode = get<br/>'); break;
		case ("post"): echo('<div id="langdebug">Methode = post<br/>'); break;
		case ("cookie"): echo('<div id="langdebug">Methode = cookie<br/>'); break;
		case ("browser"): echo('<div id="langdebug">Methode = browserdetect<br/>
	(kein GET, POST und COOKIE gefunden)<br/>
	Browserlang: '.$_SERVER["HTTP_ACCEPT_LANGUAGE"].'<br/>'); break;
		default: echo('<div id="langdebug">Methode = Unbekannt oder Fehler<br/>'); break;
	}
	// Hinweis im Debugbox wenn Sprache unbekannt
	if($unknown_lang == 1) {
		echo('!! Sprache "'.$new_lang.'" unbekannt. Auf "'.$lang.'" gesetzt<br/>');
		$unknown_lang = 0;
	}
	return $lang;
}
// ein paar zusätzliche Debuginformationen und die manuelle Sprachwahl
echo('aktuelle Sprache = '.$lang.'<br/>
Cookie Sprache = '.$_COOKIE["lang"].'<br/>
Cookie Tod = '. date("d.m.Y G:i",time()+31536000) .'<br/>
Sprache setzen:<br/>
<a href="'.$_SERVER['SCRIPT_NAME'].'?lang=de">Deutsch</a>   
<a href="'.$_SERVER['SCRIPT_NAME'].'?lang=en">Englisch</a><br/>  
<a href="'.$_SERVER['SCRIPT_NAME'].'?lang=delete">Cookie löschen</a>  
</div>');

// PHP Array mit den eigentlichen Inhalten der erlaubten Sprachen
$content["de"]["text"]="Hallo, dies ist nur ein Test";
$content["en"]["text"]="Hello, this is just a test";
$content["de"]["menu"]="
<ul>
 <li>Autos</li>
 <li>Frauen</li>
 <li>Alkohol</li>
</ul>";
$content["en"]["menu"]="
<ul>
 <li>Sex</li>
 <li>Drugs</li>
 <li>Rock'n'Roll</li>
</ul>";
$content["de"]["info"]="Zu Ihrer Information:<br/>
News 1: bla<br/>
News 2: bla<br/>
Test";
$content["en"]["info"]="For Your Information:<br/>
News 1: Foo<br/>
News 2: Bar<br/>
foobar";

?>
<html>
 <head>
  <title>Mehrsprachigkeit mit PHP, Array und COOKIES</title>
  <style type="text/css">
  div {
    margin: 20px;
	padding: 15px;
	float: left; }
  #langdebug {
    border: 1px solid black;
    font-size: 0.8em; }
  </style>
 </head>
<body>
 
 <div id="menu">
  <? echo($content[$lang]["menu"]); ?>
 </div>
 <div id="text">
  <? echo($content[$lang]["text"]); ?>
 </div>
 <div id="info">
  <? echo($content[$lang]["info"]); ?>
 </div>

</body>
</html>
Demo

Demo Link

Download

latest [.zip]

alte Versionen

Changelog


v1.1: komplett umgeschrieben… Funktion langanalyse geschrieben, gekürzt, optimiert
v1.2: + Debugmeldung (echo Ausgabe) bei unbekannter Sprache, mehr Kommentare
v1.3: + Cookie löschen Feature
v1.4: + Cookie Lebensdaueranzeige, Lebensdaueroptimierung (exakt 1 Jahr), besseres Deutsch in der Debugbox
*v1.5: minified/productive version available: Link
*v1.6: + header(Location) URL Korrektur nach Sprachwahl, keine lästigen .php?lang=?? Parameter mehr (nur in der Produktivversion verfügbar)

Problem:
Office 2003 installiert, Installationsdateien wurden auf einer beliebigen Nicht-System-Partition (z.B. D:\) im Ordner MSOCache abgelegt. Image des Computers gemacht, auf einen anderen Computer gespielt, wo eine solche Partition (z.B. D:\) nicht existiert, dieser Fehler wird bei jedem Start jeder Office Komponente erscheinen.

Lösung:
Die Office Local Installation Source (LIS) deaktivieren, neu erstellen oder einen neuen Ort für den MSOCache Ordner festlegen und diesen durch 1 Office Start neu erzeugen lassen. Danach erscheint der Fehler nicht mehr. Infos auch bei Microsoft.

Microsoft hat dafür bereits ein kleines Tool parat, das Local Installation Source Tool für Office 2003.
Mit dem Tool lässt sich dieses Feature aktivieren (MSOCache Ordner erstellen), der Ordner verschieben oder das Feature deaktivieren.

Alternativ kann man den Pfad auch in der Registry setzen:

HKEY_LOCAL_MACHINE\Software\Microsoft\Office\11.0\Delivery\{CLSID}

Dort muss beim Key „LocalCacheDrive“ der Laufwerkbuchstabe der gewünschten Partition rein.
Das ist dann natürlich sinnvoll für die Verbreitung eines neuen Pfades in einem größeren Netzwerk oder via Script.
Dann muss auf dieser Partition in der 1. Ordnerebene der MSOCache vorhanden sein, mit der typischen MSOCache Ordnerstruktur und vielen Cabs. Alternativ MSOCache durch einen Office Start nach dem Setzen des Keys neu erstellen lassen.

Okay, folgendes Ziel:
Wir wollen mit einem Batch Script aus einer Textdatei eine Textzeile löschen oder filtern (alles andere löschen).

Praktisches Beispiel:
Das im letzten Post beschriebene Thunderbird Problem soll auf allen Computern eines Netzwerks behoben werden. Die Zeile muss also aus der Einstellungsdatei jedes Nutzers gelöscht werden. Also ein Anmeldescript.

Der Vorgang besteht aus 2 Schritten:
1. Die gewünschte Zeile finden.
2. Diese Zeile oder alle anderen Zeilen löschen.

Text finden:

findstr /b /v /i /c:user_pref(\"print.print_printer\" c:\pfad\zur\prefs.js

Damit wird die komplette Zeile mit dem Anfang

user_pref("print.print_printer"

gefunden.

Zeile löschen (mit Loggingfunktion):

@echo off
title Reset Thunderbird Default Printer
set logdir=\\server\pfad\
cd /d "%appdata%\Thunderbird\Profiles\"
cd *.default
if exist prefs_backup.js del prefs_backup.js
if errorlevel 1 goto fehler
ping 127.0.0.1 >> nul
ren prefs.js prefs_backup.js
if errorlevel 1 goto fehler
findstr /b /v /i /c:user_pref(\"print.print_printer\" prefs_backup.js > prefs.js
if errorlevel 1 goto fehler
echo %date% %time% %computername% - Reset erfolgreich >> %logdir%\log.txt
goto end
:fehler
echo %date% %time% %computername% - Reset fehlerhaft >> %logdir%\log.txt
:end

Zeile löschen (pure):

@echo off
cd /d "%appdata%\Thunderbird\Profiles\"
cd *.default
if exist prefs_backup.js del prefs_backup.js
ren prefs.js prefs_backup.js
findstr /b /v /i /c:user_pref(\"print.print_printer\" prefs_backup.js > prefs.js

Und falls man einen Überblick über alle verwendeten Thunderbird drucken haben möchte:

Zeile filtern:

@echo off
cd /d "%appdata%\Thunderbird\Profiles\"
cd *.default
findstr /b /l /i /c:user_pref(\"print.print_printer\" prefs.js >> \\server\pfad\all_prefs.txt


Die Funktionalität des filterns beschränkt sich tatsäch nur darauf, diese 1 Zeile aller Benutzer, auf die das Anmeldescript angewendet wird, zusammenzutragen. Das ließe sich natürlich mit entsprachenden echo Befehlen noch detaillierter loggen.

Die genauere Erklärung der

findstr

Parameter erhaltet ihr in der cmd mit

findstr /?

, ist eigentlich alles sehr eindeutig.