// Little known PHP features: method chaining, enabling you to call methods the short way

Since PHP5, methods can return objects (including $this). This enables you to chain the method calls after preparing your class by returning the object itself. Therefore, “Method chaining” may save you e.g. much copy & paste or chars to type, reducing typing time for dozens of $obj->method() calls.

<?php
 
//common way
class foo {	
    public function one() {
        echo "one ";
    }
    public function two() {
        echo "two ";
    }
    public function three() {
        echo "three\n\n";
    }
}
$object = new foo();
$object->one();
$object->two();
$object->three();
 
 
//with method chaining (note the "return $this;")
class bar {	
    public function one() {
        echo "one ";
        return $this;
    }
    public function two() {
        echo "two ";
        return $this;
    }
    public function three() {
        echo "three\n\n";
        return $this;
    }
}
$object = new bar();
$object->one()
       ->two()
       ->three();
?>

I did not made any performance measurements right now… so I can't say if method chaining is faster/slower in common environments or not. And to be honest, I don't use method chaining for myself:

  • You simply can't see if a class supports method chaining without trying it or looking at its source.
  • The need for all these return $this; within the class methods.
  • IMHO, both reasons are leading to inconsistent coding styles and behaviors in real world applications containing 3rd party classes.

// HMAC-SHA-1 for PHP

Hash-based Message Authentication Codes (HMAC)1) are very useful. Especially HMAC-SHA-1 is used by more and more webservices (e.g. Amazon S3) to verify if a request comes from a valid user (by using a shared secret/key + submitting the result of HMAC-SHA-1($request)). The easiest way to generate them – hash_hmac() – is only available for PHP > 5.1.2 and I saw many system where the function is not available. I even saw people installing the whole PEAR system just to get PEAR::Crypt_HMAC running. :-O

If you need a simple function for creating SHA-1 based HMACs, you may be interested in the following:

hmac-sha1.php
<?php
 
/**
 * Returns the HMAC-SHA-1 of a string
 *
 * @param string The data to hash.
 * @param string The key to use. Use ASCII only for best compatibility.
 *        Otherwise, you have to take care about using the same encoding in
 *        every case.
 * @param bool (optional) TRUE leads to PHP warnings if a non-ASCII-string was
 *        submitted as key. FALSE will suppress this check. Default is TRUE.
 * @return string The HMAC-SHA1 of the data.
 * @author Andreas Haerter
 * @link http://en.wikipedia.org/wiki/HMAC
 * @link http://tools.ietf.org/html/rfc2104
 * @link http://blog.andreas-haerter.com/2010/09/30/hmac-sha-1-php
 * @license GPLv2 (http://www.gnu.org/licenses/gpl2.html)
 * @license New/3-clause BSD (http://opensource.org/licenses/bsd-license.php)
 */
function hmac_sha1($str, $key, $warn_nonasciikey = true)
{
	//check: key consists of ASCII chars only?
	//this should prevent unexpected (=not equal results) when mixing this
	//implementation and base64_encode(hash_hmac("sha1", $str, $key, true))
	//regarding different encodings etc.
	if (!empty($warn_nonasciikey)
	    //search for any bytes which are outside the ASCII range...
	    //note: the regex is *REALLY* fast. Even a "quickcheck" with ctype_alnum()
	    //      won't make the things faster but slower on *common* input!
	    && preg_match('/(?:[^\x00-\x7F])/u', $key) === 1) {  //ATTENTION: single quotes are needed here! Otherwise, PCRE is not able to find the ending delimiter!
		//inform developers
		trigger_error(//text
		              __FUNCTION__.":non-ASCII key may lead to unexpected results when switching encodings!",
		              //type
		              E_USER_WARNING);
	}
 
	//use PHP's built in functionality if available (~20% faster than the
	//following script implementation)
	if (function_exists("hash_hmac")) {
		return base64_encode(hash_hmac("sha1", $str, $key, true));
	}
	//create the secret based on the given key
	$key_lenght = strlen($key);
	//key is longer than 64 bytes, use the hash of it
	if ($key_lenght > 64) {
		$key        = sha1($key);
		$key_length = 40;
	}
	//pad secret with 0x0 to get a 64 byte secret?
	if ($key_lenght < 64) {
		$secret = $key.str_repeat(chr(0), (64 - $key_lenght));
	} else {
		//64 bytes long, we can use the key directly
		$secret = $key;
	}
	//hash and return it
	return base64_encode(sha1(//create the string we have to hash
	                          ($secret^str_repeat(chr(0x5c), 64)). //pad the key for inner digest
	                          //subhash
	                          sha1(//create substring we have to hash
	                               ($secret^str_repeat(chr(0x36), 64)). //pad the key for outer digest
	                               $str,
	                               //we need RAW output!
	                               true),
	                          //we need RAW output!
	                          true));
}
 
