จะแทนที่ฟังก์ชั่น trait และเรียกมันจากฟังก์ชั่น overridden ได้อย่างไร?


370

สถานการณ์:

trait A {
    function calc($v) {
        return $v+1;
    }
}

class MyClass {
    use A;

    function calc($v) {
        $v++;
        return A::calc($v);
    }
}

print (new MyClass())->calc(2); // should print 4

รหัสนี้ใช้งานไม่ได้และฉันไม่สามารถหาวิธีที่จะเรียกฟังก์ชั่นลักษณะได้เหมือนว่ามันได้รับมา ฉันพยายามโทรself::calc($v), static::calc($v), parent::calc($v), A::calc($v)และต่อไปนี้:

trait A {
    function calc($v) {
        return $v+1;
    }
}

class MyClass {
    use A {
        calc as traitcalc;
    }

    function calc($v) {
        $v++;
        return traitcalc($v);
    }
}

ไม่มีอะไรทำงาน

มีวิธีที่จะทำให้มันใช้งานได้หรือฉันจะต้องแทนที่ฟังก์ชั่นลักษณะที่ซับซ้อนกว่านี้ :)

คำตอบ:


641

คนสุดท้ายของคุณเกือบจะอยู่ที่นั่นแล้ว:

trait A {
    function calc($v) {
        return $v+1;
    }
}

class MyClass {
    use A {
        calc as protected traitcalc;
    }

    function calc($v) {
        $v++;
        return $this->traitcalc($v);
    }
}

ลักษณะไม่ได้เป็นคลาส คุณไม่สามารถเข้าถึงสมาชิกโดยตรง เป็นเพียงแค่คัดลอกและวางโดยอัตโนมัติ ...


20
เพื่อชี้แจง - เมื่อคลาสของคุณกำหนดวิธีการเดียวกันมันจะแทนที่ลักษณะของมันโดยอัตโนมัติ ลักษณะเติมในวิธีการเป็น @ircmaxell กล่าวถึงเมื่อมันว่างเปล่า
Yehosef

2
@PhillipWhelan จะดีถ้าคุณสามารถเพิ่มข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่ "ไม่ทำงานตามที่คาดไว้" เขียนเหมือนว่ามันไม่ได้ช่วยอะไรมากในการทำความเข้าใจกับพฤติกรรมผิดประเภทที่คาดหวังและไม่รับประกันกับเราว่านี่ไม่ใช่ความผิดพลาดชั่วคราวของคุณ อาจมีคำถาม SO เกี่ยวกับปัญหาที่คุณกำลังพูดถึงอยู่บ้าง (ในที่สุด) ขอบคุณ
Kamafeather

1
ปัญหาคือวิธีการทั้งหมดในลักษณะจะไม่รวมอีกต่อไป
58

2
เพียงเพื่อการอ้างอิง: ถ้าฟังก์ชั่นลักษณะนิสัยของคุณคงที่คุณสามารถเข้าถึงฟังก์ชั่นได้โดยการโทรA::calc(1)
velop

4
ดังที่ฟิลลิปพูดถึง (ฉันคิดว่า) คุณจะทำสิ่งนี้อย่างไรกับวิธีการหนึ่งของคุณลักษณะในขณะที่ยังรวมถึงวิธีอื่น ๆ ทั้งหมดของลักษณะเดียวกันตามปกติ โดยเฉพาะอย่างยิ่งโดยไม่ต้องอ้างอิงแต่ละวิธีอย่างชัดเจน
Gannet

14

ถ้าคลาสใช้เมธอดโดยตรงคลาสจะไม่ใช้เวอร์ชันคุณลักษณะ บางทีสิ่งที่คุณกำลังคิดคือ:

trait A {
    function calc($v) {
        return $v+1;
    }
}

class MyClass {
    function calc($v) {
        return $v+2;
    }
}

class MyChildClass extends MyClass{
}

class MyTraitChildClass extends MyClass{
    use A;
}

print (new MyChildClass())->calc(2); // will print 4

print (new MyTraitChildClass())->calc(2); // will print 3

เนื่องจากคลาสลูกไม่ได้ใช้เมธอดโดยตรงพวกเขาจะใช้คุณลักษณะนั้นก่อนหากมีการใช้คลาสแม่แบบนั้นเป็นอย่างอื่น

หากคุณต้องการลักษณะสามารถใช้วิธีการในระดับผู้ปกครอง (สมมติว่าคุณรู้วิธีที่จะมี) เช่น

trait A {
    function calc($v) {
        return parent::calc($v*3);
    }
}
// .... other code from above
print (new MyTraitChildClass())->calc(2); // will print 8 (2*3 + 2)

คุณยังสามารถระบุวิธีการที่จะแทนที่ แต่ยังคงเข้าถึงวิธีการลักษณะดังนี้:

trait A {
    function trait_calc($v) {
        return $v*3;
    }
}

class MyClass {
    function calc($v) {
        return $v+2;
    }
}


class MyTraitChildClass extends MyClass{
    use A {
      A::trait_calc as calc;
    }
}


class MySecondTraitChildClass extends MyClass{
    use A {
      A::trait_calc as calc;
    }

    public function calc($v) {
      return $this->trait_calc($v)+.5;
    }
}


print (new MyTraitChildClass())->calc(2); // will print 6
echo "\n";
print (new MySecondTraitChildClass())->calc(2); // will print 6.5

คุณสามารถดูได้ที่http://sandbox.onlinephpfunctions.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5


8

อีกทางเลือกหนึ่งหากมีความสนใจ - พร้อมคลาสระดับกลางพิเศษเพื่อใช้วิธี OOO ปกติ ทำให้การใช้งานง่ายขึ้นด้วยparent :: methodname

trait A {
    function calc($v) {
        return $v+1;
    }
}

// an intermediate class that just uses the trait
class IntClass {
    use A;
}

// an extended class from IntClass
class MyClass extends IntClass {
    function calc($v) {
        $v++;
        return parent::calc($v);
    }
}

6
วิธีการนี้จะช่วยให้คุณได้เปรียบโดยใช้traits เช่นการรวมคุณสมบัติหลายอย่างในหลายคลาส (เช่นลักษณะ A, B ในคลาส, ลักษณะ B, C, D ในคลาสอื่น, ลักษณะ A, C ในคลาสอื่นเป็นต้น)
Ionu Ion Staicu

3
ไม่การใช้วิธีนี้คุณยังคงมีข้อดีของการมีลักษณะ คุณสามารถใช้คุณลักษณะนี้ใน IntClass แต่คุณสามารถใช้ในคลาสอื่น ๆ ได้ถ้าต้องการ ลักษณะจะไร้ประโยชน์ถ้ามันถูกใช้เฉพาะใน IntClass ในกรณีนั้นจะเป็นการดีกว่าถ้าวางเมธอด calc () ลงในคลาสนั้นโดยตรง
marcini

ทั้งหมดนี้ไม่ได้ผลสำหรับฉัน ScreenablePerson::save()ที่มีอยู่Candidateใช้Validatingลักษณะและขยายและทั้งสามชั้นเรียนมีScreenablePerson save()
Theodore R. Smith

1

ใช้ลักษณะอื่น:

trait ATrait {
    function calc($v) {
        return $v+1;
    }
}

class A {
    use ATrait;
}

trait BTrait {
    function calc($v) {
        $v++;
        return parent::calc($v);
    }
}

class B extends A {
    use BTrait;
}

print (new B())->calc(2); // should print 4
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.