2021/11/25/PHP documentation gripe: Difference between revisions
(Created page with "{{nav/codeblog}} The [https://www.php.net/manual/en/language.oop5.variance.php official PHP documentation] says: <blockquote>Covariance '''allows a child's method to return a...") |
No edit summary |
||
Line 38: | Line 38: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
...but, umm: | ...but, umm: | ||
<blockquote>PHP Fatal error: Declaration of cClass2::SetIt(cClassB $v) must be compatible with cClass1::SetIt(cClassA $v) in /home/htnet/site/git/ferreteria/base/tests/php2.php on line 8<blockquote> | <blockquote>PHP Fatal error: Declaration of cClass2::SetIt(cClassB $v) must be compatible with cClass1::SetIt(cClassA $v) in /home/htnet/site/git/ferreteria/base/tests/php2.php on line 8</blockquote> | ||
Looking at the actual example in the docs: | Looking at the actual example in the docs (condensed down a bit for readability): | ||
<syntaxhighlight lang=php> | |||
abstract class Animal { | |||
protected string $name; | |||
public function __construct(string $name) { $this->name = $name; } | |||
abstract public function speak(); | |||
} | |||
class Dog extends Animal { | |||
public function speak() { echo $this->name . " barks"; } | |||
} | |||
class Cat extends Animal { | |||
public function speak() { echo $this->name . " meows"; } | |||
} | |||
//--- | |||
interface AnimalShelter { public function adopt(string $name): Animal; } | |||
class CatShelter implements AnimalShelter { | |||
public function adopt(string $name): Cat { return new Cat($name); } // returns Cat instead of Animal | |||
} | |||
class DogShelter implements AnimalShelter { | |||
public function adopt(string $name): Dog { return new Dog($name); } // returns Dog instead of Animal | |||
} | |||
$kitty = (new CatShelter)->adopt("Ricky"); | |||
$kitty->speak(); | |||
echo "\n"; | |||
$doggy = (new DogShelter)->adopt("Mavrick"); | |||
$doggy->speak(); | |||
echo "\n"; | |||
</syntaxhighlight> | |||
This works -- so what's the difference? Tentatively, it's the fact that the parent function is defined in an '''interface''', not a class or trait -- but a test shows that this isn't the problem. Modifying AnimalShelter to be an abstract class instead of an interface gives us this: | |||
<syntaxhighlight lang=php> | |||
abstract class AnimalShelter { abstract public function adopt(string $name): Animal; } | |||
class CatShelter extends AnimalShelter { | |||
public function adopt(string $name): Cat { return new Cat($name); } // returns Cat instead of Animal | |||
} | |||
class DogShelter extends AnimalShelter { | |||
public function adopt(string $name): Dog { return new Dog($name); } // returns Dog instead of Animal | |||
} | |||
</syntaxhighlight> | |||
...which also works just fine. | |||
Let's rewrite the doc example into the form in my examples at the top: | |||
<syntaxhighlight lang=php> | |||
IN PROGRESS | |||
</syntaxhighlight> |
Revision as of 15:31, 25 November 2021
The official PHP documentation says:
Covariance allows a child's method to return a more specific type than the return type of its parent's method. Whereas, contravariance allows a parameter type to be less specific in a child method, than that of its parent.
...and then goes on to note that both are supported in #PHP7.4 (which is what I am using).
However:
abstract class cClass1 {
abstract function GetIt() : mixed;
}
abstract class cClass2 extends cClass1 {
abstract function GetIt() : object;
}
PHP Fatal error: Declaration of cClass2::GetIt(): object must be compatible with cClass1::GetIt(): mixed in /home/htnet/site/git/ferreteria/base/tests/php2.php on line 6
...and also...
abstract class cClass1 {
abstract function SetIt($v);
}
abstract class cClass2 extends cClass1 {
abstract function SetIt(object $v);
}
PHP Fatal error: Declaration of cClass2::SetIt(object $v) must be compatible with cClass1::SetIt($v) in /home/htnet/site/git/ferreteria/base/tests/php2.php on line 6
So, kinda no.
The examples given in the documentation seem to be only about specificity of object types -- so I should be able to do this:
class cClassA {}
class cClassB extends cClassA {}
abstract class cClass1 {
abstract function SetIt(cClassA $v);
}
abstract class cClass2 extends cClass1 {
abstract function SetIt(cClassB $v);
}
...but, umm:
PHP Fatal error: Declaration of cClass2::SetIt(cClassB $v) must be compatible with cClass1::SetIt(cClassA $v) in /home/htnet/site/git/ferreteria/base/tests/php2.php on line 8
Looking at the actual example in the docs (condensed down a bit for readability):
abstract class Animal {
protected string $name;
public function __construct(string $name) { $this->name = $name; }
abstract public function speak();
}
class Dog extends Animal {
public function speak() { echo $this->name . " barks"; }
}
class Cat extends Animal {
public function speak() { echo $this->name . " meows"; }
}
//---
interface AnimalShelter { public function adopt(string $name): Animal; }
class CatShelter implements AnimalShelter {
public function adopt(string $name): Cat { return new Cat($name); } // returns Cat instead of Animal
}
class DogShelter implements AnimalShelter {
public function adopt(string $name): Dog { return new Dog($name); } // returns Dog instead of Animal
}
$kitty = (new CatShelter)->adopt("Ricky");
$kitty->speak();
echo "\n";
$doggy = (new DogShelter)->adopt("Mavrick");
$doggy->speak();
echo "\n";
This works -- so what's the difference? Tentatively, it's the fact that the parent function is defined in an interface, not a class or trait -- but a test shows that this isn't the problem. Modifying AnimalShelter to be an abstract class instead of an interface gives us this:
abstract class AnimalShelter { abstract public function adopt(string $name): Animal; }
class CatShelter extends AnimalShelter {
public function adopt(string $name): Cat { return new Cat($name); } // returns Cat instead of Animal
}
class DogShelter extends AnimalShelter {
public function adopt(string $name): Dog { return new Dog($name); } // returns Dog instead of Animal
}
...which also works just fine.
Let's rewrite the doc example into the form in my examples at the top:
IN PROGRESS