PHP - Data Push Apps with HTML5 SSE (2014)

Data Push Apps with HTML5 SSE (2014)

Appendix C. PHP

PHP has been chosen as the principal language for the examples in this book, for various reasons. When combined with Apache, it allows the example code to be quite short, with very little scaffolding code needed. The syntax is straightforward and readable by anyone familiar with any mainstream programming language. And it fits the existing infrastructure advantage (see Comparison with WebSockets) of SSE very nicely, because a lot of people’s existing infrastructure is already built on top of PHP.

As I said, I have tried hard to make the PHP code readable by any programmer. This appendix explains those few features where something a bit more PHP-specific was used. Explanations link directly to the related section in this appendix, at the time the feature is used.

This appendix is not intended as a general introduction to PHP. There are countless books and websites that can do that for you. The PHP online documentation is an excellent starting point. If you are looking for a book in the context of dynamic websites, Robin Nixon’s Learning PHP, MySQL, JavaScript, and CSS (O’Reilly) looks comprehensive. Programming PHP, by Kevin Tatroe, Peter MacIntyre, and Rasmus Lerdorf (O’Reilly), focuses on just the PHP language, again in the context of dynamic web pages.

Classes in PHP

Classes in PHP (as of PHP5) are closer to the classes in C++, C#, and Java than the classes you find in JavaScript, but if you have used any object-oriented language before the code used in this book should be understandable.

Each element of the class is prefixed with an access modifier: public or private. Functions are then preceded with the keyword function, and items without the function prefix are member variables. The constructor is a function that is run at the time the object is created, and is called__construct(). So fxpair.seconds.php uses encapsulation to have a number of private variables, a constructor to initialize those variables, and then a single public function to do something with a mix of those private variables and the function parameters it is given.

Member variables (and member functions) are accessed by prefixing with $this->. This is similar to JavaScript, which uses this. as a prefix. In other OOP languages, such as C++, the use of this. is considered optional (though some style guides suggest it).

For more information on OOP in PHP, please see the manual at http://php.net/class.

Random Functions

PHP has functions called rand and mt_rand—how to choose between them? We use mt_rand. The MT stands for Mersenne Twister, and gives us better-quality random numbers. (Some places also claim it is notably faster, others that rand and mt_rand are about the same speed; this appears to depend on your operating system and PHP version.)

Use mt_srand to set the random seed for mt_rand. Setting the same random seed each time allows you to get the same sequence of “random” numbers every time. This is wonderful for repeatable testing. If you want different random numbers each time, there is no particular need to callmt_srand because PHP will initialize the seed for you (based on the current server clock).

Superglobals

PHP has some superglobals, which are available in all functions, and give you ready-parsed information about the request, as well about the machine environment. They are all associative arrays. $_GET is the HTTP GET data, $_POST is the HTTP POST data, and $_COOKIES is…well, you guessed it. $_SERVER will tell you other information about the request, while $_ENV will tell you about the machine you are running on. $GLOBALS gives access to user-defined global variables.

There is also $_REQUEST, which is a combination of all of $_GET, $_POST, and $_COOKIES. Be aware that using $_REQUEST is usually discouraged, because there are security implications of cookie data overriding your form data. You should use $_REQUEST—and only use $_REQUEST—when the variable you are interested in could validly have come from any of GET, POST, or cookie data.

However, note that since PHP 5.3, the default php.ini file will exclude cookie data from $_REQUEST. See http://php.net/request_order. So, you now have to set request_order to “CGP” if you explicitly want to allow cookies to be included in $_REQUEST. By putting “C” first, POST gets priority over GET, which gets priority over cookie data.

Date Handling

PHP has some powerful functions for dealing with times and dates. time returns the time in seconds since 1970; we have seen it before. Another couple we have seen before are date() and gmdate(), which turn Unix time into a string for local and GMT time zones, respectively. They come with more format options than you can shake a stick at—take a look at http://php.net/date.

