Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP method scope binding

Tags:

object

oop

php

I am confused by how PHP call method in parent-children hierarchy. Here is the code

class A {
        private function foo() {
            echo "A!";
        }
        public function test() {
            $this->foo();
        }
 }

class C extends A {
       public function foo() {
            echo 'C!';
       }
}

$c = new C();
$c->test();  

The output is A!

consider another example, but only change the foo() method visibility in class A to be public.

class A {
        public function foo() {
            echo "A!";
        }
        public function test() {
            $this->foo();
        }
 }

 class C extends A {
        public function foo() {
            echo 'C!';
        }
 }

 $c = new C();
 $c->test();  

This output is C!

Any explanation is welcome.

like image 892
Nero Avatar asked Nov 02 '18 02:11

Nero


2 Answers

Rule: private and final methods on an object will always be called directly, without consulting the override table.

This rule is baked into the engine:

/* Check if this calls a known method on $this */
if (opline->op1_type == IS_UNUSED && opline->op2_type == IS_CONST &&
        CG(active_class_entry) && zend_is_scope_known()) {
    zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op2) + 1);
    fbc = zend_hash_find_ptr(&CG(active_class_entry)->function_table, lcname);

    /* We only know the exact method that is being called if it is either private or final.
     * Otherwise an overriding method in a child class may be called. */
    if (fbc && !(fbc->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_FINAL))) {
        fbc = NULL;
    }
}

"Why", you ask? The answer is: because that's how it works. In language design, this is called "name hiding", and it's up to the language to specify how name hiding works. Take C++ for example. It has well-defined, and complex name hiding rules. PHP has its own rules. They're different from C++. But they're unique to PHP. This is just something you have to memorize about the language.

I admit the docs could better spell this out, however.

like image 198
bishop Avatar answered Nov 15 '22 17:11

bishop


This is the behavior in most languages (by design).

If a method is private, $this->method() will call it directly (ignore the derived class's method)

If a method is virtual (this concept comes from C++, in PHP and Java, all public/protected methods are virtual), $this->method() will call the derived class's method.

Rule is rule, it is an OOP concern. For example, in OOP we may want to make sure a method never got overridden, then we can make it private. If a developer overrides it by accident, and would hence introduce weird behaviour if the parent suddenly called the child's implementation. Thanks to @deceze

like image 32
shawn Avatar answered Nov 15 '22 16:11

shawn