Posts Tagged ‘PHP’

Sonntag, 7. Oktober 2012

Syntax eines PHP-Scripts auf der Kommandozeile überprüfen

Nicht immer produziere ich auf dem lokalen Mac Code in einem Verzeichnis, welches über den lokalen Web-Server angesprochen werden kann. Wenn ich den Code auf den Server des Kunden kopiert habe und mir dort eine gähnende Leere entgegenblickt, ist die Vermutung in der Regel ein Syntax-Problem, welches den PHP-Parser zum Abbruch bewegt. So genau weiss man das natürlich nicht, da man als guter Entwickler display_errors auf einem Produktivsystem auf 0/off/false gesetzt hat und Fehlermeldungen nur in das error_log ablegt.

Entweder öffnet man das error_log via FTP, oder aber man lässt die Syntax auf dem Mac selber von der Kommandozeilenversion von PHP überprüfen:

php -l <Pfad zum syntaktisch defekten PHP-Script>

Dies führt entweder zur Absolution mit

No syntax errors detected in <Pfad zum syntaktisch defekten PHP-Script>

oder aber im unglücklicheren Fall zur folgenden oder ähnlichen Fehlermeldung:

Parse error: parse error in <Pfad zum syntaktisch defekten PHP-Script> on line 146
Errors parsing <Pfad zum syntaktisch defekten PHP-Script>

Tags: , , , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Montag, 16. Juli 2012

PHPs serialize und unserialize-Funktion mit Unicode-Problemen

Ich habe es mir angewöhnt, regelmässig in das PHP-Error-Log auf meinem privaten Web-Server zu schauen, in welches mit E_ALL jede erdenkliche Art von Fehlern aufgezeichnet werden.

Und kürzlich häuften sich durch einen Spider verursachte Fehler in der Form:

...
PHP Notice:  unserialize(): Error at offset 1573 of 20531 bytes in /var/www/sites/domain.ch/administration/inkludierung/file.php on line 1816
...

Heute hatte ich Zeit, dem Problem auf den Grund zu gehen. Die erste Aufgabe bestand einmal daraus, den serialisierten String vor der Speicherung in die Datenbank mit dem serialisierten String nach dem Extrakt aus der Datenbank zu vergleichen. Hierzu schrieb ich die beiden Zeichenketten in Dateien im Web-Root. Wie sich mit einem Blick erkennen liess, war der serialisierte String nach dem Zurücklesen aus der Datenbank um 30 Bytes grösser.

Was war die Ursache? Natürlich vermutete ich auf Anhieb Probleme mit dem Zeichensatz und Sonderzeichen, von welchen es in der deutschen Sprache halt ziemlich viele gibt (jedenfalls aus der Optik von 127 ASCII-Zeichen). Da PHP Offsets in Bytes (und nicht Zeichen) angibt, war es hilflos, den String in einem Texteditor wie TextMate anzuschauen. Stattdessen rief ich 0xED auf, und musste danach „offset 1573“ in Hex umrechnen — mit dem erstbesten Tool, welches ich im Web fand: Hexadecimal Conversion. Der Blick auf den Array-Wert bestätigte meine Befürchtung: Es handelte sich um eine Zeichenkette, welche deutsche Umlaute enthielt.

NB: Der Offset lag aber ca. 10 Zeichen hinter dem Auftreten der Sonderzeichen, aber immer noch innerhalb des Strings, welcher die Sonderzeichen enthielt.

Nach etwas Googlen realisierte ich, dass PHPs serialize() und unserialize() nicht den besten Ruf besitzen und äusserst fehleranfällig sind — besonders, wenn es um Multibyte-Strings geht. Nachdem ich kurzfristig auf json_encode() und json_decode() wechseln wollte, das Vorhaben aber abbrechen musste, weil ich mein Mega-Array mangels nicht fortlaufender Keys teilweise in Objekte umgewandelt zurückerhielt, stiess ich auf folgenden Blog-Post:

PHP Serialize() & Unserialize() Issues

Seit ich die serialisierten Zeichenketten noch durch Base64-En- resp. Dekodierung durchlaufen lasse, bleibt mein PHP-Error-Log leer:

