BUY THIS BOOK

Safari Books Online

What is this?

Looking to Reprint this content?


PHP Cookbook
PHP Cookbook

By David Sklar, Adam Trachtenberg

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: Strings
Strings in PHP are a sequence of characters, such as "We hold these truths to be self evident," or "Once upon a time," or even "111211211." When you read data from a file or output it to a web browser, your data is represented as strings.
Individual characters in strings can be referenced with array subscript style notation, as in C. The first character in the string is at index 0. For example:
$neighbor = 'Hilda';
print $neighbor[3];
d
         
However, PHP strings differ from C strings in that they are binary-safe (i.e., they can contain null bytes) and can grow and shrink on demand. Their size is limited only by the amount of memory that is available.
You can initialize strings three ways, similar in form and behavior to Perl and the Unix shell: with single quotes, with double quotes, and with the "here document" (heredoc) format. With single-quoted strings, the only special characters you need to escape inside a string are backslash and the single quote itself:
print 'I have gone to the store.';
print 'I\'ve gone to the store.';
print 'Would you pay $1.75 for 8 ounces of tap water?';
print 'In double-quoted strings, newline is represented by \n';
I have gone to the store.
            I've gone to the store.
            Would you pay $1.75 for 8 ounces of tap water?
            In double-quoted strings, newline is represented by \n
         
Because PHP doesn't check for variable interpolation or almost any escape sequences in single-quoted strings, defining strings this way is straightforward and fast.
Double-quoted strings don't recognize escaped single quotes, but they do recognize interpolated variables and the escape sequences shown in Table 1-1.
Table 1-1: Double-quoted string escape sequences
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Introduction
Strings in PHP are a sequence of characters, such as "We hold these truths to be self evident," or "Once upon a time," or even "111211211." When you read data from a file or output it to a web browser, your data is represented as strings.
Individual characters in strings can be referenced with array subscript style notation, as in C. The first character in the string is at index 0. For example:
$neighbor = 'Hilda';
print $neighbor[3];
d
         
However, PHP strings differ from C strings in that they are binary-safe (i.e., they can contain null bytes) and can grow and shrink on demand. Their size is limited only by the amount of memory that is available.
You can initialize strings three ways, similar in form and behavior to Perl and the Unix shell: with single quotes, with double quotes, and with the "here document" (heredoc) format. With single-quoted strings, the only special characters you need to escape inside a string are backslash and the single quote itself:
print 'I have gone to the store.';
print 'I\'ve gone to the store.';
print 'Would you pay $1.75 for 8 ounces of tap water?';
print 'In double-quoted strings, newline is represented by \n';
I have gone to the store.
            I've gone to the store.
            Would you pay $1.75 for 8 ounces of tap water?
            In double-quoted strings, newline is represented by \n
         
Because PHP doesn't check for variable interpolation or almost any escape sequences in single-quoted strings, defining strings this way is straightforward and fast.
Double-quoted strings don't recognize escaped single quotes, but they do recognize interpolated variables and the escape sequences shown in Table 1-1.
Table 1-1: Double-quoted string escape sequences
Escape sequence
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Accessing Substrings
You want to extract part of a string, starting at a particular place in the string. For example, you want the first eight characters of a username entered into a form.
Use substr( ) to select your substrings:
$substring = substr($string,$start,$length);
$username = substr($_REQUEST['username'],0,8);
If $start and $length are positive, substr( ) returns $length characters in the string, starting at $start. The first character in the string is at position 0:
print substr('watch out for that tree',6,5);
out f
            
If you leave out $length, substr( ) returns the string from $start to the end of the original string:
print substr('watch out for that tree',17);
t tree
            
If $start plus $length goes past the end of the string, substr( ) returns all of the string from $start forward:
print substr('watch out for that tree',20,5);
ree
            
If $start is negative, substr( ) counts back from the end of the string to determine where your substring starts:
print substr('watch out for that tree',-6);
print substr('watch out for that tree',-17,5);
t tree
               out f
            
If $length is negative, substr( ) counts back from the end of the string to determine where your substring ends:
print substr('watch out for that tree',15,-2);
print substr('watch out for that tree',-4,-1);
hat tr
               tre
            
Documentation on substr( ) at http://www.php.net/substr.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Replacing Substrings
You want to replace a substring with a different string. For example, you want to obscure all but the last four digits of a credit card number before printing it.
Use substr_replace( ) :
// Everything from position $start to the end of $old_string
// becomes $new_substring
$new_string = substr_replace($old_string,$new_substring,$start);

// $length characters, starting at position $start, become $new_substring
$new_string = substr_replace($old_string,$new_substring,$start,$length);
Without the $length argument, substr_replace( ) replaces everything from $start to the end of the string. If $length is specified, only that many characters are replaced:
print substr_replace('My pet is a blue dog.','fish.',12);
print substr_replace('My pet is a blue dog.','green',12,4);
$credit_card = '4111 1111 1111 1111';
print substr_replace($credit_card,'xxxx ',0,strlen($credit_card)-4);
My pet is a fish.
               My pet is a green dog.
               xxxx 1111
            
