PageRenderTime 77ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/text/src/test/resources/examples/php/pleac.in.php

https://github.com/rimolive/core
PHP | 5516 lines | 3126 code | 1103 blank | 1287 comment | 289 complexity | 2bff6f689da756c0f08fcd22fc63ef86 MD5 | raw file
Possible License(s): EPL-1.0, MPL-2.0-no-copyleft-exception
  1. # -*- php -*-
  2. # The examples are taken from the Perl Cookbook
  3. # By Tom Christiansen & Nathan Torkington
  4. # see http://www.oreilly.com/catalog/cookbook for more
  5. # @@PLEAC@@_NAME
  6. # @@SKIP@@ PHP
  7. # @@PLEAC@@_WEB
  8. # @@SKIP@@ http://php.net/
  9. # @@PLEAC@@_1.0
  10. #-----------------------------
  11. $string = '\n'; # two characters, \ and an n
  12. $string = 'Jon \'Maddog\' Orwant'; # literal single quotes
  13. $string = 'Jon "Maddog" Orwant'; # literal double quotes
  14. #-----------------------------
  15. $string = "\n"; # a "newline" character
  16. $string = "Jon \"Maddog\" Orwant"; # literal double quotes
  17. $string = "Jon 'Maddog' Orwant"; # literal single quotes
  18. #-----------------------------
  19. $a =
  20. "This is a multiline
  21. here document";
  22. $a = <<<EOF
  23. This is a multiline here document
  24. terminated by EOF on a line by itself
  25. EOF;
  26. #-----------------------------
  27. # @@PLEAC@@_1.1
  28. #-----------------------------
  29. $value = substr($string, $offset, $count);
  30. $value = substr($string, $offset);
  31. $string = substr_replace($string, $newstring, $offset, $count);
  32. $string = substr_replace($string, $newtail, $offset);
  33. #-----------------------------
  34. # get a 5-byte string, skip 3, then grab 2 8-byte strings, then the rest
  35. list($leading, $s1, $s2, $trailing) =
  36. array_values(unpack("A5a/x3/A8b/A8c/A*d", $data);
  37. # split at five byte boundaries
  38. preg_match_all ("/.{5}/", $data, $f, PREG_PATTERN_ORDER);
  39. $fivers = $f[0];
  40. # chop string into individual characters
  41. $chars = $string;
  42. #-----------------------------
  43. $string = "This is what you have";
  44. # +012345678901234567890 Indexing forwards (left to right)
  45. # 109876543210987654321- Indexing backwards (right to left)
  46. # note that 0 means 10 or 20, etc. above
  47. $first = substr($string, 0, 1); # "T"
  48. $start = substr($string, 5, 2); # "is"
  49. $rest = substr($string, 13); # "you have"
  50. $last = substr($string, -1); # "e"
  51. $end = substr($string, -4); # "have"
  52. $piece = substr($string, -8, 3); # "you"
  53. #-----------------------------
  54. $string = "This is what you have";
  55. print $string;
  56. #This is what you have
  57. $string = substr_replace($string, "wasn't", 5, 2); # change "is" to "wasn't"
  58. #This wasn't what you have
  59. $string = substr_replace($string, "ondrous", -12); # "This wasn't wondrous"
  60. #This wasn't wondrous
  61. $string = substr_replace($string, "", 0, 1); # delete first character
  62. #his wasn't wondrous
  63. $string = substr_replace($string, "", -10); # delete last 10 characters
  64. #his wasn'
  65. #-----------------------------
  66. if (preg_match("/pattern/", substr($string, -10)) {
  67. print "Pattern matches in last 10 characters\n";
  68. }
  69. # substitute "at" for "is", restricted to first five characters
  70. $string=(substr_replace(preg_replace("/is/", "at", substr($string,0,5)),0,5);
  71. #-----------------------------
  72. # exchange the first and last letters in a string
  73. $a = "make a hat";
  74. list($a[0], $a[strlen($a)-1]) = Array(substr($a,-1), substr($a,0,1));
  75. print $a;
  76. #-----------------------------
  77. # extract column with unpack
  78. $a = "To be or not to be";
  79. $b = unpack("x6/A6a", $a); # skip 6, grab 6
  80. print $b['a'];
  81. $b = unpack("x6/A2b/X5/A2c", $a); # forward 6, grab 2; backward 5, grab 2
  82. print $b['b']."\n".$b['c']."\n";
  83. #-----------------------------
  84. function cut2fmt() {
  85. $positions = func_get_args();
  86. $template = '';
  87. $lastpos = 1;
  88. foreach($positions as $place) {
  89. $template .= "A" . ($place - $lastpos) . " ";
  90. $lastpos = $place;
  91. }
  92. $template .= "A*";
  93. return $template;
  94. }
  95. $fmt = cut2fmt(8, 14, 20, 26, 30);
  96. print "$fmt\n";
  97. #A7 A6 A6 A6 A4 A*
  98. #-----------------------------
  99. # @@PLEAC@@_1.2
  100. #-----------------------------
  101. # use $b if $b is true, else $c
  102. $a = $b?$b:$c;
  103. # set $x to $y unless $x is already true
  104. $x || $x=$y;
  105. #-----------------------------
  106. # use $b if $b is defined, else $c
  107. $a = defined($b) ? $b : $c;
  108. #-----------------------------
  109. $foo = $bar || $foo = "DEFAULT VALUE";
  110. #-----------------------------
  111. $dir = array_shift($_SERVER['argv']) || $dir = "/tmp";
  112. #-----------------------------
  113. $dir = $_SERVER['argv'][0] || $dir = "/tmp";
  114. #-----------------------------
  115. $dir = defined($_SERVER['argv'][0]) ? array_shift($_SERVER['argv']) : "/tmp";
  116. #-----------------------------
  117. $dir = count($_SERVER['argv']) ? $_SERVER['argv'][0] : "/tmp";
  118. #-----------------------------
  119. $count[$shell?$shell:"/bin/sh"]++;
  120. #-----------------------------
  121. # find the user name on Unix systems
  122. $user = $_ENV['USER']
  123. || $user = $_ENV['LOGNAME']
  124. || $user = posix_getlogin()
  125. || $user = posix_getpwuid(posix_getuid())[0]
  126. || $user = "Unknown uid number $<";
  127. #-----------------------------
  128. $starting_point || $starting_point = "Greenwich";
  129. #-----------------------------
  130. count($a) || $a = $b; # copy only if empty
  131. $a = count($b) ? $b : $c; # assign @b if nonempty, else @c
  132. #-----------------------------
  133. # @@PLEAC@@_1.3
  134. #-----------------------------
  135. list($VAR1, $VAR2) = array($VAR2, $VAR1);
  136. #-----------------------------
  137. $temp = $a;
  138. $a = $b;
  139. $b = $temp;
  140. #-----------------------------
  141. $a = "alpha";
  142. $b = "omega";
  143. list($a, $b) = array($b, $a); # the first shall be last -- and versa vice
  144. #-----------------------------
  145. list($alpha, $beta, $production) = Array("January","March","August");
  146. # move beta to alpha,
  147. # move production to beta,
  148. # move alpha to production
  149. list($alpha, $beta, $production) = array($beta, $production, $alpha);
  150. #-----------------------------
  151. # @@PLEAC@@_1.4
  152. #-----------------------------
  153. $num = ord($char);
  154. $char = chr($num);
  155. #-----------------------------
  156. $char = sprintf("%c", $num); # slower than chr($num)
  157. printf("Number %d is character %c\n", $num, $num);
  158. #-----------------------------
  159. $ASCII = unpack("C*", $string);
  160. eval('$STRING = pack("C*", '.implode(',',$ASCII).');');
  161. #-----------------------------
  162. $ascii_value = ord("e"); # now 101
  163. $character = chr(101); # now "e"
  164. #-----------------------------
  165. printf("Number %d is character %c\n", 101, 101);
  166. #-----------------------------
  167. $ascii_character_numbers = unpack("C*", "sample");
  168. print explode(" ",$ascii_character_numbers)."\n";
  169. eval('$word = pack("C*", '.implode(',',$ascii_character_numbers).');');
  170. $word = pack("C*", 115, 97, 109, 112, 108, 101); # same
  171. print "$word\n";
  172. #-----------------------------
  173. $hal = "HAL";
  174. $ascii = unpack("C*", $hal);
  175. foreach ($ascii as $val) {
  176. $val++; # add one to each ASCII value
  177. }
  178. eval('$ibm = pack("C*", '.implode(',',$ascii).');');
  179. print "$ibm\n"; # prints "IBM"
  180. #-----------------------------
  181. # @@PLEAC@@_1.5
  182. #-----------------------------
  183. // using perl regexp
  184. $array = preg_split('//', $string ,-1, PREG_SPLIT_NO_EMPTY);
  185. // using PHP function: $array = str_split($string);
  186. // Cannot use unpack with a format of 'U*' in PHP.
  187. #-----------------------------
  188. for ($offset = 0; preg_match('/(.)/', $string, $matches, 0, $offset) > 0; $offset++) {
  189. // $matches[1] has charcter, ord($matches[1]) its number
  190. }
  191. #-----------------------------
  192. $seen = array();
  193. $string = "an apple a day";
  194. foreach (str_split($string) as $char) {
  195. $seen[$char] = 1;
  196. }
  197. $keys = array_keys($seen);
  198. sort($keys);
  199. print "unique chars are: " . implode('', $keys)) . "\n";
  200. unique chars are: adelnpy
  201. #-----------------------------
  202. $seen = array();
  203. $string = "an apple a day";
  204. for ($offset = 0; preg_match('/(.)/', $string, $matches, 0, $offset) > 0; $offset++) {
  205. $seen[$matches[1]] = 1;
  206. }
  207. $keys = array_keys($seen);
  208. sort($keys);
  209. print "unique chars are: " . implode('', $keys) . "\n";
  210. unique chars are: adelnpy
  211. #-----------------------------
  212. $sum = 0;
  213. foreach (unpack("C*", $string) as $byteval) {
  214. $sum += $byteval;
  215. }
  216. print "sum is $sum\n";
  217. // prints "1248" if $string was "an apple a day"
  218. #-----------------------------
  219. $sum = array_sum(unpack("C*", $string));
  220. #-----------------------------
  221. // sum - compute 16-bit checksum of all input files
  222. $handle = @fopen($argv[1], 'r');
  223. $checksum = 0;
  224. while (!feof($handle)) {
  225. $checksum += (array_sum(unpack("C*", fgets($handle))));
  226. }
  227. $checksum %= pow(2,16) - 1;
  228. print "$checksum\n";
  229. # @@INCLUDE@@ include/php/slowcat.php
  230. #-----------------------------
  231. # @@PLEAC@@_1.6
  232. #-----------------------------
  233. $revchars = strrev($string);
  234. #-----------------------------
  235. $revwords = implode(" ", array_reverse(explode(" ", $string)));
  236. #-----------------------------
  237. // reverse word order
  238. $string = 'Yoda said, "can you see this?"';
  239. $allwords = explode(" ", $string);
  240. $revwords = implode(" ", array_reverse($allwords));
  241. print $revwords . "\n";
  242. this?" see you "can said, Yoda
  243. #-----------------------------
  244. $revwords = implode(" ", array_reverse(explode(" ", $string)));
  245. #-----------------------------
  246. $revwords = implode(" ", array_reverse(preg_split("/(\s+)/", $string)));
  247. #-----------------------------
  248. $word = "reviver";
  249. $is_palindrome = ($word === strrev($word));
  250. #-----------------------------
  251. // quite a one-liner since "php" does not have a -n switch
  252. % php -r 'while (!feof(STDIN)) { $word = rtrim(fgets(STDIN)); if ($word == strrev($word) && strlen($word) > 5) print $word; }' < /usr/dict/words
  253. #-----------------------------
  254. # @@PLEAC@@_1.8
  255. #-----------------------------
  256. $text = preg_replace('/\$(\w+)/e', '$$1', $text);
  257. #-----------------------------
  258. list($rows, $cols) = Array(24, 80);
  259. $text = 'I am $rows high and $cols long';
  260. $text = preg_replace('/\$(\w+)/e', '$$1', $text);
  261. print $text;
  262. #-----------------------------
  263. $text = "I am 17 years old";
  264. $text = preg_replace('/(\d+)/e', '2*$1', $text);
  265. #-----------------------------
  266. # expand variables in $text, but put an error message in
  267. # if the variable isn't defined
  268. $text = preg_replace('/\$(\w+)/e','isset($$1)?$$1:\'[NO VARIABLE: $$1]\'', $text);
  269. #-----------------------------
  270. // As PHP arrays are used as hashes too, separation of section 4
  271. // and section 5 makes little sense.
  272. # @@PLEAC@@_1.9
  273. #-----------------------------
  274. $big = strtoupper($little);
  275. $little = strtolower($big);
  276. // PHP does not have the\L and\U string escapes.
  277. #-----------------------------
  278. $big = ucfirst($little);
  279. $little = strtolower(substr($big, 0, 1)) . substr($big, 1);
  280. #-----------------------------
  281. $beast = "dromedary";
  282. // capitalize various parts of $beast
  283. $capit = ucfirst($beast); // Dromedar
  284. // PHP does not have the\L and\U string escapes.
  285. $capall = strtoupper($beast); // DROMEDAR
  286. // PHP does not have the\L and\U string escapes.
  287. $caprest = strtolower(substr($beast, 0, 1)) . substr(strtoupper($beast), 1); // dROMEDAR
  288. // PHP does not have the\L and\U string escapes.
  289. #-----------------------------
  290. // titlecase each word's first character, lowercase the rest
  291. $text = "thIS is a loNG liNE";
  292. $text = ucwords(strtolower($text));
  293. print $text;
  294. This Is A Long Line
  295. #-----------------------------
  296. if (strtoupper($a) == strtoupper($b)) { // or strcasecmp($a, $b) == 0
  297. print "a and b are the same\n";
  298. }
  299. #-----------------------------
  300. # @@INCLUDE@@ include/php/randcap.php
  301. // % php randcap.php < genesis | head -9
  302. #-----------------------------
  303. # @@PLEAC@@_1.10
  304. #-----------------------------
  305. echo $var1 . func() . $var2; // scalar only
  306. #-----------------------------
  307. // PHP can only handle variable expression without operators
  308. $answer = "STRING ${[ VAR EXPR ]} MORE STRING";
  309. #-----------------------------
  310. $phrase = "I have " . ($n + 1) . " guanacos.";
  311. // PHP cannot handle the complex exression: ${\($n + 1)}
  312. #-----------------------------
  313. // Rest of Discussion is not applicable to PHP
  314. #-----------------------------
  315. // Interpolating functions not available in PHP
  316. #-----------------------------
  317. # @@PLEAC@@_1.11
  318. # @@INCOMPLETE@@
  319. # @@INCOMPLETE@@
  320. # @@PLEAC@@_1.12
  321. #-----------------------------
  322. $output = wordwrap($str, $width, $break, $cut);
  323. #-----------------------------
  324. # @@INCLUDE@@ include/php/wrapdemo.php
  325. #-----------------------------
  326. // merge multiple lines into one, then wrap one long line
  327. print wordwrap(str_replace("\n", " ", file_get_contents('php://stdin')));
  328. #-----------------------------
  329. while(!feof(STDIN)) {
  330. print wordwrap(str_replace("\n", " ", stream_get_line(STDIN, 0, "\n\n")));
  331. print "\n\n";
  332. }
  333. #-----------------------------
  334. # @@PLEAC@@_1.13
  335. #-----------------------------
  336. //backslash
  337. $var = preg_replace('/([CHARLIST])/', '\\\$1', $var);
  338. // double
  339. $var = preg_replace('/([CHARLIST])/', '$1$1', $var);
  340. #-----------------------------
  341. $var = preg_replace('/%/', '%%', $var);
  342. #-----------------------------
  343. $string = 'Mom said, "Don\'t do that."';
  344. $string = preg_replace('/([\'"])/', '\\\$1', $string);
  345. // in PHP you can also use the addslashes() function
  346. #-----------------------------
  347. $string = 'Mom said, "Don\'t do that."';
  348. $string = preg_replace('/([\'"])/', '$1$1', $string);
  349. #-----------------------------
  350. $string = preg_replace('/([^A-Z])/', '\\\$1', $string);
  351. #-----------------------------
  352. // PHP does not have the \Q and \E string metacharacters
  353. $string = "this is\\ a\\ test\\!";
  354. // PHP's quotemeta() function is not the same as perl's quotemeta() function
  355. $string = preg_replace('/(\W)/', '\\\$1', 'is a test!');
  356. #-----------------------------
  357. # @@PLEAC@@_1.14
  358. #-----------------------------
  359. $string = trim($string);
  360. #-----------------------------
  361. // print what's typed, but surrounded by > < symbols
  362. while (!feof(STDIN)) {
  363. print ">" . substr(fgets(STDIN), 0, -1) . "<\n";
  364. }
  365. #-----------------------------
  366. $string = preg_replace('/\s+/', ' ', $string); // finally, collapse middle
  367. #-----------------------------
  368. $string = trim($string);
  369. $string = preg_replace('/\s+/', ' ', $string);
  370. #-----------------------------
  371. // 1. trim leading and trailing white space
  372. // 2. collapse internal whitespace to single space each
  373. function sub_trim($string) {
  374. $string = trim($string);
  375. $string = preg_replace('/\s+/', ' ', $string);
  376. return $string;
  377. }
  378. #-----------------------------
  379. # @@PLEAC@@_1.15
  380. # @@INCOMPLETE@@
  381. # @@INCOMPLETE@@
  382. # @@PLEAC@@_1.16
  383. #-----------------------------
  384. $code = soundex($string);
  385. #-----------------------------
  386. $phoned_words = metaphone("Schwern");
  387. #-----------------------------
  388. // substitution function for getpwent():
  389. // returns an array of user entries,
  390. // each entry contains the username and the full name
  391. function getpwent() {
  392. $pwents = array();
  393. $handle = fopen("passwd", "r");
  394. while (!feof($handle)) {
  395. $line = fgets($handle);
  396. if (preg_match("/^#/", $line)) continue;
  397. $cols = explode(":", $line);
  398. $pwents[$cols[0]] = $cols[4];
  399. }
  400. return $pwents;
  401. }
  402. print "Lookup user: ";
  403. $user = rtrim(fgets(STDIN));
  404. if (empty($user)) exit;
  405. $name_code = soundex($user);
  406. $pwents = getpwent();
  407. foreach($pwents as $username => $fullname) {
  408. preg_match("/(\w+)[^,]*\b(\w+)/", $fullname, $matches);
  409. list(, $firstname, $lastname) = $matches;
  410. if ($name_code == soundex($username) ||
  411. $name_code == soundex($lastname) ||
  412. $name_code == soundex($firstname))
  413. {
  414. printf("%s: %s %s\n", $username, $firstname, $lastname);
  415. }
  416. }
  417. #-----------------------------
  418. # @@PLEAC@@_1.17
  419. #-----------------------------
  420. # @@INCLUDE@@ include/php/fixstyle.php
  421. #-----------------------------
  422. # @@INCLUDE@@ include/php/fixstyle2.php
  423. #-----------------------------
  424. // very fast, but whitespace collapse
  425. while (!feof($input)) {
  426. $i = 0;
  427. preg_match("/^(\s*)(.*)/", fgets($input), $matches); // emit leading whitespace
  428. fwrite($output, $matches[1]);
  429. foreach (preg_split("/(\s+)/", $matches[2]) as $token) { // preserve trailing whitespace
  430. fwrite($output, (array_key_exists($token, $config) ? $config[$token] : $token) . " ");
  431. }
  432. fwrite($output, "\n");
  433. }
  434. #-----------------------------
  435. // @@PLEAC@@_2.0
  436. // As is the case under so many other languages floating point use under PHP is fraught
  437. // with dangers. Although the basic techniques shown below are valid, please refer to
  438. // the official PHP documentation for known issues, bugs, and alternate approaches
  439. // @@PLEAC@@_2.1
  440. // Two basic approaches to numeric validation:
  441. // * Built-in functions like 'is_numeric', 'is_int', 'is_float' etc
  442. // * Regexes, as shown below
  443. $s = '12.345';
  444. preg_match('/\D/', $s) && die("has nondigits\n");
  445. preg_match('/^\d+$/', $s) || die("not a natural number\n");
  446. preg_match('/^-?\d+$/', $s) || die("not an integer\n");
  447. preg_match('/^[+-]?\d+$/', $s) || die("not an integer\n");
  448. preg_match('/^-?\d+\.?\d*$/', $s) || die("not a decimal\n");
  449. preg_match('/^-?(?:\d+(?:\.\d*)?|\.\d+)$/', $s) || die("not a decimal\n");
  450. preg_match('/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/', $s) || die("not a C float\n");
  451. // ----------------------------
  452. function getnum($s)
  453. {
  454. sscanf($s, "%D", $number); return isset($number) ? $number : 0;
  455. }
  456. echo getnum(123) . "\n"; // ok
  457. echo getnum(0xff) . "\n"; // ..
  458. echo getnum(044) . "\n"; // ..
  459. echo getnum('x') . "\n"; // fail
  460. // @@PLEAC@@_2.2
  461. // In PHP floating point comparisions are 'safe' [meaning the '==' comparison operator
  462. // can be used] as long as the value consists of 14 digits or less [total digits, either
  463. // side of the decimal point e.g. xxxxxxx.xxxxxxx, xxxxxxxxxxxxxx., .xxxxxxxxxxxxxx]. If
  464. // values with more digits must be compared, then:
  465. //
  466. // * Represent as strings, and take care to avoid any implicit conversions e.g. don't pass
  467. // a float as a float to a function and expect all digits to be retained - they won't be -
  468. // then use 'strcmp' to compare the strings
  469. //
  470. // * Avoid float use; perhaps use arbitrary precision arithmetic. In this case, the
  471. // 'bccomp' function is relevant
  472. // Will work as long as each floating point value is 14 digits or less
  473. if ($float_1 == $float_2)
  474. {
  475. ; // ...
  476. }
  477. // Compare as strings
  478. $cmp = strcmp('123456789.123456789123456789', '123456789.123456789123456788');
  479. // Use 'bccomp'
  480. $precision = 5; // Number of significant comparison digits after decimal point
  481. if (bccomp('1.111117', '1.111116', $precision))
  482. {
  483. ; // ...
  484. }
  485. $precision = 6;
  486. if (bccomp('1.111117', '1.111116', $precision))
  487. {
  488. ; // ...
  489. }
  490. // ----------------------------
  491. $wage = 536;
  492. $week = $wage * 40;
  493. printf("One week's wage is: $%.2f\n", $week / 100);
  494. // @@PLEAC@@_2.3
  495. // Preferred approach
  496. $rounded = round($unrounded, $precision);
  497. // Possible alternate approach
  498. $format = '%[width].[prec]f';
  499. $rounded = sprintf($format, $unrounded);
  500. // ------------
  501. $a = 0.255; $b = round($a, 2);
  502. echo "Unrounded: {$a}\nRounded: {$b}\n";
  503. $a = 0.255; $b = sprintf('%.2f', $a);
  504. echo "Unrounded: {$a}\nRounded: {$b}\n";
  505. $a = 0.255;
  506. printf("Unrounded: %.f\nRounded: %.2f\n", $a, $a);
  507. // ----------------------------
  508. echo "number\tint\tfloor\tceil\n";
  509. foreach(array(3.3, 3.5, 3.7, -3.3) as $number)
  510. {
  511. printf("%.1f\t%.1f\t%.1f\t%.1f\n", $number, (int) $number, floor($number), ceil($number));
  512. }
  513. // @@PLEAC@@_2.4
  514. // PHP offers the 'bindec' and 'decbin' functions to converting between binary and decimal
  515. $num = bindec('0110110');
  516. $binstr = decbin(54);
  517. // @@PLEAC@@_2.5
  518. foreach (range($X, $Y) as $i)
  519. {
  520. ; // ...
  521. }
  522. foreach (range($X, $Y, 7) as $i)
  523. {
  524. ; // ...
  525. }
  526. for ($i = $X; $i <= $Y; $i++)
  527. {
  528. ; // ...
  529. }
  530. for ($i = $X; $i <= $Y; $i += 7)
  531. {
  532. ; // ...
  533. }
  534. // ----------------------------
  535. echo 'Infancy is:'; foreach(range(0, 2) as $i) echo " {$i}\n";
  536. echo 'Toddling is:'; foreach(range(3, 4) as $i) echo " {$i}\n";
  537. echo 'Childhood is:'; foreach(range(5, 12) as $i) echo " {$i}\n";
  538. // @@PLEAC@@_2.6
  539. // PHP offers no native support for Roman Numerals. However, a 'Numbers_Roman' class
  540. // is available for download from PEAR: [http://pear.php.net/package/Numbers_Roman].
  541. // Note the following 'include' directives are required:
  542. //
  543. // include_once('Numbers/Roman.php');
  544. $roman = Numbers_Roman::toNumeral($arabic);
  545. $arabic = Numbers_Roman::toNumber($roman);
  546. // ----------------------------
  547. $roman_fifteen = Numbers_Roman::toNumeral(15);
  548. $arabic_fifteen = Numbers_Roman::toNumber($roman_fifteen);
  549. printf("Roman for fifteen is: %s\n", $roman_fifteen);
  550. printf("Arabic for fifteen is: %d\n", $arabic_fifteen);
  551. // @@PLEAC@@_2.7
  552. // Techniques used here simply mirror Perl examples, and are not an endorsement
  553. // of any particular RNG technique
  554. // In PHP do this ...
  555. $random = rand($lowerbound, $upperbound);
  556. $random = rand($x, $y);
  557. // ----------------------------
  558. function make_password($chars, $reqlen)
  559. {
  560. $len = strlen($chars);
  561. for ($i = 0; $i < $reqlen; $i++) $password .= substr($chars, rand(0, $len), 1);
  562. return $password;
  563. }
  564. $chars = 'ABCDEfghijKLMNOpqrstUVWXYz'; $reqlen = 8;
  565. $password = make_password($chars, $reqlen);
  566. // @@PLEAC@@_2.8
  567. // PHP sports a large number of C Standard Library routines including the 'srand'
  568. // function, used to re-seed the RNG used with calls to the 'rand' function. Thus,
  569. // as per Perl example:
  570. while (TRUE)
  571. {
  572. $seed = (int) fgets(STDIN);
  573. if (!empty($seed)) break;
  574. }
  575. srand($seed);
  576. // @@PLEAC@@_2.9
  577. // The above is considered - for many reasons - a poor way of seeding the RNG. PHP
  578. // also offers alternate versions of the functions, 'mt_srand' and 'mt_rand',
  579. // which are described as faster, and more 'random', though key to obtaining a
  580. // more 'random' distribution of generated numbers seems to be through using
  581. // a combination of a previously saved random value in combination with an
  582. // unrepeatable value [like the current time in microseconds] that is multiplied
  583. // by a large prime number, or perhaps as part of a hash [examples available in
  584. // PHP documentation for 'srand' and 'mt_srand']
  585. mt_srand($saved_random_value + microtime() * 1000003);
  586. // or
  587. mt_srand(($saved_random_value + hexdec(substr(md5(microtime()), -8))) & 0x7fffffff);
  588. // Use of 'mt_rand' together with an appropriate seeding approach should help better
  589. // approximate the generation of a 'truly random value'
  590. $truly_random_value = mt_rand();
  591. // @@PLEAC@@_2.10
  592. function random() { return (float) rand() / (float) getrandmax(); }
  593. function gaussian_rand()
  594. {
  595. $u1 = 0.0; $u2 = 0.0; $g1 = 0.0; $g2 = 0.0; $w = 0.0;
  596. do
  597. {
  598. $u1 = 2.0 * random() - 1.0; $u2 = 2.0 * random() - 1.0;
  599. $w = $u1 * $u1 + $u2 * $u2;
  600. } while ($w > 1.0);
  601. $w = sqrt((-2.0 * log($w)) / $w); $g2 = $u1 * $w; $g1 = $u2 * $w;
  602. return $g1;
  603. }
  604. // ------------
  605. $mean = 25.0; $sdev = 2.0;
  606. $salary = gaussian_rand() * $mean + $sdev;
  607. printf("You have been hired at: %.2f\n", $salary);
  608. // @@PLEAC@@_2.11
  609. // 'deg2rad' and 'rad2deg' are actually PHP built-ins, but here is how you might implement
  610. / them if needed
  611. function deg2rad_($deg) { return ($deg / 180.0) * M_PI; }
  612. function rad2deg_($rad) { return ($rad / M_PI) * 180.0; }
  613. // ------------
  614. printf("%f\n", deg2rad_(180.0));
  615. printf("%f\n", deg2rad(180.0));
  616. // ----------------------------
  617. function degree_sin($deg) { return sin(deg2rad($deg)); }
  618. // ------------
  619. $rad = deg2rad(380.0);
  620. printf("%f\n", sin($rad));
  621. printf("%f\n", degree_sin(380.0));
  622. // @@PLEAC@@_2.12
  623. function my_tan($theta) { return sin($theta) / cos($theta); }
  624. // ------------
  625. $theta = 3.7;
  626. printf("%f\n", my_tan($theta));
  627. printf("%f\n", tan($theta));
  628. // @@PLEAC@@_2.13
  629. $value = 100.0;
  630. $log_e = log($value);
  631. $log_10 = log10($value);
  632. // ----------------------------
  633. function log_base($base, $value) { return log($value) / log($base); }
  634. // ------------
  635. $answer = log_base(10.0, 10000.0);
  636. printf("log(10, 10,000) = %f\n", $answer);
  637. // @@PLEAC@@_2.14
  638. // PHP offers no native support for matrices. However, a 'Math_Matrix' class
  639. // is available for download from PEAR: [http://pear.php.net/package/Math_Matrix].
  640. // Note the following 'include' directives are required:
  641. //
  642. // include_once('Math/Matrix.php');
  643. $a = new Math_Matrix(array(array(3, 2, 3), array(5, 9, 8)));
  644. $b = new Math_Matrix(array(array(4, 7), array(9, 3), array(8, 1)));
  645. echo $a->toString() . "\n";
  646. echo $b->toString() . "\n";
  647. // NOTE: When I installed this package I had to rename the 'clone' method else
  648. // it would not load, so I chose to rename it to 'clone_', and this usage is
  649. // shown below. This bug may well be fixed by the time you obtain this package
  650. $c = $a->clone_();
  651. $c->multiply($b);
  652. echo $c->toString() . "\n";
  653. // @@PLEAC@@_2.15
  654. // PHP offers no native support for complex numbers. However, a 'Math_Complex' class
  655. // is available for download from PEAR: [http://pear.php.net/package/Math_Complex].
  656. // Note the following 'include' directives are required:
  657. //
  658. // include_once('Math/Complex.php');
  659. // include_once('Math/TrigOp.php');
  660. // include_once('Math/ComplexOp.php');
  661. $a = new Math_Complex(3, 5);
  662. $b = new Math_Complex(2, -2);
  663. $c = Math_ComplexOp::mult($a, $b);
  664. echo $c->toString() . "\n";
  665. // ----------------------------
  666. $d = new Math_Complex(3, 4);
  667. $r = Math_ComplexOp::sqrt($d);
  668. echo $r->toString() . "\n";
  669. // @@PLEAC@@_2.16
  670. // Like C, PHP supports decimal-alternate notations. Thus, for example, the integer
  671. // value, 867, is expressable in literal form as:
  672. //
  673. // Hexadecimal -> 0x363
  674. // Octal -> 01543
  675. //
  676. // For effecting such conversions using strings there is 'sprintf' and 'sscanf'.
  677. $dec = 867;
  678. $hex = sprintf('%x', $dec);
  679. $oct = sprintf('%o', $dec);
  680. // ------------
  681. $dec = 0;
  682. $hex = '363';
  683. sscanf($hex, '%x', $dec);
  684. // ------------
  685. $dec = 0;
  686. $oct = '1543';
  687. sscanf($oct, '%o', $dec);
  688. // ----------------------------
  689. $number = 0;
  690. printf('Gimme a number in decimal, octal, or hex: ');
  691. sscanf(fgets(STDIN), '%D', $number);
  692. printf("%d %x %o\n", $number, $number, $number);
  693. // @@PLEAC@@_2.17
  694. // PHP offers the 'number_format' built-in function to, among many other format tasks,
  695. // commify numbers. Perl-compatible [as well as extended] regexes are also available
  696. function commify_series($s) { return number_format($s, 0, '', ','); }
  697. // ------------
  698. $hits = 3456789;
  699. printf("Your website received %s accesses last month\n", commify_series($hits));
  700. // ----------------------------
  701. function commify($s)
  702. {
  703. return strrev(preg_replace('/(\d\d\d)(?=\d)(?!\d*\.)/', '${1},', strrev($s)));
  704. }
  705. // ------------
  706. $hits = 3456789;
  707. echo commify(sprintf("Your website received %d accesses last month\n", $hits));
  708. // @@PLEAC@@_2.18
  709. function pluralise($value, $root, $singular='' , $plural='s')
  710. {
  711. return $root . (($value > 1) ? $plural : $singular);
  712. }
  713. // ------------
  714. $duration = 1;
  715. printf("It took %d %s\n", $duration, pluralise($duration, 'hour'));
  716. printf("%d %s %s enough.\n", $duration, pluralise($duration, 'hour'),
  717. pluralise($duration, '', 'is', 'are'));
  718. $duration = 5;
  719. printf("It took %d %s\n", $duration, pluralise($duration, 'hour'));
  720. printf("%d %s %s enough.\n", $duration, pluralise($duration, 'hour'),
  721. pluralise($duration, '', 'is', 'are'));
  722. // ----------------------------
  723. function plural($singular)
  724. {
  725. $s2p = array('/ss$/' => 'sses', '/([psc]h)$/' => '${1}es', '/z$/' => 'zes',
  726. '/ff$/' => 'ffs', '/f$/' => 'ves', '/ey$/' => 'eys',
  727. '/y$/' => 'ies', '/ix$/' => 'ices', '/([sx])$/' => '$1es',
  728. '$' => 's');
  729. foreach($s2p as $s => $p)
  730. {
  731. if (preg_match($s, $singular)) return preg_replace($s, $p, $singular);
  732. }
  733. }
  734. // ------------
  735. foreach(array('mess', 'index', 'leaf', 'puppy') as $word)
  736. {
  737. printf("%6s -> %s\n", $word, plural($word));
  738. }
  739. // @@PLEAC@@_2.19
  740. // @@INCOMPLETE@@
  741. // @@INCOMPLETE@@
  742. // @@PLEAC@@_3.0
  743. // PHP's date / time suport is quite extensive, and appears grouped into three areas of
  744. // functionality:
  745. //
  746. // * UNIX / C Library [libc]-based routines, which include [among others]:
  747. // - localtime, gmtime
  748. // - strftime, strptime, mktime
  749. // - time, getdate, gettimeofday,
  750. //
  751. // * PHP 'native' functions, those date / time routines released in earlier versions,
  752. // and which otherwise provide 'convenience' functionality; these include:
  753. // - date
  754. // - strtotime
  755. //
  756. // * 'DateTime' class-based. This facility appears [according to the PHP documentation]
  757. // to be extremely new / experimental, so whilst usage examples will be provided, they
  758. // should not be taken to be 'official' examples, and obviously, subject to change.
  759. // My own impression is that this facility is currently only partially implemented,
  760. // so there is limited use for these functions. The functions included in this group
  761. // are some of the 'date_'-prefixed functions; they are, however, not used standalone,
  762. // but as methods in conjunction with an object. Typical usage:
  763. //
  764. // $today = new DateTime(); // actually calls: date_create($today, ...);
  765. // echo $today->format('U') . "\n"; // actually calls: date_format($today, ...);
  766. //
  767. // Also worth mentioning is the PEAR [PHP Extension and Repository] package, 'Calendar',
  768. // which offers a rich set of date / time manipulation facilities. However, since it is
  769. // not currently shipped with PHP, no examples appear
  770. // Helper functions for performing date arithmetic
  771. function dateOffset()
  772. {
  773. static $tbl = array('sec' => 1, 'min' => 60, 'hou' => 3600, 'day' => 86400, 'wee' => 604800);
  774. $delta = 0;
  775. foreach (func_get_args() as $arg)
  776. {
  777. $kv = explode('=', $arg);
  778. $delta += $kv[1] * $tbl[strtolower(substr($kv[0], 0, 3))];
  779. }
  780. return $delta;
  781. }
  782. function dateInterval($intvltype, $timevalue)
  783. {
  784. static $tbl = array('sec' => 1, 'min' => 60, 'hou' => 3600, 'day' => 86400, 'wee' => 604800);
  785. return (int) round($timevalue / $tbl[strtolower(substr($intvltype, 0, 3))]);
  786. }
  787. // ----------------------------
  788. // Extract indexed array from 'getdate'
  789. $today = getdate();
  790. printf("Today is day %d of the current year\n", $today['yday']);
  791. // Extract indexed, and associative arrays, respectively, from 'localtime'
  792. $today = localtime();
  793. printf("Today is day %d of the current year\n", $today[7]);
  794. $today = localtime(time(), TRUE);
  795. printf("Today is day %d of the current year\n", $today['tm_yday']);
  796. // @@PLEAC@@_3.1
  797. define(SEP, '-');
  798. // ------------
  799. $today = getdate();
  800. $day = $today['mday'];
  801. $month = $today['mon'];
  802. $year = $today['year'];
  803. // Either do this to use interpolation:
  804. $sep = SEP;
  805. echo "Current date is: {$year}{$sep}{$month}{$sep}{$day}\n";
  806. // or simply concatenate:
  807. echo 'Current date is: ' . $year . SEP . $month . SEP . $day . "\n";
  808. // ------------
  809. $today = localtime(time(), TRUE);
  810. $day = $today['tm_mday'];
  811. $month = $today['tm_mon'] + 1;
  812. $year = $today['tm_year'] + 1900;
  813. printf("Current date is: %4d%s%2d%s%2d\n", $year, SEP, $month, SEP, $day);
  814. // ------------
  815. $format = 'Y' . SEP . 'n' . SEP . 'd';
  816. $today = date($format);
  817. echo "Current date is: {$today}\n";
  818. // ------------
  819. $sep = SEP;
  820. $today = strftime("%Y$sep%m$sep%d");
  821. echo "Current date is: {$today}\n";
  822. // @@PLEAC@@_3.2
  823. $timestamp = mktime($hour, $min, $sec, $month, $day, $year);
  824. $timestamp = gmmktime($hour, $min, $sec, $month, $day, $year);
  825. // @@PLEAC@@_3.3
  826. $dmyhms = getdate(); // timestamp: current date / time
  827. $dmyhms = getdate($timestamp); // timestamp: arbitrary
  828. $day = $dmyhms['mday'];
  829. $month = $dmyhms['mon'];
  830. $year = $dmyhms['year'];
  831. $hours = $dmyhms['hours'];
  832. $minutes = $dmyhms['minutes'];
  833. $seconds = $dmyhms['seconds'];
  834. // @@PLEAC@@_3.4
  835. // Date arithmetic is probably most easily performed using timestamps [i.e. *NIX Epoch
  836. // Seconds]. Dates - in whatever form - are converted to timestamps, these are
  837. // arithmetically manipulated, and the result converted to whatever form required.
  838. // Note: use 'mktime' to create timestamps properly adjusted for daylight saving; whilst
  839. // 'strtotime' is more convenient to use, it does not, AFAIK, include this adjustment
  840. $when = $now + $difference;
  841. $then = $now - $difference;
  842. // ------------
  843. $now = mktime(0, 0, 0, 8, 6, 2003);
  844. $diff1 = dateOffset('day=1'); $diff2 = dateOffset('weeks=2');
  845. echo 'Today is: ' . date('Y-m-d', $now) . "\n";
  846. echo 'One day in the future is: ' . date('Y-m-d', $now + $diff1) . "\n";
  847. echo 'Two weeks in the past is: ' . date('Y-m-d', $now - $diff2) . "\n";
  848. // ----------------------------
  849. // Date arithmetic performed using a custom function, 'dateOffset'. Internally, offset may
  850. // be computed in one of several ways:
  851. // * Direct timestamp manipulation - fastest, but no daylight saving adjustment
  852. // * Via 'date' built-in function - slower [?], needs a base time from which to
  853. // compute values, but has daylight saving adjustment
  854. // * Via 'strtotime' built-in function - as for 'date'
  855. // * Via 'DateTime' class
  856. //
  857. // Approach used here is to utilise direct timestamp manipulation in 'dateOffset' [it's
  858. // performance can also be improved by replacing $tbl with a global definition etc],
  859. // and to illustrate how the other approaches might be used
  860. // 1. 'dateOffset'
  861. $birthtime = mktime(3, 45, 50, 1, 18, 1973);
  862. $interval = dateOffset('day=55', 'hours=2', 'min=17', 'sec=5');
  863. $then = $birthtime + $interval;
  864. printf("Birthtime is: %s\nthen is: %s\n", date(DATE_RFC1123, $birthtime), date(DATE_RFC1123, $then));
  865. // ------------
  866. // 2. 'date'
  867. // Base values, and offsets, respectively
  868. $hr = 3; $min = 45; $sec = 50; $mon = 1; $day = 18; $year = 1973;
  869. $yroff = 0; $monoff = 0; $dayoff = 55; $hroff = 2; $minoff = 17; $secoff = 5;
  870. // Base date
  871. $birthtime = mktime($hr, $min, $sec, $mon, $day, $year, TRUE);
  872. $year = date('Y', $birthtime) + $yroff;
  873. $mon = date('m', $birthtime) + $monoff;
  874. $day = date('d', $birthtime) + $dayoff;
  875. $hr = date('H', $birthtime) + $hroff;
  876. $min = date('i', $birthtime) + $minoff;
  877. $sec = date('s', $birthtime) + $secoff;
  878. // Offset date
  879. $then = mktime($hr, $min, $sec, $mon, $day, $year, TRUE);
  880. printf("Birthtime is: %s\nthen is: %s\n", date(DATE_RFC1123, $birthtime), date(DATE_RFC1123, $then));
  881. // ------------
  882. // 3. 'strtotime'
  883. // Generate timestamp whatever way is preferable
  884. $birthtime = mktime(3, 45, 50, 1, 18, 1973);
  885. $birthtime = strtotime('1/18/1973 03:45:50');
  886. $then = strtotime('+55 days 2 hours 17 minutes 2 seconds', $birthtime);
  887. printf("Birthtime is: %s\nthen is: %s\n", date(DATE_RFC1123, $birthtime), date(DATE_RFC1123, $then));
  888. // ------------
  889. // 4. 'DateTime' class
  890. $birthtime = new DateTime('1/18/1973 03:45:50');
  891. $then = new DateTime('1/18/1973 03:45:50');
  892. $then->modify('+55 days 2 hours 17 minutes 2 seconds');
  893. printf("Birthtime is: %s\nthen is: %s\n", $birthtime->format(DATE_RFC1123), $then->format(DATE_RFC1123));
  894. // @@PLEAC@@_3.5
  895. // Date intervals are most easily computed using timestamps [i.e. *NIX Epoch
  896. // Seconds] which, of course, gives the interval result is seconds from which
  897. // all other interval measures [days, weeks, months, years] may be derived.
  898. // Refer to previous section for discussion of daylight saving and other related
  899. // problems
  900. $interval_seconds = $recent - $earlier;
  901. // ----------------------------
  902. // Conventional approach ...
  903. $bree = strtotime('16 Jun 1981, 4:35:25');
  904. $nat = strtotime('18 Jan 1973, 3:45:50');
  905. // ... or, with daylight saving adjustment
  906. $bree = mktime(4, 35, 25, 6, 16, 1981, TRUE);
  907. $nat = mktime(3, 45, 50, 1, 18, 1973, TRUE);
  908. $difference = $bree - $nat;
  909. // 'dateInterval' custom function computes intervals in several measures given an
  910. // interval in seconds. Note, 'month' and 'year' measures not provided
  911. printf("There were %d seconds between Nat and Bree\n", $difference);
  912. printf("There were %d weeks between Nat and Bree\n", dateInterval('weeks', $difference));
  913. printf("There were %d days between Nat and Bree\n", dateInterval('days', $difference));
  914. printf("There were %d hours between Nat and Bree\n", dateInterval('hours', $difference));
  915. printf("There were %d minutes between Nat and Bree\n", dateInterval('mins', $difference));
  916. // @@PLEAC@@_3.6
  917. // 'getdate' accepts a timestamp [or implicitly calls 'time'] and returns an array of
  918. // date components. It returns much the same information as 'strptime' except that
  919. // the component names are different
  920. $today = getdate();
  921. $weekday = $today['wday'];
  922. $monthday = $today['mday'];
  923. $yearday = $today['yday'];
  924. $weeknumber = (int) round($yearday / 7.0);
  925. // Safter method of obtaining week number
  926. $weeknumber = strftime('%U') + 1;
  927. // ----------------------------
  928. define(SEP, '/');
  929. $day = 16;
  930. $month = 6;
  931. $year = 1981;
  932. $timestamp = mktime(0, 0, 0, $month, $day, $year);
  933. $date = getdate($timestamp);
  934. $weekday = $date['wday'];
  935. $monthday = $date['mday'];
  936. $yearday = $date['yday'];
  937. $weeknumber = (int) round($yearday / 7.0);
  938. $weeknumber = strftime('%U', $timestamp) + 1;
  939. // Interpolate ...
  940. $sep = SEP;
  941. echo "{$month}{$sep}{$day}{$sep}{$year} was a {$date['weekday']} in week {$weeknumber}\n";
  942. // ... or, concatenate
  943. echo $month . SEP . $day . SEP . $year . ' was a ' . $date['weekday']
  944. . ' in week ' . $weeknumber . "\n";
  945. // @@PLEAC@@_3.7
  946. // 'strtotime' parses a textual date expression by attempting a 'best guess' at
  947. // the format, and either fails, or generates a timestamp. Timestamp could be fed
  948. // into any one of the various functions; example:
  949. $timestamp = strtotime('1998-06-03'); echo strftime('%Y-%m-%d', $timestamp) . "\n";
  950. // 'strptime' parses a textual date expression according to a specified format,
  951. // and returns an array of date components; components can be easily dumped
  952. print_r(strptime('1998-06-03', '%Y-%m-%d'));
  953. // ----------------------------
  954. // Parse date string according to format
  955. $darr = strptime('1998-06-03', '%Y-%m-%d');
  956. if (!empty($darr))
  957. {
  958. // Show date components in 'debug' form
  959. print_r($darr);
  960. // Check whether there was a parse error i.e. one or more components could not
  961. // be extracted from the string
  962. if (empty($darr['unparsed']))
  963. {
  964. // Properly parsed date, so validate required components using, 'checkdate'
  965. if (checkdate($darr['tm_mon'] + 1, $darr['tm_mday'], $darr['tm_year'] + 1900))
  966. echo "Parsed date verified as correct\n";
  967. else
  968. echo "Parsed date failed verification\n";
  969. }
  970. else
  971. {
  972. echo "Date string parse not complete; failed components: {$darr['unparsed']}\n";
  973. }
  974. }
  975. else
  976. {
  977. echo "Date string could not be parsed\n";
  978. }
  979. // @@PLEAC@@_3.8
  980. // 'date' and 'strftime' both print a date string based on:
  981. // * Format String, describing layout of date components
  982. // * Timestamp [*NIX Epoch Seconds], either given explicitly, or implictly
  983. // via a call to 'time' which retrieves current time value
  984. $ts = 1234567890;
  985. date('Y/m/d', $ts);
  986. date('Y/m/d', mktime($h, $m, $s, $mth, $d, $y, $is_dst));
  987. date('Y/m/d'); // same as: date('Y/m/d', time());
  988. // ------------
  989. $ts = 1234567890;
  990. strftime('%Y/%m/%d', $ts);
  991. strftime('%Y/%m/%d', mktime($h, $m, $s, $mth, $d, $y, $is_dst));
  992. strftime('%Y/%m/%d'); // same as: strftime('%Y/%m/%d', time());
  993. // ----------------------------
  994. // 'mktime' creates a local time timestamp
  995. $t = strftime('%a %b %e %H:%M:%S %z %Y', mktime(3, 45, 50, 1, 18, 73, TRUE));
  996. echo "{$t}\n";
  997. // 'gmmktime' creates a GMT time timestamp
  998. $t = strftime('%a %b %e %H:%M:%S %z %Y', gmmktime(3, 45, 50, 1, 18, 73));
  999. echo "{$t}\n";
  1000. // ----------------------------
  1001. // 'strtotime' parses a textual date expression, and generates a timestamp
  1002. $t = strftime('%A %D', strtotime('18 Jan 1973, 3:45:50'));
  1003. echo "{$t}\n";
  1004. // This should generate output identical to previous example
  1005. $t = strftime('%A %D', mktime(3, 45, 50, 1, 18, 73, TRUE));
  1006. echo "{$t}\n";
  1007. // @@PLEAC@@_3.9
  1008. // PHP 5 and above can use the built-in, 'microtime'. Crude implementation for ealier versions:
  1009. // function microtime() { $t = gettimeofday(); return (float) ($t['sec'] + $t['usec'] / 1000000.0); }
  1010. // ------------
  1011. $before = microtime();
  1012. $line = fgets(STDIN);
  1013. $elapsed = microtime() - $before;
  1014. printf("You took %.3f seconds\n", $elapsed);
  1015. // ------------
  1016. define(NUMBER_OF_TIMES, 100);
  1017. define(SIZE, 500);
  1018. for($i = 0; $i < NUMBER_OF_TIMES; $i++)
  1019. {
  1020. $arr = array();
  1021. for($j = 0; $j < SIZE; $j++) $arr[] = rand();
  1022. $begin = microtime();
  1023. sort($arr);
  1024. $elapsed = microtime() - $begin;
  1025. $total_time += $elapsed;
  1026. }
  1027. printf("On average, sorting %d random numbers takes %.5f seconds\n", SIZE, $total_time / (float) NUMBER_OF_TIMES);
  1028. // @@PLEAC@@_3.10
  1029. // Low-resolution: sleep time specified in seconds
  1030. sleep(1);
  1031. // High-resolution: sleep time specified in microseconds [not reliable under Windows]
  1032. usleep(250000);
  1033. // @@PLEAC@@_3.11
  1034. // @@INCOMPLETE@@
  1035. // @@INCOMPLETE@@
  1036. // @@PLEAC@@_4.0
  1037. // Nested arrays are supported, and may be easily printed using 'print_r'
  1038. $nested = array('this', 'that', 'the', 'other');
  1039. $nested = array('this', 'that', array('the', 'other')); print_r($nested);
  1040. $tune = array('The', 'Star-Spangled', 'Banner');
  1041. // @@PLEAC@@_4.1
  1042. // PHP offers only the 'array' type which is actually an associative array, though
  1043. // may be numerically indexed, to mimic vectors and matrices; there is no separate
  1044. // 'list' type
  1045. $a = array('quick', 'brown', 'fox');
  1046. // ------------
  1047. $a = escapeshellarg('Why are you teasing me?');
  1048. // ------------
  1049. $lines = <<<END_OF_HERE_DOC
  1050. The boy stood on the burning deck,
  1051. it was as hot as glass.
  1052. END_OF_HERE_DOC;
  1053. // ------------
  1054. $bigarray = array_map('rtrim', file('mydatafile'));
  1055. // ------------
  1056. $banner = 'The mines of Moria';
  1057. $banner = escapeshellarg('The mines of Moria');
  1058. // ------------
  1059. $name = 'Gandalf';
  1060. $banner = "Speak {$name}, and enter!";
  1061. $banner = 'Speak ' . escapeshellarg($name) . ' and welcome!';
  1062. // ------------
  1063. $his_host = 'www.perl.com';
  1064. $host_info = `nslookup $his_host`;
  1065. $cmd = 'ps ' . posix_getpid(); $perl_info = `$cmd`;
  1066. $shell_info = `ps $$`;
  1067. // ------------
  1068. $banner = array('Costs', 'only', '$4.95');
  1069. $banner = array_map('escapeshellarg', split(' ', 'Costs only $4.95'));
  1070. // ------------
  1071. // AFAIK PHP doesn't support non-quoted strings ala Perl's 'q', 'qq' and 'qw', so arrays
  1072. // created from strings must use quoted strings, and make use of 'split' [or equivalent].
  1073. // A slew of functions exist for performing string quoting, including 'escapeshellarg',
  1074. // 'quotemeta', and 'preg_quote'
  1075. $brax = split(' ', '( ) < > { } [ ]');
  1076. // Do this to quote each element within '..'
  1077. // $brax = array_map('escapeshellarg', split(' ', '( ) < > { } [ ]'));
  1078. $rings = split(' ', 'Nenya Narya Vilya');
  1079. $tags = split(' ', 'LI TABLE TR TD A IMG H1 P');
  1080. $sample = split(' ', 'The vertical bar | looks and behaves like a pipe.');
  1081. // @@PLEAC@@_4.2
  1082. function commify_series($list)
  1083. {
  1084. $n = str_word_count($list); $series = str_word_count($list, 1);
  1085. if ($n == 0) return NULL;
  1086. if ($n == 1) return $series[0];
  1087. if ($n == 2) return $series[0] . ' and ' . $series[1];
  1088. return join(', ', array_slice($series, 0, -1)) . ', and ' . $series[$n - 1];
  1089. }
  1090. // ------------
  1091. echo commify_series('red') . "\n";
  1092. echo commify_series('red yellow') . "\n";
  1093. echo commify_series('red yellow green') . "\n";
  1094. $mylist = 'red yellow green';
  1095. echo 'I have ' . commify_series($mylist) . " marbles.\n";
  1096. // ----------------------------
  1097. function commify_series($arr)
  1098. {
  1099. $n = count($arr); $sepchar = ',';
  1100. foreach($arr as $str)
  1101. {
  1102. if (strpos($str, ',') === false) continue;
  1103. $sepchar = ';'; break;
  1104. }
  1105. if ($n == 0) return NULL;
  1106. if ($n == 1) return $arr[0];
  1107. if ($n == 2) return $arr[0] . ' and ' . $arr[1];
  1108. return join("{$sepchar} ", array_slice($arr, 0, -1)) . "{$sepchar} and " . $arr[$n - 1];
  1109. }
  1110. // ------------
  1111. $lists = array(
  1112. array('just one thing'),
  1113. split(' ', 'Mutt Jeff'),
  1114. split(' ', 'Peter Paul Mary'),
  1115. array('To our parents', 'Mother Theresa', 'God'),
  1116. array('pastrami', 'ham and cheese', 'peanut butter and jelly', 'tuna'),
  1117. array('recycle tired, old phrases', 'ponder big, happy thoughts'),
  1118. array('recycle tired, old phrases', 'ponder big, happy thoughts', 'sleep and dream peacefully'));
  1119. foreach($lists as $arr)
  1120. {
  1121. echo 'The list is: ' . commify_series($arr) . ".\n";
  1122. }
  1123. // @@PLEAC@@_4.3
  1124. // AFAICT you cannot grow / shrink an array to an arbitrary size. However, you can:
  1125. // * Grow an array by appending an element using subscrip notation, or using
  1126. // either 'array_unshift' or 'array_push' to add one or more elements
  1127. $arr[] = 'one';
  1128. array_unshift($arr, 'one', 'two', 'three');
  1129. array_push($arr, 'one', 'two', 'three');
  1130. // * Shrink an array by using 'unset' to remove one or more specific elements, or
  1131. // either 'array_shift' or 'array_pop' to remove an element from the ends
  1132. unset($arr[$idx1], $arr[$idx2], $arr[$idx3]);
  1133. $item = array_shift($arr);
  1134. $item = array_pop($arr);
  1135. // ----------------------------
  1136. function what_about_the_array()
  1137. {
  1138. global $people;
  1139. echo 'The array now has ' . count($people) . " elements\n";
  1140. echo 'The index value of the last element is ' . (count($people) - 1) . "\n";
  1141. echo 'Element #3 is ' . $people[3] . "\n";
  1142. }
  1143. $people = array('Crosby', 'Stills', 'Nash', 'Young');
  1144. what_about_the_array();
  1145. array_pop($people);
  1146. what_about_the_array();
  1147. // Cannot, AFAICT, resize the array to an arbitrary size
  1148. # @@PLEAC@@_4.4
  1149. foreach ($list as $item) {
  1150. // do something with $item
  1151. }
  1152. // Environment listing example
  1153. // PHP defines a superglobal $_ENV to provide access to environment
  1154. // variables.
  1155. // Beware, array assignment means copying in PHP. You need to use
  1156. // the reference operator to avoid copying. But we want a copy here.
  1157. $env = $_ENV;
  1158. // PHP can sort an array by key, so you don't need to get keys,
  1159. // and then sort.
  1160. ksort($env);
  1161. foreach ($env as $key => $value) {
  1162. echo "{$key}={$value}\n";
  1163. }
  1164. // Literal translation of Perl example would be:
  1165. $keys = array_keys($_ENV);
  1166. sort($keys);
  1167. foreach ($keys as $key) {
  1168. echo "{$key}={$_ENV[$key]}\n";
  1169. }
  1170. // This assumes that MAX_QUOTA is a named constant.
  1171. foreach ($all_users as $user) {
  1172. $disk_space = get_usage($user);
  1173. if ($disk_space > MAX_QUOTA) {
  1174. complain($user);
  1175. }
  1176. }
  1177. // You can't modify array's elements in-place.
  1178. $array = array(1, 2, 3);
  1179. $newarray = array();
  1180. foreach ($array as $item) {
  1181. $newarray[] = $item - 1;
  1182. }
  1183. print_r($newarray);
  1184. // Before PHP 5, that is. You can precede the reference operator
  1185. // before $item to get reference instead of copy.
  1186. $array = array(1, 2, 3);
  1187. foreach ($array as &$item) {
  1188. $item--;
  1189. }
  1190. print_r($array);
  1191. // TODO: explain the old each() and list() iteration construct.
  1192. // foreach is new in PHP 4, and there are subtle differences.
  1193. // @@PLEAC@@_4.5
  1194. // Conventional 'read-only' access
  1195. foreach($array as $item)
  1196. {
  1197. ; // Can access, but not update, array element referred to by '$item'
  1198. }
  1199. // ----
  1200. // '&' makes '$item' a reference
  1201. foreach($array as &$item)
  1202. {
  1203. ; // Update array element referred to by '$item'
  1204. }
  1205. // ------------
  1206. $arraylen = count($array);
  1207. for($i = 0; $i < $arraylen; $i++)
  1208. {
  1209. ; // '$array' is updateable via subscript notation
  1210. }
  1211. // ----------------------------
  1212. $fruits = array('Apple', 'Raspberry');
  1213. foreach($fruits as &$fruit)
  1214. {
  1215. echo "{$fruit} tastes good in a pie.\n";
  1216. }
  1217. $fruitlen = count($fruits);
  1218. for($i = 0; $i < $fruitlen; $i++)
  1219. {
  1220. echo "{$fruits[$i]} tastes good in a pie.\n";
  1221. }
  1222. // ----------------------------
  1223. $rogue_cats = array('Blackie', 'Goldie', 'Silkie');
  1224. // Take care to assign reference to '$rogue_cats' array via '=&'
  1225. $namelist['felines'] =& $rogue_cats;
  1226. // Take care to make '$cat' a reference via '&$' to allow updating
  1227. foreach($namelist['felines'] as &$cat)
  1228. {
  1229. $cat .= ' [meow]';
  1230. }
  1231. // Via array reference
  1232. foreach($namelist['felines'] as $cat)
  1233. {
  1234. echo "{$cat} purrs hypnotically.\n";
  1235. }
  1236. echo "---\n";
  1237. // Original array
  1238. foreach($rogue_cats as $cat)
  1239. {
  1240. echo "{$cat} purrs hypnotically.\n";
  1241. }
  1242. // @@PLEAC@@_4.6
  1243. // PHP offers the 'array_unique' function to perform this task. It works with both keyed,
  1244. // and numerically-indexed arrays; keys / indexes are preserved; use of 'array_values'
  1245. // is recommended to reindex numerically-indexed arrays since there will likely be missing
  1246. // indexes
  1247. // Remove duplicate values
  1248. $unique = array_unique($array);
  1249. // Remove duplicates, and reindex [for numerically-indexed arrays only]
  1250. $unique = array_values(array_unique($array));
  1251. // or use:
  1252. $unique = array_keys(array_flip($array));
  1253. // ----------------------------
  1254. // Selected Perl 'seen' examples
  1255. foreach($list as $item)
  1256. {
  1257. if (!isset($seen[$item]))
  1258. {
  1259. $seen[$item] = TRUE;
  1260. $unique[] = $item;
  1261. }
  1262. }
  1263. // ------------
  1264. foreach($list as $item)
  1265. {
  1266. $seen[$item] || (++$seen[$item] && ($unique[] = $item));
  1267. }
  1268. // ------------
  1269. function some_func($item)
  1270. {
  1271. ; // Do something with '$item'
  1272. }
  1273. foreach($list as $item)
  1274. {
  1275. $seen[$item] || (++$seen[$item] && some_func($item));
  1276. }
  1277. // ----------------------------
  1278. foreach(array_slice(preg_split('/\n/', `who`), 0, -1) as $user_entry)
  1279. {
  1280. $user = preg_split('/\s/', $user_entry);
  1281. $ucnt[$user[0]]++;
  1282. }
  1283. ksort($ucnt);
  1284. echo "users logged in:\n";
  1285. foreach($ucnt as $user => $cnt)
  1286. {
  1287. echo "\t{$user} => {$cnt}\n";
  1288. }
  1289. // @@PLEAC@@_4.7
  1290. // PHP offers the 'array_diff' and 'array_diff_assoc' functions to perform this task. Same
  1291. // points as made about 'array_unique' apply here also
  1292. $a = array('c', 'a', 'b', 'd');
  1293. $b = array('c', 'a', 'b', 'e');
  1294. $diff = array_diff($a, $b); // $diff -> [3] 'd'
  1295. $diff = array_diff($b, $a); // $diff -> [3] 'e'
  1296. // Numerically-indexed array, reindexed
  1297. $diff = array_values(array_diff($a, $b)); // $diff -> [0] 'd'
  1298. $diff = array_values(array_diff($b, $a)); // $diff -> [0] 'e'
  1299. // ----------------------------
  1300. // 1st Perl 'seen' example only
  1301. $a = array('k1' => 11, 'k2' => 12, 'k4' => 14);
  1302. $b = array('k1' => 11, 'k2' => 12, 'k3' => 13);
  1303. foreach($b as $item => $value) { $seen[$item] = 1; }
  1304. // Stores key only e.g. $aonly[0] contains 'k4', same as Perl example
  1305. foreach($a as $item => $value) { if (!$seen[$item]) $aonly[] = $item; }
  1306. // Stores key and value e.g. $aonly['k4'] contains 14, same entry as in $a
  1307. foreach($a as $item => $value) { if (!$seen[$item]) $aonly[$item] = $value; }
  1308. // ----------------------------
  1309. // Conventional way: $hash = array('key1' => 1, 'key2' => 2);
  1310. $hash['key1'] = 1;
  1311. $hash['key2'] = 2;
  1312. $hash = array_combine(array('key1', 'key2'), array(1, 2));
  1313. // ------------
  1314. $seen = array_slice($b, 0);
  1315. $seen = array_combine(array_keys($b), array_fill(0, count($b), 1));
  1316. // @@PLEAC@@_4.8
  1317. // PHP offers a number of array-based 'set operation' functions:
  1318. // * union: array_unique(array_merge(...))
  1319. // * intersection: array_intersect and family
  1320. // * difference: array_diff and family
  1321. // which may be used for this type of task. Also, if returned arrays need to be
  1322. // reindexed, 'array_slice($array, 0)', or 'array_values($array)' are useful
  1323. $a = array(1, 3, 5, 6, 7, 8);
  1324. $b = array(2, 3, 5, 7, 9);
  1325. $union = array_values(array_unique(array_merge($a, $b))); // 1, 2, 3, 5, 6, 7, 8, 9
  1326. $isect = array_values(array_intersect($a, $b)); // 3, 5, 7
  1327. $diff = array_values(array_diff($a, $b)); // 1, 8
  1328. // @@PLEAC@@_4.9
  1329. // PHP offers the 'array_merge' function to perform this task. Duplicate values are retained,
  1330. // but if arrays are numerically-indexed, resulting array is reindexed
  1331. $arr1 = array('c', 'a', 'b', 'd');
  1332. $arr2 = array('c', 'a', 'b', 'e');
  1333. $new = array_merge($arr1, $arr2); // $new -> 'c', 'a', 'b', 'd', 'c', 'a', 'b', 'd'
  1334. // ----------------------------
  1335. $members = array('Time', 'Flies');
  1336. $initiates = array('An', 'Arrow');
  1337. $members = array_merge($members, $initiates);
  1338. // ------------
  1339. $members = array('Time', 'Flies');
  1340. $initiates = array('An', 'Arrow');
  1341. // 'array_splice' is the PHP equivalent to Perl's 'splice'
  1342. array_splice($members, 2, 0, array_merge(array('Like'), $initiates));
  1343. echo join(' ', $members) . "\n";
  1344. array_splice($members, 0, 1, array('Fruit'));
  1345. array_splice($members, -2, 2, array('A', 'Banana'));
  1346. echo join(' ', $members) . "\n";
  1347. // @@PLEAC@@_4.10
  1348. $reversed = array_reverse($array);
  1349. // ----------------------------
  1350. foreach(array_reverse($array) as $item)
  1351. {
  1352. ; // ... do something with '$item' ...
  1353. }
  1354. // ------------
  1355. for($i = count($array) - 1; $i >= 0; $i--)
  1356. {
  1357. ; // ... do something with '$array[$i]' ...
  1358. }
  1359. // ----------------------------
  1360. sort($array);
  1361. $array = array_reverse($array);
  1362. // ------------
  1363. rsort($array);
  1364. // @@PLEAC@@_4.11
  1365. // Array elements can be deleted using 'unset'; removing several elements would require applying
  1366. // 'unset' several times, probably in a loop. However, they would most likely also need to be
  1367. // reindexed, so a better approach would be to use 'array_slice' which avoids explicit looping.
  1368. // Where elements need to be removed, and those elements also returned, it is probably best to
  1369. // combine both operations in a function. This is the approach taken here in implementing both
  1370. // 'shiftN' and 'popN', and it is these functions that are used in the examples
  1371. function popN(&$arr, $n)
  1372. {
  1373. $ret = array_slice($arr, -($n), $n);
  1374. $arr = array_slice($arr, 0, count($arr) - $n);
  1375. return $ret;
  1376. }
  1377. function shiftN(&$arr, $n)
  1378. {
  1379. $ret = array_slice($arr, 0, $n);
  1380. $arr = array_slice($arr, $n);
  1381. return $ret;
  1382. }
  1383. // ------------
  1384. // Remove $n elements from the front of $array; return them in $fron
  1385. $front = shiftN($array, $n);
  1386. // Remove $n elements from the end of $array; return them in $end
  1387. $end = popN($array, $n);
  1388. // ------------
  1389. $friends = array('Peter', 'Paul', 'Mary', 'Jim', 'Tim');
  1390. list($this_, $that) = shiftN($friends, 2);
  1391. echo "{$this_} {$that}\n";
  1392. // ------------
  1393. $beverages = array('Dew', 'Jolt', 'Cola', 'Sprite', 'Fresca');
  1394. $pair = popN($beverages, 2);
  1395. echo join(' ', $pair) . "\n";
  1396. // @@PLEAC@@_4.12
  1397. // This section illustrates various 'find first' techniques. The Perl examples all use an
  1398. // explicit loop and condition testing [also repeated here]. This is the simplest, and
  1399. // [potentially] most efficient approach because the search can be terminated as soon as a
  1400. // match is found. However, it is worth mentioning a few alternatives:
  1401. // * 'array_search' performs a 'find first' using the element value rather than a condition
  1402. // check, so isn't really applicable here
  1403. // * 'array_filter', whilst using a condition check, actually performs a 'find all', though
  1404. // all but the first returned element can be discarded. This approach is actually less error
  1405. // prone than using a loop, but the disadvantage is that each element is visited: there is no
  1406. // means of terminating the search once a match has been found. It would be nice if this
  1407. // function were to have a third parameter, a Boolean flag indicating whether to traverse
  1408. // the whole array, or quit after first match [next version, maybe :) ?]
  1409. $found = FALSE;
  1410. foreach($array as $item)
  1411. {
  1412. // Not found - skip to next item
  1413. if (!$criterion) continue;
  1414. // Found - save and leave
  1415. $match = $item;
  1416. $found = TRUE;
  1417. break;
  1418. }
  1419. if ($found)
  1420. {
  1421. ; // do something with $match
  1422. }
  1423. else
  1424. {
  1425. ; // not found
  1426. }
  1427. // ------------
  1428. function predicate($element)
  1429. {
  1430. if (criterion) return TRUE;
  1431. return FALSE;
  1432. }
  1433. $match = array_slice(array_filter($array, 'predicate'), 0, 1);
  1434. if ($match)
  1435. {
  1436. ; // do something with $match[0]
  1437. }
  1438. else
  1439. {
  1440. ; // $match is empty - not found
  1441. }
  1442. // ----------------------------
  1443. class Employee
  1444. {
  1445. public $name, $age, $ssn, $salary;
  1446. public function __construct($name, $age, $ssn, $salary, $category)
  1447. {
  1448. $this->name = $name;
  1449. $this->age = $age;
  1450. $this->ssn = $ssn;
  1451. $this->salary = $salary;
  1452. $this->category = $category;
  1453. }
  1454. }
  1455. // ------------
  1456. $employees = array(
  1457. new Employee('sdf', 27, 12345, 47000, 'Engineer'),
  1458. new Employee('ajb', 32, 12376, 51000, 'Programmer'),
  1459. new Employee('dgh', 31, 12355, 45000, 'Engineer'));
  1460. // ------------
  1461. function array_update($arr, $lambda, $updarr)
  1462. {
  1463. foreach($arr as $key) $lambda($updarr, $key);
  1464. return $updarr;
  1465. }
  1466. function highest_salaried_engineer(&$arr, $employee)
  1467. {
  1468. static $highest_salary = 0;
  1469. if ($employee->category == 'Engineer')
  1470. {
  1471. if ($employee->salary > $highest_salary)
  1472. {
  1473. $highest_salary = $employee->salary;
  1474. $arr[0] = $employee;
  1475. }
  1476. }
  1477. }
  1478. // ------------
  1479. // 'array_update' custom function is modelled on 'array_reduce' except that it allows the
  1480. // return of an array, contents and length of which are entirely dependant on what the
  1481. // callback function does. Here, it is logically working in a 'find first' capacity
  1482. $highest_salaried_engineer = array_update($employees, 'highest_salaried_engineer', array());
  1483. echo 'Highest paid engineer is: ' . $highest_salaried_engineer[0]->name . "\n";
  1484. // @@PLEAC@@_4.13
  1485. // PHP implements 'grep' functionality [as embodied in the current section] in the 'array_filter'
  1486. // function
  1487. function predicate($element)
  1488. {
  1489. if (criterion) return TRUE;
  1490. return FALSE;
  1491. }
  1492. $matching = array_filter($list, 'predicate');
  1493. // ------------
  1494. $bigs = array_filter($nums, create_function('$n', 'return $n > 1000000;'));
  1495. // ------------
  1496. function is_pig($user)
  1497. {
  1498. $user_details = preg_split('/(\s)+/', $user);
  1499. // Assuming field 5 is the resource being compared
  1500. return $user_details[5] > 1e7;
  1501. }
  1502. $pigs = array_filter(array_slice(preg_split('/\n/', `who -u`), 0, -1), 'is_pig');
  1503. // ------------
  1504. $matching = array_filter(array_slice(preg_split('/\n/', `who`), 0, -1),
  1505. create_function('$user', 'return preg_match(\'/^gnat /\', $user);'));
  1506. // ------------
  1507. class Employee
  1508. {
  1509. public $name, $age, $ssn, $salary;
  1510. public function __construct($name, $age, $ssn, $salary, $category)
  1511. {
  1512. $this->name = $name;
  1513. $this->age = $age;
  1514. $this->ssn = $ssn;
  1515. $this->salary = $salary;
  1516. $this->category = $category;
  1517. }
  1518. }
  1519. // ------------
  1520. $employees = array(
  1521. new Employee('sdf', 27, 12345, 47000, 'Engineer'),
  1522. new Employee('ajb', 32, 12376, 51000, 'Programmer'),
  1523. new Employee('dgh', 31, 12355, 45000, 'Engineer'));
  1524. // ------------
  1525. $engineers = array_filter($employees,
  1526. create_function('$employee', 'return $employee->category == "Engineer";'));
  1527. // @@PLEAC@@_4.14
  1528. // PHP offers a rich set of sorting functions. Key features:
  1529. // * Inplace sorts; the original array, not a a copy, is sorted
  1530. // * Separate functions exist for sorting [both ascending and descending order]:
  1531. // - By value, assign new keys / indices [sort, rsort]
  1532. // - By key [ksort, krsort] (for non-numerically indexed arrays)
  1533. // - By value [asort, arsort]
  1534. // - As above, but using a user-defined comparator [i.e. callback function]
  1535. // [usort, uasort, uksort]
  1536. // - Natural order sort [natsort]
  1537. // * Significantly, if sorting digit-only elements, whether strings or numbers,
  1538. // 'natural order' [i.e. 1 before 10 before 100 (ascending)] is retained. If
  1539. // the elements are alphanumeric e.g. 'z1', 'z10' then 'natsort' should be
  1540. // used [note: beware of 'natsort' with negative numbers; prefer 'sort' or 'asort']
  1541. $unsorted = array(7, 12, -13, 2, 100, 5, 1, -2, 23, 3, 6, 4);
  1542. sort($unsorted); // -13, -2, 1, 2, 3, 4, 5, 6, 7, 12, 23, 100
  1543. rsort($unsorted); // 100, 23, 12, 7, 6, 5, 4, 3, 2, 1, -2, -13
  1544. asort($unsorted); // -13, -2, 1, 2, 3, 4, 5, 6, 7, 12, 23, 100
  1545. arsort($unsorted); // 100, 23, 12, 7, 6, 5, 4, 3, 2, 1, -2, -13
  1546. natsort($unsorted); // -2, -13, 1, 2, 3, 4, 5, 6, 7, 12, 23, 100
  1547. // ------------
  1548. function ascend($left, $right) { return $left > $right; }
  1549. function descend($left, $right) { return $left < $right; }
  1550. // ------------
  1551. usort($unsorted, 'ascend'); // -13, -2, 1, 2, 3, 4, 5, 6, 7, 12, 23, 100
  1552. usort($unsorted, 'descend'); // 100, 23, 12, 7, 6, 5, 4, 3, 2, 1, -2, -13
  1553. uasort($unsorted, 'ascend'); // -13, -2, 1, 2, 3, 4, 5, 6, 7, 12, 23, 100
  1554. uasort($unsorted, 'descend'); // 100, 23, 12, 7, 6, 5, 4, 3, 2, 1, -2, -13
  1555. // ----------------------------
  1556. function kill_process($pid)
  1557. {
  1558. // Is 'killable' ?
  1559. if (!posix_kill($pid, 0)) return;
  1560. // Ok, so kill in two stages
  1561. posix_kill($pid, 15); // SIGTERM
  1562. sleep(1);
  1563. posix_kill($pid, 9); // SIGKILL
  1564. }
  1565. function pid($pentry)
  1566. {
  1567. $p = preg_split('/\s/', trim($pentry));
  1568. return $p[0];
  1569. }
  1570. $processes = array_map('pid', array_slice(preg_split('/\n/', `ps ax`), 1, -1));
  1571. sort($processes);
  1572. echo join(' ,', $processes) . "\n";
  1573. echo 'Enter a pid to kill: ';
  1574. if (($pid = trim(fgets(STDIN))))
  1575. kill_process($pid);
  1576. // @@PLEAC@@_4.15
  1577. // Tasks in this section would typically use the PHP 'usort' family of functions
  1578. // which are used with a comparator function so as to perform custom comparisions.
  1579. // A significant difference from the Perl examples is that these functions are
  1580. // inplace sorters, so it is the original array that is modified. Where this must
  1581. // be prevented a copy of the array can be made and sorted
  1582. function comparator($left, $right)
  1583. {
  1584. ; // Compare '$left' with '$right' returning result
  1585. }
  1586. // ------------
  1587. $ordered = array_slice($unordered);
  1588. usort($ordered, 'comparator');
  1589. // ----------------------------
  1590. // The Perl example looks like it is creating a hash using computed values as the key,
  1591. // array values as the value, sorting on the computed key, then extracting the sorted
  1592. // values and placing them back into an array
  1593. function compute($value)
  1594. {
  1595. ; // Return computed value utilising '$value'
  1596. }
  1597. // ------------
  1598. // Original numerically-indexed array [sample data used]
  1599. $unordered = array(5, 3, 7, 1, 4, 2, 6);
  1600. // Create hash using 'compute' function to generate the keys. This example assumes that
  1601. // each value in the '$unordered' array is used in generating the corresponding '$key'
  1602. foreach($unordered as $value)
  1603. {
  1604. $precomputed[compute($value)] = $value;
  1605. }
  1606. // Copy the hash, and sort it by key
  1607. $ordered_precomputed = array_slice($precomputed, 0); ksort($ordered_precomputed);
  1608. // Extract the values of the hash in current order placing them in a new numerically-indexed
  1609. // array
  1610. $ordered = array_values($ordered_precomputed);
  1611. // ----------------------------
  1612. // As above, except uses 'array_update' and 'accum' to help create hash
  1613. function array_update($arr, $lambda, $updarr)
  1614. {
  1615. foreach($arr as $key) $lambda($updarr, $key);
  1616. return $updarr;
  1617. }
  1618. function accum(&$arr, $value)
  1619. {
  1620. $arr[compute($value)] = $value;
  1621. }
  1622. // ------------
  1623. function compute($value)
  1624. {
  1625. ; // Return computed value utilising '$value'
  1626. }
  1627. // ------------
  1628. // Original numerically-indexed array [sample data used]
  1629. $unordered = array(5, 3, 7, 1, 4, 2, 6);
  1630. // Create hash
  1631. $precomputed = array_update($unordered, 'accum', array());
  1632. // Copy the hash, and sort it by key
  1633. $ordered_precomputed = array_slice($precomputed, 0); ksort($ordered_precomputed);
  1634. // Extract the values of the hash in current order placing them in a new numerically-indexed
  1635. // array
  1636. $ordered = array_values($ordered_precomputed);
  1637. // ----------------------------
  1638. class Employee
  1639. {
  1640. public $name, $age, $ssn, $salary;
  1641. public function __construct($name, $age, $ssn, $salary)
  1642. {
  1643. $this->name = $name;
  1644. $this->age = $age;
  1645. $this->ssn = $ssn;
  1646. $this->salary = $salary;
  1647. }
  1648. }
  1649. // ------------
  1650. $employees = array(
  1651. new Employee('sdf', 27, 12345, 47000),
  1652. new Employee('ajb', 32, 12376, 51000),
  1653. new Employee('dgh', 31, 12355, 45000));
  1654. // ------------
  1655. $ordered = array_slice($employees, 0);
  1656. usort($ordered, create_function('$left, $right', 'return $left->name > $right->name;'));
  1657. // ------------
  1658. $sorted_employees = array_slice($employees, 0);
  1659. usort($sorted_employees, create_function('$left, $right', 'return $left->name > $right->name;'));
  1660. $bonus = array(12376 => 5000, 12345 => 6000, 12355 => 0);
  1661. foreach($sorted_employees as $employee)
  1662. {
  1663. echo "{$employee->name} earns \${$employee->salary}\n";
  1664. }
  1665. foreach($sorted_employees as $employee)
  1666. {
  1667. if (($amount = $bonus[$employee->ssn]))
  1668. echo "{$employee->name} got a bonus of: \${$amount}\n";
  1669. }
  1670. // ------------
  1671. $sorted = array_slice($employees, 0);
  1672. usort($sorted, create_function('$left, $right', 'return $left->name > $right->name || $left->age != $right->age;'));
  1673. // ----------------------------
  1674. // PHP offers a swag of POSIX functions for obtaining user information [i.e. they all read
  1675. // the '/etc/passwd' file for the relevant infroamtion], and it is these that should rightly
  1676. // be used for this purpose. However, since the intent of this section is to illustrate array
  1677. // manipulation, these functions won't be used. Instead a custom function mimicing Perl's
  1678. // 'getpwent' function will be implemented so the code presented here can more faithfully match
  1679. // the Perl code
  1680. function get_pw_entries()
  1681. {
  1682. function normal_users_only($e)
  1683. {
  1684. $entry = split(':', $e); return $entry[2] > 100 && $entry[2] < 32768;
  1685. }
  1686. foreach(array_filter(file('/etc/passwd'), 'normal_users_only') as $entry)
  1687. $users[] = split(':', trim($entry));
  1688. return $users;
  1689. }
  1690. // ------------
  1691. $users = get_pw_entries();
  1692. usort($users, create_function('$left, $right', 'return $left[0] > $right[0];'));
  1693. foreach($users as $user) echo "{$user[0]}\n";
  1694. // ----------------------------
  1695. $names = array('sdf', 'ajb', 'dgh');
  1696. $sorted = array_slice($names, 0);
  1697. usort($sorted, create_function('$left, $right', 'return substr($left, 1, 1) > substr($right, 1, 1);'));
  1698. // ------------
  1699. $strings = array('bbb', 'aa', 'c');
  1700. $sorted = array_slice($strings, 0);
  1701. usort($sorted, create_function('$left, $right', 'return strlen($left) > strlen($right);'));
  1702. // ----------------------------
  1703. function array_update($arr, $lambda, $updarr)
  1704. {
  1705. foreach($arr as $key) $lambda($updarr, $key);
  1706. return $updarr;
  1707. }
  1708. function accum(&$arr, $value)
  1709. {
  1710. $arr[strlen($value)] = $value;
  1711. }
  1712. // ----
  1713. $strings = array('bbb', 'aa', 'c');
  1714. $temp = array_update($strings, 'accum', array());
  1715. ksort($temp);
  1716. $sorted = array_values($temp);
  1717. // ----------------------------
  1718. function array_update($arr, $lambda, $updarr)
  1719. {
  1720. foreach($arr as $key) $lambda($updarr, $key);
  1721. return $updarr;
  1722. }
  1723. function accum(&$arr, $value)
  1724. {
  1725. if (preg_match('/(\d+)/', $value, $matches))
  1726. $arr[$matches[1]] = $value;
  1727. }
  1728. // ----
  1729. $fields = array('b1b2b', 'a4a', 'c9', 'ddd', 'a');
  1730. $temp = array_update($fields, 'accum', array());
  1731. ksort($temp);
  1732. $sorted_fields = array_values($temp);
  1733. // @@PLEAC@@_4.16
  1734. array_unshift($a1, array_pop($a1)); // last -> first
  1735. array_push($a1, array_shift($a1)); // first -> last
  1736. // ----------------------------
  1737. function grab_and_rotate(&$arr)
  1738. {
  1739. $item = $arr[0];
  1740. array_push($arr, array_shift($arr));
  1741. return $item;
  1742. }
  1743. // ------------
  1744. $processes = array(1, 2, 3, 4, 5);
  1745. while (TRUE)
  1746. {
  1747. $process = grab_and_rotate($processes);
  1748. echo "Handling process {$process}\n";
  1749. sleep(1);
  1750. }
  1751. // @@PLEAC@@_4.17
  1752. // PHP offers the 'shuffle' function to perform this task
  1753. $arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9);
  1754. shuffle($arr);
  1755. echo join(' ', $arr) . "\n";
  1756. // ----------------------------
  1757. // Perl example equivalents
  1758. function fisher_yates_shuffle(&$a)
  1759. {
  1760. $size = count($a) - 1;
  1761. for($i = $size; $i >= 0; $i--)
  1762. {
  1763. if (($j = rand(0, $i)) != $i)
  1764. list($a[$i], $a[$j]) = array($a[$j], $a[$i]);
  1765. }
  1766. }
  1767. function naive_shuffle(&$a)
  1768. {
  1769. $size = count($a);
  1770. for($i = 0; $i < $size; $i++)
  1771. {
  1772. $j = rand(0, $size - 1);
  1773. list($a[$i], $a[$j]) = array($a[$j], $a[$i]);
  1774. }
  1775. }
  1776. // ------------
  1777. $arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9);
  1778. fisher_yates_shuffle($arr);
  1779. echo join(' ', $arr) . "\n";
  1780. naive_shuffle($arr);
  1781. echo join(' ', $arr) . "\n";
  1782. // @@PLEAC@@_4.18
  1783. // @@INCOMPLETE@@
  1784. // @@INCOMPLETE@@
  1785. // @@PLEAC@@_4.19
  1786. // @@INCOMPLETE@@
  1787. // @@INCOMPLETE@@
  1788. // @@PLEAC@@_5.0
  1789. // PHP uses the term 'array' to refer to associative arrays - referred to in Perl
  1790. // as 'hashes' - and for the sake of avoiding confusion, the Perl terminology will
  1791. // be used. As a matter of interest, PHP does not sport a vector, matrix, or list
  1792. // type: the 'array' [Perl 'hash'] serves all these roles
  1793. $age = array('Nat' => 24, 'Jules' => 25, 'Josh' => 17);
  1794. $age['Nat'] = 24;
  1795. $age['Jules'] = 25;
  1796. $age['Josh'] = 17;
  1797. $age = array_combine(array('Nat', 'Jules', 'Josh'), array(24, 25, 17));
  1798. // ------------
  1799. $food_colour = array('Apple' => 'red', 'Banana' => 'yellow',
  1800. 'Lemon' => 'yellow', 'Carrot' => 'orange');
  1801. $food_colour['Apple'] = 'red'; $food_colour['Banana'] = 'yellow';
  1802. $food_colour['Lemon'] = 'yellow'; $food_colour['Carrot'] = 'orange';
  1803. $food_colour = array_combine(array('Apple', 'Banana', 'Lemon', 'Carrot'),
  1804. array('red', 'yellow', 'yellow', 'orange'));
  1805. // @@PLEAC@@_5.1
  1806. $hash[$key] = $value;
  1807. // ------------
  1808. $food_colour = array('Apple' => 'red', 'Banana' => 'yellow',
  1809. 'Lemon' => 'yellow', 'Carrot' => 'orange');
  1810. $food_colour['Raspberry'] = 'pink';
  1811. echo "Known foods:\n";
  1812. foreach($food_colour as $food => $colour) echo "{$food}\n";
  1813. // @@PLEAC@@_5.2
  1814. // Returns TRUE on all existing entries with non-NULL values
  1815. if (isset($hash[$key]))
  1816. ; // entry exists
  1817. else
  1818. ; // no such entry
  1819. // ------------
  1820. // Returns TRUE on all existing entries regardless of attached value
  1821. if (array_key_exists($key, $hash))
  1822. ; // entry exists
  1823. else
  1824. ; // no such entry
  1825. // ----------------------------
  1826. $food_colour = array('Apple' => 'red', 'Banana' => 'yellow',
  1827. 'Lemon' => 'yellow', 'Carrot' => 'orange');
  1828. foreach(array('Banana', 'Martini') as $name)
  1829. {
  1830. if (isset($food_colour[$name]))
  1831. echo "{$name} is a food.\n";
  1832. else
  1833. echo "{$name} is a drink.\n";
  1834. }
  1835. // ----------------------------
  1836. $age = array('Toddler' => 3, 'Unborn' => 0, 'Phantasm' => NULL);
  1837. foreach(array('Toddler', 'Unborn', 'Phantasm', 'Relic') as $thing)
  1838. {
  1839. echo "{$thing}:";
  1840. if (array_key_exists($thing, $age)) echo ' exists';
  1841. if (isset($age[$thing])) echo ' non-NULL';
  1842. if ($age[$thing]) echo ' TRUE';
  1843. echo "\n";
  1844. }
  1845. // @@PLEAC@@_5.3
  1846. // Remove one, or more, hash entries
  1847. unset($hash[$key]);
  1848. unset($hash[$key1], $hash[$key2], $hash[$key3]);
  1849. // Remove entire hash
  1850. unset($hash);
  1851. // ----------------------------
  1852. function print_foods()
  1853. {
  1854. // Perl example uses a global variable
  1855. global $food_colour;
  1856. $foods = array_keys($food_colour);
  1857. echo 'Foods:';
  1858. foreach($foods as $food) echo " {$food}";
  1859. echo "\nValues:\n";
  1860. foreach($foods as $food)
  1861. {
  1862. $colour = $food_colour[$food];
  1863. if (isset($colour))
  1864. echo " {$colour}\n";
  1865. else
  1866. echo " nullified or removed\n";
  1867. }
  1868. }
  1869. // ------------
  1870. $food_colour = array('Apple' => 'red', 'Banana' => 'yellow',
  1871. 'Lemon' => 'yellow', 'Carrot' => 'orange');
  1872. echo "Initially:\n"; print_foods();
  1873. // Nullify an entry
  1874. $food_colour['Banana'] = NULL;
  1875. echo "\nWith 'Banana' nullified\n";
  1876. print_foods();
  1877. // Remove an entry
  1878. unset($food_colour['Banana']);
  1879. echo "\nWith 'Banana' removed\n";
  1880. print_foods();
  1881. // Destroy the hash
  1882. unset($food_colour);
  1883. // @@PLEAC@@_5.4
  1884. // Access keys and values
  1885. foreach($hash as $key => $value)
  1886. {
  1887. ; // ...
  1888. }
  1889. // Access keys only
  1890. foreach(array_keys($hash) as $key)
  1891. {
  1892. ; // ...
  1893. }
  1894. // Access values only
  1895. foreach($hash as $value)
  1896. {
  1897. ; // ...
  1898. }
  1899. // ----------------------------
  1900. $food_colour = array('Apple' => 'red', 'Banana' => 'yellow',
  1901. 'Lemon' => 'yellow', 'Carrot' => 'orange');
  1902. foreach($food_colour as $food => $colour)
  1903. {
  1904. echo "{$food} is {$colour}\n";
  1905. }
  1906. foreach(array_keys($food_colour) as $food)
  1907. {
  1908. echo "{$food} is {$food_colour[$food]}\n";
  1909. }
  1910. // ----------------------------
  1911. // 'countfrom' - count number of messages from each sender
  1912. $line = fgets(STDIN);
  1913. while (!feof(STDIN))
  1914. {
  1915. if (preg_match('/^From: (.*)/', $line, $matches))
  1916. {
  1917. if (isset($from[$matches[1]]))
  1918. $from[$matches[1]] += 1;
  1919. else
  1920. $from[$matches[1]] = 1;
  1921. }
  1922. $line = fgets(STDIN);
  1923. }
  1924. if (isset($from))
  1925. {
  1926. echo "Senders:\n";
  1927. foreach($from as $sender => $count) echo "{$sender} : {$count}\n";
  1928. }
  1929. else
  1930. {
  1931. echo "No valid data entered\n";
  1932. }
  1933. // @@PLEAC@@_5.5
  1934. // PHP offers, 'print_r', which prints hash contents in 'debug' form; it also
  1935. // works recursively, printing any contained arrays in similar form
  1936. // Array
  1937. // (
  1938. // [key1] => value1
  1939. // [key2] => value2
  1940. // ...
  1941. // )
  1942. print_r($hash);
  1943. // ------------
  1944. // Based on Perl example; non-recursive, so contained arrays not printed correctly
  1945. foreach($hash as $key => $value)
  1946. {
  1947. echo "{$key} => $value\n";
  1948. }
  1949. // ----------------------------
  1950. // Sorted by keys
  1951. // 1. Sort the original hash
  1952. ksort($hash);
  1953. // 2. Extract keys, sort, traverse original in key order
  1954. $keys = array_keys($hash); sort($keys);
  1955. foreach($keys as $key)
  1956. {
  1957. echo "{$key} => {$hash[$key]}\n";
  1958. }
  1959. // Sorted by values
  1960. // 1. Sort the original hash
  1961. asort($hash);
  1962. // 2. Extract values, sort, traverse original in value order [warning: finds
  1963. // only first matching key in the case where duplicate values exist]
  1964. $values = array_values($hash); sort($values);
  1965. foreach($values as $value)
  1966. {
  1967. echo $value . ' <= ' . array_search($value, $hash) . "\n";
  1968. }
  1969. // @@PLEAC@@_5.6
  1970. // Unless sorted, hash elements remain in the order of insertion. If care is taken to
  1971. // always add a new element to the end of the hash, then element order is the order
  1972. // of insertion. The following function, 'array_push_associative' [modified from original
  1973. // found at 'array_push' section of PHP documentation], does just that
  1974. function array_push_associative(&$arr)
  1975. {
  1976. foreach (func_get_args() as $arg)
  1977. {
  1978. if (is_array($arg))
  1979. foreach ($arg as $key => $value) { $arr[$key] = $value; $ret++; }
  1980. else
  1981. $arr[$arg] = '';
  1982. }
  1983. return $ret;
  1984. }
  1985. // ------------
  1986. $food_colour = array();
  1987. // Individual calls, or ...
  1988. array_push_associative($food_colour, array('Banana' => 'Yellow'));
  1989. array_push_associative($food_colour, array('Apple' => 'Green'));
  1990. array_push_associative($food_colour, array('Lemon' => 'Yellow'));
  1991. // ... one call, one array; physical order retained
  1992. // array_push_associative($food_colour, array('Banana' => 'Yellow', 'Apple' => 'Green', 'Lemon' => 'Yellow'));
  1993. print_r($food_colour);
  1994. echo "\nIn insertion order:\n";
  1995. foreach($food_colour as $food => $colour) echo " {$food} => {$colour}\n";
  1996. $foods = array_keys($food_colour);
  1997. echo "\nStill in insertion order:\n";
  1998. foreach($foods as $food) echo " {$food} => {$food_colour[$food]}\n";
  1999. // @@PLEAC@@_5.7
  2000. foreach(array_slice(preg_split('/\n/', `who`), 0, -1) as $entry)
  2001. {
  2002. list($user, $tty) = preg_split('/\s/', $entry);
  2003. $ttys[$user][] = $tty;
  2004. // Could instead do this:
  2005. // $user = array_slice(preg_split('/\s/', $entry), 0, 2);
  2006. // $ttys[$user[0]][] = $user[1];
  2007. }
  2008. ksort($ttys);
  2009. // ------------
  2010. foreach($ttys as $user => $all_ttys)
  2011. {
  2012. echo "{$user}: " . join(' ', $all_ttys) . "\n";
  2013. }
  2014. // ------------
  2015. foreach($ttys as $user => $all_ttys)
  2016. {
  2017. echo "{$user}: " . join(' ', $all_ttys) . "\n";
  2018. foreach($all_ttys as $tty)
  2019. {
  2020. $stat = stat('/dev/$tty');
  2021. $pwent = posix_getpwuid($stat['uid']);
  2022. $user = isset($pwent['name']) ? $pwent['name'] : 'Not available';
  2023. echo "{$tty} owned by: {$user}\n";
  2024. }
  2025. }
  2026. // @@PLEAC@@_5.8
  2027. // PHP offers the 'array_flip' function to perform the task of exchanging the keys / values
  2028. // of a hash i.e. invert or 'flip' a hash
  2029. $reverse = array_flip($hash);
  2030. // ----------------------------
  2031. $surname = array('Babe' => 'Ruth', 'Mickey' => 'Mantle');
  2032. $first_name = array_flip($surname);
  2033. echo "{$first_name['Mantle']}\n";
  2034. // ----------------------------
  2035. $argc == 2 || die("usage: {$argv[0]} food|colour\n");
  2036. $given = $argv[1];
  2037. $colour = array('Apple' => 'red', 'Banana' => 'yellow',
  2038. 'Lemon' => 'yellow', 'Carrot' => 'orange');
  2039. $food = array_flip($colour);
  2040. if (isset($colour[$given]))
  2041. echo "{$given} is a food with colour: {$colour[$given]}\n";
  2042. if (isset($food[$given]))
  2043. echo "{$food[$given]} is a food with colour: {$given}\n";
  2044. // ----------------------------
  2045. $food_colour = array('Apple' => 'red', 'Banana' => 'yellow',
  2046. 'Lemon' => 'yellow', 'Carrot' => 'orange');
  2047. foreach($food_colour as $food => $colour)
  2048. {
  2049. $foods_with_colour[$colour][] = $food;
  2050. }
  2051. $colour = 'yellow';
  2052. echo "foods with colour {$colour} were: " . join(' ', $foods_with_colour[$colour]) . "\n";
  2053. // @@PLEAC@@_5.9
  2054. // PHP implements a swag of sorting functions, most designed to work with numerically-indexed
  2055. // arrays. For sorting hashes, the 'key' sorting functions are required:
  2056. // * 'ksort', 'krsort', 'uksort'
  2057. // Ascending order
  2058. ksort($hash);
  2059. // Descending order [i.e. reverse sort]
  2060. krsort($hash);
  2061. // Comparator-based sort
  2062. function comparator($left, $right)
  2063. {
  2064. // Compare left key with right key
  2065. return $left > $right;
  2066. }
  2067. uksort($hash, 'comparator');
  2068. // ----------------------------
  2069. $food_colour = array('Apple' => 'red', 'Banana' => 'yellow',
  2070. 'Lemon' => 'yellow', 'Carrot' => 'orange');
  2071. // ------------
  2072. ksort($food_colour);
  2073. foreach($food_colour as $food => $colour)
  2074. {
  2075. echo "{$food} is {$colour}\n";
  2076. }
  2077. // ------------
  2078. uksort($food_colour, create_function('$left, $right', 'return $left > $right;'));
  2079. foreach($food_colour as $food => $colour)
  2080. {
  2081. echo "{$food} is {$colour}\n";
  2082. }
  2083. // @@PLEAC@@_5.10
  2084. // PHP offers the 'array_merge' function for this task [a related function, 'array_combine',
  2085. // may be used to create a hash from an array of keys, and one of values, respectively]
  2086. // Merge two, or more, arrays
  2087. $merged = array_merge($a, $b, $c);
  2088. // Create a hash from array of keys, and of values, respectively
  2089. $hash = array_combine($keys, $values);
  2090. // ------------
  2091. // Can always merge arrays manually
  2092. foreach(array($h1, $h2, $h3) as $hash)
  2093. {
  2094. foreach($hash as $key => $value)
  2095. {
  2096. // If same-key values differ, only latest retained
  2097. $merged[$key] = $value;
  2098. // Do this to append values for that key
  2099. // $merged[$key][] = $value;
  2100. }
  2101. }
  2102. // ----------------------------
  2103. $food_colour = array('Apple' => 'red', 'Banana' => 'yellow',
  2104. 'Lemon' => 'yellow', 'Carrot' => 'orange');
  2105. $drink_colour = array('Galliano' => 'yellow', 'Mai Tai' => 'blue');
  2106. // ------------
  2107. $ingested_colour = array_merge($food_colour, $drink_colour);
  2108. // ------------
  2109. $substance_colour = array();
  2110. foreach(array($food_colour, $drink_colour) as $hash)
  2111. {
  2112. foreach($hash as $substance => $colour)
  2113. {
  2114. if (array_key_exists($substance, $substance_colour))
  2115. {
  2116. echo "Warning {$substance_colour[$substance]} seen twice. Using first definition.\n";
  2117. continue;
  2118. }
  2119. $substance_colour[$substance] = $colour;
  2120. }
  2121. }
  2122. // @@PLEAC@@_5.11
  2123. // PHP offers a number of array-based 'set operation' functions:
  2124. // * union: array_merge
  2125. // * intersection: array_intersect and family
  2126. // * difference: array_diff and family
  2127. // which may be used for this type of task
  2128. // Keys occurring in both hashes
  2129. $common = array_intersect_key($h1, $h2);
  2130. // Keys occurring in the first hash [left side], but not in the second hash
  2131. $this_not_that = array_diff_key($h1, $h2);
  2132. // ----------------------------
  2133. $food_colour = array('Apple' => 'red', 'Banana' => 'yellow',
  2134. 'Lemon' => 'yellow', 'Carrot' => 'orange');
  2135. $citrus_colour = array('Lemon' => 'yellow', 'Orange' => 'orange', 'Lime' => 'green');
  2136. $non_citrus = array_diff_key($food_colour, $citrus_colour);
  2137. // @@PLEAC@@_5.12
  2138. // PHP implements a special type known as a 'resource' that encompasses things like file handles,
  2139. // sockets, database connections, and many others. The 'resource' type is, essentially, a
  2140. // reference variable that is not readily serialisable. That is to say:
  2141. // * A 'resource' may be converted to a string representation via the 'var_export' function
  2142. // * That same string cannot be converted back into a 'resource'
  2143. // So, in terms of array handling, 'resource' types may be stored as array reference values,
  2144. // but cannot be used as keys.
  2145. //
  2146. // I suspect it is this type of problem that the Perl::Tie package helps resolve. However, since
  2147. // PHP doesn't, AFAIK, sport a similar facility, the examples in this section cannot be
  2148. // implemented using file handles as keys
  2149. $filenames = array('/etc/termcap', '/vmlinux', '/bin/cat');
  2150. foreach($filenames as $filename)
  2151. {
  2152. if (!($fh = fopen($filename, 'r'))) continue;
  2153. // Cannot do this as required by the Perl code:
  2154. // $name[$fh] = $filename;
  2155. // Ok
  2156. $name[$filename] = $fh;
  2157. }
  2158. // Would traverse array via:
  2159. //
  2160. // foreach(array_keys($name) as $fh)
  2161. // ...
  2162. // or
  2163. //
  2164. // foreach($name as $fh => $filename)
  2165. // ...
  2166. // but since '$fh' cannot be a key, either of these will work:
  2167. //
  2168. // foreach($name as $filename => $fh)
  2169. // or
  2170. foreach(array_values($name) as $fh)
  2171. {
  2172. fclose($fh);
  2173. }
  2174. // @@PLEAC@@_5.13
  2175. // PHP hashes are dynamic expanding and contracting as entries are added, and removed,
  2176. // respectively. Thus, there is no need to presize a hash, nor is there, AFAIK, any
  2177. // means of doing so except by the number of datums used when defining the hash
  2178. // zero elements
  2179. $hash = array();
  2180. // ------------
  2181. // three elements
  2182. $hash = array('Apple' => 'red', 'Lemon' => 'yellow', 'Carrot' => 'orange');
  2183. // @@PLEAC@@_5.14
  2184. foreach($array as $element) $count[$element] += 1;
  2185. // @@PLEAC@@_5.15
  2186. $father = array('Cain' => 'Adam', 'Abel' => 'Adam', 'Seth' => 'Adam', 'Enoch' => 'Cain',
  2187. 'Irad' => 'Enoch', 'Mehujael' => 'Irad', 'Methusael'=> 'Mehujael',
  2188. 'Lamech' => 'Methusael', 'Jabal' => 'Lamech', 'Jubal' => 'Lamech',
  2189. 'Tubalcain' => 'Lamech', 'Enos' => 'Seth');
  2190. // ------------
  2191. $name = trim(fgets(STDIN));
  2192. while (!feof(STDIN))
  2193. {
  2194. while (TRUE)
  2195. {
  2196. echo "$name\n";
  2197. // Can use either:
  2198. if (!isset($father[$name])) break;
  2199. $name = $father[$name];
  2200. // or:
  2201. // if (!key_exists($name, $father)) break;
  2202. // $name = $father[$name];
  2203. // or combine the two lines:
  2204. // if (!($name = $father[$name])) break;
  2205. }
  2206. echo "\n";
  2207. $name = trim(fgets(STDIN));
  2208. }
  2209. // ----------------------------
  2210. define(SEP, ' ');
  2211. foreach($father as $child => $parent)
  2212. {
  2213. if (!$children[$parent])
  2214. $children[$parent] = $child;
  2215. else
  2216. $children[$parent] .= SEP . $child;
  2217. }
  2218. $name = trim(fgets(STDIN));
  2219. while (!feof(STDIN))
  2220. {
  2221. echo $name . ' begat ';
  2222. if (!$children[$name])
  2223. echo "Nothing\n"
  2224. else
  2225. echo str_replace(SEP, ', ', $children[$name]) . "\n";
  2226. $name = trim(fgets(STDIN));
  2227. }
  2228. // ----------------------------
  2229. define(SEP, ' ');
  2230. $files = array('/tmp/a', '/tmp/b', '/tmp/c');
  2231. foreach($files as $file)
  2232. {
  2233. if (!is_file($file)) { echo "Skipping {$file}\n"; continue; }
  2234. if (!($fh = fopen($file, 'r'))) { echo "Skipping {$file}\n"; continue; }
  2235. $line = fgets($fh);
  2236. while (!feof($fh))
  2237. {
  2238. if (preg_match('/^\s*#\s*include\s*<([^>]+)>/', $line, $matches))
  2239. {
  2240. if (isset($includes[$matches[1]]))
  2241. $includes[$matches[1]] .= SEP . $file;
  2242. else
  2243. $includes[$matches[1]] = $file;
  2244. }
  2245. $line = fgets($fh);
  2246. }
  2247. fclose($fh);
  2248. }
  2249. print_r($includes);
  2250. // @@PLEAC@@_5.16
  2251. // @@INCOMPLETE@@
  2252. // @@INCOMPLETE@@
  2253. // @@PLEAC@@_9.0
  2254. $entry = stat('/bin/vi');
  2255. $entry = stat('/usr/bin');
  2256. $entry = stat($argv[1]);
  2257. // ------------
  2258. $entry = stat('/bin/vi');
  2259. $ctime = $entry['ctime'];
  2260. $size = $entry['size'];
  2261. // ----------------------------
  2262. // For the simple task of determining whether a file contains, text', a simple
  2263. // function that searches for a newline could be implemented. Not exactly
  2264. // foolproof, but very simple, low overhead, no installation headaches ...
  2265. function containsText($file)
  2266. {
  2267. $status = FALSE;
  2268. if (($fp = fopen($file, 'r')))
  2269. {
  2270. while (FALSE !== ($char = fgetc($fp)))
  2271. {
  2272. if ($char == "\n") { $status = TRUE; break; }
  2273. }
  2274. fclose($fp);
  2275. }
  2276. return $status;
  2277. }
  2278. // PHP offers the [currently experimental] Fileinfo group of functions to
  2279. // determine file types based on their contents / 'magic numbers'. This
  2280. // is functionality similar to the *NIX, 'file' utility. Note that it must
  2281. // first be installed using the PEAR utility [see PHP documentation]
  2282. function isTextFile($file)
  2283. {
  2284. // Note: untested code, but I believe this is how it is supposed to work
  2285. $finfo = finfo_open(FILEINFO_NONE);
  2286. $status = (finfo_file($finfo, $file) == 'ASCII text');
  2287. finfo_close($finfo);
  2288. return $status;
  2289. }
  2290. // Alternatively, use the *NIX utility, 'file', directly
  2291. function isTextFile($file)
  2292. {
  2293. return exec(trim('file -bN ' . escapeshellarg($file))) == 'ASCII text';
  2294. }
  2295. // ----
  2296. containsText($argv[1]) || die("File {$argv[1]} doesn't have any text in it\n");
  2297. isTextFile($argv[1]) || die("File {$argv[1]} doesn't have any text in it\n");
  2298. // ----------------------------
  2299. $dirname = '/usr/bin/';
  2300. ($dirhdl = opendir($dirname)) || die("Couldn't open {$dirname}\n");
  2301. while (($file = readdir($dirhdl)) !== FALSE)
  2302. {
  2303. printf("Inside %s is something called: %s\n", $dirname, $file);
  2304. }
  2305. closedir($dirhdl);
  2306. // @@PLEAC@@_9.1
  2307. $filename = 'example.txt';
  2308. // Get the file's current access and modification time, respectively
  2309. $fs = stat($filename);
  2310. $readtime = $fs['atime'];
  2311. $writetime = $fs['mtime'];
  2312. // Alter $writetime, and $readtime ...
  2313. // Update file timestamp
  2314. touch($filename, $writetime, $readtime);
  2315. // ----------------------------
  2316. $filename = 'example.txt';
  2317. // Get the file's current access and modification time, respectively
  2318. $fs = stat($filename);
  2319. $atime = $fs['atime'];
  2320. $mtime = $fs['mtime'];
  2321. // Dedicated functions also exist to retrieve this information:
  2322. //
  2323. // $atime = $fileatime($filename);
  2324. // $mtime = $filemtime($filename);
  2325. //
  2326. // Perform date arithmetic. Traditional approach where arithmetic is performed
  2327. // directly with Epoch Seconds [i.e. the *NIX time stamp value] will work ...
  2328. define('SECONDS_PER_DAY', 60 * 60 * 24);
  2329. // Set file's access and modification times to 1 week ago
  2330. $atime -= 7 * SECONDS_PER_DAY;
  2331. $mtime -= 7 * SECONDS_PER_DAY;
  2332. // ... but care must be taken to account for daylight saving. Therefore, the
  2333. // recommended approach is to use library functions to perform such tasks:
  2334. $atime = strtotime('-7 days', $atime);
  2335. $mtime = strtotime('-7 days', $mtime);
  2336. // Update file timestamp
  2337. touch($filename, $mtime, $atime);
  2338. // Good idea to clear the cache after such updates have occurred so fresh
  2339. // values will be retrieved on next access
  2340. clearstatcache();
  2341. // ----------------------------
  2342. $argc == 2 || die("usage: {$argv[0]} filename\n");
  2343. $filename = $argv[1];
  2344. $fs = stat($filename);
  2345. $atime = $fs['atime'];
  2346. $mtime = $fs['mtime'];
  2347. // Careful here: since interactive, use, 'system', not 'exec', to launch [latter
  2348. // does not work under *NIX - at least, not for me :)]
  2349. system(trim(getenv('EDITOR') . ' vi ' . escapeshellarg($filename)), $retcode);
  2350. touch($filename, $mtime, $atime) || die("Error updating timestamp on file, {$filename}!\n");
  2351. // @@PLEAC@@_9.2
  2352. // The 'unlink' function is used to delete regular files, whilst the 'rmdir' function
  2353. // does the same on non-empty directories. AFAIK, no recursive-deletion facility
  2354. // exists, and must be manually programmed
  2355. $filename = '...';
  2356. @unlink($filename) || die("Can't delete, {$filename}!\n");
  2357. // ------------
  2358. $files = glob('...');
  2359. $problem = FALSE;
  2360. // Could simply use a foreach loop
  2361. foreach($files as $filename) { @unlink($filename) || $problem = TRUE; }
  2362. //
  2363. // Alternatively, an applicative approach could be used, one closer in spirit to
  2364. // largely-functional languages like Scheme
  2365. //
  2366. // function is_all_deleted($deleted, $filename) { return @unlink($filename) && $deleted; }
  2367. // $problem = !array_reduce($files, 'is_all_deleted', TRUE);
  2368. //
  2369. if ($problem)
  2370. {
  2371. fwrite(STDERR, 'Could not delete all of:');
  2372. foreach($files as $filename) { fwrite(STDERR, ' ' . $filename); }
  2373. fwrite(STDERR, "\n"); exit(1);
  2374. }
  2375. // ------------
  2376. function rmAll($files)
  2377. {
  2378. $count = 0;
  2379. foreach($files as $filename) { @unlink($filename) && $count++; };
  2380. return $count;
  2381. // An applicative alternative using 'create_function', PHP's rough equivalent of 'lambda' ...
  2382. //
  2383. // return array_reduce($files,
  2384. // create_function('$count, $filename', 'return @unlink($filename) && $count++;'), 0);
  2385. }
  2386. // ----
  2387. $files = glob('...');
  2388. $toBeDeleted = sizeof($files);
  2389. $count = rmAll($files);
  2390. ($count == $toBeDeleted) || die("Could only delete {$count} of {$toBeDeleted} files\n");
  2391. // @@PLEAC@@_9.3
  2392. $oldfile = '/tmp/old'; $newfile = '/tmp/new';
  2393. copy($oldfile, $newfile) || die("Error copying file\n");
  2394. // ----------------------------
  2395. // All the following copy a file by copying its contents. Examples do so in a single
  2396. // operation, but it is also possible to copy arbitrary blocks, or, line-by-line in
  2397. // the case of 'text' files
  2398. $oldfile = '/tmp/old'; $newfile = '/tmp/new';
  2399. if (is_file($oldfile))
  2400. file_put_contents($newfile, file_get_contents($oldfile));
  2401. else
  2402. die("Problem copying file {$oldfile} to file {$newfile}\n");
  2403. // ------------
  2404. $oldfile = '/tmp/old'; $newfile = '/tmp/new';
  2405. fwrite(($nh = fopen($newfile, 'wb')), fread(($oh = fopen($oldfile, 'rb')), filesize($oldfile)));
  2406. fclose($oh);
  2407. fclose($nh);
  2408. // ------------
  2409. // As above, but with some error checking / handling
  2410. $oldfile = '/tmp/old'; $newfile = '/tmp/new';
  2411. ($oh = fopen($oldfile, 'rb')) || die("Problem opening input file {$oldfile}\n");
  2412. ($nh = fopen($newfile, 'wb')) || die("Problem opening output file {$newfile}\n");
  2413. if (($filesize = filesize($oldfile)) > 0)
  2414. {
  2415. fwrite($nh, fread($oh, $filesize)) || die("Problem reading / writing file data\n");
  2416. }
  2417. fclose($oh);
  2418. fclose($nh);
  2419. // ----------------------------
  2420. // Should there be platform-specfic problems copying 'very large' files, it is
  2421. // a simple matter to call a system command utility via, 'exec'
  2422. // *NIX-specific example. Could check whether, 'exec', succeeded, but checking whether
  2423. // a file exists after the operation might be a better approach
  2424. $oldfile = '/tmp/old'; $newfile = '/tmp/new';
  2425. is_file($newfile) && unlink($newfile);
  2426. exec(trim('cp --force ' . escapeshellarg($oldfile) . ' ' . escapeshellarg($newfile)));
  2427. is_file($newfile) || die("Problem copying file {$oldfile} to file {$newfile}\n");
  2428. // For other operating systems just change:
  2429. // * filenames
  2430. // * command being 'exec'ed
  2431. // as the rest of the code is platform independant
  2432. // @@PLEAC@@_9.4
  2433. function makeDevInodePair($filename)
  2434. {
  2435. if (!($fs = @stat($filename))) return FALSE;
  2436. return strval($fs['dev'] . $fs['ino']);
  2437. }
  2438. // ------------
  2439. function do_my_thing($filename)
  2440. {
  2441. // Using a global variable to mimic Perl example, but could easily have passed
  2442. // '$seen' as an argument
  2443. global $seen;
  2444. $devino = makeDevInodePair($filename);
  2445. // Process $filename if it has not previously been seen, else just increment
  2446. if (!isset($seen[$devino]))
  2447. {
  2448. // ... process $filename ...
  2449. // Set initial count
  2450. $seen[$devino] = 1;
  2451. }
  2452. else
  2453. {
  2454. // Otherwise, just increment the count
  2455. $seen[$devino] += 1;
  2456. }
  2457. }
  2458. // ----
  2459. // Simple example
  2460. $seen = array();
  2461. do_my_thing('/tmp/old');
  2462. do_my_thing('/tmp/old');
  2463. do_my_thing('/tmp/old');
  2464. do_my_thing('/tmp/new');
  2465. foreach($seen as $devino => $count)
  2466. {
  2467. echo "{$devino} -> {$count}\n";
  2468. }
  2469. // ------------
  2470. // A variation on the above avoiding use of global variables, and illustrating use of
  2471. // easily-implemented 'higher order' techniques
  2472. // Helper function loosely modelled on, 'array_reduce', but using an array as
  2473. // 'accumulator', which is returned on completion
  2474. function array_update($arr, $lambda, $updarr)
  2475. {
  2476. foreach($arr as $key) $lambda($updarr, $key);
  2477. return $updarr;
  2478. }
  2479. function do_my_thing(&$seen, $filename)
  2480. {
  2481. if (!array_key_exists(($devino = makeDevInodePair($filename)), $seen))
  2482. {
  2483. // ... processing $filename ...
  2484. // Update $seen
  2485. $seen[$devino] = 1;
  2486. }
  2487. else
  2488. {
  2489. // Update $seen
  2490. $seen[$devino] += 1;
  2491. }
  2492. }
  2493. // ----
  2494. // Simple example
  2495. $files = array('/tmp/old', '/tmp/old', '/tmp/old', '/tmp/new');
  2496. // Could do this ...
  2497. $seen = array();
  2498. array_update($files, 'do_my_thing', &$seen);
  2499. // or this:
  2500. $seen = array_update($files, 'do_my_thing', array());
  2501. // or a 'lambda' could be used:
  2502. array_update($files,
  2503. create_function('$seen, $filename', '... code not shown ...'),
  2504. &$seen);
  2505. foreach($seen as $devino => $count)
  2506. {
  2507. echo "{$devino} -> {$count}\n";
  2508. }
  2509. // ----------------------------
  2510. $files = glob('/tmp/*');
  2511. define(SEP, ';');
  2512. $seen = array();
  2513. foreach($files as $filename)
  2514. {
  2515. if (!array_key_exists(($devino = makeDevInodePair($filename)), $seen))
  2516. $seen[$devino] = $filename;
  2517. else
  2518. $seen[$devino] = $seen[$devino] . SEP . $filename;
  2519. }
  2520. $devino = array_keys($seen);
  2521. sort($devino);
  2522. foreach($devino as $key)
  2523. {
  2524. echo $key . ':';
  2525. foreach(split(SEP, $seen[$key]) as $filename) echo ' ' . $filename;
  2526. echo "\n";
  2527. }
  2528. // @@PLEAC@@_9.5
  2529. // Conventional POSIX-like approach to directory traversal
  2530. $dirname = '/usr/bin/';
  2531. ($dirhdl = opendir($dirname)) || die("Couldn't open {$dirname}\n");
  2532. while (($file = readdir($dirhdl)) !== FALSE)
  2533. {
  2534. ; // ... do something with $dirname/$file
  2535. // ...
  2536. }
  2537. closedir($dirhdl);
  2538. // ------------
  2539. // Newer [post PHP 4], 'applicative' approach - an array of filenames is
  2540. // generated that may be processed via external loop ...
  2541. $dirname = '/usr/bin/';
  2542. foreach(scandir($dirname) as $file)
  2543. {
  2544. ; // ... do something with $dirname/$file
  2545. // ...
  2546. }
  2547. // .. or, via callback application, perhaps after massaging by one of the
  2548. // 'array' family of functions [also uses, 'array_update', from earlier section]
  2549. $newlist = array_update(array_reverse(scandir($dirname)),
  2550. create_function('$filelist, $file', ' ; '),
  2551. array());
  2552. // And don't forget that the old standby, 'glob', that returns an array of
  2553. // paths filtered using the Bourne Shell-based wildcards, '?' and '*', is
  2554. // also available
  2555. foreach(glob($dirname . '*') as $path)
  2556. {
  2557. ; // ... do something with $path
  2558. // ...
  2559. }
  2560. // ----------------------------
  2561. // Uses, 'isTextFile', from an earlier section
  2562. $dirname = '/usr/bin/';
  2563. echo "Text files in {$dirname}:\n";
  2564. foreach(scandir($dirname) as $file)
  2565. {
  2566. // Take care when constructing paths to ensure cross-platform operability
  2567. $path = $dirname . $file;
  2568. if (is_file($path) && isTextFile($path)) echo $path . "\n";
  2569. }
  2570. // ----------------------------
  2571. function plain_files($dirname)
  2572. {
  2573. ($dirlist = glob($dirname . '*')) || die("Couldn't glob {$dirname}\n");
  2574. // Pass function name directly if only a single function performs filter test
  2575. return array_filter($dirlist, 'is_file');
  2576. // Use, 'create_function', if a multi-function test is needed
  2577. //
  2578. // return array_filter($dirlist, create_function('$path', 'return is_file($path);'));
  2579. //
  2580. }
  2581. // ------------
  2582. foreach(plain_files('/tmp/') as $path)
  2583. {
  2584. echo $path . "\n";
  2585. }
  2586. // @@PLEAC@@_9.6
  2587. $dirname = '/tmp/';
  2588. // Full paths
  2589. $pathlist = glob($dirname . '*.c');
  2590. // File names only - glob-based matching
  2591. $filelist = array_filter(scandir($dirname),
  2592. create_function('$file', 'return fnmatch("*.c", $file);'));
  2593. // ----------------------------
  2594. $dirname = '/tmp/';
  2595. // File names only - regex-based matching [case-insensitive]
  2596. $filelist = array_filter(scandir($dirname),
  2597. create_function('$file', 'return eregi("\.[ch]$", $file);'));
  2598. // ----------------------------
  2599. $dirname = '/tmp/';
  2600. // Directory names - all-digit names
  2601. $dirs = array_filter(glob($dirname . '*', GLOB_ONLYDIR),
  2602. create_function('$path', 'return ereg("^[0-9]+$", basename($path));'));
  2603. // @@PLEAC@@_9.7
  2604. // Recursive directory traversal function and helper: traverses a directory tree
  2605. // applying a function [and a variable number of accompanying arguments] to each
  2606. // file
  2607. class Accumulator
  2608. {
  2609. public $value;
  2610. public function __construct($start_value) { $this->value = $start_value; }
  2611. }
  2612. // ------------
  2613. function process_directory_($op, $func_args)
  2614. {
  2615. if (is_dir($func_args[0]))
  2616. {
  2617. $current = $func_args[0];
  2618. foreach(scandir($current) as $entry)
  2619. {
  2620. if ($entry == '.' || $entry == '..') continue;
  2621. $func_args[0] = $current . '/' . $entry;
  2622. process_directory_($op, $func_args);
  2623. }
  2624. }
  2625. else
  2626. {
  2627. call_user_func_array($op, $func_args);
  2628. }
  2629. }
  2630. function process_directory($op, $dir)
  2631. {
  2632. if (!is_dir($dir)) return FALSE;
  2633. $func_args = array_slice(func_get_args(), 1);
  2634. process_directory_($op, $func_args);
  2635. return TRUE;
  2636. }
  2637. // ----------------------------
  2638. $dirlist = array('/tmp/d1', '/tmp/d2', '/tmp/d3');
  2639. // Do something with each directory in the list
  2640. foreach($dirlist as $dir)
  2641. {
  2642. ;
  2643. // Delete directory [if empty] -> rmdir($dir);
  2644. // Make it the 'current directory' -> chdir($dir);
  2645. // Get list of files it contains -> $filelist = scandir($dir);
  2646. // Get directory metadata -> $ds = stat($dir);
  2647. }
  2648. // ------------
  2649. $dirlist = array('/tmp/d1', '/tmp/d2', '/tmp/d3');
  2650. function pf($path)
  2651. {
  2652. // ... do something to the file or directory ...
  2653. printf("%s\n", $path);
  2654. }
  2655. // For each directory in the list ...
  2656. foreach($dirlist as $dir)
  2657. {
  2658. // Is this a valid directory ?
  2659. if (!is_dir($dir)) { printf("%s does not exist\n", $dir); continue; }
  2660. // Ok, so get all the directory's entries
  2661. $filelist = scandir($dir);
  2662. // An 'empty' directory will contain at least two entries: '..' and '.'
  2663. if (count($filelist) == 2) { printf("%s is empty\n", $dir); continue; }
  2664. // For each file / directory in the directory ...
  2665. foreach($filelist as $file)
  2666. {
  2667. // Ignore '..' and '.' entries
  2668. if ($file == '.' || $file == '..') continue;
  2669. // Apply function to process the file / directory
  2670. pf($dir . '/' . $file);
  2671. }
  2672. }
  2673. // ----------------------------
  2674. function accum_filesize($file, $accum)
  2675. {
  2676. is_file($file) && ($accum->value += filesize($file));
  2677. }
  2678. // ------------
  2679. // Verify arguments ...
  2680. $argc == 2 || die("usage: {$argv[0]} dir\n");
  2681. $dir = $argv[1];
  2682. is_dir($dir) || die("{$dir} does not exist / not a directory\n");
  2683. // Collect data [use an object to accumulate results]
  2684. $dirsize = new Accumulator(0);
  2685. process_directory('accum_filesize', $dir, $dirsize);
  2686. // Report results
  2687. printf("%s contains %d bytes\n", $dir, $dirsize->value);
  2688. // ----------------------------
  2689. function biggest_file($file, $accum)
  2690. {
  2691. if (is_file($file))
  2692. {
  2693. $fs = filesize($file);
  2694. if ($accum->value[1] < $fs) { $accum->value[0] = $file; $accum->value[1] = $fs; }
  2695. }
  2696. }
  2697. // ------------
  2698. // Verify arguments ...
  2699. $argc == 2 || die("usage: {$argv[0]} dir\n");
  2700. $dir = $argv[1];
  2701. is_dir($dir) || die("{$dir} does not exist / not a directory\n");
  2702. // Collect data [use an object to accumulate results]
  2703. $biggest = new Accumulator(array('', 0));
  2704. process_directory('biggest_file', $dir, $biggest);
  2705. // Report results
  2706. printf("Biggest file is %s containing %d bytes\n", $biggest->value[0], $biggest->value[1]);
  2707. // ----------------------------
  2708. function youngest_file($file, $accum)
  2709. {
  2710. if (is_file($file))
  2711. {
  2712. $fct = filectime($file);
  2713. if ($accum->value[1] > $fct) { $accum->value[0] = $file; $accum->value[1] = $fct; }
  2714. }
  2715. }
  2716. // ------------
  2717. // Verify arguments ...
  2718. $argc == 2 || die("usage: {$argv[0]} dir\n");
  2719. $dir = $argv[1];
  2720. is_dir($dir) || die("{$dir} does not exist / not a directory\n");
  2721. // Collect data [use an object to accumulate results]
  2722. $youngest = new Accumulator(array('', 2147483647));
  2723. process_directory('youngest_file', $dir, $youngest);
  2724. // Report results
  2725. printf("Youngest file is %s dating %s\n", $youngest->value[0], date(DATE_ATOM, $youngest->value[1]));
  2726. // @@PLEAC@@_9.8
  2727. // AFAICT, there is currently no library function that recursively removes a
  2728. // directory tree [i.e. a directory, it's subdirectories, and any other files]
  2729. // with a single call. Such a function needs to be custom built. PHP tools
  2730. // with which to do this:
  2731. // * 'unlink', 'rmdir', 'is_dir', and 'is_file' functions, will all take care
  2732. // of the file testing and deletion
  2733. // * Actual directory traversal requires obtaining directory / subdirectory
  2734. // lists, and here there is much choice available, though care must be taken
  2735. // as each has it's own quirks
  2736. // - 'opendir', 'readdir', 'closedir'
  2737. // - 'scandir'
  2738. // - 'glob'
  2739. // - SPL 'directory iterator' classes [newish / experimental - not shown here]
  2740. //
  2741. // The PHP documentation for 'rmdir' contains several examples, each illustrating
  2742. // one of each approach; the example shown here is loosely based on one of these
  2743. // examples
  2744. // Recursor - recursively traverses directory tree
  2745. function rmtree_($dir)
  2746. {
  2747. $dir = "$dir";
  2748. if ($dh = opendir($dir))
  2749. {
  2750. while (FALSE !== ($item = readdir($dh)))
  2751. {
  2752. if ($item != '.' && $item != '..')
  2753. {
  2754. $subdir = $dir . '/' . "$item";
  2755. if (is_dir($subdir)) rmtree_($subdir);
  2756. else @unlink($subdir);
  2757. }
  2758. }
  2759. closedir($dh); @rmdir($dir);
  2760. }
  2761. }
  2762. // Launcher - performs validation then starts recursive routine
  2763. function rmtree($dir)
  2764. {
  2765. if (is_dir($dir))
  2766. {
  2767. (substr($dir, -1, 1) == '/') && ($dir = substr($dir, 0, -1));
  2768. rmtree_($dir); return !is_dir($dir);
  2769. }
  2770. return FALSE;
  2771. }
  2772. // ------------
  2773. $argc == 2 || die("usage: rmtree dir\n");
  2774. rmtree($argv[1]) || die("Could not remove directory {$argv[1]}\n");
  2775. // @@PLEAC@@_9.9
  2776. $filepairs = array('x.txt' => 'x2.txt', 'y.txt' => 'y.doc', 'zxc.txt' => 'cxz.txt');
  2777. foreach($filepairs as $oldfile => $newfile)
  2778. {
  2779. @rename($oldfile, $newfile) || fwrite(STDERR, sprintf("Could not rename %s to %s\n", $oldfile, $newfile));
  2780. }
  2781. // ----------------------------
  2782. // Call a system command utility via, 'exec'. *NIX-specific example. Could check whether,
  2783. // 'exec', succeeded, but checking whether a renamed file exists after the operation might
  2784. // be a better approach
  2785. $oldfile = '/tmp/old'; $newfile = '/tmp/new';
  2786. is_file($newfile) && unlink($newfile);
  2787. exec(trim('mv --force ' . escapeshellarg($oldfile) . ' ' . escapeshellarg($newfile)));
  2788. is_file($oldfile) || die("Problem renaming file {$oldfile} to file {$newfile}\n");
  2789. // For other operating systems just change:
  2790. // * filenames
  2791. // * command being 'exec'ed
  2792. // as the rest of the code is platform independant
  2793. // ----------------------------
  2794. // A modified implementation of Larry's Filename Fixer. Rather than passing
  2795. // a single expression, a 'from' regexp is passed; each match in the file
  2796. // name(s) is changed to the value of 'to'. It otherwise behaves the same
  2797. //
  2798. $argc > 2 || die("usage: rename from to [file ...]\n");
  2799. $from = $argv[1];
  2800. $to = $argv[2];
  2801. if (count(($argv = array_slice($argv, 3))) < 1)
  2802. while (!feof(STDIN)) $argv[] = substr(fgets(STDIN), 0, -1);
  2803. foreach($argv as $file)
  2804. {
  2805. $was = $file;
  2806. $file = ereg_replace($from, $to, $file);
  2807. if (strcmp($was, $file) != 0)
  2808. @rename($was, $file) || fwrite(STDERR, sprintf("Could not rename %s to %s\n", $was, $file));
  2809. }
  2810. // @@PLEAC@@_9.10
  2811. $base = basename($path);
  2812. $dir = dirname($path);
  2813. // PHP's equivalent to Perl's 'fileparse'
  2814. $pathinfo = pathinfo($path);
  2815. $base = $pathinfo['basename'];
  2816. $dir = $pathinfo['dirname'];
  2817. $ext = $pathinfo['extension'];
  2818. // ----------------------------
  2819. $path = '/usr/lib/libc.a';
  2820. printf("dir is %s, file is %s\n", dirname($path), basename($path));
  2821. // ------------
  2822. $path = '/usr/lib/libc.a';
  2823. $pathinfo = pathinfo($path);
  2824. printf("dir is %s, name is %s, extension is %s\n", $pathinfo['dirname'], $pathinfo['basename'], $pathinfo['extension']);
  2825. // ----------------------------
  2826. // Handle Mac example as a simple parse task. However, AFAIK, 'pathinfo' is cross-platform,
  2827. // so should handle file path format differences transparently
  2828. $path = 'Hard%20Drive:System%20Folder:README.txt';
  2829. $macp = array_combine(array('drive', 'folder', 'filename'), split("\:", str_replace('%20', ' ', $path)));
  2830. $macf = array_combine(array('name', 'extension'), split("\.", $macp['filename']));
  2831. printf("dir is %s, name is %s, extension is %s\n", ($macp['drive'] . ':' . $macp['folder']), $macf['name'], ('.' . $macf['extension']));
  2832. // ----------------------------
  2833. // Not really necessary since we have, 'pathinfo', but better matches Perl example
  2834. function file_extension($filename, $separator = '.')
  2835. {
  2836. return end(split(("\\" . $separator), $filename));
  2837. }
  2838. // ----
  2839. echo file_extension('readme.txt') . "\n";
  2840. // @@PLEAC@@_9.11
  2841. // @@INCOMPLETE@@
  2842. // @@INCOMPLETE@@
  2843. // @@PLEAC@@_9.12
  2844. // @@INCOMPLETE@@
  2845. // @@INCOMPLETE@@
  2846. // @@PLEAC@@_10.0
  2847. // Since defined at outermost scope, $greeted may be considered a global variable
  2848. $greeted = 0;
  2849. // ------------
  2850. // Must use, 'global', keyword to inform functions that $greeted already exists as
  2851. // a global variable. If this is not done, a local variable of that name is implicitly
  2852. // defined
  2853. function howManyGreetings()
  2854. {
  2855. global $greeted;
  2856. return $greeted;
  2857. }
  2858. function hello()
  2859. {
  2860. global $greeted;
  2861. $greeted++;
  2862. echo "high there!, this procedure has been called {$greeted} times\n";
  2863. }
  2864. // ------------
  2865. hello();
  2866. $greetings = howManyGreetings();
  2867. echo "bye there!, there have been {$greetings} greetings so far\n";
  2868. // @@PLEAC@@_10.1
  2869. // Conventionally-defined function together with parameter list
  2870. function hypotenuse($side1, $side2)
  2871. {
  2872. return sqrt(pow($side1, 2) + pow($side2, 2));
  2873. }
  2874. // ----
  2875. // Alternative is to define the function without parameter list, then use
  2876. // 'func_get_arg' to extract arguments
  2877. function hypotenuse()
  2878. {
  2879. // Could check number of arguments passed with: 'func_num_args', which
  2880. // would be the approach used if dealing with variable number of arguments
  2881. $side1 = func_get_arg(0); $side2 = func_get_arg(1);
  2882. return sqrt(pow($side1, 2) + pow($side2, 2));
  2883. }
  2884. // ------------
  2885. // 1. Conventional function call
  2886. $diag = hypotenuse(3, 4);
  2887. // ------------
  2888. // 2. Function call using, 'call_user_func' library routines
  2889. $funcname = 'hypotenuse';
  2890. // a. Pass function name, and variable number of arguments
  2891. $diag = call_user_func($funcname, 3, 4);
  2892. // b. Package arguments as array, pass together with function name
  2893. $args = array(3, 4);
  2894. $diag = call_user_func_array($funcname, $args);
  2895. // ----------------------------
  2896. $nums = array(1.4, 3.5, 6.7);
  2897. // ------------
  2898. // Pass-by-value
  2899. function int_all($arr)
  2900. {
  2901. return array_map(create_function('$n', 'return (int) $n;'), $arr);
  2902. }
  2903. // Pass-by-reference
  2904. function trunc_em(&$n)
  2905. {
  2906. foreach ($n as &$value) $value = (int) $value;
  2907. }
  2908. // ------------
  2909. // $nums untouched; $ints is new array
  2910. $ints = int_all($nums);
  2911. // $nums updated
  2912. trunc_em($nums);
  2913. // @@PLEAC@@_10.2
  2914. // Strictly-speaking, PHP is neither lexically [no environment capture] nor
  2915. // dynamically [no variable shadowing] scoped. A script in which several
  2916. // functions have been defined has two, entirely separate, scopes:
  2917. //
  2918. // * A 'top-level' scope i.e. everything outside each function
  2919. //
  2920. // * A 'local scope' within each function; each function is a self-contained
  2921. // entity and cannot [via conventional means] access variables outside its
  2922. // local scope. Accessing a variable that has not been locally defined
  2923. // serves to define it i.e. accessing a variable assumed to be global
  2924. // sees a local variable of that name defined
  2925. //
  2926. // The way 'global' variables are provided is via a predefined array of
  2927. // variable names, $GLOBALS [it is one of a special set of variables known
  2928. // as 'superglobals'; such variables *are* accessable in all scopes]. Each
  2929. // entry in this array is a 'global' variable name, and may be freely
  2930. // accessed / updated. A more convenient means of accessing such variables
  2931. // is via the 'global' keyword: one or more variables within a function is
  2932. // declared 'global', and those names are then taken to refer to entries
  2933. // in the $GLOBALS array rather than seeing local variables of that name
  2934. // accessed or defined
  2935. function some_func()
  2936. {
  2937. // Variables declared within a function are local to that function
  2938. $variable = 'something';
  2939. }
  2940. // ----------------------------
  2941. // Top-level declared variables
  2942. $name = $argv[1]; $age = $argv[2];
  2943. $c = fetch_time();
  2944. $condition = 0;
  2945. // ------------
  2946. function run_check()
  2947. {
  2948. // The globally-declared variable, '$condition', is not accessable within
  2949. // the function unless it declared as 'global. Had this not been done then
  2950. // attempts to access, '$condition', would have seen a local variable
  2951. // of that name declared and updated. Same applies to other variables
  2952. global $condition, $name, $age, $c;
  2953. $condition = 1;
  2954. // ...
  2955. }
  2956. function check_x($x)
  2957. {
  2958. $y = 'whatever';
  2959. // This function only has access to the parameter, '$x', and the locally
  2960. // declared variable, '$y'.
  2961. // Whilst 'run_check' has access to several global variables, the current
  2962. // function does not. For it to access the global variable, '$condition',
  2963. // it must be declared 'global'
  2964. run_check();
  2965. global $condition;
  2966. // 'run_check' will have updated, '$condition', and since it has been
  2967. // declared 'global' here, it is accessable
  2968. if ($condition)
  2969. {
  2970. ; // ...
  2971. }
  2972. }
  2973. // @@PLEAC@@_10.3
  2974. // Local scopes are not created in the same way as in Perl [by simply enclosing
  2975. // within braces]: only via the creation of functions are local scopes created
  2976. // Doesn't create a local scope; '$myvariable' is created at top-level
  2977. {
  2978. $myvariable = 7;
  2979. }
  2980. // '$myvariable' is accessable here
  2981. echo $myvariable . "\n";
  2982. // ------------
  2983. {
  2984. $counter = 0;
  2985. // Local scope created within function, but not within surrounding braces
  2986. // so:
  2987. // * '$counter' is actually a top-level variable, so globally accessable
  2988. // * 'next_counter' has no implict access to '$counter'; must be granted
  2989. // via 'global' keyword
  2990. function next_counter() { global $counter; $counter++; }
  2991. }
  2992. // ----------------------------
  2993. // PHP doesn't, AFAIK, offer an equivalent to Perl's BEGIN block. Similar
  2994. // behaviour may be obtained by defining a class, and including such code
  2995. // in its constructor
  2996. class BEGIN
  2997. {
  2998. private $myvariable;
  2999. function __construct()
  3000. {
  3001. $this->myvariable = 5;
  3002. }
  3003. function othersub()
  3004. {
  3005. echo $this->myvariable . "\n";
  3006. }
  3007. }
  3008. // ------------
  3009. $b = new BEGIN();
  3010. $b->othersub();
  3011. // ----------------------------
  3012. // PHP, like C, supports 'static' local variables, that is, those that upon
  3013. // first access are initialised, and thence retain their value between function
  3014. // calls. However, the 'counter' example is better implemented as a class
  3015. class Counter
  3016. {
  3017. private $counter;
  3018. function __construct($counter_init)
  3019. {
  3020. $this->counter = $counter_init;
  3021. }
  3022. function next_counter() { $this->counter++; return $this->counter; }
  3023. function prev_counter() { $this->counter; return $this->counter; }
  3024. }
  3025. // ------------
  3026. $counter = new Counter(42);
  3027. echo $counter->next_counter() . "\n";
  3028. echo $counter->next_counter() . "\n";
  3029. echo $counter->prev_counter() . "\n";
  3030. // @@PLEAC@@_10.4
  3031. // AFAICT there is no means of obtaining the name of the currently executing
  3032. // function, or, for that matter, perform any stack / activation record,
  3033. // inspection. It *is* possible to:
  3034. //
  3035. // * Obtain a list of the currently-defined functions ['get_defined_functions']
  3036. // * Check whether a specific function exists ['function_exists']
  3037. // * Use the 'Reflection API'
  3038. //
  3039. // So, to solve this problem would seem to require adopting a convention where
  3040. // a string representing the function name is passed as an argument, or a local
  3041. // variable [perhaps called, '$name'] is so set [contrived, and of limited use]
  3042. function whoami()
  3043. {
  3044. $name = 'whoami';
  3045. echo "I am: {$name}\n";
  3046. }
  3047. // ------------
  3048. whoami();
  3049. // @@PLEAC@@_10.5
  3050. // In PHP all items exist as 'memory references' [i.e. non-modifiable pointers],
  3051. // so when passing an item as a function argument, or returning an item from
  3052. // a function, it is this 'memory reference' that is passed, and *not* the
  3053. // contents of that item. Should several references to an item exist [e.g. if
  3054. // passed to a function then at least two such references would exist in
  3055. // different scopes] they would all be refering to the same copy of the item.
  3056. // However, if an attempt is made to alter the item is made, a copy is made
  3057. // and it is the copy that is altered, leaving the original intact.
  3058. //
  3059. // The PHP reference mechanism is used to selectively prevent this behaviour,
  3060. // and ensure that if a change is made to an item that no copy is made, and that
  3061. // it is the original item that is changed. Importantly, there is no efficiency
  3062. // gain from passing function parameters using references if the parameter item
  3063. // is not altered.
  3064. // A copy of the item referred to by, '$arr', is made, and altered; original
  3065. // remains intact
  3066. function array_by_value($arr)
  3067. {
  3068. $arr[0] = 7;
  3069. echo $arr[0] . "\n";
  3070. }
  3071. // No copy is made; original item referred to by, '$arr', is altered
  3072. function array_by_ref(&$arr)
  3073. {
  3074. $arr[0] = 7;
  3075. echo $arr[0] . "\n";
  3076. }
  3077. // ------------
  3078. $arr = array(1, 2, 3);
  3079. echo $arr[0] . "\n"; // output: 1
  3080. array_by_value($arr); // output: 7
  3081. echo $arr[0] . "\n"; // output: 1
  3082. $arr = array(1, 2, 3);
  3083. echo $arr[0] . "\n"; // output: 1
  3084. array_by_ref($arr); // output: 7
  3085. echo $arr[0] . "\n"; // output: 7
  3086. // ----------------------------
  3087. // Since, 'add_vecpair', does not attempt to alter either, '$x' or '$y', it makes
  3088. // no difference whether they are 'passed by value', or 'passed by reference'
  3089. function add_vecpair($x, $y)
  3090. {
  3091. $r = array();
  3092. $length = count($x);
  3093. for($i = 0; $i < $length; $i++) $r[$i] = $x[$i] + $y[$i];
  3094. return $r;
  3095. }
  3096. // ...
  3097. count($arr1) == count($arr2) || die("usage: add_vecpair ARR1 ARR2\n");
  3098. // 'passed by value'
  3099. $arr3 = add_vecpair($arr1, $arr2);
  3100. // 'passed by reference' [also possible to override default 'passed by value'
  3101. // if required]
  3102. $arr3 = add_vecpair(&$arr1, &$arr2);
  3103. // @@PLEAC@@_10.6
  3104. // PHP can be described as a dynamically typed language because variables serve
  3105. // as identifiers, and the same variable may refer to data of various types.
  3106. // As such, the set of arguments passed to a function may vary in type between
  3107. // calls, as can the type of return value. Where this is likely to occur type
  3108. // checking should be performed either / both within the function body, and
  3109. // when obtaining it's return value. As for Perl-style 'return context', I
  3110. // don't believe it is supported by PHP
  3111. // Can return any type
  3112. function mysub()
  3113. {
  3114. // ...
  3115. return 5;
  3116. // ...
  3117. return array(5);
  3118. // ...
  3119. return '5';
  3120. }
  3121. // Throw away return type [i.e. returns a 'void' type ?]
  3122. mysub();
  3123. // Check return type. Can do via:
  3124. // * gettype($var)
  3125. // * is_xxx e.g. is_array($var), is_muneric($var), ...
  3126. $ret = mysub();
  3127. if (is_numeric($ret))
  3128. {
  3129. ; // ...
  3130. }
  3131. if (is_array($ret))
  3132. {
  3133. ; // ...
  3134. }
  3135. if (is_string($ret))
  3136. {
  3137. ; // ...
  3138. }
  3139. // @@PLEAC@@_10.7
  3140. // PHP doesn't directly support named / keyword parameters, but these can be
  3141. // easily mimiced using a class of key / value pairs, and passing a variable
  3142. // number of arguments
  3143. class KeyedValue
  3144. {
  3145. public $key, $value;
  3146. public function __construct($key, $value) { $this->key = $key; $this->value = $value; }
  3147. }
  3148. function the_func()
  3149. {
  3150. foreach (func_get_args() as $arg)
  3151. {
  3152. printf("Key: %10s|Value:%10s\n", $arg->key, $arg->value);
  3153. }
  3154. }
  3155. // ----
  3156. the_func(new KeyedValue('name', 'Bob'),
  3157. new KeyedValue('age', 36),
  3158. new KeyedValue('income', 51000));
  3159. // ----------------------------
  3160. // Alternatively, an associative array of key / value pairs may be constructed.
  3161. // With the aid of the 'extract' built-in function, the key part of this array
  3162. // may be intsntiated to a variable name, thus more closely approximating the
  3163. // behaviour of true named parameters
  3164. function the_func($var_array)
  3165. {
  3166. extract($var_array);
  3167. if (isset($name)) printf("Name: %s\n", $name);
  3168. if (isset($age)) printf("Age: %s\n", $age);
  3169. if (isset($income)) printf("Income: %s\n", $income);
  3170. }
  3171. // ----
  3172. the_func(array('name' => 'Bob', 'age' => 36, 'income' => 51000));
  3173. // ----------------------------
  3174. class RaceTime
  3175. {
  3176. public $time, $dim;
  3177. public function __construct($time, $dim) { $this->time = $time; $this->dim = $dim; }
  3178. }
  3179. function the_func($var_array)
  3180. {
  3181. extract($var_array);
  3182. if (isset($start_time)) printf("start: %d - %s\n", $start_time->time, $start_time->dim);
  3183. if (isset($finish_time)) printf("finish: %d - %s\n", $finish_time->time, $finish_time->dim);
  3184. if (isset($incr_time)) printf("incr: %d - %s\n", $incr_time->time, $incr_time->dim);
  3185. }
  3186. // ----
  3187. the_func(array('start_time' => new RaceTime(20, 's'),
  3188. 'finish_time' => new RaceTime(5, 'm'),
  3189. 'incr_time' => new RaceTime(3, 'm')));
  3190. the_func(array('start_time' => new RaceTime(5, 'm'),
  3191. 'finish_time' => new RaceTime(30, 'm')));
  3192. the_func(array('start_time' => new RaceTime(30, 'm')));
  3193. // @@PLEAC@@_10.8
  3194. // The 'list' keyword [looks like a function but is actually a special language
  3195. // construct] may be used to perform multiple assignments from a numerically
  3196. // indexed array of values, and offers the added bonus of being able to skip
  3197. // assignment of one, or more, of those values
  3198. function func() { return array(3, 6, 9); }
  3199. // ------------
  3200. list($a, $b, $c) = array(6, 7, 8);
  3201. // Provided 'func' returns an numerically-indexed array, the following
  3202. // multiple assignment will work
  3203. list($a, $b, $c) = func();
  3204. // Any existing variables no longer wanted would need to be 'unset'
  3205. unset($b);
  3206. // As above, but second element of return array discarded
  3207. list($a,,$c) = func();
  3208. // ----------------------------
  3209. // Care needed to ensure returned array is numerically-indexed
  3210. list($dev, $ino,,,$uid) = array_slice(array_values(stat($filename)), 0, 13);
  3211. // @@PLEAC@@_10.9
  3212. // Multiple return values are possible via packing a set of values within a
  3213. // numerically-indexed array and using 'list' to extract them
  3214. function some_func() { return array(array(1, 2, 3), array('a' => 1, 'b' => 2)); }
  3215. // ------------
  3216. list($arr, $hash) = some_func();
  3217. // ----------------------------
  3218. function some_func(&$arr, &$hash) { return array($arr, $hash); }
  3219. // ------------
  3220. $arrin = array(1, 2, 3); $hashin = array('a' => 1, 'b' => 2);
  3221. list($arr, $hash) = some_func($arrin, $hashin);
  3222. // @@PLEAC@@_10.10
  3223. // AFAICT, most of the PHP library functions are designed to return some required
  3224. // value on success, and FALSE on exit. Whilst it is possible to return NULL, or
  3225. // one of the recognised 'empty' values [e.g. '' or 0 or an empty array etc],
  3226. // FALSE actually seems to be the preferred means of indicating failure
  3227. function a_func() { return FALSE; }
  3228. a_func() || die("Function failed\n");
  3229. if (!a_func()) die("Function failed\n");
  3230. // @@PLEAC@@_10.11
  3231. // Whether PHP is seen to support prototyping depends on the accepted
  3232. // definition of this term:
  3233. //
  3234. // * Prototyping along the lines used in Ada, Modula X, and even C / C++,
  3235. // in which a function's interface is declared separately from its
  3236. // implementation, is *not* supported
  3237. //
  3238. // * Prototyping in which, as part of the function definition, parameter
  3239. // information must be supplied. In PHP a function definition neither
  3240. // parameter, nor return type, information needs to be supplied, though
  3241. // it is usual to see a parameter list supplied [indicates the number,
  3242. // positional order, and optionally, whether a parameter is passed by
  3243. // reference; no type information is present]. In short, prototyping in
  3244. // PHP is optional, and limited
  3245. function func_with_one_arg($arg1)
  3246. {
  3247. ; // ...
  3248. }
  3249. function func_with_two_arg($arg1, $arg2)
  3250. {
  3251. ; // ...
  3252. }
  3253. function func_with_three_arg($arg1, $arg2, $arg3)
  3254. {
  3255. ; // ...
  3256. }
  3257. // The following may be interpreted as meaning a function accepting no
  3258. // arguments:
  3259. function func_with_no_arg()
  3260. {
  3261. ; // ...
  3262. }
  3263. // whilst the following may mean a function taking zero or more arguments
  3264. function func_with_no_arg_information()
  3265. {
  3266. ; // ...
  3267. }
  3268. // @@PLEAC@@_10.12
  3269. // Unlike in Perl, PHP's 'die' [actually an alias for 'exit'] doesn't throw
  3270. // an exception, but instead terminates the script, optionally either
  3271. // returning an integer value to the operating system, or printing a message.
  3272. // So, the following, does not exhibit the same behaviour as the Perl example
  3273. die("some message\n");
  3274. // Instead, like so many modern languages, PHP implements exception handling
  3275. // via the 'catch' and 'throw' keywords. Furthermore, a C++ or Java programmer
  3276. // would find PHP's exception handling facility remarkably similar to those
  3277. // of their respective languages. A simple, canonical example follows:
  3278. // Usual to derive new exception classes from the built-in, 'Exception',
  3279. // class
  3280. class MyException extends Exception
  3281. {
  3282. // ...
  3283. }
  3284. // ...
  3285. try
  3286. {
  3287. // ...
  3288. if ($some_problem_detected) throw new MyException('some message', $some_error_code);
  3289. // ..
  3290. }
  3291. catch (MyException $e)
  3292. {
  3293. ; // ... handle the problem ...
  3294. }
  3295. // ----------------------------
  3296. class FullMoonException extends Exception
  3297. {
  3298. // ...
  3299. }
  3300. // ...
  3301. try
  3302. {
  3303. // ...
  3304. if ($some_problem_detected) throw new FullMoonException('...', $full_moon_error_code);
  3305. // ..
  3306. }
  3307. catch (FullMoonException $e)
  3308. {
  3309. // ... rethrow the exception - will propagate to higher level ...
  3310. throw $e;
  3311. }
  3312. // @@PLEAC@@_10.13
  3313. // Please refer to discussion about PHP scope in section two of this chapter.
  3314. // Briefly, PHP assumes a variable name within a function to be local unless
  3315. // it has been specifically declared with the, 'global', keyword, in which
  3316. // case it refers to a variable in the 'superglobal' array, '$GLOBALS'. Thus,
  3317. // inadvertant variable name shadowing cannot occur since it is it not possible
  3318. // to use the same name to refer to both a local and a global variable. If
  3319. // accessing a global variable care should be taken to not accidentally update
  3320. // it. The techniques used in this section are simply not required.
  3321. // *** NOT TRANSLATED ***
  3322. // @@PLEAC@@_10.14
  3323. // In PHP once a function has been defined it remains defined. In other words,
  3324. // it cannot be undefined / deleted, nor can that particular function name be
  3325. // reused to reference another function body. Even the lambda-like functions
  3326. // created via the 'create_function' built-in, cannot be undefined [they exist
  3327. // until script termination, thus creating too many of these can actually
  3328. // exhaust memory !]. However, since the latter can be assigned to variables,
  3329. // the same variable name can be used to reference difference functions [and
  3330. // when this is done the reference to the previous function is lost (unless
  3331. // deliberately saved), though the function itself continues to exist].
  3332. //
  3333. // If, however, all that is needed is a simple function aliasing facility,
  3334. // then just assign the function name to a variable, and execute using the
  3335. // variable name
  3336. // Original function
  3337. function expand() { echo "expand\n"; }
  3338. // Prove that function exists
  3339. echo (function_exists('expand') ? 'yes' : 'no') . "\n";
  3340. // Use a variable to alias it
  3341. $grow = 'expand';
  3342. // Call function via original name, and variable, respectively
  3343. expand();
  3344. $grow();
  3345. // Remove alias variable
  3346. unset($grow);
  3347. // ----------------------------
  3348. function fred() { echo "fred\n"; }
  3349. $barney = 'fred';
  3350. $barney();
  3351. unset($barney);
  3352. fred();
  3353. // ------------
  3354. $fred = create_function('', 'echo "fred\n";');
  3355. $barney = $fred;
  3356. $barney();
  3357. unset($barney);
  3358. $fred();
  3359. // ----------------------------
  3360. function red($text) { return "<FONT COLOR='red'>$text</FONT>"; }
  3361. echo red('careful here') . "\n";
  3362. // ------------
  3363. $colour = 'red';
  3364. $$colour = create_function('$text', 'global $colour;
  3365. return "<FONT COLOR=\'$colour\'>$text</FONT>";');
  3366. echo $$colour('careful here') . "\n";
  3367. unset($$colour);
  3368. // ----
  3369. $colours = split(' ', 'red blue green yellow orange purple violet');
  3370. foreach ($colours as $colour)
  3371. {
  3372. $$colour = create_function('$text', 'global $colour;
  3373. return "<FONT COLOR=\'$colour\'>$text</FONT>";');
  3374. }
  3375. foreach ($colours as $colour) { echo $$colour("Careful with this $colour, James") . "\n"; }
  3376. foreach ($colours as $colour) { unset($$colour); }
  3377. // @@PLEAC@@_10.15
  3378. // PHP sports an AUTOLOAD facility that is quite easy to use, but, AFAICT, is geared
  3379. // towards the detection of unavailable classes rather than for individual functions.
  3380. // Here is a rudimentary example:
  3381. function __autoload($classname)
  3382. {
  3383. if (!file_exists($classname))
  3384. {
  3385. // Class file does not exist, so handle situation; in this case,
  3386. // issue error message, and exit program
  3387. die("File for class: {$classname} not found - aborting\n");
  3388. }
  3389. else
  3390. {
  3391. // Class file exists, so load it
  3392. require_once $classname;
  3393. }
  3394. }
  3395. // ------------
  3396. // Attempt to instantiate object of undefined class
  3397. new UnknownClassObject();
  3398. // Execution continues here if class exists
  3399. // ...
  3400. // ----------------------------
  3401. // It is also possible to perform [quite extensive] introspection on functions,
  3402. // variables etc, so it is possible to check whether a function exists before
  3403. // executing it, thus allowing a non-existent functions to be searched for and
  3404. // loaded from a source file, or perhaps dynamically defined. An example of what
  3405. // could be described as a custom autoload facility appears below.
  3406. $colours = array('red', 'blue', 'green', 'yellow', 'orange', 'purple', 'violet');
  3407. foreach ($colours as $colour)
  3408. {
  3409. $$colour = create_function('$text', 'global $colour;
  3410. return "<FONT COLOR=\'$colour\'>$text</FONT>";');
  3411. }
  3412. // Let's add a new colour to the list
  3413. array_push($colours, 'chartreuse');
  3414. foreach ($colours as $colour)
  3415. {
  3416. // Checking whether function is defined
  3417. if (!function_exists($$colour))
  3418. {
  3419. // Doesn't exist, so dynamically define it
  3420. $$colour = create_function('$text', 'global $colour;
  3421. return "<FONT COLOR=\'$colour\'>$text</FONT>";');
  3422. // Alternatively, if it exists in a source file, 'include' the file:
  3423. // include 'newcolours.php'
  3424. }
  3425. echo $$colour("Careful with this $colour, James") . "\n";
  3426. }
  3427. foreach ($colours as $colour) unset($$colour);
  3428. // @@PLEAC@@_10.16
  3429. // *** Warning *** Whilst PHP *does* allow functions to be defined within other
  3430. // functions it needs to be clearly understood that these 'inner' functions:
  3431. // * Do not exist until the outer function is called a first time, at which time
  3432. // they then remain defined
  3433. // * Are global in scope, so are accessable outside the function by their name;
  3434. // the fact that they are nested within another function has, AFAICT, no bearing
  3435. // on name resolution
  3436. // * Do not form a closure: the inner function is merely 'parked' within the
  3437. // outer function, and has no implicit access to the outer function's variables
  3438. // or other inner functions
  3439. function outer($arg)
  3440. {
  3441. $x = $arg + 35;
  3442. function inner() { return $x * 19; }
  3443. // *** wrong *** 'inner' returns 0 * 19, not ($arg + 35) * 19
  3444. return $x + inner();
  3445. }
  3446. // ----------------------------
  3447. function outer($arg)
  3448. {
  3449. $x = $arg + 35;
  3450. // No implicit access to outer function scope; any required data must be
  3451. // explicity passed
  3452. function inner($x) { return $x * 19; }
  3453. return $x + inner($x);
  3454. }
  3455. // ------------
  3456. // Equivalent to previously-shown code
  3457. function inner($x)
  3458. {
  3459. return $x * 19;
  3460. }
  3461. function outer($arg)
  3462. {
  3463. $x = $arg + 35;
  3464. return $x + inner($x);
  3465. }
  3466. // @@PLEAC@@_10.17
  3467. // @@INCOMPLETE@@
  3468. // @@INCOMPLETE@@
  3469. // @@PLEAC@@_16.1
  3470. // Run a command and return its results as a string.
  3471. $output_string = shell_exec('program args');
  3472. // Same as above, using backtick operator.
  3473. $output_string = `program args`;
  3474. // Run a command and return its results as a list of strings,
  3475. // one per line.
  3476. $output_lines = array();
  3477. exec('program args', $output_lines);
  3478. // -----------------------------
  3479. // The only way to execute a program without using the shell is to
  3480. // use pcntl_exec(). However, there is no way to do redirection, so
  3481. // you can't capture its output.
  3482. $pid = pcntl_fork();
  3483. if ($pid == -1) {
  3484. die('cannot fork');
  3485. } elseif ($pid) {
  3486. pcntl_waitpid($pid, $status);
  3487. } else {
  3488. // Note that pcntl_exec() automatically prepends the program name
  3489. // to the array of arguments; the program name cannot be spoofed.
  3490. pcntl_exec($program, array($arg1, $arg2));
  3491. }
  3492. // @@PLEAC@@_16.2
  3493. // Run a simple command and retrieve its result code.
  3494. exec("vi $myfile", $output, $result_code);
  3495. // -----------------------------
  3496. // Use the shell to perform redirection.
  3497. exec('cmd1 args | cmd2 | cmd3 >outfile');
  3498. exec('cmd args <infile >outfile 2>errfile');
  3499. // -----------------------------
  3500. // Run a command, handling its result code or signal.
  3501. $pid = pcntl_fork();
  3502. if ($pid == -1) {
  3503. die('cannot fork');
  3504. } elseif ($pid) {
  3505. pcntl_waitpid($pid, $status);
  3506. if (pcntl_wifexited($status)) {
  3507. $status = pcntl_wexitstatus($status);
  3508. echo "program exited with status $status\n";
  3509. } elseif (pcntl_wifsignaled($status)) {
  3510. $signal = pcntl_wtermsig($status);
  3511. echo "program killed by signal $signal\n";
  3512. } elseif (pcntl_wifstopped($status)) {
  3513. $signal = pcntl_wstopsig($status);
  3514. echo "program stopped by signal $signal\n";
  3515. }
  3516. } else {
  3517. pcntl_exec($program, $args);
  3518. }
  3519. // -----------------------------
  3520. // Run a command while blocking interrupt signals.
  3521. $pid = pcntl_fork();
  3522. if ($pid == -1) {
  3523. die('cannot fork');
  3524. } elseif ($pid) {
  3525. // parent catches INT and berates user
  3526. declare(ticks = 1);
  3527. function handle_sigint($signal) {
  3528. echo "Tsk tsk, no process interruptus\n";
  3529. }
  3530. pcntl_signal(SIGINT, 'handle_sigint');
  3531. while (!pcntl_waitpid($pid, $status, WNOHANG)) {}
  3532. } else {
  3533. // child ignores INT and does its thing
  3534. pcntl_signal(SIGINT, SIG_IGN);
  3535. pcntl_exec('/bin/sleep', array('10'));
  3536. }
  3537. // -----------------------------
  3538. // Since there is no direct access to execv() and friends, and
  3539. // pcntl_exec() won't let us supply an alternate program name
  3540. // in the argument list, there is no way to run a command with
  3541. // a different name in the process table.
  3542. // @@PLEAC@@_16.3
  3543. // Transfer control to the shell to run another program.
  3544. pcntl_exec('/bin/sh', array('-c', 'archive *.data'));
  3545. // Transfer control directly to another program.
  3546. pcntl_exec('/path/to/archive', array('accounting.data'));
  3547. // @@PLEAC@@_16.4
  3548. // Handle each line in the output of a process.
  3549. $readme = popen('program arguments', 'r');
  3550. while (!feof($readme)) {
  3551. $line = fgets($readme);
  3552. if ($line === false) break;
  3553. // ...
  3554. }
  3555. pclose($readme);
  3556. // -----------------------------
  3557. // Write to the input of a process.
  3558. $writeme = popen('program arguments', 'w');
  3559. fwrite($writeme, 'data');
  3560. pclose($writeme);
  3561. // -----------------------------
  3562. // Wait for a process to complete.
  3563. $f = popen('sleep 1000000', 'r'); // child goes to sleep
  3564. pclose($f); // and parent goes to lala land
  3565. // -----------------------------
  3566. $writeme = popen('program arguments', 'w');
  3567. fwrite($writeme, "hello\n"); // program will get hello\n on STDIN
  3568. pclose($writeme); // program will get EOF on STDIN
  3569. // -----------------------------
  3570. // Output buffering callback that sends output to the pager.
  3571. function ob_pager($output, $mode) {
  3572. static $pipe;
  3573. if ($mode & PHP_OUTPUT_HANDLER_START) {
  3574. $pager = getenv('PAGER');
  3575. if (!$pager) $pager = '/usr/bin/less'; // XXX: might not exist
  3576. $pipe = popen($pager, 'w');
  3577. }
  3578. fwrite($pipe, $output);
  3579. if ($mode & PHP_OUTPUT_HANDLER_END) {
  3580. pclose($pipe);
  3581. }
  3582. }
  3583. // Redirect standard output to the pager.
  3584. ob_start('ob_pager');
  3585. // Do something useful that writes to standard output, then
  3586. // close the output buffer.
  3587. // ...
  3588. ob_end_flush();
  3589. // @@PLEAC@@_16.5
  3590. // Output buffering: Only display a certain number of lines of output.
  3591. class Head {
  3592. function Head($lines=20) {
  3593. $this->lines = $lines;
  3594. }
  3595. function filter($output, $mode) {
  3596. $result = array();
  3597. $newline = '';
  3598. if (strlen($output) > 0 && $output[strlen($output) - 1] == "\n") {
  3599. $newline = "\n";
  3600. $output = substr($output, 0, -1);
  3601. }
  3602. foreach (explode("\n", $output) as $i => $line) {
  3603. if ($this->lines > 0) {
  3604. $this->lines--;
  3605. $result[] = $line;
  3606. }
  3607. }
  3608. return $result ? implode("\n", $result) . $newline : '';
  3609. }
  3610. }
  3611. // Output buffering: Prepend line numbers to each line of output.
  3612. class Number {
  3613. function Number() {
  3614. $this->line_number = 0;
  3615. }
  3616. function filter($output, $mode) {
  3617. $result = array();
  3618. $newline = '';
  3619. if (strlen($output) > 0 && $output[strlen($output) - 1] == "\n") {
  3620. $newline = "\n";
  3621. $output = substr($output, 0, -1);
  3622. }
  3623. foreach (explode("\n", $output) as $i => $line) {
  3624. $this->line_number++;
  3625. $result[] = $this->line_number . ': ' . $line;
  3626. }
  3627. return implode("\n", $result) . $newline;
  3628. }
  3629. }
  3630. // Output buffering: Prepend "> " to each line of output.
  3631. class Quote {
  3632. function Quote() {
  3633. }
  3634. function filter($output, $mode) {
  3635. $result = array();
  3636. $newline = '';
  3637. if (strlen($output) > 0 && $output[strlen($output) - 1] == "\n") {
  3638. $newline = "\n";
  3639. $output = substr($output, 0, -1);
  3640. }
  3641. foreach (explode("\n", $output) as $i => $line) {
  3642. $result[] = "> $line";
  3643. }
  3644. return implode("\n", $result) . $newline;
  3645. }
  3646. }
  3647. // Use arrays as callbacks to register filter methods.
  3648. ob_start(array(new Head(100), 'filter'));
  3649. ob_start(array(new Number(), 'filter'));
  3650. ob_start(array(new Quote(), 'filter'));
  3651. // Act like /bin/cat.
  3652. while (!feof(STDIN)) {
  3653. $line = fgets(STDIN);
  3654. if ($line === false) break;
  3655. echo $line;
  3656. }
  3657. // Should match number of calls to ob_start().
  3658. ob_end_flush();
  3659. ob_end_flush();
  3660. ob_end_flush();
  3661. // @@PLEAC@@_16.6
  3662. // Process command-line arguments using fopen(). PHP supports URLs for
  3663. // filenames as long as the "allow_url_fopen" configuration option is set.
  3664. //
  3665. // Valid URL protocols include:
  3666. // - http://www.myserver.com/myfile.html
  3667. // - ftp://ftp.myserver.com/myfile.txt
  3668. // - compress.zlib://myfile.gz
  3669. // - php://stdin
  3670. //
  3671. // See http://www.php.net/manual/en/wrappers.php for details.
  3672. //
  3673. $filenames = array_slice($argv, 1);
  3674. if (!$filenames) $filenames = array('php://stdin');
  3675. foreach ($filenames as $filename) {
  3676. $handle = @fopen($filename, 'r');
  3677. if ($handle) {
  3678. while (!feof($handle)) {
  3679. $line = fgets($handle);
  3680. if ($line === false) break;
  3681. // ...
  3682. }
  3683. fclose($handle);
  3684. } else {
  3685. die("can't open $filename\n");
  3686. }
  3687. }
  3688. // @@PLEAC@@_16.7
  3689. $output = `cmd 2>&1`; // with backticks
  3690. // or
  3691. $ph = popen('cmd 2>&1'); // with an open pipe
  3692. while (!feof($ph)) { $line = fgets($ph); } // plus a read
  3693. // -----------------------------
  3694. $output = `cmd 2>/dev/null`; // with backticks
  3695. // or
  3696. $ph = popen('cmd 2>/dev/null'); // with an open pipe
  3697. while (!feof($ph)) { $line = fgets($ph); } // plus a read
  3698. // -----------------------------
  3699. $output = `cmd 2>&1 1>/dev/null`; // with backticks
  3700. // or
  3701. $ph = popen('cmd 2>&1 1>/dev/null'); // with an open pipe
  3702. while (!feof($ph)) { $line = fgets($ph); } // plus a read
  3703. // -----------------------------
  3704. $output = `cmd 3>&1 1>&2 2>&3 3>&-`; // with backticks
  3705. // or
  3706. $ph = popen('cmd 3>&1 1>&2 2>&3 3>&-|'); // with an open pipe
  3707. while (!feof($ph)) { $line = fgets($ph); } // plus a read
  3708. // -----------------------------
  3709. exec('program args 1>/tmp/program.stdout 2>/tmp/program.stderr');
  3710. // -----------------------------
  3711. $output = `cmd 3>&1 1>&2 2>&3 3>&-`;
  3712. // -----------------------------
  3713. $fd3 = $fd1;
  3714. $fd1 = $fd2;
  3715. $fd2 = $fd3;
  3716. $fd3 = null;
  3717. // -----------------------------
  3718. exec('prog args 1>tmpfile 2>&1');
  3719. exec('prog args 2>&1 1>tmpfile');
  3720. // -----------------------------
  3721. // exec('prog args 1>tmpfile 2>&1');
  3722. $fd1 = "tmpfile"; // change stdout destination first
  3723. $fd2 = $fd1; // now point stderr there, too
  3724. // -----------------------------
  3725. // exec('prog args 2>&1 1>tmpfile');
  3726. $fd2 = $fd1; // stderr same destination as stdout
  3727. $fd1 = "tmpfile"; // but change stdout destination
  3728. // @@PLEAC@@_16.8
  3729. // Connect to input and output of a process.
  3730. $proc = proc_open($program,
  3731. array(0 => array('pipe', 'r'),
  3732. 1 => array('pipe', 'w')),
  3733. $pipes);
  3734. if (is_resource($proc)) {
  3735. fwrite($pipes[0], "here's your input\n");
  3736. fclose($pipes[0]);
  3737. echo stream_get_contents($pipes[1]);
  3738. fclose($pipes[1]);
  3739. $result_code = proc_close($proc);
  3740. echo "$result_code\n";
  3741. }
  3742. // -----------------------------
  3743. $all = array();
  3744. $outlines = array();
  3745. $errlines = array();
  3746. exec("( $cmd | sed -e 's/^/stdout: /' ) 2>&1", $all);
  3747. foreach ($all as $line) {
  3748. $pos = strpos($line, 'stdout: ');
  3749. if ($pos !== false && $pos == 0) {
  3750. $outlines[] = substr($line, 8);
  3751. } else {
  3752. $errlines[] = $line;
  3753. }
  3754. }
  3755. print("STDOUT:\n");
  3756. print_r($outlines);
  3757. print("\n");
  3758. print("STDERR:\n");
  3759. print_r($errlines);
  3760. print("\n");
  3761. // @@PLEAC@@_16.9
  3762. $proc = proc_open($cmd,
  3763. array(0 => array('pipe', 'r'),
  3764. 1 => array('pipe', 'w'),
  3765. 2 => array('pipe', 'w')),
  3766. $pipes);
  3767. if (is_resource($proc)) {
  3768. // give end of file to kid, or feed him
  3769. fclose($pipes[0]);
  3770. // read till EOF
  3771. $outlines = array();
  3772. while (!feof($pipes[1])) {
  3773. $line = fgets($pipes[1]);
  3774. if ($line === false) break;
  3775. $outlines[] = rtrim($line);
  3776. }
  3777. // XXX: block potential if massive
  3778. $errlines = array();
  3779. while (!feof($pipes[2])) {
  3780. $line = fgets($pipes[2]);
  3781. if ($line === false) break;
  3782. $errlines[] = rtrim($line);
  3783. }
  3784. fclose($pipes[1]);
  3785. fclose($pipes[2]);
  3786. proc_close($proc);
  3787. print("STDOUT:\n");
  3788. print_r($outlines);
  3789. print("\n");
  3790. print("STDERR:\n");
  3791. print_r($errlines);
  3792. print("\n");
  3793. }
  3794. // -----------------------------
  3795. // cmd3sel - control all three of kids in, out, and error.
  3796. $cmd = "grep vt33 /none/such - /etc/termcap";
  3797. $proc = proc_open($cmd,
  3798. array(0 => array('pipe', 'r'),
  3799. 1 => array('pipe', 'w'),
  3800. 2 => array('pipe', 'w')),
  3801. $pipes);
  3802. if (is_resource($proc)) {
  3803. fwrite($pipes[0], "This line has a vt33 lurking in it\n");
  3804. fclose($pipes[0]);
  3805. $readers = array($pipes[1], $pipes[2]);
  3806. while (stream_select($read=$readers,
  3807. $write=null,
  3808. $except=null,
  3809. 0, 200000) > 0) {
  3810. foreach ($read as $stream) {
  3811. $line = fgets($stream);
  3812. if ($line !== false) {
  3813. if ($stream === $pipes[1]) {
  3814. print "STDOUT: $line";
  3815. } else {
  3816. print "STDERR: $line";
  3817. }
  3818. }
  3819. if (feof($stream)) {
  3820. $readers = array_diff($readers, array($stream));
  3821. }
  3822. }
  3823. }
  3824. fclose($pipes[1]);
  3825. fclose($pipes[2]);
  3826. proc_close($proc);
  3827. }
  3828. // @@PLEAC@@_16.10
  3829. // PHP supports fork/exec/wait but not pipe. However, it does
  3830. // support socketpair, which can do everything pipes can as well
  3831. // as bidirectional communication. The original recipes have been
  3832. // modified here to use socketpair only.
  3833. // -----------------------------
  3834. // pipe1 - use socketpair and fork so parent can send to child
  3835. $sockets = array();
  3836. if (!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets)) {
  3837. die(socket_strerror(socket_last_error()));
  3838. }
  3839. list($reader, $writer) = $sockets;
  3840. $pid = pcntl_fork();
  3841. if ($pid == -1) {
  3842. die('cannot fork');
  3843. } elseif ($pid) {
  3844. socket_close($reader);
  3845. $line = sprintf("Parent Pid %d is sending this\n", getmypid());
  3846. if (!socket_write($writer, $line, strlen($line))) {
  3847. socket_close($writer);
  3848. die(socket_strerror(socket_last_error()));
  3849. }
  3850. socket_close($writer);
  3851. pcntl_waitpid($pid, $status);
  3852. } else {
  3853. socket_close($writer);
  3854. $line = socket_read($reader, 1024, PHP_NORMAL_READ);
  3855. printf("Child Pid %d just read this: `%s'\n", getmypid(), rtrim($line));
  3856. socket_close($reader); // this will happen anyway
  3857. exit(0);
  3858. }
  3859. // -----------------------------
  3860. // pipe2 - use socketpair and fork so child can send to parent
  3861. $sockets = array();
  3862. if (!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets)) {
  3863. die(socket_strerror(socket_last_error()));
  3864. }
  3865. list($reader, $writer) = $sockets;
  3866. $pid = pcntl_fork();
  3867. if ($pid == -1) {
  3868. die('cannot fork');
  3869. } elseif ($pid) {
  3870. socket_close($writer);
  3871. $line = socket_read($reader, 1024, PHP_NORMAL_READ);
  3872. printf("Parent Pid %d just read this: `%s'\n", getmypid(), rtrim($line));
  3873. socket_close($reader);
  3874. pcntl_waitpid($pid, $status);
  3875. } else {
  3876. socket_close($reader);
  3877. $line = sprintf("Child Pid %d is sending this\n", getmypid());
  3878. if (!socket_write($writer, $line, strlen($line))) {
  3879. socket_close($writer);
  3880. die(socket_strerror(socket_last_error()));
  3881. }
  3882. socket_close($writer); // this will happen anyway
  3883. exit(0);
  3884. }
  3885. // -----------------------------
  3886. // pipe3 and pipe4 demonstrate the use of perl's "forking open"
  3887. // feature to reimplement pipe1 and pipe2. pipe5 uses two pipes
  3888. // to simulate socketpair. Since PHP supports socketpair but not
  3889. // pipe, and does not have a "forking open" feature, these
  3890. // examples are skipped here.
  3891. // -----------------------------
  3892. // pipe6 - bidirectional communication using socketpair
  3893. $sockets = array();
  3894. if (!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets)) {
  3895. die(socket_strerror(socket_last_error()));
  3896. }
  3897. list($child, $parent) = $sockets;
  3898. $pid = pcntl_fork();
  3899. if ($pid == -1) {
  3900. die('cannot fork');
  3901. } elseif ($pid) {
  3902. socket_close($parent);
  3903. $line = sprintf("Parent Pid %d is sending this\n", getmypid());
  3904. if (!socket_write($child, $line, strlen($line))) {
  3905. socket_close($child);
  3906. die(socket_strerror(socket_last_error()));
  3907. }
  3908. $line = socket_read($child, 1024, PHP_NORMAL_READ);
  3909. printf("Parent Pid %d just read this: `%s'\n", getmypid(), rtrim($line));
  3910. socket_close($child);
  3911. pcntl_waitpid($pid, $status);
  3912. } else {
  3913. socket_close($child);
  3914. $line = socket_read($parent, 1024, PHP_NORMAL_READ);
  3915. printf("Child Pid %d just read this: `%s'\n", getmypid(), rtrim($line));
  3916. $line = sprintf("Child Pid %d is sending this\n", getmypid());
  3917. if (!socket_write($parent, $line, strlen($line))) {
  3918. socket_close($parent);
  3919. die(socket_strerror(socket_last_error()));
  3920. }
  3921. socket_close($parent);
  3922. exit(0);
  3923. }
  3924. // @@PLEAC@@_16.11
  3925. // -----------------------------
  3926. // % mkfifo /path/to/named.pipe
  3927. // -----------------------------
  3928. $fifo = fopen('/path/to/named.pipe', 'r');
  3929. if ($fifo !== false) {
  3930. while (!feof($fifo)) {
  3931. $line = fgets($fifo);
  3932. if ($line === false) break;
  3933. echo "Got: $line";
  3934. }
  3935. fclose($fifo);
  3936. } else {
  3937. die('could not open fifo for read');
  3938. }
  3939. // -----------------------------
  3940. $fifo = fopen('/path/to/named.pipe', 'w');
  3941. if ($fifo !== false) {
  3942. fwrite($fifo, "Smoke this.\n");
  3943. fclose($fifo);
  3944. } else {
  3945. die('could not open fifo for write');
  3946. }
  3947. // -----------------------------
  3948. // % mkfifo ~/.plan # isn't this everywhere yet?
  3949. // % mknod ~/.plan p # in case you don't have mkfifo
  3950. // -----------------------------
  3951. // dateplan - place current date and time in .plan file
  3952. while (true) {
  3953. $home = getenv('HOME');
  3954. $fifo = fopen("$home/.plan", 'w');
  3955. if ($fifo === false) {
  3956. die("Couldn't open $home/.plan for writing.\n");
  3957. }
  3958. fwrite($fifo,
  3959. 'The current time is '
  3960. . strftime('%a, %d %b %Y %H:%M:%S %z')
  3961. . "\n");
  3962. fclose($fifo);
  3963. sleep(1);
  3964. }
  3965. // -----------------------------
  3966. // fifolog - read and record log msgs from fifo
  3967. $fifo = null;
  3968. declare(ticks = 1);
  3969. function handle_alarm($signal) {
  3970. global $fifo;
  3971. if ($fifo) fclose($fifo); // move on to the next queued process
  3972. }
  3973. pcntl_signal(SIGALRM, 'handle_alarm');
  3974. while (true) {
  3975. pcntl_alarm(0); // turn off alarm for blocking open
  3976. $fifo = fopen('/tmp/log', 'r');
  3977. if ($fifo === false) {
  3978. die("can't open /tmp/log");
  3979. }
  3980. pcntl_alarm(1); // you have 1 second to log
  3981. $service = fgets($fifo);
  3982. if ($service === false) continue; // interrupt or nothing logged
  3983. $service = rtrim($service);
  3984. $message = fgets($fifo);
  3985. if ($message === false) continue; // interrupt or nothing logged
  3986. $message = rtrim($message);
  3987. pcntl_alarm(0); // turn off alarms for message processing
  3988. if ($service == 'http') {
  3989. // ignoring
  3990. } elseif ($service == 'login') {
  3991. // log to /var/log/login
  3992. $log = fopen('/var/log/login', 'a');
  3993. if ($log !== false) {
  3994. fwrite($log,
  3995. strftime('%a, %d %b %Y %H:%M:%S %z')
  3996. . " $service $message\n");
  3997. fclose($log);
  3998. } else {
  3999. trigger_error("Couldn't log $service $message to /var/log/login\n",
  4000. E_USER_WARNING);
  4001. }
  4002. }
  4003. }
  4004. // @@PLEAC@@_16.12
  4005. // sharetest - test shared variables across forks
  4006. $SHM_KEY = ftok(__FILE__, chr(1));
  4007. $handle = sem_get($SHM_KEY);
  4008. $buffer = shm_attach($handle, 1024);
  4009. // The original recipe has an INT signal handler here. However, it
  4010. // causes erratic behavior with PHP, and PHP seems to do the right
  4011. // thing without it.
  4012. for ($i = 0; $i < 10; $i++) {
  4013. $child = pcntl_fork();
  4014. if ($child == -1) {
  4015. die('cannot fork');
  4016. } elseif ($child) {
  4017. $kids[] = $child; // in case we care about their pids
  4018. } else {
  4019. squabble();
  4020. exit();
  4021. }
  4022. }
  4023. while (true) {
  4024. print 'Buffer is ' . shm_get_var($buffer, 1) . "\n";
  4025. sleep(1);
  4026. }
  4027. die('Not reached');
  4028. function squabble() {
  4029. global $handle;
  4030. global $buffer;
  4031. $i = 0;
  4032. $pid = getmypid();
  4033. while (true) {
  4034. if (preg_match("/^$pid\\b/", shm_get_var($buffer, 1))) continue;
  4035. sem_acquire($handle);
  4036. $i++;
  4037. shm_put_var($buffer, 1, "$pid $i");
  4038. sem_release($handle);
  4039. }
  4040. }
  4041. // Buffer is 14357 1
  4042. // Buffer is 14355 3
  4043. // Buffer is 14355 4
  4044. // Buffer is 14354 5
  4045. // Buffer is 14353 6
  4046. // Buffer is 14351 8
  4047. // Buffer is 14351 9
  4048. // Buffer is 14350 10
  4049. // Buffer is 14348 11
  4050. // Buffer is 14348 12
  4051. // Buffer is 14357 10
  4052. // Buffer is 14357 11
  4053. // Buffer is 14355 13
  4054. // ...
  4055. // @@PLEAC@@_16.13
  4056. // Available signal constants
  4057. % php -r 'print_r(get_defined_constants());' | grep '\[SIG' | grep -v _
  4058. [SIGHUP] => 1
  4059. [SIGINT] => 2
  4060. [SIGQUIT] => 3
  4061. [SIGILL] => 4
  4062. [SIGTRAP] => 5
  4063. [SIGABRT] => 6
  4064. [SIGIOT] => 6
  4065. [SIGBUS] => 7
  4066. [SIGFPE] => 8
  4067. [SIGKILL] => 9
  4068. [SIGUSR1] => 10
  4069. [SIGSEGV] => 11
  4070. [SIGUSR2] => 12
  4071. [SIGPIPE] => 13
  4072. [SIGALRM] => 14
  4073. [SIGTERM] => 15
  4074. [SIGSTKFLT] => 16
  4075. [SIGCLD] => 17
  4076. [SIGCHLD] => 17
  4077. [SIGCONT] => 18
  4078. [SIGSTOP] => 19
  4079. [SIGTSTP] => 20
  4080. [SIGTTIN] => 21
  4081. [SIGTTOU] => 22
  4082. [SIGURG] => 23
  4083. [SIGXCPU] => 24
  4084. [SIGXFSZ] => 25
  4085. [SIGVTALRM] => 26
  4086. [SIGPROF] => 27
  4087. [SIGWINCH] => 28
  4088. [SIGPOLL] => 29
  4089. [SIGIO] => 29
  4090. [SIGPWR] => 30
  4091. [SIGSYS] => 31
  4092. [SIGBABY] => 31
  4093. // Predefined signal handler constants
  4094. % php -r 'print_r(get_defined_constants());' | grep '\[SIG' | grep _
  4095. [SIG_IGN] => 1
  4096. [SIG_DFL] => 0
  4097. [SIG_ERR] => -1
  4098. // @@PLEAC@@_16.14
  4099. // send pid a signal 9
  4100. posix_kill($pid, 9);
  4101. // send whole job a signal 1
  4102. posix_kill($pgrp, -1);
  4103. // send myself a SIGUSR1
  4104. posix_kill(getmypid(), SIGUSR1);
  4105. // send a SIGHUP to processes in pids
  4106. foreach ($pids as $pid) posix_kill($pid, SIGHUP);
  4107. // -----------------------------
  4108. // Use kill with pseudo-signal 0 to see if process is alive.
  4109. if (posix_kill($minion, 0)) {
  4110. echo "$minion is alive!\n";
  4111. } else {
  4112. echo "$minion is deceased.\n";
  4113. }
  4114. // @@PLEAC@@_16.15
  4115. // call got_sig_quit for every SIGQUIT
  4116. pcntl_signal(SIGQUIT, 'got_sig_quit');
  4117. // call got_sig_pipe for every SIGPIPE
  4118. pcntl_signal(SIGPIPE, 'got_sig_pipe');
  4119. // increment ouch for every SIGINT
  4120. function got_sig_int($signal) { global $ouch; $ouch++; }
  4121. pcntl_signal(SIGINT, 'got_sig_int');
  4122. // ignore the signal INT
  4123. pcntl_signal(SIGINT, SIG_IGN);
  4124. // restore default STOP signal handling
  4125. pcntl_signal(SIGSTOP, SIG_DFL);
  4126. // @@PLEAC@@_16.16
  4127. // the signal handler
  4128. function ding($signal) {
  4129. fwrite(STDERR, "\x07Enter your name!\n");
  4130. }
  4131. // prompt for name, overriding SIGINT
  4132. function get_name() {
  4133. declare(ticks = 1);
  4134. pcntl_signal(SIGINT, 'ding');
  4135. echo "Kindly Stranger, please enter your name: ";
  4136. while (!@stream_select($read=array(STDIN),
  4137. $write=null,
  4138. $except=null,
  4139. 1)) {
  4140. // allow signals to be observed
  4141. }
  4142. $name = fgets(STDIN);
  4143. // Since pcntl_signal() doesn't return the old signal handler, the
  4144. // best we can do here is set it back to the default behavior.
  4145. pcntl_signal(SIGINT, SIG_DFL);
  4146. return $name;
  4147. }
  4148. // @@PLEAC@@_16.17
  4149. function got_int($signal) {
  4150. pcntl_signal(SIGINT, 'got_int'); // but not for SIGCHLD!
  4151. // ...
  4152. }
  4153. pcntl_signal(SIGINT, 'got_int');
  4154. // -----------------------------
  4155. declare(ticks = 1);
  4156. $interrupted = false;
  4157. function got_int($signal) {
  4158. global $interrupted;
  4159. $interrupted = true;
  4160. // The third argument to pcntl_signal() determines if system calls
  4161. // should be restarted after a signal. It defaults to true.
  4162. pcntl_signal(SIGINT, 'got_int', false); // or SIG_IGN
  4163. }
  4164. pcntl_signal(SIGINT, 'got_int', false);
  4165. // ... long-running code that you don't want to restart
  4166. if ($interrupted) {
  4167. // deal with the signal
  4168. }
  4169. // @@PLEAC@@_16.18
  4170. // ignore signal INT
  4171. pcntl_signal(SIGINT, SIG_IGN);
  4172. // install signal handler
  4173. declare(ticks = 1);
  4174. function tsktsk($signal) {
  4175. fwrite(STDERR, "\x07The long habit of living indisposeth us for dying.");
  4176. pcntl_signal(SIGINT, 'tsktsk');
  4177. }
  4178. pcntl_signal(SIGINT, 'tsktsk');
  4179. // @@PLEAC@@_16.19
  4180. pcntl_signal(SIGCHLD, SIG_IGN);
  4181. // -----------------------------
  4182. declare(ticks = 1);
  4183. function reaper($signal) {
  4184. $pid = pcntl_waitpid(-1, $status, WNOHANG);
  4185. if ($pid > 0) {
  4186. // ...
  4187. reaper($signal);
  4188. }
  4189. // install *after* calling waitpid
  4190. pcntl_signal(SIGCHLD, 'reaper');
  4191. }
  4192. pcntl_signal(SIGCHLD, 'reaper');
  4193. // -----------------------------
  4194. declare(ticks = 1);
  4195. function reaper($signal) {
  4196. $pid = pcntl_waitpid(-1, $status, WNOHANG);
  4197. if ($pid == -1) {
  4198. // No child waiting. Ignore it.
  4199. } else {
  4200. if (pcntl_wifexited($signal)) {
  4201. echo "Process $pid exited.\n";
  4202. } else {
  4203. echo "False alarm on $pid\n";
  4204. }
  4205. reaper($signal);
  4206. }
  4207. pcntl_signal(SIGCHLD, 'reaper');
  4208. }
  4209. pcntl_signal(SIGCHLD, 'reaper');
  4210. // @@PLEAC@@_16.20
  4211. // PHP does not support sigprocmask().
  4212. // @@PLEAC@@_16.21
  4213. declare(ticks = 1);
  4214. $aborted = false;
  4215. function handle_alarm($signal) {
  4216. global $aborted;
  4217. $aborted = true;
  4218. }
  4219. pcntl_signal(SIGALRM, 'handle_alarm');
  4220. pcntl_alarm(3600);
  4221. // long-time operations here
  4222. pcntl_alarm(0);
  4223. if ($aborted) {
  4224. // timed out - do what you will here
  4225. }