├── source_code ├── Chapter 2 │ ├── closure2-output.txt │ ├── return-output.txt │ ├── constructs2-output.php │ ├── named_function-output.txt │ ├── null-return-output.txt │ ├── null-return2-output.txt │ ├── userfunc-output.txt │ ├── returns-output.txt │ ├── referential-output.txt │ ├── constructs.php │ ├── closure-output.txt │ ├── mutable-output.txt │ ├── anon.php │ ├── name-scope-output.txt │ ├── anon-output.txt │ ├── mutable.php │ ├── scope-output.txt │ ├── constructs2.php │ ├── constructs-output.php │ ├── name-scope3-output.txt │ ├── return.php │ ├── backtrace-output.txt │ ├── call_anon2-output.txt │ ├── name-scope2-output.txt │ ├── arguments.php │ ├── constant-func.php │ ├── closure.php │ ├── closure2.php │ ├── sort-output.txt │ ├── constant-func-output.txt │ ├── constant-output.txt │ ├── named_function.php │ ├── null-return2.php │ ├── examine.php │ ├── constant.php │ ├── name-scope3.php │ ├── returns.php │ ├── userfunc.php │ ├── variable-output.txt │ ├── higher-order-output.txt │ ├── name-scope.php │ ├── name-scope2.php │ ├── higher-order2-output.txt │ ├── null-return.php │ ├── backtrace.php │ ├── sort.php │ ├── call_anon-output.txt │ ├── examine-ouput.txt │ ├── splat-output.txt │ ├── splat.php │ ├── call_anon.php │ ├── variable.php │ ├── call_anon2.php │ ├── referential.php │ ├── higher-order2.php │ ├── higher-order.php │ └── scope.php ├── Chapter 3 │ ├── sums1-output.txt │ ├── dsl-output.txt │ ├── inc_dec-output.txt │ ├── foreach-output.txt │ ├── strrepeat-output.txt │ ├── concatenate-output.txt │ ├── recursive-output.txt │ ├── inc_dec_partial-output.txt │ ├── forever.php │ ├── forever-output.txt │ ├── all_recipes_recursive_output.txt │ ├── partial-output.txt │ ├── foreach2-output.txt │ ├── top_five-output.txt │ ├── inc_dec.php │ ├── shopping_list1.php │ ├── shopping_list2.php │ ├── new_ingredients-output.txt │ ├── print_first.php │ ├── foreach.php │ ├── recursive_stack.php │ ├── map_filter_reduce-output.txt │ ├── concatenate.php │ ├── all_recipes_recursive.php │ ├── shopping_list3.php │ ├── new_ingredients.php │ ├── partial.php │ ├── sums2.php │ ├── all_recipes.php │ ├── dsl.php │ ├── example2.php │ ├── sums1.php │ ├── recursive_stack-output.txt │ ├── recipe_functions.php │ ├── top_five.php │ ├── inc_dec_partial.php │ ├── all_recipes-output.txt │ ├── strrepeat.php │ ├── compose.php │ ├── partial_generator.php │ ├── sums2-output.txt │ ├── recursive.php │ └── map_filter_reduce.php ├── Chapter 4 │ ├── run_io-output.txt │ ├── types4-output.txt │ ├── types1-output.txt │ ├── maybe_test-output.txt │ ├── monad_test-output.txt │ ├── writer_monad-output.txt │ ├── bounce-output.txt │ ├── random.txt │ ├── types1.php │ ├── monoid.php │ ├── maybe_monad-output.txt │ ├── run_io.php │ ├── monad-output.txt │ ├── types2-output.txt │ ├── types2.php │ ├── types3-output.txt │ ├── currying-output.txt │ ├── types4.php │ ├── test.php │ ├── writer_monad.php │ ├── maybe_test.php │ ├── monad_test.php │ ├── io_monad.php │ ├── types3.php │ ├── currying.php │ ├── monad.php │ ├── bounce.php │ └── maybe_monad.php ├── Chapter 8 │ ├── underscore-throttle-output.txt │ ├── sabre-example-output.txt │ ├── underscore-flexible-output.txt │ ├── pramda-example-output.txt │ ├── underscore-chain-output.txt │ ├── phamda-example-output.txt │ ├── pramda-example2-output.txt │ ├── underscore-throttle.php │ ├── underscore-memoize-output.txt │ ├── functionalphp-example-output.txt │ ├── sabre-example.php │ ├── underscore-chain.php │ ├── underscore-memoize.php │ ├── underscore-flexible.php │ ├── pramda-example.php │ ├── phamda-example.php │ ├── functionalphp-example.php │ └── pramda-example2.php ├── Chapter 7 │ ├── properties-output.txt │ ├── transparent-output.txt │ ├── clones-output.txt │ ├── passing_objects-output.txt │ ├── procedural.php │ ├── procedural2-output.txt │ ├── const_array-output.txt │ ├── procedural2.php │ ├── static_methods.php │ ├── transparent.php │ ├── clones.php │ ├── properties.php │ ├── passing_objects.php │ └── const_array.php ├── Chapter 5 │ ├── ramdisk-output.txt │ ├── manual-output.txt │ ├── generators2-output.txt │ ├── generators-output.txt │ ├── ramdisk.sh │ ├── filter-output.txt │ ├── lazy_filter-output.txt │ ├── memoize-mem.php │ ├── spl-output.txt │ ├── repeat.php │ ├── static.php │ ├── parallel-output.txt │ ├── parallel-output2.txt │ ├── memo_example-output.txt │ ├── memo_mem_example-output.txt │ ├── static-output.txt │ ├── cache_files.txt │ ├── client.php │ ├── memo_mem_example.php │ ├── manual.php │ ├── parallel.php │ ├── memo_example.php │ ├── generators.php │ ├── filter.php │ ├── lazy_filter.php │ ├── memoize.php │ ├── spl.php │ └── functions.php ├── Chapter 6 │ ├── cat.jpg │ ├── shopping-output.txt │ ├── install_event.txt │ ├── business_data.php │ ├── shopping.php │ ├── web_server.php │ ├── business_logic.php │ └── server_functions.php ├── Chapter 9 │ ├── vm_console.png │ ├── hadoop_web_interface.png │ ├── part-00000.txt │ ├── install_php.txt │ ├── map_job.php │ ├── reduce_job.php │ ├── enable_ssh.txt │ ├── hdfs_commands.txt │ ├── setup_scripts.txt │ ├── hadoop_commands.txt │ ├── job_functions.php │ └── hadoop_output.txt └── Chapter 1 │ ├── declarative.sql │ └── example.php ├── LICENSE.txt ├── README.md └── contributing.md /source_code/Chapter 2/closure2-output.txt: -------------------------------------------------------------------------------- 1 | 18 -------------------------------------------------------------------------------- /source_code/Chapter 3/sums1-output.txt: -------------------------------------------------------------------------------- 1 | -10°C 2 | -------------------------------------------------------------------------------- /source_code/Chapter 2/return-output.txt: -------------------------------------------------------------------------------- 1 | int(23) 2 | -------------------------------------------------------------------------------- /source_code/Chapter 4/run_io-output.txt: -------------------------------------------------------------------------------- 1 | All done 2 | -------------------------------------------------------------------------------- /source_code/Chapter 2/constructs2-output.php: -------------------------------------------------------------------------------- 1 | hello world! -------------------------------------------------------------------------------- /source_code/Chapter 3/dsl-output.txt: -------------------------------------------------------------------------------- 1 | int(24) 2 | int(26) 3 | -------------------------------------------------------------------------------- /source_code/Chapter 3/inc_dec-output.txt: -------------------------------------------------------------------------------- 1 | int(4) 2 | int(2) 3 | -------------------------------------------------------------------------------- /source_code/Chapter 4/types4-output.txt: -------------------------------------------------------------------------------- 1 | int(5) 2 | float(5) 3 | -------------------------------------------------------------------------------- /source_code/Chapter 8/underscore-throttle-output.txt: -------------------------------------------------------------------------------- 1 | ....... -------------------------------------------------------------------------------- /source_code/Chapter 4/types1-output.txt: -------------------------------------------------------------------------------- 1 | bool(true) 2 | int(22) 3 | -------------------------------------------------------------------------------- /source_code/Chapter 2/named_function-output.txt: -------------------------------------------------------------------------------- 1 | int(30) 2 | int(15) 3 | -------------------------------------------------------------------------------- /source_code/Chapter 3/foreach-output.txt: -------------------------------------------------------------------------------- 1 | Total items to purchase : 34 2 | -------------------------------------------------------------------------------- /source_code/Chapter 3/strrepeat-output.txt: -------------------------------------------------------------------------------- 1 | The string is : ********** 2 | -------------------------------------------------------------------------------- /source_code/Chapter 8/sabre-example-output.txt: -------------------------------------------------------------------------------- 1 | int(65) 2 | int(20) 3 | -------------------------------------------------------------------------------- /source_code/Chapter 7/properties-output.txt: -------------------------------------------------------------------------------- 1 | string(20) "£15.99 inc -20% tax" 2 | -------------------------------------------------------------------------------- /source_code/Chapter 2/null-return-output.txt: -------------------------------------------------------------------------------- 1 | NULL 2 | string(7) "PEACHES" 3 | NULL 4 | -------------------------------------------------------------------------------- /source_code/Chapter 2/null-return2-output.txt: -------------------------------------------------------------------------------- 1 | NULL 2 | NULL 3 | string(6) "Yummy!" 4 | -------------------------------------------------------------------------------- /source_code/Chapter 2/userfunc-output.txt: -------------------------------------------------------------------------------- 1 | string(5) "mango" 2 | string(4) "beef" 3 | -------------------------------------------------------------------------------- /source_code/Chapter 4/maybe_test-output.txt: -------------------------------------------------------------------------------- 1 | bool(true) 2 | bool(true) 3 | bool(true) 4 | -------------------------------------------------------------------------------- /source_code/Chapter 4/monad_test-output.txt: -------------------------------------------------------------------------------- 1 | bool(true) 2 | bool(true) 3 | bool(true) 4 | -------------------------------------------------------------------------------- /source_code/Chapter 3/concatenate-output.txt: -------------------------------------------------------------------------------- 1 | what is your name 2 | what is happening here 3 | -------------------------------------------------------------------------------- /source_code/Chapter 3/recursive-output.txt: -------------------------------------------------------------------------------- 1 | List 1 : 34 2 | List 2 : 34 3 | List 3 : 34 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/pro-functional-php-programming/HEAD/LICENSE.txt -------------------------------------------------------------------------------- /source_code/Chapter 2/returns-output.txt: -------------------------------------------------------------------------------- 1 | int(16) 2 | string(28) "Tue 21 Feb 13:12:37 GMT 2017" 3 | -------------------------------------------------------------------------------- /source_code/Chapter 3/inc_dec_partial-output.txt: -------------------------------------------------------------------------------- 1 | int(4) 2 | int(2) 3 | int(30) 4 | int(34) 5 | -------------------------------------------------------------------------------- /source_code/Chapter 7/transparent-output.txt: -------------------------------------------------------------------------------- 1 | Impure Logging : 7 2 | Impure Logging : 21 3 | int(21) 4 | -------------------------------------------------------------------------------- /source_code/Chapter 2/referential-output.txt: -------------------------------------------------------------------------------- 1 | bool(true) 2 | bool(true) 3 | bool(false) 4 | bool(false) 5 | -------------------------------------------------------------------------------- /source_code/Chapter 2/constructs.php: -------------------------------------------------------------------------------- 1 | b" 3 | string(1) "c" 4 | string(6) "c -> d" 5 | string(11) "c -> d -> e" 6 | -------------------------------------------------------------------------------- /source_code/Chapter 1/declarative.sql: -------------------------------------------------------------------------------- 1 | SELECT forename, 2 | surname 3 | FROM users 4 | WHERE username = 'rob' 5 | AND password = 'password1'; 6 | -------------------------------------------------------------------------------- /source_code/Chapter 2/anon-output.txt: -------------------------------------------------------------------------------- 1 | object(Closure)#1 (1) { 2 | ["parameter"]=> 3 | array(1) { 4 | ["$a"]=> 5 | string(10) "" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /source_code/Chapter 3/forever.php: -------------------------------------------------------------------------------- 1 | Doubling 6 6 | [1] => Negating 12 7 | [2] => Adding 2 to -12 8 | ) 9 | -------------------------------------------------------------------------------- /source_code/Chapter 2/mutable.php: -------------------------------------------------------------------------------- 1 | 3 | float(282.74333882308) 4 | } 5 | object(my_class)#3 (1) { 6 | ["value":"my_class":private]=> 7 | int(10) 8 | } 9 | -------------------------------------------------------------------------------- /source_code/Chapter 4/random.txt: -------------------------------------------------------------------------------- 1 | 935998b29780e9f8f56435120208f7196854f677a666abcc510fee8a7162d12f6d923e470b4373f232dfbb0bf1a9da28e9b8a3f84af15273fc516ccf74c493ebce3931922a59d83ba80d77cfc41e8c76ffd90d79d91e32bcf2fbdf15a85ec38b1c5186cc -------------------------------------------------------------------------------- /source_code/Chapter 8/underscore-flexible-output.txt: -------------------------------------------------------------------------------- 1 | float(43) 2 | int(48) 3 | int(48) 4 | array(4) { 5 | [0]=> 6 | int(99) 7 | [1]=> 8 | int(38) 9 | [2]=> 10 | int(25) 11 | [3]=> 12 | int(10) 13 | } 14 | -------------------------------------------------------------------------------- /source_code/Chapter 2/backtrace-output.txt: -------------------------------------------------------------------------------- 1 | #0 add_h_tags(TESTING) called at [backtrace.php:12] 2 | #1 make_headline(testing) called at [backtrace.php:6] 3 | #2 prepare_text(testing) called at [backtrace.php:30] 4 |

TESTING

