search

Home  >  Q&A  >  body text

How PHP interfaces actually change the behavior of classes

<p>According to the PHP documentation, </p> <blockquote> <p>Object interfaces allow you to create code that specifies which methods a class must implement without defining how those methods are implemented. </p> </blockquote> <p>Thus, an interface is like a class with predefined methods that still need to be accessed using the <code>-></code> notation</p> <p>However, the ArrayAccess interface provides access to objects as arrays. Objects can be accessed using <code>$object->property</code> and <code>$object["property"]</code></p> <p>I can't understand how ArrayAccess makes it possible to change the object syntax. I wrote a piece of code to try to replicate the effect of the <code>ArrayAccess</code> method<em>only one</em>, but it throws the error</p> <pre class="brush:php;toolbar:false;">// Using the PHP ArrayAccess Interface namespace A { class myclass implements \ArrayAccess { public function offsetExists($offset) { return true; } public function offsetGet($offset) { // changed behavior return $this->{$offset} ?? null; } public function offsetSet($offset, $value) {} public function offsetUnset($offset) {} } $myclass = new myclass(); $myclass->access = 'Interface'; echo $myclass['access']; // "Interface" }; //Trying to implement my own ArrayAccess Interface namespace B { interface MyArrayAccess { public function offsetGet($offset); } class myclass implements MyArrayAccess { public function offsetGet($offset) { // change behavior return $this->{$offset} ?? null; } } $myclass = new myclass(); $myclass->access = 'Interface'; echo $myclass['access']; // Fatal error: Uncaught Error: Cannot use object of type B\myclass as array } </pre> <p>Please help me explain it correctly. Thank you</p>
P粉289775043P粉289775043459 days ago612

reply all(1)I'll reply

  • P粉702946921

    P粉7029469212023-09-03 20:17:24

    I'm not saying that interfaces "change the behavior of a class", but that interfaces make extending class functionality easy.

    To understand interface, as an object-oriented programming concept, we should first understand what problem it wants to solve.

    What problem is "Interface" designed to solve?

    An interface is a contract. This is how duck-typing is implemented in PHP. You need to think from the perspective of a library writer who wants to expose functionality to others. For example,

    class Greeter
    {
        public function greet($person)
        {
            echo "Hello, {$person->getName()}!\n";
        }
    }
    

    To ensure that library users know that $person needs to have a getName() method, you can create a class Person > that has a getName() method. Type declarations are then used to detect potential errors when the code is resolved.

    class Greeter
    {
        public function greet(Person $person)
        {
            echo "Hello, {$person->getName()}!\n";
        }
    }
    
    class Person
    {
        public string $name;
        public function getName(): string
        {
            return $this->name;
        }
    }
    

    Suppose there is another library that feeds things with food:

    class Feeder {
        public function feed(Eater $eater, string $food) {
            $eater->eat($food);
        }
    }
    
    class Animal {
        private $stomach = [];
        public function eat(string $food) {
            $stomach = $food;
        }
    }
    
    

    Consider this...

    Now, suppose the user wants to write a Pet class that can both eat and greet. Users don't want to have to write these features again just to Pet them.

    How to write Pet to use both the Greeter and Feeder libraries?

    Maybe this is the case?

    class Pet extends Person, Animal {
    }
    

    Unfortunately, PHP does not support multiple inheritance . A class can only extend one class. The above code is not valid. So in the current situation, users can only use one of the libraries.

    Additionally, "name" might be a very different concept for different things (e.g., one might use getName() to return $first_name and $last_name ). There may not be a reasonable default implementation of the getName() method in your library class.

    So, as a library writer, you want his/her library to be as flexible as possible for the user. what can you do?

    How to solve this problem using "interface" in PHP?

    An interface is a declaration of method signature. This is a quick way to declare library requirements without concrete class/inheritance requirements.

    Using interfaces you can override both libraries like this:

    Greeter Library

    class Greeter {
        public function greet(Namer $namer) {
            echo "Hello, {$namer->getName()}!\n";
        }
    }
    
    interface Namer {
        public function getName(): string;
    }
    

    Feeder Library

    class Feeder {
        public function feed(Eater $eater, string $food) {
            $eater->eat($food);
        }
    }
    
    interface Eater {
        public function eat(string $food);
    }
    
    

    No need for a specific class (or parent class inheritance), a class can implement multiple interfaces. So the following Pet class is completely valid in PHP:

    class Pet implements Namer, Eater {
        private array $stomach = [];
        private string $name = '';
    
        public function __construct(string $name)
        {
            $this->name = $name;
        }
    
        /**
         * Implements Namer.
         */
        public function getName(): string
        {
            return $this->name;
        }
    
        /**
         * Implements Eater.
         */
        public function eat(string $food)
        {
            $this->stomach[] = $food;
        }
    }
    
    $greeter = new Greeter();
    $feeder = new Feeder();
    $pet = new Pet('Paul');
    
    $greeter->greet($pet);
    $feeder->feed($pet, 'a biscuit');
    

    Now objects of this Pet class can be used with the Greeter library and the Feeder library.

    ArrayAccess What about the interface?

    ArrayAccess The interface is not declared by a third-party interface library writer, but is written by a core PHP writer. The core PHP writer provides deeper support for this.

    Somewhat like the interface we mentioned before, PHP provides functionality to the classes that implement it. But instead of providing the Greeter or Feeder examples above, core PHP provides syntax sugar for classes that implement ArrayAccess. This means you can use simpler code when working with classes that implement the AccessAccess interface.

    In the official example,

    <?php
    class Obj implements ArrayAccess {
        private $container = array();
    
        public function __construct() {
            $this->container = array(
                "one"   => 1,
                "two"   => 2,
                "three" => 3,
            );
        }
    
        public function offsetSet($offset, $value) {
            if (is_null($offset)) {
                $this->container[] = $value;
            } else {
                $this->container[$offset] = $value;
            }
        }
    
        public function offsetExists($offset) {
            return isset($this->container[$offset]);
        }
    
        public function offsetUnset($offset) {
            unset($this->container[$offset]);
        }
    
        public function offsetGet($offset) {
            return isset($this->container[$offset]) ? $this->container[$offset] : null;
        }
    }
    

    If you implement them, replace these with:

    $obj = new Obj;
    $obj->offsetSet(10, "hello");
    $obj->offsetSet(11, "world");
    if ($obj->offsetUnset(12)) {
        $obj->offsetUnset(12);
    }
    echo $obj->offsetGet(11);
    

    You can use $obj with array-like syntax to make the code shorter:

    $obj = new Obj;
    
    $obj[10] = "hello";
    $obj[11] = "world";
    if (isset($obj[12])) {
        unset($obj[12]);
    }
    echo $obj[11];
    

    reply
    0
  • Cancelreply