...
$toDb = base64_encode(serialize($sitemap));
...
$sitemap = unserialize(base64_decode($fromDb));

Einziger Nachteil: Durch Base64-Encodierung setzt der String 7 Kilobytes an Länge an. Aber mir soll’s Recht sein.

Tags: , ,
Labels: Programmierung

4 Kommentare | neuen Kommentar verfassen

Sonntag, 6. Mai 2012

PHP kriegt das mit den Nummern nicht gebacken

Da las ich vor einigen Tagen einen interessanten Beitrag über die haarsträubenden Schwächen von PHP — und erfuhr eines der genannten Problem heute am eigenen Leibe.

Eine meiner Funktionen skaliert Bilder, um sie danach aus der Mitte heraus zuzuschneiden. Vor dem Zuschnitt wird das Bild skaliert. Ziel ist es dabei, das Bild so zu skalieren, dass sowohl die Höhe und Breite des Zielformats überschritten werden, denn so lässt sich mit gutem Gewissen ein Zuschnitt vornehmen.

Mein Code sieht folgendermassen aus:

...
for($i = 0; $i<=1; $i++) {
        $factor = $dimDestination[$i]/$dimOriginal[$i];

        $dimTests[$i] = array($dimOriginal[0]*$factor, $dimOriginal[1]*$factor, $factor);
}

$factor = null;

foreach($dimTests as $key=>$dimTest) {
        if($dimTest[0] >= $dimDestination[0] && $dimTest[1] >= $dimDestination[1]) {
                $factor = $dimTest[2];
                $keySelected = $key;
        }
        else {
                $this->debug->add($dimTest[0] . '<' . $dimDestination[0] . ', ' . $dimTest[1] . '<' . $dimDestination[1]);
        }
}

if($factor === null) {
        $this->debug->add('$factor ' . $factor . ' is null');
        return false;
}

Bei einem speziell zugeschnittenen Bild wurde $factor immer auf null gesetzt und die Funktion brach mit false ab — obwohl das Bild eigentlich problemlos skaliert und zugeschnitten hätte werden sollen.

Nach einer halbstündigen Fehlersuche kam ich den Problem schlussendlich auf die Spur. Zuerst einmal schaute ich mir die in $this->debug gespeicherten Infos an:

emeidiImage->cropFromCenter()
505<505, 131.510416667<66

Was zum Teufel?! Wieso ist 505 kleiner als 505? Mit vardump() lichtete sich der Nebel über dem falschen Verhalten:

array(2) {
  [0]=>
  array(3) {
    [0]=>
    float(505)
    [1]=>
    float(131.510416667)
    [2]=>
    float(1.05208333333)
  }
  ...
}

array(2) {
  [0]=>
  int(505)
  [1]=>
  int(66)
}

Ich verglich float(505) mit int(505), und da lag wohl die Krux vergraben! Nun, dachte ich mir, wandeln wir die Dimension halt bei der Multiplikation der Ursprungsdimension mit dem Faktor in einen Integer-Wert um:

$dimTests[$i] = array((int)($dimOriginal[0]*$factor), (int)($dimOriginal[1]*$factor), $factor);

Weiterhin brach die Funktion ab. Was zum Teufel … ein erneuter Blick auf vardump() zeigte mir endlich die wirklich Ursache des Problems:

array(2) {
  [0]=>
  array(3) {
    [0]=>
    int(504)
    [1]=>
    int(131)
    [2]=>
    float(1.05208333333)
  }
  ...
}
array(2) {
  [0]=>
  int(505)
  [1]=>
  int(66)
}

Ahaaa! Irgendwie war der Fliesskommawert eben nicht ganz genau 505, sondern vielleicht nur 504.999999.

Nach folgender Anpassung unter Zuhilfenahme der Funktion ceil() lief die Funktion endlich fehlerfrei durch:

$dimTests[$i] = array(ceil($dimOriginal[0]*$factor), ceil($dimOriginal[1]*$factor), $factor);

Tjach. Heute habe ich deshalb gelernt, dass int(505) noch lange nicht float(505) ist.

Nachtrag

Das Problem tritt mit der bei cyon.ch installierten PHP-Version 5.2.17 auf, nicht aber mit der auf meinem Entwicklungsserver installierten PHP-Version 5.4.0-3 auf.

