Alexander (HH): Endergebnis & Danke

Beitrag lesen

Moin Moin!

  // check for invalid characters  
  for ($i = 0; $i < strlen($str); $i++) {  
  	$c = $str[$i];  
  	$cCode = ord($c);  
  	  
  	if ($cCode < 32 || $cCode > 127) {  
  		// control characters and non-ascii characters not allowed  
  		return false;  
  	}  
  	  
  	if (strpos('<>,; :\'"', $c) !== FALSE) {  
  		// invalid characters  
  		return false;  
  	}  
  	  
  	if ($i == (strlen($str) - 1) && $c == '.') {  
  		// last character should be no dot  
  		return false;  
  	}  
  
Das Stückchen Code sieht für mich fürchterlich ineffizient aus.  
  
1\. Die Prüfung auf den Punkt am Ende würde ich vor die Schleife ziehen, das spart für jedes einzelne Zeichen in $str einen Aufruf von strlen.  
  
~~~php
  
if ($str[strlen($str)-1]=='.') {  
  return false; // Alternativ: $str um ein Zeichen verkürzen, denn der Punkt ist KORREKT, nur ungewöhnlich  
}  

Das sieht so fürchterlich nach C aus:

  
if (str[strlen(c)-1]=='.') {  
  return 0;  
  /* alternativ: */  
  str[strlen(c)-1]=0;  
}  

Kann PHP das nicht einfacher? Perl kann direkt auf das letzte Zeichen zugreifen, ohne strlen zu bemühen:

  
if (substr($str,-1) eq '.') {  
  return;  
  # oder:  
  substr($str,-1)='';  
}  
# oder gleich per RE:  
$str=~s/\.$//;  

2. Ich würde nicht jedes einzelne Zeichen in einer interpretierten Schleife darauf hin untersuchen, ob es legal oder illegal ist. Vor allem würde ich es mir verkneifen, für jedes einzelne Zeichen in $str nochmal strpos() aufzurufen, das eine weitere Schleife darstellt.

Du prüfst hier effektiv, ob $str ausschließlich aus einem vorgegebenen Satz von Zeichen besteht. Das ist ein Fall für die schnelle Regular Expression Engine.

Die Blacklist-Version in Perl (analog in PHP nachbaubar):

  
if ($str=~/[^\x00-\x20<>,;:'"\\\x7F-\xFF]/) {  
  # Explizit verboten:  
  # * ASCII-Steuerzeichen von 0x00=NUL bis einschließlich 0x20=Space  
  # * <>,;.'"\  
  # * ASCII-Zeichen ab 0x7F=DEL  
  return;  
}  

Ein einziges böses Zeichen bricht das Matching ab und rennt ins return.

Haken: $str muß Bytes enthalten, keine UTF-8-Characters.
Blacklist mit UTF-8:

  
if ($str=~/[^\x00-\x20<>,;:'"\\\x7F-\x{10FFFF}]/) {  
  # Explizit verboten:  
  # * ASCII-Steuerzeichen von 0x00=NUL bis einschließlich 0x20=Space  
  # * <>,;.'"\  
  # * ASCII-Zeichen ab 0x7F=DEL einschließlich aller Unicode-Planes  
  return;  
}  

Whitelist-Version in Perl, mit etwas weniger erlaubten Zeichen (aus Tippfaulheit):

  
# Funktioniert mit Bytes und Characters.  
unless ($str=~/^[\@0-9A-Za-z._-]+$/) {  
  # Explizit erlaubt:  
  # * @ -- weil die gesamte Adresse gematcht wird  
  # * Ziffern, Buchstaben, und ausgesuchte Sonderzeichen (Minus muß aus technischen Gründen ganz hinten stehen)  
  # Der gesamte String muß aus diesen Zeichen bestehen. Die RE-Engine bricht beim ersten Mismatch ab, der Code rennt ins return.  
  return;  
}  

Ich würde die Whitelist-Version vorziehen, weil Du dann 100% garantieren kannst, welche Zeichen an die nicht escapende mail()-Funktion gehen.

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".