If $start is negative, the new substring is placed at $start characters counting from the end of $old_string, not from the beginning:
print substr_replace('My pet is a blue dog.','fish.',-9);
print substr_replace('My pet is a blue dog.','green',-9,4);
My pet is a fish.
               My pet is a green dog.
            
If $start and $length are 0, the new substring is inserted at the start of $old_string:
print substr_replace('My pet is a blue dog.','Title: ',0,0);
Title: My pet is a blue dog.
The function substr_replace( ) is useful when you've got text that's too big to display all at once, and you want to display some of the text with a link to the rest. For example, this displays the first 25 characters of a message with an ellipsis after it as a link to a page that displays more text:
$r = mysql_query("SELECT id,message FROM messages WHERE id = $id") or die( );
$ob = mysql_fetch_object($r);
printf('<a href="more-text.php?id=%d">%s</a>',
       $ob->id, substr_replace($ob->message,' ...',25));
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Processing a String One Character at a Time
You need to process each character in a string individually.
Loop through each character in the string with for. This example counts the vowels in a string:
$string = "This weekend, I'm going shopping for a pet chicken.";
$vowels = 0;
for ($i = 0, $j = strlen($string); $i < $j; $i++) {
    if (strstr('aeiouAEIOU',$string[$i])) {
        $vowels++;
    }
}
Processing a string a character at a time is an easy way to calculate the "Look and Say" sequence:
function lookandsay($s) {
    // initialize the return value to the empty string
    $r = '';
    /* $m holds the character we're counting, initialize to the first
    // character in the string*/
    $m = $s[0];
    // $n is the number of $m's we've seen, initialize to 1
    $n = 1;
    for ($i = 1, $j = strlen($s); $i < $j; $i++) {
        // if this character is the same as the last one
        if ($s[$i] == $m) {
            // increment the count of this character
            $n++;
        } else {
            // otherwise, add the count and character to the return value 
            $r .= $n.$m;
            // set the character we're looking for to the current one 
            $m = $s[$i];
            // and reset the count to 1
            $n = 1;
        }
    }
    // return the built up string as well as the last count and character
    return $r.$n.$m;
}

for ($i = 0, $s = 1; $i < 10; $i++) {
    $s = lookandsay($s);
    print "$s\n";
}
1
               11
               21
               1211
               111221
               312211
               13112221
               1113213211
               31131211131221
               13211311123113112211
            
It's called the "Look and Say" sequence because each element is what you get by looking at the previous element and saying what's in it. For example, looking at the first element, 1, you say "one one." So the second element is "11." That's two ones, so the third element is "21." Similarly, that's one two and one one, so the fourth element is "1211," and so on.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Reversing a String by Word or Character
You want to reverse the words or the characters in a string.
Use strrev( ) to reverse by character:
print strrev('This is not a palindrome.');
.emordnilap a ton si sihT
            
To reverse by words, explode the string by word boundary, reverse the words, then rejoin:
$s = "Once upon a time there was a turtle.";
// break the string up into words
$words = explode(' ',$s);
// reverse the array of words
$words = array_reverse($words);
// rebuild the string
$s = join(' ',$words);
print $s;
turtle. a was there time a upon Once
            
Reversing a string by words can also be done all in one line:
$reversed_s = join(' ',array_reverse(explode(' ',$s)));
Recipe 18.8 discusses the implications of using something other than a space character as your word boundary; documentation on strrev( ) at http://www.php.net/strrev and array_reverse( ) at http://www.php.net/array-reverse.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Expanding and Compressing Tabs
You want to change spaces to tabs (or tabs to spaces) in a string while keeping text aligned with tab stops. For example, you want to display formatted text to users in a standardized way.
Use str_replace( ) to switch spaces to tabs or tabs to spaces:
$r = mysql_query("SELECT message FROM messages WHERE id = 1") or die();
$ob = mysql_fetch_object($r);
$tabbed = str_replace(' ',"\t",$ob->message);
$spaced = str_replace("\t",' ',$ob->message);

print "With Tabs: <pre>$tabbed</pre>";
print "With Spaces: <pre>$spaced</pre>";
Using str_replace( ) for conversion, however, doesn't respect tab stops. If you want tab stops every eight characters, a line beginning with a five-letter word and a tab should have that tab replaced with three spaces, not one. Use the pc_tab_expand( ) function shown in Example 1-1 to turn tabs to spaces in a way that respects tab stops.
Example 1-1. pc_tab_expand( )
function pc_tab_expand($a) {
  $tab_stop = 8;
  while (strstr($a,"\t")) {
    $a = preg_replace('/^([^\t]*)(\t+)/e',
                      "'\\1'.str_repeat(' ',strlen('\\2') * 
                       $tab_stop - strlen('\\1') % $tab_stop)",$a);
  } 
  return $a;
}

