Kumite (ko͞omiˌtā) is the practice of taking techniques learned from Kata and applying them through the act of freestyle sparring.
You can create a new kumite by providing some initial code and optionally some test cases. From there other warriors can spar with you, by enhancing, refactoring and translating your code. There is no limit to how many warriors you can spar with.
A great use for kumite is to begin an idea for a kata as one. You can collaborate with other code warriors until you have it right, then you can convert it to a kata.
function random_case($string) {
return implode(array_map(function ($char) {
return lcg_value() < 0.5 ? strtoupper($char) : strtolower($char);
}, str_split($string)));
}
$test->describe("The random_case function", function () {
$GLOBALS['test']->it("should not return a fully lowercase string", function () {
for ($i = 0; $i < 25; $i++) {
$random_string = $GLOBALS['test']->random_token();
echo "Random String: \"$random_string\"<br />";
$output = random_case($random_string);
echo "Output: \"$output\"<br />";
$GLOBALS['test']->assert_not_equals($output, $random_string);
}
});
$GLOBALS['test']->it("should not return a fully uppercase string", function () {
for ($i = 0; $i < 25; $i++) {
$random_string = strtoupper($GLOBALS['test']->random_token());
echo "Random String: \"$random_string\"<br />";
$output = random_case($random_string);
echo "Output: \"$output\"<br />";
$GLOBALS['test']->assert_not_equals($output, $random_string);
}
});
$GLOBALS['test']->it("should return a string consisting about 50% of lowercase and uppercase characters for very long strings", function () {
$lowercase = str_split("abcdefghijklmnopqrstuvwxyz");
$random_lowercase_string = "";
for ($i = 0; $i < 1000; $i++) {
$random_lowercase_string .= $lowercase[~~(lcg_value() * count($lowercase))];
}
echo "Input: $random_lowercase_string<br />";
$output = random_case($random_lowercase_string);
echo "Output: $output<br />";
echo "Number of lowercase characters in output: " . count(array_filter(str_split($output), function ($char) {
return $char === strtolower($char);
})) . "<br />";
$GLOBALS['test']->expect(count(array_filter(str_split($output), function ($char) {
return $char === strtolower($char);
})) > 400 && count(array_filter(str_split($output), function ($char) {
return $char === strtolower($char);
})) < 600);
echo "Number of uppercase characters in output: " . count(array_filter(str_split($output), function ($char) {
return $char === strtoupper($char);
})) . "<br />";
$GLOBALS['test']->expect(count(array_filter(str_split($output), function ($char) {
return $char === strtoupper($char);
})) > 400 && count(array_filter(str_split($output), function ($char) {
return $char === strtoupper($char);
})) < 600);
});
});
In this kata you should print a given string in lowercase fashion.
#include<stdio.h>
#include<string.h>
int main() {
char* my_string="Hello, LOWERCASE C!\n";
strlwr(my_string);
printf(my_string);
return 0;
}
function fibonacci($n) {
return $n === 1 || $n === 2 ? 1 : fibonacci($n - 1) + fibonacci($n - 2);
}
$test->describe('fibonacci($n)', function () {
$GLOBALS['test']->it('should work for some fixed tests', function () {
$GLOBALS['test']->assert_equals(fibonacci(1), 1);
$GLOBALS['test']->assert_equals(fibonacci(2), 1);
$GLOBALS['test']->assert_equals(fibonacci(3), 2);
$GLOBALS['test']->assert_equals(fibonacci(4), 3);
$GLOBALS['test']->assert_equals(fibonacci(5), 5);
$GLOBALS['test']->assert_equals(fibonacci(6), 8);
$GLOBALS['test']->assert_equals(fibonacci(7), 13);
$GLOBALS['test']->assert_equals(fibonacci(8), 21);
$GLOBALS['test']->assert_equals(fibonacci(9), 34);
$GLOBALS['test']->assert_equals(fibonacci(10), 55);
$GLOBALS['test']->assert_equals(fibonacci(11), 89);
$GLOBALS['test']->assert_equals(fibonacci(12), 144);
$GLOBALS['test']->assert_equals(fibonacci(13), 233);
$GLOBALS['test']->assert_equals(fibonacci(14), 377);
$GLOBALS['test']->assert_equals(fibonacci(15), 610);
$GLOBALS['test']->assert_equals(fibonacci(16), 987);
$GLOBALS['test']->assert_equals(fibonacci(17), 1597);
$GLOBALS['test']->assert_equals(fibonacci(18), 2584);
$GLOBALS['test']->assert_equals(fibonacci(19), 4181);
$GLOBALS['test']->assert_equals(fibonacci(20), 6765);
});
});
Kata in PHP #8 - Training JS #36: methods of Math---kata author's lover:random()
Kata
Training JS #36: methods of Math---kata author's lover:random() (7kyu)
function rnd_code() {
$result = "";
$upcase = str_split("ABCDEFGHIJKLM");
$symbols = str_split("~!@#$%^&*");
for ($i = 0; $i < 2; $i++) {
$result .= $upcase[~~(lcg_value() * count($upcase))];
}
for ($i = 0; $i < 4; $i++) {
$result .= ~~(10 * lcg_value());
}
for ($i = 0; $i < 2; $i++) {
$result .= $symbols[~~(lcg_value() * count($symbols))];
}
return $result;
}
$test->describe('The \'rnd_code\' function', function () {
$GLOBALS['test']->it("should work for a basic test", function () {
$yourcode = rnd_code();
echo "Your Code: $yourcode<br />";
$GLOBALS['test']->expect(is_string($yourcode), "The result should be string");
$GLOBALS['test']->expect(strlen($yourcode) === 8, "The length should be 8");
$yourcode = str_split($yourcode);
$GLOBALS['test']->expect(array_search($yourcode[0], str_split("ABCDEFGHIJKLM")) !== false, "1st char should generate from A-M");
$GLOBALS['test']->expect(array_search($yourcode[1], str_split("ABCDEFGHIJKLM")) !== false, "2nd char should generate from A-M");
$GLOBALS['test']->expect(array_search($yourcode[2], str_split("0123456789")) !== false, "3rd char should generate from 0-9");
$GLOBALS['test']->expect(array_search($yourcode[3], str_split("0123456789")) !== false, "4th char should generate from 0-9");
$GLOBALS['test']->expect(array_search($yourcode[4], str_split("0123456789")) !== false, "5th char should generate from 0-9");
$GLOBALS['test']->expect(array_search($yourcode[5], str_split("0123456789")) !== false, "6th char should generate from 0-9");
$GLOBALS['test']->expect(array_search($yourcode[6], str_split("~!@#$%^&*")) !== false, "7th char should generate from ~!@#$%^&*");
$GLOBALS['test']->expect(array_search($yourcode[7], str_split("~!@#$%^&*")) !== false, "8th char should generate from ~!@#$%^&*");
});
$GLOBALS['test']->it('should work for 100 random tests', function () {
$codes = [];
for ($i = 0; $i < 100; $i++) {
array_push($codes, rnd_code());
}
for ($i = 0; $i < 100; $i++) {
for ($k = 0; $k < $i; $k++) {
$GLOBALS['test']->assert_not_equals($codes[$i], $codes[$k], "Your function should not generate duplicate verification codes");
}
}
});
});
Kata in PHP #9 - Genetic Algorithm Series - #1 Generate
Kata
function generate($length) {
$chromosome = "";
for ($i = 0; $i < $length; $i++) {
$chromosome .= round(lcg_value());
}
return $chromosome;
}
$test->describe("The chromosome generator", function () {
global $test;
$test->it("should respect the given length", function () {
global $test;
$test->assert_equals(strlen(generate(0)), 0);
$test->assert_equals(strlen(generate(1)), 1);
$test->assert_equals(strlen(generate(8)), 8);
$test->assert_equals(strlen(generate(16)), 16);
$test->assert_equals(strlen(generate(32)), 32);
$test->assert_equals(strlen(generate(64)), 64);
$test->assert_equals(strlen(generate(256)), 256);
});
$test->it("should probably produce chromosomes with at least 1 '1' and at least 1 '0' for length 50", function () {
global $test;
$chromosome = generate(50);
echo "Your Chromosome: $chromosome<br />";
$test->expect(preg_match('/0/', $chromosome), "Your chromosome did not contain a single '0'");
$test->expect(preg_match('/1/', $chromosome), "Your chromosome did not contain a single '1'");
});
$test->it("should probably return all possible chromosomes of length 5 when run 20000 times", function () {
global $test;
$chromosomes = [];
for ($i = 0; $i < 20000; $i++) {
array_push($chromosomes, generate(5));
}
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '00000';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '00001';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '00010';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '00011';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '00100';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '00101';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '00110';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '00111';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '01000';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '01001';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '01010';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '01011';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '01100';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '01101';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '01110';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '01111';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '10000';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '10001';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '10010';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '10011';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '10100';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '10101';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '10110';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '10111';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '11000';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '11001';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '11010';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '11011';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '11100';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '11101';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '11110';})) >= 1);
$test->expect(count(array_filter($chromosomes, function ($c) {return $c === '11111';})) >= 1);
});
});
Kata in PHP #10 - Genetic Algorithm Series - #2 Mutation
Kata
function mutate($chromosome, $probability) {
return implode(array_map(function ($d, $p) {
return lcg_value() < $p ? ($d === '0' ? '1' : '0') : $d;
}, str_split($chromosome), array_fill(0, strlen($chromosome), $probability)));
}
$test->describe("The chromosome mutating function", function () {
global $test;
$test->it("should convert all '0's to '1's and vice versa when the mutation probability is 1 (100%)", function () {
global $test;
$test->assert_equals(mutate('0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 1), '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111');
$test->assert_equals(mutate('1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111', 1), '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000');
});
$test->it("should not mutate the chromosome at all when the probability is 0", function () {
global $test;
$test->assert_equals(mutate('0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 0), '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000');
$test->assert_equals(mutate('1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111', 0), '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111');
});
$test->it('should mutate about 50% of the chromosome when the mutation probability is 0.5 (50%)', function () {
global $test;
$all_zeroes = '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000';
$all_ones = '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111';
$mutated = mutate($all_zeroes, 0.5);
$test->expect(count(array_filter(str_split($mutated), function ($d) {return $d === '0';})) >= 35 && count(array_filter(str_split($mutated), function ($d) {return $d === '0';})) <= 65);
$test->expect(count(array_filter(str_split($mutated), function ($d) {return $d === '1';})) >= 35 && count(array_filter(str_split($mutated), function ($d) {return $d === '1';})) <= 65);
$mutated = mutate($all_ones, 0.5);
$test->expect(count(array_filter(str_split($mutated), function ($d) {return $d === '0';})) >= 35 && count(array_filter(str_split($mutated), function ($d) {return $d === '0';})) <= 65);
$test->expect(count(array_filter(str_split($mutated), function ($d) {return $d === '1';})) >= 35 && count(array_filter(str_split($mutated), function ($d) {return $d === '1';})) <= 65);
});
$test->it("should work properly for random mutation probabilities", function () {
global $test;
for ($i = 0; $i < 5; $i++) {
echo "Testing a mutation probability of " . ($p = lcg_value()) . "<br />";
echo "Your mutated chromosome: " . ($mutated = mutate('0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', $p)) . "<br />";
$test->expect(preg_match('/0/', $mutated));
$test->expect(preg_match('/1/', $mutated));
}
});
});
Kata in PHP #11 - Genetic Algorithm Series - #3 Crossover
Kata
function crossover($chromosome1, $chromosome2, $index) {
$new_chromosome1 = [];
for ($i = 0; $i < $index; $i++) {
array_push($new_chromosome1, str_split($chromosome1)[$i]);
}
for ($i = $index; $i < strlen($chromosome1); $i++) {
array_push($new_chromosome1, str_split($chromosome2)[$i]);
}
$new_chromosome2 = [];
for ($i = 0; $i < $index; $i++) {
array_push($new_chromosome2, str_split($chromosome2)[$i]);
}
for ($i = $index; $i < strlen($chromosome2); $i++) {
array_push($new_chromosome2, str_split($chromosome1)[$i]);
}
return [implode($new_chromosome1), implode($new_chromosome2)];
}
$test->describe("The chromosome crossover function", function () {
global $test;
$test->it("should work for the example in the description", function () {
global $test;
$test->assert_similar(crossover('111000', '000110', 3), ['111110', '000000']);
});
$test->it("should work for basic tests", function () {
global $test;
$test->assert_similar(crossover('', '', 0), ['', '']);
$test->assert_similar(crossover('01', '10', 1), ['00', '11']);
$test->assert_similar(crossover('00000000', '11111111', 0), ['11111111', '00000000']);
$test->assert_similar(crossover('00000000', '11111111', 7), ['00000001', '11111110']);
});
$test->it("should work for more fixed tests", function () {
global $test;
$test->assert_similar(crossover('0000', '1111', 0), ['1111', '0000']);
$test->assert_similar(crossover('0000', '1111', 1), ['0111', '1000']);
$test->assert_similar(crossover('0000', '1111', 2), ['0011', '1100']);
$test->assert_similar(crossover('0000', '1111', 3), ['0001', '1110']);
$test->assert_similar(crossover('0000', '1111', 4), ['0000', '1111']);
$test->assert_similar(crossover('0010011110', '1110010100', 3), ['0010010100', '1110011110']);
$test->assert_similar(crossover('0010011110', '1110010100', 7), ['0010011100', '1110010110']);
});
});
Kata in PHP #12 - Genetic Algorithm Series - #4 Get population and fitnesses
Kata
Genetic Algorithm Series - #4 Get population and fitnesses (Beta)
function map_population_fit($population, $fitness) {
return array_map(function ($chromosome, $formula) {
return ["chromosome" => $chromosome, "fitness" => $formula($chromosome)];
}, $population, array_fill(0, count($population), $fitness));
}
$test->describe('map_population_fit($population, $fitness)', function () {
global $test;
$test->it("should return an array of the correct format and with the correct entries for the given population", function () {
global $test;
$population = ['10100111', '11011100',
'01101000', '01100111', '01000010', '10001001', '10111100', '11111000', '11001100',
'00001011', '01011011', '01000111', '11010101', '00101101', '00100111', '00000111',
'00101000', '00101011', '01011011', '10100001', '00111000', '00010110', '00101100',
'11111110', '10101001', '11101001', '00011001', '10100011', '11000001', '11010101',
'11000110', '01111000', '11011000', '00111010', '11110100', '00100111', '10001101',
'11000100', '01110010', '10011111', '10110101', '11001100', '00110111', '00000100',
'10010010', '00011000', '10111010', '10001000', '00010011', '01001011', '00100010',
'01111000', '01110111', '11101011', '00001010', '00000000', '01100011', '00011111',
'10000001', '01100010', '11011100', '10001100', '01110010', '11011011', '00000111',
'10100100', '00101101', '00001101', '10010110', '10101110', '00111010', '00011001',
'11000110', '01010101', '00101000', '00000110', '11001000', '11000110', '01010100',
'01011010', '00101101', '00011001', '00010101', '10101110', '01100010', '01110101',
'01111011', '00111000', '11101110', '00110100', '11100100', '01011101', '10000110',
'11111101', '11000001', '11000111', '11000111', '01011000', '10011011', '10110101'];
function fitness($c) {
$ideal = '10011001';
$r = 0;
for ($i = 0; $i < strlen($c); $i++) {
if (str_split($c)[$i] === str_split($ideal)[$i]) $r++;
}
return $r / strlen($ideal);
}
$actual = map_population_fit($population, 'fitness');
for ($i = 0; $i < count($population); $i++) {
$test->assert_similar($actual[$i], ["chromosome" => $population[$i], "fitness" => fitness($population[$i])]);
}
});
});
Kata in PHP #13 - Genetic Algorithm Series - #5 Roulette wheel selection
Kata
Genetic Algorithm Series - #5 Roulette wheel selection (6kyu)
Side Note
Guess what my next "Kata in PHP" Kumite might be about ;)
function select($population, $fitnesses) {
$sum_fitnesses = array_reduce($fitnesses, function ($sum, $fitness) {
return $sum + $fitness;
}, 0);
$i = 0;
$k = 0;
while (true) {
$rand = lcg_value();
if ($rand < $fitnesses[$i % count($fitnesses)] / $sum_fitnesses && $k >= 33) {
return $population[$i % count($population)];
} else if ($rand < $fitnesses[$i % count($fitnesses)] / $sum_fitnesses) {
$k++;
}
$i++;
}
}
$test->describe('select($population, $fitnesses)', function () {
global $test;
$test->it("should return the correct proportion for each 'chromosome' according to their relative fitnesses", function () {
global $test;
$population = [1, 2, 3, 4];
$fitnesses = [0.01, 0.01, 0.01, 0.01];
$selection = [];
for ($i = 0; $i < 1000; $i++) {
array_push($selection, select($population, $fitnesses));
}
$ones = count(array_filter($selection, function ($c) {
return $c === 1;
}));
$twos = count(array_filter($selection, function ($c) {
return $c === 2;
}));
$threes = count(array_filter($selection, function ($c) {
return $c === 3;
}));
$fours = count(array_filter($selection, function ($c) {
return $c === 4;
}));
$test->expect($ones >= 200 && $ones <= 300, "Your probability is more than 5% off in 1000 runs");
$test->expect($twos >= 200 && $twos <= 300, "Your probability is more than 5% off in 1000 runs");
$test->expect($threes >= 200 && $threes <= 300, "Your probability is more than 5% off in 1000 runs");
$test->expect($fours >= 200 && $fours <= 300, "Your probability is more than 5% off in 1000 runs");
$population = [1, 2, 3];
$fitnesses = [0.5, 0.5, 1];
$selection = [];
for ($i = 0; $i < 1000; $i++) {
array_push($selection, select($population, $fitnesses));
}
$ones = count(array_filter($selection, function ($c) {
return $c === 1;
}));
$twos = count(array_filter($selection, function ($c) {
return $c === 2;
}));
$threes = count(array_filter($selection, function ($c) {
return $c === 3;
}));
$test->expect($ones >= 200 && $ones <= 300, "Your probability is more than 5% off in 1000 runs");
$test->expect($twos >= 200 && $twos <= 300, "Your probability is more than 5% off in 1000 runs");
$test->expect($threes >= 450 && $threes <= 550, "Your probability is more than 5% off in 1000 runs");
$population = [1, 2, 3];
$fitnesses = [0.0001, 0.0001, 0.0002];
$selection = [];
for ($i = 0; $i < 1000; $i++) {
array_push($selection, select($population, $fitnesses));
}
$ones = count(array_filter($selection, function ($c) {
return $c === 1;
}));
$twos = count(array_filter($selection, function ($c) {
return $c === 2;
}));
$threes = count(array_filter($selection, function ($c) {
return $c === 3;
}));
$test->expect($ones >= 200 && $ones <= 300, "Your probability is more than 5% off in 1000 runs");
$test->expect($twos >= 200 && $twos <= 300, "Your probability is more than 5% off in 1000 runs");
$test->expect($threes >= 450 && $threes <= 550, "Your probability is more than 5% off in 1000 runs");
$population = [1, 2, 3, 4];
$fitnesses = [0.8, 0.6, 0.4, 0.2];
$selection = [];
for ($i = 0; $i < 1000; $i++) {
array_push($selection, select($population, $fitnesses));
}
$ones = count(array_filter($selection, function ($c) {
return $c === 1;
}));
$twos = count(array_filter($selection, function ($c) {
return $c === 2;
}));
$threes = count(array_filter($selection, function ($c) {
return $c === 3;
}));
$fours = count(array_filter($selection, function ($c) {
return $c === 4;
}));
$test->expect($ones >= 350 && $ones <= 450);
$test->expect($twos >= 250 && $twos <= 350);
$test->expect($threes >= 150 && $threes <= 250);
$test->expect($fours >= 50 && $fours <= 150);
$population = [1, 2, 3, 4, 5];
$fitnesses = [0.1, 0.4, 0, 0.3, 0.2];
$selection = [];
for ($i = 0; $i < 1000; $i++) {
array_push($selection, select($population, $fitnesses));
}
$ones = count(array_filter($selection, function ($c) {
return $c === 1;
}));
$twos = count(array_filter($selection, function ($c) {
return $c === 2;
}));
$threes = count(array_filter($selection, function ($c) {
return $c === 3;
}));
$fours = count(array_filter($selection, function ($c) {
return $c === 4;
}));
$fives = count(array_filter($selection, function ($c) {
return $c === 5;
}));
$test->expect($ones >= 50 && $ones <= 150);
$test->expect($twos >= 350 && $twos <= 450);
$test->expect($threes === 0, "For a chromosome of fitness 0, it should never be chosen");
$test->expect($fours >= 250 && $fours <= 350);
$test->expect($fives >= 150 && $fives <= 250);
});
});
PHP Classes: public, private and protected
Overview and Background
A few weeks back when I was creating my custom PHP testing framework by defining a Test
class, I realised that if my PHP testing framework were to be used to test the code of others (like here in Codewars or in Strive Qualified), I would have to ensure that certain class properties and methods cannot be directly accessed or modified by the user; otherwise, the user can hack the testing framework and cheat. Immediately, the first thought that came to my mind was the private
keyword (which I believe Codecademy has taught (me) in its PHP course) as that keyword makes that particular property or method inaccessible outside the class. However, my original intent of my PHP testing framework was that other users should be able to effortlessly inherit from my Test
class using the extends
keyword and the child class should contain all the properties and functioning methods from the original Test
class simply by inheriting. By making certain properties and methods used throughout the class private
(instead of protected
as I will explain later), inheritance from my Test
class became impossible because the child class will not have inherited the key private
properties and methods and therefore the child class (of Test
) will not function properly. Therefore, after some research, I realised that private
was not the correct keyword to use and protected
should be used instead. But then, you may ask, "What is the difference between private
and protected
if they both prevent external access to the affected properties/methods?"
public
The public
keyword is by far the most common keyword used when declaring or defining properties/methods in PHP classes. All properties/methods declared/defined with this keyword can be accessible throughout the entire PHP script being executed.
private
The private
keyword ensures that the declared property/method can only be accessed within the very class in which it is defined (the advantage of private
methods/properties). However, the major drawback of using private
(if not used correctly) is that child classes cannot inherit such properties/methods at all.
protected
The protected
keyword ensures that all properties/methods declared/defined using this keyword cannot be accessed externally. However, its main advantage over the "private" keyword is that such methods/properties can be inherited by child classes.
Code
The code shown to the right demonstrates the behaviour of properties declared according to the keywords mentioned above.
class ParentClass {
// A public property
public $public = "public";
// A private property
private $private = "private";
// A protected property
protected $protected = "protected";
// Access the public property within the class
public function echo_public() {
echo $this->public;
}
// Access the private property within the class
public function echo_private() {
echo $this->private;
}
// Access the protected property within the class
public function echo_protected() {
echo $this->protected;
}
}
class ChildClass extends ParentClass {
// Let's try to do the same as in the parent class ...
// Access the public property in the child class
public function echo_public() {
echo $this->public;
}
// Attempting to access the private property in the child class
public function echo_private() {
echo $this->private;
}
// Access the protected property in the child class
public function echo_protected() {
echo $this->protected;
}
}
$test->describe("PHP Classes", function () {
global $test;
$test->it("All three of 'public', 'private' and 'protected' properties should be accessible within the class itself", function () {
global $test, $obj;
$obj = new ParentClass;
$test->expect_no_error("An error should not be thrown", function () {
global $obj;
$obj->echo_public();
echo "<br />";
});
$test->expect_no_error("An error should not be thrown", function () {
global $obj;
$obj->echo_private();
echo "<br />";
});
$test->expect_no_error("An error should not be thrown", function () {
global $obj;
$obj->echo_protected();
echo "<br />";
});
});
$test->it("The child class should be able to access both the public and protected properties but not the private property", function () {
global $test, $obj;
$obj = new ChildClass;
$obj->echo_public();
echo "<br />";
$test->expect(property_exists("ChildClass", "public"));
$obj->echo_private();
echo "<br />";
$test->expect(!property_exists("ChildClass", "private"));
$obj->echo_protected();
echo "<br />";
$test->expect(property_exists("ChildClass", "protected"));
});
$test->it("The public property should be accessible within the entire PHP script being executed but attempts to access private or protected properties externally should thrown an error", function () {
global $test, $obj;
$obj = new ParentClass;
$test->expect_no_error("An error should not be thrown", function () {
global $obj;
echo "$obj->public<br />";
});
// NOTE: Somehow I cannot get my PHP testing framework to handle the fatal errors (caused by trying to access private/protected properties externally) properly but if you uncomment the code below you will see that it throws a fatal error.
# echo $obj->private;
# echo $obj->protected;
});
});