Tags: , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Mittwoch, 11. April 2012

XING-Bilder grösser machen

Da ich keinen Sinn darin sehe, für eine XING Premium-Mitgliedschaft Geld auszugeben, habe ich ab und zu das Problem, dass ich erraten muss, wer mein Profil so alles besucht hat.

XING zeigt mir dabei die Besucher mit einem klitzekleinen Bildchen an, worauf ich die Person partout nicht erkennen kann. Wenn ich meine Neugier befriedigen möchte, muss ich eine XING-Mitgliedschaft kaufen.

Doch zumindest etwas Licht ins Dunkel lässt sich bringen, indem man etwas mit der Bild-URL spielt. Hier ein Beispiel:


https://www.xing.com/pubimg/users/1/6/c/2450328df.16473797,2.30x40.jpg

Hmmm! Löschen wir doch mal die Pixeldimensionen nach dem Komma. Oh:


https://www.xing.com/pubimg/users/1/6/c/2450328df.16473797,2.jpg

So sieht der Mitmensch also aus, der mein Profil besucht hat …

Mein Tipp an XING: Die verschiedenen Grössen der Bilder sollten einen variablen Bestandteil haben, der von aussenstehenden weder erraten noch hergeleitet werden kann. Beispielsweise mittels md5($imageSize . $internalUserId).

Tags: , , , , , ,
Labels: Web

Keine Kommentare | neuen Kommentar verfassen

Samstag, 11. Februar 2012

Vom Nutzen einer PHP Debug-Klasse

Seit mehreren Jahren verwende ich eine Sammlung von selber entwickelten emeidi*-PHP-Klassen, um Web-Sites und -Applikationen zu entwickeln. Leider hege ich immer noch grosse Skrupel, diese Klassen via Github als Open Source Software freizugeben — einerseits weil sich im Quellcode doch der eine oder andere unschöne Bock findet, andererseits, weil die Klassen Sicherheitslücken enthalten könnten, die bei produktiven Web-Sites ausgenützt werden könnten. Hinzu kommt mein Amazon-API-Key, den ich kurzerhand in die emeidiAmazon-Klasse festkodiert habe …

Unverzichtbares Kernstück ist dabei meine emeidiDebug-Klasse, welche für die professionelle, effiziente Web-Entwicklung unabdingbar ist. Jede andere meiner Klasse instanziert beim Laden die emeidiDebug-Klasse, mit welcher ich im ganzen Programmcode nützliche Fehlermeldungen und/oder Informationen zum Programmablauf festhalte.

Auf produktiven Web-Sites gebe ich den Inhalt des Debug-Objektes natürlich nicht aus, aber auf dem Entwicklungsserver ist es eine grosse Unterstützung, wenn ich am Ende der ausgegebenen Web-Seite noch folgenden Code hinpflanze:

...
<?php print $klasse->debug->getHtml(); ?>
</body>
</html>

Im ganzen PHP-Code finden sich zur Befüllung dieses Objekts Konstrukte wie

$this->debug->add('Not dumping descriptions because length new ' . $lenNew . ' is smaller than lenght old ' . $lenOld,'error');

Teilweise gebe ich auf produktiven Web-Sites wichtige Warnmeldungen auch in das PHP Error-Log aus. Während ich das früher direkt mit error_log(); gemacht habe, benutze ich heute ebenfalls meine Debug-Klasse dafür.

Der Grund dafür ist simpel: Nicht nur möchte ich das gesamte Debugging über meine Debug-Klasse laufen lassen, nein, meine Debug-Klasse ist auch geschwätziger und hilft mir im Falle meines Testservers, das fehlerhafte Script zu lokalisieren.

Vor einigen Tagen fand ich auf meinem Testserver folgende Einträge in der php.err-Datei:

[06-Feb-2012 00:07:43 UTC] Not dumping descriptions because length new 1778 is smaller than lenght old 1780

Mittlerweile habe ich das ausgebende Script (es wird täglich per Cron-Job aufgerufen) lokalisiert und die Programmierung angepasst. Alt hiess es:

error_log('Not dumping descriptions because length new ' . $lenNew . ' is smaller than lenght old ' . $lenOld);

