Programming PHP By Kevin Tatroe, Rasmus Lerdorf The unconfirmed error reports are from readers. They have not yet been approved or disproved by the author or editor and represent solely the opinion of the reader. Here's a key to the markup: [page-number]: serious technical mistake {page-number}: minor technical mistake : important language/formatting problem (page-number): language change or minor formatting problem ?page-number?: reader question or request for clarification This page was updated September 28, 2004. UNCONFIRMED errors and comments from readers: [19] 3rd paragraph; PHP code is printed there, as follows: // convert &#nnn, entities into characters $text = preg_replace('/&#([0-9])+);/e', "chr('\\1')", $text); My comment: The right parenthesis behind the plus mark is superfluous and has to be dropped. The correct version is: $text = preg_replace('/&#([0-9])+;/e', "chr('\\1')", $text); (25) Strings Section; The example: $name = "Guido"; echo "Hi, $name\n"; echo 'Hi, $name'; The book suggest it will be outputted in two linies but it will be on one single linie . The \n will only put the two strings in seperate lines in the source code. A
is required after the \n to make it work. {29} second block of code; In the first block of code on page 29, the function "database_query($res)" is invoked. In the second block of code, the same function is invoked, but the invocation is prepended with a dollar sign ("$database_query($res)"). I believe the initial dollar sign should not be there. {30} 1st section, 3rd code example; Line that reads: if($uninitialized_variable === NULL){ should be: if($uninitialized_variable == NULL){ There's an extra '=', which can be confusing at such an early point in learning the language. {50} example of while loop; While loop tests for $i <= 10, but $i is never incremented, as in $i++ Only $total is incremented, but the loop does not test $total. The next example, showing an alternative syntax does the same. On page 51, the $i is properly incremented. (51) second full paragraph, just below the middle of the page; I think that "if ($j = 5)" should read "if ($j == 5)". In other words, the equal sign needs to be double instead of single {51} 2nd Paragraph; The text that reads: while ($i < 10) { while ($j < 10) { if ($j = 5) continue 2; // Continues through two levels $j++; } $i++; } should read: while ($i < 10) { while ($j < 10) { if ($j == 5) continue 2; // Continues through two levels $j++; } $i++; } note: $j == 5 [51] code following 2nd paragraph OR explanation following code; First of all, as a couple people noted, the third line of the code should probably be a comparison, not an assignment. However, since continue (just like in C) returns to the beginning of the code block, even with this fix, $i never gets a chance to increment itself, so once j hits 5 we get an infinite loop! Well, it is still valid CODE (even with assignment instead of comparison in if's expression), so I can't say the code is the mistake, but the explanation following the code is inconsistent with the code, as $i retains its original value. Here is replacement code that terminates, making the statement following the code somewhat more palatable: /* one of my pet peeves--initialize vars before using them anywhere other than LHS of assignments */ $i = $j = 0; while ($i < 10) { $i++; while ($j < 10) { /* echo "i == $i, j == $j\n"; */ if ($j = 5) continue 2; $j++; } } The resultant values of $i and $j are 10 and 5, respectively. (Note that this is not too elegant, since the inner loop will not execute when $i is 0, unless $i is initialized to -1 now.) Replacement code tested for PHP 4.3.1 (cli) on Linux. {54} "exit and return", last line of second paragraph; The exit() construct is NOT an alias for die() -- it's the other way around, according to the on-line PHP Manual. {57} 3rd and forth paragrahs; This section discusses using PHP to out put XHTML-style tags (i.e.
), however the code examples use the following HTML Doctype: Those examples should have an XHTML Doctype, such as the following: {67} Last Paragraph of "Passing Parameters by Reference" Section; The current text is: ================== Even in cases where your function does affect the given value, you may want a parameter to be passed by reference. When passing by value, PHP must copy the value. Particularly for large strings and objects, this can be an expensive operation. Passing by reference removes the need to copy the value. ================== The first sentence should be: ================== Even in cases where your function does not affect the given value, you may want a parameter to be passed by reference. ================== The paragraph is trying to describe why you would want to pass a parameter to an array by reference instead of by value. Usually this is only done if you want to modify the external variable. But the paragraph points out that it may also be done to save on system resources (even though you may not wish to actually modify the external variable). This is why the word "not" is a necessary addition. Otherwise there's no point to the paragraph. {69} Sample code in third paragraph under "Return Values"; Function needs to refer to global array to work properly. Sample should read: $names = array("Fred", "Barney", "Wilma", "Betty"); function & find_one($n) { global $names; return $names[$n]; } $person =& find_one(1); // Barney $person = "Barnetta"; // changes $names[1] (77) 2nd bullet from top of page; The effect of minus (-) on strings is in fact vice-versa: it left-justifies, while default is to right-justify. [77] 4th bullet from top of page; "For types other than double, this specifier is ignored." However, the statement: printf('%.2s', 'abc'); results in an output of only 'ab' (i.e., the first N chars). Also, for /some/ other types, the specifier is not ignored, it suppresses output of all data (just like an invalid type specifier): printf('%.2x', 1231); printf('%.2X', 1231); printf('%.2o', 1231); result in no output. The manual page http://www.php.net/manual/en/function.sprintf.php is equally mistaken on this. However, this could be a bug in PHP implementation instead of a bug in the book/manual. Tested for all type specifiers under PHP 4.2.3 / bash 2.05.8 / Red Hat Linux [77] Table 4-2; Table 4-2 incorrectly lists the printf() type specifiers. The following are specifiers that should be changed: printed specifier -> (changed to) correct specifier B -> b C -> c d or I -> d e, E or f -> e or f g or G -> g O -> o S -> s U -> u This info is taken from http://www.php.net/manual/en/function.sprintf.php (78) 2nd bullet-point from top of page; under "formatting a date" printf('%02d/%02d/%04y', $month, $day, $year); should be: printf('%02d/%02d/%04d', $month, $day, $year); (the y isn't a valid printf type specifier) (80) Very bottom code fragment, second line; $record = trim($record, " \r\n\0\x0B"; should be $record = trim($record, " \r\n\0\x0B"); Note the closing parenthesis (81) 3rd Caption (HTML) 2nd paragraph (Entity-quoting all special characters); Typo; first centence: The htmlspecialchars() function changes all characters with HTML entity equivelents into those equivalents (with the exeption of the space character). This should be: The htmlentities() function changes all characters with HTML entity equivelents into those equivalents (with the exeption of the space character). The example on the next page shows the proper function. {85} 2nd code sample; the original line as she hit the backslash(\\) key. would be encoded as as she hit the backslash(\\\\) key. Presumably, the author(s) meant for there to be only one slash in the first line. (88) Table 4-5; under natural order, i believe the following: pig10.jpg should be: pic10.jpg [93] middle; The comments for the two code lines say "find last comma", but should say "find FIRST comma". Considering that this is right after strrpos() is mentioned (and the difference is what differentiates them), this is probably a "serious" typo. {93} sscanf sample code; The n in the echo call should be $n and the output is incorrect. The sample code of the sscanf function is as follows: $string = "Fred\tFlintstone (35)"; $n = sscanf($string, "%s\t%s (%d)", &$first, &$last, &$age); echo "Matched n fields: $first $last is $age years old"; The out put says it is: Fred Flintstone is 35 years old I assume the echo line should read echo "Matched $n fields: $first $last is $age years old"; And the output should read: Matched 3 fields: Fred Flintstone is 35 years old [95] first code example; Should read: function has_bad_chars($str) { return strscspn($str, "\n\t\0") == strlen($str); } [100] Table 4-7; The expansion of [:punct:] is printed as [-!"#$%&'()*+,./:;<=>?@[\\]^_`{|}~ The first backslash escapes the second backslash. Should not the closing square bracket immediately following also be escaped with a third backslash?: [-!"#$%&'()*+,./:;<=>?@[\\\]^_`{|}~ ^ [101-102] Example 4-1; This script doesn't work: my credit cards are valid but not according to this. The Luhn part may not apply to all of those cards you mention (in all countries). {102} near the bottom of the code sample; There is a line of code in the Luhn checksum example that reads as follows: $theAdder << 1; This operation will not explicitly double $theAdder unless the output is assigned to that variable like this: $theAdder = $theAdder << 1; At least, that's the only way I can get left or right shift operations to work in my environment (PHP 4.1.2, Apache/1.3.9). {103} Example parts for "Splitting"; the comment parts should be // $terms is array('3', '5', 'i', '6', '12') ^ and // $terms is array('3', '5', 'i/6-12') ^^^^^^ {103} Second line of the Replacing example The expression '\[b]([^]]*)\[/b]' doesn't work. The subexpression '([^]]*)' won't parse. It's supposed to be '([^[]*)' which would match any characters that are not '['. That would match anything between [b] and [/b] [111] 2nd line of code plus comment form top of page (2 errata in one); String 'Sylvie' is missing a beginning quote! (Hence, code can't get parsed.) After this fix, captured $m is array('ylvie', 'lvi'); $m[0] (which is like the $& in perl) is NOT 'Sylvie' since it is the pattern matched within the input string, not the entirety of the input string. [I'm using a clunky term like "pattern matched within" since preg_match('/php/i', 'abcpHpxyz', $m); will result in $m[0] containing 'pHp', not 'php'.] (114) Last paragraph of "Splitting" section; At the end of section '4.10.13.3 Splitting' the text suddenly switches to a paragraph about preg_replace_callback( ) that looks like it belongs in the previous section - '4.10.13.2 Replacing' {119} first new paragraph; // $numbers holds the alphabet should be: // $letters holds // $numbers = array(5, 4, 3, 2); should be // $reversed_numbers = [120] Section "Extracting Multiple Values"; According to the documentation at php.net (and my test results), list() only works on numerical (indexed) arrays. In order to use list() with an associative array, array_values() must be used. Therefore: $person = array('name => 'Fred', 'age' => 35, 'wife' => 'Betty'); list($n, $a, $w) = $person; // $n is 'Fred', $a is 35, $w is 'Betty' Should be: $person = array('name => 'Fred', 'age' => 35, 'wife' => 'Betty'); list($n, $a, $w) = array_values($person); // $n is 'Fred', $a is 35, $w is 'Betty' And: $person = array('name => 'Fred', 'age' => 35, 'wife' => 'Betty'); list($n, $a) = $person; // $n is 'Fred', $a is 35 Should be: $person = array('name => 'Fred', 'age' => 35, 'wife' => 'Betty'); list($n, $a) = array_values($person); // $n is 'Fred', $a is 35 {122} sub-chapter "Checking Whether an Element Exists"; There one can read: if (array_key_exists(key, array)) {...} The function returns a Boolean value that indicates whether the second argument is a valid key in the array given as the first argument. Remark by the reader: The key is the first argument and the given array is the second argument. {123} 5th code segment; // $subjects is array('physics', 'chem'); should be: // $subjects is array('physics', 'chem', 'drama', 'classics'); [124] first paragraph; The size of the replacement array doesn't have to be the same as the number of elements you delete. The array grows or shinks as needed: $new = array('law', 'business', 'IS'); array_splice($subjects, 2, 4, $new); // $subjects should be array('physics', 'chem', 'law', 'business', 'IS', 'classics') // assuming $subjects has the same starting value as before or changing the code to achieve the wanted result array_splice($subjects, 3, 4, $new); [125] 1st Paragraph and 1st Code Example; This states "The most useful value is EXTR_PREFIX_SAME, which says that the third arguement to extract() is a prefix for the variable names that are created". However, it will only create a prefix if the associative name as a variable exists (see http://www.php.net/manual/en/function.extract.php). The code example gives the following results: $book_cover == "bird"; $book_shape == "rectangular"; $shape == "round"; In reality it is: $cover == "bird"; $book_cover == ""; $book_shape == "rectangular"; $shape == "round"; I think the author meant EXTR_PREFIX_ALL instead of EXTR_PREFIX_SAME. That would return the expected results. {127} Example 5-1; the first call to list() should be removed since it seems to cause the first record to be skipped. reset($ages); list($c1, $c2) = each($ages); \\ remove this line. echo("\n"; while (list($c1, $c2) = each($ages)) {.... should be: reset($ages); echo("
$c1$c2
\n"; while (list($c1, $c2) = each($ages)) {.... {127} last example; for($i = 0; $i < count($array); $i++){ should read for($i = 0; $i < count($addresses); $i++){ {128} Reducing An Array - Example of function calls created by array_reduce(); the first function call is listed as: add_up(2,3) when it should really be: add_up(4,3) {128} Section "Reducing an Array"; Reducing An Array - Example of function calls created by array_reduce(); The array reduce() line makes these function calls: add_up(2,3) add_up(13,5) add_up(38,7) The correct list of calls should be: add_up(0,2) // This line is missing in the book add_up(4,3) // This line is incorrect in the book add_up(13,5) add_up(38,7) [129] first example; the resulting calls of the second example of array_reduce() should be: add_up(11, 2) add_up(15, 3) add_up(24, 5) add_up(49, 7) (130) Code snippet following 2nd paragraph; Following the second paragraph is a code snippet, which reads as: -- $person = array('name' => 'Fred', 'age' => 35, 'wife' => 'Wilma'); $k = array_search($person, 'Wilma'); echo("Fred's $k is Wilma\n"); -- The parameters for the function array_search() should be reversed, as in: -- $k = array_search('Wilma', $person); -- [132] Example 5-3. Sorting arrays; When the user_sort functions is supposed to be called, the variable '$sort_type' is used in it's place. --> $sort_type($values, 'user_sort'); should read: --> user_sort($values, $sort_type); and, --> $sort_type($values); should read: --> user_sort($values); [132] Example 5-3; Along with the other reported mistakes in example 5-3, the variables $submitted and $sort_type are referenced without ever being set. Most likely, there is a missing extract call before the if($submitted) statement: extract($_REQUEST); {132} 17 lines from page bottom (the tag); This script only works if you change: to: Also the script provided for downloading contains only the part of the script on page 132 the continuation of the file on page 132 is not included in 5-3.php. (144) 3rd paragraph; When invoking a static method the syntax is ClassName::methodName(); You cannot use the -> operator to write ClassName->methodName(); [147] Line 10 of first code snippet; $barney =& new Person; should read: $barney = new Person; (157) Figure 6-4. The next page; In figure 6-4 it shows the output of next.php as follows: The log contains: Created Fri Jan 4 08:19:30 2002 Viewed first page Fri Jan 4 08:19:30 2002 Viewed first page Fri Jan 4 08:20:47 2002 According to the code in the above paragraph it should read as follows: The log contains: Created Fri Jan 4 08:19:30 2002 Viewed first page Fri Jan 4 08:19:30 2002 Viewed page 2 at Fri Jan 4 08:20:47 2002 [160] 8th paragraph; Here on p. 160 we are told that "the $_REQUEST array is also created by PHP if the register_globals option is on". However, on page 287, para. 5, we are encouraged to use $_REQUEST when register_globals has been disabled. Contradictory and confusing! I think the mistake is on p 160. {173} Fourth line from the top.; current line is: move_uploaded_file($FILES['toProcess']['tmp_name'], "path/to/put/file/$file); it should be: move_uploaded_file($FILES['toProcess']['tmp_name'], "path/to/put/file/$file"); missing last quote after $file [175] 6th line of code at top of page; Missing single quote before the comma in the preg_match function. [178] 8th line of code; Regarding: header('HTTP/1.0 401 Unauthorized'); This will not work if you have the CGI version of PHP installed (cf. PHPBuilder.com forum). You need to put: header('status: 401 Unauthorized'); Otherwise you get a server error like this one (in your Apache error.log): [Sat Jun 15 13:34:18 2002] [error] [client 127.0.0.1] malformed header from script. Bad header=HTTP/1.0 401 Unauthorized: c:/php/php.exe [178] 8th line of code; Further to my last remark, it should be mentioned as follows (from a tutorial at Zend.com): The $PHP_AUTH_USER, $PHP_AUTH_PW and $PHP_AUTH_TYPE global variables are only available when PHP is installed as a module. If you're using the CGI version of PHP, you will be limited to Web server-based authentication or other custom types of authentication (such as using HTML forms) to match passwords in a database. http://www.zend.com/zend/tut/authentication.php (178) Code sample; The script as it stands is not sufficient to deny access to a web page if password authentication fails. To correct this add the following line after the header() lines: exit; {191} (details of the 'date' function). The format character for Minutes is listed incorrectly as I - it should be i (lower case). (197) 1st code example; The DB_FETCHMODE_OBJECT mode turns the row into an object, with a property for each column in the result row: $row = $result->fetchRow(DB_FETCHMODE_ASSOC); should be The DB_FETCHMODE_OBJECT mode turns the row into an object, with a property for each column in the result row: $row = $result->fetchRow(DB_FETCHMODE_OBJECT); (199) First paragraph of the Shortcuts section; The description of the different query methods lists 5 function names, including getAssoc. However, the section never explains or gives an example of getAssoc. The index has no other reference to the command other than this page. (199) "better way" code example; The line: $db->insertMultiple($compiled, $movies); should be %db->executeMultiple($compiled, $movies); {200} first line of first example on page; first example on the page is presumably showing the use of getCol() but it contains a call to getAll(). $titles = $db->getCol (.....) ****** [200] 3rd paragraph under section ' Details About a Query Response'; The text says: "The affectedRows() method tells you about the number of rows affected by an INSERT, DELETE or UPDATE operation. $howmany = $response->affectedRows() Indeed, the affectedRows() method does return the correct number, but it is a method on the database connection object, NOT the query response object as shown in the sample code. [202] Code snippert on top; The foreach loop on the top of the page is using the function splice(). That function does not exist. I believe the authors want to replace splice($a, 0, 0, $id) with array_splice($a, 0, 0, $id) (209) line 19 of script on the page; Although accurately displayed on page 209, the "examples" script for Example 8-5 is missing an ending semi-colon on the 29th line (causing a parse error), as follows: $pick_message = 'Click on one, or control-click on
multiple categories:' (233) 1st line of last paragraph; The sentence "A PHP document is made up of a numer of pages" should read "A PDF document is made up of a number of pages," since this section is describing the structure of PDF documents, and much of what follows specifically applies to PDF documents and their pages, not to PHP programs in general. (262) Chapter; The examples given for using PHP/Sablotron and XSLT *will not* work with the recently-released PHP 4.2.0; however, if you're still using PHP 4.1.2 they work fine. I fussed with this for three days before trying the older version of PHP on a hunch, and it worked. Apparently they've changed something to do with integration of Sablotron in the new release that has broken things. {266} Example 11-2; Example 11-2 shows this: function start_element($inParser, $inName, &$inAttributes) { $attributes = array(); foreach($inAttributes as $key) { $value = $inAttributes[$key]; $attributes[] = " $key = \"$value\" "; ... This appears to use the elements of $inAttributes as its keys. I had better luck with this: function start_element($inParser, $inName, &$inAttributes) { $attributes = array(); $arrkeys=array_keys($inAttributes); foreach($arrkeys as $key) { $value = $inAttributes[$key]; $attributes[] = " $key = \"$value\" "; ... 2cents (275) start of the example 11-10; It reads Example 11-10 bookparse.xml It should read Example 11-10 bookparse.php [387] last paragraph; chop() ist not an alias for ltrim() as mentioned in the book but from rtrim() according to the PHP documentation. (411) top (gettimeofday); the microseconds key should be usec not msec: http://www.php.net/manual/en/function.gettimeofday.php (451) tan definition; In your definition of the tan function, you say the tan "returns the arc tangent of value in radians", whereas it infact it returns the tangent of a value in radians. Thanks! (451) Section on system(); The description of the system() call is not accurate. The sentence reads "Executes command via the shell and returns the last line of output from the command's results." A more accurate description would be "Executes command via the shell, returns and outputs the results of the command."
$c1$c2