├── LICENSE.txt ├── README.md └── code ├── 001-begin.php ├── 002-extract_method.php ├── 003-rename_variables.php ├── 004-rename_method.php ├── 005-move_method.php ├── 006-replace_temp_with_query.php ├── 007-extract_method.php ├── 008-replace_temp_with_query.php ├── 009-replace_temp_with_query.php ├── 010-multiple_statement_types.php ├── 011-move_method.php ├── 012-move_method.php ├── 013-replace_type_code_with_state.php ├── 014-move_method.php ├── 015-repl_conditional_w_polymorphism.php └── 016-repl_conditional_w_polymorphism.php /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Adam R Culp (geekyboy.com refactoring101.com) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Refactoring101 2 | ============== 3 | 4 | The code here, represented as steps, is the progression of multiple refactorings on a legacy codebase. The files were created to go along with a "Refactoring 101" talk (outdated slides found at [http://www.slideshare.net/adamculp/refactoring-23666462](http://www.slideshare.net/adamculp/refactoring-23666462)), as a PHP version of the Java code shown in Martin Fowler's book "Refactoring". [https://www.refactoring.com/](https://www.refactoring.com/) 5 | 6 | More recently, I created a collection of refactoring videos on my Beachcasts YouTube Channel. These can be found at [https://www.youtube.com/playlist?list=PL6_nF0awZMoNH0_9n_Qjq925IB5RGiB2g](https://www.youtube.com/playlist?list=PL6_nF0awZMoNH0_9n_Qjq925IB5RGiB2g) 7 | 8 | The code is based on the PHP 7.4 which includes types, so older versions of PHP may not work as expected. If you notice errors preventing usage, please let me know. 9 | 10 | IMPORTANT: 11 | 12 | * In an IDE, ignore the warnings of duplicated code fragments. Those are is expected. 13 | * While this series of files/steps highlights common refactorings, it is not representative of "great code" or a finished product. But is in fact a "work in progress". By that I mean there is still more refactoring/work to be done which is beyond the scope of this project. These files are meant for training only, and not meant to be a completed project. 14 | * For brevity of this training material, some common code conventions, and/or PHP conventions were not followed. Such as one class per file, and proper namespaces. 15 | * Also for brevity, this code should not be thought of as the optimal way to create logic. After any refactor, performance should also be taken into account. 16 | 17 | Also note the whitespace is not how I would normally handle it. However, since this code will be used in presentation slides for a talk I have handled whitespace differently so code blocks are compact, thus enabling code to be shown larger on the slides. 18 | 19 | Files/Steps 20 | ----------- 21 | 22 | * 001-begin.php 23 | * 002-extract_method.php 24 | * 003-rename_variables.php 25 | * 004-rename_method.php 26 | * 005-move_method.php 27 | * 006-replace_temp_with_query.php 28 | * 007-extract_method.php 29 | * 008-replace_temp_with_query.php 30 | * 009-replace_temp_with_query.php 31 | * 010-multiple_statement_types.php 32 | * 011-move_method.php 33 | * 012-move_method.php 34 | * 013-replace_type_code_with_state.php 35 | * 014-move_method.php 36 | * 015-repl_conditional_w_polymorphism.php 37 | * 016-repl_conditional_w_polymorphism.php 38 | 39 | How to use these files: 40 | ----------------------- 41 | 42 | These files are best used by doing a side-by-side diff-like view of the previous and next versions. I have included a doc block in the head of each file to explains the changes, and in some cases why they were done. (Which also coincides with the book mentioned earlier.) However, it is best to look at the code itself to see what was done with each iteration. 43 | 44 | Enjoy! 45 | -------------------------------------------------------------------------------- /code/001-begin.php: -------------------------------------------------------------------------------- 1 | name = $name; 16 | } 17 | 18 | public function addRental(Rental $rental) { 19 | $this->rentals[] = $rental; 20 | } 21 | 22 | public function getName(): string { 23 | return $this->name; 24 | } 25 | 26 | public function statement(): string { 27 | $totalAmount = 0; 28 | $frequentRenterPoints = 0; 29 | $result = "Rental Record for " . $this->getName() . "\n"; 30 | 31 | foreach ($this->rentals as $each) { 32 | 33 | $thisAmount = 0; 34 | 35 | switch ($each->movie->getPriceCode()) { 36 | case Movie::REGULAR: 37 | $thisAmount += 2; 38 | if ($each->getDaysRented() > 2) { 39 | $thisAmount += ($each->getDaysRented() - 2) * 1.5; 40 | } 41 | break; 42 | 43 | case Movie::NEW_RELEASE: 44 | $thisAmount += $each->getDaysRented() * 3; 45 | break; 46 | 47 | case Movie::CHILDREN: 48 | $thisAmount += 1.5; 49 | if ($each->getDaysRented() > 3) { 50 | $thisAmount += ($each->getDaysRented() - 3) * 1.5; 51 | } 52 | break; 53 | } 54 | 55 | $frequentRenterPoints++; 56 | 57 | // add bonus for a two-day new release rental 58 | if (($each->movie->getPriceCode() == Movie::NEW_RELEASE) && ($each->getDaysRented() > 1)) { 59 | $frequentRenterPoints++; 60 | } 61 | 62 | $result .= "\t" . $each->movie->getTitle() . "\t" . $thisAmount . "\n"; 63 | $totalAmount += $thisAmount; 64 | } 65 | 66 | // add footer lines 67 | $result .= "Amount owed is " . $totalAmount . "\n"; 68 | $result .= "You earned " . $frequentRenterPoints . " frequent renter points"; 69 | 70 | return $result; 71 | } 72 | } 73 | 74 | class Movie { 75 | const CHILDREN = 2; 76 | const REGULAR = 0; 77 | const NEW_RELEASE = 1; 78 | 79 | protected string $title; 80 | protected int $priceCode; 81 | 82 | public function __construct($title, $priceCode) { 83 | $this->title = $title; 84 | $this->setPriceCode($priceCode); 85 | } 86 | 87 | public function getPriceCode(): int { 88 | return $this->priceCode; 89 | } 90 | 91 | public function setPriceCode($priceCode) { 92 | $this->priceCode = $priceCode; 93 | } 94 | 95 | public function getTitle(): string { 96 | return $this->title; 97 | } 98 | } 99 | 100 | class Rental { 101 | public Movie $movie; 102 | protected int $daysRented; 103 | 104 | public function __construct(Movie $movie, $daysRented) { 105 | $this->movie = $movie; 106 | $this->daysRented = $daysRented; 107 | } 108 | 109 | public function getDaysRented(): int { 110 | return $this->daysRented; 111 | } 112 | 113 | public function getMovie(): Movie { 114 | return $this->movie; 115 | } 116 | } 117 | 118 | 119 | // define customer 120 | $customer = new Customer('Adam Culp'); 121 | 122 | // choose movie to be rented, define rental, add it to the customer 123 | $movie = new Movie('Gladiator', 0); 124 | $rental = new Rental($movie, 1); 125 | $customer->addRental($rental); 126 | 127 | // choose 2nd movie to be rented, define rental, add it to the customer 128 | $movie = new Movie('Spiderman', 1); 129 | $rental = new Rental($movie, 2); 130 | $customer->addRental($rental); 131 | 132 | // print the statement 133 | echo $customer->statement(); 134 | -------------------------------------------------------------------------------- /code/002-extract_method.php: -------------------------------------------------------------------------------- 1 | statement() to its own method named amountFor(). Then we call this 5 | * new amountFor() method while passing it $each as a param, which is a Rental object. 6 | * 7 | * For a video showing this step, see: https://youtu.be/mtBrzU13Yqc 8 | */ 9 | 10 | namespace Refactoring002; 11 | 12 | class Customer { 13 | protected string $name; 14 | protected array $rentals; 15 | 16 | public function __construct($name) { 17 | $this->name = $name; 18 | } 19 | 20 | public function addRental(Rental $rental) { 21 | $this->rentals[] = $rental; 22 | } 23 | 24 | public function getName(): string { 25 | return $this->name; 26 | } 27 | 28 | public function statement(): string { 29 | $totalAmount = 0; 30 | $frequentRenterPoints = 0; 31 | $result = "Rental Record for " . $this->getName() . "\n"; 32 | 33 | foreach ($this->rentals as $each) { 34 | 35 | $thisAmount = $this->amountFor($each); 36 | 37 | $frequentRenterPoints++; 38 | 39 | // add bonus for a two-day new release rental 40 | if (($each->movie->getPriceCode() == Movie::NEW_RELEASE) && ($each->getDaysRented() > 1)) { 41 | $frequentRenterPoints++; 42 | } 43 | 44 | $result .= "\t" . $each->movie->getTitle() . "\t" . $thisAmount . "\n"; 45 | $totalAmount += $thisAmount; 46 | } 47 | 48 | // add footer lines 49 | $result .= "Amount owed is " . $totalAmount . "\n"; 50 | $result .= "You earned " . $frequentRenterPoints . " frequent renter points"; 51 | 52 | return $result; 53 | } 54 | 55 | public function amountFor($each): float { 56 | $thisAmount = 0; 57 | 58 | switch ($each->movie->getPriceCode()) { 59 | case Movie::REGULAR: 60 | $thisAmount += 2; 61 | if ($each->getDaysRented() > 2) { 62 | $thisAmount += ($each->getDaysRented() - 2) * 1.5; 63 | } 64 | break; 65 | 66 | case Movie::NEW_RELEASE: 67 | $thisAmount += $each->getDaysRented() * 3; 68 | break; 69 | 70 | case Movie::CHILDREN: 71 | $thisAmount += 1.5; 72 | if ($each->getDaysRented() > 3) { 73 | $thisAmount += ($each->getDaysRented() - 3) * 1.5; 74 | } 75 | break; 76 | } 77 | 78 | return $thisAmount; 79 | } 80 | } 81 | 82 | class Movie { 83 | const CHILDREN = 2; 84 | const REGULAR = 0; 85 | const NEW_RELEASE = 1; 86 | 87 | protected string $title; 88 | protected int $priceCode; 89 | 90 | public function __construct($title, $priceCode) { 91 | $this->title = $title; 92 | $this->setPriceCode($priceCode); 93 | } 94 | 95 | public function getPriceCode(): int { 96 | return $this->priceCode; 97 | } 98 | 99 | public function setPriceCode($priceCode) { 100 | $this->priceCode = $priceCode; 101 | } 102 | 103 | public function getTitle(): string { 104 | return $this->title; 105 | } 106 | } 107 | 108 | class Rental { 109 | public Movie $movie; 110 | protected int $daysRented; 111 | 112 | public function __construct(Movie $movie, $daysRented) { 113 | $this->movie = $movie; 114 | $this->daysRented = $daysRented; 115 | } 116 | 117 | public function getDaysRented(): int { 118 | return $this->daysRented; 119 | } 120 | 121 | public function getMovie(): Movie { 122 | return $this->movie; 123 | } 124 | } 125 | 126 | 127 | // define customer 128 | $customer = new Customer('Adam Culp'); 129 | 130 | // choose movie to be rented, define rental, add it to the customer 131 | $movie = new Movie('Gladiator', 0); 132 | $rental = new Rental($movie, 1); 133 | $customer->addRental($rental); 134 | 135 | // choose 2nd movie to be rented, define rental, add it to the customer 136 | $movie = new Movie('Spiderman', 1); 137 | $rental = new Rental($movie, 2); 138 | $customer->addRental($rental); 139 | 140 | // print the statement 141 | echo $customer->statement(); 142 | -------------------------------------------------------------------------------- /code/003-rename_variables.php: -------------------------------------------------------------------------------- 1 | statement() and in Customer->amountFor() 7 | * 2 - $thisAmount to $result in Customer->amountFor() 8 | * 9 | * For a video showing this step, see: https://youtu.be/_GbB73VPRfY 10 | */ 11 | 12 | namespace Refactoring003; 13 | 14 | class Customer { 15 | protected string $name; 16 | protected array $rentals; 17 | 18 | public function __construct($name) { 19 | $this->name = $name; 20 | } 21 | 22 | public function addRental(Rental $rental) { 23 | $this->rentals[] = $rental; 24 | } 25 | 26 | public function getName(): string { 27 | return $this->name; 28 | } 29 | 30 | public function statement(): string { 31 | $totalAmount = 0; 32 | $frequentRenterPoints = 0; 33 | $result = "Rental Record for " . $this->getName() . "\n"; 34 | 35 | foreach ($this->rentals as $rental) { 36 | 37 | $thisAmount = $this->amountFor($rental); 38 | 39 | $frequentRenterPoints++; 40 | 41 | // add bonus for a two-day new release rental 42 | if (($rental->movie->getPriceCode() == Movie::NEW_RELEASE) && ($rental->getDaysRented() > 1)) { 43 | $frequentRenterPoints++; 44 | } 45 | 46 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $thisAmount . "\n"; 47 | $totalAmount += $thisAmount; 48 | } 49 | 50 | // add footer lines 51 | $result .= "Amount owed is " . $totalAmount . "\n"; 52 | $result .= "You earned " . $frequentRenterPoints . " frequent renter points"; 53 | 54 | return $result; 55 | } 56 | 57 | public function amountFor($rental): float { 58 | $result = 0; 59 | 60 | switch ($rental->movie->getPriceCode()) { 61 | case Movie::REGULAR: 62 | $result += 2; 63 | if ($rental->getDaysRented() > 2) { 64 | $result += ($rental->getDaysRented() - 2) * 1.5; 65 | } 66 | break; 67 | 68 | case Movie::NEW_RELEASE: 69 | $result += $rental->getDaysRented() * 3; 70 | break; 71 | 72 | case Movie::CHILDREN: 73 | $result += 1.5; 74 | if ($rental->getDaysRented() > 3) { 75 | $result += ($rental->getDaysRented() - 3) * 1.5; 76 | } 77 | break; 78 | } 79 | 80 | return $result; 81 | } 82 | } 83 | 84 | class Movie { 85 | const CHILDREN = 2; 86 | const REGULAR = 0; 87 | const NEW_RELEASE = 1; 88 | 89 | protected string $title; 90 | protected int $priceCode; 91 | 92 | public function __construct($title, $priceCode) { 93 | $this->title = $title; 94 | $this->setPriceCode($priceCode); 95 | } 96 | 97 | public function getPriceCode(): int { 98 | return $this->priceCode; 99 | } 100 | 101 | public function setPriceCode($priceCode) { 102 | $this->priceCode = $priceCode; 103 | } 104 | 105 | public function getTitle(): string { 106 | return $this->title; 107 | } 108 | } 109 | 110 | class Rental { 111 | public Movie $movie; 112 | protected int $daysRented; 113 | 114 | public function __construct(Movie $movie, $daysRented) { 115 | $this->movie = $movie; 116 | $this->daysRented = $daysRented; 117 | } 118 | 119 | public function getDaysRented(): int { 120 | return $this->daysRented; 121 | } 122 | 123 | public function getMovie(): Movie { 124 | return $this->movie; 125 | } 126 | } 127 | 128 | 129 | // define customer 130 | $customer = new Customer('Adam Culp'); 131 | 132 | // choose movie to be rented, define rental, add it to the customer 133 | $movie = new Movie('Gladiator', 0); 134 | $rental = new Rental($movie, 1); 135 | $customer->addRental($rental); 136 | 137 | // choose 2nd movie to be rented, define rental, add it to the customer 138 | $movie = new Movie('Spiderman', 1); 139 | $rental = new Rental($movie, 2); 140 | $customer->addRental($rental); 141 | 142 | // print the statement 143 | echo $customer->statement(); 144 | -------------------------------------------------------------------------------- /code/004-rename_method.php: -------------------------------------------------------------------------------- 1 | amountFor() to Customer->getCharge() 7 | * 2 - Then we change all calls to the method to use new naming 8 | * 9 | * For a video showing this step, see: https://youtu.be/_GbB73VPRfY 10 | */ 11 | 12 | namespace Refactoring004; 13 | 14 | class Customer { 15 | protected string $name; 16 | protected array $rentals; 17 | 18 | public function __construct($name) { 19 | $this->name = $name; 20 | } 21 | 22 | public function addRental(Rental $rental) { 23 | $this->rentals[] = $rental; 24 | } 25 | 26 | public function getName(): string { 27 | return $this->name; 28 | } 29 | 30 | public function statement(): string { 31 | $totalAmount = 0; 32 | $frequentRenterPoints = 0; 33 | $result = "Rental Record for " . $this->getName() . "\n"; 34 | 35 | foreach ($this->rentals as $rental) { 36 | 37 | $thisAmount = $this->getCharge($rental); 38 | 39 | $frequentRenterPoints++; 40 | 41 | // add bonus for a two-day new release rental 42 | if (($rental->movie->getPriceCode() == Movie::NEW_RELEASE) && ($rental->getDaysRented() > 1)) { 43 | $frequentRenterPoints++; 44 | } 45 | 46 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $thisAmount . "\n"; 47 | $totalAmount += $thisAmount; 48 | } 49 | 50 | // add footer lines 51 | $result .= "Amount owed is " . $totalAmount . "\n"; 52 | $result .= "You earned " . $frequentRenterPoints . " frequent renter points"; 53 | 54 | return $result; 55 | } 56 | 57 | public function getCharge($rental): float { 58 | $result = 0; 59 | 60 | switch ($rental->movie->getPriceCode()) { 61 | case Movie::REGULAR: 62 | $result += 2; 63 | if ($rental->getDaysRented() > 2) { 64 | $result += ($rental->getDaysRented() - 2) * 1.5; 65 | } 66 | break; 67 | 68 | case Movie::NEW_RELEASE: 69 | $result += $rental->getDaysRented() * 3; 70 | break; 71 | 72 | case Movie::CHILDREN: 73 | $result += 1.5; 74 | if ($rental->getDaysRented() > 3) { 75 | $result += ($rental->getDaysRented() - 3) * 1.5; 76 | } 77 | break; 78 | } 79 | 80 | return $result; 81 | } 82 | } 83 | 84 | class Movie { 85 | const CHILDREN = 2; 86 | const REGULAR = 0; 87 | const NEW_RELEASE = 1; 88 | 89 | protected string $title; 90 | protected int $priceCode; 91 | 92 | public function __construct($title, $priceCode) { 93 | $this->title = $title; 94 | $this->setPriceCode($priceCode); 95 | } 96 | 97 | public function getPriceCode(): int { 98 | return $this->priceCode; 99 | } 100 | 101 | public function setPriceCode($priceCode) { 102 | $this->priceCode = $priceCode; 103 | } 104 | 105 | public function getTitle(): string { 106 | return $this->title; 107 | } 108 | } 109 | 110 | class Rental { 111 | public Movie $movie; 112 | protected int $daysRented; 113 | 114 | public function __construct(Movie $movie, $daysRented) { 115 | $this->movie = $movie; 116 | $this->daysRented = $daysRented; 117 | } 118 | 119 | public function getDaysRented(): int { 120 | return $this->daysRented; 121 | } 122 | 123 | public function getMovie(): Movie { 124 | return $this->movie; 125 | } 126 | } 127 | 128 | 129 | // define customer 130 | $customer = new Customer('Adam Culp'); 131 | 132 | // choose movie to be rented, define rental, add it to the customer 133 | $movie = new Movie('Gladiator', 0); 134 | $rental = new Rental($movie, 1); 135 | $customer->addRental($rental); 136 | 137 | // choose 2nd movie to be rented, define rental, add it to the customer 138 | $movie = new Movie('Spiderman', 1); 139 | $rental = new Rental($movie, 2); 140 | $customer->addRental($rental); 141 | 142 | // print the statement 143 | echo $customer->statement(); 144 | -------------------------------------------------------------------------------- /code/005-move_method.php: -------------------------------------------------------------------------------- 1 | getCharge() method and move it 4 | * to the Rental class since it's using information from Rental (Movie object and 5 | * $daysRented), and uses no information from the customer. Keeps the method with 6 | * the data. 7 | * 8 | * Now we calculate the price for each movie through the Rental object. 9 | * 10 | * For a video showing this step, see: https://youtu.be/KCVHVuWkihk 11 | */ 12 | 13 | namespace Refactoring005; 14 | 15 | class Customer { 16 | protected string $name; 17 | protected array $rentals; 18 | 19 | public function __construct($name) { 20 | $this->name = $name; 21 | } 22 | 23 | public function addRental(Rental $rental) { 24 | $this->rentals[] = $rental; 25 | } 26 | 27 | public function getName(): string { 28 | return $this->name; 29 | } 30 | 31 | public function statement(): string { 32 | $totalAmount = 0; 33 | $frequentRenterPoints = 0; 34 | $result = "Rental Record for " . $this->getName() . "\n"; 35 | 36 | foreach ($this->rentals as $rental) { 37 | 38 | $thisAmount = $rental->getCharge(); 39 | 40 | $frequentRenterPoints++; 41 | 42 | // add bonus for a two-day new release rental 43 | if (($rental->movie->getPriceCode() == Movie::NEW_RELEASE) && ($rental->getDaysRented() > 1)) { 44 | $frequentRenterPoints++; 45 | } 46 | 47 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $thisAmount . "\n"; 48 | $totalAmount += $thisAmount; 49 | } 50 | 51 | // add footer lines 52 | $result .= "Amount owed is " . $totalAmount . "\n"; 53 | $result .= "You earned " . $frequentRenterPoints . " frequent renter points"; 54 | 55 | return $result; 56 | } 57 | } 58 | 59 | class Movie { 60 | const CHILDREN = 2; 61 | const REGULAR = 0; 62 | const NEW_RELEASE = 1; 63 | 64 | protected string $title; 65 | protected int $priceCode; 66 | 67 | public function __construct($title, $priceCode) { 68 | $this->title = $title; 69 | $this->setPriceCode($priceCode); 70 | } 71 | 72 | public function getPriceCode(): int { 73 | return $this->priceCode; 74 | } 75 | 76 | public function setPriceCode($priceCode) { 77 | $this->priceCode = $priceCode; 78 | } 79 | 80 | public function getTitle(): string { 81 | return $this->title; 82 | } 83 | } 84 | 85 | class Rental { 86 | public Movie $movie; 87 | protected int $daysRented; 88 | 89 | public function __construct(Movie $movie, $daysRented) { 90 | $this->movie = $movie; 91 | $this->daysRented = $daysRented; 92 | } 93 | 94 | public function getDaysRented(): int { 95 | return $this->daysRented; 96 | } 97 | 98 | public function getMovie(): Movie { 99 | return $this->movie; 100 | } 101 | 102 | public function getCharge(): float { 103 | $result = 0; 104 | 105 | switch ($this->movie->getPriceCode()) { 106 | case Movie::REGULAR: 107 | $result += 2; 108 | if ($this->getDaysRented() > 2) { 109 | $result += ($this->getDaysRented() - 2) * 1.5; 110 | } 111 | break; 112 | 113 | case Movie::NEW_RELEASE: 114 | $result += $this->getDaysRented() * 3; 115 | break; 116 | 117 | case Movie::CHILDREN: 118 | $result += 1.5; 119 | if ($this->getDaysRented() > 3) { 120 | $result += ($this->getDaysRented() - 3) * 1.5; 121 | } 122 | break; 123 | } 124 | 125 | return $result; 126 | } 127 | } 128 | 129 | 130 | // define customer 131 | $customer = new Customer('Adam Culp'); 132 | 133 | // choose movie to be rented, define rental, add it to the customer 134 | $movie = new Movie('Gladiator', 0); 135 | $rental = new Rental($movie, 1); 136 | $customer->addRental($rental); 137 | 138 | // choose 2nd movie to be rented, define rental, add it to the customer 139 | $movie = new Movie('Spiderman', 1); 140 | $rental = new Rental($movie, 2); 141 | $customer->addRental($rental); 142 | 143 | // print the statement 144 | echo $customer->statement(); 145 | -------------------------------------------------------------------------------- /code/006-replace_temp_with_query.php: -------------------------------------------------------------------------------- 1 | getCharge() instead. 5 | * 6 | * Remember, we are refactoring, not optimizing. Refactor first, then optimize 7 | * later if/when needed. 8 | * 9 | */ 10 | 11 | namespace Refactoring006; 12 | 13 | class Customer { 14 | protected string $name; 15 | protected array $rentals; 16 | 17 | public function __construct($name) { 18 | $this->name = $name; 19 | } 20 | 21 | public function addRental(Rental $rental) { 22 | $this->rentals[] = $rental; 23 | } 24 | 25 | public function getName(): string { 26 | return $this->name; 27 | } 28 | 29 | public function statement(): string { 30 | $totalAmount = 0; 31 | $frequentRenterPoints = 0; 32 | $result = "Rental Record for " . $this->getName() . "\n"; 33 | 34 | foreach ($this->rentals as $rental) { 35 | 36 | $frequentRenterPoints++; 37 | 38 | // add bonus for a two-day new release rental 39 | if (($rental->movie->getPriceCode() == Movie::NEW_RELEASE) && ($rental->getDaysRented() > 1)) { 40 | $frequentRenterPoints++; 41 | } 42 | 43 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $rental->getCharge() . "\n"; 44 | $totalAmount += $rental->getCharge(); 45 | } 46 | 47 | // add footer lines 48 | $result .= "Amount owed is " . $totalAmount . "\n"; 49 | $result .= "You earned " . $frequentRenterPoints . " frequent renter points"; 50 | 51 | return $result; 52 | } 53 | } 54 | 55 | class Movie { 56 | const CHILDREN = 2; 57 | const REGULAR = 0; 58 | const NEW_RELEASE = 1; 59 | 60 | protected string $title; 61 | protected int $priceCode; 62 | 63 | public function __construct($title, $priceCode) { 64 | $this->title = $title; 65 | $this->setPriceCode($priceCode); 66 | } 67 | 68 | public function getPriceCode(): int { 69 | return $this->priceCode; 70 | } 71 | 72 | public function setPriceCode($priceCode) { 73 | $this->priceCode = $priceCode; 74 | } 75 | 76 | public function getTitle(): string { 77 | return $this->title; 78 | } 79 | } 80 | 81 | class Rental { 82 | public Movie $movie; 83 | protected int $daysRented; 84 | 85 | public function __construct(Movie $movie, $daysRented) { 86 | $this->movie = $movie; 87 | $this->daysRented = $daysRented; 88 | } 89 | 90 | public function getDaysRented(): int { 91 | return $this->daysRented; 92 | } 93 | 94 | public function getMovie(): Movie { 95 | return $this->movie; 96 | } 97 | 98 | public function getCharge(): float { 99 | $result = 0; 100 | 101 | switch ($this->movie->getPriceCode()) { 102 | case Movie::REGULAR: 103 | $result += 2; 104 | if ($this->getDaysRented() > 2) { 105 | $result += ($this->getDaysRented() - 2) * 1.5; 106 | } 107 | break; 108 | 109 | case Movie::NEW_RELEASE: 110 | $result += $this->getDaysRented() * 3; 111 | break; 112 | 113 | case Movie::CHILDREN: 114 | $result += 1.5; 115 | if ($this->getDaysRented() > 3) { 116 | $result += ($this->getDaysRented() - 3) * 1.5; 117 | } 118 | break; 119 | } 120 | 121 | return $result; 122 | } 123 | } 124 | 125 | 126 | // define customer 127 | $customer = new Customer('Adam Culp'); 128 | 129 | // choose movie to be rented, define rental, add it to the customer 130 | $movie = new Movie('Gladiator', 0); 131 | $rental = new Rental($movie, 1); 132 | $customer->addRental($rental); 133 | 134 | // choose 2nd movie to be rented, define rental, add it to the customer 135 | $movie = new Movie('Spiderman', 1); 136 | $rental = new Rental($movie, 2); 137 | $customer->addRental($rental); 138 | 139 | // print the statement 140 | echo $customer->statement(); 141 | -------------------------------------------------------------------------------- /code/007-extract_method.php: -------------------------------------------------------------------------------- 1 | name = $name; 17 | } 18 | 19 | public function addRental(Rental $rental) { 20 | $this->rentals[] = $rental; 21 | } 22 | 23 | public function getName(): string { 24 | return $this->name; 25 | } 26 | 27 | public function statement(): string { 28 | $totalAmount = 0; 29 | $frequentRenterPoints = 0; 30 | $result = "Rental Record for " . $this->getName() . "\n"; 31 | 32 | foreach ($this->rentals as $rental) { 33 | $frequentRenterPoints += $rental->getFrequentRenterPoints(); 34 | 35 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $rental->getCharge() . "\n"; 36 | $totalAmount += $rental->getCharge(); 37 | } 38 | 39 | // add footer lines 40 | $result .= "Amount owed is " . $totalAmount . "\n"; 41 | $result .= "You earned " . $frequentRenterPoints . " frequent renter points"; 42 | 43 | return $result; 44 | } 45 | } 46 | 47 | class Movie { 48 | const CHILDREN = 2; 49 | const REGULAR = 0; 50 | const NEW_RELEASE = 1; 51 | 52 | protected string $title; 53 | protected int $priceCode; 54 | 55 | public function __construct($title, $priceCode) { 56 | $this->title = $title; 57 | $this->setPriceCode($priceCode); 58 | } 59 | 60 | public function getPriceCode(): int { 61 | return $this->priceCode; 62 | } 63 | 64 | public function setPriceCode($priceCode) { 65 | $this->priceCode = $priceCode; 66 | } 67 | 68 | public function getTitle(): string { 69 | return $this->title; 70 | } 71 | } 72 | 73 | class Rental { 74 | public Movie $movie; 75 | protected int $daysRented; 76 | 77 | public function __construct(Movie $movie, $daysRented) { 78 | $this->movie = $movie; 79 | $this->daysRented = $daysRented; 80 | } 81 | 82 | public function getDaysRented(): int { 83 | return $this->daysRented; 84 | } 85 | 86 | public function getMovie(): Movie { 87 | return $this->movie; 88 | } 89 | 90 | public function getCharge(): float { 91 | $result = 0; 92 | 93 | switch ($this->movie->getPriceCode()) { 94 | case Movie::REGULAR: 95 | $result += 2; 96 | if ($this->getDaysRented() > 2) { 97 | $result += ($this->getDaysRented() - 2) * 1.5; 98 | } 99 | break; 100 | 101 | case Movie::NEW_RELEASE: 102 | $result += $this->getDaysRented() * 3; 103 | break; 104 | 105 | case Movie::CHILDREN: 106 | $result += 1.5; 107 | if ($this->getDaysRented() > 3) { 108 | $result += ($this->getDaysRented() - 3) * 1.5; 109 | } 110 | break; 111 | } 112 | 113 | return $result; 114 | } 115 | 116 | public function getFrequentRenterPoints(): int { 117 | $result = 0; 118 | 119 | // add bonus for a two-day new release rental 120 | if (($this->movie->getPriceCode() == Movie::NEW_RELEASE) && ($this->getDaysRented() > 1)) { 121 | $result += 2; 122 | } else { 123 | $result += 1; 124 | } 125 | 126 | return $result; 127 | } 128 | } 129 | 130 | // define customer 131 | $customer = new Customer('Adam Culp'); 132 | 133 | // choose movie to be rented, define rental, add it to the customer 134 | $movie = new Movie('Gladiator', 0); 135 | $rental = new Rental($movie, 1); 136 | $customer->addRental($rental); 137 | 138 | // choose 2nd movie to be rented, define rental, add it to the customer 139 | $movie = new Movie('Spiderman', 1); 140 | $rental = new Rental($movie, 2); 141 | $customer->addRental($rental); 142 | 143 | // print the statement 144 | echo $customer->statement(); 145 | -------------------------------------------------------------------------------- /code/008-replace_temp_with_query.php: -------------------------------------------------------------------------------- 1 | name = $name; 17 | } 18 | 19 | public function addRental(Rental $rental) { 20 | $this->rentals[] = $rental; 21 | } 22 | 23 | public function getName(): string { 24 | return $this->name; 25 | } 26 | 27 | public function statement(): string { 28 | $frequentRenterPoints = 0; 29 | $result = "Rental Record for " . $this->getName() . "\n"; 30 | 31 | foreach ($this->rentals as $rental) { 32 | $frequentRenterPoints += $rental->getFrequentRenterPoints(); 33 | 34 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $rental->getCharge() . "\n"; 35 | } 36 | 37 | // add footer lines 38 | $result .= "Amount owed is " . $this->getTotalCharge() . "\n"; 39 | $result .= "You earned " . $frequentRenterPoints . " frequent renter points"; 40 | 41 | return $result; 42 | } 43 | 44 | public function getTotalCharge(): float { 45 | $result = 0; 46 | 47 | foreach ($this->rentals as $rental) { 48 | $result += $rental->getCharge(); 49 | } 50 | 51 | return $result; 52 | } 53 | } 54 | 55 | class Movie { 56 | const CHILDREN = 2; 57 | const REGULAR = 0; 58 | const NEW_RELEASE = 1; 59 | 60 | protected string $title; 61 | protected int $priceCode; 62 | 63 | public function __construct($title, $priceCode) { 64 | $this->title = $title; 65 | $this->setPriceCode($priceCode); 66 | } 67 | 68 | public function getPriceCode(): int { 69 | return $this->priceCode; 70 | } 71 | 72 | public function setPriceCode($priceCode) { 73 | $this->priceCode = $priceCode; 74 | } 75 | 76 | public function getTitle(): string { 77 | return $this->title; 78 | } 79 | } 80 | 81 | class Rental { 82 | public Movie $movie; 83 | protected int $daysRented; 84 | 85 | public function __construct(Movie $movie, $daysRented) { 86 | $this->movie = $movie; 87 | $this->daysRented = $daysRented; 88 | } 89 | 90 | public function getDaysRented(): int { 91 | return $this->daysRented; 92 | } 93 | 94 | public function getMovie(): Movie { 95 | return $this->movie; 96 | } 97 | 98 | public function getCharge(): float { 99 | $result = 0; 100 | 101 | switch ($this->movie->getPriceCode()) { 102 | case Movie::REGULAR: 103 | $result += 2; 104 | if ($this->getDaysRented() > 2) { 105 | $result += ($this->getDaysRented() - 2) * 1.5; 106 | } 107 | break; 108 | 109 | case Movie::NEW_RELEASE: 110 | $result += $this->getDaysRented() * 3; 111 | break; 112 | 113 | case Movie::CHILDREN: 114 | $result += 1.5; 115 | if ($this->getDaysRented() > 3) { 116 | $result += ($this->getDaysRented() - 3) * 1.5; 117 | } 118 | break; 119 | } 120 | 121 | return $result; 122 | } 123 | 124 | public function getFrequentRenterPoints(): int { 125 | $result = 0; 126 | 127 | // add bonus for a two-day new release rental 128 | if (($this->movie->getPriceCode() == Movie::NEW_RELEASE) && ($this->getDaysRented() > 1)) { 129 | $result += 2; 130 | } else { 131 | $result += 1; 132 | } 133 | 134 | return $result; 135 | } 136 | } 137 | 138 | 139 | // define customer 140 | $customer = new Customer('Adam Culp'); 141 | 142 | // choose movie to be rented, define rental, add it to the customer 143 | $movie = new Movie('Gladiator', 0); 144 | $rental = new Rental($movie, 1); 145 | $customer->addRental($rental); 146 | 147 | // choose 2nd movie to be rented, define rental, add it to the customer 148 | $movie = new Movie('Spiderman', 1); 149 | $rental = new Rental($movie, 2); 150 | $customer->addRental($rental); 151 | 152 | // print the statement 153 | echo $customer->statement(); 154 | -------------------------------------------------------------------------------- /code/009-replace_temp_with_query.php: -------------------------------------------------------------------------------- 1 | name = $name; 17 | } 18 | 19 | public function addRental(Rental $rental) { 20 | $this->rentals[] = $rental; 21 | } 22 | 23 | public function getName(): string { 24 | return $this->name; 25 | } 26 | 27 | public function statement(): string { 28 | $result = "Rental Record for " . $this->getName() . "\n"; 29 | 30 | foreach ($this->rentals as $rental) { 31 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $rental->getCharge() . "\n"; 32 | } 33 | 34 | // add footer lines 35 | $result .= "Amount owed is " . $this->getTotalCharge() . "\n"; 36 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points"; 37 | 38 | return $result; 39 | } 40 | 41 | public function getTotalFrequentRenterPoints(): int { 42 | $result = 0; 43 | 44 | foreach ($this->rentals as $rental) { 45 | $result += $rental->getFrequentRenterPoints(); 46 | } 47 | 48 | return $result; 49 | } 50 | 51 | public function getTotalCharge(): float { 52 | $result = 0; 53 | 54 | foreach ($this->rentals as $rental) { 55 | $result += $rental->getCharge(); 56 | } 57 | 58 | return $result; 59 | } 60 | } 61 | 62 | class Movie { 63 | const CHILDREN = 2; 64 | const REGULAR = 0; 65 | const NEW_RELEASE = 1; 66 | 67 | protected string $title; 68 | protected int $priceCode; 69 | 70 | public function __construct($title, $priceCode) { 71 | $this->title = $title; 72 | $this->setPriceCode($priceCode); 73 | } 74 | 75 | public function getPriceCode(): int { 76 | return $this->priceCode; 77 | } 78 | 79 | public function setPriceCode($priceCode) { 80 | $this->priceCode = $priceCode; 81 | } 82 | 83 | public function getTitle(): string { 84 | return $this->title; 85 | } 86 | } 87 | 88 | class Rental { 89 | public Movie $movie; 90 | protected int $daysRented; 91 | 92 | public function __construct(Movie $movie, $daysRented) { 93 | $this->movie = $movie; 94 | $this->daysRented = $daysRented; 95 | } 96 | 97 | public function getDaysRented(): int { 98 | return $this->daysRented; 99 | } 100 | 101 | public function getMovie(): Movie { 102 | return $this->movie; 103 | } 104 | 105 | public function getCharge(): float { 106 | $result = 0; 107 | 108 | switch ($this->movie->getPriceCode()) { 109 | case Movie::REGULAR: 110 | $result += 2; 111 | if ($this->getDaysRented() > 2) { 112 | $result += ($this->getDaysRented() - 2) * 1.5; 113 | } 114 | break; 115 | 116 | case Movie::NEW_RELEASE: 117 | $result += $this->getDaysRented() * 3; 118 | break; 119 | 120 | case Movie::CHILDREN: 121 | $result += 1.5; 122 | if ($this->getDaysRented() > 3) { 123 | $result += ($this->getDaysRented() - 3) * 1.5; 124 | } 125 | break; 126 | } 127 | 128 | return $result; 129 | } 130 | 131 | public function getFrequentRenterPoints(): int { 132 | $result = 0; 133 | 134 | // add bonus for a two-day new release rental 135 | if (($this->movie->getPriceCode() == Movie::NEW_RELEASE) && ($this->getDaysRented() > 1)) { 136 | $result += 2; 137 | } else { 138 | $result += 1; 139 | } 140 | 141 | return $result; 142 | } 143 | } 144 | 145 | 146 | // define customer 147 | $customer = new Customer('Adam Culp'); 148 | 149 | // choose movie to be rented, define rental, add it to the customer 150 | $movie = new Movie('Gladiator', 0); 151 | $rental = new Rental($movie, 1); 152 | $customer->addRental($rental); 153 | 154 | // choose 2nd movie to be rented, define rental, add it to the customer 155 | $movie = new Movie('Spiderman', 1); 156 | $rental = new Rental($movie, 2); 157 | $customer->addRental($rental); 158 | 159 | // print the statement 160 | echo $customer->statement(); 161 | -------------------------------------------------------------------------------- /code/010-multiple_statement_types.php: -------------------------------------------------------------------------------- 1 | name = $name; 24 | } 25 | 26 | public function addRental(Rental $rental) { 27 | $this->rentals[] = $rental; 28 | } 29 | 30 | public function getName(): string { 31 | return $this->name; 32 | } 33 | 34 | public function statementText(): string { 35 | $result = "Rental Record for " . $this->getName() . "\n"; 36 | 37 | foreach ($this->rentals as $rental) { 38 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $rental->getCharge() . "\n"; 39 | } 40 | 41 | // add footer lines 42 | $result .= "Amount owed is " . $this->getTotalCharge() . "\n"; 43 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points"; 44 | 45 | return $result; 46 | } 47 | 48 | public function statementHtml(): string { 49 | $result = "
Amount owed is " . $this->getTotalCharge() . "
\n"; 57 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points
"; 58 | 59 | return $result; 60 | } 61 | 62 | public function getTotalFrequentRenterPoints(): int { 63 | $result = 0; 64 | 65 | foreach ($this->rentals as $rental) { 66 | $result += $rental->getFrequentRenterPoints(); 67 | } 68 | 69 | return $result; 70 | } 71 | 72 | public function getTotalCharge(): float { 73 | $result = 0; 74 | 75 | foreach ($this->rentals as $rental) { 76 | $result += $rental->getCharge(); 77 | } 78 | 79 | return $result; 80 | } 81 | } 82 | 83 | class Movie { 84 | const CHILDREN = 2; 85 | const REGULAR = 0; 86 | const NEW_RELEASE = 1; 87 | 88 | protected string $title; 89 | protected int $priceCode; 90 | 91 | public function __construct($title, $priceCode) { 92 | $this->title = $title; 93 | $this->setPriceCode($priceCode); 94 | } 95 | 96 | public function getPriceCode(): int { 97 | return $this->priceCode; 98 | } 99 | 100 | public function setPriceCode($priceCode) { 101 | $this->priceCode = $priceCode; 102 | } 103 | 104 | public function getTitle(): string { 105 | return $this->title; 106 | } 107 | } 108 | 109 | class Rental { 110 | public Movie $movie; 111 | protected int $daysRented; 112 | 113 | public function __construct(Movie $movie, $daysRented) { 114 | $this->movie = $movie; 115 | $this->daysRented = $daysRented; 116 | } 117 | 118 | public function getDaysRented(): int { 119 | return $this->daysRented; 120 | } 121 | 122 | public function getMovie(): Movie { 123 | return $this->movie; 124 | } 125 | 126 | public function getCharge(): float { 127 | $result = 0; 128 | 129 | switch ($this->movie->getPriceCode()) { 130 | case Movie::REGULAR: 131 | $result += 2; 132 | if ($this->getDaysRented() > 2) { 133 | $result += ($this->getDaysRented() - 2) * 1.5; 134 | } 135 | break; 136 | 137 | case Movie::NEW_RELEASE: 138 | $result += $this->getDaysRented() * 3; 139 | break; 140 | 141 | case Movie::CHILDREN: 142 | $result += 1.5; 143 | if ($this->getDaysRented() > 3) { 144 | $result += ($this->getDaysRented() - 3) * 1.5; 145 | } 146 | break; 147 | } 148 | 149 | return $result; 150 | } 151 | 152 | public function getFrequentRenterPoints(): int { 153 | $result = 0; 154 | 155 | // add bonus for a two-day new release rental 156 | if (($this->movie->getPriceCode() == Movie::NEW_RELEASE) && ($this->getDaysRented() > 1)) { 157 | $result += 2; 158 | } else { 159 | $result += 1; 160 | } 161 | 162 | return $result; 163 | } 164 | } 165 | 166 | 167 | // define customer 168 | $customer = new Customer('Adam Culp'); 169 | 170 | // choose movie to be rented, define rental, add it to the customer 171 | $movie = new Movie('Gladiator', 0); 172 | $rental = new Rental($movie, 1); 173 | $customer->addRental($rental); 174 | 175 | // choose 2nd movie to be rented, define rental, add it to the customer 176 | $movie = new Movie('Spiderman', 1); 177 | $rental = new Rental($movie, 2); 178 | $customer->addRental($rental); 179 | 180 | // print the statement 181 | echo $customer->statementText(); 182 | echo $customer->statementHtml(); 183 | -------------------------------------------------------------------------------- /code/011-move_method.php: -------------------------------------------------------------------------------- 1 | name = $name; 20 | } 21 | 22 | public function addRental(Rental $rental) { 23 | $this->rentals[] = $rental; 24 | } 25 | 26 | public function getName(): string { 27 | return $this->name; 28 | } 29 | 30 | public function statementText(): string { 31 | $result = "Rental Record for " . $this->getName() . "\n"; 32 | 33 | foreach ($this->rentals as $rental) { 34 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $rental->getCharge() . "\n"; 35 | } 36 | 37 | // add footer lines 38 | $result .= "Amount owed is " . $this->getTotalCharge() . "\n"; 39 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points"; 40 | 41 | return $result; 42 | } 43 | 44 | public function statementHtml(): string { 45 | $result = "Amount owed is " . $this->getTotalCharge() . "
\n"; 53 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points
"; 54 | 55 | return $result; 56 | } 57 | 58 | public function getTotalFrequentRenterPoints(): int { 59 | $result = 0; 60 | 61 | foreach ($this->rentals as $rental) { 62 | $result += $rental->getFrequentRenterPoints(); 63 | } 64 | 65 | return $result; 66 | } 67 | 68 | public function getTotalCharge(): float { 69 | $result = 0; 70 | 71 | foreach ($this->rentals as $rental) { 72 | $result += $rental->getCharge(); 73 | } 74 | 75 | return $result; 76 | } 77 | } 78 | 79 | class Movie { 80 | const CHILDREN = 2; 81 | const REGULAR = 0; 82 | const NEW_RELEASE = 1; 83 | 84 | protected string $title; 85 | protected int $priceCode; 86 | 87 | public function __construct($title, $priceCode) { 88 | $this->title = $title; 89 | $this->setPriceCode($priceCode); 90 | } 91 | 92 | public function getPriceCode(): int { 93 | return $this->priceCode; 94 | } 95 | 96 | public function setPriceCode($priceCode) { 97 | $this->priceCode = $priceCode; 98 | } 99 | 100 | public function getTitle(): string { 101 | return $this->title; 102 | } 103 | 104 | public function getCharge($daysRented): float { 105 | $result = 0; 106 | 107 | switch ($this->getPriceCode()) { 108 | case self::REGULAR: 109 | $result += 2; 110 | if ($daysRented > 2) { 111 | $result += ($daysRented - 2) * 1.5; 112 | } 113 | break; 114 | 115 | case self::NEW_RELEASE: 116 | $result += $daysRented * 3; 117 | break; 118 | 119 | case self::CHILDREN: 120 | $result += 1.5; 121 | if ($daysRented > 3) { 122 | $result += ($daysRented - 3) * 1.5; 123 | } 124 | break; 125 | } 126 | 127 | return $result; 128 | } 129 | } 130 | 131 | class Rental { 132 | public Movie $movie; 133 | protected int $daysRented; 134 | 135 | public function __construct(Movie $movie, $daysRented) { 136 | $this->movie = $movie; 137 | $this->daysRented = $daysRented; 138 | } 139 | 140 | public function getDaysRented(): int { 141 | return $this->daysRented; 142 | } 143 | 144 | public function getMovie(): Movie { 145 | return $this->movie; 146 | } 147 | 148 | public function getCharge(): float { 149 | return $this->movie->getCharge($this->getDaysRented()); 150 | } 151 | 152 | public function getFrequentRenterPoints(): int { 153 | $result = 0; 154 | 155 | // add bonus for a two-day new release rental 156 | if (($this->movie->getPriceCode() == Movie::NEW_RELEASE) && ($this->getDaysRented() > 1)) { 157 | $result += 2; 158 | } else { 159 | $result += 1; 160 | } 161 | 162 | return $result; 163 | } 164 | } 165 | 166 | 167 | // define customer 168 | $customer = new Customer('Adam Culp'); 169 | 170 | // choose movie to be rented, define rental, add it to the customer 171 | $movie = new Movie('Gladiator', 0); 172 | $rental = new Rental($movie, 1); 173 | $customer->addRental($rental); 174 | 175 | // choose 2nd movie to be rented, define rental, add it to the customer 176 | $movie = new Movie('Spiderman', 1); 177 | $rental = new Rental($movie, 2); 178 | $customer->addRental($rental); 179 | 180 | // print the statement 181 | echo $customer->statementText(); 182 | echo $customer->statementHtml(); 183 | -------------------------------------------------------------------------------- /code/012-move_method.php: -------------------------------------------------------------------------------- 1 | name = $name; 21 | } 22 | 23 | public function addRental(Rental $rental) { 24 | $this->rentals[] = $rental; 25 | } 26 | 27 | public function getName(): string { 28 | return $this->name; 29 | } 30 | 31 | public function statementText(): string { 32 | $result = "Rental Record for " . $this->getName() . "\n"; 33 | 34 | foreach ($this->rentals as $rental) { 35 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $rental->getCharge() . "\n"; 36 | } 37 | 38 | // add footer lines 39 | $result .= "Amount owed is " . $this->getTotalCharge() . "\n"; 40 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points"; 41 | 42 | return $result; 43 | } 44 | 45 | public function statementHtml(): string { 46 | $result = "Amount owed is " . $this->getTotalCharge() . "
\n"; 54 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points
"; 55 | 56 | return $result; 57 | } 58 | 59 | public function getTotalFrequentRenterPoints(): int { 60 | $result = 0; 61 | 62 | foreach ($this->rentals as $rental) { 63 | $result += $rental->getFrequentRenterPoints(); 64 | } 65 | 66 | return $result; 67 | } 68 | 69 | public function getTotalCharge(): float { 70 | $result = 0; 71 | 72 | foreach ($this->rentals as $rental) { 73 | $result += $rental->getCharge(); 74 | } 75 | 76 | return $result; 77 | } 78 | } 79 | 80 | class Movie { 81 | const CHILDREN = 2; 82 | const REGULAR = 0; 83 | const NEW_RELEASE = 1; 84 | 85 | protected string $title; 86 | protected int $priceCode; 87 | 88 | public function __construct($title, $priceCode) { 89 | $this->title = $title; 90 | $this->setPriceCode($priceCode); 91 | } 92 | 93 | public function getPriceCode(): int { 94 | return $this->priceCode; 95 | } 96 | 97 | public function setPriceCode($priceCode) { 98 | $this->priceCode = $priceCode; 99 | } 100 | 101 | public function getTitle(): string { 102 | return $this->title; 103 | } 104 | 105 | public function getCharge($daysRented): float { 106 | $result = 0; 107 | 108 | switch ($this->getPriceCode()) { 109 | case self::REGULAR: 110 | $result += 2; 111 | if ($daysRented > 2) { 112 | $result += ($daysRented - 2) * 1.5; 113 | } 114 | break; 115 | 116 | case self::NEW_RELEASE: 117 | $result += $daysRented * 3; 118 | break; 119 | 120 | case self::CHILDREN: 121 | $result += 1.5; 122 | if ($daysRented > 3) { 123 | $result += ($daysRented - 3) * 1.5; 124 | } 125 | break; 126 | } 127 | 128 | return $result; 129 | } 130 | 131 | public function getFrequentRenterPoints($daysRented): int { 132 | $result = 0; 133 | 134 | // add bonus for a two-day new release rental 135 | if (($this->getPriceCode() == self::NEW_RELEASE) && ($daysRented > 1)) { 136 | $result += 2; 137 | } else { 138 | $result += 1; 139 | } 140 | 141 | return $result; 142 | } 143 | } 144 | 145 | class Rental { 146 | public Movie $movie; 147 | protected int $daysRented; 148 | 149 | public function __construct(Movie $movie, $daysRented) { 150 | $this->movie = $movie; 151 | $this->daysRented = $daysRented; 152 | } 153 | 154 | public function getDaysRented(): int { 155 | return $this->daysRented; 156 | } 157 | 158 | public function getMovie(): Movie { 159 | return $this->movie; 160 | } 161 | 162 | public function getCharge(): float { 163 | return $this->movie->getCharge($this->getDaysRented()); 164 | } 165 | 166 | public function getFrequentRenterPoints(): int { 167 | return $this->movie->getFrequentRenterPoints($this->getDaysRented()); 168 | } 169 | } 170 | 171 | 172 | // define customer 173 | $customer = new Customer('Adam Culp'); 174 | 175 | // choose movie to be rented, define rental, add it to the customer 176 | $movie = new Movie('Gladiator', 0); 177 | $rental = new Rental($movie, 1); 178 | $customer->addRental($rental); 179 | 180 | // choose 2nd movie to be rented, define rental, add it to the customer 181 | $movie = new Movie('Spiderman', 1); 182 | $rental = new Rental($movie, 2); 183 | $customer->addRental($rental); 184 | 185 | // print the statement 186 | echo $customer->statementText(); 187 | echo $customer->statementHtml(); 188 | -------------------------------------------------------------------------------- /code/013-replace_type_code_with_state.php: -------------------------------------------------------------------------------- 1 | setPriceCode() to Movie->setPrice() to follow variable renaming. 9 | * 5 - We create a switch in Movie->setPriceCode() to use the new constructs. 10 | * 6 - In the Movie->__construct() we populate the $price object by calling setPriceCode(). 11 | * 7 - Finally, we alter Movie->getPriceCode() to return the code from $price. 12 | * 13 | */ 14 | 15 | namespace Refactoring013; 16 | 17 | use Exception; 18 | 19 | class Customer { 20 | protected string $name; 21 | protected array $rentals; 22 | 23 | public function __construct($name) { 24 | $this->name = $name; 25 | } 26 | 27 | public function addRental(Rental $rental) { 28 | $this->rentals[] = $rental; 29 | } 30 | 31 | public function getName(): string { 32 | return $this->name; 33 | } 34 | 35 | public function statementText(): string { 36 | $result = "Rental Record for " . $this->getName() . "\n"; 37 | 38 | foreach ($this->rentals as $rental) { 39 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $rental->getCharge() . "\n"; 40 | } 41 | 42 | // add footer lines 43 | $result .= "Amount owed is " . $this->getTotalCharge() . "\n"; 44 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points"; 45 | 46 | return $result; 47 | } 48 | 49 | public function statementHtml(): string { 50 | $result = "Amount owed is " . $this->getTotalCharge() . "
\n"; 58 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points
"; 59 | 60 | return $result; 61 | } 62 | 63 | public function getTotalFrequentRenterPoints(): int { 64 | $result = 0; 65 | 66 | foreach ($this->rentals as $rental) { 67 | $result += $rental->getFrequentRenterPoints(); 68 | } 69 | 70 | return $result; 71 | } 72 | 73 | public function getTotalCharge(): float { 74 | $result = 0; 75 | 76 | foreach ($this->rentals as $rental) { 77 | $result += $rental->getCharge(); 78 | } 79 | 80 | return $result; 81 | } 82 | } 83 | 84 | class Movie { 85 | const CHILDREN = 2; 86 | const REGULAR = 0; 87 | const NEW_RELEASE = 1; 88 | 89 | protected string $title; 90 | protected Price $price; 91 | 92 | public function __construct($title, $priceCode) { 93 | $this->title = $title; 94 | $this->setPrice($priceCode); 95 | } 96 | 97 | public function getPriceCode(): int { 98 | return $this->price->getPriceCode(); 99 | } 100 | 101 | /** 102 | * @throws Exception 103 | */ 104 | public function setPrice($priceCode) { 105 | 106 | switch ($priceCode) { 107 | case self::REGULAR: 108 | $this->price = new RegularPrice(); 109 | break; 110 | 111 | case self::CHILDREN: 112 | $this->price = new ChildrenPrice(); 113 | break; 114 | 115 | case self::NEW_RELEASE: 116 | $this->price = new NewReleasePrice(); 117 | break; 118 | 119 | default: 120 | throw new Exception('Incorrect Price Code.'); 121 | } 122 | } 123 | 124 | public function getTitle(): string { 125 | return $this->title; 126 | } 127 | 128 | public function getCharge($daysRented): float { 129 | $result = 0; 130 | 131 | switch ($this->getPriceCode()) { 132 | case self::REGULAR: 133 | $result += 2; 134 | if ($daysRented > 2) { 135 | $result += ($daysRented - 2) * 1.5; 136 | } 137 | break; 138 | 139 | case self::NEW_RELEASE: 140 | $result += $daysRented * 3; 141 | break; 142 | 143 | case self::CHILDREN: 144 | $result += 1.5; 145 | if ($daysRented > 3) { 146 | $result += ($daysRented - 3) * 1.5; 147 | } 148 | break; 149 | } 150 | 151 | return $result; 152 | } 153 | 154 | public function getFrequentRenterPoints($daysRented): int { 155 | $result = 0; 156 | 157 | // add bonus for a two-day new release rental 158 | if (($this->getPriceCode() == self::NEW_RELEASE) && ($daysRented > 1)) { 159 | $result += 2; 160 | } else { 161 | $result += 1; 162 | } 163 | 164 | return $result; 165 | } 166 | } 167 | 168 | class Rental { 169 | public Movie $movie; 170 | protected int $daysRented; 171 | 172 | public function __construct(Movie $movie, $daysRented) { 173 | $this->movie = $movie; 174 | $this->daysRented = $daysRented; 175 | } 176 | 177 | public function getDaysRented(): int { 178 | return $this->daysRented; 179 | } 180 | 181 | public function getMovie(): Movie { 182 | return $this->movie; 183 | } 184 | 185 | public function getCharge(): float { 186 | return $this->movie->getCharge($this->getDaysRented()); 187 | } 188 | 189 | public function getFrequentRenterPoints(): int { 190 | return $this->movie->getFrequentRenterPoints($this->getDaysRented()); 191 | } 192 | } 193 | 194 | abstract class Price { 195 | abstract function getPriceCode(): int; 196 | } 197 | 198 | class ChildrenPrice extends Price { 199 | public function getPriceCode(): int { 200 | return Movie::CHILDREN; 201 | } 202 | } 203 | 204 | class NewReleasePrice extends Price { 205 | public function getPriceCode(): int { 206 | return Movie::NEW_RELEASE; 207 | } 208 | } 209 | 210 | class RegularPrice extends Price { 211 | public function getPriceCode(): int { 212 | return Movie::REGULAR; 213 | } 214 | } 215 | 216 | // define customer 217 | $customer = new Customer('Adam Culp'); 218 | 219 | // Test of Exception throwing 220 | //$movie = new Movie('Superman', 3); 221 | //$rental = new Rental($movie, 1); 222 | //$customer->addRental($rental); 223 | 224 | // choose movie to be rented, define rental, add it to the customer 225 | $movie = new Movie('Gladiator', 0); 226 | $rental = new Rental($movie, 1); 227 | $customer->addRental($rental); 228 | 229 | // choose 2nd movie to be rented, define rental, add it to the customer 230 | $movie = new Movie('Spiderman', 1); 231 | $rental = new Rental($movie, 2); 232 | $customer->addRental($rental); 233 | 234 | // print the statement 235 | echo $customer->statementText(); 236 | echo $customer->statementHtml(); 237 | -------------------------------------------------------------------------------- /code/014-move_method.php: -------------------------------------------------------------------------------- 1 | name = $name; 24 | } 25 | 26 | public function addRental(Rental $rental) { 27 | $this->rentals[] = $rental; 28 | } 29 | 30 | public function getName(): string { 31 | return $this->name; 32 | } 33 | 34 | public function statementText(): string { 35 | $result = "Rental Record for " . $this->getName() . "\n"; 36 | 37 | foreach ($this->rentals as $rental) { 38 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $rental->getCharge() . "\n"; 39 | } 40 | 41 | // add footer lines 42 | $result .= "Amount owed is " . $this->getTotalCharge() . "\n"; 43 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points"; 44 | 45 | return $result; 46 | } 47 | 48 | public function statementHtml(): string { 49 | $result = "Amount owed is " . $this->getTotalCharge() . "
\n"; 57 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points
"; 58 | 59 | return $result; 60 | } 61 | 62 | public function getTotalFrequentRenterPoints(): int { 63 | $result = 0; 64 | 65 | foreach ($this->rentals as $rental) { 66 | $result += $rental->getFrequentRenterPoints(); 67 | } 68 | 69 | return $result; 70 | } 71 | 72 | public function getTotalCharge(): float { 73 | $result = 0; 74 | 75 | foreach ($this->rentals as $rental) { 76 | $result += $rental->getCharge(); 77 | } 78 | 79 | return $result; 80 | } 81 | } 82 | 83 | class Movie { 84 | const CHILDREN = 2; 85 | const REGULAR = 0; 86 | const NEW_RELEASE = 1; 87 | 88 | protected string $title; 89 | protected Price $price; 90 | 91 | public function __construct($title, $priceCode) { 92 | $this->title = $title; 93 | $this->setPrice($priceCode); 94 | } 95 | 96 | public function getPriceCode(): int { 97 | return $this->price->getPriceCode(); 98 | } 99 | 100 | /** 101 | * @throws Exception 102 | */ 103 | public function setPrice($priceCode) { 104 | 105 | switch ($priceCode) { 106 | case self::REGULAR: 107 | $this->price = new RegularPrice(); 108 | break; 109 | 110 | case self::CHILDREN: 111 | $this->price = new ChildrenPrice(); 112 | break; 113 | 114 | case self::NEW_RELEASE: 115 | $this->price = new NewReleasePrice(); 116 | break; 117 | 118 | default: 119 | throw new Exception('Incorrect Price Code.'); 120 | } 121 | } 122 | 123 | public function getTitle(): string { 124 | return $this->title; 125 | } 126 | 127 | public function getCharge($daysRented): float { 128 | return $this->price->getCharge($daysRented); 129 | } 130 | 131 | public function getFrequentRenterPoints($daysRented): int { 132 | $result = 0; 133 | 134 | // add bonus for a two-day new release rental 135 | if (($this->getPriceCode() == self::NEW_RELEASE) && ($daysRented > 1)) { 136 | $result += 2; 137 | } else { 138 | $result += 1; 139 | } 140 | 141 | return $result; 142 | } 143 | } 144 | 145 | class Rental { 146 | public Movie $movie; 147 | protected int $daysRented; 148 | 149 | public function __construct(Movie $movie, $daysRented) { 150 | $this->movie = $movie; 151 | $this->daysRented = $daysRented; 152 | } 153 | 154 | public function getDaysRented(): int { 155 | return $this->daysRented; 156 | } 157 | 158 | public function getMovie(): Movie { 159 | return $this->movie; 160 | } 161 | 162 | public function getCharge(): float { 163 | return $this->movie->getCharge($this->getDaysRented()); 164 | } 165 | 166 | public function getFrequentRenterPoints(): int { 167 | return $this->movie->getFrequentRenterPoints($this->getDaysRented()); 168 | } 169 | } 170 | 171 | class Price { 172 | public function getCharge($daysRented): float { 173 | $result = 0; 174 | 175 | switch ($this->getPriceCode()) { 176 | case Movie::REGULAR: 177 | $result += 2; 178 | if ($daysRented > 2) { 179 | $result += ($daysRented - 2) * 1.5; 180 | } 181 | break; 182 | 183 | case Movie::NEW_RELEASE: 184 | $result += $daysRented * 3; 185 | break; 186 | 187 | case Movie::CHILDREN: 188 | $result += 1.5; 189 | if ($daysRented > 3) { 190 | $result += ($daysRented - 3) * 1.5; 191 | } 192 | break; 193 | } 194 | 195 | return $result; 196 | } 197 | } 198 | 199 | class ChildrenPrice extends Price { 200 | public function getPriceCode(): int { 201 | return Movie::CHILDREN; 202 | } 203 | } 204 | 205 | class NewReleasePrice extends Price { 206 | public function getPriceCode(): int { 207 | return Movie::NEW_RELEASE; 208 | } 209 | } 210 | 211 | class RegularPrice extends Price { 212 | public function getPriceCode(): int { 213 | return Movie::REGULAR; 214 | } 215 | } 216 | 217 | // define customer 218 | $customer = new Customer('Adam Culp'); 219 | 220 | // Test of Exception throwing 221 | //$movie = new Movie('Superman', 3); 222 | //$rental = new Rental($movie, 1); 223 | //$customer->addRental($rental); 224 | 225 | // choose movie to be rented, define rental, add it to the customer 226 | $movie = new Movie('Gladiator', 0); 227 | $rental = new Rental($movie, 1); 228 | $customer->addRental($rental); 229 | 230 | // choose 2nd movie to be rented, define rental, add it to the customer 231 | $movie = new Movie('Spiderman', 1); 232 | $rental = new Rental($movie, 2); 233 | $customer->addRental($rental); 234 | 235 | // print the statement 236 | echo $customer->statementText(); 237 | echo $customer->statementHtml(); 238 | -------------------------------------------------------------------------------- /code/015-repl_conditional_w_polymorphism.php: -------------------------------------------------------------------------------- 1 | name = $name; 24 | } 25 | 26 | public function addRental(Rental $rental) { 27 | $this->rentals[] = $rental; 28 | } 29 | 30 | public function getName(): string { 31 | return $this->name; 32 | } 33 | 34 | public function statementText(): string { 35 | $result = "Rental Record for " . $this->getName() . "\n"; 36 | 37 | foreach ($this->rentals as $rental) { 38 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $rental->getCharge() . "\n"; 39 | } 40 | 41 | // add footer lines 42 | $result .= "Amount owed is " . $this->getTotalCharge() . "\n"; 43 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points"; 44 | 45 | return $result; 46 | } 47 | 48 | public function statementHtml(): string { 49 | $result = "Amount owed is " . $this->getTotalCharge() . "
\n"; 57 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points
"; 58 | 59 | return $result; 60 | } 61 | 62 | public function getTotalFrequentRenterPoints(): int { 63 | $result = 0; 64 | 65 | foreach ($this->rentals as $rental) { 66 | $result += $rental->getFrequentRenterPoints(); 67 | } 68 | 69 | return $result; 70 | } 71 | 72 | public function getTotalCharge(): float { 73 | $result = 0; 74 | 75 | foreach ($this->rentals as $rental) { 76 | $result += $rental->getCharge(); 77 | } 78 | 79 | return $result; 80 | } 81 | } 82 | 83 | class Movie { 84 | const CHILDREN = 2; 85 | const REGULAR = 0; 86 | const NEW_RELEASE = 1; 87 | 88 | protected string $title; 89 | protected Price $price; 90 | 91 | public function __construct($title, $priceCode) { 92 | $this->title = $title; 93 | $this->setPrice($priceCode); 94 | } 95 | 96 | public function getPriceCode(): int { 97 | return $this->price->getPriceCode(); 98 | } 99 | 100 | /** 101 | * @throws Exception 102 | */ 103 | public function setPrice($priceCode) { 104 | 105 | switch ($priceCode) { 106 | case self::REGULAR: 107 | $this->price = new RegularPrice(); 108 | break; 109 | 110 | case self::CHILDREN: 111 | $this->price = new ChildrenPrice(); 112 | break; 113 | 114 | case self::NEW_RELEASE: 115 | $this->price = new NewReleasePrice(); 116 | break; 117 | 118 | default: 119 | throw new Exception('Incorrect Price Code.'); 120 | } 121 | } 122 | 123 | public function getTitle(): string { 124 | return $this->title; 125 | } 126 | 127 | public function getCharge($daysRented): float { 128 | return $this->price->getCharge($daysRented); 129 | } 130 | 131 | public function getFrequentRenterPoints($daysRented): int { 132 | $result = 0; 133 | 134 | // add bonus for a two-day new release rental 135 | if (($this->getPriceCode() == self::NEW_RELEASE) && ($daysRented > 1)) { 136 | $result += 2; 137 | } else { 138 | $result += 1; 139 | } 140 | 141 | return $result; 142 | } 143 | } 144 | 145 | class Rental { 146 | public Movie $movie; 147 | protected int $daysRented; 148 | 149 | public function __construct(Movie $movie, $daysRented) { 150 | $this->movie = $movie; 151 | $this->daysRented = $daysRented; 152 | } 153 | 154 | public function getDaysRented():int { 155 | return $this->daysRented; 156 | } 157 | 158 | public function getMovie(): Movie { 159 | return $this->movie; 160 | } 161 | 162 | public function getCharge(): float { 163 | return $this->movie->getCharge($this->getDaysRented()); 164 | } 165 | 166 | public function getFrequentRenterPoints(): int { 167 | return $this->movie->getFrequentRenterPoints($this->getDaysRented()); 168 | } 169 | } 170 | 171 | abstract class Price { 172 | abstract function getPriceCode(): int; 173 | 174 | abstract function getCharge($daysRented): float; 175 | } 176 | 177 | class ChildrenPrice extends Price { 178 | public function getPriceCode(): int { 179 | return Movie::CHILDREN; 180 | } 181 | 182 | public function getCharge($daysRented): float { 183 | $result = 1.5; 184 | if ($daysRented > 3) { 185 | $result += ($daysRented - 3) * 1.5; 186 | } 187 | 188 | return $result; 189 | } 190 | } 191 | 192 | class NewReleasePrice extends Price { 193 | public function getPriceCode(): int { 194 | return Movie::NEW_RELEASE; 195 | } 196 | 197 | public function getCharge($daysRented): float { 198 | return $daysRented * 3; 199 | } 200 | } 201 | 202 | class RegularPrice extends Price { 203 | public function getPriceCode(): int { 204 | return Movie::REGULAR; 205 | } 206 | 207 | public function getCharge($daysRented): float { 208 | $result = 2; 209 | if ($daysRented > 2) { 210 | $result += ($daysRented - 2) * 1.5; 211 | } 212 | 213 | return $result; 214 | } 215 | } 216 | 217 | // define customer 218 | $customer = new Customer('Adam Culp'); 219 | 220 | // Test of Exception throwing 221 | //$movie = new Movie('Superman', 3); 222 | //$rental = new Rental($movie, 1); 223 | //$customer->addRental($rental); 224 | 225 | // choose movie to be rented, define rental, add it to the customer 226 | $movie = new Movie('Gladiator', 0); 227 | $rental = new Rental($movie, 1); 228 | $customer->addRental($rental); 229 | 230 | // choose 2nd movie to be rented, define rental, add it to the customer 231 | $movie = new Movie('Spiderman', 1); 232 | $rental = new Rental($movie, 2); 233 | $customer->addRental($rental); 234 | 235 | // print the statement 236 | echo $customer->statementText(); 237 | echo $customer->statementHtml(); 238 | -------------------------------------------------------------------------------- /code/016-repl_conditional_w_polymorphism.php: -------------------------------------------------------------------------------- 1 | name = $name; 25 | } 26 | 27 | public function addRental(Rental $rental) { 28 | $this->rentals[] = $rental; 29 | } 30 | 31 | public function getName(): string { 32 | return $this->name; 33 | } 34 | 35 | public function statementText(): string { 36 | $result = "Rental Record for " . $this->getName() . "\n"; 37 | 38 | foreach ($this->rentals as $rental) { 39 | $result .= "\t" . $rental->movie->getTitle() . "\t" . $rental->getCharge() . "\n"; 40 | } 41 | 42 | // add footer lines 43 | $result .= "Amount owed is " . $this->getTotalCharge() . "\n"; 44 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points"; 45 | 46 | return $result; 47 | } 48 | 49 | public function statementHtml(): string { 50 | $result = "Amount owed is " . $this->getTotalCharge() . "
\n"; 58 | $result .= "You earned " . $this->getTotalFrequentRenterPoints() . " frequent renter points
"; 59 | 60 | return $result; 61 | } 62 | 63 | public function getTotalFrequentRenterPoints(): int { 64 | $result = 0; 65 | 66 | foreach ($this->rentals as $rental) { 67 | $result += $rental->getFrequentRenterPoints(); 68 | } 69 | 70 | return $result; 71 | } 72 | 73 | public function getTotalCharge(): float { 74 | $result = 0; 75 | 76 | foreach ($this->rentals as $rental) { 77 | $result += $rental->getCharge(); 78 | } 79 | 80 | return $result; 81 | } 82 | } 83 | 84 | class Movie { 85 | const CHILDREN = 2; 86 | const REGULAR = 0; 87 | const NEW_RELEASE = 1; 88 | 89 | protected string $title; 90 | protected Price $price; 91 | 92 | public function __construct($title, $priceCode) { 93 | $this->title = $title; 94 | $this->setPrice($priceCode); 95 | } 96 | 97 | public function getPriceCode(): int { 98 | return $this->price->getPriceCode(); 99 | } 100 | 101 | /** 102 | * @throws Exception 103 | */ 104 | public function setPrice($priceCode) { 105 | 106 | switch ($priceCode) { 107 | case self::REGULAR: 108 | $this->price = new RegularPrice(); 109 | break; 110 | 111 | case self::CHILDREN: 112 | $this->price = new ChildrenPrice(); 113 | break; 114 | 115 | case self::NEW_RELEASE: 116 | $this->price = new NewReleasePrice(); 117 | break; 118 | 119 | default: 120 | throw new Exception('Incorrect Price Code.'); 121 | } 122 | } 123 | 124 | public function getTitle(): string { 125 | return $this->title; 126 | } 127 | 128 | public function getCharge($daysRented): float { 129 | return $this->price->getCharge($daysRented); 130 | } 131 | 132 | public function getFrequentRenterPoints($daysRented): int { 133 | return $this->price->getFrequentRenterPoints($daysRented); 134 | } 135 | } 136 | 137 | class Rental { 138 | public Movie $movie; 139 | protected int $daysRented; 140 | 141 | public function __construct(Movie $movie, $daysRented) { 142 | $this->movie = $movie; 143 | $this->daysRented = $daysRented; 144 | } 145 | 146 | public function getDaysRented(): int { 147 | return $this->daysRented; 148 | } 149 | 150 | public function getMovie(): Movie { 151 | return $this->movie; 152 | } 153 | 154 | public function getCharge(): float { 155 | return $this->movie->getCharge($this->getDaysRented()); 156 | } 157 | 158 | public function getFrequentRenterPoints(): int { 159 | return $this->movie->getFrequentRenterPoints($this->getDaysRented()); 160 | } 161 | } 162 | 163 | abstract class Price { 164 | abstract function getPriceCode(): int; 165 | 166 | abstract function getCharge($daysRented): float; 167 | 168 | public function getFrequentRenterPoints($daysRented): int { 169 | return 1; 170 | } 171 | } 172 | 173 | class ChildrenPrice extends Price { 174 | public function getPriceCode(): int { 175 | return Movie::CHILDREN; 176 | } 177 | 178 | public function getCharge($daysRented): float { 179 | $result = 1.5; 180 | if ($daysRented > 3) { 181 | $result += ($daysRented - 3) * 1.5; 182 | } 183 | 184 | return $result; 185 | } 186 | } 187 | 188 | class NewReleasePrice extends Price { 189 | public function getPriceCode(): int { 190 | return Movie::NEW_RELEASE; 191 | } 192 | 193 | public function getCharge($daysRented): float { 194 | return $daysRented * 3; 195 | } 196 | 197 | public function getFrequentRenterPoints($daysRented): int { 198 | return ($daysRented > 1) ? 2 : 1; 199 | } 200 | } 201 | 202 | class RegularPrice extends Price { 203 | public function getPriceCode(): int { 204 | return Movie::REGULAR; 205 | } 206 | 207 | public function getCharge($daysRented): float { 208 | $result = 2; 209 | if ($daysRented > 2) { 210 | $result += ($daysRented - 2) * 1.5; 211 | } 212 | 213 | return $result; 214 | } 215 | } 216 | 217 | // define customer 218 | $customer = new Customer('Adam Culp'); 219 | 220 | // Test of Exception throwing 221 | //$movie = new Movie('Superman', 3); 222 | //$rental = new Rental($movie, 1); 223 | //$customer->addRental($rental); 224 | 225 | // choose movie to be rented, define rental, add it to the customer 226 | $movie = new Movie('Gladiator', 0); 227 | $rental = new Rental($movie, 1); 228 | $customer->addRental($rental); 229 | 230 | // choose 2nd movie to be rented, define rental, add it to the customer 231 | $movie = new Movie('Spiderman', 1); 232 | $rental = new Rental($movie, 2); 233 | $customer->addRental($rental); 234 | 235 | // print the statement 236 | echo $customer->statementText(); 237 | echo $customer->statementHtml(); 238 | --------------------------------------------------------------------------------