├── 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 |
--------------------------------------------------------------------------------