Neu heisst es:

$this->debug->add('Not dumping descriptions because length new ' . $lenNew . ' is smaller than lenght old ' . $lenOld,'error',true);

Der letzte der Funktion übergebene Parameter true weist meine Debug-Klasse an, einen Eintrag in die php.err-Datei zu machen. Dieser schaut folgendermassen aus:

[11-Feb-2012 16:57:16 UTC] Not dumping descriptions because length new 1778 is smaller than lenght old 1780 in /var/www/apps/weather2ics/weather2ics.class.php on line 95 for URI </bern>

Anhand dieser Informationen kann ich nicht nur die Datei und die verantwortliche Zeile auf einen Blick lokalisieren, welche den Fehler ausgibt, sondern sehe auch noch gerade die URI, mit welcher die Web-App aufgerufen wurde.

Tags: , ,
Labels: Web

1 Kommentar | neuen Kommentar verfassen

Sonntag, 13. November 2011

Der Associate Tag ist für die Amazon Product Advertisting-API nun zwingend

Seit einigen Jahren verwende ich eine selber geschriebene schicke kleine PHP-Klasse, um Produkte mit ihrer ISBN oder EAN aus dem Amazon-Katalog abzufragen. Seit einigen Wochen (oder sind es gar Monaten?) scheint der Zugriff nicht mehr zu funktionieren.

Ursache ist eine neue Bedingung von Seiten Amazons, die für jeden Request neu nicht nur den AWSAccessKeyId verlangt, sondern auch den AssociateTag.

Diesen erstellt man, in dem man sich einen Account unter affiliate-program.amazon.com einrichtet — wer Wert auf einen schönen und einfachen Tag legt, sollte sich beim Formularfeld für den Titel der Web-Site einen einprägsamen Namen einfallen lassen. Bei mir lautet der AssociateTag dementsprechend emeidicom-20.

Wird dieser zusätzliche Parameter mit Requests mitgeschickt, kriegt man keine Fehlermeldung mehr zu Gesicht, sondern die tatsächlich erwarteten Daten zu Büchern, Audio-CDs und DVDs.

Tags: , ,
Labels: Allgemein

2 Kommentare | neuen Kommentar verfassen

Dienstag, 26. Oktober 2010

Darf ein Web-Entwickler seine geliebte Scripting-Sprache aufgeben?

How do you hire a programmer if you’re not one yourself? Some things to look for …

1. How opinionated are they?

Ask them about a juicy programming topic (e.g. Ruby or Python?). The tone and reasoning of the answer will reveal a lot. In our recent podcast on programming, Jeff said, “When people have strong opinions about things — when they can talk at length about something — it’s a good indication that they’re passionate about it.”

Quelle: How to hire a programmer when you’re not a programmer – (37signals)

Genau dies habe ich letzte Woche erlebt. Ich auf der Seite des Programmierers, auf der anderen Seite ein Headhunter, der für ein „internationales“ Unternehmen in Zürich einen Web-Entwickler suchte. Er war über Xing an meine Kontaktangaben gelangt.

Auf die Frage, ob ich Erfahrung in ASP.NET hätte, erwiderte ich ein klares Nein, um anzufügen, dass ich das letzte Mal im Jahr 2000 ASP programmiert hätte. ASP war damals mein erster Einstieg in webbasierte Scriptingsprachen. Innert weniger Monate wurde ich dann aber äusserst rasch auf die gute Seite der Macht gezogen — und entwickelte fortan auf den LAMP-Stack aufbauend.

Der Headhunter hakte nach: Ob ich es mir denn vorstellen könne, ASP.NET zu erlernen? Darauf erwirderte ich ein klares und dezidiertes „Nein“. Ich, der Mac OS X/Linux-Fan, der plötzlich in Visual Studio rumeiert? Das wäre wie wenn ein Kommunist zur SD überlaufen würde. Oder ein Wechselstromverfechter ins Camp der Gleichstromfreaks übertreten würde.

Ich habe mich noch ein/zwei Male gefragt, ob ich wirklich die richtige Antwort gegeben habe — doch mit obiger Bemerkung von Seiten der Web-Entwicklerprofis bin ich ein für allemal sicher, dass ich mich richtig entschieden habe.