5 | -------------------------------------------------------------------------------- /source_code/Chapter 4/types1.php: -------------------------------------------------------------------------------- 1 | 6 | int(2) 7 | [1]=> 8 | int(4) 9 | [2]=> 10 | int(6) 11 | [3]=> 12 | int(8) 13 | } 14 | -------------------------------------------------------------------------------- /source_code/Chapter 3/top_five-output.txt: -------------------------------------------------------------------------------- 1 | Showing 5 of 11 items: 2 | 1. Rob 3 | 2. Robert 4 | 3. Robbie 5 | 4. Izzy 6 | 5. Ellie 7 | Showing 5 of 11 items: 8 | 1. Rob 9 | 2. Robert 10 | 3. Robbie 11 | 4. Izzy 12 | 5. Ellie 13 | -------------------------------------------------------------------------------- /source_code/Chapter 2/name-scope2-output.txt: -------------------------------------------------------------------------------- 1 | string(1) "a" 2 | string(6) "a -> b" 3 | PHP Fatal error: Uncaught Error: Call to undefined function d() in name-scope2.php:38 4 | Stack trace: 5 | #0 {main} 6 | thrown in name-scope2.php on line 38 7 | -------------------------------------------------------------------------------- /source_code/Chapter 2/arguments.php: -------------------------------------------------------------------------------- 1 | [ "apples" => 7, "pears" => 4, "bananas" => 6 ], 5 | "bakery" => [ "bread" => 1, "apple pie" => 2], 6 | "meat" => [ "sausages" => 10, "steaks" => 3, "chorizo" => 1 ] 7 | ]; 8 | -------------------------------------------------------------------------------- /source_code/Chapter 5/generators2-output.txt: -------------------------------------------------------------------------------- 1 | *** range() *** 2 | Array 3 | ( 4 | [Memory] => 134222280 5 | [Time] => 26.05708694458 6 | ) 7 | *** gen_range() *** 8 | Array 9 | ( 10 | [Memory] => 4952 11 | [Time] => 41.604923009872 12 | ) 13 | -------------------------------------------------------------------------------- /source_code/Chapter 2/constant-func.php: -------------------------------------------------------------------------------- 1 | 134222280 5 | [Time] => 8.9564578533173 6 | ) 7 | *** gen_range() *** 8 | Array 9 | ( 10 | [Memory] => 4952 11 | [Time] => 0.0016660690307617 12 | ) 13 | -------------------------------------------------------------------------------- /source_code/Chapter 2/closure.php: -------------------------------------------------------------------------------- 1 | [ "apples" => [ "red" => 3, "green" => 4], "pears" => 4, "bananas" => 6 ], 5 | "bakery" => [ "bread" => 1, "apple pie" => 2], 6 | "meat" => [ "sausages" => 10, "steaks" => 3, "chorizo" => 1 ] 7 | ]; 8 | -------------------------------------------------------------------------------- /source_code/Chapter 9/part-00000.txt: -------------------------------------------------------------------------------- 1 | Array 2 | ( 3 | [beard] => 76 4 | [buried] => 43 5 | [bright] => 43 6 | [bred] => 36 7 | [breed] => 35 8 | [bird] => 34 9 | [bride] => 30 10 | [board] => 15 11 | [broad] => 15 12 | [bread] => 15 13 | ) 14 | -------------------------------------------------------------------------------- /source_code/Chapter 2/closure2.php: -------------------------------------------------------------------------------- 1 | $count) { 10 | 11 | $total += $count; 12 | 13 | } 14 | 15 | } 16 | 17 | echo "Total items to purchase : $total\n"; 18 | -------------------------------------------------------------------------------- /source_code/Chapter 3/recursive_stack.php: -------------------------------------------------------------------------------- 1 | value = $value; 8 | 9 | } 10 | 11 | public function concat($to) { 12 | 13 | return new Monoid(array_merge($to->value, $this->value)); 14 | 15 | } 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /source_code/Chapter 2/sort-output.txt: -------------------------------------------------------------------------------- 1 | Array 2 | ( 3 | [0] => Asparagus 4 | [1] => Beetroot 5 | [2] => Carrot 6 | ) 7 | Array 8 | ( 9 | [0] => Carrot 10 | [1] => Beetroot 11 | [2] => Asparagus 12 | ) 13 | Array 14 | ( 15 | [0] => Asparagus 16 | [1] => Beetroot 17 | [2] => Carrot 18 | ) 19 | -------------------------------------------------------------------------------- /source_code/Chapter 2/constant-func-output.txt: -------------------------------------------------------------------------------- 1 | PHP Warning: Constants may only evaluate to scalar values or arrays in constant-func.php on line 8 2 | Double 2 is 4 3 | PHP Fatal error: Uncaught Error: Call to undefined function DOUBLE() in constant-func.php:12 4 | Stack trace: 5 | #0 {main} 6 | thrown in constant-func.php on line 12 7 | -------------------------------------------------------------------------------- /source_code/Chapter 3/map_filter_reduce-output.txt: -------------------------------------------------------------------------------- 1 | Array 2 | ( 3 | [0] => kiwi smoothie 4 | [1] => vinegar crumble 5 | [2] => egg ice cream 6 | [3] => cod tart 7 | [4] => beef pie 8 | ) 9 | Array 10 | ( 11 | [1] => vinegar crumble 12 | [3] => cod tart 13 | [4] => beef pie 14 | ) 15 | vinegar crumble -------------------------------------------------------------------------------- /source_code/Chapter 2/constant-output.txt: -------------------------------------------------------------------------------- 1 | int(4) 2 | int(6) 3 | string(6) "orange" 4 | PHP Warning: Constants may only evaluate to scalar values or arrays in constant.php on line 15 5 | PHP Fatal error: Uncaught Error: Call to undefined function ADD_NINE() in constant.php:17 6 | Stack trace: 7 | #0 {main} 8 | thrown in constant.php on line 17 9 | -------------------------------------------------------------------------------- /source_code/Chapter 2/named_function.php: -------------------------------------------------------------------------------- 1 | 7 | int(60) 8 | } 9 | object(my_class)#1 (1) { 10 | ["value":"my_class":private]=> 11 | int(60) 12 | } 13 | object(my_class)#2 (1) { 14 | ["value":"my_class":private]=> 15 | int(-1) 16 | } 17 | -------------------------------------------------------------------------------- /source_code/Chapter 2/examine.php: -------------------------------------------------------------------------------- 1 | [ "apples" => [ "red" => 3, "green" => 4], "pears" => 4, "bananas" => 6 ], 5 | "bakery" => [ "bread" => 1, "apple pie" => 2], 6 | "meat" => [ "sausages" => 7 | ["pork" => ["chipolata" => 5, "cumberland" => 2], "beef" => 3], 8 | "steaks" => 3, "chorizo" => 1 ] 9 | ]; 10 | -------------------------------------------------------------------------------- /source_code/Chapter 5/filter-output.txt: -------------------------------------------------------------------------------- 1 | Array 2 | ( 3 | [0] => Enter DON PEDRO, DON JOHN, LEONATO, FRIAR FRANCIS, CLAUDIO, BENEDICK, HERO, BEATRICE, and Attendants 4 | 5 | [1] => Sweet Hero! She is wronged, she is slandered, she is undone. 6 | 7 | [2] => Think you in your soul the Count Claudio hath wronged Hero? 8 | 9 | ) 10 | Time taken : 6.2691030502319 -------------------------------------------------------------------------------- /source_code/Chapter 5/lazy_filter-output.txt: -------------------------------------------------------------------------------- 1 | Array 2 | ( 3 | [0] => Enter DON PEDRO, DON JOHN, LEONATO, FRIAR FRANCIS, CLAUDIO, BENEDICK, HERO, BEATRICE, and Attendants 4 | 5 | [1] => Sweet Hero! She is wronged, she is slandered, she is undone. 6 | 7 | [2] => Think you in your soul the Count Claudio hath wronged Hero? 8 | 9 | ) 10 | Time taken : 2.1842160224915 -------------------------------------------------------------------------------- /source_code/Chapter 2/constant.php: -------------------------------------------------------------------------------- 1 | Enter DON PEDRO, DON JOHN, LEONATO, FRIAR FRANCIS, CLAUDIO, BENEDICK, HERO, BEATRICE, and Attendants 4 | 5 | [1] => Sweet Hero! She is wronged, she is slandered, she is undone. 6 | 7 | [2] => Think you in your soul the Count Claudio hath wronged Hero? 8 | 9 | ) 10 | Time taken : 1.4434311389923 -------------------------------------------------------------------------------- /source_code/Chapter 2/name-scope3.php: -------------------------------------------------------------------------------- 1 | 7 | string(14) "GRAPES - WHITE" 8 | ["Cheese"]=> 9 | string(16) "CHEESE - CHEDDAR" 10 | ["Apples"]=> 11 | string(12) "APPLES - RED" 12 | ["Milk"]=> 13 | string(12) "MILK - WHOLE" 14 | } 15 | -------------------------------------------------------------------------------- /source_code/Chapter 2/userfunc.php: -------------------------------------------------------------------------------- 1 | unsafePerform(); 19 | -------------------------------------------------------------------------------- /source_code/Chapter 3/all_recipes.php: -------------------------------------------------------------------------------- 1 | 3 | int(33) 4 | } 5 | object(MonadPHP\Identity)#3 (1) { 6 | ["value":protected]=> 7 | int(33) 8 | } 9 | object(MonadPHP\Identity)#4 (1) { 10 | ["value":protected]=> 11 | int(66) 12 | } 13 | int(66) 14 | int(132) 15 | int(66) 16 | int(132) 17 | int(284) 18 | object(MonadPHP\Identity)#7 (1) { 19 | ["value":protected]=> 20 | int(284) 21 | } 22 | -------------------------------------------------------------------------------- /source_code/Chapter 1/example.php: -------------------------------------------------------------------------------- 1 | Array 4 | ( 5 | [Item] => APPLE PIE 6 | [Price] => 4.99 7 | ) 8 | 9 | [1] => Array 10 | ( 11 | [Item] => STRAWBERRY ICE CREAM 12 | [Price] => 2.22 13 | ) 14 | 15 | [2] => Array 16 | ( 17 | [Item] => CHOCOLATE AND STRAWBERRY CAKE 18 | [Price] => Special: 5.99 19 | ) 20 | 21 | ) 22 | -------------------------------------------------------------------------------- /source_code/Chapter 5/spl-output.txt: -------------------------------------------------------------------------------- 1 | Generator Object 2 | ( 3 | ) 4 | Array 5 | ( 6 | [0] => 1 7 | [1] => 2 8 | [2] => 3 9 | [3] => 4 10 | [4] => 5 11 | [5] => 6 12 | [6] => 7 13 | [7] => 8 14 | [8] => 9 15 | [9] => 10 16 | ) 17 | Filled Standard Array : 528384 bytes 18 | 1st Filled SPLFixedArray : 0 bytes 19 | 2nd Filled SPLFixedArray : 163968 bytes 20 | Empty Standard Array : 56 bytes 21 | Empty SPLFixedArray : 163968 bytes 22 | int(3334) 23 | -------------------------------------------------------------------------------- /source_code/Chapter 5/repeat.php: -------------------------------------------------------------------------------- 1 | apple 4 | ) 5 | Array 6 | ( 7 | [1] => bacon 8 | [2] => beef 9 | ) 10 | Array 11 | ( 12 | [2] => cheddar 13 | ) 14 | Array 15 | ( 16 | [0] => Array 17 | ( 18 | [0] => apple 19 | ) 20 | 21 | [1] => Array 22 | ( 23 | [1] => bacon 24 | [2] => beef 25 | ) 26 | 27 | [2] => Array 28 | ( 29 | [2] => cheddar 30 | ) 31 | 32 | ) 33 | -------------------------------------------------------------------------------- /source_code/Chapter 2/name-scope.php: -------------------------------------------------------------------------------- 1 | b"; 8 | 9 | } 10 | 11 | return "a"; 12 | 13 | } 14 | 15 | function c() { 16 | 17 | function d() { 18 | 19 | function e() { 20 | 21 | return "c -> d -> e"; 22 | 23 | } 24 | 25 | return "c -> d"; 26 | 27 | } 28 | 29 | return "c"; 30 | 31 | } 32 | 33 | var_dump( a() ); 34 | 35 | var_dump( b() ); 36 | 37 | var_dump( c() ); 38 | 39 | var_dump( d() ); 40 | 41 | var_dump( e() ); 42 | -------------------------------------------------------------------------------- /source_code/Chapter 2/name-scope2.php: -------------------------------------------------------------------------------- 1 | b"; 8 | 9 | } 10 | 11 | return "a"; 12 | 13 | } 14 | 15 | function c() { 16 | 17 | function d() { 18 | 19 | function e() { 20 | 21 | return "c -> d -> e"; 22 | 23 | } 24 | 25 | return "c -> d"; 26 | 27 | } 28 | 29 | return "c"; 30 | 31 | } 32 | 33 | 34 | var_dump( a() ); 35 | 36 | var_dump( b() ); 37 | 38 | var_dump( d() ); 39 | 40 | var_dump( c() ); 41 | 42 | var_dump( e() ); 43 | -------------------------------------------------------------------------------- /source_code/Chapter 7/procedural2-output.txt: -------------------------------------------------------------------------------- 1 | a is 25 2 | We're doing procedural stuff here 3 | We're doing procedural stuff here 4 | We're doing procedural stuff here 5 | We're doing procedural stuff here 6 | We're doing procedural stuff here 7 | int(60) 8 | int(25) 9 | int(50) 10 | a is 50 11 | We're doing procedural stuff here 12 | We're doing procedural stuff here 13 | We're doing procedural stuff here 14 | We're doing procedural stuff here 15 | We're doing procedural stuff here 16 | int(110) 17 | int(25) 18 | int(0) 19 | -------------------------------------------------------------------------------- /source_code/Chapter 2/higher-order2-output.txt: -------------------------------------------------------------------------------- 1 | 5 2 | hello world! 3 | Array 4 | ( 5 | [0] => 7 6 | [1] => 5 7 | [2] => 0 8 | [3] => 12.5 9 | [4] => 0 10 | [5] => 11.9 11 | ) 12 | Array 13 | ( 14 | [0] => 16 15 | [1] => 23 16 | [2] => catch 17 | [3] => 39.5 18 | [4] => orangeish 19 | [5] => 5.46.5 20 | ) 21 | Array 22 | ( 23 | [0] => 16 24 | [1] => 23 25 | [2] => catch 26 | [3] => 39.5 27 | [4] => orangeish 28 | [5] => 5.46.5 29 | ) 30 | That's all, folks! 31 | -------------------------------------------------------------------------------- /source_code/Chapter 2/null-return.php: -------------------------------------------------------------------------------- 1 | '.$text.''; 27 | 28 | } 29 | 30 | $title = prepare_text('testing'); 31 | 32 | echo $title; 33 | -------------------------------------------------------------------------------- /source_code/Chapter 9/install_php.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | # SSH into the VM. Change the xxx's for your VM's IP address 4 | 5 | ssh xxx.xxx.xxx.xxx -l bitnami 6 | 7 | # Install the commands that will let us add a software repository 8 | 9 | sudo apt-get install software-properties-common 10 | 11 | # Now add the repository containing PHP and install PHP 7.1 12 | 13 | sudo add-apt-repository ppa:ondrej/php 14 | 15 | sudo apt-get update 16 | 17 | sudo apt-get install php7.1 18 | 19 | # Check that we have the right version now 20 | 21 | php -v 22 | -------------------------------------------------------------------------------- /source_code/Chapter 9/map_job.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 3 | array(1) { 4 | ["$a"]=> 5 | string(10) "" 6 | } 7 | } 8 | int(8) 9 | int(16) 10 | int(20) 11 | array(4) { 12 | [0]=> 13 | int(2) 14 | [1]=> 15 | int(4) 16 | [2]=> 17 | int(6) 18 | [3]=> 19 | int(8) 20 | } 21 | int(40) 22 | int(20) 23 | PHP Notice: Undefined variable: double in call_anon.php on line 39 24 | PHP Fatal error: Uncaught Error: Function name must be a string in call_anon.php:39 25 | Stack trace: 26 | #0 {main} 27 | thrown in call_anon.php on line 39 28 | -------------------------------------------------------------------------------- /source_code/Chapter 3/recursive_stack-output.txt: -------------------------------------------------------------------------------- 1 | --- 2 | #0 sum(4) called at [recursive_stack.php:17] 3 | --- 4 | #0 sum(3) called at [recursive_stack.php:13] 5 | #1 sum(4) called at [recursive_stack.php:17] 6 | --- 7 | #0 sum(2) called at [recursive_stack.php:13] 8 | #1 sum(3) called at [recursive_stack.php:13] 9 | #2 sum(4) called at [recursive_stack.php:17] 10 | --- 11 | #0 sum(1) called at [recursive_stack.php:13] 12 | #1 sum(2) called at [recursive_stack.php:13] 13 | #2 sum(3) called at [recursive_stack.php:13] 14 | #3 sum(4) called at [recursive_stack.php:17] 15 | The result is : 10 16 | -------------------------------------------------------------------------------- /source_code/Chapter 4/types2.php: -------------------------------------------------------------------------------- 1 | Lamb 4 | [Chili hotness] => mild 5 | [Quantity to make] => 2 portions 6 | [Extras] => Naan bread 7 | [Eat in or take out] => Eat in 8 | ) 9 | Array 10 | ( 11 | [Meat type] => Chicken 12 | [Chili hotness] => hot 13 | [Quantity to make] => 1 portion 14 | [Extras] => Poppadoms 15 | [Eat in or take out] => Take out 16 | ) 17 | Array 18 | ( 19 | [Meat type] => Chicken 20 | [Chili hotness] => Extra mild 21 | [Quantity to make] => Bucket full 22 | [Extras] => Diet cola 23 | [Eat in or take out] => Eat in 24 | ) 25 | -------------------------------------------------------------------------------- /source_code/Chapter 8/pramda-example2-output.txt: -------------------------------------------------------------------------------- 1 | array(11) { 2 | [0]=> 3 | string(6) "Apples" 4 | [1]=> 5 | string(6) "Pastry" 6 | [2]=> 7 | string(5) "Magic" 8 | [3]=> 9 | string(12) "Strawberries" 10 | [4]=> 11 | string(4) "Milk" 12 | [5]=> 13 | string(5) "Sugar" 14 | [6]=> 15 | string(9) "Chocolate" 16 | [7]=> 17 | string(4) "Cake" 18 | [8]=> 19 | string(6) "Cheese" 20 | [9]=> 21 | string(5) "Bread" 22 | [10]=> 23 | string(6) "Butter" 24 | } 25 | int(28) 26 | Array 27 | ( 28 | [Chocolate and Strawberry Cake] => 5.99 29 | [Apple Pie] => 4.99 30 | [Strawberry Ice Cream] => 2.22 31 | ) 32 | -------------------------------------------------------------------------------- /source_code/Chapter 5/parallel-output.txt: -------------------------------------------------------------------------------- 1 | Array 2 | ( 3 | [beard] => 76 4 | [bright] => 43 5 | [buried] => 43 6 | [bred] => 36 7 | [breed] => 35 8 | [bird] => 34 9 | [bride] => 30 10 | [broad] => 15 11 | [bread] => 15 12 | [board] => 15 13 | ) 14 | Launched 4 clients 15 | Array 16 | ( 17 | [beard] => 76 18 | [bright] => 43 19 | [buried] => 43 20 | [bred] => 36 21 | [breed] => 35 22 | [bird] => 34 23 | [bride] => 30 24 | [broad] => 15 25 | [bread] => 15 26 | [board] => 15 27 | ) 28 | string(24) "Single : 48.808692932129" 29 | string(25) "Parallel : 25.10250711441" 30 | -------------------------------------------------------------------------------- /source_code/Chapter 5/parallel-output2.txt: -------------------------------------------------------------------------------- 1 | Array 2 | ( 3 | [beard] => 76 4 | [bright] => 43 5 | [buried] => 43 6 | [bred] => 36 7 | [breed] => 35 8 | [bird] => 34 9 | [bride] => 30 10 | [broad] => 15 11 | [bread] => 15 12 | [board] => 15 13 | ) 14 | Launched 38 clients 15 | Array 16 | ( 17 | [beard] => 76 18 | [bright] => 43 19 | [buried] => 43 20 | [bred] => 36 21 | [breed] => 35 22 | [bird] => 34 23 | [bride] => 30 24 | [broad] => 15 25 | [bread] => 15 26 | [board] => 15 27 | ) 28 | string(24) "Single : 49.230798959732" 29 | string(26) "Parallel : 145.74519586563" 30 | -------------------------------------------------------------------------------- /source_code/Chapter 6/shopping-output.txt: -------------------------------------------------------------------------------- 1 | Your shopping cart contains : 2 | 3 | Item - Quantity - Net Price Each - Total Price inc. Tax 4 | ======================================================= 5 | 6 | Brie - 3 - 7 - 21.42 7 | Shorts - 1 - 9.99 - 10.99 8 | The Dictionary - 2 - 4.99 - 10.48 9 | ======================================================= 10 | 11 | Thank you for your order. 12 | USD 42,89 will be charged to your card when your order is dispatched. 13 | 14 | Tax Summary for this order : 15 | 16 | array(3) { 17 | ["cheeses"]=> 18 | float(0.42) 19 | ["clothes"]=> 20 | float(1) 21 | ["books"]=> 22 | float(0.5) 23 | } 24 | -------------------------------------------------------------------------------- /source_code/Chapter 3/recipe_functions.php: -------------------------------------------------------------------------------- 1 | 6* 4 | [Result] => 8 5 | [Time] => 2.5 6 | ) 7 | Array 8 | ( 9 | [Param] => 6 10 | [Result] => 8 11 | [Time] => 0.7 12 | ) 13 | Array 14 | ( 15 | [Param] => 6* 16 | [Result] => 8 17 | [Time] => 2.5 18 | ) 19 | Array 20 | ( 21 | [Param] => 6 22 | [Result] => 8 23 | [Time] => 0 24 | ) 25 | Array 26 | ( 27 | [Param] => 10 28 | [Result] => 55 29 | [Time] => 0.4 30 | ) 31 | Array 32 | ( 33 | [Param] => 11 34 | [Result] => 89 35 | [Time] => 0.1 36 | ) 37 | Array 38 | ( 39 | [Param] => 8 40 | [Result] => 21 41 | [Time] => 0 42 | ) 43 | -------------------------------------------------------------------------------- /source_code/Chapter 5/memo_mem_example-output.txt: -------------------------------------------------------------------------------- 1 | Array 2 | ( 3 | [Param] => 6* 4 | [Result] => 8 5 | [Time] => 2.51 6 | ) 7 | Array 8 | ( 9 | [Param] => 6 10 | [Result] => 8 11 | [Time] => 0.7 12 | ) 13 | Array 14 | ( 15 | [Param] => 6* 16 | [Result] => 8 17 | [Time] => 2.51 18 | ) 19 | Array 20 | ( 21 | [Param] => 6 22 | [Result] => 8 23 | [Time] => 0 24 | ) 25 | Array 26 | ( 27 | [Param] => 10 28 | [Result] => 55 29 | [Time] => 0.4 30 | ) 31 | Array 32 | ( 33 | [Param] => 11 34 | [Result] => 89 35 | [Time] => 0.1 36 | ) 37 | Array 38 | ( 39 | [Param] => 8 40 | [Result] => 21 41 | [Time] => 0 42 | ) 43 | -------------------------------------------------------------------------------- /source_code/Chapter 2/examine-ouput.txt: -------------------------------------------------------------------------------- 1 | print_r output : 2 | 3 | Array 4 | ( 5 | [0] => 1 6 | [1] => 2 7 | [2] => apple 8 | [3] => banana 9 | [4] => Closure Object 10 | ( 11 | [parameter] => Array 12 | ( 13 | [$data] => 14 | ) 15 | 16 | ) 17 | 18 | ) 19 | 20 | 21 | var_dump output : 22 | 23 | array(5) { 24 | [0]=> 25 | int(1) 26 | [1]=> 27 | int(2) 28 | [2]=> 29 | string(5) "apple" 30 | [3]=> 31 | string(6) "banana" 32 | [4]=> 33 | object(Closure)#1 (1) { 34 | ["parameter"]=> 35 | array(1) { 36 | ["$data"]=> 37 | string(10) "" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Apress Source Code 2 | 3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. 4 | 5 | ## How to Contribute 6 | 7 | 1. Make sure you have a GitHub account. 8 | 2. Fork the repository for the relevant book. 9 | 3. Create a new branch on which to make your change, e.g. 10 | `git checkout -b my_code_contribution` 11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. 12 | 5. Submit a pull request. 13 | 14 | Thank you for your contribution! -------------------------------------------------------------------------------- /source_code/Chapter 4/types4.php: -------------------------------------------------------------------------------- 1 | cod pie 4 | [1] => cod smoothie 5 | [2] => cod tart 6 | [3] => cod ice cream 7 | [4] => cod crumble 8 | [5] => beef pie 9 | [6] => beef smoothie 10 | [7] => beef tart 11 | [8] => beef ice cream 12 | [9] => beef crumble 13 | [10] => kiwi pie 14 | [11] => kiwi smoothie 15 | [12] => kiwi tart 16 | [13] => kiwi ice cream 17 | [14] => kiwi crumble 18 | [15] => egg pie 19 | [16] => egg smoothie 20 | [17] => egg tart 21 | [18] => egg ice cream 22 | [19] => egg crumble 23 | [20] => vinegar pie 24 | [21] => vinegar smoothie 25 | [22] => vinegar tart 26 | [23] => vinegar ice cream 27 | [24] => vinegar crumble 28 | ) 29 | -------------------------------------------------------------------------------- /source_code/Chapter 8/underscore-memoize-output.txt: -------------------------------------------------------------------------------- 1 | Array 2 | ( 3 | [Param] => 6* 4 | [Result] => 8 5 | [Time] => 2.5 6 | ) 7 | Array 8 | ( 9 | [Param] => 6 10 | [Result] => 8 11 | [Time] => 2.5 12 | ) 13 | Array 14 | ( 15 | [Param] => 6* 16 | [Result] => 8 17 | [Time] => 2.5 18 | ) 19 | Array 20 | ( 21 | [Param] => 6 22 | [Result] => 8 23 | [Time] => 0 24 | ) 25 | Array 26 | ( 27 | [Param] => 10 28 | [Result] => 55 29 | [Time] => 17.72 30 | ) 31 | Array 32 | ( 33 | [Param] => 11 34 | [Result] => 89 35 | [Time] => 28.73 36 | ) 37 | Array 38 | ( 39 | [Param] => 8 40 | [Result] => 21 41 | [Time] => 6.71 42 | ) 43 | Array 44 | ( 45 | [Param] => 8 46 | [Result] => 21 47 | [Time] => 0 48 | ) 49 | -------------------------------------------------------------------------------- /source_code/Chapter 8/functionalphp-example-output.txt: -------------------------------------------------------------------------------- 1 | Desserts: 2 | Array 3 | ( 4 | [0] => Apple Pie 5 | [1] => Strawberry Ice Cream 6 | [2] => Chocolate and Strawberry Cake 7 | ) 8 | Main Courses: 9 | Array 10 | ( 11 | [3] => Cheese Toasty 12 | ) 13 | Expensive Items: 14 | Array 15 | ( 16 | [2] => Chocolate and Strawberry Cake 17 | [0] => Apple Pie 18 | [3] => Cheese Toasty 19 | ) 20 | New menu: 21 | Array 22 | ( 23 | [2] => Array 24 | ( 25 | [Item] => Chocolate and Strawberry Cake 26 | [Price] => 5.99 27 | ) 28 | 29 | [0] => Array 30 | ( 31 | [Item] => Apple Pie 32 | [Price] => 4.99 33 | ) 34 | 35 | [3] => Array 36 | ( 37 | [Item] => Cheese Toasty 38 | [Price] => 3.45 39 | ) 40 | 41 | ) 42 | -------------------------------------------------------------------------------- /source_code/Chapter 2/splat-output.txt: -------------------------------------------------------------------------------- 1 | string(6) "apples" 2 | array(3) { 3 | [0]=> 4 | string(5) "bacon" 5 | [1]=> 6 | string(7) "carrots" 7 | [2]=> 8 | string(8) "doughnut" 9 | } 10 | array(4) { 11 | [0]=> 12 | string(3) "red" 13 | [1]=> 14 | string(6) "yellow" 15 | [2]=> 16 | string(4) "pink" 17 | [3]=> 18 | string(5) "green" 19 | } 20 | array(1) { 21 | [0]=> 22 | array(3) { 23 | [0]=> 24 | string(6) "purple" 25 | [1]=> 26 | string(6) "orange" 27 | [2]=> 28 | string(4) "blue" 29 | } 30 | } 31 | array(4) { 32 | [0]=> 33 | string(3) "red" 34 | [1]=> 35 | string(6) "yellow" 36 | [2]=> 37 | string(4) "pink" 38 | [3]=> 39 | string(5) "green" 40 | } 41 | array(3) { 42 | [0]=> 43 | string(6) "purple" 44 | [1]=> 45 | string(6) "orange" 46 | [2]=> 47 | string(4) "blue" 48 | } 49 | -------------------------------------------------------------------------------- /source_code/Chapter 2/splat.php: -------------------------------------------------------------------------------- 1 | "foo", "author" => array("name" => "Bob", "email" => "bob@example.com")), 13 | array("title" => "bar", "author" => array("name" => "Tom", "email" => "tom@example.com")), 14 | array("title" => "baz"), 15 | array("title" => "biz", "author" => array("name" => "Mark", "email" => "mark@example.com")), 16 | ); 17 | 18 | 19 | function index($key) { 20 | return function($array) use ($key) { 21 | return isset($array[$key]) ? $array[$key] : null; 22 | }; 23 | } 24 | 25 | 26 | $postMonad = new MonadPHP\ListMonad($posts); 27 | $names = $postMonad 28 | ->bind(MonadPHP\Maybe::unit) 29 | ->bind(index("author")) 30 | ->bind(index("name")) 31 | ->extract(); 32 | 33 | print_r($names); 34 | -------------------------------------------------------------------------------- /source_code/Chapter 9/setup_scripts.txt: -------------------------------------------------------------------------------- 1 | # Create a directory for our scripts 2 | 3 | mkdir scripts 4 | 5 | # Edit the files and copy/paste the scripts into them 6 | 7 | nano scripts/map_job.php 8 | 9 | nano scripts/reduce_job.php 10 | 11 | # Make sure both we and Hadoop can run them by adding 12 | # execute permissions for everyone. If you install Hadoop on 13 | # another system, depending on the sensitivity of your system 14 | # and scripts, and which user you run Hadoop as, you may want 15 | # to change who has execute permission here. 16 | 17 | chmod a+x scripts/*.php 18 | 19 | # Add the functions file. This is "require"'d by the scripts 20 | # and not exectued directly, so does not need execute permission 21 | 22 | nano scripts/job_functions.php 23 | 24 | # We can test that it all works by using the Standard Streams 25 | # that are available to all shell scripts, using redirection 26 | # and piping. 27 | 28 | php scripts/map_job.php < all_shakespeare.txt | php scripts/reduce_job.php 29 | -------------------------------------------------------------------------------- /source_code/Chapter 4/writer_monad.php: -------------------------------------------------------------------------------- 1 | map( 13 | 14 | function () use ($number) 15 | { 16 | return $number * 2; 17 | } 18 | ); 19 | }; 20 | 21 | 22 | function negate($number) { 23 | 24 | $log = new Monoid(["Negating $number"]); 25 | 26 | return Writer::tell($log)->map( 27 | 28 | function () use ($number) 29 | { 30 | return -$number; 31 | } 32 | ); 33 | }; 34 | 35 | function add_two($number) { 36 | 37 | $log = new Monoid(["Adding 2 to $number"]); 38 | 39 | return Writer::tell($log)->map( 40 | 41 | function () use ($number) 42 | { 43 | return $number + 2; 44 | } 45 | ); 46 | 47 | }; 48 | 49 | 50 | list ($mango_temp, $log) = double(6)->chain('negate')->chain('add_two')->run(); 51 | 52 | echo $mango_temp."°C\nLog :\n"; 53 | 54 | print_r($log->value); 55 | -------------------------------------------------------------------------------- /source_code/Chapter 5/cache_files.txt: -------------------------------------------------------------------------------- 1 | :::::::::::::: 2 | /tmp/memo-cache-10ae24979c5028fa873651bca338152dc0484245 3 | 5 4 | :::::::::::::: 5 | /tmp/memo-cache-1184f5b8d4b6dd08709cf1513f26744167065e0d 6 | 0 7 | :::::::::::::: 8 | /tmp/memo-cache-1fb0856518ee0490ff78e43d1b6dae12ad6ec686 9 | 21 10 | :::::::::::::: 11 | /tmp/memo-cache-2499831338ca5dc8c44f3d063e076799bea9bdff 12 | 1 13 | :::::::::::::: 14 | /tmp/memo-cache-3ad009a144b1e8e065a75ca775c76b2fc2e5ff76 15 | 89 16 | :::::::::::::: 17 | /tmp/memo-cache-4a0a63ce33cc030f270c607ea7bf90a6717572bb 18 | 8 19 | :::::::::::::: 20 | /tmp/memo-cache-7a60554107407bfe358bedce2bfcb95c90a8ea0d 21 | 34 22 | :::::::::::::: 23 | /tmp/memo-cache-8f4e345e7cd51e4e633816f5a52a47df465da189 24 | 3 25 | :::::::::::::: 26 | /tmp/memo-cache-bd703dc0b11593277a5a82dd893f2880b8d0f32a 27 | 13 28 | :::::::::::::: 29 | /tmp/memo-cache-e9310b0c165be166c43d717718981dd6c9379fbe 30 | 55 31 | :::::::::::::: 32 | /tmp/memo-cache-f1e31df9806ce94c5bdbbfff9608324930f4d3f1 33 | 2 34 | :::::::::::::: 35 | /tmp/memo-cache-f629ae44b7b3dcfed444d363e626edf411ec69a8 36 | 1 37 | -------------------------------------------------------------------------------- /source_code/Chapter 8/sabre-example.php: -------------------------------------------------------------------------------- 1 | increment() # 21 24 | 25 | -> increment() # 22 26 | 27 | -> multiply( IInt32\Type::box(3) ) # 66 28 | 29 | -> decrement(); # 65 30 | 31 | # To get the value back out into a standard PHP variable we need to "unbox" it 32 | 33 | var_dump( $boxed_result->unbox() ); # 65 34 | 35 | # And check that the original boxed value object that we chained the 36 | # functions on is unmutated 37 | 38 | var_dump( $boxed_value->unbox() ); # 20 39 | -------------------------------------------------------------------------------- /source_code/Chapter 7/const_array-output.txt: -------------------------------------------------------------------------------- 1 | object(const_array)#1 (1) { 2 | ["stored_array":"const_array":private]=> 3 | array(3) { 4 | ["country"]=> 5 | string(2) "UK" 6 | ["currency"]=> 7 | string(3) "GBP" 8 | ["symbol"]=> 9 | string(2) "£" 10 | } 11 | } 12 | object(const_array)#1 (1) { 13 | ["stored_array":"const_array":private]=> 14 | array(3) { 15 | ["country"]=> 16 | string(2) "UK" 17 | ["currency"]=> 18 | string(3) "GBP" 19 | ["symbol"]=> 20 | string(2) "£" 21 | } 22 | } 23 | Key [country] is set to value [UK] 24 | 25 | Key [currency] is set to value [GBP] 26 | 27 | Key [symbol] is set to value [£] 28 | 29 | The currency symbol is £ 30 | 31 | object(const_array)#2 (1) { 32 | ["stored_array":"const_array":private]=> 33 | array(3) { 34 | ["country"]=> 35 | string(2) "UK" 36 | ["currency"]=> 37 | string(3) "GBP" 38 | ["symbol"]=> 39 | string(2) "£" 40 | } 41 | } 42 | object(const_array)#3 (1) { 43 | ["stored_array":"const_array":private]=> 44 | array(3) { 45 | [0]=> 46 | int(1) 47 | [1]=> 48 | int(2) 49 | [2]=> 50 | int(3) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /source_code/Chapter 5/client.php: -------------------------------------------------------------------------------- 1 | bind($func)->extract() == $func($i) ); 22 | 23 | # 2. bind($monad, unit) == $monad 24 | 25 | // and see if the 2nd Axiom also holds 26 | 27 | var_dump ( $monad->bind(Maybe::unit) == $monad ); 28 | 29 | # 3. bind ( bind($monad, $f1), $f2) == 30 | # bind ($monad, function($i) { return bind( $f1($i), $f2($i) } ) 31 | 32 | 33 | // create some more test functions 34 | 35 | $f1 = function ($i) { return Maybe::unit($i); }; // returns a monad 36 | $f2 = function ($i) { return $i*6; }; 37 | 38 | // and see if Axiom 3 holds 39 | 40 | var_dump ( 41 | 42 | $monad->bind($f1)->bind($f2) == 43 | $monad->bind(function ($i) use ($f1, $f2) 44 | { return $f1($i)->bind($f2); } 45 | ) 46 | 47 | ); 48 | -------------------------------------------------------------------------------- /source_code/Chapter 3/compose.php: -------------------------------------------------------------------------------- 1 | bind($func)->extract() == $func($i) ); 22 | 23 | # 2. bind($monad, unit) == $monad 24 | 25 | // and see if the 2nd Axiom also holds 26 | 27 | var_dump ( $monad->bind(Identity::unit) == $monad ); 28 | 29 | # 3. bind ( bind($monad, $f1), $f2) == 30 | # bind ($monad, function($i) { return bind( $f1($i), $f2($i) } ) 31 | 32 | 33 | // create some more test functions 34 | 35 | $f1 = function ($i) { return Identity::unit($i); }; // returns a monad 36 | $f2 = function ($i) { return $i*6; }; 37 | 38 | // and see if Axiom 3 holds 39 | 40 | var_dump ( 41 | 42 | $monad->bind($f1)->bind($f2) == 43 | $monad->bind(function ($i) use ($f1, $f2) 44 | { return $f1($i)->bind($f2); } 45 | ) 46 | 47 | ); 48 | -------------------------------------------------------------------------------- /source_code/Chapter 3/partial_generator.php: -------------------------------------------------------------------------------- 1 | 'Cheddar', 16 | 'Milk' => 'Whole', 17 | 'Apples' => 'Red', 18 | 'Grapes' => 'White' 19 | ]; 20 | 21 | # The ::from function "loads" an array of data into the chain 22 | 23 | $result = Underscore::from($foods) 24 | 25 | # Let's map a function to uppercase each value and prepend the 26 | # array key to it. 27 | 28 | ->map(function($item,$key) { 29 | 30 | return strtoupper($key.' - '.$item); 31 | 32 | }) 33 | 34 | # Invoke invokes a function over each element like map 35 | 36 | ->invoke(function($item) { var_dump($item);}) 37 | 38 | # Shuffle the order of the array 39 | 40 | ->shuffle() 41 | 42 | # Finally generate the return value for the function chain which 43 | # is the array returned by shuffle() 44 | 45 | ->value(); 46 | 47 | # Output the final array 48 | 49 | var_dump($result); 50 | -------------------------------------------------------------------------------- /source_code/Chapter 2/referential.php: -------------------------------------------------------------------------------- 1 | 50, 'b'=>100] ) ); # 110 38 | # this clearly operated on our parameter "a" and not the global $a 39 | 40 | var_dump ( $a ); # 25 41 | var_dump ( $b ); # 0 - unchanged this time 42 | -------------------------------------------------------------------------------- /source_code/Chapter 3/sums2-output.txt: -------------------------------------------------------------------------------- 1 | -10°C 2 | 3 | Closure Object 4 | ( 5 | [static] => Array 6 | ( 7 | [chain] => Closure Object 8 | ( 9 | [static] => Array 10 | ( 11 | [chain] => Closure Object 12 | ( 13 | [static] => Array 14 | ( 15 | [chain] => identity 16 | [function] => double 17 | ) 18 | 19 | [parameter] => Array 20 | ( 21 | [$input] => 22 | ) 23 | 24 | ) 25 | 26 | [function] => negate 27 | ) 28 | 29 | [parameter] => Array 30 | ( 31 | [$input] => 32 | ) 33 | 34 | ) 35 | 36 | [function] => add_two 37 | ) 38 | 39 | [parameter] => Array 40 | ( 41 | [$input] => 42 | ) 43 | 44 | ) 45 | -------------------------------------------------------------------------------- /source_code/Chapter 8/underscore-memoize.php: -------------------------------------------------------------------------------- 1 | implode($params), 32 | "Result" => $results, 33 | "Time" => $time_taken ]; 34 | 35 | }; 36 | 37 | print_r( $timer( $fibonacci, [6, '*'] ) ); 38 | 39 | print_r( $timer( $memo_fibonacci, [6] ) ); 40 | 41 | print_r( $timer( $fibonacci, [6, '*'] ) ); 42 | 43 | print_r( $timer( $memo_fibonacci, [6] ) ); 44 | 45 | print_r( $timer( $memo_fibonacci, [10] ) ); 46 | 47 | print_r( $timer( $memo_fibonacci, [11] ) ); 48 | 49 | print_r( $timer( $memo_fibonacci, [8] ) ); 50 | 51 | # We'll add an extra call with parameter 8 52 | 53 | print_r( $timer( $memo_fibonacci, [8] ) ); 54 | -------------------------------------------------------------------------------- /source_code/Chapter 5/memo_mem_example.php: -------------------------------------------------------------------------------- 1 | implode($params), 42 | "Result" => $results, 43 | "Time" => $time_taken ]; 44 | 45 | }; 46 | 47 | print_r( $timer( $fibonacci, [6, '*'] ) ); 48 | 49 | print_r( $timer( $memo_fibonacci, [6] ) ); 50 | 51 | print_r( $timer( $fibonacci, [6, '*'] ) ); 52 | 53 | print_r( $timer( $memo_fibonacci, [6] ) ); 54 | 55 | print_r( $timer( $memo_fibonacci, [10] ) ); 56 | 57 | print_r( $timer( $memo_fibonacci, [11] ) ); 58 | 59 | print_r( $timer( $memo_fibonacci, [8] ) ); 60 | -------------------------------------------------------------------------------- /source_code/Chapter 2/higher-order2.php: -------------------------------------------------------------------------------- 1 | chain($file_writer('random.txt')) 59 | ->chain($printer('All done')); 60 | -------------------------------------------------------------------------------- /source_code/Chapter 2/higher-order.php: -------------------------------------------------------------------------------- 1 | filter($is_even) 33 | ->sum() 34 | ); #10+38 = 48 35 | 36 | # We can also instantiate an object to encapsulate our data, 37 | # and call the methods directly on that (which is effectively what the 38 | # static methods do in the background. 39 | 40 | $array = new Arrays($data); 41 | 42 | var_dump( $array->filter($is_even)->sum() ); #48 again 43 | 44 | # The following chain contains a "reverse" function. However no such 45 | # function exists in the library. The library will attempt to use 46 | # native PHP functions for such calls, for arrays it tries to find 47 | # a native function with the same name prefixed by array_, so in 48 | # this case it will use the native array_reverse function. 49 | 50 | var_dump( Arrays::from($data)->reverse()->obtain() ); 51 | -------------------------------------------------------------------------------- /source_code/Chapter 7/static_methods.php: -------------------------------------------------------------------------------- 1 | value = $initial_value; 17 | 18 | } 19 | 20 | public function get_value() { 21 | 22 | return $this->value; 23 | 24 | } 25 | 26 | public function set_value($new_value) { 27 | 28 | $this->value = $new_value; 29 | 30 | } 31 | 32 | # a static method to halve the provided value 33 | 34 | public static function halve($value) { 35 | 36 | return $value / 2; 37 | 38 | } 39 | 40 | } 41 | 42 | # Let's create a new object with an initial value of 25 43 | 44 | $my_object = new new_class(73.4); 45 | 46 | # Let's stack some math functions together including our 47 | # static method above 48 | 49 | $do_math = compose ( 50 | 51 | 'acosh', 52 | 'new_class::halve', 53 | 'floor' 54 | ); 55 | 56 | # Now let's actually do the math. We set the object value 57 | # to the result of $do_math being called on the original value. 58 | 59 | $my_object->set_value( 60 | 61 | $do_math( 62 | 63 | $my_object->get_value() 64 | 65 | ) 66 | ); 67 | 68 | # Show that our object value has been changed. Note that nothing changed 69 | # while we were in our functional (compose) code. 70 | 71 | var_dump ( $my_object->get_value() ); # float(2) 72 | -------------------------------------------------------------------------------- /source_code/Chapter 9/hadoop_commands.txt: -------------------------------------------------------------------------------- 1 | 2 | # We need to know the location of the Hadoop streaming service .jar 3 | # file. The path I've used below works at the time of writing, 4 | # but if the version or path have changed when you try you'll need 5 | # to locate it yourself. To do that, install and run the locate command. 6 | 7 | sudo apt-get install locate 8 | 9 | sudo updatedb 10 | 11 | locate hadoop-streaming 12 | 13 | # Once you've got the location, we're ready to run hadoop. The following 14 | # command is split over several lines using the \ character, make sure 15 | # you include all parts when you run it. 16 | 17 | #The first line tells Hadoop what type of application we want to run. 18 | 19 | # The -input line specifies the HDFS directory where our data resides 20 | # (it will assume all files in that directory contain input data). 21 | 22 | # The -output line specifies an HDFS output directory for the results, it 23 | # must not already exist. 24 | 25 | # The -mapper and -reduce lines specify 26 | # our map and reduce scripts. Use the full directory path/filename. 27 | 28 | hadoop jar /opt/bitnami/hadoop/share/hadoop/tools/lib/hadoop-streaming-2.8.0.jar \ 29 | -input shakespeare \ 30 | -output job_output \ 31 | -mapper /home/bitnami/scripts/map_job.php \ 32 | -reducer /home/bitnami/scripts/reduce_job.php 33 | 34 | # Once Hadoop has run, you can examine the output directory 35 | 36 | hadoop fs -ls /user/hadoop/job_output 37 | 38 | # The main output is stored in part files, for us there is only one. 39 | 40 | hadoop fs -cat /user/hadoop/job_output/part-00000 41 | -------------------------------------------------------------------------------- /source_code/Chapter 5/manual.php: -------------------------------------------------------------------------------- 1 | ['local' => 0, 'country' => 5, 'global' => 10], 20 | 'books' => ['local' => 0, 'country' => 5, 'global' => 5], 21 | 'cheeses' => ['local' => 20, 'country' => 17.5, 'global' =>2] 22 | ]; 23 | }; 24 | 25 | # A list of our products, with their category and price 26 | 27 | $products = function () { 28 | return [ 29 | 30 | 'T-shirt' => [ 'Category' => 'clothes', 'Price' => 15.99 ], 31 | 'Shorts' => ['Category' => 'clothes', 'Price' => 9.99 ], 32 | 'The Dictionary' => ['Category' => 'books', 'Price' => 4.99 ], 33 | 'War and Peace' => ['Category' => 'books', 'Price' => 29.45 ], 34 | 'Camembert' => ['Category' => 'cheeses', 'Price' => 3.50 ], 35 | 'Brie' => ['Category' => 'cheeses', 'Price' => 7.00 ] 36 | 37 | ]; 38 | }; 39 | 40 | # We only sell in dollars, but we format the prices differently 41 | # depending on the location of the purchaser. 42 | 43 | $price_formats = function () { 44 | return [ 45 | 'local' => ['symbol' => '$', 'separator' => '.'], 46 | 'country' => ['symbol' => '$', 'separator' => '.'], 47 | 'global' => ['symbol' => 'USD ', 'separator' => ','] 48 | ]; 49 | }; 50 | -------------------------------------------------------------------------------- /source_code/Chapter 7/transparent.php: -------------------------------------------------------------------------------- 1 | getMessage()."\n"; 43 | 44 | } 45 | 46 | }; 47 | 48 | run( $divide, [10, 2] ); # int(5) 49 | 50 | run( $divide, ["10","2"]); # Type Error, as no type coercion 51 | 52 | run( $divide, [10, 2.5] ); # Type Error, as no type coercion 53 | 54 | run( $divide, [true, false] ); # Type Error, as no type coercion 55 | 56 | run( $divide, [23, null] ); # Division by zero warning & intdiv type error. 57 | # Note that our input parameter is declared an int, and intdiv requires 58 | # an int. But we still get an error, because ints are nullable in 59 | # user function parameters, but not in all PHP function parameters 60 | 61 | run( $divide, [10,3]); # Return Type Error (float 3.3333333...) 62 | 63 | run( $divide, [6.4444 % 4.333, 9.6666 % 2.0003]); # int(2) 64 | # all that matters is the type of the value of an expression passed 65 | # as a parameter, not the types of the operands of that expression. 66 | -------------------------------------------------------------------------------- /source_code/Chapter 3/recursive.php: -------------------------------------------------------------------------------- 1 | $value) { 12 | 13 | # for each value in the array, which check if it 14 | # is infact another array ... 15 | 16 | if (is_array ($value)) { 17 | 18 | # ... in which case we call *this* function 19 | # on the new (sub)array, and add the result 20 | # to the $total. This is the recursive part. 21 | 22 | $total += count_total ($value); 23 | 24 | } else { 25 | 26 | # ... or if it's just a plain old value 27 | # we add that straight to the total 28 | 29 | $total += $value; 30 | 31 | } 32 | 33 | } 34 | 35 | # once we've finished the foreach loop, we will have 36 | # added ALL of the values of the array together, and 37 | # also called count_total() on all of the sub-arrays 38 | # and added that to our total (and each of those 39 | # calls to count_total() will have done the same on 40 | # any sub-arrays within those sub-arrays, and so on) 41 | # so we can return the total. 42 | 43 | return $total; 44 | 45 | }; 46 | 47 | # Let's call it on our original shopping list 48 | 49 | require('shopping_list1.php'); 50 | 51 | echo "List 1 : ".count_total($shopping)."\n"; 52 | 53 | # and then on the list with the apples sub-array 54 | 55 | require('shopping_list2.php'); 56 | 57 | echo "List 2 : ".count_total($shopping)."\n"; 58 | 59 | # and finally on a new list which has sausages broken 60 | # into pork and beef, with pork broken down to a third 61 | # level between chipolatas and cumberland sausages. 62 | 63 | require('shopping_list3.php'); 64 | 65 | echo "List 3 : ".count_total($shopping)."\n"; 66 | -------------------------------------------------------------------------------- /source_code/Chapter 7/clones.php: -------------------------------------------------------------------------------- 1 | value = $initial_value; 17 | 18 | } 19 | 20 | public function get_value() { 21 | 22 | return $this->value; 23 | 24 | } 25 | 26 | public function set_value($new_value) { 27 | 28 | $this->value = $new_value; 29 | 30 | } 31 | 32 | } 33 | 34 | # A function to triple the value of the object 35 | 36 | $triple_object = function ($an_object) { 37 | 38 | # First clone it to make sure we don't mutate the object that 39 | # $an_object refers to 40 | 41 | $cloned_object = clone $an_object; 42 | 43 | # Then set the value to triple the current value 44 | 45 | $cloned_object->set_value( $cloned_object->get_value() * 3 ); 46 | 47 | # and return the new object 48 | 49 | return $cloned_object; 50 | 51 | }; 52 | 53 | # A function to multiply the value of the object by Pi. 54 | # Again we clone the object first and return the mutated clone 55 | 56 | $multiply_object_by_pi = function ($an_object) { 57 | 58 | $cloned_object = clone $an_object; 59 | 60 | $cloned_object->set_value( $cloned_object->get_value() * pi() ); 61 | 62 | return $cloned_object; 63 | 64 | }; 65 | 66 | # Let's create an object encapsulating the value 10. 67 | 68 | $my_object = new my_class(10); 69 | 70 | # We'll compose the above functions together 71 | 72 | $more_maths = compose( 73 | $triple_object, 74 | $multiply_object_by_pi, 75 | $triple_object 76 | ); 77 | 78 | # and then call that composition on our object. 79 | 80 | var_dump ( $more_maths($my_object) ); 81 | 82 | # Let's check our original object remains unchanged 83 | 84 | var_dump ($my_object); 85 | -------------------------------------------------------------------------------- /source_code/Chapter 5/parallel.php: -------------------------------------------------------------------------------- 1 | implode($params), 48 | "Result" => $results, 49 | "Time" => $time_taken ]; 50 | 51 | }; 52 | 53 | # And now let's do a set of runs of both our 54 | # ordinary function and it's memoized sister. 55 | # I've added an extra * parameter to the 56 | # non-memoized runs so that you can spot them 57 | # easier in the output (the '*' isn't used 58 | # by the fibonacci functions, it's just passed 59 | # through to the output of the timer function) 60 | 61 | print_r( $timer( $fibonacci, [6, '*'] ) ); 62 | 63 | print_r( $timer( $memo_fibonacci, [6] ) ); 64 | 65 | print_r( $timer( $fibonacci, [6, '*'] ) ); 66 | 67 | print_r( $timer( $memo_fibonacci, [6] ) ); 68 | 69 | print_r( $timer( $memo_fibonacci, [10] ) ); 70 | 71 | print_r( $timer( $memo_fibonacci, [11] ) ); 72 | 73 | print_r( $timer( $memo_fibonacci, [8] ) ); 74 | -------------------------------------------------------------------------------- /source_code/Chapter 5/generators.php: -------------------------------------------------------------------------------- 1 | memory_get_usage(), "time" => microtime(true)]; 58 | 59 | $end = ["mem" => $func(...$args), "time" => microtime(true)]; 60 | 61 | return [ 62 | "Memory" => $end["mem"] - $start["mem"], 63 | "Time" => $end["time"] - $start["time"] 64 | ]; 65 | }; 66 | 67 | # Finally let's run each of range() and gen_range() 100 times, 68 | # and output the time taken for each and memory used 69 | 70 | Echo "*** range() ***\n"; 71 | 72 | print_r ( $profile($repeat, $run, 100, 'range') ); 73 | 74 | Echo "*** gen_range() ***\n"; 75 | 76 | print_r ( $profile($repeat, $run, 100, 'gen_range') ); 77 | -------------------------------------------------------------------------------- /source_code/Chapter 4/currying.php: -------------------------------------------------------------------------------- 1 | $meat, 15 | "Chili hotness"=>$chili, 16 | "Quantity to make"=>$amount, 17 | "Extras"=>$extras, 18 | "Eat in or take out"=>$where 19 | ]; 20 | }; 21 | 22 | # We think that everyone will want a mild Rogan Josh, so 23 | # let's curry the function with the first two parameters 24 | 25 | $rogan_josh = C\curry($make_a_curry, 'Lamb','mild'); 26 | 27 | # $rogan_josh is now a closure that will continue to 28 | # curry with the arguments we give it 29 | 30 | $dishes = $rogan_josh("2 portions"); 31 | 32 | # likewise $dishes is now a closure that will continue 33 | # to curry 34 | 35 | $meal = $dishes('Naan bread'); 36 | 37 | # and so on for meal. However, we only have 1 parameter 38 | # which we've not used, $where, and so when we add 39 | # that, rather than returning another closure, $meal 40 | # will execute and return the result of $make_a_curry 41 | 42 | $order = $meal('Eat in'); 43 | 44 | print_r( $order ); 45 | 46 | # To show that our original function remains unmutated, when 47 | # we realize that actually people only want 1 portion of curry 48 | # at a time, with popadoms, and they want to eat it at home, we 49 | # can curry it again. This time, the parameters we want to bind 50 | # are at the end, so we use curry_right. 51 | 52 | $meal_type = C\curry_right($make_a_curry, 'Take out', 'Poppadoms', '1 portion'); 53 | 54 | $madrass = $meal_type('hot', 'Chicken'); 55 | 56 | print_r( $madrass ); 57 | 58 | # We could curry the function with all of the parameters 59 | # provided, this creates a parameter-less closure but doesn't 60 | # execute it until we explicitly do so. 61 | 62 | $korma = C\curry($make_a_curry, 63 | 'Chicken', 'Extra mild', 'Bucket full', 'Diet cola', 'Eat in'); 64 | 65 | print_r($korma()); 66 | -------------------------------------------------------------------------------- /source_code/Chapter 4/monad.php: -------------------------------------------------------------------------------- 1 | bind($double); 33 | 34 | # $monad_b should be a new monad object. 35 | # Let's check that it is and that we haven't 36 | # just mutated monad1 37 | 38 | var_dump( $monad_a ); # should be the same as above 39 | 40 | var_dump( $monad_b ); # should be a new monad encapsulating 66 41 | 42 | # This library includes a helper method "extract" to 43 | # get the encapsulated value back out of the monad 44 | 45 | var_dump( $monad_b->extract() ); #66 46 | 47 | # Let's bind that function again to the new monad... 48 | 49 | $monad_c = $monad_b->bind($double); 50 | 51 | var_dump( $monad_c->extract() ); #132 52 | 53 | # ... and check that monad_b is unchanged 54 | 55 | var_dump( $monad_b->extract() ); #66 56 | 57 | # finally, bind the function again to monad_b, 58 | # to demonstrate again that its encapsulated value 59 | # isn't mutated. 60 | 61 | $monad_d = $monad_b->bind($double); 62 | 63 | var_dump( $monad_d->extract() ); #132 64 | 65 | # Let's now repeatedly bind methods 66 | # in a chain 67 | 68 | $monad_e = $monad_d->bind($double) # *2 69 | ->bind($add_ten) # +10 70 | ->bind($add_ten); # +10 71 | 72 | var_dump( $monad_e->extract() ); # 284 73 | 74 | # and take a look at the returned monad_e, 75 | # take note of the object identifier (#7) 76 | 77 | var_dump( $monad_e ); 78 | -------------------------------------------------------------------------------- /source_code/Chapter 7/properties.php: -------------------------------------------------------------------------------- 1 | tax_rate = $rate; 19 | 20 | } 21 | 22 | # Provide a method to set the tax rate at any point 23 | 24 | public function set_rate($rate) { 25 | 26 | $this->tax_rate = $rate; 27 | 28 | } 29 | 30 | # A method to add tax at the $tax_rate to the $amount 31 | 32 | public function add_tax($amount) { 33 | 34 | return $amount * (1 + $this->tax_rate / 100); 35 | 36 | } 37 | 38 | # A method to round the $amount down to the nearest penny 39 | 40 | public function round_to($amount) { 41 | 42 | return floor($amount * 100) / 100; 43 | 44 | } 45 | 46 | # A function to format the $amount for display 47 | 48 | public function display_price($amount) { 49 | 50 | return '£'.$amount.' inc '.$this->tax_rate.'% tax'; 51 | 52 | } 53 | 54 | } 55 | 56 | # So let's create an object for our program containing the 57 | # methods, with the tax rate set at 10% 58 | 59 | $funcs = new tax_functions(10); 60 | 61 | # Now let's compose our methods into a flow that adds tax, rounds 62 | # the figure and then formats it for display. 63 | 64 | # Note that to pass a method of an object as a callable, you need 65 | # to give an array of the object and method name. If you are using 66 | # static class methods, you can use the class::method notation instead 67 | 68 | $add_ten_percent = compose ( 69 | 70 | [$funcs, 'add_tax'], 71 | 72 | [$funcs, 'round_to'], 73 | 74 | [$funcs, 'display_price'] 75 | 76 | ); 77 | 78 | # We've composed our $add_ten_percent function, but we may not want to use it 79 | # until much later in our script. 80 | 81 | # In the mean-time, another programmer inserts the following line in our 82 | # code in between... 83 | 84 | $funcs->set_rate(-20); 85 | 86 | # and then we try to use our $add_ten_percent function to add 87 | # tax to 19.99, hopefully getting the answer £21.98 inc 10% tax 88 | 89 | var_dump( $add_ten_percent(19.99) ); # £15.99 inc -20% tax 90 | -------------------------------------------------------------------------------- /source_code/Chapter 4/bounce.php: -------------------------------------------------------------------------------- 1 | value = $initial_value; 17 | 18 | } 19 | 20 | # Method to get the value 21 | 22 | public function get_value() { 23 | 24 | return $this->value; 25 | 26 | } 27 | 28 | # Method to set the value 29 | 30 | public function set_value($new_value) { 31 | 32 | $this->value = $new_value; 33 | 34 | } 35 | } 36 | 37 | # Let's create a new object with a value of 20 38 | 39 | $my_object = new my_class(20); 40 | 41 | # Check the value 42 | 43 | var_dump ($my_object->get_value()); # int(20) 44 | 45 | # Demonstrate we can mutate the value to 30 46 | 47 | $my_object->set_value(30); 48 | 49 | var_dump ($my_object->get_value()); # int (30) 50 | 51 | # Now let's create a function which doubles the value 52 | # of the object. Note that the function parameter 53 | # doesn't have a "&" to indicate it's passed by reference 54 | 55 | $double_object = function ($an_object) { 56 | 57 | # Get the value from $an_object, double it and set it back 58 | 59 | $an_object->set_value( $an_object->get_value() * 2 ); 60 | 61 | # return the object 62 | 63 | return $an_object; 64 | 65 | }; 66 | 67 | # Now we call the function on our $my_object object from 68 | # above, and assign the returned object to a new variable 69 | 70 | $new_object = $double_object($my_object); 71 | 72 | # Check that the returned object has double the value (30) 73 | # of the object we passed in as a parameter 74 | 75 | var_dump( $new_object->get_value() ); # int(60) 76 | 77 | # Let's just check the value on the original object 78 | 79 | var_dump( $my_object->get_value()); # int(60) 80 | 81 | # It's also changed. Let's var_dump the original object 82 | # and returned object, and check their object reference number 83 | # (look for the number after the #) 84 | 85 | var_dump ($my_object); # #1 86 | 87 | var_dump ($new_object); # #1 88 | 89 | # They're both the same. Just for clarity, create a new 90 | # object from scratch and check it's reference number 91 | 92 | $last_object = new my_class(); 93 | 94 | var_dump ($last_object); # #3 (#2 was our closure object $double_object) 95 | -------------------------------------------------------------------------------- /source_code/Chapter 8/pramda-example.php: -------------------------------------------------------------------------------- 1 | $len; 33 | 34 | }; 35 | 36 | # Create a function to get lines with the word hero in. Pramda doesn't have 37 | # a simple partial function, so instead we curry the function. 38 | 39 | $match_hero = P::curry2($match_word)('hero'); 40 | 41 | # Ditto with a function to get lines with more than 60 chars 42 | 43 | $over_sixty = P::curry2($longer_than)(60); 44 | 45 | # Pramda's own functions are mostly auto-currying (where it make sense), 46 | # and so we can simply call the P::filter method (similar to array_filter) 47 | # with just a callback, which creates a partial/curried function that 48 | # just needs an array to be called with. We don't need to explicitly 49 | # call a currying function on it. 50 | 51 | $filter_hero = P::filter($match_hero); 52 | 53 | $filter_sixty = P::filter($over_sixty); 54 | 55 | $first_three = P::slice(0,3); 56 | 57 | # Now we'll compose these together. Note that we use P::pipe and not P::compose, 58 | # as mentioned above P::compose operates right-to-left, whereas it's easier 59 | # to read left-to-right (or top-to-bottom as we've laid the code out here). 60 | # If you look at the Pramda source code, P::pipe simply reverses the arguments 61 | # and calls P::compose on them! 62 | 63 | $three_long_heros = P::pipe( 64 | 'P::file', //lazy file reader 65 | $filter_hero, 66 | $filter_sixty, 67 | $first_three 68 | ); 69 | 70 | # We call the composed function in the normal way 71 | 72 | $result = $three_long_heros('../Chapter 5/all_shakespeare.txt'); 73 | 74 | print_r($result); 75 | 76 | echo 'Time taken : '.(microtime(true) - $start_time); 77 | -------------------------------------------------------------------------------- /source_code/Chapter 5/filter.php: -------------------------------------------------------------------------------- 1 | $len; 38 | 39 | }; 40 | 41 | # A partial function, fixing hero as the word to search for 42 | 43 | $match_hero = partial($match_word, 'hero'); 44 | 45 | # Another partial function, picking out strings longer than 60 chars 46 | 47 | $over_sixty = partial($longer_than, 60); 48 | 49 | # A partial function which uses array_filter to apply $match_hero 50 | # to all elements of an array and return only those with 'hero' in 51 | 52 | $filter_hero = $partial_right('array_filter', $match_hero ); 53 | 54 | # Similarly, we'll filter an array with the $over_sixty function 55 | 56 | $filter_sixty = $partial_right('array_filter', $over_sixty ); 57 | 58 | # A function to grab the first 3 elements from an array 59 | 60 | $first_three = $partial_right('array_slice', 3, 0); 61 | 62 | # Let's now compose the function above to create a 63 | # function which grabs the first three long 64 | # sentences mentioning hero. 65 | 66 | $three_long_heros = compose( 67 | $filter_hero, 68 | $filter_sixty, 69 | $first_three 70 | ); 71 | 72 | # Finally, let's actually call our composed function 100 times 73 | # on the contents of all_shakespeare.txt 74 | # Note that calling file() as a parameter means that it is 75 | # only evaluated once (and not 100 times), so the time for disk 76 | # IO won't be a major element of our timings 77 | 78 | $result = $repeat( 79 | $three_long_heros, 80 | file('all_shakespeare.txt'), 81 | 100 82 | ); 83 | 84 | # Print out the result of the last call (which should be the 85 | # same as all of the rest, as all of our composed functions are 86 | # pure and are called on exactly the same input parameter) 87 | 88 | print_r($result); 89 | 90 | # and the time taken 91 | 92 | echo 'Time taken : '.(microtime(true) - $start_time); 93 | -------------------------------------------------------------------------------- /source_code/Chapter 6/shopping.php: -------------------------------------------------------------------------------- 1 | 3, 'Shorts' => 1, 'The Dictionary' => 2 ]; 14 | $user = ["location" => 'global']; 15 | 16 | # One common function is to list the contents of the cart. Let's do 17 | # that here 18 | 19 | echo "Your shopping cart contains :\n\n"; 20 | 21 | echo "Item - Quantity - Net Price Each - Total Price inc. Tax\n"; 22 | echo "=======================================================\n\n"; 23 | 24 | foreach ($cart as $product => $quantity) { 25 | 26 | $net_price = $get_net_price($product); 27 | 28 | $total = $get_gross_price($product, $quantity, $user["location"]); 29 | 30 | echo "$product - $quantity - $net_price - $total \n"; 31 | 32 | }; 33 | echo "=======================================================\n\n"; 34 | 35 | # In a confirmation e-mail we may want to just list a (formatted) total price... 36 | 37 | $total_price = array_reduce( array_keys($cart), 38 | 39 | # loop through the cart and add gross price for each item 40 | 41 | function ($running_total, $product) use 42 | ( $user, $get_gross_price, $cart ) { 43 | 44 | return $running_total + 45 | $get_gross_price( $product, 46 | $cart[$product], 47 | $user["location"]); 48 | }, 0); 49 | 50 | echo "Thank you for your order.\n"; 51 | echo $format_price($total_price, $user["location"]).' will '; 52 | echo "be charged to your card when your order is dispatched.\n\n"; 53 | 54 | # And on the backend system we may have a routine that keeps details of 55 | # all the tax charged, ready to send to the Government. Let's create a 56 | # summary of the tax for this order. 57 | 58 | $tax_summary = array_reduce( array_keys($cart), 59 | 60 | # Loop through each item and add the tax charged to the relevant category 61 | 62 | function ($taxes, $product) use 63 | ( $user, $get_tax_charged, $cart, $get_category, $get_product_details ) { 64 | 65 | $category = $get_category($get_product_details($product)); 66 | 67 | $tax = $get_tax_charged($product, $cart[$product], $user["location"]); 68 | 69 | isset($taxes[$category]) ? 70 | $taxes[$category] =+ $tax : $taxes[$category] = $tax; 71 | 72 | return $taxes; 73 | 74 | }, []); 75 | 76 | echo "Tax Summary for this order :\n\n"; 77 | 78 | var_dump($tax_summary); 79 | -------------------------------------------------------------------------------- /source_code/Chapter 6/web_server.php: -------------------------------------------------------------------------------- 1 | setAllowedMethods( EventHttpRequest::CMD_GET ); 23 | 24 | # Next we'll tie our functions we created above to specific URIs using 25 | # function callbacks. We've created them all as anonymous/closure functions 26 | # and so we just bind the variable holding them to the URI. We 27 | # could use named functions if we want, suppling the name in "quotes". 28 | # In fact, you can use any kind of callable here. All will be called 29 | # with the EventHttpRequest object representing the current request as 30 | # the first paramter. If you need other parameters here for your callback, 31 | # you can specify them as an optional third parameter below. 32 | 33 | # Our set of maths functions... 34 | 35 | $http->setCallback("/add", $add); 36 | 37 | $http->setCallback("/subtract", $subtract); 38 | 39 | $http->setCallback("/multiply", $multiply); 40 | 41 | $http->setCallback("/divide", $divide); 42 | 43 | $http->setCallback("/floor", $floor); 44 | 45 | # A function to shut down the server, which needs access to the server $base 46 | 47 | $http->setCallback("/close_server", $close_server, $base); 48 | 49 | # A utility function to explore the headers your browser is sending 50 | 51 | $http->setCallback("/show_headers", $show_headers); 52 | 53 | # And a compulsory function for all internet connected devices 54 | 55 | $http->setCallback("/really/cute", $send_cat); 56 | 57 | 58 | # Finally we'll add a default function callback to handle all other URIs. 59 | # You could, in fact, just specify this default handler and not those 60 | # above, and then handle URIs as you wish from inside this function using 61 | # it as a router function. 62 | 63 | $http->setDefaultCallback($default_handler); 64 | 65 | # We'll bind our script to an address and port to enable it to listen for 66 | # connections. In this case, 0.0.0.0 will bind it to the localhost, and 67 | # we'll choose port 12345 68 | 69 | $http->bind("0.0.0.0", 12345); 70 | 71 | # Then we start our event loop using the loop() function of our base. Our 72 | # script will remain in this loop indefinitely, servicing http requests 73 | # with the functions above, until we exit it by killing the script or, 74 | # more ideally, calling $base->exit() as we do in the close_server() 75 | # function above. 76 | 77 | $base->loop(); 78 | 79 | # We'll only hit this point in the script if some code has called 80 | # $base->exit(); 81 | 82 | echo "Server has been gracefully closed\n"; 83 | -------------------------------------------------------------------------------- /source_code/Chapter 9/job_functions.php: -------------------------------------------------------------------------------- 1 | 1) 53 | 54 | && (metaphone($word) == metaphone('bard')) 55 | 56 | && ( (strlen($word) > 3 ) 57 | 58 | ); 59 | }); 60 | 61 | return array_count_values($filtered); 62 | 63 | }; 64 | 65 | 66 | $only_letters_and_spaces = function($string) { 67 | 68 | return preg_replace('/[^A-Za-z]+/', ' ', $string); 69 | 70 | }; 71 | 72 | $sort_results = function($array) { 73 | 74 | asort($array, SORT_NUMERIC); 75 | 76 | return $array; 77 | 78 | }; 79 | 80 | $top_ten = function ($array) { 81 | 82 | return array_slice($array, 0 ,10); 83 | 84 | }; 85 | 86 | $combine_results = function ($results) { 87 | 88 | return array_reduce($results, function($output, $array) { 89 | 90 | foreach ($array as $word => $count) { 91 | 92 | isset($output[$word]) ? 93 | $output[$word] += $count : 94 | $output[$word] = $count ; 95 | } 96 | 97 | return $output; 98 | 99 | }, []); 100 | 101 | }; 102 | 103 | function identity ($value) { return $value; }; 104 | 105 | function compose(...$functions) 106 | { 107 | return array_reduce( 108 | 109 | 110 | $functions, 111 | 112 | 113 | function ($chain, $function) { 114 | 115 | return function ($input) use ($chain, $function) { 116 | 117 | return $function( $chain($input) ); 118 | 119 | }; 120 | 121 | }, 122 | 123 | 'identity' 124 | 125 | ); 126 | } 127 | -------------------------------------------------------------------------------- /source_code/Chapter 4/maybe_monad.php: -------------------------------------------------------------------------------- 1 | [ "apples" => [ "red" => 3, "green" => 4], "pears" => 4, "bananas" => 6 ], 14 | "bakery" => [ "bread" => 1, "apple pie" => 2], 15 | "meat" => [ "sausages" => 16 | ["pork" => ["chipolata" => 5, "cumberland" => 2], "beef" => 3], 17 | "steaks" => 3, "chorizo" => 1 ] 18 | ]; 19 | 20 | # Let's create some functions. 21 | 22 | # This function takes a category (e.g. fruits) and returns either 23 | # a) a closure that returns that category from the supplied list 24 | # or 25 | # b) null if the category doesn't exist. 26 | 27 | $get_foods = function ($category) { 28 | 29 | return function ($list) use ($category) { 30 | 31 | echo("get_foods return closure called\n"); 32 | 33 | return isset($list[$category]) ? $list[$category] : null; 34 | 35 | }; 36 | 37 | }; 38 | 39 | # This function does the same, except it returns a closure that returns 40 | # the foods (e.g. apples, pears...) from the category (fruit), or null 41 | 42 | 43 | $get_types = function ($food) { 44 | 45 | return function ($category) use ($food) { 46 | 47 | echo("get_types return closure called\n"); 48 | 49 | return isset($category[$food]) ? $category[$food] : null; 50 | 51 | }; 52 | 53 | }; 54 | 55 | # and lastly another function of the same type to get the types of food 56 | # (e.g. red, green) from the food, or null 57 | 58 | $get_count = function ($type) { 59 | 60 | return function ($types) use ($type) { 61 | 62 | echo("get_count return closure called\n"); 63 | 64 | return isset($types[$type]) ? $types[$type] : null; 65 | 66 | }; 67 | 68 | }; 69 | 70 | # Now let's create a Maybe monad, encapsulating our 71 | # shopping list as its value. 72 | 73 | $monad = Maybe::unit($shopping_list); 74 | 75 | # We'll repeatedly bind our functions against it as 76 | # we did in the previous example. 77 | 78 | var_dump( $monad ->bind($get_foods('fruits')) 79 | ->bind($get_types('apples')) 80 | ->bind($get_count('red')) 81 | ->extract() # returns 3 82 | ); 83 | 84 | # None of our closures test for null parameters, so what 85 | # happens if we try to look for something that doesn't exist? 86 | 87 | var_dump( $monad ->bind($get_foods('fruits')) 88 | ->bind($get_types('apples')) 89 | ->bind($get_count('purple')) # doesn't exist 90 | ->extract() # returns null 91 | ); 92 | 93 | var_dump( $monad ->bind($get_foods('cheeses')) # doesn't exist 94 | ->bind($get_types('cheddar')) # doesn't exist 95 | ->bind($get_count('mature')) # doesn't exist 96 | ->extract() # returns null 97 | ); 98 | 99 | var_dump( $monad ->bind($get_foods('bakery')) 100 | ->bind($get_types('pastries')) # doesn't exist 101 | ->bind($get_count('danish')) # doesn't exist 102 | ->extract() # returns null 103 | ); 104 | -------------------------------------------------------------------------------- /source_code/Chapter 5/lazy_filter.php: -------------------------------------------------------------------------------- 1 | $len; 45 | 46 | }; 47 | 48 | $match_hero = partial($match_word, 'hero'); 49 | 50 | $over_sixty = partial($longer_than, 60); 51 | 52 | # Our $filter_hero function is almost the same, 53 | # but note that it calls $lazy_filter instead of 54 | # array_filter (and it uses partial() rather than 55 | # $partial_right, as I've implemented $lazy_filter 56 | # with the parameters in the opposite order to 57 | # array_filter. 58 | 59 | $filter_hero = partial($lazy_filter, $match_hero ); 60 | 61 | # Again $filter_sixty uses $lazy_filter rather than array_filter 62 | 63 | $filter_sixty = partial($lazy_filter, $over_sixty ); 64 | 65 | # As the output from filter_sixty will be a generator object 66 | # rather than an array, we can't use array_slice to 67 | # get the first three items (as data doesn't exist in a 68 | # generator until you call for it). Instead, we'll create 69 | # a $gen_slice function which calls the generator $n times 70 | # and returns the $n returned values as an array. We'll take 71 | # advantage of that fact that a generator is an iterable object, 72 | # and so has current() and next() methods to get each value. 73 | # We'll practice our recursion, rather than just using 74 | # a for loop! 75 | 76 | $gen_slice = function ($n, $output = [], $generator) use (&$gen_slice) { 77 | 78 | $output[] = $generator->current(); 79 | 80 | $generator->next(); 81 | 82 | if ($n > 1) { 83 | 84 | $output = $gen_slice(--$n, $output, $generator); 85 | 86 | } 87 | 88 | return $output; 89 | 90 | }; 91 | 92 | # $first_three uses $gen_slice rather than array_slice 93 | 94 | $first_three = partial($gen_slice, 3, []); 95 | 96 | # We'll compose them together, repeatedly call them 97 | # and output the results using exactly the same 98 | # code as in the non-lazy version 99 | 100 | $three_long_heros = compose( 101 | $filter_hero, 102 | $filter_sixty, 103 | $first_three 104 | ); 105 | 106 | $result = $repeat( $three_long_heros, file('all_shakespeare.txt'), 100 ); 107 | 108 | print_r($result); 109 | 110 | echo 'Time taken : '.(microtime(true) - $start_time); 111 | -------------------------------------------------------------------------------- /source_code/Chapter 9/hadoop_output.txt: -------------------------------------------------------------------------------- 1 | packageJobJar: [/tmp/hadoop-unjar3885899711350629262/] [] /tmp/streamjob5472160291450525372.jar tmpDir=null 2 | 17/06/14 19:45:56 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032 3 | 17/06/14 19:45:57 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032 4 | 17/06/14 19:45:57 INFO mapred.FileInputFormat: Total input files to process : 1 5 | 17/06/14 19:45:57 INFO mapreduce.JobSubmitter: number of splits:2 6 | 17/06/14 19:45:58 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1497468403783_0001 7 | 17/06/14 19:45:58 INFO impl.YarnClientImpl: Submitted application application_1497468403783_0001 8 | 17/06/14 19:45:58 INFO mapreduce.Job: The url to track the job: http://localhost:8088/proxy/application_1497468403783_0001/ 9 | 17/06/14 19:45:58 INFO mapreduce.Job: Running job: job_1497468403783_0001 10 | 17/06/14 19:46:08 INFO mapreduce.Job: Job job_1497468403783_0001 running in uber mode : false 11 | 17/06/14 19:46:08 INFO mapreduce.Job: map 0% reduce 0% 12 | 17/06/14 19:46:19 INFO mapreduce.Job: map 100% reduce 0% 13 | 17/06/14 19:46:25 INFO mapreduce.Job: map 100% reduce 100% 14 | 17/06/14 19:46:26 INFO mapreduce.Job: Job job_1497468403783_0001 completed successfully 15 | 17/06/14 19:46:26 INFO mapreduce.Job: Counters: 49 16 | File System Counters 17 | FILE: Number of bytes read=353 18 | FILE: Number of bytes written=417495 19 | FILE: Number of read operations=0 20 | FILE: Number of large read operations=0 21 | FILE: Number of write operations=0 22 | HDFS: Number of bytes read=3767109 23 | HDFS: Number of bytes written=203 24 | HDFS: Number of read operations=9 25 | HDFS: Number of large read operations=0 26 | HDFS: Number of write operations=2 27 | Job Counters 28 | Launched map tasks=2 29 | Launched reduce tasks=1 30 | Data-local map tasks=2 31 | Total time spent by all maps in occupied slots (ms)=16341 32 | Total time spent by all reduces in occupied slots (ms)=3773 33 | Total time spent by all map tasks (ms)=16341 34 | Total time spent by all reduce tasks (ms)=3773 35 | Total vcore-milliseconds taken by all map tasks=16341 36 | Total vcore-milliseconds taken by all reduce tasks=3773 37 | Total megabyte-milliseconds taken by all map tasks=16733184 38 | Total megabyte-milliseconds taken by all reduce tasks=3863552 39 | Map-Reduce Framework 40 | Map input records=157244 41 | Map output records=2 42 | Map output bytes=341 43 | Map output materialized bytes=359 44 | Input split bytes=234 45 | Combine input records=0 46 | Combine output records=0 47 | Reduce input groups=2 48 | Reduce shuffle bytes=359 49 | Reduce input records=2 50 | Reduce output records=13 51 | Spilled Records=4 52 | Shuffled Maps =2 53 | Failed Shuffles=0 54 | Merged Map outputs=2 55 | GC time elapsed (ms)=261 56 | CPU time spent (ms)=1610 57 | Physical memory (bytes) snapshot=599236608 58 | Virtual memory (bytes) snapshot=5641613312 59 | Total committed heap usage (bytes)=460849152 60 | Shuffle Errors 61 | BAD_ID=0 62 | CONNECTION=0 63 | IO_ERROR=0 64 | WRONG_LENGTH=0 65 | WRONG_MAP=0 66 | WRONG_REDUCE=0 67 | File Input Format Counters 68 | Bytes Read=3766875 69 | File Output Format Counters 70 | Bytes Written=203 71 | 17/06/14 19:46:26 INFO streaming.StreamJob: Output directory: job_output 72 | -------------------------------------------------------------------------------- /source_code/Chapter 5/memoize.php: -------------------------------------------------------------------------------- 1 | 'Apple Pie', 17 | 'Category' => 'Dessert', 18 | 'Price' => 4.99, 19 | 'Ingredients' => ['Apples' => 3, 'Pastry' => 1, 'Magic' => 100] 20 | ], 21 | 22 | [ 'Item' => 'Strawberry Ice Cream', 23 | 'Category' => 'Dessert', 24 | 'Price' => 2.22, 25 | 'Ingredients' => ['Strawberries' => 20, 'Milk' => 10, 'Sugar' => 200] 26 | ], 27 | 28 | [ 'Item' => 'Chocolate and Strawberry Cake', 29 | 'Category' => 'Dessert', 30 | 'Price' => 5.99, 31 | 'Ingredients' => ['Chocolate' => 4, 'Strawberries' => 5, 'Cake' => 4] 32 | ], 33 | 34 | [ 'Item' => 'Cheese Toasty', 35 | 'Category' => 'Main Courses', 36 | 'Price' => 3.45, 37 | 'Ingredients' => ['Cheese' => 5, 'Bread' => 2, 'Butter' => 6] 38 | ] 39 | ]; 40 | 41 | # A function to mark an item as a "special" if it's price is over 5. The 42 | # Phamda functions we use here are : 43 | # P::ifElse - If the first parameter is true, call the second parameter, else 44 | # call the third 45 | # P::lt - If the first parameter (5) is less than the second, then return true 46 | # Note that due to auto currying the $price will be supplied as the second 47 | # parameter, which is why we use lt rather than gt 48 | # P::concat - add the "Special: " string to the price, (called if P::lt returns 49 | # true) 50 | # P::identity - the identity function returns the value passed in, so if P::lt 51 | # returns false this is called, and the price is returned unchanged. 52 | # 53 | # Note that we add ($price) on the end to execute the function straight away 54 | 55 | $specials = function ($price) { 56 | 57 | return P::ifElse(P::lt(5), P::concat('Special: '), P::identity())($price); 58 | 59 | }; 60 | 61 | # A function to format the menu item for our menu 62 | 63 | $format_item = P::pipe( 64 | # Get just the fields that we want for the menu 65 | 66 | P::pick(['Item','Price']), 67 | 68 | # "Evolve" those fields, by applying callbacks to them. 69 | # Item is made into uppercase letters, and Price 70 | # is passed through our $specials function above 71 | # to add Special: to any item that costs over 5 72 | 73 | P::evolve(['Item'=>'strtoupper', 'Price'=>$specials]) 74 | 75 | ); 76 | 77 | # A function to parse our menu data, filter out non-desserts, 78 | # and format the remaining items 79 | 80 | $new_dessert_menu = P::pipe( 81 | 82 | # It would be more robust to use P::contains('Dessert') 83 | # on the P::prop('Category') lest we introduce 84 | # entrées at a later date, but for now to demonstrate 85 | # P::not and the scope of P::contains, we'll do this: 86 | 87 | P::filter( P::not ( P::contains('Main Courses') ) ), 88 | 89 | # Map the $format_item function above onto the remaining 90 | # (hopefully only Dessert) items 91 | 92 | P::map($format_item) 93 | 94 | ); 95 | 96 | # Finally, create our menu 97 | 98 | print_r( $new_dessert_menu( $menu ) ); 99 | -------------------------------------------------------------------------------- /source_code/Chapter 3/map_filter_reduce.php: -------------------------------------------------------------------------------- 1 | strlen($current_longest) ? 61 | $recipe : $current_longest; 62 | 63 | }; 64 | 65 | # the PHP function shuffle is not immutable, it changes the array it is 66 | # given. So we create our own function $reshuffle which is immutable. 67 | # Note that shuffle also has a side effect (it uses an external source 68 | # of entropy to randomise the array), and so is not referentially 69 | # transparent. But it will do for now. 70 | 71 | $reshuffle = function ($array) { shuffle($array); return $array;}; 72 | 73 | # Now we actually do some work. 74 | 75 | # We'll take a shuffled version of $ingredients and $dish_types (to add 76 | # a little variety) and map the $make_recipe function over them, producing 77 | # a new array $all_recipes with some delicious new dishes 78 | 79 | $all_recipes = array_map($make_recipe, 80 | $reshuffle($ingredients), 81 | $reshuffle($dish_types) 82 | ); 83 | 84 | print_r($all_recipes); 85 | 86 | # Everyone knows that only baked foods are nice, so we'll filter 87 | # $all_recipes using the $is_baked function. If $is_baked returns 88 | # false for a recipe, it won't appear in the $baking_recipes output array. 89 | 90 | $baking_recipes = array_filter($all_recipes, $is_baked); 91 | 92 | print_r($baking_recipes); 93 | 94 | # Finally we need to pick our favorite dish, and everyone knows that food 95 | # with the longest name tastes the best. $get_longest compares two strings 96 | # and returns the longest. Array_reduce applies the $get_longest 97 | # function to each element of $baking_recipes in turn, supplying the result 98 | # of the last call to $get_longest and the current array element. After all 99 | # elements have been processed, the result of the last $get_longest call 100 | # must be the longest of all of the elements. It is returned as the output 101 | 102 | $best_recipe = array_reduce($baking_recipes, $get_longest, ''); 103 | 104 | print_r($best_recipe); 105 | -------------------------------------------------------------------------------- /source_code/Chapter 8/functionalphp-example.php: -------------------------------------------------------------------------------- 1 | 'Apple Pie', 25 | 'Category' => 'Dessert', 26 | 'Price' => 4.99, 27 | 'Ingredients' => ['Apples' => 3, 'Pastry' => 1, 'Magic' => 100] 28 | ], 29 | 30 | [ 'Item' => 'Strawberry Ice Cream', 31 | 'Category' => 'Dessert', 32 | 'Price' => 2.22, 33 | 'Ingredients' => ['Strawberries' => 20, 'Milk' => 10, 'Sugar' => 200] 34 | ], 35 | 36 | [ 'Item' => 'Chocolate and Strawberry Cake', 37 | 'Category' => 'Dessert', 38 | 'Price' => 5.99, 39 | 'Ingredients' => ['Chocolate' => 4, 'Strawberries' => 5, 'Cake' => 4] 40 | ], 41 | 42 | [ 'Item' => 'Cheese Toasty', 43 | 'Category' => 'Main Courses', 44 | 'Price' => 3.45, 45 | 'Ingredients' => ['Cheese' => 5, 'Bread' => 2, 'Butter' => 6] 46 | ] 47 | ]; 48 | 49 | # Define a function to check if a food is a dessert, using the contains 50 | # function. Returns true if it's a dessert 51 | 52 | $is_dessert = function ($food) { 53 | return contains($food, 'Dessert'); 54 | }; 55 | 56 | # Using the function above, we can apply it in two different ways to our menu 57 | # data using the select and reject functions. 58 | 59 | $desserts = select($menu, $is_dessert); 60 | 61 | $mains = reject($menu, $is_dessert); 62 | 63 | # A helper function using map and pick to return an array of just item names 64 | 65 | $list_foods = function ($foods) { 66 | 67 | return map($foods, function ($item) { 68 | 69 | return pick($item, 'Item'); 70 | 71 | }); 72 | 73 | }; 74 | 75 | # Output the results of the select and reject statements above, using our 76 | # helper function so we don't dump the whole array contents 77 | 78 | echo "Desserts:\n"; 79 | 80 | print_r ( $list_foods( $desserts ) ); 81 | 82 | echo "Main Courses:\n"; 83 | 84 | print_r ( $list_foods( $mains ) ); 85 | 86 | # Our restaurant is struggling, so we want to dump our cheapest dishes. 87 | # First, we need to use the libraries sort function (with a custom callback 88 | # function) to sort our $menu based on $price. 89 | 90 | $sorted = sort($menu, function($item1,$item2) { 91 | 92 | return $item1["Price"] < $item2["Price"]; 93 | 94 | }, true); 95 | 96 | # Now we want to drop any items that cost less than 3. We use the drop_last 97 | # function to drop the last elements of our sorted array that are >=3 98 | 99 | $expensive_items = drop_last($sorted, function ($item) { 100 | 101 | return $item["Price"] >= 3; 102 | 103 | }); 104 | 105 | # Let's see what we're left with :s 106 | 107 | echo "Expensive Items:\n"; 108 | 109 | print_r( $list_foods( $expensive_items ) ); 110 | 111 | # To create our menu, we want to pick out just the Item and Price, so 112 | # we'll map the select_keys function against each element to pick those out. 113 | 114 | $new_menu = map($expensive_items, function ($item) { 115 | 116 | return select_keys($item, ['Item','Price']); 117 | 118 | }); 119 | 120 | echo "New menu:\n"; 121 | 122 | print_r($new_menu); 123 | -------------------------------------------------------------------------------- /source_code/Chapter 5/spl.php: -------------------------------------------------------------------------------- 1 | current(); 131 | 132 | 133 | if ($value % 3 == 0) { 134 | 135 | # return true to include the value in the output 136 | 137 | return true; 138 | 139 | } 140 | 141 | # or false to filter it out 142 | 143 | return false; 144 | } 145 | 146 | }; 147 | 148 | # Let's use it to filter our previous SPL array 149 | 150 | $nums = new by_three($spl_array); 151 | 152 | var_dump(iterator_count($nums)); # int(3334) (~third of the array is returned) 153 | -------------------------------------------------------------------------------- /source_code/Chapter 8/pramda-example2.php: -------------------------------------------------------------------------------- 1 | 'Apple Pie', 12 | 'Category' => 'Dessert', 13 | 'Price' => 4.99, 14 | 'Ingredients' => ['Apples' => 3, 'Pastry' => 1, 'Magic' => 100] 15 | ], 16 | 17 | [ 'Item' => 'Strawberry Ice Cream', 18 | 'Category' => 'Dessert', 19 | 'Price' => 2.22, 20 | 'Ingredients' => ['Strawberries' => 20, 'Milk' => 10, 'Sugar' => 200] 21 | ], 22 | 23 | [ 'Item' => 'Chocolate and Strawberry Cake', 24 | 'Category' => 'Dessert', 25 | 'Price' => 5.99, 26 | 'Ingredients' => ['Chocolate' => 4, 'Strawberries' => 5, 'Cake' => 4] 27 | ], 28 | 29 | [ 'Item' => 'Cheese Toasty', 30 | 'Category' => 'Main Courses', 31 | 'Price' => 3.45, 32 | 'Ingredients' => ['Cheese' => 5, 'Bread' => 2, 'Butter' => 6] 33 | ] 34 | ]; 35 | 36 | # Let's get a list of all the distinct ingredients used in the menu 37 | 38 | $all_ingredients = P::pipe( 39 | 40 | # get just the ingredient array from each element 41 | 42 | P::pluck('Ingredients'), 43 | 44 | # reduce them into a single array 45 | 46 | P::reduce('P::merge', []), # 47 | 48 | # grab just the keys (the ingredient names) 49 | # which will be unique due to the merge above 50 | 51 | 'array_keys' 52 | 53 | ); 54 | 55 | var_dump( $all_ingredients($menu) ); 56 | 57 | # This time we want to count the quantity of fruit used in our menu, if 58 | # we were making one of each dish 59 | 60 | $fruit = ['Apples' => true, 'Strawberries' => true, 'Plums' => true]; 61 | 62 | # A function to get only items who contain fruit 63 | 64 | $get_only_fruit = function($item) use ($fruit) { 65 | 66 | # P::prop returns an array element with a particular key, in this 67 | # case the element holding an array of Ingredients, from which 68 | # we get the elements which intersect with the keys in $fruit 69 | 70 | return array_intersect_key(P::prop('Ingredients', $item), $fruit); 71 | 72 | }; 73 | 74 | 75 | 76 | $count_fruit = P::pipe ( # compose a function which... 77 | 78 | P::map( # ... maps a function onto the input which 79 | 80 | P::pipe( 81 | 82 | $get_only_fruit, # gets the fruits and 83 | 84 | 'P::sum' # sums the quantities 85 | 86 | ) # for each element/item 87 | 88 | ), 89 | 90 | 'P::sum' # ...and then sums all the quantities 91 | 92 | ); 93 | 94 | 95 | 96 | var_dump( $count_fruit($menu) ); #28 (3 apples, 20 strawberries, 5 strawberries) 97 | 98 | # Now let's say we want to get a dessert menu, ordered by price, 99 | # starting with the most expensive to increase our profits 100 | 101 | $dessert_menu = P::pipe( 102 | 103 | # First, sort the data by price 104 | 105 | P::sort( P::prop('Price') ), 106 | 107 | # Reverse the results so the most expensive is first 108 | 109 | 'P::reverse', 110 | 111 | # Filter the results so that we only have 112 | # desserts 113 | 114 | P::filter( 115 | 116 | function ($value, $key) { 117 | 118 | return P::contains('Dessert', $value); 119 | 120 | } 121 | 122 | ), 123 | 124 | # P::filter returns a generator, but because we need 125 | # to iterate over it twice below, we need to convert 126 | # to an array first 127 | 128 | 'P::toArray', 129 | 130 | # Now let's pick out just the information we want to 131 | # display in our menu 132 | 133 | function ($items) { 134 | 135 | # Get an array of Item names to use as keys, 136 | # and an array of Prices to use as values, 137 | # and array_combine them into a single array. 138 | # Again, P:pluck returns a generator, we want 139 | # an array. 140 | 141 | return array_combine( 142 | 143 | P::toArray( P::pluck('Item',$items) ), 144 | 145 | P::toArray( P::pluck('Price',$items) ) 146 | 147 | ); 148 | 149 | } 150 | ); 151 | 152 | print_r( $dessert_menu($menu) ); 153 | -------------------------------------------------------------------------------- /source_code/Chapter 7/const_array.php: -------------------------------------------------------------------------------- 1 | stored_array)) { 29 | 30 | throw new BadMethodCallException( 31 | 'Constructor called on already created object' 32 | ); 33 | 34 | }; 35 | 36 | # And finally store the array passed in as our immutable array. 37 | 38 | $this->stored_array = $an_array; 39 | 40 | } 41 | 42 | # A function to get the array 43 | 44 | public function get_array() { 45 | 46 | return $this->stored_array; 47 | 48 | } 49 | 50 | # We don't want people to be able to set additional properties on this 51 | # object, as it de facto mutates it by doing so. So we'll throw an 52 | # exception if they try to 53 | 54 | public function __set($key,$val) { 55 | 56 | throw new BadMethodCallException( 57 | 'Attempted to set a new property on immutable class.' 58 | ); 59 | 60 | } 61 | 62 | # Likewise, we don't want people to be able to unset properties, so 63 | # we'll do the same again. As it happens, we don't have any public 64 | # properties, and the methods above stop the user adding any, so 65 | # it's redundant in this case, but here for completeness. 66 | 67 | public function __unset($key) { 68 | 69 | throw new BadMethodCallException( 70 | 'Attempted to unset a property on immutable object.' 71 | ); 72 | 73 | } 74 | 75 | } 76 | 77 | # Let's create a normal array 78 | 79 | $mutable_array = ["country" => "UK", "currency" => "GBP", "symbol" => "£"]; 80 | 81 | # and create an const_array object from it 82 | 83 | $immutable_array = new const_array($mutable_array); 84 | 85 | var_dump ($immutable_array); 86 | 87 | # Let's mutate our original array 88 | 89 | $mutable_array["currency"] = "EURO"; 90 | 91 | # our const_array is unaffected 92 | 93 | var_dump ($immutable_array); 94 | 95 | # We can read the array values like normal 96 | 97 | foreach ( $immutable_array->get_array() as $key => $value) { 98 | 99 | echo "Key [$key] is set to value [$value] \n\n"; 100 | 101 | }; 102 | 103 | # And use dereferencing to get individual elements 104 | 105 | echo "The currency symbol is ". $immutable_array->get_array()["symbol"]."\n\n"; 106 | 107 | # Need to copy it? Just clone it like any other object, and the methods 108 | # which make it immutable will be cloned too. 109 | 110 | $new_array = clone $immutable_array; 111 | 112 | var_dump ($new_array); 113 | 114 | # The following operations aren't permitted though, and will throw exceptions 115 | 116 | # $immutable_array->stored_array = [1,2,3]; 117 | # BadMethodCallException: Attempted to set a new property on immutable class 118 | 119 | # $immutable_array->__construct([1,2,3]); 120 | # BadMethodCallException: Constructor called on already created object 121 | 122 | # unset($immutable_array->get_array); 123 | # BadMethodCallException: Attempted to unset a property on immutable object. 124 | 125 | # $immutable_array->new_prop = [1,2,3]; 126 | # BadMethodCallException: Attempted to set a new property on immutable class 127 | 128 | # $test = new const_array(); 129 | # TypeError: Argument 1 passed to const_array::__construct() 130 | # must be of the type array, none given 131 | 132 | # class my_mutable_array extends const_array { 133 | # 134 | # function set_array ($new_array) { 135 | # 136 | # $this->stored_array = $new_array; 137 | # 138 | # } 139 | # 140 | # }; 141 | # Fatal error: Class my_mutable_array may not inherit from final 142 | # class (const_array) 143 | 144 | # Unfortunately, there is no practical way to stop us overwriting the object 145 | # completely, either by unset()ing it or by assigning a new value to the 146 | # object variable, such as by creating a new const_array on it 147 | 148 | $immutable_array = new const_array([1,2,3]); 149 | 150 | var_dump($immutable_array); # new values stored 151 | -------------------------------------------------------------------------------- /source_code/Chapter 5/functions.php: -------------------------------------------------------------------------------- 1 | 1) 40 | 41 | # ... AND sound like the word "bard" 42 | 43 | && (metaphone($word) == metaphone('bard')) 44 | 45 | # ... AND have more than three characters in them 46 | 47 | && ( (strlen($word) > 3 ) 48 | 49 | ); 50 | }); 51 | 52 | # Finally, count up the number of times each of the filtered 53 | # words appears in the analyzed text, and return that 54 | 55 | return array_count_values($filtered); 56 | 57 | }; 58 | 59 | 60 | # Slice the top 10 items off the top of the array 61 | 62 | $top_ten = function ($array) { 63 | 64 | return array_slice($array, 0 ,10); 65 | 66 | }; 67 | 68 | # Sort the results numerically 69 | 70 | # asort mutates the array, so we wrap it in a function 71 | 72 | $sort_results = function($array) { 73 | 74 | asort($array, SORT_NUMERIC); 75 | 76 | return $array; 77 | 78 | }; 79 | 80 | # The following functions manage the execution of parallel client scripts 81 | 82 | # A function to split the text into chunks and launch the 83 | # appropriate number of clients to process it 84 | 85 | $launch_clients = function ($string) { 86 | 87 | # Split the string into chunks of 1 million characters, 88 | # a value which I found by trial and error to give the 89 | # best results on this machine for this process 90 | 91 | $strings = str_split($string, 1000000); 92 | 93 | # An array to hold the resource identifiers for the client scripts 94 | 95 | $clients = []; 96 | 97 | # Descriptors for "pipes" to read/write the data to/from our client 98 | # scripts 99 | 100 | $descriptors = [ 101 | 0 => ["pipe", "r"], #STDIN, to get data 102 | 1 => ["pipe", "w"] #STDOUT, to send data 103 | ]; 104 | 105 | # Iterate through the chunks... 106 | 107 | foreach ($strings as $key => $string) { 108 | 109 | # $key will be the array index, 0, 1, 2, 3... etc. 110 | # We'll use it as a handy way to number our clients 111 | 112 | # Define the command that runs the client 113 | 114 | $command = "php client.php"; 115 | 116 | # Open the clients with proc_open. This returns a resource identifier. 117 | # We'll store it, although our script won't actually use it. 118 | 119 | $clients[$key]["resource"] = proc_open( $command, 120 | $descriptors, 121 | $clients[$key]["pipes"] 122 | ); 123 | # Note the third parameter above is a variable passed by reference. 124 | # This is used by proc_open to store an array of file pointers 125 | # identifying PHP's end of the pipes that are created. 126 | 127 | # We use that info here to write our text chunk to. This writes 128 | # it to STDOUT, and our client script reads it in through STDIN 129 | # at its end of the pipe. 130 | 131 | fwrite($clients[$key]["pipes"][0], $string); 132 | 133 | # Close the pipe now we're done writing to this client. 134 | 135 | fclose($clients[$key]["pipes"][0]); 136 | 137 | 138 | }; 139 | 140 | # Once all of the clients have been launched, return their 141 | # resource identifiers and pipe details 142 | 143 | return $clients; 144 | }; 145 | 146 | # Simple impure function to report how many clients were 147 | # launched. You could use a writer monad instead if you wanted 148 | 149 | $report_clients = function ($clients) { 150 | 151 | # The escape code at the end minimizes our output when 152 | # when running the script many times, by going up one line 153 | # and overwriting the output each time. 154 | 155 | echo("Launched ".sizeof($clients)." clients\n\033[1A"); 156 | 157 | return $clients; 158 | 159 | }; 160 | 161 | # A function to get the results back from the clients. 162 | # The clients will send a JSON encoded array back to us 163 | 164 | $get_results = function ($clients) { 165 | 166 | # An array to gather the results. Each clients' result 167 | # will be stored as an element of the array 168 | 169 | $results = []; 170 | 171 | # Iterate through the client resource identifiers 172 | 173 | foreach ($clients as $key => $client) { 174 | 175 | # Clients write output to STDOUT, which corresponds to the 176 | # STDIN Pipe at our end. We'll read that JSON data and 177 | # decode it to a PHP array. Each client's results will be 178 | # stored as a separate element of the $results array. 179 | 180 | $results[] = json_decode( 181 | 182 | stream_get_contents($client["pipes"][1]), 183 | 184 | true); 185 | 186 | # We've done reading from the client, so we can close the pipe. 187 | 188 | fclose($clients[$key]["pipes"][1]); 189 | 190 | }; 191 | 192 | # And finally return all of the results from all of the clients 193 | 194 | return $results; 195 | 196 | }; 197 | 198 | # This function takes the results array from $get_results above and 199 | # combines it into a single array 200 | 201 | $combine_results = function ($results) { 202 | 203 | # Reduce and return the input array by... 204 | 205 | return array_reduce($results, function($output, $array) { 206 | 207 | #... iterating over each individual clients results array 208 | # and either creating or adding the count for each word to 209 | # the output depending on whether that word already exists in 210 | # the output 211 | 212 | foreach ($array as $word => $count) { 213 | 214 | isset($output[$word]) ? 215 | $output[$word] += $count : 216 | $output[$word] = $count ; 217 | } 218 | 219 | # return $output through to the next iteration of array_reduce 220 | 221 | return $output; 222 | 223 | }, []); # starting with a blank array [] as output 224 | 225 | }; 226 | -------------------------------------------------------------------------------- /source_code/Chapter 6/server_functions.php: -------------------------------------------------------------------------------- 1 | addHeader ( $name , $value, EventHttpRequest::OUTPUT_HEADER ); 17 | 18 | }; 19 | 20 | # We are going to be serving different types of content (html, images etc.) 21 | # so we need to output a content header each time. Let's create a 22 | # partial function based on $header... 23 | 24 | $content_header = partial($header, 'Content-Type' ); 25 | 26 | # and then make it specific for each type of content... 27 | 28 | $image_header = partial($content_header, "image/jpeg"); 29 | 30 | $text_header = partial($content_header, "text/plain; charset=ISO-8859-1"); 31 | 32 | $html_header = partial($content_header, "text/html; charset=utf-8"); 33 | 34 | # The following function creates a "buffer" to hold our $content and 35 | # then sends it to the browser along with an appropriate HTTP status 36 | # code (Let's assume our requests always work fine so send 200 for everything). 37 | # Note that it's a pure function right up until we call sendReply. You could 38 | # return the EventBuffer instead, and wrap it all into an IO or Writer monad to 39 | # put the impure sendReply at the end if you wish. 40 | 41 | $send_content = function($req, $content) { 42 | 43 | $output = new EventBuffer; 44 | 45 | $output->add($content); 46 | 47 | $req->sendReply(200, "OK", $output); 48 | 49 | }; 50 | 51 | # The input parameters for our maths functions are held in the URI parameters. 52 | # The URI is held in the $req request object as a string. Let's get the 53 | # URI and parse out the parameters into an associative array. 54 | 55 | $parse_uri_params = function ($req) { 56 | 57 | $uri = $req->getUri(); 58 | 59 | parse_str( 60 | 61 | # Grab just the parameters (everything after the ?) 62 | 63 | substr( $uri, strpos( $uri, '?' ) + 1 ), 64 | 65 | # and parse it into $params array 66 | 67 | $params); 68 | 69 | return $params; 70 | 71 | }; 72 | 73 | # Get the URI "value" parameter 74 | 75 | $current_value = function($req) use ($parse_uri_params) { 76 | 77 | return $parse_uri_params($req)["value"]; 78 | 79 | }; 80 | 81 | # Get the URL "amount" parameter 82 | 83 | $amount = function($req) use ($parse_uri_params) { 84 | 85 | return $parse_uri_params($req)["amount"]; 86 | 87 | }; 88 | 89 | # A function to send the results of one of our maths functions which follow. 90 | 91 | $send_sum_results = function($req, $result) use ($html_header, $send_content) { 92 | 93 | # Create some HTML output, with the current result, plus some links 94 | # to perform more maths functions. Note the uri parameters contain 95 | # all of the state needed for the function to give a deterministic, 96 | # reproducable result each time. We also include some links to 97 | # the other utility functions. When you visit them, note that you 98 | # can use your browser back button to come back to the maths functions 99 | # and carry on where you left off, as the parameters the functions 100 | # need are provided by the URI parameters and no "state" has been 101 | # altered of lost 102 | 103 | $output = <<The current value is : $result