//example
echo hmac_sha1("this is the data to hash", "my secret key, ASCII only for best compatibility");
 
?>

The source code of this function is dual-licensed under GPLv2 and New/3-clause BSD. Have fun. :-)

1)
see RFC2104 for details

// PHP: Happy 15th Birthday!

Fifteen successful years are gone since Rasmus Lerdorf released PHP 1.0 – a long time for a software, especially for a language generally grown and maintained as an open source project. Here's to the next 15 years!

I did my first steps regarding web development with PHP3, first serious projects with PHP4.1 (back then, MSIE 6 was brand new and innovative as hell LOL).

Some links, containing more background information:

// Little known PHP features: calling echo with multiple parameters

First of all, many PHP newbies do not even know that echo is not a function but a language construct. This means, the following two lines of code do the same:

  echo "Hello world\n"; //because echo is NO function, brackets are not needed.
  echo("Hello world\n");

IMHO, you should use the first variant without brackets to signalize that echo is not a function2).

But even experienced developers do not know the possibility to pass more than one parameter - there is no need to concatenate strings with a dot (which may be useful in some situations). The following code does the same three times:

  $str1 = "one";
  $str2 = "two";
  $str3 = "three\n\n";
 
  //newbie style, most overhead because echo is called more often than needed
  echo $str1;
  echo $str2;
  echo $str3;
 
  //common style with concatenated string (on my machines with PHP 5.2,
  //64bit *ix, this is the fastest)
  echo $str1.$str2.$str3;
 
  //little known: pass more than one parameter (on my machines with PHP <5.1,
  //this is the fastest)
  echo $str1, $str2, $str3;

My personal experience/small note about the performance: the more variables are involved + the bigger their data is, the slower is a concatenated string in comparison to passing the vars as parameter. But the difference is getting really small on PHP >=5.2. Additionally, echo is really fast, no matter if you use concatenation or commas. Just prevent unneeded echo calls and everything is fine. :-)

2)
BTW: same with include[_once] and require[_once]

// HTTP digest authentication with PHP (safe mode enabled)

Doing HTTP digest authentication with PHP is an easy task. The manual entry is providing all needed information as long as you do not skip the last note on the page:

Note: If safe mode is enabled, the uid of the script is added to the realm part of the WWW-Authenticate header.

I just talked to one of my friends who did not notice the safe mode behavior, he had problems because HTTP digest authentication simply did not work on his server where safe mode is active. Unfortunately, the manual is not providing an example working with both active and inactive safe mode, therefore I am releasing one here. You may use the function directly… or better build a nice auth-class for doing the job. However, I think the example should help in both cases providing all needed information for creating your own HTTP digest authentication. Have fun. :-)

http-auth.php
<?php
/**
 * HTTP digest authentication
 *
 * @return true TRUE if everything worked/auth was successful.
 *         In case of errors and/or wrong credentials, the script will be killed
 *         (providing a message to the current client).
 * @author Andreas Haerter
 * @link http://en.wikipedia.org/wiki/Digest_access_authentication
 * @link http://de.wikipedia.org/wiki/HTTP-Authentifizierung
 * @link http://www.php.net/manual/features.http-auth.php
 * @link http://blog.andreas-haerter.com/2010/04/19/http-digest-authentication-with-php-safe-mode-enabled
 * @link http://www.php.net/manual/features.http-auth.php#93427
 */
