diff options
Diffstat (limited to 'test/scanners/php/pleac.in.php')
-rw-r--r-- | test/scanners/php/pleac.in.php | 5516 |
1 files changed, 0 insertions, 5516 deletions
diff --git a/test/scanners/php/pleac.in.php b/test/scanners/php/pleac.in.php deleted file mode 100644 index 4bd0300..0000000 --- a/test/scanners/php/pleac.in.php +++ /dev/null @@ -1,5516 +0,0 @@ -# -*- php -*- -# The examples are taken from the Perl Cookbook -# By Tom Christiansen & Nathan Torkington -# see http://www.oreilly.com/catalog/cookbook for more - -# @@PLEAC@@_NAME -# @@SKIP@@ PHP - -# @@PLEAC@@_WEB -# @@SKIP@@ http://php.net/ - -# @@PLEAC@@_1.0 -#----------------------------- -$string = '\n'; # two characters, \ and an n -$string = 'Jon \'Maddog\' Orwant'; # literal single quotes -$string = 'Jon "Maddog" Orwant'; # literal double quotes -#----------------------------- -$string = "\n"; # a "newline" character -$string = "Jon \"Maddog\" Orwant"; # literal double quotes -$string = "Jon 'Maddog' Orwant"; # literal single quotes -#----------------------------- -$a = -"This is a multiline -here document"; - -$a = <<<EOF -This is a multiline here document -terminated by EOF on a line by itself -EOF; -#----------------------------- - -# @@PLEAC@@_1.1 -#----------------------------- -$value = substr($string, $offset, $count); -$value = substr($string, $offset); - -$string = substr_replace($string, $newstring, $offset, $count); -$string = substr_replace($string, $newtail, $offset); -#----------------------------- -# get a 5-byte string, skip 3, then grab 2 8-byte strings, then the rest -list($leading, $s1, $s2, $trailing) = - array_values(unpack("A5a/x3/A8b/A8c/A*d", $data); - -# split at five byte boundaries -preg_match_all ("/.{5}/", $data, $f, PREG_PATTERN_ORDER); -$fivers = $f[0]; - -# chop string into individual characters -$chars = $string; -#----------------------------- -$string = "This is what you have"; -# +012345678901234567890 Indexing forwards (left to right) -# 109876543210987654321- Indexing backwards (right to left) -# note that 0 means 10 or 20, etc. above - -$first = substr($string, 0, 1); # "T" -$start = substr($string, 5, 2); # "is" -$rest = substr($string, 13); # "you have" -$last = substr($string, -1); # "e" -$end = substr($string, -4); # "have" -$piece = substr($string, -8, 3); # "you" -#----------------------------- -$string = "This is what you have"; -print $string; -#This is what you have - -$string = substr_replace($string, "wasn't", 5, 2); # change "is" to "wasn't" -#This wasn't what you have - -$string = substr_replace($string, "ondrous", -12); # "This wasn't wondrous" -#This wasn't wondrous - -$string = substr_replace($string, "", 0, 1); # delete first character -#his wasn't wondrous - -$string = substr_replace($string, "", -10); # delete last 10 characters -#his wasn' -#----------------------------- -if (preg_match("/pattern/", substr($string, -10)) { - print "Pattern matches in last 10 characters\n"; -} - -# substitute "at" for "is", restricted to first five characters -$string=(substr_replace(preg_replace("/is/", "at", substr($string,0,5)),0,5); -#----------------------------- -# exchange the first and last letters in a string -$a = "make a hat"; -list($a[0], $a[strlen($a)-1]) = Array(substr($a,-1), substr($a,0,1)); -print $a; - -#----------------------------- -# extract column with unpack -$a = "To be or not to be"; -$b = unpack("x6/A6a", $a); # skip 6, grab 6 -print $b['a']; - - -$b = unpack("x6/A2b/X5/A2c", $a); # forward 6, grab 2; backward 5, grab 2 -print $b['b']."\n".$b['c']."\n"; - -#----------------------------- -function cut2fmt() { - $positions = func_get_args(); - $template = ''; - $lastpos = 1; - foreach($positions as $place) { - $template .= "A" . ($place - $lastpos) . " "; - $lastpos = $place; - } - $template .= "A*"; - return $template; -} - -$fmt = cut2fmt(8, 14, 20, 26, 30); -print "$fmt\n"; -#A7 A6 A6 A6 A4 A* -#----------------------------- - -# @@PLEAC@@_1.2 -#----------------------------- -# use $b if $b is true, else $c -$a = $b?$b:$c; - -# set $x to $y unless $x is already true -$x || $x=$y; -#----------------------------- -# use $b if $b is defined, else $c -$a = defined($b) ? $b : $c; -#----------------------------- -$foo = $bar || $foo = "DEFAULT VALUE"; -#----------------------------- -$dir = array_shift($_SERVER['argv']) || $dir = "/tmp"; -#----------------------------- -$dir = $_SERVER['argv'][0] || $dir = "/tmp"; -#----------------------------- -$dir = defined($_SERVER['argv'][0]) ? array_shift($_SERVER['argv']) : "/tmp"; -#----------------------------- -$dir = count($_SERVER['argv']) ? $_SERVER['argv'][0] : "/tmp"; -#----------------------------- -$count[$shell?$shell:"/bin/sh"]++; -#----------------------------- -# find the user name on Unix systems -$user = $_ENV['USER'] - || $user = $_ENV['LOGNAME'] - || $user = posix_getlogin() - || $user = posix_getpwuid(posix_getuid())[0] - || $user = "Unknown uid number $<"; -#----------------------------- -$starting_point || $starting_point = "Greenwich"; -#----------------------------- -count($a) || $a = $b; # copy only if empty -$a = count($b) ? $b : $c; # assign @b if nonempty, else @c -#----------------------------- - -# @@PLEAC@@_1.3 -#----------------------------- -list($VAR1, $VAR2) = array($VAR2, $VAR1); -#----------------------------- -$temp = $a; -$a = $b; -$b = $temp; -#----------------------------- -$a = "alpha"; -$b = "omega"; -list($a, $b) = array($b, $a); # the first shall be last -- and versa vice -#----------------------------- -list($alpha, $beta, $production) = Array("January","March","August"); -# move beta to alpha, -# move production to beta, -# move alpha to production -list($alpha, $beta, $production) = array($beta, $production, $alpha); -#----------------------------- - -# @@PLEAC@@_1.4 -#----------------------------- -$num = ord($char); -$char = chr($num); -#----------------------------- -$char = sprintf("%c", $num); # slower than chr($num) -printf("Number %d is character %c\n", $num, $num); -#----------------------------- -$ASCII = unpack("C*", $string); -eval('$STRING = pack("C*", '.implode(',',$ASCII).');'); -#----------------------------- -$ascii_value = ord("e"); # now 101 -$character = chr(101); # now "e" -#----------------------------- -printf("Number %d is character %c\n", 101, 101); -#----------------------------- -$ascii_character_numbers = unpack("C*", "sample"); -print explode(" ",$ascii_character_numbers)."\n"; - -eval('$word = pack("C*", '.implode(',',$ascii_character_numbers).');'); -$word = pack("C*", 115, 97, 109, 112, 108, 101); # same -print "$word\n"; -#----------------------------- -$hal = "HAL"; -$ascii = unpack("C*", $hal); -foreach ($ascii as $val) { - $val++; # add one to each ASCII value -} -eval('$ibm = pack("C*", '.implode(',',$ascii).');'); -print "$ibm\n"; # prints "IBM" -#----------------------------- - -# @@PLEAC@@_1.5 -#----------------------------- -// using perl regexp -$array = preg_split('//', $string ,-1, PREG_SPLIT_NO_EMPTY); -// using PHP function: $array = str_split($string); - -// Cannot use unpack with a format of 'U*' in PHP. -#----------------------------- -for ($offset = 0; preg_match('/(.)/', $string, $matches, 0, $offset) > 0; $offset++) { - // $matches[1] has charcter, ord($matches[1]) its number -} -#----------------------------- -$seen = array(); -$string = "an apple a day"; -foreach (str_split($string) as $char) { - $seen[$char] = 1; -} -$keys = array_keys($seen); -sort($keys); -print "unique chars are: " . implode('', $keys)) . "\n"; -unique chars are: adelnpy -#----------------------------- -$seen = array(); -$string = "an apple a day"; -for ($offset = 0; preg_match('/(.)/', $string, $matches, 0, $offset) > 0; $offset++) { - $seen[$matches[1]] = 1; -} -$keys = array_keys($seen); -sort($keys); -print "unique chars are: " . implode('', $keys) . "\n"; -unique chars are: adelnpy -#----------------------------- -$sum = 0; -foreach (unpack("C*", $string) as $byteval) { - $sum += $byteval; -} -print "sum is $sum\n"; -// prints "1248" if $string was "an apple a day" -#----------------------------- -$sum = array_sum(unpack("C*", $string)); -#----------------------------- - -// sum - compute 16-bit checksum of all input files -$handle = @fopen($argv[1], 'r'); -$checksum = 0; -while (!feof($handle)) { - $checksum += (array_sum(unpack("C*", fgets($handle)))); -} -$checksum %= pow(2,16) - 1; -print "$checksum\n"; - -# @@INCLUDE@@ include/php/slowcat.php -#----------------------------- - -# @@PLEAC@@_1.6 -#----------------------------- -$revchars = strrev($string); -#----------------------------- -$revwords = implode(" ", array_reverse(explode(" ", $string))); -#----------------------------- -// reverse word order -$string = 'Yoda said, "can you see this?"'; -$allwords = explode(" ", $string); -$revwords = implode(" ", array_reverse($allwords)); -print $revwords . "\n"; -this?" see you "can said, Yoda -#----------------------------- -$revwords = implode(" ", array_reverse(explode(" ", $string))); -#----------------------------- -$revwords = implode(" ", array_reverse(preg_split("/(\s+)/", $string))); -#----------------------------- -$word = "reviver"; -$is_palindrome = ($word === strrev($word)); -#----------------------------- -// quite a one-liner since "php" does not have a -n switch -% php -r 'while (!feof(STDIN)) { $word = rtrim(fgets(STDIN)); if ($word == strrev($word) && strlen($word) > 5) print $word; }' < /usr/dict/words -#----------------------------- - -# @@PLEAC@@_1.8 -#----------------------------- -$text = preg_replace('/\$(\w+)/e', '$$1', $text); -#----------------------------- -list($rows, $cols) = Array(24, 80); -$text = 'I am $rows high and $cols long'; -$text = preg_replace('/\$(\w+)/e', '$$1', $text); -print $text; - -#----------------------------- -$text = "I am 17 years old"; -$text = preg_replace('/(\d+)/e', '2*$1', $text); -#----------------------------- -# expand variables in $text, but put an error message in -# if the variable isn't defined -$text = preg_replace('/\$(\w+)/e','isset($$1)?$$1:\'[NO VARIABLE: $$1]\'', $text); -#----------------------------- - -// As PHP arrays are used as hashes too, separation of section 4 -// and section 5 makes little sense. - -# @@PLEAC@@_1.9 -#----------------------------- -$big = strtoupper($little); -$little = strtolower($big); -// PHP does not have the\L and\U string escapes. -#----------------------------- -$big = ucfirst($little); -$little = strtolower(substr($big, 0, 1)) . substr($big, 1); -#----------------------------- -$beast = "dromedary"; -// capitalize various parts of $beast -$capit = ucfirst($beast); // Dromedar -// PHP does not have the\L and\U string escapes. -$capall = strtoupper($beast); // DROMEDAR -// PHP does not have the\L and\U string escapes. -$caprest = strtolower(substr($beast, 0, 1)) . substr(strtoupper($beast), 1); // dROMEDAR -// PHP does not have the\L and\U string escapes. -#----------------------------- -// titlecase each word's first character, lowercase the rest -$text = "thIS is a loNG liNE"; -$text = ucwords(strtolower($text)); -print $text; -This Is A Long Line -#----------------------------- -if (strtoupper($a) == strtoupper($b)) { // or strcasecmp($a, $b) == 0 - print "a and b are the same\n"; -} -#----------------------------- -# @@INCLUDE@@ include/php/randcap.php - -// % php randcap.php < genesis | head -9 -#----------------------------- - -# @@PLEAC@@_1.10 -#----------------------------- -echo $var1 . func() . $var2; // scalar only -#----------------------------- -// PHP can only handle variable expression without operators -$answer = "STRING ${[ VAR EXPR ]} MORE STRING"; -#----------------------------- -$phrase = "I have " . ($n + 1) . " guanacos."; -// PHP cannot handle the complex exression: ${\($n + 1)} -#----------------------------- -// Rest of Discussion is not applicable to PHP -#----------------------------- -// Interpolating functions not available in PHP -#----------------------------- - -# @@PLEAC@@_1.11 -# @@INCOMPLETE@@ -# @@INCOMPLETE@@ - -# @@PLEAC@@_1.12 -#----------------------------- -$output = wordwrap($str, $width, $break, $cut); -#----------------------------- -# @@INCLUDE@@ include/php/wrapdemo.php -#----------------------------- -// merge multiple lines into one, then wrap one long line -print wordwrap(str_replace("\n", " ", file_get_contents('php://stdin'))); -#----------------------------- -while(!feof(STDIN)) { - print wordwrap(str_replace("\n", " ", stream_get_line(STDIN, 0, "\n\n"))); - print "\n\n"; -} -#----------------------------- - -# @@PLEAC@@_1.13 -#----------------------------- -//backslash -$var = preg_replace('/([CHARLIST])/', '\\\$1', $var); -// double -$var = preg_replace('/([CHARLIST])/', '$1$1', $var); -#----------------------------- -$var = preg_replace('/%/', '%%', $var); -#----------------------------- -$string = 'Mom said, "Don\'t do that."'; -$string = preg_replace('/([\'"])/', '\\\$1', $string); -// in PHP you can also use the addslashes() function -#----------------------------- -$string = 'Mom said, "Don\'t do that."'; -$string = preg_replace('/([\'"])/', '$1$1', $string); -#----------------------------- -$string = preg_replace('/([^A-Z])/', '\\\$1', $string); -#----------------------------- -// PHP does not have the \Q and \E string metacharacters -$string = "this is\\ a\\ test\\!"; -// PHP's quotemeta() function is not the same as perl's quotemeta() function -$string = preg_replace('/(\W)/', '\\\$1', 'is a test!'); -#----------------------------- - -# @@PLEAC@@_1.14 -#----------------------------- -$string = trim($string); -#----------------------------- -// print what's typed, but surrounded by > < symbols -while (!feof(STDIN)) { - print ">" . substr(fgets(STDIN), 0, -1) . "<\n"; -} -#----------------------------- -$string = preg_replace('/\s+/', ' ', $string); // finally, collapse middle -#----------------------------- -$string = trim($string); -$string = preg_replace('/\s+/', ' ', $string); -#----------------------------- -// 1. trim leading and trailing white space -// 2. collapse internal whitespace to single space each -function sub_trim($string) { - $string = trim($string); - $string = preg_replace('/\s+/', ' ', $string); - return $string; -} -#----------------------------- - -# @@PLEAC@@_1.15 -# @@INCOMPLETE@@ -# @@INCOMPLETE@@ - -# @@PLEAC@@_1.16 -#----------------------------- -$code = soundex($string); -#----------------------------- -$phoned_words = metaphone("Schwern"); -#----------------------------- -// substitution function for getpwent(): -// returns an array of user entries, -// each entry contains the username and the full name -function getpwent() { - $pwents = array(); - $handle = fopen("passwd", "r"); - while (!feof($handle)) { - $line = fgets($handle); - if (preg_match("/^#/", $line)) continue; - $cols = explode(":", $line); - $pwents[$cols[0]] = $cols[4]; - } - return $pwents; -} - -print "Lookup user: "; -$user = rtrim(fgets(STDIN)); -if (empty($user)) exit; -$name_code = soundex($user); -$pwents = getpwent(); -foreach($pwents as $username => $fullname) { - preg_match("/(\w+)[^,]*\b(\w+)/", $fullname, $matches); - list(, $firstname, $lastname) = $matches; - - if ($name_code == soundex($username) || - $name_code == soundex($lastname) || - $name_code == soundex($firstname)) - { - printf("%s: %s %s\n", $username, $firstname, $lastname); - } -} -#----------------------------- - -# @@PLEAC@@_1.17 -#----------------------------- -# @@INCLUDE@@ include/php/fixstyle.php -#----------------------------- -# @@INCLUDE@@ include/php/fixstyle2.php -#----------------------------- -// very fast, but whitespace collapse -while (!feof($input)) { - $i = 0; - preg_match("/^(\s*)(.*)/", fgets($input), $matches); // emit leading whitespace - fwrite($output, $matches[1]); - foreach (preg_split("/(\s+)/", $matches[2]) as $token) { // preserve trailing whitespace - fwrite($output, (array_key_exists($token, $config) ? $config[$token] : $token) . " "); - } - fwrite($output, "\n"); -} -#----------------------------- - -// @@PLEAC@@_2.0 -// As is the case under so many other languages floating point use under PHP is fraught -// with dangers. Although the basic techniques shown below are valid, please refer to -// the official PHP documentation for known issues, bugs, and alternate approaches - -// @@PLEAC@@_2.1 -// Two basic approaches to numeric validation: -// * Built-in functions like 'is_numeric', 'is_int', 'is_float' etc -// * Regexes, as shown below - -$s = '12.345'; - -preg_match('/\D/', $s) && die("has nondigits\n"); -preg_match('/^\d+$/', $s) || die("not a natural number\n"); -preg_match('/^-?\d+$/', $s) || die("not an integer\n"); -preg_match('/^[+-]?\d+$/', $s) || die("not an integer\n"); -preg_match('/^-?\d+\.?\d*$/', $s) || die("not a decimal\n"); -preg_match('/^-?(?:\d+(?:\.\d*)?|\.\d+)$/', $s) || die("not a decimal\n"); -preg_match('/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/', $s) || die("not a C float\n"); - -// ---------------------------- - -function getnum($s) -{ - sscanf($s, "%D", $number); return isset($number) ? $number : 0; -} - -echo getnum(123) . "\n"; // ok -echo getnum(0xff) . "\n"; // .. -echo getnum(044) . "\n"; // .. - -echo getnum('x') . "\n"; // fail - -// @@PLEAC@@_2.2 -// In PHP floating point comparisions are 'safe' [meaning the '==' comparison operator -// can be used] as long as the value consists of 14 digits or less [total digits, either -// side of the decimal point e.g. xxxxxxx.xxxxxxx, xxxxxxxxxxxxxx., .xxxxxxxxxxxxxx]. If -// values with more digits must be compared, then: -// -// * Represent as strings, and take care to avoid any implicit conversions e.g. don't pass -// a float as a float to a function and expect all digits to be retained - they won't be - -// then use 'strcmp' to compare the strings -// -// * Avoid float use; perhaps use arbitrary precision arithmetic. In this case, the -// 'bccomp' function is relevant - -// Will work as long as each floating point value is 14 digits or less -if ($float_1 == $float_2) -{ - ; // ... -} - -// Compare as strings -$cmp = strcmp('123456789.123456789123456789', '123456789.123456789123456788'); - -// Use 'bccomp' -$precision = 5; // Number of significant comparison digits after decimal point -if (bccomp('1.111117', '1.111116', $precision)) -{ - ; // ... -} - -$precision = 6; -if (bccomp('1.111117', '1.111116', $precision)) -{ - ; // ... -} - -// ---------------------------- - -$wage = 536; -$week = $wage * 40; -printf("One week's wage is: $%.2f\n", $week / 100); - -// @@PLEAC@@_2.3 -// Preferred approach -$rounded = round($unrounded, $precision); - -// Possible alternate approach -$format = '%[width].[prec]f'; -$rounded = sprintf($format, $unrounded); - -// ------------ - -$a = 0.255; $b = round($a, 2); -echo "Unrounded: {$a}\nRounded: {$b}\n"; - -$a = 0.255; $b = sprintf('%.2f', $a); -echo "Unrounded: {$a}\nRounded: {$b}\n"; - -$a = 0.255; -printf("Unrounded: %.f\nRounded: %.2f\n", $a, $a); - -// ---------------------------- - -echo "number\tint\tfloor\tceil\n"; - -foreach(array(3.3, 3.5, 3.7, -3.3) as $number) -{ - printf("%.1f\t%.1f\t%.1f\t%.1f\n", $number, (int) $number, floor($number), ceil($number)); -} - -// @@PLEAC@@_2.4 -// PHP offers the 'bindec' and 'decbin' functions to converting between binary and decimal - -$num = bindec('0110110'); - -$binstr = decbin(54); - -// @@PLEAC@@_2.5 -foreach (range($X, $Y) as $i) -{ - ; // ... -} - -foreach (range($X, $Y, 7) as $i) -{ - ; // ... -} - -for ($i = $X; $i <= $Y; $i++) -{ - ; // ... -} - -for ($i = $X; $i <= $Y; $i += 7) -{ - ; // ... -} - -// ---------------------------- - -echo 'Infancy is:'; foreach(range(0, 2) as $i) echo " {$i}\n"; -echo 'Toddling is:'; foreach(range(3, 4) as $i) echo " {$i}\n"; -echo 'Childhood is:'; foreach(range(5, 12) as $i) echo " {$i}\n"; - -// @@PLEAC@@_2.6 -// PHP offers no native support for Roman Numerals. However, a 'Numbers_Roman' class -// is available for download from PEAR: [http://pear.php.net/package/Numbers_Roman]. -// Note the following 'include' directives are required: -// -// include_once('Numbers/Roman.php'); - -$roman = Numbers_Roman::toNumeral($arabic); -$arabic = Numbers_Roman::toNumber($roman); - -// ---------------------------- - -$roman_fifteen = Numbers_Roman::toNumeral(15); - -$arabic_fifteen = Numbers_Roman::toNumber($roman_fifteen); - -printf("Roman for fifteen is: %s\n", $roman_fifteen); -printf("Arabic for fifteen is: %d\n", $arabic_fifteen); - -// @@PLEAC@@_2.7 -// Techniques used here simply mirror Perl examples, and are not an endorsement -// of any particular RNG technique - -// In PHP do this ... -$random = rand($lowerbound, $upperbound); -$random = rand($x, $y); - -// ---------------------------- - -function make_password($chars, $reqlen) -{ - $len = strlen($chars); - for ($i = 0; $i < $reqlen; $i++) $password .= substr($chars, rand(0, $len), 1); - return $password; -} - -$chars = 'ABCDEfghijKLMNOpqrstUVWXYz'; $reqlen = 8; - -$password = make_password($chars, $reqlen); - -// @@PLEAC@@_2.8 -// PHP sports a large number of C Standard Library routines including the 'srand' -// function, used to re-seed the RNG used with calls to the 'rand' function. Thus, -// as per Perl example: - -while (TRUE) -{ - $seed = (int) fgets(STDIN); - if (!empty($seed)) break; -} - -srand($seed); - -// @@PLEAC@@_2.9 -// The above is considered - for many reasons - a poor way of seeding the RNG. PHP -// also offers alternate versions of the functions, 'mt_srand' and 'mt_rand', -// which are described as faster, and more 'random', though key to obtaining a -// more 'random' distribution of generated numbers seems to be through using -// a combination of a previously saved random value in combination with an -// unrepeatable value [like the current time in microseconds] that is multiplied -// by a large prime number, or perhaps as part of a hash [examples available in -// PHP documentation for 'srand' and 'mt_srand'] - -mt_srand($saved_random_value + microtime() * 1000003); - -// or - -mt_srand(($saved_random_value + hexdec(substr(md5(microtime()), -8))) & 0x7fffffff); - -// Use of 'mt_rand' together with an appropriate seeding approach should help better -// approximate the generation of a 'truly random value' -$truly_random_value = mt_rand(); - -// @@PLEAC@@_2.10 -function random() { return (float) rand() / (float) getrandmax(); } - -function gaussian_rand() -{ - $u1 = 0.0; $u2 = 0.0; $g1 = 0.0; $g2 = 0.0; $w = 0.0; - - do - { - $u1 = 2.0 * random() - 1.0; $u2 = 2.0 * random() - 1.0; - $w = $u1 * $u1 + $u2 * $u2; - } while ($w > 1.0); - - $w = sqrt((-2.0 * log($w)) / $w); $g2 = $u1 * $w; $g1 = $u2 * $w; - - return $g1; -} - -// ------------ - -$mean = 25.0; $sdev = 2.0; -$salary = gaussian_rand() * $mean + $sdev; - -printf("You have been hired at: %.2f\n", $salary); - -// @@PLEAC@@_2.11 -// 'deg2rad' and 'rad2deg' are actually PHP built-ins, but here is how you might implement -/ them if needed -function deg2rad_($deg) { return ($deg / 180.0) * M_PI; } -function rad2deg_($rad) { return ($rad / M_PI) * 180.0; } - -// ------------ - -printf("%f\n", deg2rad_(180.0)); -printf("%f\n", deg2rad(180.0)); - -// ---------------------------- - -function degree_sin($deg) { return sin(deg2rad($deg)); } - -// ------------ - -$rad = deg2rad(380.0); - -printf("%f\n", sin($rad)); -printf("%f\n", degree_sin(380.0)); - -// @@PLEAC@@_2.12 -function my_tan($theta) { return sin($theta) / cos($theta); } - -// ------------ - -$theta = 3.7; - -printf("%f\n", my_tan($theta)); -printf("%f\n", tan($theta)); - -// @@PLEAC@@_2.13 -$value = 100.0; - -$log_e = log($value); -$log_10 = log10($value); - -// ---------------------------- - -function log_base($base, $value) { return log($value) / log($base); } - -// ------------ - -$answer = log_base(10.0, 10000.0); - -printf("log(10, 10,000) = %f\n", $answer); - -// @@PLEAC@@_2.14 -// PHP offers no native support for matrices. However, a 'Math_Matrix' class -// is available for download from PEAR: [http://pear.php.net/package/Math_Matrix]. -// Note the following 'include' directives are required: -// -// include_once('Math/Matrix.php'); - -$a = new Math_Matrix(array(array(3, 2, 3), array(5, 9, 8))); -$b = new Math_Matrix(array(array(4, 7), array(9, 3), array(8, 1))); - -echo $a->toString() . "\n"; -echo $b->toString() . "\n"; - -// NOTE: When I installed this package I had to rename the 'clone' method else -// it would not load, so I chose to rename it to 'clone_', and this usage is -// shown below. This bug may well be fixed by the time you obtain this package - -$c = $a->clone_(); -$c->multiply($b); - -echo $c->toString() . "\n"; - -// @@PLEAC@@_2.15 -// PHP offers no native support for complex numbers. However, a 'Math_Complex' class -// is available for download from PEAR: [http://pear.php.net/package/Math_Complex]. -// Note the following 'include' directives are required: -// -// include_once('Math/Complex.php'); -// include_once('Math/TrigOp.php'); -// include_once('Math/ComplexOp.php'); - -$a = new Math_Complex(3, 5); -$b = new Math_Complex(2, -2); - -$c = Math_ComplexOp::mult($a, $b); - -echo $c->toString() . "\n"; - -// ---------------------------- - -$d = new Math_Complex(3, 4); -$r = Math_ComplexOp::sqrt($d); - -echo $r->toString() . "\n"; - -// @@PLEAC@@_2.16 -// Like C, PHP supports decimal-alternate notations. Thus, for example, the integer -// value, 867, is expressable in literal form as: -// -// Hexadecimal -> 0x363 -// Octal -> 01543 -// -// For effecting such conversions using strings there is 'sprintf' and 'sscanf'. - -$dec = 867; -$hex = sprintf('%x', $dec); -$oct = sprintf('%o', $dec); - -// ------------ - -$dec = 0; -$hex = '363'; - -sscanf($hex, '%x', $dec); - -// ------------ - -$dec = 0; -$oct = '1543'; - -sscanf($oct, '%o', $dec); - -// ---------------------------- - -$number = 0; - -printf('Gimme a number in decimal, octal, or hex: '); -sscanf(fgets(STDIN), '%D', $number); - -printf("%d %x %o\n", $number, $number, $number); - -// @@PLEAC@@_2.17 -// PHP offers the 'number_format' built-in function to, among many other format tasks, -// commify numbers. Perl-compatible [as well as extended] regexes are also available - -function commify_series($s) { return number_format($s, 0, '', ','); } - -// ------------ - -$hits = 3456789; - -printf("Your website received %s accesses last month\n", commify_series($hits)); - -// ---------------------------- - -function commify($s) -{ - return strrev(preg_replace('/(\d\d\d)(?=\d)(?!\d*\.)/', '${1},', strrev($s))); -} - -// ------------ - -$hits = 3456789; - -echo commify(sprintf("Your website received %d accesses last month\n", $hits)); - -// @@PLEAC@@_2.18 -function pluralise($value, $root, $singular='' , $plural='s') -{ - return $root . (($value > 1) ? $plural : $singular); -} - -// ------------ - -$duration = 1; -printf("It took %d %s\n", $duration, pluralise($duration, 'hour')); -printf("%d %s %s enough.\n", $duration, pluralise($duration, 'hour'), - pluralise($duration, '', 'is', 'are')); - -$duration = 5; -printf("It took %d %s\n", $duration, pluralise($duration, 'hour')); -printf("%d %s %s enough.\n", $duration, pluralise($duration, 'hour'), - pluralise($duration, '', 'is', 'are')); - -// ---------------------------- - -function plural($singular) -{ - $s2p = array('/ss$/' => 'sses', '/([psc]h)$/' => '${1}es', '/z$/' => 'zes', - '/ff$/' => 'ffs', '/f$/' => 'ves', '/ey$/' => 'eys', - '/y$/' => 'ies', '/ix$/' => 'ices', '/([sx])$/' => '$1es', - '$' => 's'); - - foreach($s2p as $s => $p) - { - if (preg_match($s, $singular)) return preg_replace($s, $p, $singular); - } -} - -// ------------ - -foreach(array('mess', 'index', 'leaf', 'puppy') as $word) -{ - printf("%6s -> %s\n", $word, plural($word)); -} - -// @@PLEAC@@_2.19 -// @@INCOMPLETE@@ -// @@INCOMPLETE@@ - -// @@PLEAC@@_3.0 -// PHP's date / time suport is quite extensive, and appears grouped into three areas of -// functionality: -// -// * UNIX / C Library [libc]-based routines, which include [among others]: -// - localtime, gmtime -// - strftime, strptime, mktime -// - time, getdate, gettimeofday, -// -// * PHP 'native' functions, those date / time routines released in earlier versions, -// and which otherwise provide 'convenience' functionality; these include: -// - date -// - strtotime -// -// * 'DateTime' class-based. This facility appears [according to the PHP documentation] -// to be extremely new / experimental, so whilst usage examples will be provided, they -// should not be taken to be 'official' examples, and obviously, subject to change. -// My own impression is that this facility is currently only partially implemented, -// so there is limited use for these functions. The functions included in this group -// are some of the 'date_'-prefixed functions; they are, however, not used standalone, -// but as methods in conjunction with an object. Typical usage: -// -// $today = new DateTime(); // actually calls: date_create($today, ...); -// echo $today->format('U') . "\n"; // actually calls: date_format($today, ...); -// -// Also worth mentioning is the PEAR [PHP Extension and Repository] package, 'Calendar', -// which offers a rich set of date / time manipulation facilities. However, since it is -// not currently shipped with PHP, no examples appear - -// Helper functions for performing date arithmetic - -function dateOffset() -{ - static $tbl = array('sec' => 1, 'min' => 60, 'hou' => 3600, 'day' => 86400, 'wee' => 604800); - $delta = 0; - - foreach (func_get_args() as $arg) - { - $kv = explode('=', $arg); - $delta += $kv[1] * $tbl[strtolower(substr($kv[0], 0, 3))]; - } - - return $delta; -} - -function dateInterval($intvltype, $timevalue) -{ - static $tbl = array('sec' => 1, 'min' => 60, 'hou' => 3600, 'day' => 86400, 'wee' => 604800); - return (int) round($timevalue / $tbl[strtolower(substr($intvltype, 0, 3))]); -} - -// ---------------------------- - -// Extract indexed array from 'getdate' -$today = getdate(); -printf("Today is day %d of the current year\n", $today['yday']); - -// Extract indexed, and associative arrays, respectively, from 'localtime' -$today = localtime(); -printf("Today is day %d of the current year\n", $today[7]); - -$today = localtime(time(), TRUE); -printf("Today is day %d of the current year\n", $today['tm_yday']); - -// @@PLEAC@@_3.1 -define(SEP, '-'); - -// ------------ - -$today = getdate(); - -$day = $today['mday']; -$month = $today['mon']; -$year = $today['year']; - -// Either do this to use interpolation: -$sep = SEP; -echo "Current date is: {$year}{$sep}{$month}{$sep}{$day}\n"; - -// or simply concatenate: -echo 'Current date is: ' . $year . SEP . $month . SEP . $day . "\n"; - -// ------------ - -$today = localtime(time(), TRUE); - -$day = $today['tm_mday']; -$month = $today['tm_mon'] + 1; -$year = $today['tm_year'] + 1900; - -printf("Current date is: %4d%s%2d%s%2d\n", $year, SEP, $month, SEP, $day); - -// ------------ - -$format = 'Y' . SEP . 'n' . SEP . 'd'; - -$today = date($format); - -echo "Current date is: {$today}\n"; - -// ------------ - -$sep = SEP; - -$today = strftime("%Y$sep%m$sep%d"); - -echo "Current date is: {$today}\n"; - -// @@PLEAC@@_3.2 -$timestamp = mktime($hour, $min, $sec, $month, $day, $year); - -$timestamp = gmmktime($hour, $min, $sec, $month, $day, $year); - -// @@PLEAC@@_3.3 -$dmyhms = getdate(); // timestamp: current date / time - -$dmyhms = getdate($timestamp); // timestamp: arbitrary - -$day = $dmyhms['mday']; -$month = $dmyhms['mon']; -$year = $dmyhms['year']; - -$hours = $dmyhms['hours']; -$minutes = $dmyhms['minutes']; -$seconds = $dmyhms['seconds']; - -// @@PLEAC@@_3.4 -// Date arithmetic is probably most easily performed using timestamps [i.e. *NIX Epoch -// Seconds]. Dates - in whatever form - are converted to timestamps, these are -// arithmetically manipulated, and the result converted to whatever form required. -// Note: use 'mktime' to create timestamps properly adjusted for daylight saving; whilst -// 'strtotime' is more convenient to use, it does not, AFAIK, include this adjustment - -$when = $now + $difference; -$then = $now - $difference; - -// ------------ - -$now = mktime(0, 0, 0, 8, 6, 2003); - -$diff1 = dateOffset('day=1'); $diff2 = dateOffset('weeks=2'); - -echo 'Today is: ' . date('Y-m-d', $now) . "\n"; -echo 'One day in the future is: ' . date('Y-m-d', $now + $diff1) . "\n"; -echo 'Two weeks in the past is: ' . date('Y-m-d', $now - $diff2) . "\n"; - -// ---------------------------- - -// Date arithmetic performed using a custom function, 'dateOffset'. Internally, offset may -// be computed in one of several ways: -// * Direct timestamp manipulation - fastest, but no daylight saving adjustment -// * Via 'date' built-in function - slower [?], needs a base time from which to -// compute values, but has daylight saving adjustment -// * Via 'strtotime' built-in function - as for 'date' -// * Via 'DateTime' class -// -// Approach used here is to utilise direct timestamp manipulation in 'dateOffset' [it's -// performance can also be improved by replacing $tbl with a global definition etc], -// and to illustrate how the other approaches might be used - -// 1. 'dateOffset' - -$birthtime = mktime(3, 45, 50, 1, 18, 1973); - -$interval = dateOffset('day=55', 'hours=2', 'min=17', 'sec=5'); - -$then = $birthtime + $interval; - -printf("Birthtime is: %s\nthen is: %s\n", date(DATE_RFC1123, $birthtime), date(DATE_RFC1123, $then)); - -// ------------ - -// 2. 'date' - -// Base values, and offsets, respectively -$hr = 3; $min = 45; $sec = 50; $mon = 1; $day = 18; $year = 1973; - -$yroff = 0; $monoff = 0; $dayoff = 55; $hroff = 2; $minoff = 17; $secoff = 5; - -// Base date -$birthtime = mktime($hr, $min, $sec, $mon, $day, $year, TRUE); - -$year = date('Y', $birthtime) + $yroff; -$mon = date('m', $birthtime) + $monoff; -$day = date('d', $birthtime) + $dayoff; - -$hr = date('H', $birthtime) + $hroff; -$min = date('i', $birthtime) + $minoff; -$sec = date('s', $birthtime) + $secoff; - -// Offset date -$then = mktime($hr, $min, $sec, $mon, $day, $year, TRUE); - -printf("Birthtime is: %s\nthen is: %s\n", date(DATE_RFC1123, $birthtime), date(DATE_RFC1123, $then)); - -// ------------ - -// 3. 'strtotime' - -// Generate timestamp whatever way is preferable -$birthtime = mktime(3, 45, 50, 1, 18, 1973); -$birthtime = strtotime('1/18/1973 03:45:50'); - -$then = strtotime('+55 days 2 hours 17 minutes 2 seconds', $birthtime); - -printf("Birthtime is: %s\nthen is: %s\n", date(DATE_RFC1123, $birthtime), date(DATE_RFC1123, $then)); - -// ------------ - -// 4. 'DateTime' class - -$birthtime = new DateTime('1/18/1973 03:45:50'); -$then = new DateTime('1/18/1973 03:45:50'); -$then->modify('+55 days 2 hours 17 minutes 2 seconds'); - -printf("Birthtime is: %s\nthen is: %s\n", $birthtime->format(DATE_RFC1123), $then->format(DATE_RFC1123)); - -// @@PLEAC@@_3.5 -// Date intervals are most easily computed using timestamps [i.e. *NIX Epoch -// Seconds] which, of course, gives the interval result is seconds from which -// all other interval measures [days, weeks, months, years] may be derived. -// Refer to previous section for discussion of daylight saving and other related -// problems - -$interval_seconds = $recent - $earlier; - -// ---------------------------- - -// Conventional approach ... -$bree = strtotime('16 Jun 1981, 4:35:25'); -$nat = strtotime('18 Jan 1973, 3:45:50'); - -// ... or, with daylight saving adjustment -$bree = mktime(4, 35, 25, 6, 16, 1981, TRUE); -$nat = mktime(3, 45, 50, 1, 18, 1973, TRUE); - -$difference = $bree - $nat; - -// 'dateInterval' custom function computes intervals in several measures given an -// interval in seconds. Note, 'month' and 'year' measures not provided -printf("There were %d seconds between Nat and Bree\n", $difference); -printf("There were %d weeks between Nat and Bree\n", dateInterval('weeks', $difference)); -printf("There were %d days between Nat and Bree\n", dateInterval('days', $difference)); -printf("There were %d hours between Nat and Bree\n", dateInterval('hours', $difference)); -printf("There were %d minutes between Nat and Bree\n", dateInterval('mins', $difference)); - -// @@PLEAC@@_3.6 -// 'getdate' accepts a timestamp [or implicitly calls 'time'] and returns an array of -// date components. It returns much the same information as 'strptime' except that -// the component names are different - -$today = getdate(); - -$weekday = $today['wday']; -$monthday = $today['mday']; -$yearday = $today['yday']; - -$weeknumber = (int) round($yearday / 7.0); - -// Safter method of obtaining week number -$weeknumber = strftime('%U') + 1; - -// ---------------------------- - -define(SEP, '/'); - -$day = 16; -$month = 6; -$year = 1981; - -$timestamp = mktime(0, 0, 0, $month, $day, $year); - -$date = getdate($timestamp); - -$weekday = $date['wday']; -$monthday = $date['mday']; -$yearday = $date['yday']; - -$weeknumber = (int) round($yearday / 7.0); - -$weeknumber = strftime('%U', $timestamp) + 1; - -// Interpolate ... -$sep = SEP; -echo "{$month}{$sep}{$day}{$sep}{$year} was a {$date['weekday']} in week {$weeknumber}\n"; - -// ... or, concatenate -echo $month . SEP . $day . SEP . $year . ' was a ' . $date['weekday'] - . ' in week ' . $weeknumber . "\n"; - -// @@PLEAC@@_3.7 -// 'strtotime' parses a textual date expression by attempting a 'best guess' at -// the format, and either fails, or generates a timestamp. Timestamp could be fed -// into any one of the various functions; example: -$timestamp = strtotime('1998-06-03'); echo strftime('%Y-%m-%d', $timestamp) . "\n"; - -// 'strptime' parses a textual date expression according to a specified format, -// and returns an array of date components; components can be easily dumped -print_r(strptime('1998-06-03', '%Y-%m-%d')); - -// ---------------------------- - -// Parse date string according to format -$darr = strptime('1998-06-03', '%Y-%m-%d'); - -if (!empty($darr)) -{ - // Show date components in 'debug' form - print_r($darr); - - // Check whether there was a parse error i.e. one or more components could not - // be extracted from the string - if (empty($darr['unparsed'])) - { - // Properly parsed date, so validate required components using, 'checkdate' - if (checkdate($darr['tm_mon'] + 1, $darr['tm_mday'], $darr['tm_year'] + 1900)) - echo "Parsed date verified as correct\n"; - else - echo "Parsed date failed verification\n"; - } - else - { - echo "Date string parse not complete; failed components: {$darr['unparsed']}\n"; - } -} -else -{ - echo "Date string could not be parsed\n"; -} - -// @@PLEAC@@_3.8 -// 'date' and 'strftime' both print a date string based on: -// * Format String, describing layout of date components -// * Timestamp [*NIX Epoch Seconds], either given explicitly, or implictly -// via a call to 'time' which retrieves current time value - -$ts = 1234567890; - -date('Y/m/d', $ts); -date('Y/m/d', mktime($h, $m, $s, $mth, $d, $y, $is_dst)); - -date('Y/m/d'); // same as: date('Y/m/d', time()); - -// ------------ - -$ts = 1234567890; - -strftime('%Y/%m/%d', $ts); -strftime('%Y/%m/%d', mktime($h, $m, $s, $mth, $d, $y, $is_dst)); - -strftime('%Y/%m/%d'); // same as: strftime('%Y/%m/%d', time()); - -// ---------------------------- - -// 'mktime' creates a local time timestamp -$t = strftime('%a %b %e %H:%M:%S %z %Y', mktime(3, 45, 50, 1, 18, 73, TRUE)); -echo "{$t}\n"; - -// 'gmmktime' creates a GMT time timestamp -$t = strftime('%a %b %e %H:%M:%S %z %Y', gmmktime(3, 45, 50, 1, 18, 73)); -echo "{$t}\n"; - -// ---------------------------- - -// 'strtotime' parses a textual date expression, and generates a timestamp -$t = strftime('%A %D', strtotime('18 Jan 1973, 3:45:50')); -echo "{$t}\n"; - -// This should generate output identical to previous example -$t = strftime('%A %D', mktime(3, 45, 50, 1, 18, 73, TRUE)); -echo "{$t}\n"; - -// @@PLEAC@@_3.9 -// PHP 5 and above can use the built-in, 'microtime'. Crude implementation for ealier versions: -// function microtime() { $t = gettimeofday(); return (float) ($t['sec'] + $t['usec'] / 1000000.0); } - -// ------------ - -$before = microtime(); - -$line = fgets(STDIN); - -$elapsed = microtime() - $before; - -printf("You took %.3f seconds\n", $elapsed); - -// ------------ - -define(NUMBER_OF_TIMES, 100); -define(SIZE, 500); - -for($i = 0; $i < NUMBER_OF_TIMES; $i++) -{ - $arr = array(); - for($j = 0; $j < SIZE; $j++) $arr[] = rand(); - - $begin = microtime(); - sort($arr); - $elapsed = microtime() - $begin; - - $total_time += $elapsed; -} - -printf("On average, sorting %d random numbers takes %.5f seconds\n", SIZE, $total_time / (float) NUMBER_OF_TIMES); - -// @@PLEAC@@_3.10 -// Low-resolution: sleep time specified in seconds -sleep(1); - -// High-resolution: sleep time specified in microseconds [not reliable under Windows] -usleep(250000); - -// @@PLEAC@@_3.11 -// @@INCOMPLETE@@ -// @@INCOMPLETE@@ - -// @@PLEAC@@_4.0 -// Nested arrays are supported, and may be easily printed using 'print_r' - -$nested = array('this', 'that', 'the', 'other'); - -$nested = array('this', 'that', array('the', 'other')); print_r($nested); - -$tune = array('The', 'Star-Spangled', 'Banner'); - -// @@PLEAC@@_4.1 -// PHP offers only the 'array' type which is actually an associative array, though -// may be numerically indexed, to mimic vectors and matrices; there is no separate -// 'list' type - -$a = array('quick', 'brown', 'fox'); - -// ------------ - -$a = escapeshellarg('Why are you teasing me?'); - -// ------------ - -$lines = <<<END_OF_HERE_DOC - The boy stood on the burning deck, - it was as hot as glass. -END_OF_HERE_DOC; - -// ------------ - -$bigarray = array_map('rtrim', file('mydatafile')); - -// ------------ - -$banner = 'The mines of Moria'; - -$banner = escapeshellarg('The mines of Moria'); - -// ------------ - -$name = 'Gandalf'; - -$banner = "Speak {$name}, and enter!"; - -$banner = 'Speak ' . escapeshellarg($name) . ' and welcome!'; - -// ------------ - -$his_host = 'www.perl.com'; - -$host_info = `nslookup $his_host`; - -$cmd = 'ps ' . posix_getpid(); $perl_info = `$cmd`; - -$shell_info = `ps $$`; - -// ------------ - -$banner = array('Costs', 'only', '$4.95'); - -$banner = array_map('escapeshellarg', split(' ', 'Costs only $4.95')); - -// ------------ - -// AFAIK PHP doesn't support non-quoted strings ala Perl's 'q', 'qq' and 'qw', so arrays -// created from strings must use quoted strings, and make use of 'split' [or equivalent]. -// A slew of functions exist for performing string quoting, including 'escapeshellarg', -// 'quotemeta', and 'preg_quote' - -$brax = split(' ', '( ) < > { } [ ]'); - -// Do this to quote each element within '..' -// $brax = array_map('escapeshellarg', split(' ', '( ) < > { } [ ]')); - -$rings = split(' ', 'Nenya Narya Vilya'); - -$tags = split(' ', 'LI TABLE TR TD A IMG H1 P'); - -$sample = split(' ', 'The vertical bar | looks and behaves like a pipe.'); - -// @@PLEAC@@_4.2 -function commify_series($list) -{ - $n = str_word_count($list); $series = str_word_count($list, 1); - - if ($n == 0) return NULL; - if ($n == 1) return $series[0]; - if ($n == 2) return $series[0] . ' and ' . $series[1]; - - return join(', ', array_slice($series, 0, -1)) . ', and ' . $series[$n - 1]; -} - -// ------------ - -echo commify_series('red') . "\n"; -echo commify_series('red yellow') . "\n"; -echo commify_series('red yellow green') . "\n"; - -$mylist = 'red yellow green'; -echo 'I have ' . commify_series($mylist) . " marbles.\n"; - -// ---------------------------- - -function commify_series($arr) -{ - $n = count($arr); $sepchar = ','; - - foreach($arr as $str) - { - if (strpos($str, ',') === false) continue; - $sepchar = ';'; break; - } - - if ($n == 0) return NULL; - if ($n == 1) return $arr[0]; - if ($n == 2) return $arr[0] . ' and ' . $arr[1]; - - return join("{$sepchar} ", array_slice($arr, 0, -1)) . "{$sepchar} and " . $arr[$n - 1]; -} - -// ------------ - -$lists = array( - array('just one thing'), - split(' ', 'Mutt Jeff'), - split(' ', 'Peter Paul Mary'), - array('To our parents', 'Mother Theresa', 'God'), - array('pastrami', 'ham and cheese', 'peanut butter and jelly', 'tuna'), - array('recycle tired, old phrases', 'ponder big, happy thoughts'), - array('recycle tired, old phrases', 'ponder big, happy thoughts', 'sleep and dream peacefully')); - -foreach($lists as $arr) -{ - echo 'The list is: ' . commify_series($arr) . ".\n"; -} - -// @@PLEAC@@_4.3 -// AFAICT you cannot grow / shrink an array to an arbitrary size. However, you can: -// * Grow an array by appending an element using subscrip notation, or using -// either 'array_unshift' or 'array_push' to add one or more elements - -$arr[] = 'one'; -array_unshift($arr, 'one', 'two', 'three'); -array_push($arr, 'one', 'two', 'three'); - -// * Shrink an array by using 'unset' to remove one or more specific elements, or -// either 'array_shift' or 'array_pop' to remove an element from the ends - -unset($arr[$idx1], $arr[$idx2], $arr[$idx3]); -$item = array_shift($arr); -$item = array_pop($arr); - -// ---------------------------- - -function what_about_the_array() -{ - global $people; - - echo 'The array now has ' . count($people) . " elements\n"; - echo 'The index value of the last element is ' . (count($people) - 1) . "\n"; - echo 'Element #3 is ' . $people[3] . "\n"; -} - -$people = array('Crosby', 'Stills', 'Nash', 'Young'); -what_about_the_array(); - -array_pop($people); -what_about_the_array(); - -// Cannot, AFAICT, resize the array to an arbitrary size - -# @@PLEAC@@_4.4 -foreach ($list as $item) { - // do something with $item -} - -// Environment listing example - -// PHP defines a superglobal $_ENV to provide access to environment -// variables. - -// Beware, array assignment means copying in PHP. You need to use -// the reference operator to avoid copying. But we want a copy here. -$env = $_ENV; - -// PHP can sort an array by key, so you don't need to get keys, -// and then sort. -ksort($env); - -foreach ($env as $key => $value) { - echo "{$key}={$value}\n"; -} - -// Literal translation of Perl example would be: -$keys = array_keys($_ENV); -sort($keys); -foreach ($keys as $key) { - echo "{$key}={$_ENV[$key]}\n"; -} - -// This assumes that MAX_QUOTA is a named constant. -foreach ($all_users as $user) { - $disk_space = get_usage($user); - if ($disk_space > MAX_QUOTA) { - complain($user); - } -} - -// You can't modify array's elements in-place. -$array = array(1, 2, 3); -$newarray = array(); -foreach ($array as $item) { - $newarray[] = $item - 1; -} -print_r($newarray); - -// Before PHP 5, that is. You can precede the reference operator -// before $item to get reference instead of copy. -$array = array(1, 2, 3); -foreach ($array as &$item) { - $item--; -} -print_r($array); - -// TODO: explain the old each() and list() iteration construct. -// foreach is new in PHP 4, and there are subtle differences. - -// @@PLEAC@@_4.5 -// Conventional 'read-only' access -foreach($array as $item) -{ - ; // Can access, but not update, array element referred to by '$item' -} - -// ---- - -// '&' makes '$item' a reference -foreach($array as &$item) -{ - ; // Update array element referred to by '$item' -} - -// ------------ - -$arraylen = count($array); - -for($i = 0; $i < $arraylen; $i++) -{ - ; // '$array' is updateable via subscript notation -} - -// ---------------------------- - -$fruits = array('Apple', 'Raspberry'); - -foreach($fruits as &$fruit) -{ - echo "{$fruit} tastes good in a pie.\n"; -} - -$fruitlen = count($fruits); - -for($i = 0; $i < $fruitlen; $i++) -{ - echo "{$fruits[$i]} tastes good in a pie.\n"; -} - -// ---------------------------- - -$rogue_cats = array('Blackie', 'Goldie', 'Silkie'); - -// Take care to assign reference to '$rogue_cats' array via '=&' -$namelist['felines'] =& $rogue_cats; - -// Take care to make '$cat' a reference via '&$' to allow updating -foreach($namelist['felines'] as &$cat) -{ - $cat .= ' [meow]'; -} - -// Via array reference -foreach($namelist['felines'] as $cat) -{ - echo "{$cat} purrs hypnotically.\n"; -} - -echo "---\n"; - -// Original array -foreach($rogue_cats as $cat) -{ - echo "{$cat} purrs hypnotically.\n"; -} - -// @@PLEAC@@_4.6 -// PHP offers the 'array_unique' function to perform this task. It works with both keyed, -// and numerically-indexed arrays; keys / indexes are preserved; use of 'array_values' -// is recommended to reindex numerically-indexed arrays since there will likely be missing -// indexes - -// Remove duplicate values -$unique = array_unique($array); - -// Remove duplicates, and reindex [for numerically-indexed arrays only] -$unique = array_values(array_unique($array)); - -// or use: -$unique = array_keys(array_flip($array)); - -// ---------------------------- - -// Selected Perl 'seen' examples -foreach($list as $item) -{ - if (!isset($seen[$item])) - { - $seen[$item] = TRUE; - $unique[] = $item; - } -} - -// ------------ - -foreach($list as $item) -{ - $seen[$item] || (++$seen[$item] && ($unique[] = $item)); -} - -// ------------ - -function some_func($item) -{ - ; // Do something with '$item' -} - -foreach($list as $item) -{ - $seen[$item] || (++$seen[$item] && some_func($item)); -} - -// ---------------------------- - -foreach(array_slice(preg_split('/\n/', `who`), 0, -1) as $user_entry) -{ - $user = preg_split('/\s/', $user_entry); - $ucnt[$user[0]]++; -} - -ksort($ucnt); - -echo "users logged in:\n"; - -foreach($ucnt as $user => $cnt) -{ - echo "\t{$user} => {$cnt}\n"; -} - -// @@PLEAC@@_4.7 -// PHP offers the 'array_diff' and 'array_diff_assoc' functions to perform this task. Same -// points as made about 'array_unique' apply here also - -$a = array('c', 'a', 'b', 'd'); -$b = array('c', 'a', 'b', 'e'); - -$diff = array_diff($a, $b); // $diff -> [3] 'd' -$diff = array_diff($b, $a); // $diff -> [3] 'e' - -// Numerically-indexed array, reindexed -$diff = array_values(array_diff($a, $b)); // $diff -> [0] 'd' -$diff = array_values(array_diff($b, $a)); // $diff -> [0] 'e' - -// ---------------------------- - -// 1st Perl 'seen' example only - -$a = array('k1' => 11, 'k2' => 12, 'k4' => 14); -$b = array('k1' => 11, 'k2' => 12, 'k3' => 13); - -foreach($b as $item => $value) { $seen[$item] = 1; } - -// Stores key only e.g. $aonly[0] contains 'k4', same as Perl example -foreach($a as $item => $value) { if (!$seen[$item]) $aonly[] = $item; } - -// Stores key and value e.g. $aonly['k4'] contains 14, same entry as in $a -foreach($a as $item => $value) { if (!$seen[$item]) $aonly[$item] = $value; } - -// ---------------------------- - -// Conventional way: $hash = array('key1' => 1, 'key2' => 2); - -$hash['key1'] = 1; -$hash['key2'] = 2; - -$hash = array_combine(array('key1', 'key2'), array(1, 2)); - -// ------------ - -$seen = array_slice($b, 0); - -$seen = array_combine(array_keys($b), array_fill(0, count($b), 1)); - -// @@PLEAC@@_4.8 -// PHP offers a number of array-based 'set operation' functions: -// * union: array_unique(array_merge(...)) -// * intersection: array_intersect and family -// * difference: array_diff and family -// which may be used for this type of task. Also, if returned arrays need to be -// reindexed, 'array_slice($array, 0)', or 'array_values($array)' are useful - -$a = array(1, 3, 5, 6, 7, 8); -$b = array(2, 3, 5, 7, 9); - -$union = array_values(array_unique(array_merge($a, $b))); // 1, 2, 3, 5, 6, 7, 8, 9 -$isect = array_values(array_intersect($a, $b)); // 3, 5, 7 -$diff = array_values(array_diff($a, $b)); // 1, 8 - -// @@PLEAC@@_4.9 -// PHP offers the 'array_merge' function to perform this task. Duplicate values are retained, -// but if arrays are numerically-indexed, resulting array is reindexed - -$arr1 = array('c', 'a', 'b', 'd'); -$arr2 = array('c', 'a', 'b', 'e'); - -$new = array_merge($arr1, $arr2); // $new -> 'c', 'a', 'b', 'd', 'c', 'a', 'b', 'd' - -// ---------------------------- - -$members = array('Time', 'Flies'); -$initiates = array('An', 'Arrow'); - -$members = array_merge($members, $initiates); - -// ------------ - -$members = array('Time', 'Flies'); -$initiates = array('An', 'Arrow'); - -// 'array_splice' is the PHP equivalent to Perl's 'splice' -array_splice($members, 2, 0, array_merge(array('Like'), $initiates)); -echo join(' ', $members) . "\n"; - -array_splice($members, 0, 1, array('Fruit')); -array_splice($members, -2, 2, array('A', 'Banana')); -echo join(' ', $members) . "\n"; - -// @@PLEAC@@_4.10 -$reversed = array_reverse($array); - -// ---------------------------- - -foreach(array_reverse($array) as $item) -{ - ; // ... do something with '$item' ... -} - -// ------------ - -for($i = count($array) - 1; $i >= 0; $i--) -{ - ; // ... do something with '$array[$i]' ... -} - -// ---------------------------- - -sort($array); -$array = array_reverse($array); - -// ------------ - -rsort($array); - -// @@PLEAC@@_4.11 -// Array elements can be deleted using 'unset'; removing several elements would require applying -// 'unset' several times, probably in a loop. However, they would most likely also need to be -// reindexed, so a better approach would be to use 'array_slice' which avoids explicit looping. -// Where elements need to be removed, and those elements also returned, it is probably best to -// combine both operations in a function. This is the approach taken here in implementing both -// 'shiftN' and 'popN', and it is these functions that are used in the examples - -function popN(&$arr, $n) -{ - $ret = array_slice($arr, -($n), $n); - $arr = array_slice($arr, 0, count($arr) - $n); - return $ret; -} - -function shiftN(&$arr, $n) -{ - $ret = array_slice($arr, 0, $n); - $arr = array_slice($arr, $n); - return $ret; -} - -// ------------ - -// Remove $n elements from the front of $array; return them in $fron -$front = shiftN($array, $n); - -// Remove $n elements from the end of $array; return them in $end -$end = popN($array, $n); - -// ------------ - -$friends = array('Peter', 'Paul', 'Mary', 'Jim', 'Tim'); - -list($this_, $that) = shiftN($friends, 2); - -echo "{$this_} {$that}\n"; - -// ------------ - -$beverages = array('Dew', 'Jolt', 'Cola', 'Sprite', 'Fresca'); - -$pair = popN($beverages, 2); - -echo join(' ', $pair) . "\n"; - -// @@PLEAC@@_4.12 -// This section illustrates various 'find first' techniques. The Perl examples all use an -// explicit loop and condition testing [also repeated here]. This is the simplest, and -// [potentially] most efficient approach because the search can be terminated as soon as a -// match is found. However, it is worth mentioning a few alternatives: -// * 'array_search' performs a 'find first' using the element value rather than a condition -// check, so isn't really applicable here -// * 'array_filter', whilst using a condition check, actually performs a 'find all', though -// all but the first returned element can be discarded. This approach is actually less error -// prone than using a loop, but the disadvantage is that each element is visited: there is no -// means of terminating the search once a match has been found. It would be nice if this -// function were to have a third parameter, a Boolean flag indicating whether to traverse -// the whole array, or quit after first match [next version, maybe :) ?] - -$found = FALSE; - -foreach($array as $item) -{ - // Not found - skip to next item - if (!$criterion) continue; - - // Found - save and leave - $match = $item; - $found = TRUE; - break; -} - -if ($found) -{ - ; // do something with $match -} -else -{ - ; // not found -} - -// ------------ - -function predicate($element) -{ - if (criterion) return TRUE; - return FALSE; -} - -$match = array_slice(array_filter($array, 'predicate'), 0, 1); - -if ($match) -{ - ; // do something with $match[0] -} -else -{ - ; // $match is empty - not found -} - -// ---------------------------- - -class Employee -{ - public $name, $age, $ssn, $salary; - - public function __construct($name, $age, $ssn, $salary, $category) - { - $this->name = $name; - $this->age = $age; - $this->ssn = $ssn; - $this->salary = $salary; - $this->category = $category; - } -} - -// ------------ - -$employees = array( - new Employee('sdf', 27, 12345, 47000, 'Engineer'), - new Employee('ajb', 32, 12376, 51000, 'Programmer'), - new Employee('dgh', 31, 12355, 45000, 'Engineer')); - -// ------------ - -function array_update($arr, $lambda, $updarr) -{ - foreach($arr as $key) $lambda($updarr, $key); - return $updarr; -} - -function highest_salaried_engineer(&$arr, $employee) -{ - static $highest_salary = 0; - - if ($employee->category == 'Engineer') - { - if ($employee->salary > $highest_salary) - { - $highest_salary = $employee->salary; - $arr[0] = $employee; - } - } -} - -// ------------ - -// 'array_update' custom function is modelled on 'array_reduce' except that it allows the -// return of an array, contents and length of which are entirely dependant on what the -// callback function does. Here, it is logically working in a 'find first' capacity -$highest_salaried_engineer = array_update($employees, 'highest_salaried_engineer', array()); - -echo 'Highest paid engineer is: ' . $highest_salaried_engineer[0]->name . "\n"; - -// @@PLEAC@@_4.13 -// PHP implements 'grep' functionality [as embodied in the current section] in the 'array_filter' -// function - -function predicate($element) -{ - if (criterion) return TRUE; - return FALSE; -} - -$matching = array_filter($list, 'predicate'); - -// ------------ - -$bigs = array_filter($nums, create_function('$n', 'return $n > 1000000;')); - -// ------------ - -function is_pig($user) -{ - $user_details = preg_split('/(\s)+/', $user); - // Assuming field 5 is the resource being compared - return $user_details[5] > 1e7; -} - -$pigs = array_filter(array_slice(preg_split('/\n/', `who -u`), 0, -1), 'is_pig'); - -// ------------ - -$matching = array_filter(array_slice(preg_split('/\n/', `who`), 0, -1), - create_function('$user', 'return preg_match(\'/^gnat /\', $user);')); - -// ------------ - -class Employee -{ - public $name, $age, $ssn, $salary; - - public function __construct($name, $age, $ssn, $salary, $category) - { - $this->name = $name; - $this->age = $age; - $this->ssn = $ssn; - $this->salary = $salary; - $this->category = $category; - } -} - -// ------------ - -$employees = array( - new Employee('sdf', 27, 12345, 47000, 'Engineer'), - new Employee('ajb', 32, 12376, 51000, 'Programmer'), - new Employee('dgh', 31, 12355, 45000, 'Engineer')); - -// ------------ - -$engineers = array_filter($employees, - create_function('$employee', 'return $employee->category == "Engineer";')); - -// @@PLEAC@@_4.14 -// PHP offers a rich set of sorting functions. Key features: -// * Inplace sorts; the original array, not a a copy, is sorted -// * Separate functions exist for sorting [both ascending and descending order]: -// - By value, assign new keys / indices [sort, rsort] -// - By key [ksort, krsort] (for non-numerically indexed arrays) -// - By value [asort, arsort] -// - As above, but using a user-defined comparator [i.e. callback function] -// [usort, uasort, uksort] -// - Natural order sort [natsort] -// * Significantly, if sorting digit-only elements, whether strings or numbers, -// 'natural order' [i.e. 1 before 10 before 100 (ascending)] is retained. If -// the elements are alphanumeric e.g. 'z1', 'z10' then 'natsort' should be -// used [note: beware of 'natsort' with negative numbers; prefer 'sort' or 'asort'] - -$unsorted = array(7, 12, -13, 2, 100, 5, 1, -2, 23, 3, 6, 4); - -sort($unsorted); // -13, -2, 1, 2, 3, 4, 5, 6, 7, 12, 23, 100 -rsort($unsorted); // 100, 23, 12, 7, 6, 5, 4, 3, 2, 1, -2, -13 - -asort($unsorted); // -13, -2, 1, 2, 3, 4, 5, 6, 7, 12, 23, 100 -arsort($unsorted); // 100, 23, 12, 7, 6, 5, 4, 3, 2, 1, -2, -13 - -natsort($unsorted); // -2, -13, 1, 2, 3, 4, 5, 6, 7, 12, 23, 100 - -// ------------ - -function ascend($left, $right) { return $left > $right; } -function descend($left, $right) { return $left < $right; } - -// ------------ - -usort($unsorted, 'ascend'); // -13, -2, 1, 2, 3, 4, 5, 6, 7, 12, 23, 100 -usort($unsorted, 'descend'); // 100, 23, 12, 7, 6, 5, 4, 3, 2, 1, -2, -13 - -uasort($unsorted, 'ascend'); // -13, -2, 1, 2, 3, 4, 5, 6, 7, 12, 23, 100 -uasort($unsorted, 'descend'); // 100, 23, 12, 7, 6, 5, 4, 3, 2, 1, -2, -13 - -// ---------------------------- - -function kill_process($pid) -{ - // Is 'killable' ? - if (!posix_kill($pid, 0)) return; - - // Ok, so kill in two stages - posix_kill($pid, 15); // SIGTERM - sleep(1); - posix_kill($pid, 9); // SIGKILL -} - -function pid($pentry) -{ - $p = preg_split('/\s/', trim($pentry)); - return $p[0]; -} - -$processes = array_map('pid', array_slice(preg_split('/\n/', `ps ax`), 1, -1)); -sort($processes); - -echo join(' ,', $processes) . "\n"; - -echo 'Enter a pid to kill: '; -if (($pid = trim(fgets(STDIN)))) - kill_process($pid); - -// @@PLEAC@@_4.15 -// Tasks in this section would typically use the PHP 'usort' family of functions -// which are used with a comparator function so as to perform custom comparisions. -// A significant difference from the Perl examples is that these functions are -// inplace sorters, so it is the original array that is modified. Where this must -// be prevented a copy of the array can be made and sorted - -function comparator($left, $right) -{ - ; // Compare '$left' with '$right' returning result -} - -// ------------ - -$ordered = array_slice($unordered); -usort($ordered, 'comparator'); - -// ---------------------------- - -// The Perl example looks like it is creating a hash using computed values as the key, -// array values as the value, sorting on the computed key, then extracting the sorted -// values and placing them back into an array - -function compute($value) -{ - ; // Return computed value utilising '$value' -} - -// ------------ - -// Original numerically-indexed array [sample data used] -$unordered = array(5, 3, 7, 1, 4, 2, 6); - -// Create hash using 'compute' function to generate the keys. This example assumes that -// each value in the '$unordered' array is used in generating the corresponding '$key' -foreach($unordered as $value) -{ - $precomputed[compute($value)] = $value; -} - -// Copy the hash, and sort it by key -$ordered_precomputed = array_slice($precomputed, 0); ksort($ordered_precomputed); - -// Extract the values of the hash in current order placing them in a new numerically-indexed -// array -$ordered = array_values($ordered_precomputed); - -// ---------------------------- - -// As above, except uses 'array_update' and 'accum' to help create hash - -function array_update($arr, $lambda, $updarr) -{ - foreach($arr as $key) $lambda($updarr, $key); - return $updarr; -} - -function accum(&$arr, $value) -{ - $arr[compute($value)] = $value; -} - -// ------------ - -function compute($value) -{ - ; // Return computed value utilising '$value' -} - -// ------------ - -// Original numerically-indexed array [sample data used] -$unordered = array(5, 3, 7, 1, 4, 2, 6); - -// Create hash -$precomputed = array_update($unordered, 'accum', array()); - -// Copy the hash, and sort it by key -$ordered_precomputed = array_slice($precomputed, 0); ksort($ordered_precomputed); - -// Extract the values of the hash in current order placing them in a new numerically-indexed -// array -$ordered = array_values($ordered_precomputed); - -// ---------------------------- - -class Employee -{ - public $name, $age, $ssn, $salary; - - public function __construct($name, $age, $ssn, $salary) - { - $this->name = $name; - $this->age = $age; - $this->ssn = $ssn; - $this->salary = $salary; - } -} - -// ------------ - -$employees = array( - new Employee('sdf', 27, 12345, 47000), - new Employee('ajb', 32, 12376, 51000), - new Employee('dgh', 31, 12355, 45000)); - -// ------------ - -$ordered = array_slice($employees, 0); -usort($ordered, create_function('$left, $right', 'return $left->name > $right->name;')); - -// ------------ - -$sorted_employees = array_slice($employees, 0); -usort($sorted_employees, create_function('$left, $right', 'return $left->name > $right->name;')); - -$bonus = array(12376 => 5000, 12345 => 6000, 12355 => 0); - -foreach($sorted_employees as $employee) -{ - echo "{$employee->name} earns \${$employee->salary}\n"; -} - -foreach($sorted_employees as $employee) -{ - if (($amount = $bonus[$employee->ssn])) - echo "{$employee->name} got a bonus of: \${$amount}\n"; -} - -// ------------ - -$sorted = array_slice($employees, 0); -usort($sorted, create_function('$left, $right', 'return $left->name > $right->name || $left->age != $right->age;')); - -// ---------------------------- - -// PHP offers a swag of POSIX functions for obtaining user information [i.e. they all read -// the '/etc/passwd' file for the relevant infroamtion], and it is these that should rightly -// be used for this purpose. However, since the intent of this section is to illustrate array -// manipulation, these functions won't be used. Instead a custom function mimicing Perl's -// 'getpwent' function will be implemented so the code presented here can more faithfully match -// the Perl code - -function get_pw_entries() -{ - function normal_users_only($e) - { - $entry = split(':', $e); return $entry[2] > 100 && $entry[2] < 32768; - } - - foreach(array_filter(file('/etc/passwd'), 'normal_users_only') as $entry) - $users[] = split(':', trim($entry)); - - return $users; -} - -// ------------ - -$users = get_pw_entries(); - -usort($users, create_function('$left, $right', 'return $left[0] > $right[0];')); -foreach($users as $user) echo "{$user[0]}\n"; - -// ---------------------------- - -$names = array('sdf', 'ajb', 'dgh'); - -$sorted = array_slice($names, 0); -usort($sorted, create_function('$left, $right', 'return substr($left, 1, 1) > substr($right, 1, 1);')); - -// ------------ - -$strings = array('bbb', 'aa', 'c'); - -$sorted = array_slice($strings, 0); -usort($sorted, create_function('$left, $right', 'return strlen($left) > strlen($right);')); - -// ---------------------------- - -function array_update($arr, $lambda, $updarr) -{ - foreach($arr as $key) $lambda($updarr, $key); - return $updarr; -} - -function accum(&$arr, $value) -{ - $arr[strlen($value)] = $value; -} - -// ---- - -$strings = array('bbb', 'aa', 'c'); - -$temp = array_update($strings, 'accum', array()); -ksort($temp); -$sorted = array_values($temp); - -// ---------------------------- - -function array_update($arr, $lambda, $updarr) -{ - foreach($arr as $key) $lambda($updarr, $key); - return $updarr; -} - -function accum(&$arr, $value) -{ - if (preg_match('/(\d+)/', $value, $matches)) - $arr[$matches[1]] = $value; -} - -// ---- - -$fields = array('b1b2b', 'a4a', 'c9', 'ddd', 'a'); - -$temp = array_update($fields, 'accum', array()); -ksort($temp); -$sorted_fields = array_values($temp); - -// @@PLEAC@@_4.16 -array_unshift($a1, array_pop($a1)); // last -> first -array_push($a1, array_shift($a1)); // first -> last - -// ---------------------------- - -function grab_and_rotate(&$arr) -{ - $item = $arr[0]; - array_push($arr, array_shift($arr)); - return $item; -} - -// ------------ - -$processes = array(1, 2, 3, 4, 5); - -while (TRUE) -{ - $process = grab_and_rotate($processes); - echo "Handling process {$process}\n"; - sleep(1); -} - -// @@PLEAC@@_4.17 -// PHP offers the 'shuffle' function to perform this task - -$arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9); - -shuffle($arr); - -echo join(' ', $arr) . "\n"; - -// ---------------------------- - -// Perl example equivalents -function fisher_yates_shuffle(&$a) -{ - $size = count($a) - 1; - - for($i = $size; $i >= 0; $i--) - { - if (($j = rand(0, $i)) != $i) - list($a[$i], $a[$j]) = array($a[$j], $a[$i]); - } -} - -function naive_shuffle(&$a) -{ - $size = count($a); - - for($i = 0; $i < $size; $i++) - { - $j = rand(0, $size - 1); - list($a[$i], $a[$j]) = array($a[$j], $a[$i]); - } -} - -// ------------ - -$arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9); - -fisher_yates_shuffle($arr); -echo join(' ', $arr) . "\n"; - -naive_shuffle($arr); -echo join(' ', $arr) . "\n"; - -// @@PLEAC@@_4.18 -// @@INCOMPLETE@@ -// @@INCOMPLETE@@ - -// @@PLEAC@@_4.19 -// @@INCOMPLETE@@ -// @@INCOMPLETE@@ - -// @@PLEAC@@_5.0 -// PHP uses the term 'array' to refer to associative arrays - referred to in Perl -// as 'hashes' - and for the sake of avoiding confusion, the Perl terminology will -// be used. As a matter of interest, PHP does not sport a vector, matrix, or list -// type: the 'array' [Perl 'hash'] serves all these roles - -$age = array('Nat' => 24, 'Jules' => 25, 'Josh' => 17); - -$age['Nat'] = 24; -$age['Jules'] = 25; -$age['Josh'] = 17; - -$age = array_combine(array('Nat', 'Jules', 'Josh'), array(24, 25, 17)); - -// ------------ - -$food_colour = array('Apple' => 'red', 'Banana' => 'yellow', - 'Lemon' => 'yellow', 'Carrot' => 'orange'); - -$food_colour['Apple'] = 'red'; $food_colour['Banana'] = 'yellow'; -$food_colour['Lemon'] = 'yellow'; $food_colour['Carrot'] = 'orange'; - -$food_colour = array_combine(array('Apple', 'Banana', 'Lemon', 'Carrot'), - array('red', 'yellow', 'yellow', 'orange')); - -// @@PLEAC@@_5.1 -$hash[$key] = $value; - -// ------------ - -$food_colour = array('Apple' => 'red', 'Banana' => 'yellow', - 'Lemon' => 'yellow', 'Carrot' => 'orange'); - -$food_colour['Raspberry'] = 'pink'; - -echo "Known foods:\n"; -foreach($food_colour as $food => $colour) echo "{$food}\n"; - -// @@PLEAC@@_5.2 -// Returns TRUE on all existing entries with non-NULL values -if (isset($hash[$key])) - ; // entry exists -else - ; // no such entry - -// ------------ - -// Returns TRUE on all existing entries regardless of attached value -if (array_key_exists($key, $hash)) - ; // entry exists -else - ; // no such entry - -// ---------------------------- - -$food_colour = array('Apple' => 'red', 'Banana' => 'yellow', - 'Lemon' => 'yellow', 'Carrot' => 'orange'); - -foreach(array('Banana', 'Martini') as $name) -{ - if (isset($food_colour[$name])) - echo "{$name} is a food.\n"; - else - echo "{$name} is a drink.\n"; -} - -// ---------------------------- - -$age = array('Toddler' => 3, 'Unborn' => 0, 'Phantasm' => NULL); - -foreach(array('Toddler', 'Unborn', 'Phantasm', 'Relic') as $thing) -{ - echo "{$thing}:"; - if (array_key_exists($thing, $age)) echo ' exists'; - if (isset($age[$thing])) echo ' non-NULL'; - if ($age[$thing]) echo ' TRUE'; - echo "\n"; -} - -// @@PLEAC@@_5.3 -// Remove one, or more, hash entries -unset($hash[$key]); - -unset($hash[$key1], $hash[$key2], $hash[$key3]); - -// Remove entire hash -unset($hash); - -// ---------------------------- - -function print_foods() -{ - // Perl example uses a global variable - global $food_colour; - - $foods = array_keys($food_colour); - - echo 'Foods:'; - foreach($foods as $food) echo " {$food}"; - - echo "\nValues:\n"; - foreach($foods as $food) - { - $colour = $food_colour[$food]; - - if (isset($colour)) - echo " {$colour}\n"; - else - echo " nullified or removed\n"; - } -} - -// ------------ - -$food_colour = array('Apple' => 'red', 'Banana' => 'yellow', - 'Lemon' => 'yellow', 'Carrot' => 'orange'); - -echo "Initially:\n"; print_foods(); - -// Nullify an entry -$food_colour['Banana'] = NULL; -echo "\nWith 'Banana' nullified\n"; -print_foods(); - -// Remove an entry -unset($food_colour['Banana']); -echo "\nWith 'Banana' removed\n"; -print_foods(); - -// Destroy the hash -unset($food_colour); - -// @@PLEAC@@_5.4 -// Access keys and values -foreach($hash as $key => $value) -{ - ; // ... -} - -// Access keys only -foreach(array_keys($hash) as $key) -{ - ; // ... -} - -// Access values only -foreach($hash as $value) -{ - ; // ... -} - -// ---------------------------- - -$food_colour = array('Apple' => 'red', 'Banana' => 'yellow', - 'Lemon' => 'yellow', 'Carrot' => 'orange'); - -foreach($food_colour as $food => $colour) -{ - echo "{$food} is {$colour}\n"; -} - -foreach(array_keys($food_colour) as $food) -{ - echo "{$food} is {$food_colour[$food]}\n"; -} - -// ---------------------------- - -// 'countfrom' - count number of messages from each sender - -$line = fgets(STDIN); - -while (!feof(STDIN)) -{ - if (preg_match('/^From: (.*)/', $line, $matches)) - { - if (isset($from[$matches[1]])) - $from[$matches[1]] += 1; - else - $from[$matches[1]] = 1; - } - - $line = fgets(STDIN); -} - -if (isset($from)) -{ - echo "Senders:\n"; - foreach($from as $sender => $count) echo "{$sender} : {$count}\n"; -} -else -{ - echo "No valid data entered\n"; -} - -// @@PLEAC@@_5.5 -// PHP offers, 'print_r', which prints hash contents in 'debug' form; it also -// works recursively, printing any contained arrays in similar form -// Array -// ( -// [key1] => value1 -// [key2] => value2 -// ... -// ) - -print_r($hash); - -// ------------ - -// Based on Perl example; non-recursive, so contained arrays not printed correctly -foreach($hash as $key => $value) -{ - echo "{$key} => $value\n"; -} - -// ---------------------------- - -// Sorted by keys - -// 1. Sort the original hash -ksort($hash); - -// 2. Extract keys, sort, traverse original in key order -$keys = array_keys($hash); sort($keys); - -foreach($keys as $key) -{ - echo "{$key} => {$hash[$key]}\n"; -} - -// Sorted by values - -// 1. Sort the original hash -asort($hash); - -// 2. Extract values, sort, traverse original in value order [warning: finds -// only first matching key in the case where duplicate values exist] -$values = array_values($hash); sort($values); - -foreach($values as $value) -{ - echo $value . ' <= ' . array_search($value, $hash) . "\n"; -} - -// @@PLEAC@@_5.6 -// Unless sorted, hash elements remain in the order of insertion. If care is taken to -// always add a new element to the end of the hash, then element order is the order -// of insertion. The following function, 'array_push_associative' [modified from original -// found at 'array_push' section of PHP documentation], does just that -function array_push_associative(&$arr) -{ - foreach (func_get_args() as $arg) - { - if (is_array($arg)) - foreach ($arg as $key => $value) { $arr[$key] = $value; $ret++; } - else - $arr[$arg] = ''; - } - - return $ret; -} - -// ------------ - -$food_colour = array(); - -// Individual calls, or ... -array_push_associative($food_colour, array('Banana' => 'Yellow')); -array_push_associative($food_colour, array('Apple' => 'Green')); -array_push_associative($food_colour, array('Lemon' => 'Yellow')); - -// ... one call, one array; physical order retained -// array_push_associative($food_colour, array('Banana' => 'Yellow', 'Apple' => 'Green', 'Lemon' => 'Yellow')); - -print_r($food_colour); - -echo "\nIn insertion order:\n"; -foreach($food_colour as $food => $colour) echo " {$food} => {$colour}\n"; - -$foods = array_keys($food_colour); - -echo "\nStill in insertion order:\n"; -foreach($foods as $food) echo " {$food} => {$food_colour[$food]}\n"; - -// @@PLEAC@@_5.7 -foreach(array_slice(preg_split('/\n/', `who`), 0, -1) as $entry) -{ - list($user, $tty) = preg_split('/\s/', $entry); - $ttys[$user][] = $tty; - - // Could instead do this: - // $user = array_slice(preg_split('/\s/', $entry), 0, 2); - // $ttys[$user[0]][] = $user[1]; -} - -ksort($ttys); - -// ------------ - -foreach($ttys as $user => $all_ttys) -{ - echo "{$user}: " . join(' ', $all_ttys) . "\n"; -} - -// ------------ - -foreach($ttys as $user => $all_ttys) -{ - echo "{$user}: " . join(' ', $all_ttys) . "\n"; - - foreach($all_ttys as $tty) - { - $stat = stat('/dev/$tty'); - $pwent = posix_getpwuid($stat['uid']); - $user = isset($pwent['name']) ? $pwent['name'] : 'Not available'; - echo "{$tty} owned by: {$user}\n"; - } -} - -// @@PLEAC@@_5.8 -// PHP offers the 'array_flip' function to perform the task of exchanging the keys / values -// of a hash i.e. invert or 'flip' a hash - -$reverse = array_flip($hash); - -// ---------------------------- - -$surname = array('Babe' => 'Ruth', 'Mickey' => 'Mantle'); -$first_name = array_flip($surname); - -echo "{$first_name['Mantle']}\n"; - -// ---------------------------- - -$argc == 2 || die("usage: {$argv[0]} food|colour\n"); - -$given = $argv[1]; - -$colour = array('Apple' => 'red', 'Banana' => 'yellow', - 'Lemon' => 'yellow', 'Carrot' => 'orange'); - -$food = array_flip($colour); - -if (isset($colour[$given])) - echo "{$given} is a food with colour: {$colour[$given]}\n"; - -if (isset($food[$given])) - echo "{$food[$given]} is a food with colour: {$given}\n"; - -// ---------------------------- - -$food_colour = array('Apple' => 'red', 'Banana' => 'yellow', - 'Lemon' => 'yellow', 'Carrot' => 'orange'); - -foreach($food_colour as $food => $colour) -{ - $foods_with_colour[$colour][] = $food; -} - -$colour = 'yellow'; -echo "foods with colour {$colour} were: " . join(' ', $foods_with_colour[$colour]) . "\n"; - -// @@PLEAC@@_5.9 -// PHP implements a swag of sorting functions, most designed to work with numerically-indexed -// arrays. For sorting hashes, the 'key' sorting functions are required: -// * 'ksort', 'krsort', 'uksort' - -// Ascending order -ksort($hash); - -// Descending order [i.e. reverse sort] -krsort($hash); - -// Comparator-based sort - -function comparator($left, $right) -{ - // Compare left key with right key - return $left > $right; -} - -uksort($hash, 'comparator'); - -// ---------------------------- - -$food_colour = array('Apple' => 'red', 'Banana' => 'yellow', - 'Lemon' => 'yellow', 'Carrot' => 'orange'); - -// ------------ - -ksort($food_colour); - -foreach($food_colour as $food => $colour) -{ - echo "{$food} is {$colour}\n"; -} - -// ------------ - -uksort($food_colour, create_function('$left, $right', 'return $left > $right;')); - -foreach($food_colour as $food => $colour) -{ - echo "{$food} is {$colour}\n"; -} - -// @@PLEAC@@_5.10 -// PHP offers the 'array_merge' function for this task [a related function, 'array_combine', -// may be used to create a hash from an array of keys, and one of values, respectively] - -// Merge two, or more, arrays -$merged = array_merge($a, $b, $c); - -// Create a hash from array of keys, and of values, respectively -$hash = array_combine($keys, $values); - -// ------------ - -// Can always merge arrays manually -foreach(array($h1, $h2, $h3) as $hash) -{ - foreach($hash as $key => $value) - { - // If same-key values differ, only latest retained - $merged[$key] = $value; - - // Do this to append values for that key - // $merged[$key][] = $value; - } -} - -// ---------------------------- - -$food_colour = array('Apple' => 'red', 'Banana' => 'yellow', - 'Lemon' => 'yellow', 'Carrot' => 'orange'); - -$drink_colour = array('Galliano' => 'yellow', 'Mai Tai' => 'blue'); - -// ------------ - -$ingested_colour = array_merge($food_colour, $drink_colour); - -// ------------ - -$substance_colour = array(); - -foreach(array($food_colour, $drink_colour) as $hash) -{ - foreach($hash as $substance => $colour) - { - if (array_key_exists($substance, $substance_colour)) - { - echo "Warning {$substance_colour[$substance]} seen twice. Using first definition.\n"; - continue; - } - $substance_colour[$substance] = $colour; - } -} - -// @@PLEAC@@_5.11 -// PHP offers a number of array-based 'set operation' functions: -// * union: array_merge -// * intersection: array_intersect and family -// * difference: array_diff and family -// which may be used for this type of task - -// Keys occurring in both hashes -$common = array_intersect_key($h1, $h2); - -// Keys occurring in the first hash [left side], but not in the second hash -$this_not_that = array_diff_key($h1, $h2); - -// ---------------------------- - -$food_colour = array('Apple' => 'red', 'Banana' => 'yellow', - 'Lemon' => 'yellow', 'Carrot' => 'orange'); - -$citrus_colour = array('Lemon' => 'yellow', 'Orange' => 'orange', 'Lime' => 'green'); - -$non_citrus = array_diff_key($food_colour, $citrus_colour); - -// @@PLEAC@@_5.12 -// PHP implements a special type known as a 'resource' that encompasses things like file handles, -// sockets, database connections, and many others. The 'resource' type is, essentially, a -// reference variable that is not readily serialisable. That is to say: -// * A 'resource' may be converted to a string representation via the 'var_export' function -// * That same string cannot be converted back into a 'resource' -// So, in terms of array handling, 'resource' types may be stored as array reference values, -// but cannot be used as keys. -// -// I suspect it is this type of problem that the Perl::Tie package helps resolve. However, since -// PHP doesn't, AFAIK, sport a similar facility, the examples in this section cannot be -// implemented using file handles as keys - -$filenames = array('/etc/termcap', '/vmlinux', '/bin/cat'); - -foreach($filenames as $filename) -{ - if (!($fh = fopen($filename, 'r'))) continue; - - // Cannot do this as required by the Perl code: - // $name[$fh] = $filename; - - // Ok - $name[$filename] = $fh; -} - -// Would traverse array via: -// -// foreach(array_keys($name) as $fh) -// ... -// or -// -// foreach($name as $fh => $filename) -// ... -// but since '$fh' cannot be a key, either of these will work: -// -// foreach($name as $filename => $fh) -// or -foreach(array_values($name) as $fh) -{ - fclose($fh); -} - -// @@PLEAC@@_5.13 -// PHP hashes are dynamic expanding and contracting as entries are added, and removed, -// respectively. Thus, there is no need to presize a hash, nor is there, AFAIK, any -// means of doing so except by the number of datums used when defining the hash - -// zero elements -$hash = array(); - -// ------------ - -// three elements -$hash = array('Apple' => 'red', 'Lemon' => 'yellow', 'Carrot' => 'orange'); - -// @@PLEAC@@_5.14 -foreach($array as $element) $count[$element] += 1; - -// @@PLEAC@@_5.15 -$father = array('Cain' => 'Adam', 'Abel' => 'Adam', 'Seth' => 'Adam', 'Enoch' => 'Cain', - 'Irad' => 'Enoch', 'Mehujael' => 'Irad', 'Methusael'=> 'Mehujael', - 'Lamech' => 'Methusael', 'Jabal' => 'Lamech', 'Jubal' => 'Lamech', - 'Tubalcain' => 'Lamech', 'Enos' => 'Seth'); - -// ------------ - -$name = trim(fgets(STDIN)); - -while (!feof(STDIN)) -{ - while (TRUE) - { - echo "$name\n"; - - // Can use either: - if (!isset($father[$name])) break; - $name = $father[$name]; - - // or: - // if (!key_exists($name, $father)) break; - // $name = $father[$name]; - - // or combine the two lines: - // if (!($name = $father[$name])) break; - } - - echo "\n"; - $name = trim(fgets(STDIN)); -} - -// ---------------------------- - -define(SEP, ' '); - -foreach($father as $child => $parent) -{ - if (!$children[$parent]) - $children[$parent] = $child; - else - $children[$parent] .= SEP . $child; -} - -$name = trim(fgets(STDIN)); - -while (!feof(STDIN)) -{ - echo $name . ' begat '; - - if (!$children[$name]) - echo "Nothing\n" - else - echo str_replace(SEP, ', ', $children[$name]) . "\n"; - - $name = trim(fgets(STDIN)); -} - -// ---------------------------- - -define(SEP, ' '); - -$files = array('/tmp/a', '/tmp/b', '/tmp/c'); - -foreach($files as $file) -{ - if (!is_file($file)) { echo "Skipping {$file}\n"; continue; } - if (!($fh = fopen($file, 'r'))) { echo "Skipping {$file}\n"; continue; } - - $line = fgets($fh); - - while (!feof($fh)) - { - if (preg_match('/^\s*#\s*include\s*<([^>]+)>/', $line, $matches)) - { - if (isset($includes[$matches[1]])) - $includes[$matches[1]] .= SEP . $file; - else - $includes[$matches[1]] = $file; - } - - $line = fgets($fh); - } - - fclose($fh); -} - -print_r($includes); - -// @@PLEAC@@_5.16 -// @@INCOMPLETE@@ -// @@INCOMPLETE@@ - -// @@PLEAC@@_9.0 -$entry = stat('/bin/vi'); -$entry = stat('/usr/bin'); -$entry = stat($argv[1]); - -// ------------ - -$entry = stat('/bin/vi'); - -$ctime = $entry['ctime']; -$size = $entry['size']; - -// ---------------------------- - -// For the simple task of determining whether a file contains, text', a simple -// function that searches for a newline could be implemented. Not exactly -// foolproof, but very simple, low overhead, no installation headaches ... -function containsText($file) -{ - $status = FALSE; - - if (($fp = fopen($file, 'r'))) - { - while (FALSE !== ($char = fgetc($fp))) - { - if ($char == "\n") { $status = TRUE; break; } - } - - fclose($fp); - } - - return $status; -} - -// PHP offers the [currently experimental] Fileinfo group of functions to -// determine file types based on their contents / 'magic numbers'. This -// is functionality similar to the *NIX, 'file' utility. Note that it must -// first be installed using the PEAR utility [see PHP documentation] -function isTextFile($file) -{ - // Note: untested code, but I believe this is how it is supposed to work - $finfo = finfo_open(FILEINFO_NONE); - $status = (finfo_file($finfo, $file) == 'ASCII text'); - finfo_close($finfo); - return $status; -} - -// Alternatively, use the *NIX utility, 'file', directly -function isTextFile($file) -{ - return exec(trim('file -bN ' . escapeshellarg($file))) == 'ASCII text'; -} - -// ---- - -containsText($argv[1]) || die("File {$argv[1]} doesn't have any text in it\n"); - -isTextFile($argv[1]) || die("File {$argv[1]} doesn't have any text in it\n"); - -// ---------------------------- - -$dirname = '/usr/bin/'; - -($dirhdl = opendir($dirname)) || die("Couldn't open {$dirname}\n"); - -while (($file = readdir($dirhdl)) !== FALSE) -{ - printf("Inside %s is something called: %s\n", $dirname, $file); -} - -closedir($dirhdl); - -// @@PLEAC@@_9.1 -$filename = 'example.txt'; - -// Get the file's current access and modification time, respectively -$fs = stat($filename); - -$readtime = $fs['atime']; -$writetime = $fs['mtime']; - -// Alter $writetime, and $readtime ... - -// Update file timestamp -touch($filename, $writetime, $readtime); - -// ---------------------------- - -$filename = 'example.txt'; - -// Get the file's current access and modification time, respectively -$fs = stat($filename); - -$atime = $fs['atime']; -$mtime = $fs['mtime']; - -// Dedicated functions also exist to retrieve this information: -// -// $atime = $fileatime($filename); -// $mtime = $filemtime($filename); -// - -// Perform date arithmetic. Traditional approach where arithmetic is performed -// directly with Epoch Seconds [i.e. the *NIX time stamp value] will work ... - -define('SECONDS_PER_DAY', 60 * 60 * 24); - -// Set file's access and modification times to 1 week ago -$atime -= 7 * SECONDS_PER_DAY; -$mtime -= 7 * SECONDS_PER_DAY; - -// ... but care must be taken to account for daylight saving. Therefore, the -// recommended approach is to use library functions to perform such tasks: - -$atime = strtotime('-7 days', $atime); -$mtime = strtotime('-7 days', $mtime); - -// Update file timestamp -touch($filename, $mtime, $atime); - -// Good idea to clear the cache after such updates have occurred so fresh -// values will be retrieved on next access -clearstatcache(); - -// ---------------------------- - -$argc == 2 || die("usage: {$argv[0]} filename\n"); - -$filename = $argv[1]; -$fs = stat($filename); - -$atime = $fs['atime']; -$mtime = $fs['mtime']; - -// Careful here: since interactive, use, 'system', not 'exec', to launch [latter -// does not work under *NIX - at least, not for me :)] -system(trim(getenv('EDITOR') . ' vi ' . escapeshellarg($filename)), $retcode); - -touch($filename, $mtime, $atime) || die("Error updating timestamp on file, {$filename}!\n"); - -// @@PLEAC@@_9.2 -// The 'unlink' function is used to delete regular files, whilst the 'rmdir' function -// does the same on non-empty directories. AFAIK, no recursive-deletion facility -// exists, and must be manually programmed - -$filename = '...'; - -@unlink($filename) || die("Can't delete, {$filename}!\n"); - -// ------------ - -$files = glob('...'); -$problem = FALSE; - -// Could simply use a foreach loop -foreach($files as $filename) { @unlink($filename) || $problem = TRUE; } - -// -// Alternatively, an applicative approach could be used, one closer in spirit to -// largely-functional languages like Scheme -// -// function is_all_deleted($deleted, $filename) { return @unlink($filename) && $deleted; } -// $problem = !array_reduce($files, 'is_all_deleted', TRUE); -// - -if ($problem) -{ - fwrite(STDERR, 'Could not delete all of:'); - foreach($files as $filename) { fwrite(STDERR, ' ' . $filename); } - fwrite(STDERR, "\n"); exit(1); -} - -// ------------ - -function rmAll($files) -{ - $count = 0; - - foreach($files as $filename) { @unlink($filename) && $count++; }; - - return $count; - -// An applicative alternative using 'create_function', PHP's rough equivalent of 'lambda' ... -// -// return array_reduce($files, -// create_function('$count, $filename', 'return @unlink($filename) && $count++;'), 0); -} - -// ---- - -$files = glob('...'); -$toBeDeleted = sizeof($files); -$count = rmAll($files); - -($count == $toBeDeleted) || die("Could only delete {$count} of {$toBeDeleted} files\n"); - -// @@PLEAC@@_9.3 -$oldfile = '/tmp/old'; $newfile = '/tmp/new'; - -copy($oldfile, $newfile) || die("Error copying file\n"); - -// ---------------------------- - -// All the following copy a file by copying its contents. Examples do so in a single -// operation, but it is also possible to copy arbitrary blocks, or, line-by-line in -// the case of 'text' files -$oldfile = '/tmp/old'; $newfile = '/tmp/new'; - -if (is_file($oldfile)) - file_put_contents($newfile, file_get_contents($oldfile)); -else - die("Problem copying file {$oldfile} to file {$newfile}\n"); - -// ------------ - -$oldfile = '/tmp/old'; $newfile = '/tmp/new'; - -fwrite(($nh = fopen($newfile, 'wb')), fread(($oh = fopen($oldfile, 'rb')), filesize($oldfile))); -fclose($oh); -fclose($nh); - -// ------------ - -// As above, but with some error checking / handling -$oldfile = '/tmp/old'; $newfile = '/tmp/new'; - -($oh = fopen($oldfile, 'rb')) || die("Problem opening input file {$oldfile}\n"); -($nh = fopen($newfile, 'wb')) || die("Problem opening output file {$newfile}\n"); - -if (($filesize = filesize($oldfile)) > 0) -{ - fwrite($nh, fread($oh, $filesize)) || die("Problem reading / writing file data\n"); -} - -fclose($oh); -fclose($nh); - -// ---------------------------- - -// Should there be platform-specfic problems copying 'very large' files, it is -// a simple matter to call a system command utility via, 'exec' - -// *NIX-specific example. Could check whether, 'exec', succeeded, but checking whether -// a file exists after the operation might be a better approach -$oldfile = '/tmp/old'; $newfile = '/tmp/new'; - -is_file($newfile) && unlink($newfile); - -exec(trim('cp --force ' . escapeshellarg($oldfile) . ' ' . escapeshellarg($newfile))); - -is_file($newfile) || die("Problem copying file {$oldfile} to file {$newfile}\n"); - -// For other operating systems just change: -// * filenames -// * command being 'exec'ed -// as the rest of the code is platform independant - -// @@PLEAC@@_9.4 -function makeDevInodePair($filename) -{ - if (!($fs = @stat($filename))) return FALSE; - return strval($fs['dev'] . $fs['ino']); -} - -// ------------ - -function do_my_thing($filename) -{ - // Using a global variable to mimic Perl example, but could easily have passed - // '$seen' as an argument - global $seen; - - $devino = makeDevInodePair($filename); - - // Process $filename if it has not previously been seen, else just increment - if (!isset($seen[$devino])) - { - // ... process $filename ... - - // Set initial count - $seen[$devino] = 1; - } - else - { - // Otherwise, just increment the count - $seen[$devino] += 1; - } -} - -// ---- - -// Simple example -$seen = array(); - -do_my_thing('/tmp/old'); -do_my_thing('/tmp/old'); -do_my_thing('/tmp/old'); -do_my_thing('/tmp/new'); - -foreach($seen as $devino => $count) -{ - echo "{$devino} -> {$count}\n"; -} - -// ------------ - -// A variation on the above avoiding use of global variables, and illustrating use of -// easily-implemented 'higher order' techniques - -// Helper function loosely modelled on, 'array_reduce', but using an array as -// 'accumulator', which is returned on completion -function array_update($arr, $lambda, $updarr) -{ - foreach($arr as $key) $lambda($updarr, $key); - return $updarr; -} - -function do_my_thing(&$seen, $filename) -{ - if (!array_key_exists(($devino = makeDevInodePair($filename)), $seen)) - { - // ... processing $filename ... - - // Update $seen - $seen[$devino] = 1; - } - else - { - // Update $seen - $seen[$devino] += 1; - } -} - -// ---- - -// Simple example -$files = array('/tmp/old', '/tmp/old', '/tmp/old', '/tmp/new'); - -// Could do this ... -$seen = array(); -array_update($files, 'do_my_thing', &$seen); - -// or this: -$seen = array_update($files, 'do_my_thing', array()); - -// or a 'lambda' could be used: -array_update($files, - create_function('$seen, $filename', '... code not shown ...'), - &$seen); - -foreach($seen as $devino => $count) -{ - echo "{$devino} -> {$count}\n"; -} - -// ---------------------------- - -$files = glob('/tmp/*'); - -define(SEP, ';'); -$seen = array(); - -foreach($files as $filename) -{ - if (!array_key_exists(($devino = makeDevInodePair($filename)), $seen)) - $seen[$devino] = $filename; - else - $seen[$devino] = $seen[$devino] . SEP . $filename; -} - -$devino = array_keys($seen); -sort($devino); - -foreach($devino as $key) -{ - echo $key . ':'; - foreach(split(SEP, $seen[$key]) as $filename) echo ' ' . $filename; - echo "\n"; -} - -// @@PLEAC@@_9.5 -// Conventional POSIX-like approach to directory traversal -$dirname = '/usr/bin/'; - -($dirhdl = opendir($dirname)) || die("Couldn't open {$dirname}\n"); - -while (($file = readdir($dirhdl)) !== FALSE) -{ - ; // ... do something with $dirname/$file - // ... -} - -closedir($dirhdl); - -// ------------ - -// Newer [post PHP 4], 'applicative' approach - an array of filenames is -// generated that may be processed via external loop ... - -$dirname = '/usr/bin/'; - -foreach(scandir($dirname) as $file) -{ - ; // ... do something with $dirname/$file - // ... -} - -// .. or, via callback application, perhaps after massaging by one of the -// 'array' family of functions [also uses, 'array_update', from earlier section] - -$newlist = array_update(array_reverse(scandir($dirname)), - create_function('$filelist, $file', ' ; '), - array()); - -// And don't forget that the old standby, 'glob', that returns an array of -// paths filtered using the Bourne Shell-based wildcards, '?' and '*', is -// also available - -foreach(glob($dirname . '*') as $path) -{ - ; // ... do something with $path - // ... -} - -// ---------------------------- - -// Uses, 'isTextFile', from an earlier section -$dirname = '/usr/bin/'; - -echo "Text files in {$dirname}:\n"; - -foreach(scandir($dirname) as $file) -{ - // Take care when constructing paths to ensure cross-platform operability - $path = $dirname . $file; - - if (is_file($path) && isTextFile($path)) echo $path . "\n"; -} - -// ---------------------------- - -function plain_files($dirname) -{ - ($dirlist = glob($dirname . '*')) || die("Couldn't glob {$dirname}\n"); - - // Pass function name directly if only a single function performs filter test - return array_filter($dirlist, 'is_file'); - - // Use, 'create_function', if a multi-function test is needed - // - // return array_filter($dirlist, create_function('$path', 'return is_file($path);')); - // -} - -// ------------ - -foreach(plain_files('/tmp/') as $path) -{ - echo $path . "\n"; -} - -// @@PLEAC@@_9.6 -$dirname = '/tmp/'; - -// Full paths -$pathlist = glob($dirname . '*.c'); - -// File names only - glob-based matching -$filelist = array_filter(scandir($dirname), - create_function('$file', 'return fnmatch("*.c", $file);')); - -// ---------------------------- - -$dirname = '/tmp/'; - -// File names only - regex-based matching [case-insensitive] -$filelist = array_filter(scandir($dirname), - create_function('$file', 'return eregi("\.[ch]$", $file);')); - -// ---------------------------- - -$dirname = '/tmp/'; - -// Directory names - all-digit names -$dirs = array_filter(glob($dirname . '*', GLOB_ONLYDIR), - create_function('$path', 'return ereg("^[0-9]+$", basename($path));')); - -// @@PLEAC@@_9.7 -// Recursive directory traversal function and helper: traverses a directory tree -// applying a function [and a variable number of accompanying arguments] to each -// file - -class Accumulator -{ - public $value; - public function __construct($start_value) { $this->value = $start_value; } -} - -// ------------ - -function process_directory_($op, $func_args) -{ - if (is_dir($func_args[0])) - { - $current = $func_args[0]; - foreach(scandir($current) as $entry) - { - if ($entry == '.' || $entry == '..') continue; - $func_args[0] = $current . '/' . $entry; - process_directory_($op, $func_args); - } - } - else - { - call_user_func_array($op, $func_args); - } -} - -function process_directory($op, $dir) -{ - if (!is_dir($dir)) return FALSE; - $func_args = array_slice(func_get_args(), 1); - process_directory_($op, $func_args); - return TRUE; -} - -// ---------------------------- - -$dirlist = array('/tmp/d1', '/tmp/d2', '/tmp/d3'); - -// Do something with each directory in the list -foreach($dirlist as $dir) -{ - ; - // Delete directory [if empty] -> rmdir($dir); - // Make it the 'current directory' -> chdir($dir); - // Get list of files it contains -> $filelist = scandir($dir); - // Get directory metadata -> $ds = stat($dir); -} - -// ------------ - -$dirlist = array('/tmp/d1', '/tmp/d2', '/tmp/d3'); - -function pf($path) -{ - // ... do something to the file or directory ... - printf("%s\n", $path); -} - -// For each directory in the list ... -foreach($dirlist as $dir) -{ - // Is this a valid directory ? - if (!is_dir($dir)) { printf("%s does not exist\n", $dir); continue; } - - // Ok, so get all the directory's entries - $filelist = scandir($dir); - - // An 'empty' directory will contain at least two entries: '..' and '.' - if (count($filelist) == 2) { printf("%s is empty\n", $dir); continue; } - - // For each file / directory in the directory ... - foreach($filelist as $file) - { - // Ignore '..' and '.' entries - if ($file == '.' || $file == '..') continue; - - // Apply function to process the file / directory - pf($dir . '/' . $file); - } -} - -// ---------------------------- - -function accum_filesize($file, $accum) -{ - is_file($file) && ($accum->value += filesize($file)); -} - -// ------------ - -// Verify arguments ... -$argc == 2 || die("usage: {$argv[0]} dir\n"); -$dir = $argv[1]; - -is_dir($dir) || die("{$dir} does not exist / not a directory\n"); - -// Collect data [use an object to accumulate results] -$dirsize = new Accumulator(0); -process_directory('accum_filesize', $dir, $dirsize); - -// Report results -printf("%s contains %d bytes\n", $dir, $dirsize->value); - -// ---------------------------- - -function biggest_file($file, $accum) -{ - if (is_file($file)) - { - $fs = filesize($file); - if ($accum->value[1] < $fs) { $accum->value[0] = $file; $accum->value[1] = $fs; } - } -} - -// ------------ - -// Verify arguments ... -$argc == 2 || die("usage: {$argv[0]} dir\n"); -$dir = $argv[1]; - -is_dir($dir) || die("{$dir} does not exist / not a directory\n"); - -// Collect data [use an object to accumulate results] -$biggest = new Accumulator(array('', 0)); -process_directory('biggest_file', $dir, $biggest); - -// Report results -printf("Biggest file is %s containing %d bytes\n", $biggest->value[0], $biggest->value[1]); - -// ---------------------------- - -function youngest_file($file, $accum) -{ - if (is_file($file)) - { - $fct = filectime($file); - if ($accum->value[1] > $fct) { $accum->value[0] = $file; $accum->value[1] = $fct; } - } -} - -// ------------ - -// Verify arguments ... -$argc == 2 || die("usage: {$argv[0]} dir\n"); -$dir = $argv[1]; - -is_dir($dir) || die("{$dir} does not exist / not a directory\n"); - -// Collect data [use an object to accumulate results] -$youngest = new Accumulator(array('', 2147483647)); -process_directory('youngest_file', $dir, $youngest); - -// Report results -printf("Youngest file is %s dating %s\n", $youngest->value[0], date(DATE_ATOM, $youngest->value[1])); - -// @@PLEAC@@_9.8 -// AFAICT, there is currently no library function that recursively removes a -// directory tree [i.e. a directory, it's subdirectories, and any other files] -// with a single call. Such a function needs to be custom built. PHP tools -// with which to do this: -// * 'unlink', 'rmdir', 'is_dir', and 'is_file' functions, will all take care -// of the file testing and deletion -// * Actual directory traversal requires obtaining directory / subdirectory -// lists, and here there is much choice available, though care must be taken -// as each has it's own quirks -// - 'opendir', 'readdir', 'closedir' -// - 'scandir' -// - 'glob' -// - SPL 'directory iterator' classes [newish / experimental - not shown here] -// -// The PHP documentation for 'rmdir' contains several examples, each illustrating -// one of each approach; the example shown here is loosely based on one of these -// examples - -// Recursor - recursively traverses directory tree -function rmtree_($dir) -{ - $dir = "$dir"; - - if ($dh = opendir($dir)) - { - while (FALSE !== ($item = readdir($dh))) - { - if ($item != '.' && $item != '..') - { - $subdir = $dir . '/' . "$item"; - - if (is_dir($subdir)) rmtree_($subdir); - else @unlink($subdir); - } - } - - closedir($dh); @rmdir($dir); - } -} - -// Launcher - performs validation then starts recursive routine -function rmtree($dir) -{ - if (is_dir($dir)) - { - (substr($dir, -1, 1) == '/') && ($dir = substr($dir, 0, -1)); - rmtree_($dir); return !is_dir($dir); - } - - return FALSE; -} - -// ------------ - -$argc == 2 || die("usage: rmtree dir\n"); - -rmtree($argv[1]) || die("Could not remove directory {$argv[1]}\n"); - -// @@PLEAC@@_9.9 -$filepairs = array('x.txt' => 'x2.txt', 'y.txt' => 'y.doc', 'zxc.txt' => 'cxz.txt'); - -foreach($filepairs as $oldfile => $newfile) -{ - @rename($oldfile, $newfile) || fwrite(STDERR, sprintf("Could not rename %s to %s\n", $oldfile, $newfile)); -} - -// ---------------------------- - -// Call a system command utility via, 'exec'. *NIX-specific example. Could check whether, -// 'exec', succeeded, but checking whether a renamed file exists after the operation might -// be a better approach - -$oldfile = '/tmp/old'; $newfile = '/tmp/new'; - -is_file($newfile) && unlink($newfile); - -exec(trim('mv --force ' . escapeshellarg($oldfile) . ' ' . escapeshellarg($newfile))); - -is_file($oldfile) || die("Problem renaming file {$oldfile} to file {$newfile}\n"); - -// For other operating systems just change: -// * filenames -// * command being 'exec'ed -// as the rest of the code is platform independant - -// ---------------------------- - -// A modified implementation of Larry's Filename Fixer. Rather than passing -// a single expression, a 'from' regexp is passed; each match in the file -// name(s) is changed to the value of 'to'. It otherwise behaves the same -// - -$argc > 2 || die("usage: rename from to [file ...]\n"); - -$from = $argv[1]; -$to = $argv[2]; - -if (count(($argv = array_slice($argv, 3))) < 1) - while (!feof(STDIN)) $argv[] = substr(fgets(STDIN), 0, -1); - -foreach($argv as $file) -{ - $was = $file; - $file = ereg_replace($from, $to, $file); - - if (strcmp($was, $file) != 0) - @rename($was, $file) || fwrite(STDERR, sprintf("Could not rename %s to %s\n", $was, $file)); -} - -// @@PLEAC@@_9.10 -$base = basename($path); -$dir = dirname($path); - -// PHP's equivalent to Perl's 'fileparse' -$pathinfo = pathinfo($path); - -$base = $pathinfo['basename']; -$dir = $pathinfo['dirname']; -$ext = $pathinfo['extension']; - -// ---------------------------- - -$path = '/usr/lib/libc.a'; - -printf("dir is %s, file is %s\n", dirname($path), basename($path)); - -// ------------ - -$path = '/usr/lib/libc.a'; - -$pathinfo = pathinfo($path); - -printf("dir is %s, name is %s, extension is %s\n", $pathinfo['dirname'], $pathinfo['basename'], $pathinfo['extension']); - -// ---------------------------- - -// Handle Mac example as a simple parse task. However, AFAIK, 'pathinfo' is cross-platform, -// so should handle file path format differences transparently -$path = 'Hard%20Drive:System%20Folder:README.txt'; - -$macp = array_combine(array('drive', 'folder', 'filename'), split("\:", str_replace('%20', ' ', $path))); -$macf = array_combine(array('name', 'extension'), split("\.", $macp['filename'])); - -printf("dir is %s, name is %s, extension is %s\n", ($macp['drive'] . ':' . $macp['folder']), $macf['name'], ('.' . $macf['extension'])); - -// ---------------------------- - -// Not really necessary since we have, 'pathinfo', but better matches Perl example -function file_extension($filename, $separator = '.') -{ - return end(split(("\\" . $separator), $filename)); -} - -// ---- - -echo file_extension('readme.txt') . "\n"; - -// @@PLEAC@@_9.11 -// @@INCOMPLETE@@ -// @@INCOMPLETE@@ - -// @@PLEAC@@_9.12 -// @@INCOMPLETE@@ -// @@INCOMPLETE@@ - -// @@PLEAC@@_10.0 -// Since defined at outermost scope, $greeted may be considered a global variable -$greeted = 0; - -// ------------ - -// Must use, 'global', keyword to inform functions that $greeted already exists as -// a global variable. If this is not done, a local variable of that name is implicitly -// defined -function howManyGreetings() -{ - global $greeted; - return $greeted; -} - -function hello() -{ - global $greeted; - $greeted++; - echo "high there!, this procedure has been called {$greeted} times\n"; -} - -// ------------ - -hello(); -$greetings = howManyGreetings(); -echo "bye there!, there have been {$greetings} greetings so far\n"; - -// @@PLEAC@@_10.1 -// Conventionally-defined function together with parameter list -function hypotenuse($side1, $side2) -{ - return sqrt(pow($side1, 2) + pow($side2, 2)); -} - -// ---- - -// Alternative is to define the function without parameter list, then use -// 'func_get_arg' to extract arguments -function hypotenuse() -{ - // Could check number of arguments passed with: 'func_num_args', which - // would be the approach used if dealing with variable number of arguments - $side1 = func_get_arg(0); $side2 = func_get_arg(1); - - return sqrt(pow($side1, 2) + pow($side2, 2)); -} - -// ------------ - -// 1. Conventional function call -$diag = hypotenuse(3, 4); - -// ------------ - -// 2. Function call using, 'call_user_func' library routines -$funcname = 'hypotenuse'; - -// a. Pass function name, and variable number of arguments -$diag = call_user_func($funcname, 3, 4); - -// b. Package arguments as array, pass together with function name -$args = array(3, 4); -$diag = call_user_func_array($funcname, $args); - -// ---------------------------- - -$nums = array(1.4, 3.5, 6.7); - -// ------------ - -// Pass-by-value -function int_all($arr) -{ - return array_map(create_function('$n', 'return (int) $n;'), $arr); -} - -// Pass-by-reference -function trunc_em(&$n) -{ - foreach ($n as &$value) $value = (int) $value; -} - -// ------------ - -// $nums untouched; $ints is new array -$ints = int_all($nums); - -// $nums updated -trunc_em($nums); - -// @@PLEAC@@_10.2 -// Strictly-speaking, PHP is neither lexically [no environment capture] nor -// dynamically [no variable shadowing] scoped. A script in which several -// functions have been defined has two, entirely separate, scopes: -// -// * A 'top-level' scope i.e. everything outside each function -// -// * A 'local scope' within each function; each function is a self-contained -// entity and cannot [via conventional means] access variables outside its -// local scope. Accessing a variable that has not been locally defined -// serves to define it i.e. accessing a variable assumed to be global -// sees a local variable of that name defined -// -// The way 'global' variables are provided is via a predefined array of -// variable names, $GLOBALS [it is one of a special set of variables known -// as 'superglobals'; such variables *are* accessable in all scopes]. Each -// entry in this array is a 'global' variable name, and may be freely -// accessed / updated. A more convenient means of accessing such variables -// is via the 'global' keyword: one or more variables within a function is -// declared 'global', and those names are then taken to refer to entries -// in the $GLOBALS array rather than seeing local variables of that name -// accessed or defined - -function some_func() -{ - // Variables declared within a function are local to that function - $variable = 'something'; -} - -// ---------------------------- - -// Top-level declared variables -$name = $argv[1]; $age = $argv[2]; - -$c = fetch_time(); - -$condition = 0; - -// ------------ - -function run_check() -{ - // The globally-declared variable, '$condition', is not accessable within - // the function unless it declared as 'global. Had this not been done then - // attempts to access, '$condition', would have seen a local variable - // of that name declared and updated. Same applies to other variables - global $condition, $name, $age, $c; - - $condition = 1; - // ... -} - -function check_x($x) -{ - $y = 'whatever'; - - // This function only has access to the parameter, '$x', and the locally - // declared variable, '$y'. - - // Whilst 'run_check' has access to several global variables, the current - // function does not. For it to access the global variable, '$condition', - // it must be declared 'global' - run_check(); - - global $condition; - - // 'run_check' will have updated, '$condition', and since it has been - // declared 'global' here, it is accessable - - if ($condition) - { - ; // ... - } -} - -// @@PLEAC@@_10.3 -// Local scopes are not created in the same way as in Perl [by simply enclosing -// within braces]: only via the creation of functions are local scopes created - -// Doesn't create a local scope; '$myvariable' is created at top-level -{ - $myvariable = 7; -} - -// '$myvariable' is accessable here -echo $myvariable . "\n"; - -// ------------ - -{ - $counter = 0; - - // Local scope created within function, but not within surrounding braces - // so: - // * '$counter' is actually a top-level variable, so globally accessable - // * 'next_counter' has no implict access to '$counter'; must be granted - // via 'global' keyword - - function next_counter() { global $counter; $counter++; } -} - -// ---------------------------- - -// PHP doesn't, AFAIK, offer an equivalent to Perl's BEGIN block. Similar -// behaviour may be obtained by defining a class, and including such code -// in its constructor - -class BEGIN -{ - private $myvariable; - - function __construct() - { - $this->myvariable = 5; - } - - function othersub() - { - echo $this->myvariable . "\n"; - } -} - -// ------------ - -$b = new BEGIN(); - -$b->othersub(); - -// ---------------------------- - -// PHP, like C, supports 'static' local variables, that is, those that upon -// first access are initialised, and thence retain their value between function -// calls. However, the 'counter' example is better implemented as a class - -class Counter -{ - private $counter; - - function __construct($counter_init) - { - $this->counter = $counter_init; - } - - function next_counter() { $this->counter++; return $this->counter; } - function prev_counter() { $this->counter; return $this->counter; } -} - -// ------------ - -$counter = new Counter(42); -echo $counter->next_counter() . "\n"; -echo $counter->next_counter() . "\n"; -echo $counter->prev_counter() . "\n"; - -// @@PLEAC@@_10.4 -// AFAICT there is no means of obtaining the name of the currently executing -// function, or, for that matter, perform any stack / activation record, -// inspection. It *is* possible to: -// -// * Obtain a list of the currently-defined functions ['get_defined_functions'] -// * Check whether a specific function exists ['function_exists'] -// * Use the 'Reflection API' -// -// So, to solve this problem would seem to require adopting a convention where -// a string representing the function name is passed as an argument, or a local -// variable [perhaps called, '$name'] is so set [contrived, and of limited use] - -function whoami() -{ - $name = 'whoami'; - echo "I am: {$name}\n"; -} - -// ------------ - -whoami(); - -// @@PLEAC@@_10.5 -// In PHP all items exist as 'memory references' [i.e. non-modifiable pointers], -// so when passing an item as a function argument, or returning an item from -// a function, it is this 'memory reference' that is passed, and *not* the -// contents of that item. Should several references to an item exist [e.g. if -// passed to a function then at least two such references would exist in -// different scopes] they would all be refering to the same copy of the item. -// However, if an attempt is made to alter the item is made, a copy is made -// and it is the copy that is altered, leaving the original intact. -// -// The PHP reference mechanism is used to selectively prevent this behaviour, -// and ensure that if a change is made to an item that no copy is made, and that -// it is the original item that is changed. Importantly, there is no efficiency -// gain from passing function parameters using references if the parameter item -// is not altered. - -// A copy of the item referred to by, '$arr', is made, and altered; original -// remains intact -function array_by_value($arr) -{ - $arr[0] = 7; - echo $arr[0] . "\n"; -} - -// No copy is made; original item referred to by, '$arr', is altered -function array_by_ref(&$arr) -{ - $arr[0] = 7; - echo $arr[0] . "\n"; -} - -// ------------ - -$arr = array(1, 2, 3); - -echo $arr[0] . "\n"; // output: 1 -array_by_value($arr); // output: 7 -echo $arr[0] . "\n"; // output: 1 - -$arr = array(1, 2, 3); - -echo $arr[0] . "\n"; // output: 1 -array_by_ref($arr); // output: 7 -echo $arr[0] . "\n"; // output: 7 - -// ---------------------------- - -// Since, 'add_vecpair', does not attempt to alter either, '$x' or '$y', it makes -// no difference whether they are 'passed by value', or 'passed by reference' -function add_vecpair($x, $y) -{ - $r = array(); - $length = count($x); - for($i = 0; $i < $length; $i++) $r[$i] = $x[$i] + $y[$i]; - return $r; -} - -// ... -count($arr1) == count($arr2) || die("usage: add_vecpair ARR1 ARR2\n"); - -// 'passed by value' -$arr3 = add_vecpair($arr1, $arr2); - -// 'passed by reference' [also possible to override default 'passed by value' -// if required] -$arr3 = add_vecpair(&$arr1, &$arr2); - -// @@PLEAC@@_10.6 -// PHP can be described as a dynamically typed language because variables serve -// as identifiers, and the same variable may refer to data of various types. -// As such, the set of arguments passed to a function may vary in type between -// calls, as can the type of return value. Where this is likely to occur type -// checking should be performed either / both within the function body, and -// when obtaining it's return value. As for Perl-style 'return context', I -// don't believe it is supported by PHP - -// Can return any type -function mysub() -{ - // ... - return 5; - // ... - return array(5); - // ... - return '5'; -} - -// Throw away return type [i.e. returns a 'void' type ?] -mysub(); - -// Check return type. Can do via: -// * gettype($var) -// * is_xxx e.g. is_array($var), is_muneric($var), ... -$ret = mysub(); - -if (is_numeric($ret)) -{ - ; // ... -} - -if (is_array($ret)) -{ - ; // ... -} - -if (is_string($ret)) -{ - ; // ... -} - -// @@PLEAC@@_10.7 -// PHP doesn't directly support named / keyword parameters, but these can be -// easily mimiced using a class of key / value pairs, and passing a variable -// number of arguments - -class KeyedValue -{ - public $key, $value; - public function __construct($key, $value) { $this->key = $key; $this->value = $value; } -} - -function the_func() -{ - foreach (func_get_args() as $arg) - { - printf("Key: %10s|Value:%10s\n", $arg->key, $arg->value); - } -} - -// ---- - -the_func(new KeyedValue('name', 'Bob'), - new KeyedValue('age', 36), - new KeyedValue('income', 51000)); - -// ---------------------------- - -// Alternatively, an associative array of key / value pairs may be constructed. -// With the aid of the 'extract' built-in function, the key part of this array -// may be intsntiated to a variable name, thus more closely approximating the -// behaviour of true named parameters - -function the_func($var_array) -{ - extract($var_array); - - if (isset($name)) printf("Name: %s\n", $name); - if (isset($age)) printf("Age: %s\n", $age); - if (isset($income)) printf("Income: %s\n", $income); -} - -// ---- - -the_func(array('name' => 'Bob', 'age' => 36, 'income' => 51000)); - -// ---------------------------- - -class RaceTime -{ - public $time, $dim; - public function __construct($time, $dim) { $this->time = $time; $this->dim = $dim; } -} - -function the_func($var_array) -{ - extract($var_array); - - if (isset($start_time)) printf("start: %d - %s\n", $start_time->time, $start_time->dim); - if (isset($finish_time)) printf("finish: %d - %s\n", $finish_time->time, $finish_time->dim); - if (isset($incr_time)) printf("incr: %d - %s\n", $incr_time->time, $incr_time->dim); -} - -// ---- - -the_func(array('start_time' => new RaceTime(20, 's'), - 'finish_time' => new RaceTime(5, 'm'), - 'incr_time' => new RaceTime(3, 'm'))); - -the_func(array('start_time' => new RaceTime(5, 'm'), - 'finish_time' => new RaceTime(30, 'm'))); - -the_func(array('start_time' => new RaceTime(30, 'm'))); - -// @@PLEAC@@_10.8 -// The 'list' keyword [looks like a function but is actually a special language -// construct] may be used to perform multiple assignments from a numerically -// indexed array of values, and offers the added bonus of being able to skip -// assignment of one, or more, of those values - -function func() { return array(3, 6, 9); } - -// ------------ - -list($a, $b, $c) = array(6, 7, 8); - -// Provided 'func' returns an numerically-indexed array, the following -// multiple assignment will work -list($a, $b, $c) = func(); - -// Any existing variables no longer wanted would need to be 'unset' -unset($b); - -// As above, but second element of return array discarded -list($a,,$c) = func(); - -// ---------------------------- - -// Care needed to ensure returned array is numerically-indexed -list($dev, $ino,,,$uid) = array_slice(array_values(stat($filename)), 0, 13); - -// @@PLEAC@@_10.9 -// Multiple return values are possible via packing a set of values within a -// numerically-indexed array and using 'list' to extract them - -function some_func() { return array(array(1, 2, 3), array('a' => 1, 'b' => 2)); } - -// ------------ - -list($arr, $hash) = some_func(); - -// ---------------------------- - -function some_func(&$arr, &$hash) { return array($arr, $hash); } - -// ------------ - -$arrin = array(1, 2, 3); $hashin = array('a' => 1, 'b' => 2); - -list($arr, $hash) = some_func($arrin, $hashin); - -// @@PLEAC@@_10.10 -// AFAICT, most of the PHP library functions are designed to return some required -// value on success, and FALSE on exit. Whilst it is possible to return NULL, or -// one of the recognised 'empty' values [e.g. '' or 0 or an empty array etc], -// FALSE actually seems to be the preferred means of indicating failure - -function a_func() { return FALSE; } - -a_func() || die("Function failed\n"); - -if (!a_func()) die("Function failed\n"); - -// @@PLEAC@@_10.11 -// Whether PHP is seen to support prototyping depends on the accepted -// definition of this term: -// -// * Prototyping along the lines used in Ada, Modula X, and even C / C++, -// in which a function's interface is declared separately from its -// implementation, is *not* supported -// -// * Prototyping in which, as part of the function definition, parameter -// information must be supplied. In PHP a function definition neither -// parameter, nor return type, information needs to be supplied, though -// it is usual to see a parameter list supplied [indicates the number, -// positional order, and optionally, whether a parameter is passed by -// reference; no type information is present]. In short, prototyping in -// PHP is optional, and limited - -function func_with_one_arg($arg1) -{ - ; // ... -} - -function func_with_two_arg($arg1, $arg2) -{ - ; // ... -} - -function func_with_three_arg($arg1, $arg2, $arg3) -{ - ; // ... -} - -// The following may be interpreted as meaning a function accepting no -// arguments: -function func_with_no_arg() -{ - ; // ... -} - -// whilst the following may mean a function taking zero or more arguments -function func_with_no_arg_information() -{ - ; // ... -} - -// @@PLEAC@@_10.12 -// Unlike in Perl, PHP's 'die' [actually an alias for 'exit'] doesn't throw -// an exception, but instead terminates the script, optionally either -// returning an integer value to the operating system, or printing a message. -// So, the following, does not exhibit the same behaviour as the Perl example - -die("some message\n"); - -// Instead, like so many modern languages, PHP implements exception handling -// via the 'catch' and 'throw' keywords. Furthermore, a C++ or Java programmer -// would find PHP's exception handling facility remarkably similar to those -// of their respective languages. A simple, canonical example follows: - -// Usual to derive new exception classes from the built-in, 'Exception', -// class -class MyException extends Exception -{ - // ... -} - -// ... - -try -{ - // ... - if ($some_problem_detected) throw new MyException('some message', $some_error_code); - // .. -} - -catch (MyException $e) -{ - ; // ... handle the problem ... -} - -// ---------------------------- - -class FullMoonException extends Exception -{ - // ... -} - -// ... - -try -{ - // ... - if ($some_problem_detected) throw new FullMoonException('...', $full_moon_error_code); - // .. -} - -catch (FullMoonException $e) -{ - // ... rethrow the exception - will propagate to higher level ... - throw $e; -} - -// @@PLEAC@@_10.13 -// Please refer to discussion about PHP scope in section two of this chapter. -// Briefly, PHP assumes a variable name within a function to be local unless -// it has been specifically declared with the, 'global', keyword, in which -// case it refers to a variable in the 'superglobal' array, '$GLOBALS'. Thus, -// inadvertant variable name shadowing cannot occur since it is it not possible -// to use the same name to refer to both a local and a global variable. If -// accessing a global variable care should be taken to not accidentally update -// it. The techniques used in this section are simply not required. - -// *** NOT TRANSLATED *** - -// @@PLEAC@@_10.14 -// In PHP once a function has been defined it remains defined. In other words, -// it cannot be undefined / deleted, nor can that particular function name be -// reused to reference another function body. Even the lambda-like functions -// created via the 'create_function' built-in, cannot be undefined [they exist -// until script termination, thus creating too many of these can actually -// exhaust memory !]. However, since the latter can be assigned to variables, -// the same variable name can be used to reference difference functions [and -// when this is done the reference to the previous function is lost (unless -// deliberately saved), though the function itself continues to exist]. -// -// If, however, all that is needed is a simple function aliasing facility, -// then just assign the function name to a variable, and execute using the -// variable name - -// Original function -function expand() { echo "expand\n"; } - -// Prove that function exists -echo (function_exists('expand') ? 'yes' : 'no') . "\n"; - -// Use a variable to alias it -$grow = 'expand'; - -// Call function via original name, and variable, respectively -expand(); - -$grow(); - -// Remove alias variable -unset($grow); - -// ---------------------------- - -function fred() { echo "fred\n"; } - -$barney = 'fred'; - -$barney(); - -unset($barney); - -fred(); - -// ------------ - -$fred = create_function('', 'echo "fred\n";'); - -$barney = $fred; - -$barney(); - -unset($barney); - -$fred(); - -// ---------------------------- - -function red($text) { return "<FONT COLOR='red'>$text</FONT>"; } - -echo red('careful here') . "\n"; - -// ------------ - -$colour = 'red'; - -$$colour = create_function('$text', 'global $colour; -return "<FONT COLOR=\'$colour\'>$text</FONT>";'); - -echo $$colour('careful here') . "\n"; - -unset($$colour); - -// ---- - -$colours = split(' ', 'red blue green yellow orange purple violet'); - -foreach ($colours as $colour) -{ - $$colour = create_function('$text', 'global $colour; - return "<FONT COLOR=\'$colour\'>$text</FONT>";'); -} - -foreach ($colours as $colour) { echo $$colour("Careful with this $colour, James") . "\n"; } - -foreach ($colours as $colour) { unset($$colour); } - -// @@PLEAC@@_10.15 -// PHP sports an AUTOLOAD facility that is quite easy to use, but, AFAICT, is geared -// towards the detection of unavailable classes rather than for individual functions. -// Here is a rudimentary example: - -function __autoload($classname) -{ - if (!file_exists($classname)) - { - // Class file does not exist, so handle situation; in this case, - // issue error message, and exit program - die("File for class: {$classname} not found - aborting\n"); - } - else - { - // Class file exists, so load it - require_once $classname; - } -} - -// ------------ - -// Attempt to instantiate object of undefined class -new UnknownClassObject(); - -// Execution continues here if class exists -// ... - -// ---------------------------- - -// It is also possible to perform [quite extensive] introspection on functions, -// variables etc, so it is possible to check whether a function exists before -// executing it, thus allowing a non-existent functions to be searched for and -// loaded from a source file, or perhaps dynamically defined. An example of what -// could be described as a custom autoload facility appears below. - -$colours = array('red', 'blue', 'green', 'yellow', 'orange', 'purple', 'violet'); - -foreach ($colours as $colour) -{ - $$colour = create_function('$text', 'global $colour; - return "<FONT COLOR=\'$colour\'>$text</FONT>";'); -} - -// Let's add a new colour to the list -array_push($colours, 'chartreuse'); - -foreach ($colours as $colour) -{ - // Checking whether function is defined - if (!function_exists($$colour)) - { - // Doesn't exist, so dynamically define it - $$colour = create_function('$text', 'global $colour; - return "<FONT COLOR=\'$colour\'>$text</FONT>";'); - - // Alternatively, if it exists in a source file, 'include' the file: - // include 'newcolours.php' - } - - echo $$colour("Careful with this $colour, James") . "\n"; -} - -foreach ($colours as $colour) unset($$colour); - -// @@PLEAC@@_10.16 -// *** Warning *** Whilst PHP *does* allow functions to be defined within other -// functions it needs to be clearly understood that these 'inner' functions: -// * Do not exist until the outer function is called a first time, at which time -// they then remain defined -// * Are global in scope, so are accessable outside the function by their name; -// the fact that they are nested within another function has, AFAICT, no bearing -// on name resolution -// * Do not form a closure: the inner function is merely 'parked' within the -// outer function, and has no implicit access to the outer function's variables -// or other inner functions - -function outer($arg) -{ - $x = $arg + 35; - function inner() { return $x * 19; } - - // *** wrong *** 'inner' returns 0 * 19, not ($arg + 35) * 19 - return $x + inner(); -} - -// ---------------------------- - -function outer($arg) -{ - $x = $arg + 35; - - // No implicit access to outer function scope; any required data must be - // explicity passed - function inner($x) { return $x * 19; } - - return $x + inner($x); -} - -// ------------ - -// Equivalent to previously-shown code -function inner($x) -{ - return $x * 19; -} - -function outer($arg) -{ - $x = $arg + 35; - return $x + inner($x); -} - -// @@PLEAC@@_10.17 -// @@INCOMPLETE@@ -// @@INCOMPLETE@@ - -// @@PLEAC@@_16.1 -// Run a command and return its results as a string. -$output_string = shell_exec('program args'); - -// Same as above, using backtick operator. -$output_string = `program args`; - -// Run a command and return its results as a list of strings, -// one per line. -$output_lines = array(); -exec('program args', $output_lines); - -// ----------------------------- - -// The only way to execute a program without using the shell is to -// use pcntl_exec(). However, there is no way to do redirection, so -// you can't capture its output. - -$pid = pcntl_fork(); -if ($pid == -1) { - die('cannot fork'); -} elseif ($pid) { - pcntl_waitpid($pid, $status); -} else { - // Note that pcntl_exec() automatically prepends the program name - // to the array of arguments; the program name cannot be spoofed. - pcntl_exec($program, array($arg1, $arg2)); -} - -// @@PLEAC@@_16.2 -// Run a simple command and retrieve its result code. -exec("vi $myfile", $output, $result_code); - -// ----------------------------- - -// Use the shell to perform redirection. -exec('cmd1 args | cmd2 | cmd3 >outfile'); -exec('cmd args <infile >outfile 2>errfile'); - -// ----------------------------- - -// Run a command, handling its result code or signal. -$pid = pcntl_fork(); -if ($pid == -1) { - die('cannot fork'); -} elseif ($pid) { - pcntl_waitpid($pid, $status); - if (pcntl_wifexited($status)) { - $status = pcntl_wexitstatus($status); - echo "program exited with status $status\n"; - } elseif (pcntl_wifsignaled($status)) { - $signal = pcntl_wtermsig($status); - echo "program killed by signal $signal\n"; - } elseif (pcntl_wifstopped($status)) { - $signal = pcntl_wstopsig($status); - echo "program stopped by signal $signal\n"; - } -} else { - pcntl_exec($program, $args); -} - -// ----------------------------- - -// Run a command while blocking interrupt signals. -$pid = pcntl_fork(); -if ($pid == -1) { - die('cannot fork'); -} elseif ($pid) { - // parent catches INT and berates user - declare(ticks = 1); - function handle_sigint($signal) { - echo "Tsk tsk, no process interruptus\n"; - } - pcntl_signal(SIGINT, 'handle_sigint'); - while (!pcntl_waitpid($pid, $status, WNOHANG)) {} -} else { - // child ignores INT and does its thing - pcntl_signal(SIGINT, SIG_IGN); - pcntl_exec('/bin/sleep', array('10')); -} - -// ----------------------------- - -// Since there is no direct access to execv() and friends, and -// pcntl_exec() won't let us supply an alternate program name -// in the argument list, there is no way to run a command with -// a different name in the process table. - -// @@PLEAC@@_16.3 -// Transfer control to the shell to run another program. -pcntl_exec('/bin/sh', array('-c', 'archive *.data')); -// Transfer control directly to another program. -pcntl_exec('/path/to/archive', array('accounting.data')); - -// @@PLEAC@@_16.4 -// Handle each line in the output of a process. -$readme = popen('program arguments', 'r'); -while (!feof($readme)) { - $line = fgets($readme); - if ($line === false) break; - // ... -} -pclose($readme); - -// ----------------------------- - -// Write to the input of a process. -$writeme = popen('program arguments', 'w'); -fwrite($writeme, 'data'); -pclose($writeme); - -// ----------------------------- - -// Wait for a process to complete. -$f = popen('sleep 1000000', 'r'); // child goes to sleep -pclose($f); // and parent goes to lala land - -// ----------------------------- - -$writeme = popen('program arguments', 'w'); -fwrite($writeme, "hello\n"); // program will get hello\n on STDIN -pclose($writeme); // program will get EOF on STDIN - -// ----------------------------- - -// Output buffering callback that sends output to the pager. -function ob_pager($output, $mode) { - static $pipe; - if ($mode & PHP_OUTPUT_HANDLER_START) { - $pager = getenv('PAGER'); - if (!$pager) $pager = '/usr/bin/less'; // XXX: might not exist - $pipe = popen($pager, 'w'); - } - fwrite($pipe, $output); - if ($mode & PHP_OUTPUT_HANDLER_END) { - pclose($pipe); - } -} - -// Redirect standard output to the pager. -ob_start('ob_pager'); - -// Do something useful that writes to standard output, then -// close the output buffer. -// ... -ob_end_flush(); - -// @@PLEAC@@_16.5 -// Output buffering: Only display a certain number of lines of output. -class Head { - function Head($lines=20) { - $this->lines = $lines; - } - - function filter($output, $mode) { - $result = array(); - $newline = ''; - if (strlen($output) > 0 && $output[strlen($output) - 1] == "\n") { - $newline = "\n"; - $output = substr($output, 0, -1); - } - foreach (explode("\n", $output) as $i => $line) { - if ($this->lines > 0) { - $this->lines--; - $result[] = $line; - } - } - return $result ? implode("\n", $result) . $newline : ''; - } -} - -// Output buffering: Prepend line numbers to each line of output. -class Number { - function Number() { - $this->line_number = 0; - } - - function filter($output, $mode) { - $result = array(); - $newline = ''; - if (strlen($output) > 0 && $output[strlen($output) - 1] == "\n") { - $newline = "\n"; - $output = substr($output, 0, -1); - } - foreach (explode("\n", $output) as $i => $line) { - $this->line_number++; - $result[] = $this->line_number . ': ' . $line; - } - return implode("\n", $result) . $newline; - } -} - -// Output buffering: Prepend "> " to each line of output. -class Quote { - function Quote() { - } - - function filter($output, $mode) { - $result = array(); - $newline = ''; - if (strlen($output) > 0 && $output[strlen($output) - 1] == "\n") { - $newline = "\n"; - $output = substr($output, 0, -1); - } - foreach (explode("\n", $output) as $i => $line) { - $result[] = "> $line"; - } - return implode("\n", $result) . $newline; - } -} - -// Use arrays as callbacks to register filter methods. -ob_start(array(new Head(100), 'filter')); -ob_start(array(new Number(), 'filter')); -ob_start(array(new Quote(), 'filter')); - -// Act like /bin/cat. -while (!feof(STDIN)) { - $line = fgets(STDIN); - if ($line === false) break; - echo $line; -} - -// Should match number of calls to ob_start(). -ob_end_flush(); -ob_end_flush(); -ob_end_flush(); - -// @@PLEAC@@_16.6 -// Process command-line arguments using fopen(). PHP supports URLs for -// filenames as long as the "allow_url_fopen" configuration option is set. -// -// Valid URL protocols include: -// - http://www.myserver.com/myfile.html -// - ftp://ftp.myserver.com/myfile.txt -// - compress.zlib://myfile.gz -// - php://stdin -// -// See http://www.php.net/manual/en/wrappers.php for details. -// -$filenames = array_slice($argv, 1); -if (!$filenames) $filenames = array('php://stdin'); -foreach ($filenames as $filename) { - $handle = @fopen($filename, 'r'); - if ($handle) { - while (!feof($handle)) { - $line = fgets($handle); - if ($line === false) break; - // ... - } - fclose($handle); - } else { - die("can't open $filename\n"); - } -} - -// @@PLEAC@@_16.7 -$output = `cmd 2>&1`; // with backticks -// or -$ph = popen('cmd 2>&1'); // with an open pipe -while (!feof($ph)) { $line = fgets($ph); } // plus a read -// ----------------------------- -$output = `cmd 2>/dev/null`; // with backticks -// or -$ph = popen('cmd 2>/dev/null'); // with an open pipe -while (!feof($ph)) { $line = fgets($ph); } // plus a read -// ----------------------------- -$output = `cmd 2>&1 1>/dev/null`; // with backticks -// or -$ph = popen('cmd 2>&1 1>/dev/null'); // with an open pipe -while (!feof($ph)) { $line = fgets($ph); } // plus a read -// ----------------------------- -$output = `cmd 3>&1 1>&2 2>&3 3>&-`; // with backticks -// or -$ph = popen('cmd 3>&1 1>&2 2>&3 3>&-|'); // with an open pipe -while (!feof($ph)) { $line = fgets($ph); } // plus a read -// ----------------------------- -exec('program args 1>/tmp/program.stdout 2>/tmp/program.stderr'); -// ----------------------------- -$output = `cmd 3>&1 1>&2 2>&3 3>&-`; -// ----------------------------- -$fd3 = $fd1; -$fd1 = $fd2; -$fd2 = $fd3; -$fd3 = null; -// ----------------------------- -exec('prog args 1>tmpfile 2>&1'); -exec('prog args 2>&1 1>tmpfile'); -// ----------------------------- -// exec('prog args 1>tmpfile 2>&1'); -$fd1 = "tmpfile"; // change stdout destination first -$fd2 = $fd1; // now point stderr there, too -// ----------------------------- -// exec('prog args 2>&1 1>tmpfile'); -$fd2 = $fd1; // stderr same destination as stdout -$fd1 = "tmpfile"; // but change stdout destination - -// @@PLEAC@@_16.8 -// Connect to input and output of a process. -$proc = proc_open($program, - array(0 => array('pipe', 'r'), - 1 => array('pipe', 'w')), - $pipes); -if (is_resource($proc)) { - fwrite($pipes[0], "here's your input\n"); - fclose($pipes[0]); - echo stream_get_contents($pipes[1]); - fclose($pipes[1]); - $result_code = proc_close($proc); - echo "$result_code\n"; -} - -// ----------------------------- - -$all = array(); -$outlines = array(); -$errlines = array(); -exec("( $cmd | sed -e 's/^/stdout: /' ) 2>&1", $all); -foreach ($all as $line) { - $pos = strpos($line, 'stdout: '); - if ($pos !== false && $pos == 0) { - $outlines[] = substr($line, 8); - } else { - $errlines[] = $line; - } -} -print("STDOUT:\n"); -print_r($outlines); -print("\n"); -print("STDERR:\n"); -print_r($errlines); -print("\n"); - -// @@PLEAC@@_16.9 -$proc = proc_open($cmd, - array(0 => array('pipe', 'r'), - 1 => array('pipe', 'w'), - 2 => array('pipe', 'w')), - $pipes); - -if (is_resource($proc)) { - // give end of file to kid, or feed him - fclose($pipes[0]); - - // read till EOF - $outlines = array(); - while (!feof($pipes[1])) { - $line = fgets($pipes[1]); - if ($line === false) break; - $outlines[] = rtrim($line); - } - - // XXX: block potential if massive - $errlines = array(); - while (!feof($pipes[2])) { - $line = fgets($pipes[2]); - if ($line === false) break; - $errlines[] = rtrim($line); - } - - fclose($pipes[1]); - fclose($pipes[2]); - proc_close($proc); - - print("STDOUT:\n"); - print_r($outlines); - print("\n"); - print("STDERR:\n"); - print_r($errlines); - print("\n"); -} - -// ----------------------------- - -// cmd3sel - control all three of kids in, out, and error. -$cmd = "grep vt33 /none/such - /etc/termcap"; -$proc = proc_open($cmd, - array(0 => array('pipe', 'r'), - 1 => array('pipe', 'w'), - 2 => array('pipe', 'w')), - $pipes); - -if (is_resource($proc)) { - fwrite($pipes[0], "This line has a vt33 lurking in it\n"); - fclose($pipes[0]); - - $readers = array($pipes[1], $pipes[2]); - while (stream_select($read=$readers, - $write=null, - $except=null, - 0, 200000) > 0) { - foreach ($read as $stream) { - $line = fgets($stream); - if ($line !== false) { - if ($stream === $pipes[1]) { - print "STDOUT: $line"; - } else { - print "STDERR: $line"; - } - } - if (feof($stream)) { - $readers = array_diff($readers, array($stream)); - } - } - } - - fclose($pipes[1]); - fclose($pipes[2]); - proc_close($proc); -} - -// @@PLEAC@@_16.10 -// PHP supports fork/exec/wait but not pipe. However, it does -// support socketpair, which can do everything pipes can as well -// as bidirectional communication. The original recipes have been -// modified here to use socketpair only. - -// ----------------------------- - -// pipe1 - use socketpair and fork so parent can send to child -$sockets = array(); -if (!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets)) { - die(socket_strerror(socket_last_error())); -} -list($reader, $writer) = $sockets; - -$pid = pcntl_fork(); -if ($pid == -1) { - die('cannot fork'); -} elseif ($pid) { - socket_close($reader); - $line = sprintf("Parent Pid %d is sending this\n", getmypid()); - if (!socket_write($writer, $line, strlen($line))) { - socket_close($writer); - die(socket_strerror(socket_last_error())); - } - socket_close($writer); - pcntl_waitpid($pid, $status); -} else { - socket_close($writer); - $line = socket_read($reader, 1024, PHP_NORMAL_READ); - printf("Child Pid %d just read this: `%s'\n", getmypid(), rtrim($line)); - socket_close($reader); // this will happen anyway - exit(0); -} - -// ----------------------------- - -// pipe2 - use socketpair and fork so child can send to parent -$sockets = array(); -if (!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets)) { - die(socket_strerror(socket_last_error())); -} -list($reader, $writer) = $sockets; - -$pid = pcntl_fork(); -if ($pid == -1) { - die('cannot fork'); -} elseif ($pid) { - socket_close($writer); - $line = socket_read($reader, 1024, PHP_NORMAL_READ); - printf("Parent Pid %d just read this: `%s'\n", getmypid(), rtrim($line)); - socket_close($reader); - pcntl_waitpid($pid, $status); -} else { - socket_close($reader); - $line = sprintf("Child Pid %d is sending this\n", getmypid()); - if (!socket_write($writer, $line, strlen($line))) { - socket_close($writer); - die(socket_strerror(socket_last_error())); - } - socket_close($writer); // this will happen anyway - exit(0); -} - -// ----------------------------- - -// pipe3 and pipe4 demonstrate the use of perl's "forking open" -// feature to reimplement pipe1 and pipe2. pipe5 uses two pipes -// to simulate socketpair. Since PHP supports socketpair but not -// pipe, and does not have a "forking open" feature, these -// examples are skipped here. - -// ----------------------------- - -// pipe6 - bidirectional communication using socketpair -$sockets = array(); -if (!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets)) { - die(socket_strerror(socket_last_error())); -} -list($child, $parent) = $sockets; - -$pid = pcntl_fork(); -if ($pid == -1) { - die('cannot fork'); -} elseif ($pid) { - socket_close($parent); - $line = sprintf("Parent Pid %d is sending this\n", getmypid()); - if (!socket_write($child, $line, strlen($line))) { - socket_close($child); - die(socket_strerror(socket_last_error())); - } - $line = socket_read($child, 1024, PHP_NORMAL_READ); - printf("Parent Pid %d just read this: `%s'\n", getmypid(), rtrim($line)); - socket_close($child); - pcntl_waitpid($pid, $status); -} else { - socket_close($child); - $line = socket_read($parent, 1024, PHP_NORMAL_READ); - printf("Child Pid %d just read this: `%s'\n", getmypid(), rtrim($line)); - $line = sprintf("Child Pid %d is sending this\n", getmypid()); - if (!socket_write($parent, $line, strlen($line))) { - socket_close($parent); - die(socket_strerror(socket_last_error())); - } - socket_close($parent); - exit(0); -} - -// @@PLEAC@@_16.11 -// ----------------------------- -// % mkfifo /path/to/named.pipe -// ----------------------------- - -$fifo = fopen('/path/to/named.pipe', 'r'); -if ($fifo !== false) { - while (!feof($fifo)) { - $line = fgets($fifo); - if ($line === false) break; - echo "Got: $line"; - } - fclose($fifo); -} else { - die('could not open fifo for read'); -} - -// ----------------------------- - -$fifo = fopen('/path/to/named.pipe', 'w'); -if ($fifo !== false) { - fwrite($fifo, "Smoke this.\n"); - fclose($fifo); -} else { - die('could not open fifo for write'); -} - -// ----------------------------- -// % mkfifo ~/.plan # isn't this everywhere yet? -// % mknod ~/.plan p # in case you don't have mkfifo -// ----------------------------- - -// dateplan - place current date and time in .plan file -while (true) { - $home = getenv('HOME'); - $fifo = fopen("$home/.plan", 'w'); - if ($fifo === false) { - die("Couldn't open $home/.plan for writing.\n"); - } - fwrite($fifo, - 'The current time is ' - . strftime('%a, %d %b %Y %H:%M:%S %z') - . "\n"); - fclose($fifo); - sleep(1); -} - -// ----------------------------- - -// fifolog - read and record log msgs from fifo - -$fifo = null; - -declare(ticks = 1); -function handle_alarm($signal) { - global $fifo; - if ($fifo) fclose($fifo); // move on to the next queued process -} -pcntl_signal(SIGALRM, 'handle_alarm'); - -while (true) { - pcntl_alarm(0); // turn off alarm for blocking open - $fifo = fopen('/tmp/log', 'r'); - if ($fifo === false) { - die("can't open /tmp/log"); - } - pcntl_alarm(1); // you have 1 second to log - - $service = fgets($fifo); - if ($service === false) continue; // interrupt or nothing logged - $service = rtrim($service); - - $message = fgets($fifo); - if ($message === false) continue; // interrupt or nothing logged - $message = rtrim($message); - - pcntl_alarm(0); // turn off alarms for message processing - - if ($service == 'http') { - // ignoring - } elseif ($service == 'login') { - // log to /var/log/login - $log = fopen('/var/log/login', 'a'); - if ($log !== false) { - fwrite($log, - strftime('%a, %d %b %Y %H:%M:%S %z') - . " $service $message\n"); - fclose($log); - } else { - trigger_error("Couldn't log $service $message to /var/log/login\n", - E_USER_WARNING); - } - } -} - -// @@PLEAC@@_16.12 -// sharetest - test shared variables across forks - -$SHM_KEY = ftok(__FILE__, chr(1)); -$handle = sem_get($SHM_KEY); -$buffer = shm_attach($handle, 1024); - -// The original recipe has an INT signal handler here. However, it -// causes erratic behavior with PHP, and PHP seems to do the right -// thing without it. - -for ($i = 0; $i < 10; $i++) { - $child = pcntl_fork(); - if ($child == -1) { - die('cannot fork'); - } elseif ($child) { - $kids[] = $child; // in case we care about their pids - } else { - squabble(); - exit(); - } -} - -while (true) { - print 'Buffer is ' . shm_get_var($buffer, 1) . "\n"; - sleep(1); -} -die('Not reached'); - -function squabble() { - global $handle; - global $buffer; - $i = 0; - $pid = getmypid(); - while (true) { - if (preg_match("/^$pid\\b/", shm_get_var($buffer, 1))) continue; - sem_acquire($handle); - $i++; - shm_put_var($buffer, 1, "$pid $i"); - sem_release($handle); - } -} - -// Buffer is 14357 1 -// Buffer is 14355 3 -// Buffer is 14355 4 -// Buffer is 14354 5 -// Buffer is 14353 6 -// Buffer is 14351 8 -// Buffer is 14351 9 -// Buffer is 14350 10 -// Buffer is 14348 11 -// Buffer is 14348 12 -// Buffer is 14357 10 -// Buffer is 14357 11 -// Buffer is 14355 13 -// ... - -// @@PLEAC@@_16.13 -// Available signal constants -% php -r 'print_r(get_defined_constants());' | grep '\[SIG' | grep -v _ - [SIGHUP] => 1 - [SIGINT] => 2 - [SIGQUIT] => 3 - [SIGILL] => 4 - [SIGTRAP] => 5 - [SIGABRT] => 6 - [SIGIOT] => 6 - [SIGBUS] => 7 - [SIGFPE] => 8 - [SIGKILL] => 9 - [SIGUSR1] => 10 - [SIGSEGV] => 11 - [SIGUSR2] => 12 - [SIGPIPE] => 13 - [SIGALRM] => 14 - [SIGTERM] => 15 - [SIGSTKFLT] => 16 - [SIGCLD] => 17 - [SIGCHLD] => 17 - [SIGCONT] => 18 - [SIGSTOP] => 19 - [SIGTSTP] => 20 - [SIGTTIN] => 21 - [SIGTTOU] => 22 - [SIGURG] => 23 - [SIGXCPU] => 24 - [SIGXFSZ] => 25 - [SIGVTALRM] => 26 - [SIGPROF] => 27 - [SIGWINCH] => 28 - [SIGPOLL] => 29 - [SIGIO] => 29 - [SIGPWR] => 30 - [SIGSYS] => 31 - [SIGBABY] => 31 - -// Predefined signal handler constants -% php -r 'print_r(get_defined_constants());' | grep '\[SIG' | grep _ - [SIG_IGN] => 1 - [SIG_DFL] => 0 - [SIG_ERR] => -1 - -// @@PLEAC@@_16.14 -// send pid a signal 9 -posix_kill($pid, 9); -// send whole job a signal 1 -posix_kill($pgrp, -1); -// send myself a SIGUSR1 -posix_kill(getmypid(), SIGUSR1); -// send a SIGHUP to processes in pids -foreach ($pids as $pid) posix_kill($pid, SIGHUP); - -// ----------------------------- - -// Use kill with pseudo-signal 0 to see if process is alive. -if (posix_kill($minion, 0)) { - echo "$minion is alive!\n"; -} else { - echo "$minion is deceased.\n"; -} - -// @@PLEAC@@_16.15 -// call got_sig_quit for every SIGQUIT -pcntl_signal(SIGQUIT, 'got_sig_quit'); -// call got_sig_pipe for every SIGPIPE -pcntl_signal(SIGPIPE, 'got_sig_pipe'); -// increment ouch for every SIGINT -function got_sig_int($signal) { global $ouch; $ouch++; } -pcntl_signal(SIGINT, 'got_sig_int'); -// ignore the signal INT -pcntl_signal(SIGINT, SIG_IGN); -// restore default STOP signal handling -pcntl_signal(SIGSTOP, SIG_DFL); - -// @@PLEAC@@_16.16 -// the signal handler -function ding($signal) { - fwrite(STDERR, "\x07Enter your name!\n"); -} - -// prompt for name, overriding SIGINT -function get_name() { - declare(ticks = 1); - pcntl_signal(SIGINT, 'ding'); - - echo "Kindly Stranger, please enter your name: "; - while (!@stream_select($read=array(STDIN), - $write=null, - $except=null, - 1)) { - // allow signals to be observed - } - $name = fgets(STDIN); - - // Since pcntl_signal() doesn't return the old signal handler, the - // best we can do here is set it back to the default behavior. - pcntl_signal(SIGINT, SIG_DFL); - - return $name; -} - -// @@PLEAC@@_16.17 -function got_int($signal) { - pcntl_signal(SIGINT, 'got_int'); // but not for SIGCHLD! - // ... -} -pcntl_signal(SIGINT, 'got_int'); - -// ----------------------------- - -declare(ticks = 1); -$interrupted = false; - -function got_int($signal) { - global $interrupted; - $interrupted = true; - // The third argument to pcntl_signal() determines if system calls - // should be restarted after a signal. It defaults to true. - pcntl_signal(SIGINT, 'got_int', false); // or SIG_IGN -} -pcntl_signal(SIGINT, 'got_int', false); - -// ... long-running code that you don't want to restart - -if ($interrupted) { - // deal with the signal -} - -// @@PLEAC@@_16.18 -// ignore signal INT -pcntl_signal(SIGINT, SIG_IGN); - -// install signal handler -declare(ticks = 1); -function tsktsk($signal) { - fwrite(STDERR, "\x07The long habit of living indisposeth us for dying."); - pcntl_signal(SIGINT, 'tsktsk'); -} -pcntl_signal(SIGINT, 'tsktsk'); - -// @@PLEAC@@_16.19 -pcntl_signal(SIGCHLD, SIG_IGN); - -// ----------------------------- - -declare(ticks = 1); -function reaper($signal) { - $pid = pcntl_waitpid(-1, $status, WNOHANG); - if ($pid > 0) { - // ... - reaper($signal); - } - // install *after* calling waitpid - pcntl_signal(SIGCHLD, 'reaper'); -} -pcntl_signal(SIGCHLD, 'reaper'); - -// ----------------------------- - -declare(ticks = 1); -function reaper($signal) { - $pid = pcntl_waitpid(-1, $status, WNOHANG); - if ($pid == -1) { - // No child waiting. Ignore it. - } else { - if (pcntl_wifexited($signal)) { - echo "Process $pid exited.\n"; - } else { - echo "False alarm on $pid\n"; - } - reaper($signal); - } - pcntl_signal(SIGCHLD, 'reaper'); -} -pcntl_signal(SIGCHLD, 'reaper'); - -// @@PLEAC@@_16.20 -// PHP does not support sigprocmask(). - -// @@PLEAC@@_16.21 -declare(ticks = 1); -$aborted = false; - -function handle_alarm($signal) { - global $aborted; - $aborted = true; -} -pcntl_signal(SIGALRM, 'handle_alarm'); - -pcntl_alarm(3600); -// long-time operations here -pcntl_alarm(0); -if ($aborted) { - // timed out - do what you will here -} |