106 | 107 |

Add 3

108 |

Add 13

109 |

Add 50

110 |

Subtract 2

111 |

Subtract 5

112 |

Multiply by 2

113 |

Multiply by 4

114 |

Divide by 2

115 |

Divide by 3

116 |

Floor

117 | 118 |

[Show headers]  119 | [Get cat]  120 | [Close down server]

121 | 122 | ENDCONTENT; 123 | 124 | # Send the content header and content. 125 | 126 | $html_header($req); 127 | 128 | $send_content($req, $output); 129 | 130 | }; 131 | 132 | # These are our key maths functions. Each one operates like a good Functional 133 | # function by only using the values supplied as input parameters, in this 134 | # case as part of $req. We call a couple of helper functions ($current_value 135 | # and $amount) to help extract those values, $req isn't necessarily 136 | # immutable (we could alter values or call methods), but we'll use 137 | # our discipline to keep it so right up until we're ready to send_contents. 138 | # While we don't formally "return" a value, $send_sum_results effectively 139 | # acts a return statement for us. Any return value would simply go back to 140 | # libevent (which is the caller, and it just ignore it). 141 | # If we want to keep to strictly using explicit return statements, we could 142 | # wrap this in another function that does the same as $send_sum_results, (and 143 | # for the same reason wouldn't have a return statement) or we could create an 144 | # Writer monad or similar to gather the results and only output to the browser 145 | # at the end. For this simple example we'll go with using $send_sum_results 146 | # though for simplicity and clarity. 147 | 148 | $add = function ($req) use ($send_sum_results, $current_value, $amount) { 149 | 150 | $send_sum_results($req, $current_value($req) + $amount($req) ); 151 | 152 | }; 153 | 154 | $subtract = function ($req) use ($send_sum_results, $current_value, $amount) { 155 | 156 | $send_sum_results($req, $current_value($req) - $amount($req) ); 157 | 158 | }; 159 | 160 | $multiply = function ($req) use ($send_sum_results, $current_value, $amount) { 161 | 162 | $send_sum_results($req, $current_value($req) * $amount($req) ); 163 | 164 | }; 165 | 166 | $divide = function ($req) use ($send_sum_results, $current_value, $amount) { 167 | 168 | $send_sum_results($req, $current_value($req) / $amount($req) ); 169 | 170 | }; 171 | 172 | $floor = function ($req) use ($send_sum_results, $current_value) { 173 | 174 | $send_sum_results($req, floor($current_value($req)) ); 175 | 176 | }; 177 | 178 | # Now we'll define some utility functions 179 | 180 | # Grab the HTTP headers from the current request and return them as an array 181 | 182 | $get_input_headers = function ($req) { 183 | 184 | return $req->getInputHeaders(); 185 | 186 | }; 187 | 188 | # A recursive function to loop through an array of headers and return 189 | # an HTML formatted string 190 | 191 | $format_headers = function ($headers, $output = '') use (&$format_headers) { 192 | 193 | # if we've done all the headers, return the $output 194 | 195 | if (!$headers) { 196 | 197 | return $output; 198 | 199 | } else { 200 | 201 | # else grab a header off the top of the array, add it to the 202 | # $output and recursively call this function on the remaining headers. 203 | 204 | $output .= '
'.array_shift($headers).'
'; 205 | 206 | return $format_headers($headers, $output); 207 | 208 | }; 209 | 210 | }; 211 | 212 | # Use the function above to format the headers of the current request for 213 | # viewing 214 | 215 | $show_headers = function ($req) use ($html_header, $send_content, $format_headers) { 216 | 217 | $html_header($req); 218 | 219 | $send_content($req, $format_headers( $req->getInputHeaders() ) ); 220 | 221 | }; 222 | 223 | # Let's handle all requests, so there are no 404's 224 | 225 | $default_handler = function ($req) use ($html_header, $send_content) { 226 | 227 | $html_header($req); 228 | 229 | $output = '

This is the default response

'; 230 | 231 | $output .= '

Why not try some math

'; 232 | 233 | $send_content($req, $output); 234 | 235 | }; 236 | 237 | # Ensure that there are sufficient supplies of cat pictures available 238 | # in all corners of the Internet 239 | 240 | $send_cat = function($req) use ($image_header, $send_content) { 241 | 242 | # Note we send a different header so that the browser knows 243 | # a binary image is coming 244 | 245 | $image_header($req); 246 | 247 | # An impure function, you could alway use an IO monad or 248 | # embed the image binary data here! 249 | 250 | $send_content($req, file_get_contents('cat.jpg')); 251 | 252 | }; 253 | 254 | # A function to shut down the web server script by visiting a particular URI. 255 | 256 | $close_server = function($req, $base) use ($html_header, $send_content) { 257 | 258 | $html_header($req); 259 | 260 | $send_content($req, '

Server is now shutting down

'); 261 | 262 | $base->exit(); 263 | 264 | }; 265 | --------------------------------------------------------------------------------