von weiten reisen, nerdigen skripten und kreativem agenturleben
E-Mails mit PHP zu versenden geht ja ansich ganz einfach und komfortabel. Interessant wird es jedoch, wenn Textmails mit manuellen Zeilenumbrüchen verschickt werden sollen.
Ein Zeilenumbruch wird im Mailtext beispielsweise wie folgt erzeugt:
$mailtext = "Ich bin ein Absatz.\n"; $mailtext.= "Und ich auch!";
Damit die Steuerzeichen \n, \r oder \t interpretiert werden müssen sie in doppelten Anführungszeichen stehen! Einfache Anführungszeichen im obigen Beispiel würden bewirken, dass das abschließende \n im E-Mail-Text ausgegeben wird.
Soll nun Text aus einer MySQL-Datenbank in die E-Mail fließen, und wurde dieser Text zuvor über ein HTML-<textarea>–Feld in der Datenbank gespeichert, werden plötzlich alle Datenbankinhalte mit doppelten Zeilenumbrüchen in der E-Mail dargestellt.
Grund dafür ist die Form des Zeilenumbruchs der beim Speichern in die Datenbank geschrieben wird. Und das ist bei der Übergabe des HTML-<textarea>–Feldes ein sogenannter „Windows-Zeilenumbruch“ im Format \r\n. Normalerweise interpretieren die meisten Programme diese Umbruchs-Kombination (Carriage-Return + NewLine) als einen zusammenhängenden Umbruch, die gängigen E-Mail-Clients hingegen interpretieren \r\n als doppelten Zeilenumbruch. Auch unter Windows.
Um diese Missinterpretation zu umgehen sollte vor Versand der E-Mail der komplette E-Mail-Text nach dieser Zeichenkombination durchsucht werden. Am einfachsten geht dies mit einem Regulären Ausdruck der alle Vorkomnisse von \r\n durch \n ersetzt.
$mailtext = "Ich bin ein Absatz.\n"; $mailtext.= "Und ich auch! Nach mir kommt Datenbankinhalt."; $mailtext.= $datenbankinhalt; $mailtext = preg_replace("%\r\n%", "\n", $mailtext);
Vor einigen Monaten habe ich eine Lösung zum Upload von docx– und xslx-Dateien in Mediawiki-Wiki’s gepostet.
Inzwischen ist das E-Book in aller Munde, und natürlich soll man auch frei verfügbare E-Books im Wiki hochladen dürfen. Doch auch hier wird der MimeType wieder nicht richtig erkannt.
file -bi unter Linux liefert für ein E-Book im populären „EPUB“-Format den MimeType application/x-zip, PHP interpretiert jedoch als application/zip. Ganz richtig wäre übrigens application/epub+zip.
Um den EPUB-Upload zu gestatten muss also die Extension epub dem Array $wgFileExtensions in der LocalConfig.php hinzugefügt werden. Danach muss noch die Datei mime.types im Ordner /includes angepasst werden. Dazu muss lediglich die Zeile application/zip [...] um den Eintrag epub erweitert werden.
Wer viel mit CSS arbeitet und („krasse“) tabellenfreie Web-Layouts erstellt, der wird nicht drumherum kommen, sich mit den CSS-Selektoren zu beschäftigen.
Inzwischen bin ich etwas XPATH–verwöhnt was Selektoren angeht, denn mit self, child, parent, descendant, descendant-or-self, ancestor, ancestor-or-self, preceding, following, preceding-sibling und following-sibling hat man dort alle, und damit meine ich wirklich ALLE, Möglichkeiten im XML-(Struktur-)Baum zu navigieren und Elemente zu selektieren.
Die Möglichkeiten in CSS sind dagegen eher bescheiden. Hier eine kurze Übersicht über die wichtigsten CSS-Selektoren:
klasse klasse2
Selektiert klasse2 die sich *irgendwo* innerhalb von klasse befindet.
Beispiel:<div class="klasse"> <span class="klasse2">Dieser SPAN wird selektiert. <p class="klasse2">Dieser Absatz wird selektiert.</p> </span> </div>klasse>klasse2
Selektiert klasse2, wenn sich diese genau EINE Ebene unterhalb von klasse befindet.
Beispiel:<div class="klasse"> <span class="klasse2">Dieser SPAN wird selektiert! <p class="klasse2">Dieser Absatz wird NICHT selektiert.</p> </span> </div>klasse*klasse2
Selektiert klasse2, wenn sich diese genau ZWEI Ebenen unterhalb von klasse befindet.
Beispiel:<div class="klasse"> <span class="klasse2">Dieser SPAN wird NICHT selektiert. <p class="klasse2">Dieser Absatz wird selektiert.</p> </span> </div>klasse+klasse2
Selektiert klasse2, wenn sich diese direkt NACH klasse befindet.
Beispiel:<div class="klasse"> <span class="klasse2">Dieser SPAN wird NICHT selektiert.</span> </div> <div class="klasse2"> Dafür wird dieses DIV selektiert. </div>
Was ich mir für CSS wirklich wünsche, ist ein parent-Selektor. Der würde viel Arbeit abnehmen! Ist aber in CSS3 leider nicht vorgesehen und bis CSS4 rauskommt könnten noch ein paar Jährchen vergehen. Schade…
CSS 4 You bietet auf zwei Seiten noch weitere Infos zum Umgang mit Selektoren.
Wir nehmen folgenden Fall an:
Es soll eine permanente Weiterleitung erfolgen von der alten URL http://www.domain.de/wiki/ nach http://wiki.domain.de/. Alle angeforderten Seiten und Verzeichnisse (beispielsweise index.php?title=Hauptseite) sollen ebenfalls weitergeleitet werden.
Am effektivsten wäre hier eine Weiterleitung mittels mod_rewrite, für Anfänger jedoch nicht ohne weiteres zu implementieren, und auch nur dann möglich, wenn der Webserver, sprich Apache dies erlaubt.
Eine Alternative dazu stellt das folgende kleine PHP-Script dar:
<?php /** * Permanente Weiterleitung * @author Tobias Fischer / tobias (at) mediaversal (punkt) de * @date 2009-04-24 */ $url = $PHP_SELF . '?' . $QUERY_STRING; $url = str_replace('/wiki', '', $url); header("Status: 301 Moved Permanently"); header("Location: http://wiki.domain.de" . $url); exit; ?>
$PHP_SELF gibt das Verzeichnis und die aufgerufene Datei ab Domainebene zurück, in unserem Fall also /wiki/index.php. $QUERY_STRING liefert alle angehängten Variablen „nach dem Fragezeichen“.
Nun müssen wir diese beiden Fragmente nur noch zusammensetzen, daraus das aktuelle Arbeitsverzeichnis /wiki entfernen und an die neue URL http://wiki.domain.de/ anhängen.
Die Statusmeldung 301 teilt dem Browser oder auch eventuellen Suchmaschinen mit, dass die angeforderte Seite permanent umgezogen ist – 302 dagegen würde temporär umleiten.
Folgendes Szenario: Ein Datenbanktabelle enthält alle Bankleitzahlen Deutschlands inklusive der Kurzbezeichnung der Bank. 20.107 Datensätze (Stand 04/2009). Allerdings enthält die Datenbank viele doppelte Datensätze die gelöscht werden sollen um Platz zu sparen.
CREATE TABLE IF NOT EXISTS `bankcode` ( `ID` SMALLINT(5) UNSIGNED NOT NULL AUTO_INCREMENT, `Bankleitzahl` VARCHAR(8) NOT NULL, `Kurzbezeichnung` VARCHAR(50) NOT NULL, PRIMARY KEY (`ID`), KEY `Bankleitzahl` (`Bankleitzahl`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='Bankleitzahlen' AUTO_INCREMENT=20108;
Code: SQL-Tabellenstruktur
Eine reine SQL-Möglichkeit, Dubletten zu finden und zu löschen habe ich nicht herausgefunden, deshalb habe ich mir ein kurzes php-Script dazu geschrieben.
Anmerkung: Bei 20.000 Datenbankeinträgen musste ich dazu jedoch die max_execution_time in der php.ini von 60 (Sekunden) auf 320 erhöhen. Ob dies bei einem Produktivsystem sinnvoll ist, sei dahingestellt, das Script lief bei mir lokal auf dem Rechner und ich habe die Änderung somit für gut heißen können.
<?php /** * Dubletten finden und löschen * @author Tobias Fischer / tobias (at) mediaversal (punkt) de * @date 2009-04-12 */ // Datenbankverbindung aufbauen /* ... */ // Anzahl der Datensätze auslesen und ausgeben $result = mysql_query("SELECT `ID` FROM `bankcode`;"); $datensaetze = mysql_num_rows($result); echo 'Datensätze: '.$anzahl.'<br /><br />'; // Arrays erstellen die in den for-Schleifen verwendet werden $dubletten_ids = array(); //zählt die Dubletten eines Datensatzes $overall_ids = array(); // zählt die Gesamtanzahl der Dubletten // Erste Schleife - durchläuft jeden der 20.000 Datensätze einzeln for($i=0;$i< $datensaetze;$i++) { if(!in_array($i, $overall_ids)) { $fid = mysql_result($result,$i,0); $result2 = mysql_query("SELECT * FROM `bankcode` WHERE `ID` = " . $fid . ";"); for($k=0;$k<mysql_num_rows($result2);$k++) { $bcid = mysql_result($result2,$k,0); $blz = mysql_result($result2,$k,1); $kurz = mysql_result($result2,$k,2); // Sucht nach Datensätzen mit gleicher BLZ und gleicher Kurzbezeichnung // AND `ID` <> '".$bcid."' schließt den aktuellen Datensatz aus $result3 = mysql_query("SELECT `ID` FROM `bankcode` WHERE `Bankleitzahl` = '" . $blz . "' AND `Kurzbezeichnung` = '" . $kurz . "' AND `ID` <> '" . $bcid . "';"); // Mache weiter falls Dubletten vorhanden sind if(mysql_num_rows($result3) != 0) { // Durchläuft die Ergebnismenge und speichert alle ID's der gefundenen Dubletten for($m=0;$m<mysql_num_rows ($result3);$m++) { $dubletten_ids[$bcid][$m] = mysql_result($result3,$m,0); } echo '<br />'; echo 'BLZ "'.$blz.'" und Kurzbez. "'.$kurz.'" (ID: '.$bcid.') kommen unter folgenden IDs nochmals vor:'; echo '<br /> '; // Aktuelles Dubletten-ID-Array nochmals durchlaufen und // a) ID's ausgeben // b) jede ID im Array $overall_ids speichern // dies hat den Zweck, dass Datensätze mit dieser ID in der ersten Schleife übersprungen werden // da sonst eine Dopplung vorliegen würde while(list($key,$value) = each($dubletten_ids[$bcid])) { $overall_ids[] = $value; echo '<span '; // Um die Dubletten sofort zu löschen, folgende Zeilen einkommentieren /* $delete = mysql_query("DELETE FROM `bankcode` WHERE `ID` = " . $value . " LIMIT 1;"); if($delete) { echo ' style="color:green;"'; } else { echo ' style="color:red;"'; } */ echo '>'; echo $value; echo ',</span> '; } echo '<br />'; // Frühzeitige Übergabe an den Browser flush(); } } } } echo '<br />'; // Gesamtanzahl aller Dubletten echo count($overall_ids); ?>
Code: PHP-Code-Snippet
Ich konnte damit ca. 12.000 Dubletten aus der BLZ-Tabelle löschen und die Datenmenge somit um mehr als 50 % reduzieren.
Für den Einsatz in einem anderen Datenbank– und Projektumfeld müssen die Datenbankabfragen, die HTML-Textausgaben und eventuell auch einige Variablen angepasst bzw. umbenannt werden!
Ich übernehme keine Gewähr für die Richtigkeit des Skripts und hafte somit auch nicht für eventuell entstandene Schäden durch falschen Einsatz auf Dritt– oder Produktivsystemen!
Hej hej, jag heter Tobias och är tjugofyra år gammal. Das, liebe Freunde der Sprachenkultur, war Schwedisch. Und nochmals auf deutsch: mein Name ist Tobias und ich bin 24 Jahre alt. An der Hochschule der Medien in Stuttgart habe ich acht Semester "Druck- und Medientechnologie" studiert. Anfang 2007 habe ich mich mit [mediaversal] selbstständig gemacht, meinem StartUp das mir die Zeit des Studiums etwas versüßte. Seit April 2011 arbeite ich jedoch festangestellt bei der pagina GmbH und entwickle und gestalte E-Books. Und was sonst noch so los ist erfahrt ihr hier im Blog!