mirror of
https://github.com/ysoftdevs/Theatrical-Players-Refactoring-Kata.git
synced 2026-07-09 14:25:16 +02:00
@@ -1 +1,3 @@
|
|||||||
vendor/
|
vendor/
|
||||||
|
/.phpunit.result.cache
|
||||||
|
/.idea
|
||||||
+131
-5
@@ -1,9 +1,135 @@
|
|||||||
Theatrical Players Refactoring Kata - PHP version
|
# Theatrical Players Refactoring Kata - PHP version
|
||||||
========================================================
|
|
||||||
|
|
||||||
See the [top level readme](https://github.com/emilybache/Theatrical-Players-Refactoring-Kata) for general information about this exercise.
|
See the [top level readme](../../Theatrical-Players-Refactoring-Kata/README.md) for general information
|
||||||
|
about this exercise. Download the PDF of the first chapter of
|
||||||
|
['Refactoring' by Martin Fowler, 2nd Edition](https://www.thoughtworks.com/books/refactoring2) which contains a worked
|
||||||
|
example of this exercise, in javascript.
|
||||||
|
|
||||||
This project uses [PHPUnit](https://github.com/sebastianbergmann/phpunit) and [approvaltests](https://github.com/approvals/ApprovalTests.php).
|
## Installation
|
||||||
|
|
||||||
Run ```composer install``` then ```./vendor/bin/phpunit``` to run the tests
|
The project uses:
|
||||||
|
|
||||||
|
- [PHP 7.2+](https://www.php.net/downloads.php)
|
||||||
|
- [Composer](https://getcomposer.org)
|
||||||
|
|
||||||
|
Recommended:
|
||||||
|
|
||||||
|
- [Git](https://git-scm.com/downloads)
|
||||||
|
|
||||||
|
Clone the repository
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone git@github.com:emilybache/Theatrical-Players-Refactoring-Kata.git
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
git clone https://github.com/emilybache/Theatrical-Players-Refactoring-Kata.git
|
||||||
|
```
|
||||||
|
|
||||||
|
Install all the dependencies using composer:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd ./Theatrical-Players-Refactoring-Kata/php
|
||||||
|
composer install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
The project uses composer to install:
|
||||||
|
|
||||||
|
- [PHPUnit](https://phpunit.de/)
|
||||||
|
- [ApprovalTests.PHP](https://github.com/approvals/ApprovalTests.php)
|
||||||
|
- [PHPStan](https://github.com/phpstan/phpstan)
|
||||||
|
- [Easy Coding Standard (ECS)](https://github.com/symplify/easy-coding-standard)
|
||||||
|
- [PHP CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer/wiki)
|
||||||
|
|
||||||
|
## Folders
|
||||||
|
|
||||||
|
- `src` - Contains the **StatementPrinter** Class along with the setup classes. Only **StatementPrinter.php** is
|
||||||
|
refactored.
|
||||||
|
- `tests` - Contains the corresponding tests. There should be no need to amend the test.
|
||||||
|
- `approvals` - Contains the text output for the tests. There should be no need to amend.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
PHPUnit is used to run tests, to help this can be run using a composer script. To run the unit tests, from the root of
|
||||||
|
the project run:
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
composer test
|
||||||
|
```
|
||||||
|
|
||||||
|
On Windows a batch file has been created, similar to an alias on Linux/Mac (e.g. `alias pu="composer test"`), the same
|
||||||
|
PHPUnit `composer test` can be run:
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
pu
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tests with Coverage Report
|
||||||
|
|
||||||
|
To run all test and generate a html coverage report run:
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
composer test-coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
The coverage report is created in /builds, it is best viewed by opening **index.html** in your browser.
|
||||||
|
|
||||||
|
The [XDEbug](https://xdebug.org/download) extension is required for coverage report generating.
|
||||||
|
|
||||||
|
## Code Standard
|
||||||
|
|
||||||
|
Easy Coding Standard (ECS) is used to check for style and code standards,
|
||||||
|
**[PSR-12](https://www.php-fig.org/psr/psr-12/)** is used. As the code is constantly being refactored only run code
|
||||||
|
standard checks once the chapter is complete.
|
||||||
|
|
||||||
|
### Check Code
|
||||||
|
|
||||||
|
To check code, but not fix errors:
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
composer check-cs
|
||||||
|
```
|
||||||
|
|
||||||
|
On Windows a batch file has been created, similar to an alias on Linux/Mac (e.g. `alias cc="composer check-cs"`), the
|
||||||
|
same ECS `composer check-cs` can be run:
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
cc
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fix Code
|
||||||
|
|
||||||
|
Many code fixes are automatically provided by ECS, if advised to run --fix, the following script can be run:
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
composer fix-cs
|
||||||
|
```
|
||||||
|
|
||||||
|
On Windows a batch file has been created, similar to an alias on Linux/Mac (e.g. `alias fc="composer fix-cs"`), the same
|
||||||
|
ECS `composer fix-cs` can be run:
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
fc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Static Analysis
|
||||||
|
|
||||||
|
PHPStan is used to run static analysis checks. As the code is constantly being refactored only run static analysis
|
||||||
|
checks once the chapter is complete.
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
composer phpstan
|
||||||
|
```
|
||||||
|
|
||||||
|
On Windows a batch file has been created, similar to an alias on Linux/Mac (e.g. `alias ps="composer phpstan"`), the
|
||||||
|
same PHPStan `composer phpstan` can be run:
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
ps
|
||||||
|
```
|
||||||
|
|
||||||
|
**Happy coding**!
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
composer check-cs
|
||||||
+24
-4
@@ -2,8 +2,12 @@
|
|||||||
"name": "sam/theatrical",
|
"name": "sam/theatrical",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^9",
|
"phpunit/phpunit": "^8.5",
|
||||||
"approvals/approval-tests": "^1.4"
|
"approvals/approval-tests": "^1.4",
|
||||||
|
"phpstan/phpstan": "^0.12.25",
|
||||||
|
"phpstan/phpstan-phpunit": "^0.12.8",
|
||||||
|
"symplify/easy-coding-standard": "^7.3",
|
||||||
|
"symplify/phpstan-extensions": "^7.3"
|
||||||
},
|
},
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
@@ -12,11 +16,27 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.4"
|
"php": "^7.2",
|
||||||
|
"ext-intl": "*"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Theatrical\\": "src/"
|
"Theatrical\\": "src/"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"checkcode": "phpcs src tests --standard=PSR12",
|
||||||
|
"fixcode": "phpcbf src tests --standard=PSR12",
|
||||||
|
"test": "phpunit",
|
||||||
|
"tests": "phpunit",
|
||||||
|
"test-coverage": "phpunit --coverage-html build/coverage",
|
||||||
|
"check-cs": "ecs check src tests --ansi",
|
||||||
|
"fix-cs": "ecs check src tests --fix --ansi",
|
||||||
|
"phpstan": "phpstan analyse --ansi"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Generated
+3507
-297
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
parameters:
|
||||||
|
sets:
|
||||||
|
- 'psr12'
|
||||||
|
- 'php71'
|
||||||
|
- 'symplify'
|
||||||
|
- 'common'
|
||||||
|
- 'clean-code'
|
||||||
|
|
||||||
|
line_ending: "\n"
|
||||||
|
|
||||||
|
# 4 spaces
|
||||||
|
indentation: " "
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
composer fix-cs
|
||||||
+26
-1
@@ -1,4 +1,29 @@
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Theatrical\HtmlStatement;
|
||||||
|
use Theatrical\Invoice;
|
||||||
|
use Theatrical\Performance;
|
||||||
|
use Theatrical\Play;
|
||||||
|
|
||||||
require __DIR__ . '/vendor/autoload.php';
|
require __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
$plays = [
|
||||||
|
"hamlet" => new Play("Hamlet", "tragedy"),
|
||||||
|
"as-like" => new Play("As You Like It", "comedy"),
|
||||||
|
"othello" => new Play("Othello", "tragedy")
|
||||||
|
];
|
||||||
|
|
||||||
|
$performances = [
|
||||||
|
new Performance("hamlet", 55),
|
||||||
|
new Performance("as-like", 35),
|
||||||
|
new Performance("othello", 40)
|
||||||
|
];
|
||||||
|
$invoice = new Invoice("BigCo", $performances);
|
||||||
|
|
||||||
|
echo "<h1>Html Statement</h1>";
|
||||||
|
|
||||||
|
// Uncomment the following two line once the HtmlStatement Class is written (Ch.1 page 31)
|
||||||
|
//$statementPrinter = new HtmlStatement();
|
||||||
|
//echo $statementPrinter->print($invoice, $plays);
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
includes:
|
||||||
|
- vendor/symplify/phpstan-extensions/config/config.neon
|
||||||
|
- vendor/phpstan/phpstan-phpunit/extension.neon
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
paths:
|
||||||
|
- src
|
||||||
|
- tests
|
||||||
|
|
||||||
|
# The level 8 is the highest level
|
||||||
|
level: 8
|
||||||
|
|
||||||
|
# Larstan recommendation:
|
||||||
|
checkMissingIterableValueType: false
|
||||||
|
|
||||||
|
ignoreErrors:
|
||||||
|
|
||||||
|
# buggy
|
||||||
|
|
||||||
|
# mixed
|
||||||
|
|
||||||
|
# cache buggy
|
||||||
|
|
||||||
|
# tests
|
||||||
|
|
||||||
|
# iterable
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
composer phpstan
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
composer test
|
||||||
+12
-4
@@ -1,16 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Theatrical;
|
namespace Theatrical;
|
||||||
|
|
||||||
class Invoice
|
class Invoice
|
||||||
{
|
{
|
||||||
public string $customer;
|
/**
|
||||||
public array $performances;
|
* @var string
|
||||||
|
*/
|
||||||
|
public $customer;
|
||||||
|
|
||||||
public function __construct($customer, $performances)
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $performances;
|
||||||
|
|
||||||
|
public function __construct(string $customer, array $performances)
|
||||||
{
|
{
|
||||||
$this->customer = $customer;
|
$this->customer = $customer;
|
||||||
$this->performances = $performances;
|
$this->performances = $performances;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+17
-3
@@ -1,13 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Theatrical;
|
namespace Theatrical;
|
||||||
|
|
||||||
class Performance
|
class Performance
|
||||||
{
|
{
|
||||||
public string $play_id;
|
/**
|
||||||
public int $audience;
|
* @var string
|
||||||
|
*/
|
||||||
|
public $play_id;
|
||||||
|
|
||||||
public function __construct($play_id, $audience)
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $audience;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Play
|
||||||
|
*/
|
||||||
|
public $play;
|
||||||
|
|
||||||
|
public function __construct(string $play_id, int $audience)
|
||||||
{
|
{
|
||||||
$this->play_id = $play_id;
|
$this->play_id = $play_id;
|
||||||
$this->audience = $audience;
|
$this->audience = $audience;
|
||||||
|
|||||||
+12
-3
@@ -1,13 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Theatrical;
|
namespace Theatrical;
|
||||||
|
|
||||||
class Play
|
class Play
|
||||||
{
|
{
|
||||||
public string $name;
|
/**
|
||||||
public string $type;
|
* @var string
|
||||||
|
*/
|
||||||
|
public $name;
|
||||||
|
|
||||||
public function __construct($name, $type)
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $type;
|
||||||
|
|
||||||
|
public function __construct(string $name, string $type)
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
|
|||||||
@@ -1,63 +1,62 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Theatrical;
|
namespace Theatrical;
|
||||||
use Theatrical\Play;
|
|
||||||
use Theatrical\Invoice;
|
use Error;
|
||||||
use Theatrical\Performance;
|
use NumberFormatter;
|
||||||
|
|
||||||
class StatementPrinter
|
class StatementPrinter
|
||||||
{
|
{
|
||||||
public function print(Invoice $invoice, array $plays)
|
public function print(Invoice $invoice, array $plays): string
|
||||||
{
|
{
|
||||||
$totalAmount = 0;
|
$totalAmount = 0;
|
||||||
$volumeCredits = 0;
|
$volumeCredits = 0;
|
||||||
|
|
||||||
$result = 'Statement for ' . $invoice->customer . '\n';
|
$result = "Statement for {$invoice->customer}\n";
|
||||||
|
$format = new NumberFormatter('en_US', NumberFormatter::CURRENCY);
|
||||||
|
|
||||||
foreach($invoice->performances as $performance)
|
foreach ($invoice->performances as $performance) {
|
||||||
{
|
|
||||||
$play = $plays[$performance->play_id];
|
$play = $plays[$performance->play_id];
|
||||||
|
|
||||||
$thisAmount = 0;
|
$thisAmount = 0;
|
||||||
|
|
||||||
switch($play->type)
|
switch ($play->type) {
|
||||||
{
|
case 'tragedy':
|
||||||
case "tragedy":
|
|
||||||
$thisAmount = 40000;
|
$thisAmount = 40000;
|
||||||
if($performance->audience > 30)
|
if ($performance->audience > 30) {
|
||||||
{
|
|
||||||
$thisAmount += 1000 * ($performance->audience - 30);
|
$thisAmount += 1000 * ($performance->audience - 30);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "comedy":
|
case 'comedy':
|
||||||
$thisAmount = 30000;
|
$thisAmount = 30000;
|
||||||
if($performance->audience > 20)
|
if ($performance->audience > 20) {
|
||||||
{
|
|
||||||
$thisAmount += 10000 + 500 * ($performance->audience - 20);
|
$thisAmount += 10000 + 500 * ($performance->audience - 20);
|
||||||
}
|
}
|
||||||
$thisAmount += 300 * $performance->audience;
|
$thisAmount += 300 * $performance->audience;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new \Error("Unknow type: $play->type");
|
throw new Error("Unknown type: {$play->type}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add volume credits
|
||||||
$volumeCredits += max($performance->audience - 30, 0);
|
$volumeCredits += max($performance->audience - 30, 0);
|
||||||
if($play->type == 'comedy')
|
// add extra credit for every ten comedy attendees
|
||||||
{
|
if ($play->type === 'comedy') {
|
||||||
$volumeCredits += floor($performance->audience / 5);
|
$volumeCredits += floor($performance->audience / 5);
|
||||||
}
|
}
|
||||||
$thisFinalAmount = $thisAmount / 100;
|
|
||||||
$result = "$play->name: $thisFinalAmount ($performance->audience seats)\n";
|
// print line for this order
|
||||||
|
$result .= " {$play->name}: {$format->formatCurrency($thisAmount / 100, 'USD')} ";
|
||||||
|
$result .= "({$performance->audience} seats)\n";
|
||||||
|
|
||||||
$totalAmount += $thisAmount;
|
$totalAmount += $thisAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
$finalTotal = ($totalAmount / 100);
|
$result .= "Amount owed is {$format ->formatCurrency($totalAmount / 100, 'USD')}\n";
|
||||||
$result .= "Amount owed is $finalTotal\n";
|
$result .= "You earned {$volumeCredits} credits";
|
||||||
$result .= "You earned $volumeCredits credits\n";
|
|
||||||
return $result;
|
return $result;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,50 +1,51 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
declare(strict_types=1);
|
||||||
use ApprovalTests\Approvals;
|
|
||||||
|
|
||||||
use Theatrical\StatementPrinter;
|
namespace Tests;
|
||||||
use Theatrical\Play;
|
|
||||||
use Theatrical\Performance;
|
use ApprovalTests\Approvals;
|
||||||
|
use Error;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
use Theatrical\Invoice;
|
use Theatrical\Invoice;
|
||||||
|
use Theatrical\Performance;
|
||||||
|
use Theatrical\Play;
|
||||||
|
use Theatrical\StatementPrinter;
|
||||||
|
|
||||||
final class StatementPrinterTest extends TestCase
|
final class StatementPrinterTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testCanPrintInvoice() : void
|
public function testCanPrintInvoice(): void
|
||||||
{
|
{
|
||||||
$plays = [
|
$plays = [
|
||||||
"hamlet" => new Play("Hamlet", "tragedy"),
|
'hamlet' => new Play('Hamlet', 'tragedy'),
|
||||||
"as-like" => new Play("As You Like It", "comedy"),
|
'as-like' => new Play('As You Like It', 'comedy'),
|
||||||
"othello" => new Play("Othello", "tragedy")
|
'othello' => new Play('Othello', 'tragedy'),
|
||||||
];
|
];
|
||||||
|
|
||||||
$performances = [
|
$performances = [
|
||||||
new Performance("hamlet", 55),
|
new Performance('hamlet', 55),
|
||||||
new Performance("as-like", 35),
|
new Performance('as-like', 35),
|
||||||
new Performance("othello", 40)
|
new Performance('othello', 40),
|
||||||
];
|
];
|
||||||
$invoice = new Invoice("BigCo", $performances);
|
$invoice = new Invoice('BigCo', $performances);
|
||||||
$statementPrinter = new StatementPrinter();
|
$statementPrinter = new StatementPrinter();
|
||||||
$result = $statementPrinter->print($invoice, $plays);
|
$result = $statementPrinter->print($invoice, $plays);
|
||||||
|
|
||||||
Approvals::verifyString($result);
|
Approvals::verifyString($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNewPlayTypes() : void
|
public function testNewPlayTypes(): void
|
||||||
{
|
{
|
||||||
$plays = [
|
$plays = [
|
||||||
"henry-v" => new Play("Henry V", "history"),
|
'henry-v' => new Play('Henry V', 'history'),
|
||||||
"as-like" => new Play("As You Like It", "comedy"),
|
'as-like' => new Play('As You Like It', 'comedy'),
|
||||||
];
|
];
|
||||||
|
|
||||||
$performances = [
|
$performances = [new Performance('henry-v', 53), new Performance('as-like', 55)];
|
||||||
new Performance("henry-v", 53),
|
|
||||||
new Performance("as-like", 55)
|
|
||||||
];
|
|
||||||
|
|
||||||
$invoice = new Invoice("BigCo", $performances);
|
$invoice = new Invoice('BigCo', $performances);
|
||||||
$statementPrinter = new StatementPrinter();
|
$statementPrinter = new StatementPrinter();
|
||||||
$this->expectException(\Error::class);
|
$this->expectException(Error::class);
|
||||||
$statementPrinter->print($invoice, $plays);
|
$statementPrinter->print($invoice, $plays);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
Othello: 500 (40 seats)
|
Statement for BigCo
|
||||||
Amount owed is 1730
|
Hamlet: $650.00 (55 seats)
|
||||||
|
As You Like It: $580.00 (35 seats)
|
||||||
|
Othello: $500.00 (40 seats)
|
||||||
|
Amount owed is $1,730.00
|
||||||
You earned 47 credits
|
You earned 47 credits
|
||||||
Reference in New Issue
Block a user