$spaced = pc_tab_expand($ob->message);
You can use the pc_tab_unexpand() function shown in Example 1-2 to turn spaces back to tabs.
Example 1-2. pc_tab_unexpand( )
function pc_tab_unexpand($x) {
  $tab_stop = 8;
  
  $lines = explode("\n",$x);
  for ($i = 0, $j = count($lines); $i < $j; $i++) {
    $lines[$i] = pc_tab_expand($lines[$i]);
    $e = preg_split("/(.\{$tab_stop})/",$lines[$i],-1,PREG_SPLIT_DELIM_CAPTURE);
    $lastbit = array_pop($e);
    if (!isset($lastbit)) { $lastbit = ''; }
    if ($lastbit == str_repeat(' ',$tab_stop)) { $lastbit = "\t"; }
    for ($m = 0, $n = count($e); $m < $n; $m++) {
      $e[$m] = preg_replace('/  +$',"\t",$e[$m]);
    }
    $lines[$i] = join('',$e).$lastbit;
  }
  $x = join("\n", $lines);
  return $x;
}

$tabbed = pc_tab_unexpand($ob->message);
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Controlling Case
You need to capitalize, lowercase, or otherwise modify the case of letters in a string. For example, you want to capitalize the initial letters of names but lowercase the rest.
Use ucfirst( ) or ucwords( ) to capitalize the first letter of one or more words:
print ucfirst("how do you do today?");
print ucwords("the prince of wales");
How do you do today?
               The Prince Of Wales
            
Use strtolower( ) or strtoupper( ) to modify the case of entire strings:
print strtoupper("i'm not yelling!");
// Tags must be lowercase to be XHTML compliant
print strtolower('<A HREF="one.php">one</A>');
I'M NOT YELLING!
               <a href="one.php">one</a>
            
Use ucfirst( ) to capitalize the first character in a string:
print ucfirst('monkey face');
print ucfirst('1 monkey face');
Monkey face
               1 monkey face
            
Note that the second line of output is not "1 Monkey face".
Use ucwords( ) to capitalize the first character of each word in a string:
print ucwords('1 monkey face');
print ucwords("don't play zone defense against the philadelphia 76-ers");
1 Monkey Face
               Don't Play Zone Defense Against The Philadelphia 76-ers
            
As expected, ucwords( ) doesn't capitalize the "t" in "don't." But it also doesn't capitalize the "e" in "76-ers." For ucwords( ), a word is any sequence of nonwhitespace characters that follows one or more whitespace characters. Since both ' and - aren't whitespace characters, ucwords( ) doesn't consider the "t" in "don't" or the "e" in "76-ers" to be word-starting characters.
Both ucfirst( ) and ucwords( ) don't change the case of nonfirst letters:
print ucfirst('macWorld says I should get a iBook');
print ucwords('eTunaFish.com might buy itunaFish.Com!');
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Interpolating Functions and Expressions Within Strings
You want to include the results of executing a function or expression within a string.
Use the string concatenation operator (.) when the value you want to include can't be inside the string:
print 'You have '.($_REQUEST['boys'] + $_REQUEST['girls']).' children.';
print "The word '$word' is ".strlen($word).' characters long.';
print 'You owe '.$amounts['payment'].' immediately';
print "My circle's diameter is ".$circle->getDiameter().' inches.';
You can put variables, object properties, and array elements (if the subscript is unquoted) directly in double-quoted strings:
print "I have $children children.";
print "You owe $amounts[payment] immediately.";
print "My circle's diameter is $circle->diameter inches.";
Direct interpolation or using string concatenation also works with heredocs. Interpolating with string concatenation in heredocs can look a little strange because the heredoc delimiter and the string concatenation operator have to be on separate lines:
print <<< END
Right now, the time is 
END
. strftime('%c') . <<< END
 but tomorrow it will be 
END
. strftime('%c',time() + 86400);
Also, if you're interpolating with heredocs, make sure to include appropriate spacing for the whole string to appear properly. In the previous example, "Right now the time" has to include a trailing space, and "but tomorrow it will be" has to include leading and trailing spaces.
For the syntax to interpolate variable variables (like ${"amount_$i"}), see Recipe 5.5; documentation on the string concatenation operator at http://www.php.net/language.operators.string.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Trimming Blanks from a String
You want to remove whitespace from the beginning or end of a string. For example, you want to clean up user input before validating it.
Use ltrim( ) , rtrim( ), or trim( ). ltrim( ) removes whitespace from the beginning of a string, rtrim( ) from the end of a string, and trim( ) from both the beginning and end of a string:
$zipcode = trim($_REQUEST['zipcode']);
$no_linefeed = rtrim($_REQUEST['text']);
$name = ltrim($_REQUEST['name']);
For these functions, whitespace is defined as the following characters: newline, carriage return, space, horizontal and vertical tab, and null.
Trimming whitespace off of strings saves storage space and can make for more precise display of formatted data or text within <pre> tags, for example. If you are doing comparisons with user input, you should trim the data first, so that someone who mistakenly enters "98052 " as their Zip Code isn't forced to fix an error that really isn't. Trimming before exact text comparisons also ensures that, for example, "salami\n" equals "salami." It's also a good idea to normalize string data by trimming it before storing it in a database.
The trim( ) functions can also remove user-specified characters from strings. Pass the characters you want to remove as a second argument. You can indicate a range of characters with two dots between the first and last characters in the range.
// Remove numerals and space from the beginning of the line
print ltrim('10 PRINT A$',' 0..9');
// Remove semicolon from the end of the line
print rtrim('SELECT * FROM turtles;',';');
PRINT A$
               SELECT * FROM turtles
            