function http_digest_authentication()
{
	//existing users/credentials
	$users = array("username1" => "password1",
	               "username2" => "password2");
 
	//message to show
	$realm = "Please enter your credentials";
 
	//send needed digest auth headers
	if (empty($_SERVER["PHP_AUTH_DIGEST"])) {
		header("HTTP/1.1 401 Unauthorized");
		header("WWW-Authenticate: Digest realm=\"".$realm."\",qop=\"auth\",nonce=\"".uniqid(mt_rand(), true)."\",opaque=\"".md5($realm."salt-for-opaque")."\"");
		die("unauthorized access");
	}
 
	//parse http digest (inspired through http://www.php.net/manual/features.http-auth.php#93427)
	$mandatory = array("nonce"    => true,
	                   "nc"       => true,
	                   "cnonce"   => true,
	                   "qop"      => true,
	                   "username" => true,
	                   "uri"      => true,
	                   "response" => true);
	$data = array();
	preg_match_all('@(\w+)=(?:(?:\'([^\']+)\'|"([^"]+)")|([^\s,]+))@', $_SERVER["PHP_AUTH_DIGEST"], $matches, PREG_SET_ORDER);
	foreach ($matches as $m) {
		$data[$m[1]] = $m[2] ? $m[2] : ($m[3] ? $m[3] : $m[4]);
		unset($mandatory[$m[1]]); //mandatory part was found, kick it out of the "to do" list (=$mandatory array)
	}
 
	//create valid digest to validate the credentials
	$digest = "";
	if (isset($users[$data["username"]])) {
		$realm_digest = $realm;
		//As mentioned at <http://www.php.net/manual/en/features.http-auth.php>:
		//If safe mode is enabled, the uid of the script is added to the realm part of
		//the WWW-Authenticate header (you cannot supress this!). Therefore we have to
		//do this here, too.
		if (6 > (int)PHP_VERSION //safe_mode will be removed in PHP 6.0
		    && (int)ini_get("safe_mode") !== 0) {
			$realm_digest .= "-".getmyuid();
		}
		$digest = md5(md5($data["username"].":".$realm_digest.":".$users[$data["username"]]) //A1
		              .":".$data["nonce"].":".$data["nc"].":".$data["cnonce"].":".$data["qop"].":"
		              .md5($_SERVER["REQUEST_METHOD"].":".$data["uri"]));                    //A2
	}
	if (empty($digest)
	    || $data["response"] !== $digest) {
		header("HTTP/1.1 401 Unauthorized");
		header("WWW-Authenticate: Digest realm=\"".$realm."\",qop=\"auth\",nonce=\"".uniqid(mt_rand(), true)."\",opaque=\"".md5($realm."salt-for-opaque")."\"");
		die("wrong credentials");
	}
	//if we are here, auth was successful
	return true;
}
?>

// Bessere Performance durch PHP-Klassenkonstanten statt define()

Kleiner PHP-Tipp am Rande: wer wie ich Konfigurationswerte und andere eigentlich konstanten Werte äußerst ungern in nicht-konstanten Variablen oder Arrays unterbringt (z.B. $_CONFIG['foo'] = “bar”;), sollte sich dennoch das massenhafte Einsetzen von define() gut überlegen. Eine bessere Alternative stellen Klassenkostanten dar:

class config {
    const foo = "bar";
}
echo config::foo;

define() ist relativ langsam und hat einen nicht unerheblichen Ausführungs-Overhead. Auch apc_define_constants() hilft nicht wirklich weiter, da nur geringfügige Geschwindigkeitsvorteile erzielbar sind (und man auf IMHO hässliche Art und Weise in den Code eingreifen und sich an den ggf. nicht verfügbaren APC bindet).

Klassenkonstanten haben hingegen enorme Vorteile:

  • Klassenkonstanten werden zur Kompilierzeit geparst, es gibt keinen Ausführungs-Overhead.
  • Genau deshalb können sie OP-Code-Cache freundlich verarbeitet werden (define() wird immer wieder ausgeführt, OP-Code-Cache hin oder her!). Dies ist IMHO auch der Hauptvorteil, der für einen Einsatz in performance-krtitischen Anwendungen spricht, da OP-Code-Caches gut greifen ohne den Code anfassen zu müssen.
  • auf Klassenkonstanten kann performanter zugegriffen werden (kleinere Hashs!)
  • man hat nen schicken Namespace.

Also, falls man sich noch nicht damit befasst hat nochmal nachlesen, einsetzen und über die Vorteile freuen. :-)

// Da will man einmal was von der Bundesnetzagentur...

…und dann ist deren Website down. Lustig dass da Windows als Webserver rennt. Und die noch die alte mysql-PHP-Lib einsetzen. Folglich auch keine Stored Procedures (sind erst via mysqli nutzbar). Dazu passt dann, dass Fehlermeldungen öffentlich rausgepustet werden. Weia. :-P

I'm no native speaker (English)
Please let me know if you find any errors (I want to improve my English skills). Thank you!
QR Code: URL of current page
QR Code: URL of current page start (generated for current page)