function pleac_Processing_All_Files_in_a_Directory() { // Conventional POSIX-like approach to directory traversal $dirname = '/usr/bin/'; ($dirhdl = opendir($dirname)) || die("Couldn't open {$dirname}\n"); while (($file = readdir($dirhdl)) !== FALSE) { // ... do something with $dirname/$file // ... } closedir($dirhdl); // ------------ // Newer [post PHP 4], 'applicative' approach - an array of filenames is // generated that may be processed via external loop ... $dirname = '/usr/bin/'; foreach (scandir($dirname) as $file) { // ... do something with $dirname/$file // ... } // .. or, via callback application, perhaps after massaging by one of the // 'array' family of functions [also uses, 'array_update', from earlier section] $newlist = array_update(array_reverse(scandir($dirname)), create_function('$filelist, $file', ' ; '), array()); // And don't forget that the old standby, 'glob', that returns an array of // paths filtered using the Bourne Shell-based wildcards, '?' and '*', is // also available foreach (glob($dirname . '*') as $path) { // ... do something with $path // ... } // ---------------------------- // Uses, 'isTextFile', from an earlier section $dirname = '/usr/bin/'; echo "Text files in {$dirname}:\n"; foreach (scandir($dirname) as $file) { // Take care when constructing paths to ensure cross-platform operability $path = $dirname . $file; if (is_file($path) && isTextFile($path)) { echo $path . "\n"; } } // ---------------------------- function plain_files($dirname) { ($dirlist = glob($dirname . '*')) || die("Couldn't glob {$dirname}\n"); // Pass function name directly if only a single function performs filter test return array_filter($dirlist, 'is_file'); // Use, 'create_function', if a multi-function test is needed // // return array_filter($dirlist, create_function('$path', 'return is_file($path);')); // } // ------------ foreach (plain_files('/tmp/') as $path) { echo $path . "\n"; } }
public function testArrayUpdateMissing() { $array = array_update($this->array, 42, function ($v) { return $v + 1; }); $this->assertSame($this->array, $array); $array = array_update($this->array, 3, function ($v) { return $v + 1; }); $this->assertSame($this->array, $array); }
function pleac_Finding_the_First_List_Element_That_Passes_a_Test() { // This section illustrates various 'find first' techniques. The Perl examples all use an // explicit loop and condition testing [also repeated here]. This is the simplest, and // [potentially] most efficient approach because the search can be terminated as soon as a // match is found. However, it is worth mentioning a few alternatives: // * 'array_search' performs a 'find first' using the element value rather than a condition // check, so isn't really applicable here // * 'array_filter', whilst using a condition check, actually performs a 'find all', though // all but the first returned element can be discarded. This approach is actually less error // prone than using a loop, but the disadvantage is that each element is visited: there is no // means of terminating the search once a match has been found. It would be nice if this // function were to have a third parameter, a Boolean flag indicating whether to traverse // the whole array, or quit after first match [next version, maybe :) ?] $found = FALSE; foreach ($array as $item) { // Not found - skip to next item if (!$criterion) { continue; } // Found - save and leave $match = $item; $found = TRUE; break; } if ($found) { // do something with $match } else { // not found } // ------------ function predicate($element) { if (criterion) { return TRUE; } return FALSE; } $match = array_slice(array_filter($array, 'predicate'), 0, 1); if ($match) { // do something with $match[0] } else { // $match is empty - not found } // ---------------------------- class Employee { public $name, $age, $ssn, $salary; public function __construct($name, $age, $ssn, $salary, $category) { $this->name = $name; $this->age = $age; $this->ssn = $ssn; $this->salary = $salary; $this->category = $category; } } // ------------ $employees = array(new Employee('sdf', 27, 12345, 47000, 'Engineer'), new Employee('ajb', 32, 12376, 51000, 'Programmer'), new Employee('dgh', 31, 12355, 45000, 'Engineer')); // ------------ function array_update($arr, $lambda, $updarr) { foreach ($arr as $key) { $lambda($updarr, $key); } return $updarr; } function highest_salaried_engineer(&$arr, $employee) { static $highest_salary = 0; if ($employee->category == 'Engineer') { if ($employee->salary > $highest_salary) { $highest_salary = $employee->salary; $arr[0] = $employee; } } } // ------------ // 'array_update' custom function is modelled on 'array_reduce' except that it allows the // return of an array, contents and length of which are entirely dependant on what the // callback function does. Here, it is logically working in a 'find first' capacity $highest_salaried_engineer = array_update($employees, 'highest_salaried_engineer', array()); echo 'Highest paid engineer is: ' . $highest_salaried_engineer[0]->name . "\n"; }
function pleac_Sorting_a_List_by_Computable_Field() { // Tasks in this section would typically use the PHP 'usort' family of functions // which are used with a comparator function so as to perform custom comparisions. // A significant difference from the Perl examples is that these functions are // inplace sorters, so it is the original array that is modified. Where this must // be prevented a copy of the array can be made and sorted function comparator($left, $right) { // Compare '$left' with '$right' returning result } // ------------ $ordered = array_slice($unordered); usort($ordered, 'comparator'); // ---------------------------- // The Perl example looks like it is creating a hash using computed values as the key, // array values as the value, sorting on the computed key, then extracting the sorted // values and placing them back into an array function compute($value) { // Return computed value utilising '$value' } // ------------ // Original numerically-indexed array [sample data used] $unordered = array(5, 3, 7, 1, 4, 2, 6); // Create hash using 'compute' function to generate the keys. This example assumes that // each value in the '$unordered' array is used in generating the corresponding '$key' foreach ($unordered as $value) { $precomputed[compute($value)] = $value; } // Copy the hash, and sort it by key $ordered_precomputed = array_slice($precomputed, 0); ksort($ordered_precomputed); // Extract the values of the hash in current order placing them in a new numerically-indexed // array $ordered = array_values($ordered_precomputed); // ---------------------------- // As above, except uses 'array_update' and 'accum' to help create hash function array_update($arr, $lambda, $updarr) { foreach ($arr as $key) { $lambda($updarr, $key); } return $updarr; } function accum(&$arr, $value) { $arr[compute($value)] = $value; } // ------------ function compute($value) { // Return computed value utilising '$value' } // ------------ // Original numerically-indexed array [sample data used] $unordered = array(5, 3, 7, 1, 4, 2, 6); // Create hash $precomputed = array_update($unordered, 'accum', array()); // Copy the hash, and sort it by key $ordered_precomputed = array_slice($precomputed, 0); ksort($ordered_precomputed); // Extract the values of the hash in current order placing them in a new numerically-indexed // array $ordered = array_values($ordered_precomputed); // ---------------------------- class Employee { public $name, $age, $ssn, $salary; public function __construct($name, $age, $ssn, $salary) { $this->name = $name; $this->age = $age; $this->ssn = $ssn; $this->salary = $salary; } } // ------------ $employees = array(new Employee('sdf', 27, 12345, 47000), new Employee('ajb', 32, 12376, 51000), new Employee('dgh', 31, 12355, 45000)); // ------------ $ordered = array_slice($employees, 0); usort($ordered, create_function('$left, $right', 'return $left->name > $right->name;')); // ------------ $sorted_employees = array_slice($employees, 0); usort($sorted_employees, create_function('$left, $right', 'return $left->name > $right->name;')); $bonus = array(12376 => 5000, 12345 => 6000, 12355 => 0); foreach ($sorted_employees as $employee) { echo "{$employee->name} earns \${$employee->salary}\n"; } foreach ($sorted_employees as $employee) { if ($amount = $bonus[$employee->ssn]) { echo "{$employee->name} got a bonus of: \${$amount}\n"; } } // ------------ $sorted = array_slice($employees, 0); usort($sorted, create_function('$left, $right', 'return $left->name > $right->name || $left->age != $right->age;')); // ---------------------------- // PHP offers a swag of POSIX functions for obtaining user information [i.e. they all read // the '/etc/passwd' file for the relevant infroamtion], and it is these that should rightly // be used for this purpose. However, since the intent of this section is to illustrate array // manipulation, these functions won't be used. Instead a custom function mimicing Perl's // 'getpwent' function will be implemented so the code presented here can more faithfully match // the Perl code function get_pw_entries() { function normal_users_only($e) { $entry = split(':', $e); return $entry[2] > 100 && $entry[2] < 32768; } foreach (array_filter(file('/etc/passwd'), 'normal_users_only') as $entry) { $users[] = split(':', trim($entry)); } return $users; } // ------------ $users = get_pw_entries(); usort($users, create_function('$left, $right', 'return $left[0] > $right[0];')); foreach ($users as $user) { echo "{$user[0]}\n"; } // ---------------------------- $names = array('sdf', 'ajb', 'dgh'); $sorted = array_slice($names, 0); usort($sorted, create_function('$left, $right', 'return substr($left, 1, 1) > substr($right, 1, 1);')); // ------------ $strings = array('bbb', 'aa', 'c'); $sorted = array_slice($strings, 0); usort($sorted, create_function('$left, $right', 'return strlen($left) > strlen($right);')); // ---------------------------- function array_update($arr, $lambda, $updarr) { foreach ($arr as $key) { $lambda($updarr, $key); } return $updarr; } function accum(&$arr, $value) { $arr[strlen($value)] = $value; } // ---- $strings = array('bbb', 'aa', 'c'); $temp = array_update($strings, 'accum', array()); ksort($temp); $sorted = array_values($temp); // ---------------------------- function array_update($arr, $lambda, $updarr) { foreach ($arr as $key) { $lambda($updarr, $key); } return $updarr; } function accum(&$arr, $value) { if (preg_match('/(\\d+)/', $value, $matches)) { $arr[$matches[1]] = $value; } } // ---- $fields = array('b1b2b', 'a4a', 'c9', 'ddd', 'a'); $temp = array_update($fields, 'accum', array()); ksort($temp); $sorted_fields = array_values($temp); }
function array_update($arr, $ins) { if (is_array($arr) && is_array($ins)) { foreach ($ins as $k => $v) { if (isset($arr[$k]) && is_array($v) && is_array($arr[$k])) { $arr[$k] = array_update($arr[$k], $v); } else { $arr[$k] = $v; } } } elseif (!is_array($arr) && (strlen($arr) == 0 || $arr == 0)) { $arr = $ins; } return $arr; }
function pleac_Recognizing_Two_Names_for_the_Same_File() { function makeDevInodePair($filename) { if (!($fs = @stat($filename))) { return FALSE; } return strval($fs['dev'] . $fs['ino']); } // ------------ function do_my_thing($filename) { // Using a global variable to mimic Perl example, but could easily have passed // '$seen' as an argument global $seen; $devino = makeDevInodePair($filename); // Process $filename if it has not previously been seen, else just increment if (!isset($seen[$devino])) { // ... process $filename ... // Set initial count $seen[$devino] = 1; } else { // Otherwise, just increment the count $seen[$devino] += 1; } } // ---- // Simple example $seen = array(); do_my_thing('/tmp/old'); do_my_thing('/tmp/old'); do_my_thing('/tmp/old'); do_my_thing('/tmp/new'); foreach ($seen as $devino => $count) { echo "{$devino} -> {$count}\n"; } // ------------ // A variation on the above avoiding use of global variables, and illustrating use of // easily-implemented 'higher order' techniques // Helper function loosely modelled on, 'array_reduce', but using an array as // 'accumulator', which is returned on completion function array_update($arr, $lambda, $updarr) { foreach ($arr as $key) { $lambda($updarr, $key); } return $updarr; } function do_my_thing(&$seen, $filename) { if (!array_key_exists($devino = makeDevInodePair($filename), $seen)) { // ... processing $filename ... // Update $seen $seen[$devino] = 1; } else { // Update $seen $seen[$devino] += 1; } } // ---- // Simple example $files = array('/tmp/old', '/tmp/old', '/tmp/old', '/tmp/new'); // Could do this ... $seen = array(); array_update($files, 'do_my_thing', &$seen); // or this: $seen = array_update($files, 'do_my_thing', array()); // or a 'lambda' could be used: array_update($files, create_function('$seen, $filename', '... code not shown ...'), &$seen); foreach ($seen as $devino => $count) { echo "{$devino} -> {$count}\n"; } // ---------------------------- $files = glob('/tmp/*'); define(SEP, ';'); $seen = array(); foreach ($files as $filename) { if (!array_key_exists($devino = makeDevInodePair($filename), $seen)) { $seen[$devino] = $filename; } else { $seen[$devino] = $seen[$devino] . SEP . $filename; } } $devino = array_keys($seen); sort($devino); foreach ($devino as $key) { echo $key . ':'; foreach (split(SEP, $seen[$key]) as $filename) { echo ' ' . $filename; } echo "\n"; } }