php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Doc Bug #79538 Incomplete example in language.oop5.variance
Submitted: 2020-04-29 12:12 UTC Modified: 2020-04-29 16:48 UTC
From: david at codepoets dot co dot uk Assigned: cmb (profile)
Status: Not a bug Package: Documentation problem
PHP Version: Irrelevant OS: n/a
Private report: No CVE-ID: None
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please !
Your email address:
MUST BE VALID
Solve the problem:
29 + 3 = ?
Subscribe to this entry?

 
 [2020-04-29 12:12 UTC] david at codepoets dot co dot uk
Description:
------------
---
From manual page: https://php.net/language.oop5.variance
---

in the Contravariance section there is :

abstract class Animal
{
    protected string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function eat(AnimalFood $food)
    {
        echo $this->name . " eats " . get_class($food);
    }
}


The eat method should instead take Food as a type hint, and not AnimalFood.

i.e. something more like :

abstract class Animal
{
    .....
    public function eat(Food $food)
    {
        echo $this->name . " eats " . get_class($food);
    }
}




Test script:
---------------
<?php

abstract class Animal
{
    protected $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    abstract public function speak();


    public function eat(AnimalFood $food)
    {
        echo $this->name . " eats " . get_class($food);
    }
}

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 // instead of returning class type Animal, it can return class type Cat
    {
        return new Cat($name);
    }
}

class DogShelter implements AnimalShelter
{
    public function adopt(string $name): Dog // instead of returning class type Animal, it can return class type Dog
    {
        return new Dog($name);
    }
}

class Food {}

class AnimalFood extends Food {}

$kitty = (new CatShelter)->adopt("Ricky");
$catFood = new AnimalFood();
$kitty->eat($catFood);
echo "\n";

$doggy = (new DogShelter)->adopt("Mavrick");
$banana = new Food();
$doggy->eat($banana);


Expected result:
----------------
Ricky eats AnimalFood
Mavrick eats Food

Actual result:
--------------
david@walnut:~/src/php-library/animals$ php7.4 animals.php 

Ricky eats AnimalFood
PHP Fatal error:  Uncaught TypeError: Argument 1 passed to Animal::eat() must be an instance of AnimalFood, instance of Food given, called in /home/david/src/php-library/animals/animals.php on line 75 and defined in /home/david/src/php-library/animals/animals.php:15
Stack trace:
#0 /home/david/src/php-library/animals/animals.php(75): Animal->eat()
#1 {main}
  thrown in /home/david/src/php-library/animals/animals.php on line 15


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-04-29 15:44 UTC] cmb@php.net
-Summary: Incorrect type hint in language.oop5.variance +Summary: Incomplete example in language.oop5.variance -Status: Open +Status: Verified
 [2020-04-29 15:44 UTC] cmb@php.net
> The eat method should instead take Food as a type hint, and not
> AnimalFood.

That would defeat the purpose of the example, though, because it
is about contravariance, not invariance.

The problem is that Cat::eat() is not defined.  See
<https://3v4l.org/14rTK> for a working example.
 [2020-04-29 16:40 UTC] cmb@php.net
-Status: Verified +Status: Closed -Assigned To: +Assigned To: cmb
 [2020-04-29 16:40 UTC] cmb@php.net
> The problem is that Cat::eat() is not defined.

Nope.  Actually, there is no problem.
 [2020-04-29 16:40 UTC] cmb@php.net
-Status: Closed +Status: Not a bug
 [2020-04-29 16:53 UTC] tiffany@php.net
The examples are a bit silly (intentionally), they're written as a play on dogs eating almost anything, and cats being picky eaters. The Cat class inherits the eat() method from Animal, which only accepts AnimalFood. The eat() method inside Dog overrides Animal::eat() so it can eat any kind of Food, which is how contravariance works - the child method accepts a less specific parameter than its parent. The error you receive is documented near the bottom when $kitty tries to eat() the $banana.
 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Sun Sep 20 03:01:24 2020 UTC