If I had to name a single PHP function I miss more than any other when using other languages, it is strtotime(). This takes a date in string form and returns it in Unix time (seconds since January 1, 1970). Of course it deals with standard timestamp formats, such as “2013-12-25 13:25:50,” as well as dealing with the month in words, e.g., “25th December 2013.” But it gets better! You can also give date offsets, so you can write strtotime("+1 day") to get the timestamp for 24 hours from now. You can write “last day of February.” And you can write “next month,” “last Thursday,” etc.

By default, the calculations are relative to the current time. But by specifying the second parameter you can have it relative to any other time. And that second parameter could be using strtotime too! Here is an example that finds the last Friday before Christmas in the year 2001:

$friday = strtotime("last Friday",

strtotime("2001-12-25"));

echo date("Y-m-d",$friday);

//2001-12-21

Passwords

User passwords should obviously not be stored in a database as plain text. Though better than nothing, encryption is also usually a bad idea—if the key is stolen, all the passwords can be recovered. Instead of encryption you should be hashing your passwords. Hashing is a one-way process: it takes the plain-text password, and applies a number of mathematical operations to it to give some random-looking string. You cannot reverse the operation, and there is no key to be stolen. But given the same plain-text password, you will always get the same hashed password out of the algorithm.

However, with rogues and rogue governments building faster and faster computers to hack you, your grandfather’s password hashing algorithms are no longer good enough. It used to be that md5() was enough, and if you used salt with it you could hold your head high in the nerdiest of company. But no longer.

As of PHP 5.5.0 the security best practice is to use password_hash() to make your passwords, and password_verify() to validate them. If you are using an earlier version of PHP, add the following code to the top of your script[44] (the if(!defined(...)){...} wrapper simply means this block will be ignored when the script is run on PHP 5.5 or later):

if (!defined("PASSWORD_DEFAULT")) {

function password_hash($password){

$salt = str_replace("+", ".", base64_encode(sha1(time(),true)));

$salt = substr($salt, 0, 22); //We want exactly 22 characters

if(PHP_VERSION_ID<50307)return crypt($password, '$2a$10$'.$salt);

else return crypt($password, '$2y$10$'.$salt);

}

function password_verify($password, $hash) {

return crypt($password,$hash) === $hash;

}

} //End of if (!defined('PASSWORD_DEFAULT'))

PHP 5.3.7 introduced a new, better hashing algorithm. The preceding code uses that when available; otherwise, it uses the previous best choice. The 10 in '$2y$10$' is a measure of how slow to be. In the weird world of password hashing, slow is good; 10 is the default as of PHP 5.5. It means password hashing might take a significant fraction of a CPU second. Read the PHP manual’s descriptions of these functions if you want to tweak these parameters. To be future-proof, use a VARCHAR(255) field for storing password hashes in an SQL database, though currently they will always be exactly 60 characters long.

Falling Asleep

There are two easily confused functions in PHP: sleep() and usleep(). The former takes an integer, the number of seconds to sleep. The latter also takes an integer, but as the number of microseconds to sleep. So, for example, sleep(2) and usleep(2000000) are identical; they both put the script to sleep for two seconds. However, if you want to sleep for 0.25 seconds, or 1.5 seconds, your only choice is to use usleep (usleep(250000) and usleep(1500000), respectively).

This is a good time to mention max_execution_time (a configuration setting) and set_time_limit() (a function to allow resetting the max_execution_time). The special value of zero means “run forever,” and that is the default when you run scripts from the command line. However, through a web browser, the default is 30 seconds; with Linux/Mac, that is 30 seconds of CPU time, but with Windows it is measured as wall-clock time. For a streaming server, that 30 seconds will come quite quickly; you might not even notice unless you look at your browser console, but your SSE script will end up reconnecting to the backend every 30 seconds. (Unless doing something compute-intensive, over on Linux you won’t notice it for tens of minutes or even hours.)

The fix is simple—at the very top of your script add this line:

set_time_limit(0);


[44] For a more comprehensive version of the PHP 5.5 functions, see https://github.com/ircmaxell/password_compat.