My attempts at exercism.org
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(php): add already done exercises

cosmeak 5d4e9231

+3196
+4
.gitignore
··· 1 + */**/.exercism/ 2 + */**/HELP.md 3 + */**/HINTS.md 4 + */**/flake.lock
+45
README.md
··· 1 + # Exercism Exercises 2 + 3 + A collection of my solutions to exercises completed on the [Exercism](https://exercism.org) platform. 4 + 5 + ## About Exercism 6 + 7 + Exercism is a free, open-source platform that offers code practice and mentorship across dozens of programming language tracks. Each track focuses on a specific programming language and provides a set of exercises ranging from beginner to advanced level. Exercises can be completed locally using the Exercism CLI and submitted for community or mentor feedback. 8 + 9 + ## Repository Structure 10 + 11 + Exercises are organized by language track. Each track folder contains a `flake.nix` file that provides a Nix development shell with all the necessary tooling to work on exercises locally, along with one subdirectory per completed exercise. 12 + 13 + Some files and folders such as `.exercism` and `HELP.md` are ignored because they are identical across every exercise and are not relevant to reading the solutions. 14 + 15 + ``` 16 + . 17 + ├── zig/ 18 + │ ├── flake.nix 19 + │ ├── hello-world/ 20 + │ └── ... 21 + ├── php/ 22 + │ ├── flake.nix 23 + │ ├── hello-world/ 24 + │ └── ... 25 + ``` 26 + 27 + ## Development Environment 28 + 29 + Each language folder contains a `flake.nix` that sets up a ready-to-use development shell via Nix. This ensures a reproducible environment without manually installing compilers, runtimes, or package managers on the host system. 30 + 31 + To enter the development shell for a given track, run the following from inside the track folder: 32 + 33 + ```bash 34 + nix develop 35 + ``` 36 + 37 + This requires [Nix](https://nixos.org/download) to be installed with flakes enabled. 38 + 39 + ## Purpose 40 + 41 + This repository serves as a personal archive of progress made on the platform. It is not intended as a reference for official solutions and reflects my own learning process. 42 + 43 + ## Missing Exercises 44 + 45 + Some exercises cannot be downloaded to be completed locally. All solutions, including those done directly on the platform, can be found on my profile: [cosmeak](https://exercism.org/profiles/cosmeak).
+8
php/city-office/Address.php
··· 1 + <?php 2 + 3 + class Address 4 + { 5 + public string $street; 6 + public string $postal_code; 7 + public string $city; 8 + }
+232
php/city-office/CityOfficeTest.php
··· 1 + <?php 2 + 3 + use PHPUnit\Framework\TestCase; 4 + use PHPUnit\Framework\Attributes\TestDox; 5 + 6 + require_once 'ReflectionAssertions.php'; 7 + require_once 'Address.php'; 8 + require_once 'Form.php'; 9 + 10 + class CityOfficeTest extends TestCase 11 + { 12 + use ReflectionAssertions; 13 + 14 + /** 15 + * @task_id 1 16 + */ 17 + #[TestDox('specify a string type for Address::$street')] 18 + public function testTypeOfAddressStreetProperty() 19 + { 20 + $this->assertProperty( 21 + class_name: Address::class, 22 + property_name: 'street', 23 + assertions: [ 24 + 'has_type' => true, 25 + 'type_name' => 'string', 26 + 'type_allows_null' => false 27 + ] 28 + ); 29 + } 30 + 31 + /** 32 + * @task_id 1 33 + */ 34 + #[TestDox('specify a string type for Address::$postal_code')] 35 + public function testTypeOfAddressPostalCodeProperty() 36 + { 37 + $this->assertProperty( 38 + class_name: Address::class, 39 + property_name: 'postal_code', 40 + assertions: [ 41 + 'has_type' => true, 42 + 'type_name' => 'string', 43 + 'type_allows_null' => false 44 + ] 45 + ); 46 + } 47 + 48 + /** 49 + * @task_id 1 50 + */ 51 + #[TestDox('specify a string type for Address::$city')] 52 + public function testTypeOfAddressCityProperty() 53 + { 54 + $this->assertProperty( 55 + class_name: Address::class, 56 + property_name: 'city', 57 + assertions: [ 58 + 'has_type' => true, 59 + 'type_name' => 'string', 60 + 'type_allows_null' => false 61 + ] 62 + ); 63 + } 64 + 65 + /** 66 + * @task_id 2 67 + */ 68 + #[TestDox('specify an int type for Form::blanks length parameter')] 69 + public function testParameterTypeOfFormBlanksLengthParameter() 70 + { 71 + $this->assertMethodParameter( 72 + class_name: Form::class, 73 + method_name: 'blanks', 74 + parameter_name: 'length', 75 + parameter_index: 0, 76 + assertions: [ 77 + 'has_type' => true, 78 + 'type_name' => 'int', 79 + 'type_allows_null' => false, 80 + 'has_default_value' => false 81 + ] 82 + ); 83 + } 84 + 85 + /** 86 + * @task_id 2 87 + */ 88 + #[TestDox('specify an int type for Form::blanks return type')] 89 + public function testParameterTypeOfFormBlanksReturnType() 90 + { 91 + $this->assertMethodReturnType( 92 + class_name: Form::class, 93 + method_name: 'blanks', 94 + assertions: [ 95 + 'has_type' => true, 96 + 'type_name' => 'string', 97 + 'type_allows_null' => false, 98 + ] 99 + ); 100 + } 101 + 102 + /** 103 + * @task_id 3 104 + */ 105 + #[TestDox('specify an int type for Form::letters word parameter')] 106 + public function testParameterTypeOfFormLettersWordParameter() 107 + { 108 + $this->assertMethodParameter( 109 + class_name: Form::class, 110 + method_name: 'letters', 111 + parameter_name: 'word', 112 + parameter_index: 0, 113 + assertions: [ 114 + 'has_type' => true, 115 + 'type_name' => 'string', 116 + 'type_allows_null' => false, 117 + 'has_default_value' => false 118 + ] 119 + ); 120 + } 121 + 122 + /** 123 + * @task_id 3 124 + */ 125 + #[TestDox('specify an int type for Form::letters return type')] 126 + public function testParameterTypeOfFormLettersReturnType() 127 + { 128 + $this->assertMethodReturnType( 129 + class_name: Form::class, 130 + method_name: 'letters', 131 + assertions: [ 132 + 'has_type' => true, 133 + 'type_name' => 'array', 134 + 'type_allows_null' => false, 135 + ] 136 + ); 137 + } 138 + 139 + /** 140 + * @task_id 4 141 + */ 142 + #[TestDox('specify an int type for Form::checkLength word parameter')] 143 + public function testParameterTypeOfFormCheckLengthWordParameter() 144 + { 145 + $this->assertMethodParameter( 146 + class_name: Form::class, 147 + method_name: 'checkLength', 148 + parameter_name: 'word', 149 + parameter_index: 0, 150 + assertions: [ 151 + 'has_type' => true, 152 + 'type_name' => 'string', 153 + 'type_allows_null' => false, 154 + 'has_default_value' => false 155 + ] 156 + ); 157 + } 158 + 159 + /** 160 + * @task_id 4 161 + */ 162 + #[TestDox('specify an int type for Form::checkLength max_length parameter')] 163 + public function testParameterTypeOfFormCheckLengthMaxLengthParameter() 164 + { 165 + $this->assertMethodParameter( 166 + class_name: Form::class, 167 + method_name: 'checkLength', 168 + parameter_name: 'max_length', 169 + parameter_index: 1, 170 + assertions: [ 171 + 'has_type' => true, 172 + 'type_name' => 'int', 173 + 'type_allows_null' => false, 174 + 'has_default_value' => false 175 + ] 176 + ); 177 + } 178 + 179 + /** 180 + * @task_id 4 181 + */ 182 + #[TestDox('specify an int type for Form::checkLength return type')] 183 + public function testParameterTypeOfFormCheckLengthReturnType() 184 + { 185 + $this->assertMethodReturnType( 186 + class_name: Form::class, 187 + method_name: 'checkLength', 188 + assertions: [ 189 + 'has_type' => true, 190 + 'type_name' => 'bool', 191 + 'type_allows_null' => false, 192 + ] 193 + ); 194 + } 195 + 196 + /** 197 + * @task_id 5 198 + */ 199 + #[TestDox('specify an Address type for Form::formatAddress address parameter')] 200 + public function testParameterTypeOfFormFormatAddressParameter() 201 + { 202 + $this->assertMethodParameter( 203 + class_name: Form::class, 204 + method_name: 'formatAddress', 205 + parameter_name: 'address', 206 + parameter_index: 0, 207 + assertions: [ 208 + 'has_type' => true, 209 + 'type_name' => 'Address', 210 + 'type_allows_null' => false, 211 + 'has_default_value' => false 212 + ] 213 + ); 214 + } 215 + 216 + /** 217 + * @task_id 5 218 + */ 219 + #[TestDox('specify an int type for Form::checkLength return type')] 220 + public function testParameterTypeOfFormFormatAddressReturnType() 221 + { 222 + $this->assertMethodReturnType( 223 + class_name: Form::class, 224 + method_name: 'formatAddress', 225 + assertions: [ 226 + 'has_type' => true, 227 + 'type_name' => 'string', 228 + 'type_allows_null' => false, 229 + ] 230 + ); 231 + } 232 + }
+102
php/city-office/README.md
··· 1 + # City Office 2 + 3 + Welcome to City Office on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) 6 + 7 + ## Introduction 8 + 9 + ## Type Declaration 10 + 11 + Type declarations in PHP provide type assertions at run time for function arguments, return values and class properties. 12 + On functions, a `void` return type can be added to indicate that no value is returned from the function. 13 + Declared types also serve as run time assertions that functions are returning a reasonably typed response. 14 + 15 + ```php 16 + <?php 17 + 18 + class Driver 19 + { 20 + private int $serial_number; 21 + 22 + public function setSerialNumber(int $number): void 23 + { 24 + $this->serial_number = $number; 25 + } 26 + 27 + public function getSerialNumber(): int 28 + { 29 + return $this->serial_number; 30 + } 31 + } 32 + 33 + $driver = new Driver(); 34 + $driver->setSerialNumber("Version 1b"); // This will throw a TypeError 35 + ``` 36 + 37 + ### Type Unions 38 + 39 + If a function argument, return value or class property can be more than one type a type union may be declared. 40 + 41 + ```php 42 + <?php 43 + 44 + class IdentityCard 45 + { 46 + private int|null $id = null; 47 + 48 + public function assign(int|float $id): void 49 + { 50 + $this->id = intval($id); 51 + } 52 + } 53 + 54 + $card = new IdentityCard(); 55 + $card->assign(5.0); 56 + ``` 57 + 58 + ### Mixed Types 59 + 60 + When working with code, we may not be able to specify a type. 61 + Starting in PHP 8.0,an escape-hatch type named `mixed` is provided. 62 + Mixed is equivalent to a type union of `object|resource|array|string|float|int|bool|null`. 63 + 64 + ## Instructions 65 + 66 + You have been working in the city office for a while, and you have developed a set of tools that speed up your day-to-day work, for example with filling out forms. 67 + 68 + Now, a new colleague is joining you, and you realized your tools might not be self-explanatory. 69 + There are a lot of weird conventions in your office, like always filling out forms with uppercase letters and avoiding leaving fields empty. 70 + 71 + As a first step you decide to add PHP type declarations so that it is easier for your new colleague to hop right in and start using your tools. 72 + 73 + ## 1. Declare the types for the Address class 74 + 75 + Add property type declarations to each of the `Address` class properties declared. 76 + Each class property should be declared as a string. 77 + 78 + ## 2. Declare the types to fill out the form with blank values 79 + 80 + Add a parameter type declaration and a return type declaration to the `blanks` method in the `Form` class. 81 + The method should take in an integer length, and return a string representation of the blank line. 82 + 83 + ## 3. Declare the type when splitting a value into separate letters 84 + 85 + Add a parameter type declaration and a return type declaration to the `letters` method in the `Form` class. 86 + The method should take in a string, representing words, and return an array of letters. 87 + 88 + ## 4. Declare the type when checking if a value will fit into a form 89 + 90 + Add parameter type declarations and a return type declaration to the `checkLength` method in the `Form` class. 91 + The method should take in a string word, and an integer maximum length, and return a true or false value. 92 + 93 + ## 5. Declare the type when formatting an address in the form 94 + 95 + Add a parameter type declaration, making use of the `Address` class updated previously, and a return type declaration to the `formatAddress` method in the `Form` class. 96 + The method should take in an `Address` and return a formatted string. 97 + 98 + ## Source 99 + 100 + ### Created by 101 + 102 + - @neenjaw
+32
php/flake.nix
··· 1 + { 2 + description = "Exercism PHP Track"; 3 + 4 + inputs = { 5 + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; 6 + }; 7 + 8 + outputs = { self, nixpkgs }: 9 + let 10 + systems = [ 11 + "x86_64-linux" 12 + "aarch64-linux" 13 + "aarch64-darwin" 14 + ]; 15 + 16 + forAllSystems = f: 17 + nixpkgs.lib.genAttrs systems (system: 18 + f (import nixpkgs { inherit system; }) 19 + ); 20 + in 21 + { 22 + devShells = forAllSystems (pkgs: { 23 + default = pkgs.mkShell { 24 + packages = with pkgs; [ 25 + exercism 26 + php85 27 + phpunit 28 + ]; 29 + }; 30 + }); 31 + }; 32 + }
+20
php/hamming/Hamming.php
··· 1 + <?php 2 + 3 + declare(strict_types=1); 4 + 5 + function distance(string $strandA, string $strandB): int 6 + { 7 + if (strlen($strandA) != strlen($strandB)) { 8 + throw new InvalidArgumentException("strands must be of equal length"); 9 + } 10 + 11 + $distance = 0; 12 + 13 + for ($i = 0; $i < strlen($strandA); $i++) { 14 + if ($strandA[$i] != $strandB[$i]) { 15 + $distance++; 16 + } 17 + } 18 + 19 + return $distance; 20 + }
+109
php/hamming/HammingTest.php
··· 1 + <?php 2 + 3 + declare(strict_types=1); 4 + 5 + use PHPUnit\Framework\TestCase; 6 + use PHPUnit\Framework\Attributes\TestDox; 7 + 8 + class HammingTest extends TestCase 9 + { 10 + public static function setUpBeforeClass(): void 11 + { 12 + require_once 'Hamming.php'; 13 + } 14 + 15 + /** 16 + * uuid: f6dcb64f-03b0-4b60-81b1-3c9dbf47e887 17 + */ 18 + #[TestDox('Empty strands')] 19 + public function testEmptyStrands(): void 20 + { 21 + $this->assertEquals(0, distance('', '')); 22 + } 23 + 24 + /** 25 + * uuid: 54681314-eee2-439a-9db0-b0636c656156 26 + */ 27 + #[TestDox('Single letter identical strands')] 28 + public function testSingleLetterIdenticalStrands(): void 29 + { 30 + $this->assertEquals(0, distance('A', 'A')); 31 + } 32 + 33 + /** 34 + * uuid: 294479a3-a4c8-478f-8d63-6209815a827b 35 + */ 36 + #[TestDox('Single letter different strands')] 37 + public function testSingleLetterDifferentStrands(): void 38 + { 39 + $this->assertEquals(1, distance('G', 'T')); 40 + } 41 + 42 + /** 43 + * uuid: 9aed5f34-5693-4344-9b31-40c692fb5592 44 + */ 45 + #[TestDox('Long identical strands')] 46 + public function testLongIdenticalStrands(): void 47 + { 48 + $this->assertEquals(0, distance( 49 + 'GGACTGAAATCTG', 50 + 'GGACTGAAATCTG' 51 + )); 52 + } 53 + 54 + /** 55 + * uuid: cd2273a5-c576-46c8-a52b-dee251c3e6e5 56 + */ 57 + #[TestDox('Long different strands')] 58 + public function testLongDifferentStrand(): void 59 + { 60 + $this->assertEquals(9, distance( 61 + 'GGACGGATTCTG', 62 + 'AGGACGGATTCT' 63 + )); 64 + } 65 + 66 + /** 67 + * uuid: b9228bb1-465f-4141-b40f-1f99812de5a8 68 + */ 69 + #[TestDox('Disallow first strand longer')] 70 + public function testDisallowFirstStrandLonger(): void 71 + { 72 + $this->expectException('InvalidArgumentException'); 73 + $this->expectExceptionMessage('strands must be of equal length'); 74 + distance('AATG', 'AAA'); 75 + } 76 + 77 + /** 78 + * uuid: dab38838-26bb-4fff-acbe-3b0a9bfeba2d 79 + */ 80 + #[TestDox(': Disallow second strand longer')] 81 + public function testDisallowSecondStrandLonger(): void 82 + { 83 + $this->expectException('InvalidArgumentException'); 84 + $this->expectExceptionMessage('strands must be of equal length'); 85 + distance('ATA', 'AATG'); 86 + } 87 + 88 + /** 89 + * uuid: b764d47c-83ff-4de2-ab10-6cfe4b15c0f3 90 + */ 91 + #[TestDox(': Disallow empty first strand')] 92 + public function testDisallowEmptyFirstStrand(): void 93 + { 94 + $this->expectException('InvalidArgumentException'); 95 + $this->expectExceptionMessage('strands must be of equal length'); 96 + distance('', 'G'); 97 + } 98 + 99 + /** 100 + * uuid: 9ab9262f-3521-4191-81f5-0ed184a5aa89 101 + */ 102 + #[TestDox(': Disallow empty second strand')] 103 + public function testDisallowEmptySecondStrand(): void 104 + { 105 + $this->expectException('InvalidArgumentException'); 106 + $this->expectExceptionMessage('strands must be of equal length'); 107 + distance('G', ''); 108 + } 109 + }
+53
php/hamming/README.md
··· 1 + # Hamming 2 + 3 + Welcome to Hamming on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + 6 + ## Introduction 7 + 8 + Your body is made up of cells that contain DNA. 9 + Those cells regularly wear out and need replacing, which they achieve by dividing into daughter cells. 10 + In fact, the average human body experiences about 10 quadrillion cell divisions in a lifetime! 11 + 12 + When cells divide, their DNA replicates too. 13 + Sometimes during this process mistakes happen and single pieces of DNA get encoded with the incorrect information. 14 + If we compare two strands of DNA and count the differences between them, we can see how many mistakes occurred. 15 + This is known as the "Hamming distance". 16 + 17 + The Hamming distance is useful in many areas of science, not just biology, so it's a nice phrase to be familiar with :) 18 + 19 + ## Instructions 20 + 21 + Calculate the Hamming distance between two DNA strands. 22 + 23 + We read DNA using the letters C, A, G and T. 24 + Two strands might look like this: 25 + 26 + GAGCCTACTAACGGGAT 27 + CATCGTAATGACGGCCT 28 + ^ ^ ^ ^ ^ ^^ 29 + 30 + They have 7 differences, and therefore the Hamming distance is 7. 31 + 32 + ## Implementation notes 33 + 34 + The Hamming distance is only defined for sequences of equal length, so an attempt to calculate it between sequences of different lengths should not work. 35 + 36 + ## Source 37 + 38 + ### Contributed to by 39 + 40 + - @arueckauer 41 + - @dkinzer 42 + - @Dog 43 + - @kip-13 44 + - @kunicmarko20 45 + - @kytrinyx 46 + - @lafent 47 + - @marvinrabe 48 + - @petemcfarlane 49 + - @rossbearman 50 + 51 + ### Based on 52 + 53 + The Calculating Point Mutations problem at Rosalind - https://rosalind.info/problems/hamm/
+6
php/hello-world/HelloWorld.php
··· 1 + <?php 2 + 3 + function helloWorld() 4 + { 5 + return "Hello, World!"; 6 + }
+23
php/hello-world/HelloWorldTest.php
··· 1 + <?php 2 + 3 + declare(strict_types=1); 4 + 5 + use PHPUnit\Framework\TestCase; 6 + use PHPUnit\Framework\Attributes\TestDox; 7 + 8 + class HelloWorldTest extends TestCase 9 + { 10 + public static function setUpBeforeClass(): void 11 + { 12 + require_once 'HelloWorld.php'; 13 + } 14 + 15 + /** 16 + * uuid: af9ffe10-dc13-42d8-a742-e7bdafac449d 17 + */ 18 + #[TestDox('Say Hi!')] 19 + public function testHelloWorld(): void 20 + { 21 + $this->assertEquals('Hello, World!', helloWorld()); 22 + } 23 + }
+38
php/hello-world/README.md
··· 1 + # Hello World 2 + 3 + Welcome to Hello World on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + 6 + ## Instructions 7 + 8 + The classical introductory exercise. 9 + Just say "Hello, World!". 10 + 11 + ["Hello, World!"][hello-world] is the traditional first program for beginning programming in a new language or environment. 12 + 13 + The objectives are simple: 14 + 15 + - Modify the provided code so that it produces the string "Hello, World!". 16 + - Run the test suite and make sure that it succeeds. 17 + - Submit your solution and check it at the website. 18 + 19 + If everything goes well, you will be ready to fetch your first real exercise. 20 + 21 + [hello-world]: https://en.wikipedia.org/wiki/%22Hello,_world!%22_program 22 + 23 + ## Source 24 + 25 + ### Created by 26 + 27 + - @duffn 28 + 29 + ### Contributed to by 30 + 31 + - @arueckauer 32 + - @joseph-walker 33 + - @kytrinyx 34 + - @petemcfarlane 35 + 36 + ### Based on 37 + 38 + This is an exercise to introduce users to using Exercism - https://en.wikipedia.org/wiki/%22Hello,_world!%22_program
+30
php/language-list/LanguageList.php
··· 1 + <?php 2 + 3 + function language_list(string ...$languages) 4 + { 5 + return $languages; 6 + } 7 + 8 + function add_to_language_list(array $language_list, string $language) 9 + { 10 + $language_list[] = $language; 11 + 12 + return $language_list; 13 + } 14 + 15 + function prune_language_list($language_list) 16 + { 17 + array_shift($language_list); 18 + 19 + return $language_list; 20 + } 21 + 22 + function current_language($language_list) 23 + { 24 + return $language_list[0] || null; 25 + } 26 + 27 + function language_list_length($language_list) 28 + { 29 + return count($language_list); 30 + }
+140
php/language-list/LanguageListTest.php
··· 1 + <?php 2 + 3 + use PHPUnit\Framework\TestCase; 4 + use PHPUnit\Framework\Attributes\TestDox; 5 + 6 + class LanguageListTest extends TestCase 7 + { 8 + public static function setUpBeforeClass(): void 9 + { 10 + require_once 'LanguageList.php'; 11 + } 12 + 13 + /** 14 + * @task_id 1 15 + */ 16 + #[TestDox('calling language_list without arguments, returns empty array')] 17 + public function testEmpty() 18 + { 19 + $language_list = language_list(); 20 + $this->assertEquals([], $language_list); 21 + } 22 + 23 + /** 24 + * @task_id 2 25 + */ 26 + #[TestDox('variadic call to language_list with 1 arg returns args')] 27 + public function testVariadicCallWithOne() 28 + { 29 + $language_list = language_list('c'); 30 + $this->assertEquals(['c'], $language_list); 31 + } 32 + 33 + /** 34 + * @task_id 2 35 + */ 36 + #[TestDox('variadic call to language_list with 2 arg returns args')] 37 + public function testVariadicCallWithTwo() 38 + { 39 + $language_list = language_list('c', 'cpp'); 40 + $this->assertEquals(['c', 'cpp'], $language_list); 41 + } 42 + 43 + /** 44 + * @task_id 2 45 + */ 46 + #[TestDox('variadic call to language_list with 2 arg returns args')] 47 + public function testVariadicCallWithThree() 48 + { 49 + $language_list = language_list('c', 'cpp', 'php'); 50 + $this->assertEquals(['c', 'cpp', 'php'], $language_list); 51 + } 52 + 53 + /** 54 + * @task_id 3 55 + */ 56 + #[TestDox('push new languages to the back of the list')] 57 + public function testAddingToLanguageList() 58 + { 59 + $language_list = language_list('c', 'cpp', 'php'); 60 + $pushed_list = add_to_language_list($language_list, 'java'); 61 + $this->assertEquals(['c', 'cpp', 'php', 'java'], $pushed_list); 62 + } 63 + 64 + /** 65 + * @task_id 3 66 + */ 67 + #[TestDox('when pushing, original is unchanged')] 68 + public function testAddingDoesNotMutate() 69 + { 70 + $language_list = language_list('c', 'cpp', 'php'); 71 + $pushed_list = add_to_language_list($language_list, 'java'); 72 + $this->assertNotEquals($language_list, $pushed_list); 73 + } 74 + 75 + /** 76 + * @task_id 4 77 + */ 78 + #[TestDox('remove completed language from the front of the list')] 79 + public function testCompleteLanguageList() 80 + { 81 + $language_list = language_list('c', 'cpp', 'php'); 82 + $pruned_list = prune_language_list($language_list); 83 + $this->assertEquals(['cpp', 'php'], $pruned_list); 84 + } 85 + 86 + /** 87 + * @task_id 4 88 + */ 89 + #[TestDox('when pushing, original is unchanged')] 90 + public function testPruningDoesNotMutate() 91 + { 92 + $language_list = language_list('c', 'cpp', 'php'); 93 + $pruned_list = prune_language_list($language_list); 94 + $this->assertNotEquals($language_list, $pruned_list); 95 + } 96 + 97 + /** 98 + * @task_id 5 99 + */ 100 + #[TestDox('index and return the first language')] 101 + public function testCurrentReturnsTheFirstLanguage() 102 + { 103 + $language_list = language_list('php'); 104 + $current = current_language($language_list); 105 + $this->assertEquals('php', $current); 106 + } 107 + 108 + /** 109 + * @task_id 5 110 + */ 111 + #[TestDox('when getting the first language, original is unchanged')] 112 + public function testGettingFirstDoesNotMutate() 113 + { 114 + $language_list = language_list('c', 'cpp', 'php'); 115 + current_language($language_list); 116 + $this->assertEquals(['c', 'cpp', 'php'], $language_list); 117 + } 118 + 119 + /** 120 + * @task_id 6 121 + */ 122 + #[TestDox('the count of the languages in the language list')] 123 + public function testLanguageListCount() 124 + { 125 + $language_list = language_list('c', 'cpp', 'php'); 126 + $languages_count = language_list_length($language_list); 127 + $this->assertEquals(3, $languages_count); 128 + } 129 + 130 + /** 131 + * @task_id 6 132 + */ 133 + #[TestDox('when getting the language count, original is unchanged')] 134 + public function testLanguageListCountDoesNotMutate() 135 + { 136 + $language_list = language_list('c', 'cpp', 'php'); 137 + language_list_length($language_list); 138 + $this->assertCount(3, $language_list); 139 + } 140 + }
+147
php/language-list/README.md
··· 1 + # Language List 2 + 3 + Welcome to Language List on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) 6 + 7 + ## Introduction 8 + 9 + ## Arrays 10 + 11 + Arrays in PHP are ordered maps. 12 + A map is a data structure that associates a key to a value. 13 + Arrays are versatile and can be treated as a linear array (like in `C` or `Java`), list [vector], hash-table, dictionary, collection, stack, or a queue. 14 + 15 + A key is optional, but if specified must either be an `int` or a `string`. 16 + When a key is not provided, PHP defaults to integer keys in increasing order, starting at `0`. 17 + 18 + ```php 19 + $no_keys = ["my", "first", "array"]; 20 + $integer_keys = [0 => "my", 1 => "first", 2 => "array"]; 21 + 22 + $no_keys == $integer_keys // => equal returns true 23 + $no_keys === $integer_keys // => strictly equal returns true 24 + ``` 25 + 26 + An array can store values of all types. 27 + Each value can have a different type. 28 + 29 + ### Using Arrays 30 + 31 + Arrays can be declared as a literal (written in code, as done above) or created and manipulated as functions. 32 + Access, assign, append values using the index operator: 33 + 34 + ```php 35 + $prime_numbers = [2, 3, 5, 6]; 36 + 37 + $prime_numbers[3] = 7; // replace 6 with 7 38 + 39 + $prime_numbers[] = 11; // array now contains [2, 3, 5, 7, 11] 40 + ``` 41 + 42 + ## Variable-Length Arguments 43 + 44 + Function arguments can be specified such that it can take any number of arguments: 45 + 46 + ```php 47 + <?php 48 + 49 + function actOnItems(...$items) { 50 + // $items is an array containing 0 or more values 51 + // used to call the function. 52 + } 53 + 54 + actOnItems(1, 2, 3, 4); // $items => [1, 2, 3, 4] 55 + ``` 56 + 57 + ## Instructions 58 + 59 + In this exercise you need to implement some functions to manipulate a list of programming languages. 60 + 61 + ## 1. Define a function to return an empty language list 62 + 63 + Define the `language_list` function that takes no arguments and returns an empty list. 64 + 65 + ```php 66 + <?php 67 + 68 + language_list(); 69 + // => [] 70 + ``` 71 + 72 + ## 2. Modify function to create a list from any number of languages 73 + 74 + Modify the `language_list` function, so it takes a variadic argument of languages (strings). 75 + It should return the resulting list with the languages in the list. 76 + 77 + ```php 78 + <?php 79 + 80 + $language_list = language_list(); 81 + // => [] 82 + $language_list = language_list("Clojure", "PHP"); 83 + // => ["Clojure", "PHP"] 84 + $language_list = language_list("PHP", "Haskell", "Java", "C++", "Rust") 85 + // => ["PHP", "Haskell", "Java", "C++", "Rust"] 86 + ``` 87 + 88 + ## 3. Define a function to add a language to the list 89 + 90 + Define the `add_to_language_list` function that takes 2 arguments, an array of languages, and the new language. 91 + 92 + ```php 93 + <?php 94 + 95 + $language_list = language_list(); 96 + // => [] 97 + $language_list = add_to_language_list($language_list, "Clojure"); 98 + // => ["Clojure"] 99 + ``` 100 + 101 + ## 4. Define a function to remove an item from the language list 102 + 103 + Define the `prune_language_list` function to remove the first language from the array of languages. 104 + 105 + ```php 106 + <?php 107 + 108 + $language_list = language_list("PHP"); 109 + // => ["PHP"] 110 + $language_list = prune_language_list($language_list); 111 + // => [] 112 + ``` 113 + 114 + ## 5. Define a function to get the first item in the list 115 + 116 + Define the `current_language` function that takes 1 argument (a _language list_). 117 + It should return the first language in the list. 118 + Assume the list will always have at least one item. 119 + 120 + ```php 121 + <?php 122 + 123 + $language_list = language_list("PHP", "Prolog"); 124 + // => ["PHP", "Prolog"] 125 + $first = current_language($language_list); 126 + // => "PHP" 127 + ``` 128 + 129 + ## 6. Define a function to return how many languages are in the list 130 + 131 + Define the `language_list_length` function that takes 1 argument (a _language list_). 132 + It should return the number of languages in the list. 133 + 134 + ```php 135 + <?php 136 + 137 + $language_list = language_list("PHP", "Prolog", "Wren"); 138 + // => ["PHP", "Prolog", "Wren"] 139 + language_list_length($language_list); 140 + // => 3 141 + ``` 142 + 143 + ## Source 144 + 145 + ### Created by 146 + 147 + - @neenjaw
+29
php/lasagna/Lasagna.php
··· 1 + <?php 2 + 3 + class Lasagna 4 + { 5 + public function expectedCookTime(): int 6 + { 7 + return 40; 8 + } 9 + 10 + public function remainingCookTime($elapsed_minutes): int 11 + { 12 + return $this->expectedCookTime() - $elapsed_minutes; 13 + } 14 + 15 + public function totalPreparationTime($layers_to_prep): int 16 + { 17 + return $layers_to_prep * 2; 18 + } 19 + 20 + public function totalElapsedTime($layers_to_prep, $elapsed_minutes): int 21 + { 22 + return $this->totalPreparationTime($layers_to_prep) + ($this->expectedCookTime() - $this->remainingCookTime($elapsed_minutes)); 23 + } 24 + 25 + public function alarm(): string 26 + { 27 + return "Ding!"; 28 + } 29 + }
+86
php/lasagna/LasagnaTest.php
··· 1 + <?php 2 + 3 + declare(strict_types=1); 4 + 5 + use PHPUnit\Framework\TestCase; 6 + use PHPUnit\Framework\Attributes\TestDox; 7 + 8 + /** 9 + * We use `assertEquals()` here for its loose type checking. We don't care if 10 + * the student returns numeric strings or integers in this exercise. 11 + * 12 + * - Please use `assertSame()` whenever possible. Add a comment when it is not possible. 13 + * - Do not use calls with named arguments. 14 + * Use them only when the exercise requires named arguments (e.g. because the exercise is about named arguments). 15 + * Named arguments are in the way of defining argument names the students want (e.g. in their native language). 16 + * - Add `#[TestDox()]` with a useful test title, e.g. the task heading from `instructions.md`. 17 + * The online editor shows that to students. 18 + * - Add fail messages to assertions where helpful to tell students more than `#[TestDox()]` says. 19 + */ 20 + class LasagnaTest extends TestCase 21 + { 22 + public static function setUpBeforeClass(): void 23 + { 24 + require_once 'Lasagna.php'; 25 + } 26 + 27 + /** 28 + * @task_id 1 29 + */ 30 + #[TestDox('Returns cooking time in minutes as stated in the cook book')] 31 + public function testExpectedCookTime(): void 32 + { 33 + $lasagna = new Lasagna(); 34 + $this->assertEquals(40, $lasagna->expectedCookTime()); 35 + } 36 + 37 + /** 38 + * @task_id 2 39 + */ 40 + #[TestDox('Returns how many minutes more the lasagna must be in the oven when it is 20 minutes in the oven already')] 41 + public function testRemainingCookTime(): void 42 + { 43 + $lasagna = new Lasagna(); 44 + $this->assertEquals(20, $lasagna->remainingCookTime(20)); 45 + } 46 + 47 + /** 48 + * @task_id 2 49 + */ 50 + #[TestDox('Returns how many minutes more the lasagna must be in the oven when it is 30 minutes in the oven already')] 51 + public function testAnotherRemainingCookTime(): void 52 + { 53 + $lasagna = new Lasagna(); 54 + $this->assertEquals(10, $lasagna->remainingCookTime(30)); 55 + } 56 + 57 + /** 58 + * @task_id 3 59 + */ 60 + #[TestDox('Returns how many minutes you spent preparing the lasagna with 7 layers')] 61 + public function testTotalPreparationTime(): void 62 + { 63 + $lasagna = new Lasagna(); 64 + $this->assertEquals(14, $lasagna->totalPreparationTime(7)); 65 + } 66 + 67 + /** 68 + * @task_id 4 69 + */ 70 + #[TestDox('Returns the total minutes you have worked on the lasagna with 4 layers that is 13 minutes in the oven')] 71 + public function testTotalElapsedTime(): void 72 + { 73 + $lasagna = new Lasagna(); 74 + $this->assertEquals(21, $lasagna->totalElapsedTime(4, 13)); 75 + } 76 + 77 + /** 78 + * @task_id 5 79 + */ 80 + #[TestDox('Returns the message indicating that the lasagna is ready to eat')] 81 + public function testAlarm(): void 82 + { 83 + $lasagna = new Lasagna(); 84 + $this->assertEquals("Ding!", $lasagna->alarm()); 85 + } 86 + }
+158
php/lasagna/README.md
··· 1 + # Lucian's Luscious Lasagna 2 + 3 + Welcome to Lucian's Luscious Lasagna on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) 6 + 7 + ## Introduction 8 + 9 + ## Basics 10 + 11 + For more detailed information about these topics visit the [concept page][exercism-concept]. 12 + 13 + ### General syntax 14 + 15 + The PHP opening tag `<?php` marks the start of PHP code. 16 + All statements must end with a `;` for instruction separation. 17 + 18 + ```php 19 + <?php 20 + 21 + $message = "Success!"; // Statement correctly ends with `;` 22 + $message = "I fail." // PHP Parse error: syntax error, [...] 23 + ``` 24 + 25 + For easier reading, all code examples omit the PHP opening tag `<?php`. 26 + 27 + ### Comments 28 + 29 + Single line comments start with `//`. 30 + 31 + ```php 32 + // Single line comment 33 + ``` 34 + 35 + ### Values and Variables 36 + 37 + Using the assignment `=` operator, a value may be assigned to a variable. 38 + Variable names must start with a dollar `$` sign and follow the [naming conventions][exercism-concept-naming-conventions]. 39 + 40 + ```php 41 + $count = 1; // Assign value of 1 42 + 43 + // Strings can be created by enclosing the characters 44 + // within single `'` quotes or double `"` quotes. 45 + $message = "Success!"; 46 + ``` 47 + 48 + ### Functions and Methods 49 + 50 + Values and variables can be passed to functions. 51 + Function arguments become new variables to hold values passed in. 52 + Functions may return any value using the keyword `return`. 53 + 54 + ```php 55 + function window_height($height) 56 + { 57 + return $height + 10; 58 + } 59 + 60 + window_height(100); 61 + // => 110 62 + ``` 63 + 64 + Functions inside classes and their instances are called methods. 65 + To call a method, the name is preceded by the instance and `->`. 66 + Methods have access to the special variable `$this`, which refers to the current instance. 67 + 68 + ```php 69 + <?php 70 + 71 + class Calculator { 72 + public function sub($x, $y) 73 + { 74 + return $this->add($x, -$y); // Calls the method add() of the current instance 75 + } 76 + 77 + public function add($x, $y) 78 + { 79 + return $x + $y; 80 + } 81 + } 82 + 83 + $calculator = new Calculator(); // Creates a new instance of Calculator class 84 + $calculator->sub(3, 1); // Calls the method sub() of the instance stored in $calculator 85 + // => 2 86 + ``` 87 + 88 + [exercism-concept]: /tracks/php/concepts/basic-syntax 89 + [exercism-concept-naming-conventions]: /tracks/php/concepts/basic-syntax#h-naming-conventions 90 + 91 + ## Instructions 92 + 93 + In this exercise you're going to write some code to help you cook a brilliant lasagna from your favorite cooking book. 94 + 95 + You have five tasks, all related to the time spent cooking the lasagna. 96 + 97 + ## 1. Define the expected oven time in minutes 98 + 99 + Implement the `expectedCookTime` function in class `Lasagna` that does not take any arguments and returns how many minutes the lasagna should be in the oven. 100 + According to the cooking book, the expected oven time in minutes is 40: 101 + 102 + ```php 103 + <?php 104 + $timer = new Lasagna(); 105 + $timer->expectedCookTime() 106 + // => 40 107 + ``` 108 + 109 + ## 2. Calculate the remaining oven time in minutes 110 + 111 + Implement the `remainingCookTime` function in class `Lasagna` that takes the actual minutes the lasagna has been in the oven as an argument and returns how many minutes the lasagna still has to remain in the oven, based on the expected oven time in minutes from the previous task. 112 + 113 + ```php 114 + <?php 115 + $timer = new Lasagna(); 116 + $timer->remainingCookTime(30) 117 + // => 10 118 + ``` 119 + 120 + ## 3. Calculate the preparation time in minutes 121 + 122 + Implement the `totalPreparationTime` function in class `Lasagna` that takes the number of layers you added to the lasagna as an argument and returns how many minutes you spent preparing the lasagna, assuming each layer takes you 2 minutes to prepare. 123 + 124 + ```php 125 + <?php 126 + $timer = new Lasagna(); 127 + $timer->totalPreparationTime(3) 128 + // => 6 129 + ``` 130 + 131 + ## 4. Calculate the total working time in minutes 132 + 133 + Implement the `totalElapsedTime` function in class `Lasagna` that takes two arguments: the first argument is the number of layers you added to the lasagna, and the second argument is the number of minutes the lasagna has been in the oven. 134 + The function should return how many minutes in total you've worked on cooking the lasagna, which is the sum of the preparation time in minutes, and the time in minutes the lasagna has spent in the oven at the moment. 135 + 136 + ```php 137 + <?php 138 + $timer = new Lasagna(); 139 + $timer->totalElapsedTime(3, 20) 140 + // => 26 141 + ``` 142 + 143 + ## 5. Create a notification that the lasagna is ready 144 + 145 + Implement the `alarm` function in class `Lasagna` that does not take any arguments and returns a message indicating that the lasagna is ready to eat. 146 + 147 + ```php 148 + <?php 149 + $timer = new Lasagna(); 150 + $timer->alarm() 151 + // => "Ding!" 152 + ``` 153 + 154 + ## Source 155 + 156 + ### Created by 157 + 158 + - @neenjaw
+21
php/line-up/LineUp.php
··· 1 + <?php 2 + 3 + declare(strict_types=1); 4 + 5 + function format(string $name, int $number): string 6 + { 7 + $splittedNumber = str_split((string) $number); 8 + $lastNumber = end($splittedNumber); 9 + $secondToLastNumber = prev($splittedNumber) ?: "0"; 10 + if ($secondToLastNumber === "1") { 11 + $suffix = "th"; 12 + } else { 13 + $suffix = match ($lastNumber) { 14 + "1" => "st", 15 + "2" => "nd", 16 + "3" => "rd", 17 + default => "th", 18 + }; 19 + } 20 + return "{$name}, you are the {$number}{$suffix} customer we serve today. Thank you!"; 21 + }
+204
php/line-up/LineUpTest.php
··· 1 + <?php 2 + 3 + declare(strict_types=1); 4 + 5 + use PHPUnit\Framework\TestCase; 6 + use PHPUnit\Framework\Attributes\TestDox; 7 + 8 + final class LineUpTest extends TestCase 9 + { 10 + public static function setUpBeforeClass(): void 11 + { 12 + require_once('LineUp.php'); 13 + } 14 + 15 + /** uuid: 7760d1b8-4864-4db4-953b-0fa7c047dbc0 */ 16 + #[TestDox('Format smallest non-exceptional ordinal numeral 4')] 17 + public function testFormatSmallestNonExceptionalOrdinalNumeral4(): void 18 + { 19 + $this->assertSame( 20 + 'Gianna, you are the 4th customer we serve today. Thank you!', 21 + format('Gianna', 4) 22 + ); 23 + } 24 + 25 + /** uuid: e8b7c715-6baa-4f7b-8fb3-2fa48044ab7a */ 26 + #[TestDox('Format greatest single digit non-exceptional ordinal numeral 9')] 27 + public function testFormatGreatestSingleDigitNonExceptionalOrdinalNumeral9(): void 28 + { 29 + $this->assertSame( 30 + 'Maarten, you are the 9th customer we serve today. Thank you!', 31 + format('Maarten', 9) 32 + ); 33 + } 34 + 35 + /** uuid: f370aae9-7ae7-4247-90ce-e8ff8c6934df */ 36 + #[TestDox('Format non-exceptional ordinal numeral 5')] 37 + public function testFormatNonExceptionalOrdinalNumeral5(): void 38 + { 39 + $this->assertSame( 40 + 'Petronila, you are the 5th customer we serve today. Thank you!', 41 + format('Petronila', 5) 42 + ); 43 + } 44 + 45 + /** uuid: 37f10dea-42a2-49de-bb92-0b690b677908 */ 46 + #[TestDox('Format non-exceptional ordinal numeral 6')] 47 + public function testFormatNonExceptionalOrdinalNumeral6(): void 48 + { 49 + $this->assertSame( 50 + 'Attakullakulla, you are the 6th customer we serve today. Thank you!', 51 + format('Attakullakulla', 6) 52 + ); 53 + } 54 + 55 + /** uuid: d8dfb9a2-3a1f-4fee-9dae-01af3600054e */ 56 + #[TestDox('Format non-exceptional ordinal numeral 7')] 57 + public function testFormatNonExceptionalOrdinalNumeral7(): void 58 + { 59 + $this->assertSame( 60 + 'Kate, you are the 7th customer we serve today. Thank you!', 61 + format('Kate', 7) 62 + ); 63 + } 64 + 65 + /** uuid: 505ec372-1803-42b1-9377-6934890fd055 */ 66 + #[TestDox('Format non-exceptional ordinal numeral 8')] 67 + public function testFormatNonExceptionalOrdinalNumeral8(): void 68 + { 69 + $this->assertSame( 70 + 'Maximiliano, you are the 8th customer we serve today. Thank you!', 71 + format('Maximiliano', 8) 72 + ); 73 + } 74 + 75 + /** uuid: 8267072d-be1f-4f70-b34a-76b7557a47b9 */ 76 + #[TestDox('Format exceptional ordinal numeral 1')] 77 + public function testFormatExceptionalOrdinalNumeral1(): void 78 + { 79 + $this->assertSame( 80 + 'Mary, you are the 1st customer we serve today. Thank you!', 81 + format('Mary', 1) 82 + ); 83 + } 84 + 85 + /** uuid: 4d8753cb-0364-4b29-84b8-4374a4fa2e3f */ 86 + #[TestDox('Format exceptional ordinal numeral 2')] 87 + public function testFormatExceptionalOrdinalNumeral2(): void 88 + { 89 + $this->assertSame( 90 + 'Haruto, you are the 2nd customer we serve today. Thank you!', 91 + format('Haruto', 2) 92 + ); 93 + } 94 + 95 + /** uuid: 8d44c223-3a7e-4f48-a0ca-78e67bf98aa7 */ 96 + #[TestDox('Format exceptional ordinal numeral 3')] 97 + public function testFormatExceptionalOrdinalNumeral3(): void 98 + { 99 + $this->assertSame( 100 + 'Henriette, you are the 3rd customer we serve today. Thank you!', 101 + format('Henriette', 3) 102 + ); 103 + } 104 + 105 + /** uuid: 6c4f6c88-b306-4f40-bc78-97cdd583c21a */ 106 + #[TestDox('Format smallest two digit non-exceptional ordinal numeral 10')] 107 + public function testFormatSmallestTwoDigitNonExceptionalOrdinalNumeral10(): void 108 + { 109 + $this->assertSame( 110 + 'Alvarez, you are the 10th customer we serve today. Thank you!', 111 + format('Alvarez', 10) 112 + ); 113 + } 114 + 115 + /** uuid: e257a43f-d2b1-457a-97df-25f0923fc62a */ 116 + #[TestDox('Format non-exceptional ordinal numeral 11')] 117 + public function testFormatNonExceptionalOrdinalNumeral11(): void 118 + { 119 + $this->assertSame( 120 + 'Jacqueline, you are the 11th customer we serve today. Thank you!', 121 + format('Jacqueline', 11) 122 + ); 123 + } 124 + 125 + /** uuid: bb1db695-4d64-457f-81b8-4f5a2107e3f4 */ 126 + #[TestDox('Format non-exceptional ordinal numeral 12')] 127 + public function testFormatNonExceptionalOrdinalNumeral12(): void 128 + { 129 + $this->assertSame( 130 + 'Juan, you are the 12th customer we serve today. Thank you!', 131 + format('Juan', 12) 132 + ); 133 + } 134 + 135 + /** uuid: 60a3187c-9403-4835-97de-4f10ebfd63e2 */ 136 + #[TestDox('Format non-exceptional ordinal numeral 13')] 137 + public function testFormatNonExceptionalOrdinalNumeral13(): void 138 + { 139 + $this->assertSame( 140 + 'Patricia, you are the 13th customer we serve today. Thank you!', 141 + format('Patricia', 13) 142 + ); 143 + } 144 + 145 + /** uuid: 2bdcebc5-c029-4874-b6cc-e9bec80d603a */ 146 + #[TestDox('Format exceptional ordinal numeral 21')] 147 + public function testFormatExceptionalOrdinalNumeral21(): void 148 + { 149 + $this->assertSame( 150 + 'Washi, you are the 21st customer we serve today. Thank you!', 151 + format('Washi', 21) 152 + ); 153 + } 154 + 155 + /** uuid: 74ee2317-0295-49d2-baf0-d56bcefa14e3 */ 156 + #[TestDox('Format exceptional ordinal numeral 62')] 157 + public function testFormatExceptionalOrdinalNumeral62(): void 158 + { 159 + $this->assertSame( 160 + 'Nayra, you are the 62nd customer we serve today. Thank you!', 161 + format('Nayra', 62) 162 + ); 163 + } 164 + 165 + /** uuid: b37c332d-7f68-40e3-8503-e43cbd67a0c4 */ 166 + #[TestDox('Format exceptional ordinal numeral 100')] 167 + public function testFormatExceptionalOrdinalNumeral100(): void 168 + { 169 + $this->assertSame( 170 + 'John, you are the 100th customer we serve today. Thank you!', 171 + format('John', 100) 172 + ); 173 + } 174 + 175 + /** uuid: 0375f250-ce92-4195-9555-00e28ccc4d99 */ 176 + #[TestDox('Format exceptional ordinal numeral 101')] 177 + public function testFormatExceptionalOrdinalNumeral101(): void 178 + { 179 + $this->assertSame( 180 + 'Zeinab, you are the 101st customer we serve today. Thank you!', 181 + format('Zeinab', 101) 182 + ); 183 + } 184 + 185 + /** uuid: 0d8a4974-9a8a-45a4-aca7-a9fb473c9836 */ 186 + #[TestDox('Format non-exceptional ordinal numeral 112')] 187 + public function testFormatNonExceptionalOrdinalNumeral112(): void 188 + { 189 + $this->assertSame( 190 + 'Knud, you are the 112th customer we serve today. Thank you!', 191 + format('Knud', 112) 192 + ); 193 + } 194 + 195 + /** uuid: 06b62efe-199e-4ce7-970d-4bf73945713f */ 196 + #[TestDox('Format exceptional ordinal numeral 123')] 197 + public function testFormatExceptionalOrdinalNumeral123(): void 198 + { 199 + $this->assertSame( 200 + 'Yma, you are the 123rd customer we serve today. Thank you!', 201 + format('Yma', 123) 202 + ); 203 + } 204 + }
+44
php/line-up/README.md
··· 1 + # Line up 2 + 3 + Welcome to Line up on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + 6 + ## Introduction 7 + 8 + Your friend Yaʻqūb works the counter at a deli in town, slicing, weighing, and wrapping orders for a line of hungry customers that gets longer every day. 9 + Waiting customers are starting to lose track of who is next, so he wants numbered tickets they can use to track the order in which they arrive. 10 + 11 + To make the customers feel special, he does not want the ticket to have only a number on it. 12 + They shall get a proper English sentence with their name and number on it. 13 + 14 + ## Instructions 15 + 16 + Given a name and a number, your task is to produce a sentence using that name and that number as an [ordinal numeral][ordinal-numeral]. 17 + Yaʻqūb expects to use numbers from 1 up to 999. 18 + 19 + Rules: 20 + 21 + - Numbers ending in 1 (unless ending in 11) → `"st"` 22 + - Numbers ending in 2 (unless ending in 12) → `"nd"` 23 + - Numbers ending in 3 (unless ending in 13) → `"rd"` 24 + - All other numbers → `"th"` 25 + 26 + Examples: 27 + 28 + - `"Mary", 1` → `"Mary, you are the 1st customer we serve today. Thank you!"` 29 + - `"John", 12` → `"John, you are the 12th customer we serve today. Thank you!"` 30 + - `"Dahir", 162` → `"Dahir, you are the 162nd customer we serve today. Thank you!"` 31 + 32 + [ordinal-numeral]: https://en.wikipedia.org/wiki/Ordinal_numeral 33 + 34 + ## Source 35 + 36 + ### Created by 37 + 38 + - @codedge 39 + - @neenjaw 40 + - @mk-mxp 41 + 42 + ### Based on 43 + 44 + mk-mxp, based on previous work from Exercism contributors codedge and neenjaw - https://forum.exercism.org/t/new-exercise-ordinal-numbers/19147
+23
php/lucky-numbers/LuckyNumbers.php
··· 1 + <?php 2 + 3 + class LuckyNumbers 4 + { 5 + public function sumUp(array $digitsOfNumber1, array $digitsOfNumber2): int 6 + { 7 + return (int) implode('', $digitsOfNumber1) + (int) implode('', $digitsOfNumber2); 8 + } 9 + 10 + public function isPalindrome(int $number): bool 11 + { 12 + return $number == implode('', array_reverse(str_split((string) $number))); 13 + } 14 + 15 + public function validate(string $input): string 16 + { 17 + return match (true) { 18 + $input == '' => 'Required field', 19 + (int) $input <= 0 => 'Must be a whole number larger than 0', 20 + default => '', 21 + }; 22 + } 23 + }
+156
php/lucky-numbers/LuckyNumbersTest.php
··· 1 + <?php 2 + 3 + use PHPUnit\Framework\TestCase; 4 + use PHPUnit\Framework\Attributes\DataProvider; 5 + use PHPUnit\Framework\Attributes\TestDox; 6 + 7 + class LuckyNumbersTest extends TestCase 8 + { 9 + public static function setUpBeforeClass(): void 10 + { 11 + require_once 'LuckyNumbers.php'; 12 + } 13 + 14 + /** 15 + * @task_id 1 16 + */ 17 + #[DataProvider('sumUpTestCases')] 18 + #[TestDox('Sums up $digitsOfNumber1 and $digitsOfNumber2 to $expected')] 19 + public function testSumUp( 20 + array $digitsOfNumber1, 21 + array $digitsOfNumber2, 22 + int $expected 23 + ): void { 24 + $class = new LuckyNumbers(); 25 + 26 + $actual = $class->sumUp($digitsOfNumber1, $digitsOfNumber2); 27 + 28 + $this->assertSame($expected, $actual); 29 + } 30 + 31 + public static function sumUpTestCases() 32 + { 33 + return [ 34 + 'both numbers same length 1' => [ [2], [7], 9 ], 35 + 'both numbers same length 2' => [ [2, 4], [5, 7], 81 ], 36 + 'both numbers same length 3' => [ [5, 3, 4], [3, 6, 2], 896 ], 37 + 'first shorter than second number' => [ [2, 4], [1, 5, 7], 181 ], 38 + 'first longer than second number' => [ [2, 2, 5], [5, 7], 282 ], 39 + 'handles overflow' => [ [9, 9, 9], [1, 0, 1], 1100 ], 40 + 'handles large numbers' => [ [9, 9, 9, 9, 9, 9], [1], 1000000 ], 41 + ]; 42 + } 43 + 44 + /** 45 + * @task_id 2 46 + */ 47 + #[DataProvider('isPalindromeTestCases')] 48 + #[TestDox('Detects palindromic number $number')] 49 + public function testIsPalindrome(int $number): void 50 + { 51 + $class = new LuckyNumbers(); 52 + 53 + $actual = $class->isPalindrome($number); 54 + 55 + $this->assertTrue($actual); 56 + } 57 + 58 + public static function isPalindromeTestCases() 59 + { 60 + return [ 61 + [ 0 ], 62 + [ 6 ], 63 + [ 33 ], 64 + [ 15651 ], 65 + [ 48911984 ], 66 + ]; 67 + } 68 + 69 + /** 70 + * @task_id 2 71 + */ 72 + #[DataProvider('isNoPalindromeTestCases')] 73 + #[TestDox('Detects non-palindromic number $number')] 74 + public function testIsNoPalindrome(int $number): void 75 + { 76 + $class = new LuckyNumbers(); 77 + 78 + $actual = $class->isPalindrome($number); 79 + 80 + $this->assertFalse($actual); 81 + } 82 + 83 + public static function isNoPalindromeTestCases() 84 + { 85 + return [ 86 + [ 12 ], 87 + [ 156512 ], 88 + [ 48921984 ], 89 + ]; 90 + } 91 + 92 + /** 93 + * @task_id 3 94 + */ 95 + #[TestDox('Error message for empty input')] 96 + public function testErrorMessageForEmptyInput(): void 97 + { 98 + $class = new LuckyNumbers(); 99 + 100 + $actual = $class->validate(''); 101 + 102 + $this->assertSame('Required field', $actual); 103 + } 104 + 105 + /** 106 + * @task_id 3 107 + */ 108 + #[DataProvider('invalidInputTestCases')] 109 + #[TestDox('Error message for invalid input $input')] 110 + public function testErrorMessageForInvalidInput( 111 + string $input 112 + ): void { 113 + $class = new LuckyNumbers(); 114 + 115 + $actual = $class->validate($input); 116 + 117 + $this->assertSame('Must be a whole number larger than 0', $actual); 118 + } 119 + 120 + public static function invalidInputTestCases() 121 + { 122 + return [ 123 + [ 'some text' ], 124 + [ 'f861' ], 125 + [ '-42' ], 126 + [ '0' ], 127 + ]; 128 + } 129 + 130 + /** 131 + * @task_id 3 132 + */ 133 + #[DataProvider('validInputTestCases')] 134 + #[TestDox('Error message for invalid input $input')] 135 + public function testErrorMessageForValidInput( 136 + string $input 137 + ): void { 138 + $class = new LuckyNumbers(); 139 + 140 + $actual = $class->validate($input); 141 + 142 + $this->assertSame('', $actual); 143 + } 144 + 145 + public static function validInputTestCases() 146 + { 147 + return [ 148 + [ '1' ], 149 + [ '123456789' ], 150 + [ ' 123 ' ], 151 + [ '5e3' ], 152 + [ '4.2E1' ], 153 + [ '00015-plus' ], 154 + ]; 155 + } 156 + }
+122
php/lucky-numbers/README.md
··· 1 + # Lucky Numbers 2 + 3 + Welcome to Lucky Numbers on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) 6 + 7 + ## Introduction 8 + 9 + ## Type Juggling 10 + 11 + Type juggling may also be known as type coercion. 12 + Type juggling is when two values of different types are coerced to the same type to perform an operation. 13 + 14 + ```php 15 + $baskets = 5 // int 16 + $apples_per_basket = "3" // string 17 + $baskets * $apples_per_basket 18 + # => 15 19 + ``` 20 + 21 + This can be done implicitly (see above example), or explicitly with `C`-style type casting: 22 + 23 + ```php 24 + $apples_per_basket = "3" // string 25 + $my_number = (int) $apples_per_basket // cast string to int 26 + # => 3 // int 27 + ``` 28 + 29 + Allowed types for manual type casting are: `bool`, `int`, `float`, `string`, `array`, `object`. 30 + Most commonly, type casting is used to change an integer (`int`) to a float (`float`) or vice-versa. 31 + 32 + ## Instructions 33 + 34 + Your friend Kojo is a big fan of numbers. 35 + He has a small website for playing around with numbers. 36 + Kojo is not that good at programming so he asked you for help. 37 + 38 + You will build two helper methods for new number games on Kojos' website and a third one to validate some input the user can enter. 39 + 40 + ## 1. Calculate the sum for the numbers on the slot machine 41 + 42 + One of the games on Kojos' website looks like a slot machine that shows two groups of wheels with digits on them. 43 + Each group of digits represents a number. 44 + For the game mechanics, Kojo needs to know the sum of those two numbers. 45 + 46 + Write a method `sumUp` that accepts two arrays as parameters. 47 + Each array consists of one or more digits between 0 and 9. 48 + The function should interpret each array as a number and return the sum of those two numbers. 49 + 50 + ```php 51 + <?php 52 + $lucky_numbers = new LuckyNumbers() 53 + 54 + $lucky_numbers->sumUp([1, 2, 3], [0, 7]); 55 + //=> 130 56 + 57 + // [1, 2, 3] represents 123 and [0, 7] represents 7. 58 + // 123 + 7 = 130 59 + ``` 60 + 61 + ## 2. Determine if a number is a palindrome 62 + 63 + Another game on the website is a little quiz called "Lucky Numbers". 64 + A user can enter a number and then sees whether the number belongs to some secret sequence or pattern. 65 + The sequence or pattern of the "lucky numbers" changes each month and each user only has a limited number of tries to guess it. 66 + 67 + This months' lucky numbers should be numbers that are palindromes. 68 + Palindromic numbers remain the same when the digits are reversed. 69 + 70 + Implement the new `isPalindrome` function that accepts an integer as a parameter. 71 + The function should return `true` if the number is a palindrome and `false` otherwise. 72 + The input number will always be a positive integer. 73 + 74 + ```php 75 + <?php 76 + $lucky_numbers = new LuckyNumbers() 77 + 78 + $lucky_numbers->isPalindrome(1441); 79 + //=> true 80 + 81 + $lucky_numbers->isPalindrome(123); 82 + //=> false 83 + ``` 84 + 85 + ## 3. Generate an error message for invalid user input 86 + 87 + In various places on the website, there are input fields where the users can enter numbers and click a button to trigger some action. 88 + Kojo wants to improve the user experience of his site. 89 + He wants to show an error message in case the user clicks the button but the field contains an invalid input value. 90 + 91 + Here is some more information on how the value of an input field is provided. 92 + 93 + - If the user types something into a field, the associated value is always a string even if the user only typed in numbers. 94 + - If the user types something but deletes it again, the variable will be an empty string. 95 + - Before the user even started typing, the variable will be an empty string. 96 + 97 + Write a function `validate` that accepts the user input as a parameter. 98 + If the user did not provide any input, `validate` should return `'Required field'`. 99 + If the input does not represent a positive non-zero whole number (according to the [PHP type casting rules][php-type-cast-int]), `'Must be a whole number larger than 0'` should be returned. 100 + In all other cases you can assume the input is valid, the return value should be an empty string then. 101 + 102 + ```php 103 + <?php 104 + $lucky_numbers = new LuckyNumbers() 105 + 106 + $lucky_numbers->validate('123'); 107 + // => '' 108 + 109 + $lucky_numbers->validate(''); 110 + // => 'Required field' 111 + 112 + $lucky_numbers->validate('abc'); 113 + // => 'Must be a whole number larger than 0' 114 + ``` 115 + 116 + [php-type-cast-int]: https://www.php.net/manual/en/language.types.integer.php#language.types.integer.casting.from-string 117 + 118 + ## Source 119 + 120 + ### Created by 121 + 122 + - @mk-mxp
+24
php/pizza-pi/PizzaPi.php
··· 1 + <?php 2 + 3 + class PizzaPi 4 + { 5 + public function calculateDoughRequirement($pizzas, $persons): int 6 + { 7 + return $pizzas * (($persons * 20) + 200); 8 + } 9 + 10 + public function calculateSauceRequirement(int $pizzas, int $sauceCanVolume): float|int 11 + { 12 + return $pizzas * 125 / $sauceCanVolume; 13 + } 14 + 15 + public function calculateCheeseCubeCoverage(int $cheeseDimension, int|float $cheeseTickness, int $pizzaDiameter): int 16 + { 17 + return floor(($cheeseDimension**3) / ($cheeseTickness * pi() * $pizzaDiameter)); 18 + } 19 + 20 + public function calculateLeftOverSlices(int $pizzas, int $friends): float|int 21 + { 22 + return ($pizzas * 8) % $friends; 23 + } 24 + }
+72
php/pizza-pi/PizzaPiTest.php
··· 1 + <?php 2 + 3 + use PHPUnit\Framework\TestCase; 4 + use PHPUnit\Framework\Attributes\TestDox; 5 + 6 + class PizzaPiTest extends TestCase 7 + { 8 + public static function setUpBeforeClass(): void 9 + { 10 + require_once 'PizzaPi.php'; 11 + } 12 + 13 + /** 14 + * @task_id 1 15 + */ 16 + #[TestDox('determine how much dough is required')] 17 + public function testCalculateDoughRequirement() 18 + { 19 + $pizza_pi = new PizzaPi(); 20 + $actual = $pizza_pi->calculateDoughRequirement(5, 7); 21 + $expected = 1700; 22 + $this->assertEquals($expected, $actual); 23 + } 24 + 25 + /** 26 + * @task_id 2 27 + */ 28 + #[TestDox('determine how many cans of sauce are required')] 29 + public function testCalculateSauceRequirement() 30 + { 31 + $pizza_pi = new PizzaPi(); 32 + $actual = $pizza_pi->calculateSauceRequirement(8, 250); 33 + $expected = 4; 34 + $this->assertEquals($expected, $actual); 35 + } 36 + 37 + /** 38 + * @task_id 3 39 + */ 40 + #[TestDox('determine how many pizzas a cube of cheese can cover')] 41 + public function testCalculateCheeseCoverage() 42 + { 43 + $pizza_pi = new PizzaPi(); 44 + $actual = $pizza_pi->calculateCheeseCubeCoverage(25, 0.5, 30); 45 + $expected = 331; 46 + $this->assertEquals($expected, $actual); 47 + } 48 + 49 + /** 50 + * @task_id 4 51 + */ 52 + #[TestDox('determine number of pieces remaining when evenly dividing')] 53 + public function testCalculateLeftOverSlicesWithoutLeftOver() 54 + { 55 + $pizza_pi = new PizzaPi(); 56 + $actual = $pizza_pi->calculateLeftOverSlices(2, 4); 57 + $expected = 0; 58 + $this->assertEquals($expected, $actual); 59 + } 60 + 61 + /** 62 + * @task_id 4 63 + */ 64 + #[TestDox('determine number of pieces remaining when not evenly dividing')] 65 + public function testCalculateLeftOverSlicesWithLeftOver() 66 + { 67 + $pizza_pi = new PizzaPi(); 68 + $actual = $pizza_pi->calculateLeftOverSlices(4, 3); 69 + $expected = 2; 70 + $this->assertEquals($expected, $actual); 71 + } 72 + }
+181
php/pizza-pi/README.md
··· 1 + # Pizza Pi 2 + 3 + Welcome to Pizza Pi on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) 6 + 7 + ## Introduction 8 + 9 + ## Integers 10 + 11 + Integers are whole numbers that belong to the set `{..., -2, -1, 0, 1, 2, ...}`. 12 + The maximum and minimum values of an integer is determined by the system PHP runs in. 13 + Typically for most modern systems, the integer is a 64-bit value -- meaning it can represent `-9_223_372_036_854_775_808` to `9_223_372_036_854_775_807` inclusive. 14 + 15 + ```php 16 + <?php 17 + 18 + $a = 1234; 19 + $a = 1_234_567; 20 + ``` 21 + 22 + You can write integer literal values in other than base 10 using a base prefix: 23 + 24 + ```php 25 + <?php 26 + 27 + $a = 0123; // octal number (equivalent to 83 decimal) 28 + $a = 0o123; // octal number (as of PHP 8.1.0) 29 + $a = 0x1A; // hexadecimal number (equivalent to 26 decimal) 30 + $a = 0b11111111; // binary number (equivalent to 255 decimal) 31 + ``` 32 + 33 + > Taken from PHP's [documentation][syntax] 34 + 35 + ## Floating Point Numbers 36 + 37 + Floating point numbers are used in PHP to represent a subset of real numbers. 38 + They start with one or more digits separated by a decimal separator. 39 + 40 + ```php 41 + <?php 42 + 43 + $a = 1.234; 44 + $b = 1.2e3; 45 + $c = 7E-10; 46 + $d = 1_234.567; // as of PHP 7.4.0 47 + ``` 48 + 49 + ### Common pitfalls 50 + 51 + Not all numbers and arithmetic operations can be performed with floating point numbers. 52 + Using floating point numbers to represent dollars and cents can lead to rounding errors. 53 + 54 + ```php 55 + <?php 56 + 57 + $result = 0.1 + 0.2; // => 0.30000000000000004 58 + ``` 59 + 60 + ## Arithmetic Operators 61 + 62 + PHP provides a number of operators for performing arithmetic operations. PHP follows the standard mathematical order of operations for its arithmetic operators. The operators that are provided by PHP are: 63 + 64 + * Identity (+) 65 + * Negation (-) 66 + * Addition (+) 67 + * Subtraction (-) 68 + * Multiplication (*) 69 + * Division (/) 70 + * Modulo (%) 71 + * Exponentiation (**) 72 + 73 + ```php 74 + $moles = +'10'; 75 + $aLotOfMoles = 6.022 * 10**23 * $moles; 76 + ``` 77 + 78 + [syntax]: https://www.php.net/manual/en/language.types.integer.php#language.types.integer.syntax 79 + 80 + ## Instructions 81 + 82 + Lilly loves cooking. 83 + She wants to throw a pizza party with her friends. 84 + To make the most of her ingredients, she needs to know how much of each ingredient she requires before starting. 85 + 86 + To solve this, Lilly has drafted a program to automate some of the planning but needs your help finishing it. 87 + Will you help Lilly throw the proportionally perfect pizza party? 88 + 89 + ## 1. A Dough Ratio 90 + 91 + Lilly is a fan of thin, crispy pizzas with a thinner crust. 92 + The dough needed for the middle is a minimum 200g, but every person it serves requires another 20g of dough. 93 + 94 + `grams = pizzas * ((persons * 20) + 200)` 95 + 96 + Lilly needs a function that: 97 + 98 + - Takes the number of pizzas 99 + - The number of persons each pizza will serve 100 + - And returns the dough needed to the nearest gram. 101 + 102 + For example, to make 4 pizzas that feed 8 people: 103 + 104 + ```php 105 + <?php 106 + 107 + $pizza_pi = new PizzaPi(); 108 + $pizza_pi->calculateDoughRequirement(4, 8); 109 + // => 1440 110 + ``` 111 + 112 + ## 2. A Splash of Sauce 113 + 114 + Lilly is meticulous when applying her sauce, but the size of her pizzas can be inconsistent. 115 + From her experience, she knows that it takes 125mL of sauce per pizza. 116 + Lilly needs a function that calculates how many cans of sauce to buy. 117 + 118 + `cans of sauce = pizzas * sauce per pizza / sauce can volume` 119 + 120 + For example, given Lilly needs to make 8 pizzas, and each can is 250mL: 121 + 122 + ```php 123 + <?php 124 + 125 + $pizza_pi = new PizzaPi(); 126 + $pizza_pi->calculateSauceRequirement(8, 250); 127 + // => 4 128 + ``` 129 + 130 + ## 3. Some Cheese, Please 131 + 132 + Cheese comes in perfect cubes and is sold by size. 133 + 134 + The formula Lily uses is not the formula for the area of the pizza since she does _not_ want the cheese to cover the entire area. 135 + She decided to use the following formula to determine how many pizzas of some diameter (`diameter`) can be made from a cheese cube of some side-length (`cheese_dimension`): 136 + 137 + `pizzas = (cheese_dimension³) / (thickness * PI * diameter)` 138 + 139 + Create a function that: 140 + 141 + - Takes a side-length dimension of a cheese cube 142 + - Takes the desired thickness of the cheese layer 143 + - Takes the diameter of the pizza 144 + - And uses Lilly's formula to return the number of pizzas that can be made while rounding down. 145 + 146 + For example, given a 25x25x25cm cheese cube, 0.5cm thick cheese layer and pizzas 30cm in diameter: 147 + 148 + ```php 149 + <?php 150 + 151 + $pizza_pi = new PizzaPi(); 152 + $pizza_pi->calculateCheeseCubeCoverage(25, 0.5, 30); 153 + // => 331 154 + ``` 155 + 156 + ## 4. A Fair Share 157 + 158 + Finally, Lilly wants her pizzas to divide into 8 slices each and distributed evenly among her friends. 159 + 160 + Create a function that: 161 + 162 + - Takes a number of pizzas and number of friends 163 + - and returns the number of slices that will be left over if each person takes an equal number of slices. 164 + 165 + For example: 166 + 167 + ```php 168 + <?php 169 + 170 + $pizza_pi = new PizzaPi(); 171 + $pizza_pi->calculateLeftOverSlices(2, 4); 172 + // => 0 173 + $pizza_pi->calculateLeftOverSlices(4, 3); 174 + // => 2 175 + ``` 176 + 177 + ## Source 178 + 179 + ### Created by 180 + 181 + - @neenjaw
+48
php/resistor-color-duo/README.md
··· 1 + # Resistor Color Duo 2 + 3 + Welcome to Resistor Color Duo on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + 6 + ## Instructions 7 + 8 + If you want to build something using a Raspberry Pi, you'll probably use _resistors_. 9 + For this exercise, you need to know two things about them: 10 + 11 + - Each resistor has a resistance value. 12 + - Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read. 13 + 14 + To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values. 15 + Each band has a position and a numeric value. 16 + 17 + The first 2 bands of a resistor have a simple encoding scheme: each color maps to a single number. 18 + For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15. 19 + 20 + In this exercise you are going to create a helpful program so that you don't have to remember the values of the bands. 21 + The program will take color names as input and output a two digit number, even if the input is more than two colors! 22 + 23 + The band colors are encoded as follows: 24 + 25 + - black: 0 26 + - brown: 1 27 + - red: 2 28 + - orange: 3 29 + - yellow: 4 30 + - green: 5 31 + - blue: 6 32 + - violet: 7 33 + - grey: 8 34 + - white: 9 35 + 36 + From the example above: 37 + brown-green should return 15, and 38 + brown-green-violet should return 15 too, ignoring the third color. 39 + 40 + ## Source 41 + 42 + ### Created by 43 + 44 + - @MichaelBunker 45 + 46 + ### Based on 47 + 48 + Maud de Vries, Erik Schierboom - https://github.com/exercism/problem-specifications/issues/1464
+24
php/resistor-color-duo/ResistorColorDuo.php
··· 1 + <?php 2 + 3 + declare(strict_types=1); 4 + 5 + class ResistorColorDuo 6 + { 7 + public function getColorsValue(array $colors): int 8 + { 9 + $bandColors = [ 10 + "black" => 0, 11 + "brown" => 1, 12 + "red" => 2, 13 + "orange" => 3, 14 + "yellow" => 4, 15 + "green" => 5, 16 + "blue" => 6, 17 + "violet" => 7, 18 + "grey" => 8, 19 + "white" => 9, 20 + ]; 21 + 22 + return (int) "{$bandColors[$colors[0]]}{$bandColors[$colors[1]]}"; 23 + } 24 + }
+77
php/resistor-color-duo/ResistorColorDuoTest.php
··· 1 + <?php 2 + 3 + /* 4 + * By adding type hints and enabling strict type checking, code can become 5 + * easier to read, self-documenting and reduce the number of potential bugs. 6 + * By default, type declarations are non-strict, which means they will attempt 7 + * to change the original type to match the type specified by the 8 + * type-declaration. 9 + * 10 + * In other words, if you pass a string to a function requiring a float, 11 + * it will attempt to convert the string value to a float. 12 + * 13 + * To enable strict mode, a single declare directive must be placed at the top 14 + * of the file. 15 + * This means that the strictness of typing is configured on a per-file basis. 16 + * This directive not only affects the type declarations of parameters, but also 17 + * a function's return type. 18 + * 19 + * For more info review the Concept on strict type checking in the PHP track 20 + * <link>. 21 + * 22 + * To disable strict typing, comment out the directive below. 23 + */ 24 + 25 + declare(strict_types=1); 26 + 27 + use PHPUnit\Framework\TestCase; 28 + 29 + class ResistorColorDuoTest extends TestCase 30 + { 31 + private ResistorColorDuo $resistor; 32 + 33 + public static function setUpBeforeClass(): void 34 + { 35 + require_once 'ResistorColorDuo.php'; 36 + } 37 + 38 + public function setUp(): void 39 + { 40 + $this->resistor = new ResistorColorDuo(); 41 + } 42 + 43 + public function testBrownAndBlack(): void 44 + { 45 + $this->assertEquals(10, $this->resistor->getColorsValue(['brown', 'black'])); 46 + } 47 + 48 + public function testBlueAndGrey(): void 49 + { 50 + $this->assertEquals(68, $this->resistor->getColorsValue(['blue', 'grey'])); 51 + } 52 + 53 + public function testYellowAndViolet(): void 54 + { 55 + $this->assertEquals(47, $this->resistor->getColorsValue(['yellow', 'violet'])); 56 + } 57 + 58 + public function testWhiteAndRed(): void 59 + { 60 + $this->assertEquals(92, $this->resistor->getColorsValue(['white', 'red'])); 61 + } 62 + 63 + public function testOrangeAndOrange(): void 64 + { 65 + $this->assertEquals(33, $this->resistor->getColorsValue(['orange', 'orange'])); 66 + } 67 + 68 + public function testAdditionalColorsAreIgnored(): void 69 + { 70 + $this->assertEquals(51, $this->resistor->getColorsValue(['green', 'brown', 'orange'])); 71 + } 72 + 73 + public function testBlackAndBrownSingleDigit(): void 74 + { 75 + $this->assertEquals(1, $this->resistor->getColorsValue(['black', 'brown'])); 76 + } 77 + }
+58
php/resistor-color/README.md
··· 1 + # Resistor Color 2 + 3 + Welcome to Resistor Color on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + 6 + ## Instructions 7 + 8 + If you want to build something using a Raspberry Pi, you'll probably use _resistors_. 9 + For this exercise, you need to know two things about them: 10 + 11 + - Each resistor has a resistance value. 12 + - Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read. 13 + 14 + To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values. 15 + Each band has a position and a numeric value. 16 + 17 + The first 2 bands of a resistor have a simple encoding scheme: each color maps to a single number. 18 + 19 + In this exercise you are going to create a helpful program so that you don't have to remember the values of the bands. 20 + 21 + These colors are encoded as follows: 22 + 23 + - black: 0 24 + - brown: 1 25 + - red: 2 26 + - orange: 3 27 + - yellow: 4 28 + - green: 5 29 + - blue: 6 30 + - violet: 7 31 + - grey: 8 32 + - white: 9 33 + 34 + The goal of this exercise is to create a way: 35 + 36 + - to look up the numerical value associated with a particular color band 37 + - to list the different band colors 38 + 39 + Mnemonics map the colors to the numbers, that, when stored as an array, happen to map to their index in the array: 40 + Better Be Right Or Your Great Big Values Go Wrong. 41 + 42 + More information on the color encoding of resistors can be found in the [Electronic color code Wikipedia article][e-color-code]. 43 + 44 + [e-color-code]: https://en.wikipedia.org/wiki/Electronic_color_code 45 + 46 + ## Source 47 + 48 + ### Created by 49 + 50 + - @camilopayan 51 + 52 + ### Contributed to by 53 + 54 + - @MichaelBunker 55 + 56 + ### Based on 57 + 58 + Maud de Vries, Erik Schierboom - https://github.com/exercism/problem-specifications/issues/1458
+24
php/resistor-color/ResistorColor.php
··· 1 + <?php 2 + 3 + declare(strict_types=1); 4 + 5 + function getAllColors(): array 6 + { 7 + return [ 8 + 0 => "black", 9 + 1 => "brown", 10 + 2 => "red", 11 + 3 => "orange", 12 + 4 => "yellow", 13 + 5 => "green", 14 + 6 => "blue", 15 + 7 => "violet", 16 + 8 => "grey", 17 + 9 => "white", 18 + ]; 19 + } 20 + 21 + function colorCode(string $color): int 22 + { 23 + return array_search($color, getAllColors()); 24 + }
+66
php/resistor-color/ResistorColorTest.php
··· 1 + <?php 2 + 3 + /* 4 + * By adding type hints and enabling strict type checking, code can become 5 + * easier to read, self-documenting and reduce the number of potential bugs. 6 + * By default, type declarations are non-strict, which means they will attempt 7 + * to change the original type to match the type specified by the 8 + * type-declaration. 9 + * 10 + * In other words, if you pass a string to a function requiring a float, 11 + * it will attempt to convert the string value to a float. 12 + * 13 + * To enable strict mode, a single declare directive must be placed at the top 14 + * of the file. 15 + * This means that the strictness of typing is configured on a per-file basis. 16 + * This directive not only affects the type declarations of parameters, but also 17 + * a function's return type. 18 + * 19 + * For more info review the Concept on strict type checking in the PHP track 20 + * <link>. 21 + * 22 + * To disable strict typing, comment out the directive below. 23 + */ 24 + 25 + declare(strict_types=1); 26 + 27 + use PHPUnit\Framework\TestCase; 28 + 29 + class ResistorColorTest extends TestCase 30 + { 31 + public static function setUpBeforeClass(): void 32 + { 33 + require_once 'ResistorColor.php'; 34 + } 35 + 36 + public function testColors(): void 37 + { 38 + $this->assertEquals([ 39 + "black", 40 + "brown", 41 + "red", 42 + "orange", 43 + "yellow", 44 + "green", 45 + "blue", 46 + "violet", 47 + "grey", 48 + "white" 49 + ], getAllColors()); 50 + } 51 + 52 + public function testBlackColorCode(): void 53 + { 54 + $this->assertEquals(0, colorCode("black")); 55 + } 56 + 57 + public function testOrangeColorCode(): void 58 + { 59 + $this->assertEquals(3, colorCode("orange")); 60 + } 61 + 62 + public function testWhiteColorCode(): void 63 + { 64 + $this->assertEquals(9, colorCode("white")); 65 + } 66 + }
+30
php/reverse-string/README.md
··· 1 + # Reverse String 2 + 3 + Welcome to Reverse String on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + 6 + ## Introduction 7 + 8 + Reversing strings (reading them from right to left, rather than from left to right) is a surprisingly common task in programming. 9 + 10 + For example, in bioinformatics, reversing the sequence of DNA or RNA strings is often important for various analyses, such as finding complementary strands or identifying palindromic sequences that have biological significance. 11 + 12 + ## Instructions 13 + 14 + Your task is to reverse a given string. 15 + 16 + Some examples: 17 + 18 + - Turn `"stressed"` into `"desserts"`. 19 + - Turn `"strops"` into `"sports"`. 20 + - Turn `"racecar"` into `"racecar"`. 21 + 22 + ## Source 23 + 24 + ### Created by 25 + 26 + - @MichaelBunker 27 + 28 + ### Based on 29 + 30 + Introductory challenge to reverse an input string - https://www.freecodecamp.org/news/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb
+8
php/reverse-string/ReverseString.php
··· 1 + <?php 2 + 3 + declare(strict_types=1); 4 + 5 + function reverseString(string $text): string 6 + { 7 + return implode("", array_reverse(str_split($text))); 8 + }
+66
php/reverse-string/ReverseStringTest.php
··· 1 + <?php 2 + 3 + declare(strict_types=1); 4 + 5 + use PHPUnit\Framework\TestCase; 6 + use PHPUnit\Framework\Attributes\TestDox; 7 + 8 + class ReverseStringTest extends TestCase 9 + { 10 + public static function setUpBeforeClass(): void 11 + { 12 + require_once 'ReverseString.php'; 13 + } 14 + 15 + /** 16 + * uuid c3b7d806-dced-49ee-8543-933fd1719b1c 17 + */ 18 + #[TestDox('an empty string')] 19 + public function testEmptyString(): void 20 + { 21 + $this->assertEquals("", reverseString("")); 22 + } 23 + 24 + /** uuid 01ebf55b-bebb-414e-9dec-06f7bb0bee3c */ 25 + #[TestDox('a word')] 26 + public function testWord(): void 27 + { 28 + $this->assertEquals("tobor", reverseString("robot")); 29 + } 30 + 31 + /** 32 + * uuid 0f7c07e4-efd1-4aaa-a07a-90b49ce0b746 33 + */ 34 + #[TestDox('a capitalized word')] 35 + public function testCapitalizedWord(): void 36 + { 37 + $this->assertEquals("nemaR", reverseString("Ramen")); 38 + } 39 + 40 + /** 41 + * uuid 71854b9c-f200-4469-9f5c-1e8e5eff5614 42 + */ 43 + #[TestDox('a sentence with punctuation')] 44 + public function testSentenceWithPunctuation(): void 45 + { 46 + $this->assertEquals("!yrgnuh m'I", reverseString("I'm hungry!")); 47 + } 48 + 49 + /** 50 + * uuid 1f8ed2f3-56f3-459b-8f3e-6d8d654a1f6c 51 + */ 52 + #[TestDox('a palindrome')] 53 + public function testPalindrome(): void 54 + { 55 + $this->assertEquals("racecar", reverseString("racecar")); 56 + } 57 + 58 + /** 59 + * uuid b9e7dec1-c6df-40bd-9fa3-cd7ded010c4c 60 + */ 61 + #[TestDox('an even-sized word')] 62 + public function testEvenSizedWord(): void 63 + { 64 + $this->assertEquals("reward", reverseString("drawer")); 65 + } 66 + }
+46
php/sweethearts/HighSchoolSweetheart.php
··· 1 + <?php 2 + 3 + class HighSchoolSweetheart 4 + { 5 + public function firstLetter(string $name): string 6 + { 7 + return substr(trim($name), 0, 1); 8 + } 9 + 10 + public function initial(string $name): string 11 + { 12 + return strtoupper($this->firstLetter($name)) . "."; 13 + } 14 + 15 + public function initials(string $name): string 16 + { 17 + $result = explode(' ', $name); 18 + for ($i = 0; $i < count($result); $i++) { 19 + $result[$i] = $this->initial($result[$i]); 20 + } 21 + 22 + return implode(' ', $result); 23 + } 24 + 25 + public function pair(string $sweetheart_a, string $sweetheart_b): string 26 + { 27 + $a = $this->initials($sweetheart_a); 28 + $b = $this->initials($sweetheart_b); 29 + return <<<EOD 30 + ****** ****** 31 + ** ** ** ** 32 + ** ** ** ** 33 + ** * ** 34 + ** ** 35 + ** $a + $b ** 36 + ** ** 37 + ** ** 38 + ** ** 39 + ** ** 40 + ** ** 41 + ** ** 42 + *** 43 + * 44 + EOD; 45 + } 46 + }
+102
php/sweethearts/HighSchoolSweetheartTest.php
··· 1 + <?php 2 + 3 + use PHPUnit\Framework\TestCase; 4 + use PHPUnit\Framework\Attributes\TestDox; 5 + 6 + class HighSchoolSweetheartTest extends TestCase 7 + { 8 + public static function setUpBeforeClass(): void 9 + { 10 + require_once 'HighSchoolSweetheart.php'; 11 + } 12 + 13 + /** 14 + * @task_id 1 15 + */ 16 + #[TestDox('gets the first letter from a string')] 17 + public function testFirstLetter() 18 + { 19 + $sweetheart = new HighSchoolSweetheart(); 20 + $this->assertEquals('J', $sweetheart->firstLetter('Jane')); 21 + } 22 + 23 + /** 24 + * @task_id 1 25 + */ 26 + #[TestDox("getting the first letter doesn't change the case")] 27 + public function testFirstLetterDoesNotChangeCase() 28 + { 29 + $sweetheart = new HighSchoolSweetheart(); 30 + $this->assertEquals('j', $sweetheart->firstLetter('jane')); 31 + } 32 + 33 + /** 34 + * @task_id 1 35 + */ 36 + #[TestDox('getting the first letter removes whitespace from the name')] 37 + public function testFirstLetterRemovesWhitespace() 38 + { 39 + $sweetheart = new HighSchoolSweetheart(); 40 + $this->assertEquals('J', $sweetheart->firstLetter(' Jane')); 41 + } 42 + 43 + /** 44 + * @task_id 2 45 + */ 46 + #[TestDox('gets the first letter and appends a dot')] 47 + public function testCreatesInitial() 48 + { 49 + $sweetheart = new HighSchoolSweetheart(); 50 + $this->assertEquals('B.', $sweetheart->initial('Betty')); 51 + } 52 + 53 + /** 54 + * @task_id 2 55 + */ 56 + #[TestDox('creates an uppercase initial')] 57 + public function testCreatesUppercaseInitial() 58 + { 59 + $sweetheart = new HighSchoolSweetheart(); 60 + $this->assertEquals('J.', $sweetheart->initial('jim')); 61 + } 62 + 63 + /** 64 + * @task_id 3 65 + */ 66 + #[TestDox('creates a set of initials')] 67 + public function testCreatesInitials() 68 + { 69 + $sweetheart = new HighSchoolSweetheart(); 70 + $this->assertEquals('L. M.', $sweetheart->initials('Linda Miller')); 71 + } 72 + 73 + /** 74 + * @task_id 4 75 + */ 76 + #[TestDox('creates a set of initials, wrapped in a heart')] 77 + public function testPair() 78 + { 79 + $sweetheart = new HighSchoolSweetheart(); 80 + $expected = <<<EXPECTED_HEART 81 + ****** ****** 82 + ** ** ** ** 83 + ** ** ** ** 84 + ** * ** 85 + ** ** 86 + ** A. B. + C. D. ** 87 + ** ** 88 + ** ** 89 + ** ** 90 + ** ** 91 + ** ** 92 + ** ** 93 + *** 94 + * 95 + EXPECTED_HEART; 96 + 97 + $this->assertEquals( 98 + $expected, 99 + $sweetheart->pair('Avery Bryant', 'Charlie Dixon') 100 + ); 101 + } 102 + }
+143
php/sweethearts/README.md
··· 1 + # Highschool Sweethearts 2 + 3 + Welcome to Highschool Sweethearts on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) 6 + 7 + ## Introduction 8 + 9 + ## Strings 10 + 11 + Strings are a series of characters, surrounded by quotes. 12 + Both single and double quotes are supported. 13 + 14 + ```php 15 + <?php 16 + 17 + $where = "Spain"; 18 + $what = 'rain'; 19 + ``` 20 + 21 + ### String variable expansion 22 + 23 + In double quoted strings, if a variable is referenced while it is in scope, the value is inserted into the string. 24 + 25 + ```php 26 + <?php 27 + 28 + $answer = 42; 29 + $expanded = "The answer to life, the universe, and everything is $answer"; 30 + ``` 31 + 32 + ### Character Encoding Support 33 + 34 + Historically, string functions in PHP only supported ASCII characters and did not support modern Unicode encodings. 35 + ASCII characters are each 1 byte, whereas Unicode characters may be 1-4 bytes in length. 36 + If needing to manipulate Unicode strings safely, refer to the [multibyte versions][multi-byte-fns] of the historic functions. 37 + 38 + ```php 39 + <?php 40 + 41 + $byte_length = strlen('😃'); // => 4 42 + $string_length = mb_strlen('😃'); // => 1 43 + ``` 44 + 45 + [multi-byte-fns]: https://www.php.net/manual/en/ref.mbstring.php 46 + 47 + ## Instructions 48 + 49 + In this exercise, you are going to help high school sweethearts profess their love on social media by generating an ASCII heart with their initials: 50 + 51 + ``` 52 + ****** ****** 53 + ** ** ** ** 54 + ** ** ** ** 55 + ** * ** 56 + ** ** 57 + ** J. K. + M. B. ** 58 + ** ** 59 + ** ** 60 + ** ** 61 + ** ** 62 + ** ** 63 + ** ** 64 + *** 65 + * 66 + ``` 67 + 68 + ## 1. Get the name's first letter 69 + 70 + Implement the `HighSchoolSweetheart::firstLetter` function. 71 + It should take a name and return its first letter. 72 + It should clean up any unnecessary whitespace from the name. 73 + 74 + ```php 75 + <?php 76 + 77 + $sweetheart = new HighSchoolSweetheart(); 78 + $sweetheart->firstLetter("Jane"); 79 + # => "J" 80 + ``` 81 + 82 + ## 2. Format the first letter as an initial 83 + 84 + Implement the `HighSchoolSweetheart::initial` function. 85 + It should take a name and return its first letter, uppercase, followed by a dot. 86 + Make sure to reuse `HighSchoolSweetheart::first_letter` that you defined in the previous step. 87 + 88 + ```php 89 + <?php 90 + 91 + $sweetheart = new HighSchoolSweetheart(); 92 + $sweetheart->initial("jane"); 93 + # => "J." 94 + ``` 95 + 96 + ## 3. Split the full name into the first name and the last name 97 + 98 + Implement the `HighSchoolSweetheart::initials` function. 99 + It should take a full name, consisting of a first name and a last name separated by a space, and return the initials. 100 + Make sure to reuse `HighSchoolSweetheart::initial` that you defined in the previous step. 101 + 102 + ```php 103 + <?php 104 + 105 + $sweetheart = new HighSchoolSweetheart(); 106 + $sweetheart->initials("Jane Doe"); 107 + # => "J. D." 108 + ``` 109 + 110 + ## 4. Put the initials inside of the heart 111 + 112 + Implement the `HighSchoolSweetheart::pair` function. 113 + It should take two full names and return the initials inside an ASCII heart. 114 + Make sure to reuse `HighSchoolSweetheart::initials` that you defined in the previous step. 115 + 116 + ```php 117 + <?php 118 + 119 + $sweetheart = new HighSchoolSweetheart(); 120 + $sweetheart->pair("Blake Miller", "Riley Lewis") 121 + # => """ 122 + # ****** ****** 123 + # ** ** ** ** 124 + # ** ** ** ** 125 + # ** * ** 126 + # ** ** 127 + # ** B. M. + R. L. ** 128 + # ** ** 129 + # ** ** 130 + # ** ** 131 + # ** ** 132 + # ** ** 133 + # ** ** 134 + # *** 135 + # * 136 + # """ 137 + ``` 138 + 139 + ## Source 140 + 141 + ### Created by 142 + 143 + - @neenjaw
+13
php/windowing-system/Position.php
··· 1 + <?php 2 + 3 + class Position 4 + { 5 + public $y; 6 + public $x; 7 + 8 + public function __construct(int $y, int $x) 9 + { 10 + $this->y = $y; 11 + $this->x = $x; 12 + } 13 + }
+30
php/windowing-system/ProgramWindow.php
··· 1 + <?php 2 + 3 + class ProgramWindow 4 + { 5 + public $y; 6 + public $x; 7 + 8 + public $height; 9 + public $width; 10 + 11 + public function __construct() 12 + { 13 + $this->y = 0; 14 + $this->x = 0; 15 + $this->height = 600; 16 + $this->width = 800; 17 + } 18 + 19 + public function resize(Size $size): void 20 + { 21 + $this->height = $size->height; 22 + $this->width = $size->width; 23 + } 24 + 25 + public function move(Position $position): void 26 + { 27 + $this->y = $position->y; 28 + $this->x = $position->x; 29 + } 30 + }
+169
php/windowing-system/ProgramWindowTest.php
··· 1 + <?php 2 + 3 + use PHPUnit\Framework\TestCase; 4 + use PHPUnit\Framework\Attributes\TestDox; 5 + 6 + class ProgramWindowTest extends TestCase 7 + { 8 + public static function setUpBeforeClass(): void 9 + { 10 + require_once 'ProgramWindow.php'; 11 + require_once 'Size.php'; 12 + require_once 'Position.php'; 13 + } 14 + 15 + /** 16 + * @task_id 1 17 + */ 18 + #[TestDox('assert ProgramWindow has a $y property')] 19 + public function testHasPropertyY() 20 + { 21 + $reflector = new ReflectionClass(ProgramWindow::class); 22 + $this->assertHasProperty($reflector, 'y', [ 23 + 'has_type' => false, 24 + 'has_default' => false 25 + ]); 26 + } 27 + 28 + /** 29 + * @task_id 1 30 + */ 31 + #[TestDox('assert ProgramWindow has a $x property')] 32 + public function testHasPropertyX() 33 + { 34 + $reflector = new ReflectionClass(ProgramWindow::class); 35 + $this->assertHasProperty($reflector, 'x', [ 36 + 'has_type' => false, 37 + 'has_default' => false 38 + ]); 39 + } 40 + 41 + /** 42 + * @task_id 1 43 + */ 44 + #[TestDox('assert ProgramWindow has a $height property')] 45 + public function testHasPropertyHeight() 46 + { 47 + $reflector = new ReflectionClass(ProgramWindow::class); 48 + $this->assertHasProperty($reflector, 'height', [ 49 + 'has_type' => false, 50 + 'has_default' => false 51 + ]); 52 + } 53 + 54 + /** 55 + * @task_id 1 56 + */ 57 + #[TestDox('assert ProgramWindow has a $width property')] 58 + public function testHasPropertyWidth() 59 + { 60 + $reflector = new ReflectionClass(ProgramWindow::class); 61 + $this->assertHasProperty($reflector, 'width', [ 62 + 'has_type' => false, 63 + 'has_default' => false 64 + ]); 65 + } 66 + 67 + /** 68 + * @task_id 2 69 + */ 70 + #[TestDox('assert ProgramWindow has a constructor initial values')] 71 + public function testHasConstructorSettingInitialValues() 72 + { 73 + $window = new ProgramWindow(); 74 + $this->assertEquals(0, $window->y); 75 + $this->assertEquals(0, $window->x); 76 + $this->assertEquals(600, $window->height); 77 + $this->assertEquals(800, $window->width); 78 + } 79 + 80 + /** 81 + * @task_id 3 82 + */ 83 + #[TestDox('assert Position class exists, with constructor, properties')] 84 + public function testSizeHasConstructorSettingInitialValues() 85 + { 86 + $size = new Size(300, 700); 87 + $this->assertEquals(300, $size->height); 88 + $this->assertEquals(700, $size->width); 89 + } 90 + 91 + /** 92 + * @task_id 3 93 + */ 94 + #[TestDox('assert ProgramWindow::resize function exists')] 95 + public function testProgramWindowResize() 96 + { 97 + $window = new ProgramWindow(); 98 + $size = new Size(430, 2135); 99 + $window->resize($size); 100 + $this->assertEquals(430, $window->height); 101 + $this->assertEquals(2135, $window->width); 102 + } 103 + 104 + /** 105 + * @task_id 4 106 + */ 107 + #[TestDox('assert Position class exists, with constructor, properties')] 108 + public function testPositionHasConstructorSettingInitialValues() 109 + { 110 + $position = new Position(30, 70); 111 + $this->assertEquals(30, $position->y); 112 + $this->assertEquals(70, $position->x); 113 + } 114 + 115 + /** 116 + * @task_id 4 117 + */ 118 + #[TestDox('assert ProgramWindow::move function exists')] 119 + public function testProgramWindowMove() 120 + { 121 + $window = new ProgramWindow(); 122 + $position = new Position(40, 235); 123 + $window->move($position); 124 + $this->assertEquals(40, $window->y); 125 + $this->assertEquals(235, $window->x); 126 + } 127 + 128 + private function assertHasProperty( 129 + ReflectionClass $class, 130 + string $name, 131 + array $assertions = [] 132 + ) { 133 + $assertions = array_merge([ 134 + 'has_type' => false, 135 + 'has_default_value' => false 136 + ], $assertions); 137 + 138 + try { 139 + $property = $class->getProperty($name); 140 + } catch (ReflectionException) { 141 + $this->fail( 142 + "Property '$name' missing from class '{$class->getName()}'" 143 + ); 144 + }; 145 + 146 + if ($assertions['has_type']) { 147 + $this->assertTrue($property->hasType()); 148 + } else { 149 + $this->assertFalse( 150 + $property->hasType(), 151 + "Property '$name' should not have a type declared" 152 + ); 153 + } 154 + 155 + if ($assertions['has_default_value']) { 156 + $this->assertTrue($property->hasDefaultValue()); 157 + } else { 158 + if ($assertions['has_type'] === false) { 159 + $this->assertTrue($property->hasDefaultValue()); 160 + $this->assertNull($property->getDefaultValue()); 161 + } else { 162 + $this->assertFalse( 163 + $property->hasDefaultValue(), 164 + "Property '$name' should not have a default value declared" 165 + ); 166 + } 167 + } 168 + } 169 + }
+170
php/windowing-system/README.md
··· 1 + # Windowing System 2 + 3 + Welcome to Windowing System on Exercism's PHP Track. 4 + If you need help running the tests or submitting your code, check out `HELP.md`. 5 + If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) 6 + 7 + ## Introduction 8 + 9 + ## Classes 10 + 11 + Classes are a unit of code organization in PHP. 12 + Code represents the behavior of an item or the behavior of a process can be grouped together as a class. 13 + Functions that are assigned to a class are called `methods` by convention. 14 + 15 + ```php 16 + <?php 17 + 18 + class Door 19 + { 20 + function lock() 21 + { 22 + // ... 23 + } 24 + 25 + function unlock() 26 + { 27 + // ... 28 + } 29 + } 30 + ``` 31 + 32 + Classes are instantiated with the `new` keyword, this allocates memory for the class instance and calls the classes constructor method. 33 + Instantiated classes may be assigned to a variable. 34 + To invoke the methods of a class instance, we can use the `->` access operator. 35 + 36 + ```php 37 + <?php 38 + 39 + $a_door = new Door(); 40 + $a_door->lock(); 41 + $a_door->unlock(); 42 + ``` 43 + 44 + An instantiated class may reference its own instance using the special `$this` variable. 45 + Classes may also have properties, these act as variables that are tied to the instance of the class object. 46 + These properties may be referenced internally or externally. 47 + 48 + ```php 49 + <?php 50 + 51 + class Car 52 + { 53 + public $color; 54 + 55 + function __construct($color) 56 + { 57 + $this->color = $color; 58 + } 59 + 60 + function getColor() 61 + { 62 + return $this->color; 63 + } 64 + } 65 + 66 + $a_car = new Car("red"); 67 + $a_car->color; // => "red" by accessing the property 68 + $a_car->getColor(); // => "red" by invoking the instance method 69 + ``` 70 + 71 + ## Instructions 72 + 73 + In this exercise, you will be simulating a windowing based computer system. 74 + You will create some windows that can be moved and resized. 75 + The following image is representative of the values you will be working with below. 76 + 77 + ```text 78 + ╔════════════════════════════════════════════════════════════╗ 79 + ║ ║ 80 + ║ position::$y,_ ║ 81 + ║ position::$x \ ║ 82 + ║ \<---- size::$width ----> ║ 83 + ║ ^ *──────────────────────┐ ║ 84 + ║ | │ title │ ║ 85 + ║ | ├──────────────────────┤ ║ 86 + ║ | │ │ ║ 87 + ║ size::$height │ │ ║ 88 + ║ | │ contents │ ║ 89 + ║ | │ │ ║ 90 + ║ | │ │ ║ 91 + ║ v └──────────────────────┘ ║ 92 + ║ ║ 93 + ║ ║ 94 + ╚════════════════════════════════════════════════════════════╝ 95 + ``` 96 + 97 + ## 1. Define the properties of the Program Window 98 + 99 + Using the provided class scaffold, provide the properties for the `y`, `x`, `height`, and `width` values. 100 + 101 + ```php 102 + <?php 103 + 104 + $window = new ProgramWindow(); 105 + $window->y; // => null 106 + $window->x; // => null 107 + $window->height; // => null 108 + $window->width; // => null 109 + ``` 110 + 111 + ## 2. Define the initial values for the program window 112 + 113 + Define a constructor function for `ProgramWindow`. 114 + It should not take any arguments, but during the constructor execution set the default values for its properties. 115 + It should set the initial `y` and `x` values to `0`. 116 + It should set the initial `height` and `width` to a `600x800` screen size. 117 + 118 + ```php 119 + <?php 120 + 121 + $window = new ProgramWindow(); 122 + $window->y; // => 0 123 + $window->x; // => 0 124 + $window->height; // => 600 125 + $window->width; // => 800 126 + ``` 127 + 128 + ## 3. Define a function to resize the window 129 + 130 + Define a new class in `Size.php` for a Size class. 131 + It should take constructor arguments to set the `height` and `width` properties. 132 + Additionally `ProgramWindow` requires a resize function, which receives the `Size` object instance and updates its own properties. 133 + 134 + ```php 135 + <?php 136 + 137 + $window = new ProgramWindow(); 138 + $size = new Size(764, 1080); 139 + $window->resize($size) 140 + 141 + $window->height; 142 + // => 764 143 + $window->width; 144 + // => 1080 145 + ``` 146 + 147 + ## 4. Define a function to move the window 148 + 149 + Define a new class in `Position.php` for a Position class. 150 + It should take constructor arguments to set the `y` and `x` properties. 151 + Additionally `ProgramWindow` requires a move function, which receives the `Position` object instance and updates its own properties. 152 + 153 + ```php 154 + <?php 155 + 156 + $window = new ProgramWindow(); 157 + $position = new Position(80, 313); 158 + $window->move($position) 159 + 160 + $window->y; 161 + // => 80 162 + $window->x; 163 + // => 313 164 + ``` 165 + 166 + ## Source 167 + 168 + ### Created by 169 + 170 + - @neenjaw
+13
php/windowing-system/Size.php
··· 1 + <?php 2 + 3 + class Size 4 + { 5 + public int $height; 6 + public int $width; 7 + 8 + public function __construct(int $height, int $width) 9 + { 10 + $this->height = $height; 11 + $this->width = $width; 12 + } 13 + }