Tags: , , , , , , ,
Labels: IT, Linux, Web

Keine Kommentare | neuen Kommentar verfassen

Mittwoch, 22. September 2010

Höhere Datenbankkunde mit Facebook

„We need some way of applying the changes that we missed after the copy was started,“ he said. OSC does so using database triggers. „When the copy finishing, we replay all of the changes that were logged by the triggers, and then we briefly – for a fraction of a second – block access to the table and then we switch the original table with the copy.“

Quelle: Facebook open sources live MySQL makeover • The Register

Jede populäre Web-Applikation kann an einen Punkt heranwachsen, bei dessen Überschreitung das mit den Datenbanken kein Spass mehr ist, sondern eine grosse, grosse Bürde. Inkrementelle Backups und Schema-Upgrades — halleluja. Aber meist gibt es halt keinen Weg drumherum.

Tags: , , ,
Labels: IT, Web

Keine Kommentare | neuen Kommentar verfassen

Dienstag, 27. Juli 2010

Vacation-Meldung als Antwort auf automatisch generierte Mails verhindern

Sommerzeit — Ferienzeit — Vacation-Meldungszeit!

Dies hat heute dazu geführt, dass auf der Arbeit auf automatisch generierte Mails (mit Infos über Änderungen im Personalverzeichnis) mit Vacation-Meldungen der angeschriebenen Mitarbeiterinnen geantwortet wurde.

Um dies zu verhindern, sollte es ausreichen, wenn dem automatisch generiert Mail folgende zwei Header-Zeilen mitgegeben werden:

...
Precedence: bulk
Auto-submitted: auto-generated
...

Die meisten aktuellen Mail-Server sollten auf solche Mails nicht mehr mit einer Vacation-Meldung reagieren.

PHP Mailer

In meinem konkreten Fall sah die Anpassung im PHP-Script folgendermassen aus (ich verwende die vorzügliche Klasse PHP Mailer, um Mails zu generieren und zu versenden):

...
$mail->AddCustomHeader('Precedence: bulk');
$mail->AddCustomHeader('Auto-submitted: auto-generated');
...

Und still ward’s auf dem Web-Server …

Tags: , , ,
Labels: Allgemein

Keine Kommentare | neuen Kommentar verfassen

Samstag, 20. Februar 2010

Zugriff auf SVN-Verzeichnisse im Web-Root verhindern

Vor einigen Monaten standen auf unzähligen Web-Sites von professionellen Web-Entwicklern die Tore sperrangelweit offen: Auf Grund der Nachlässigkeit der Entwickler waren deren mit SVN versionierten Projekte statt mit svn export mit svn checkout auf das Produktivsystem ausgecheckt worden — und so gelangten automatisch die .svn-Verzeichnisse mit ins Web-Root.

Da Apache nachlässig konfiguriert war, hatte anschliessend jedermann mit einer klitzekleinen spielerischen Ader Zugriff auf die Struktur und den Source-Code einer jeden so Web-Site. Versuchen wir es gleich mal: Man hänge an die Domain versuchsweise „.svn“ an, wie beispielsweise bei www.stromzukunft.ch/.svn. In diesem Fall ist ein .svn-Ordner vorhanden, weil nicht ein 404er, sondern ein 403er angezeigt wird. Der Server wurde aber glücklicherweise längst gegen solche „Schnupperattacken“ gesichert …

Auch ich gehöre zu jenen Entwicklern, die sich bis zu diesem Zeitpunkt kaum über die „Best Practices“ der Entwicklergemeinde geschert hatten — tatsächlich bin ich auch heute immer noch so faul und verwende oftmals das verpönte svn checkout auf Produktivsystemen.

Um sich dennoch nicht gleich mit heruntergelassenen Hosen im Netz zu präsentieren, sollten solche unbelehrbaren Entwickler immerhin ihre /etc/apache2/apache2.conf anpassen und in dieser den Zugriff auf jedes .svn-Verzeichnis grundsätzlich verwehren:

...
<DirectoryMatch \.svn>
   Order allow,deny
   Deny from all
</DirectoryMatch>
...

Tags: , , ,
Labels: Web

2 Kommentare | neuen Kommentar verfassen