Bagaimana cara mengesampingkan fungsi trait dan memanggilnya dari fungsi override?

370

Skenario:

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

Kode ini tidak berfungsi, dan saya tidak dapat menemukan cara untuk memanggil fungsi sifat seperti itu diwarisi. Aku mencoba menelepon self::calc($v), static::calc($v), parent::calc($v), A::calc($v)dan berikut ini:

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

class MyClass {
    use A {
        calc as traitcalc;
    }

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

Tidak ada yang berhasil.

Apakah ada cara untuk membuatnya berfungsi atau harus saya timpa sepenuhnya fungsi trait yang jauh lebih kompleks dari ini :)

Shu
sumber

Jawaban:

641

Yang terakhir Anda hampir sampai:

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

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

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

Sifatnya bukan kelas. Anda tidak dapat mengakses anggotanya secara langsung. Ini pada dasarnya hanya salin dan tempel otomatis ...

ircmaxell
sumber
20
hanya untuk memperjelas - setelah kelas Anda mendefinisikan metode yang sama, itu secara otomatis menimpa ciri itu. Ciri mengisi metode sebagai @ircmaxell menyebutkan ketika itu kosong.
Yehosef
2
@PhillipWhelan akan menyenangkan jika Anda dapat menambahkan informasi lebih lanjut tentang apa yang "tidak berfungsi seperti yang diharapkan". Ditulis seperti itu tidak banyak membantu dalam memahami perilaku salah seperti apa yang diharapkan, dan tidak meyakinkan kami bahwa ini bukan kesalahan sementara Anda. Mungkin ada beberapa pertanyaan SO tentang masalah yang sedang Anda bicarakan? (Akhirnya) Terima kasih.
Kamafeather
1
Masalahnya adalah semua metode lain dalam sifat tidak lagi disertakan.
Malhal
2
Hanya untuk referensi: Jika fungsi trait Anda statis, Anda dapat mengakses fungsinya dengan memanggilA::calc(1)
velop
4
Seperti yang disebutkan Phillip (saya pikir), bagaimana Anda melakukan ini untuk satu metode sifat sementara masih termasuk semua metode lain dari sifat yang sama seperti normal? Lebih disukai tanpa merujuk secara eksplisit setiap metode.
Gannet
14

Jika kelas mengimplementasikan metode secara langsung, itu tidak akan menggunakan versi ciri. Mungkin yang Anda pikirkan adalah:

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

Karena kelas anak tidak mengimplementasikan metode secara langsung, mereka akan terlebih dahulu menggunakan sifat tersebut jika ada yang menggunakan kelas induk.

Jika Anda ingin, sifat tersebut dapat menggunakan metode di kelas induk (dengan asumsi Anda tahu metode itu akan ada di sana) misalnya

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)

Anda juga dapat menyediakan cara untuk menimpa, tetapi masih mengakses metode sifat sebagai berikut:

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

Anda dapat melihatnya berfungsi di http://sandbox.onlinephpfunctions.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5

Yehosef
sumber
8

Pendekatan alternatif jika tertarik - dengan kelas menengah tambahan untuk menggunakan cara OOO normal. Ini menyederhanakan penggunaan dengan 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);
    }
}
Kartik V
sumber
6
Pendekatan ini akan mencukur setiap keuntungan yang Anda miliki dengan menggunakan traits. Seperti menggabungkan beberapa sifat dalam beberapa kelas (misalnya sifat A, B di kelas, sifat B, C, D di kelas lain, sifat A, C di kelas lain dan sebagainya)
Ionuț Staicu
3
Tidak, dengan menggunakan pendekatan ini Anda masih memiliki kelebihan memiliki sifat. Anda bisa menggunakan sifat ini di IntClass, tetapi Anda juga bisa menggunakannya di banyak kelas lain jika Anda mau. Trait tidak akan berguna, jika hanya digunakan di IntClass. Dalam hal ini, akan lebih baik untuk menempatkan metode calc () langsung di kelas itu.
marcini
Ini sama sekali tidak bekerja untuk saya. ScreenablePerson::save()ada, Candidatemenggunakan Validatingsifat dan meluas ScreenablePerson, dan ketiga kelas miliki save().
Theodore R. Smith
1

Menggunakan sifat lain:

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
tarkhov
sumber