PHP also provides chop( ) as an alias for rtrim( ). However, you're best off using rtrim( ) instead, because PHP's chop( ) behaves differently than Perl's
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Parsing Comma-Separated Data
You have data in comma-separated values ( CSV) format, for example a file exported from Excel or a database, and you want to extract the records and fields into a format you can manipulate in PHP.
If the CSV data is in a file (or available via a URL), open the file with fopen( ) and read in the data with fgetcsv( ). This prints out the data in an HTML table:
$fp = fopen('sample2.csv','r') or die("can't open file");
print "<table>\n";
while($csv_line = fgetcsv($fp,1024)) {
    print '<tr>';
    for ($i = 0, $j = count($csv_line); $i < $j; $i++) {
        print '<td>'.$csv_line[$i].'</td>';
    }
    print "</tr>\n";
}
print '</table>\n';
fclose($fp) or die("can't close file");
The second argument to fgetcsv( ) must be longer than the maximum length of a line in your CSV file. (Don't forget to count the end-of-line whitespace.) If you read in CSV lines longer than 1K, change the 1024 used in this recipe to something that accommodates your line length.
You can pass fgetcsv( ) an optional third argument, a delimiter to use instead of a comma (,). Using a different delimiter however, somewhat defeats the purpose of CSV as an easy way to exchange tabular data.
Don't be tempted to bypass fgetcsv( ) and just read a line in and explode( ) on the commas. CSV is more complicated than that, in order to deal with embedded commas and double quotes. Using fgetcsv( ) protects you and your code from subtle errors.
Documentation on fgetcsv( ) at http://www.php.net/fgetcsv.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Parsing Fixed-Width Delimited Data
You need to break apart fixed-width records in strings.
Use substr( ) :
$fp = fopen('fixed-width-records.txt','r') or die ("can't open file");
while ($s = fgets($fp,1024)) {
    $fields[1] = substr($s,0,10);  // first field:  first 10 characters of the line
    $fields[2] = substr($s,10,5);  // second field: next 5 characters of the line
    $fields[3] = substr($s,15,12); // third field:  next 12 characters of the line
    // a function to do something with the fields
    process_fields($fields);
}
fclose($fp) or die("can't close file");
Or unpack( ) :
$fp = fopen('fixed-width-records.txt','r') or die ("can't open file");
while ($s = fgets($fp,1024)) {
    // an associative array with keys "title", "author", and "publication_year"
    $fields = unpack('A25title/A14author/A4publication_year',$s);
    // a function to do something with the fields
    process_fields($fields);
}
fclose($fp) or die("can't close file");
Data in which each field is allotted a fixed number of characters per line may look like this list of books, titles, and publication dates:
$booklist=<<<END
Elmer Gantry             Sinclair Lewis1927
The Scarlatti InheritanceRobert Ludlum 1971
The Parsifal Mosaic      Robert Ludlum 1982
Sophie's Choice          William Styron1979
END;
In each line, the title occupies the first 25 characters, the author's name the next 14 characters, and the publication year the next 4 characters. Knowing those field widths, it's straightforward to use substr( ) to parse the fields into an array:
$books = explode("\n",$booklist);

for($i = 0, $j = count($books); $i < $j; $i++) {
  $book_array[$i]['title'] = substr($books[$i],0,25);
  $book_array[$i]['author'] = substr($books[$i],25,14);
  $book_array[$i]['publication_year'] = substr($books[$i],39,4);
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Taking Strings Apart
You need to break a string into pieces. For example, you want to access each line that a user enters in a <textarea> form field.
Use explode( ) if what separates the pieces is a constant string:
$words = explode(' ','My sentence is not very complicated');
Use split( ) or preg_split( ) if you need a POSIX or Perl regular expression to describe the separator:
$words = split(' +','This sentence  has  some extra whitespace  in it.');
$words = preg_split('/\d\. /','my day: 1. get up 2. get dressed 3. eat toast');
$lines = preg_split('/[\n\r]+/',$_REQUEST['textarea']);
Use spliti( ) or the /i flag to preg_split( ) for case-insensitive separator matching:
$words = spliti(' x ','31 inches x 22 inches X 9 inches');
$words = preg_split('/ x /i','31 inches x 22 inches X 9 inches');
The simplest solution of the bunch is explode( ). Pass it your separator string, the string to be separated, and an optional limit on how many elements should be returned:
$dwarves = 'dopey,sleepy,happy,grumpy,sneezy,bashful,doc';
$dwarf_array = explode(',',$dwarves);
Now $dwarf_array is a seven element array:
print_r($dwarf_array);
Array
               (
                   [0] => dopey
                   [1] => sleepy
                   [2] => happy
                   [3] => grumpy
                   [4] => sneezy
                   [5] => bashful
                   [6] => doc
               )
            
If the specified limit is less than the number of possible chunks, the last chunk contains the remainder:
$dwarf_array = explode(',',$dwarves,5);
print_r($dwarf_array);
Array
               (
                   [0] => dopey
                   [1] => sleepy
                   [2] => happy
                   [3] => grumpy
                   [4] => sneezy,bashful,doc
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Wrapping Text at a Certain Line Length
You need to wrap lines in a string. For example, you want to display text in <pre>/</pre> tags but have it stay within a regularly sized browser window.
Use wordwrap( ) :
$s = "Four score and seven years ago our fathers brought forth on this continen
t a new nation, conceived in liberty and dedicated to the proposition that all 
men are created equal.";

print "<pre>\n".wordwrap($s)."\n</pre>";
<pre>
               Four score and seven years ago our fathers brought forth on this continent
               a new nation, conceived in liberty and dedicated to the proposition that
               all men are created equal.
               </pre>
            
By default, wordwrap( ) wraps text at 75 characters per line. An optional second argument specifies different line length:
print wordwrap($s,50);
Four score and seven years ago our fathers brought
               forth on this continent a new nation, conceived in
               liberty and dedicated to the proposition that all
               men are created equal.
            
Other characters besides "\n" can be used for linebreaks. For double spacing, use "\n\n":
print wordwrap($s,50,"\n\n");
Four score and seven years ago our fathers brought

               forth on this continent a new nation, conceived in

               liberty and dedicated to the proposition that all

               men are created equal.
            
There is an optional fourth argument to wordwrap( ) that controls the treatment of words that are longer than the specified line length. If this argument is 1, these words are wrapped. Otherwise, they span past the specified line length:
print wordwrap('jabberwocky',5);
print wordwrap('jabberwocky',5,"\n",1);
jabberwocky

               jabbe
               rwock
               y
            
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Storing Binary Data in Strings
You want to parse a string that contains values encoded as a binary structure or encode values into a string. For example, you want to store numbers in their binary representation instead of as sequences of ASCII characters.
Use pack( ) to store binary data in a string:
$packed = pack('S4',1974,106,28225,32725);
Use unpack( ) to extract binary data from a string:
$nums = unpack('S4',$packed);
The first argument to pack( ) is a format string that describes how to encode the data that's passed in the rest of the arguments. The format string S4 tells pack( ) to produce four unsigned short 16-bit numbers in machine byte order from its input data. Given 1974, 106, 28225, and 32725 as input, this returns eight bytes: 182, 7, 106, 0, 65, 110, 213, and 127. Each two-byte pair corresponds to one of the input numbers: 7 * 256 + 182 is 1974; 0 * 256 + 106 is 106; 110 * 256 + 65 = 28225; 127 * 256 + 213 = 32725.
The first argument to unpack( ) is also a format string, and the second argument is the data to decode. Passing a format string of S4, the eight-byte sequence that pack( ) produced returns a four-element array of the original numbers:
print_r($nums);
Array
               (
                   [1] => 1974
                   [2] => 106
                   [3] => 28225
                   [4] => 32725
               )
            
In unpack( ), format characters and their count can be followed by a string to be used as an array key. For example:
$nums = unpack('S4num',$packed);
print_r($nums);
Array
               (
                   [num1] => 1974
                   [num2] => 106
                   [num3] => 28225
                   [num4] => 32725
               )
            
Multiple format characters must be separated with
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Numbers
In everyday life, numbers are easy to identify. They're 3:00 P.M., as in the current time, or $1.29, as in the cost of a pint of milk. Maybe they're like π, the ratio of the circumference to the diameter of a circle. They can be pretty large, like Avogadro's number, which is about 6 x 1023. In PHP, numbers can be all these things.
However, PHP doesn't treat all these numbers as "numbers." Instead, it breaks them down into two groups: integers and floating-point numbers. Integers are whole numbers, such as -4, 0, 5, and 1,975. Floating-point numbers are decimal numbers, such as -1.23, 0.0, 3.14159, and 9.9999999999.
Conveniently, most of the time PHP doesn't make you worry about the differences between the two because it automatically converts integers to floating-point numbers and floating-point numbers to integers. This conveniently allows you to ignore the underlying details. It also means 3/2 is 1.5, not 1, as it would be in some programming languages. PHP also automatically converts from strings to numbers and back. For instance, 1+"1" is 2.
However, sometimes this blissful ignorance can cause trouble. First, numbers can't be infinitely large or small; there's a minimum size of 2.2e-308 and a maximum size of about 1.8e308. If you need larger (or smaller) numbers, you must use the BCMath or GMP libraries, which are discussed in Recipe 2.14.
Next, floating-point numbers aren't guaranteed to be exactly correct but only correct plus or a minus a small amount. Now, this amount is small enough for most occasions, but you can end up with problems in certain instances. For instance, humans automatically convert 6 followed by an endless string of 9s after the decimal point to 7, but PHP thinks it's 6 with a bunch of 9s. Therefore, if you ask PHP for the integer value of that number, it returns 6, not 7. For similar reasons, if the digit located in the 200th decimal place is significant, floating-point numbers aren't useful. Again, the BCMath and GMP libraries ride to the rescue. But, for most occasions, PHP behaves very nicely when playing with numbers and lets you treat them just as you do in real life.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Introduction
In everyday life, numbers are easy to identify. They're 3:00 P.M., as in the current time, or $1.29, as in the cost of a pint of milk. Maybe they're like π, the ratio of the circumference to the diameter of a circle. They can be pretty large, like Avogadro's number, which is about 6 x 1023. In PHP, numbers can be all these things.
However, PHP doesn't treat all these numbers as "numbers." Instead, it breaks them down into two groups: integers and floating-point numbers. Integers are whole numbers, such as -4, 0, 5, and 1,975. Floating-point numbers are decimal numbers, such as -1.23, 0.0, 3.14159, and 9.9999999999.
Conveniently, most of the time PHP doesn't make you worry about the differences between the two because it automatically converts integers to floating-point numbers and floating-point numbers to integers. This conveniently allows you to ignore the underlying details. It also means 3/2 is 1.5, not 1, as it would be in some programming languages. PHP also automatically converts from strings to numbers and back. For instance, 1+"1" is 2.
However, sometimes this blissful ignorance can cause trouble. First, numbers can't be infinitely large or small; there's a minimum size of 2.2e-308 and a maximum size of about 1.8e308. If you need larger (or smaller) numbers, you must use the BCMath or GMP libraries, which are discussed in Recipe 2.14.
Next, floating-point numbers aren't guaranteed to be exactly correct but only correct plus or a minus a small amount. Now, this amount is small enough for most occasions, but you can end up with problems in certain instances. For instance, humans automatically convert 6 followed by an endless string of 9s after the decimal point to 7, but PHP thinks it's 6 with a bunch of 9s. Therefore, if you ask PHP for the integer value of that number, it returns 6, not 7. For similar reasons, if the digit located in the 200th decimal place is significant, floating-point numbers aren't useful. Again, the BCMath and GMP libraries ride to the rescue. But, for most occasions, PHP behaves very nicely when playing with numbers and lets you treat them just as you do in real life.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Checking Whether a String Contains a Valid Number
You want to ensure that a string contains a number. For example, you want to validate an age that the user has typed into a form input field.
Use is_numeric( ) :
if (is_numeric('five')) { /* false */ }

if (is_numeric(5))      { /* true  */ }
if (is_numeric('5'))    { /* true  */ }

if (is_numeric(-5))     { /* true  */ }
if (is_numeric('-5'))   { /* true  */ }
Besides working on numbers, is_numeric( ) can also be applied to numeric strings. The distinction here is that the integer 5 and the string 5 technically aren't the same in PHP.
Helpfully, is_numeric( ) properly parses decimal numbers, such as 5.1; however, numbers with thousands separators, such as 5,100, cause is_numeric( ) to return false.
To strip the thousands separators from your number before calling is_numeric( ) use str_replace( ):
is_numeric(str_replace($number, ',', ''));
To check if your number is a specific type, there are a variety of self-explanatorily named related functions: is_bool( ) , is_float( ) (or is_double( ) or is_real( ); they're all the same), and is_int( ) (or is_integer( ) or is_long( ) ).
Documentation on is_numeric( ) at http://www.php.net/is-numeric and str_replace( ) at http://www.php.net/str-replace.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Comparing Floating-Point Numbers
You want to check whether two floating-point numbers are equal.
Use a small delta value, and check if the numbers are equal within that delta:
$delta = 0.00001;

$a = 1.00000001;
$b = 1.00000000;

if (abs($a - $b) < $delta) { /* $a and $b are equal */ }
Floating-point numbers are represented in binary form with only a finite number of bits for the mantissa and the exponent. You get overflows when you exceed those bits. As a result, sometimes PHP (and other languages, too) don't believe two equal numbers are actually equal because they may differ toward the very end.
To avoid this problem, instead of checking if $a == $b, make sure the first number is within a very small amount ($delta) of the second one. The size of your delta should be the smallest amount of difference you care about between two numbers. Then use abs( ) to get the absolute value of the difference.
Recipe 2.4 for information on rounding floating-point numbers; documentation on floating-point numbers in PHP at http://www.php.net/language.types.float.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Rounding Floating-Point Numbers
You want to round a floating-point number, either to an integer value or to a set number of decimal places.
To round a number to the closest integer, use round( ) :
$number = round(2.4);   // $number = 2
To round up, use ceil( ):
$number = ceil(2.4);    // $number = 3
To round down, use floor( ):
$number = floor(2.4);   // $number = 2
If a number falls exactly between two integers, its behavior is undefined:
$number = round(2.5);   // $number is 2 or 3!
Be careful! As we mention in Recipe 2.3, floating-point numbers don't always work out to exact values because of how they're stored internally by the computer. This can create situations in which the obvious answer isn't. A value you expect to have a decimal part of "0.5" might instead be ".499999...9" (with a whole bunch of 9s) or ".500000...1" (with many 0s and a trailing 1). If you want to ensure that a number is rounded up as you might expect, add a small delta value to it before rounding:
$delta = 0.0000001;
$number = round(2.5 + $delta);   // $number = 3
To keep a set number of digits after the decimal point, round( ) accepts an optional precision argument. For example, if you are calculating the total price for the items in a user's shopping cart:
$cart = 54.23;
$tax = $cart * .05;
$total = $cart + $tax;       // $total = 56.9415

$final = round($total, 2);   // $final = 56.94
Recipe 2.3 for information on comparing floating-point numbers; documentation on round( ) at http://www.php.net/round.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Operating on a Series of Integers
You want to apply a piece of code over a range of integers.
Use the range( ) function, which returns an array populated with integers:
foreach(range($start,$end) as $i) {
    plot_point($i);
}
Instead of using range( ), it can be more efficient to use a for loop. Also, you can increment using values other than 1. For example:
for ($i = $start; $i <= $end; $i += $increment) {
    plot_point($i);
}
Loops like this are common. For instance, you could be plotting a function and need to calculate the results for multiple points on the graph. Or, you could be NASA counting down until the launch of the Space Shuttle Columbia.
In the first example, range( ) returns an array with values from $start to $end. Then foreach pulls out each element and assigns it to $i inside of the loop. The advantage of using range( ) is its brevity, but this technique has a few disadvantages. For one, a large array can take up unnecessary memory. Also, you're forced to increment the series one number at a time, so you can't loop through a series of even integers, for example.
As of PHP 4.1, it is valid for $start to be larger than $end. In this case, the numbers returned by range( ) are in descending order. Also, you can use iterate over character sequences:
print_r(range('l', 'p'));
Array
               (
                   [0] => l
                   [1] => m
                   [2] => n
                   [3] => o
                   [4] => p
               )
            
The for loop method just uses a single integer and avoids the array entirely. While it's longer, you have greater control over the loop, because you can increment and decrement $i more freely. Also, you can modify $i from inside the loop, something you can't do with
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Generating Random Numbers Within a Range
You want to generate a random number within a range of numbers.
Use mt_rand( ):
// random number between $upper and $lower, inclusive
$random_number = mt_rand($lower, $upper);
Generating random numbers is useful when you want to display a random image on a page, randomize the starting position of a game, select a random record from a database, or generate a unique session identifier.
To generate a random number between two end points, pass mt_rand( ) two arguments:
$random_number = mt_rand(1, 100);
Calling mt_rand( ) without any arguments returns a number between 0 and the maximum random number, which is returned by mt_getrandmax( ) .
Generating truly random numbers is hard for computers to do. Computers excel at following instructions methodically; they're not so good at spontaneity. If you want to instruct a computer to return random numbers, you need to give it a specific set of repeatable commands; the very fact that they're repeatable undermines the desired randomness.
PHP has two different random number generators, a classic function called rand( ) and a better function called mt_rand( ). MT stands for Mersenne Twister, which is named for the French monk and mathematician Marin Mersenne and the type of prime numbers he's associated with. The algorithm is based on these prime numbers. Since mt_rand( ) is more random and faster than rand( ), we prefer it to rand( ).
If you're running a version of PHP earlier than 4.2, before using mt_rand( ) (or rand( )) for the first time in a script, you need to seed the generator, by calling mt_srand( ) (or srand( )). The seed is a number the random function uses as the basis for generating the random numbers it returns; it's how to solve the repeatable versus random dilemma mentioned earlier. Use the value returned by
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Generating Biased Random Numbers
You want to generate random numbers, but you want these numbers to be somewhat biased, so that numbers in certain ranges appear more frequently than others. For example, you want to spread out a series of banner ad impressions in proportion to the number of impressions remaining for each ad campaign.
Use the pc_rand_weighted( ) function shown in Example 2-1.
Example 2-1. pc_rand_weighted( )
// returns the weighted randomly selected key
function pc_rand_weighted($numbers) {
    $total = 0;
    foreach ($numbers as $number => $weight) {
        $total += $weight;
        $distribution[$number] = $total;
    }
    $rand = mt_rand(0, $total - 1);
    foreach ($distribution as $number => $weights) {
        if ($rand < $weights) { return $number; }
    }
}
Imagine if instead of an array in which the values are the number of remaining impressions, you have an array of ads in which each ad occurs exactly as many times as its remaining number of impressions. You can simply pick an unweighted random place within the array, and that'd be the ad that shows.
This technique can consume a lot of memory if you have millions of impressions remaining. Instead, you can calculate how large that array would be (by totalling the remaining impressions), pick a random number within the size of the make-believe array, and then go through the array figuring out which ad corresponds to the number you picked. For instance:
$ads = array('ford' => 12234, // advertiser, remaining impressions
             'att'  => 33424,
             'ibm'  => 16823);

$ad = pc_rand_weighted($ads);
Recipe 2.6 for how to generate random numbers within a range.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Taking Logarithms
You want to take the logarithm of a number.
For logs using base e (natural log), use log( ):
$log = log(10);          // 2.30258092994
For logs using base 10, use log10( ):
$log10 = log10(10);      // 1
For logs using other bases, use pc_logn( ):
function pc_logn($number, $base) {
    return log($number) / log($base);
}

$log2  = pc_logn(10, 2); // 3.3219280948874
Both log( ) and log10( ) are defined only for numbers that are greater than zero. The pc_logn( ) function uses the change of base formula, which says that the log of a number in base n is equal to the log of that number, divided by the log of n.
Documentation on log( ) at http://www.php.net/log and log10( ) at http://www.php.net/log10.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Calculating Exponents
You want to raise a number to a power.
To raise e to a power, use exp( ):
$exp = exp(2);        // 7.3890560989307
To raise it to any power, use pow( ) :
$exp = pow( 2, M_E);  // 6.5808859910179

$pow = pow( 2, 10);   // 1024
$pow = pow( 2, -2);   // 0.25
$pow = pow( 2, 2.5);  // 5.6568542494924

$pow = pow(-2, 10);   // 1024
$pow = pow( 2, -2);   // 0.25
$pow = pow(-2, -2.5); // NAN (Error: Not a Number)
The built-in constant M_E is an approximation of the value of e. It equals 2.7182818284590452354. So exp($n) and pow(M_E, $n) are identical.
It's easy to create very large numbers using exp( ) and pow( ); if you outgrow PHP's maximum size (almost 1.8e308), see Recipe 2.14 for how to use the arbitrary precision functions. With these functions, PHP returns INF, infinity, if the result is too large and NAN, not-a-number, on an error.
Documentation on pow( ) at http://www.php.net/pow, exp( ) at http://www.php.net/exp, and information on predefined mathematical constants at http://www.php.net/math.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Formatting Numbers
You have a number and you want to print it with thousands and decimals separators. For instance, you want to display prices for items in a shopping cart.
Use the number_format( ) function to format as an integer:
$number = 1234.56;
print number_format($number);    // 1,235 because number is rounded up
Specify a number of decimal places to format as a decimal:
print number_format($number, 2); // 1,234.56
The number_format( ) function formats a number by inserting the correct decimal and thousands separators for your locale. If you want to manually specify these values, pass them as the third and fourth parameters:
$number = 1234.56;
print number_format($number, 2, '@', '#'); // 1#234@56
The third argument is used as the decimal point and the last separates thousands. If you use these options, you must specify both arguments.
By default, number_format( ) rounds the number to the nearest integer. If you want to preserve the entire number, but you don't know ahead of time how many digits follow the decimal point in your number, use this:
$number = 1234.56; // your number
list($int, $dec) = explode('.', $number);
print number_format($number, strlen($dec));
Documentation on number_format( ) at http://www.php.net/number-format.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Printing Correct Plurals
You want to correctly pluralize words based on the value of a variable. For instance, you are returning text that depends on the number of matches found by a search.
Use a conditional expression:
$number = 4;
print "Your search returned $number " . ($number == 1 ? 'hit' : 'hits') . '.';
Your search returned 4 hits.
It's slightly shorter to write the line as:
print "Your search returned $number hit" . ($number == 1 ? '' : 's') . '.';
However, for odd pluralizations, such as "person" versus "people," we find it clearer to break out the entire word rather than just the letter.
Another option is to use one function for all pluralization, as shown in the pc_may_pluralize( ) function in Example 2-2.
Example 2-2. pc_may_pluralize( )
function pc_may_pluralize($singular_word, $amount_of) {

    // array of special plurals
    $plurals = array(
        'fish' =>