Rilis terbaru: 1.0.0-beta
Pustaka PHP 5.4+ untuk mewakili hubungan kesetaraan dan strategi untuk hashing dan pengurutan nilai.
Hubungan kesetaraan berguna untuk menetapkan cara umum untuk membandingkan nilai sehubungan dengan persyaratan khusus domain, serta untuk mewakili kriteria khusus untuk membandingkan nilai ke dalam konteks terbatas, khususnya untuk digunakan dalam koleksi.
Kemampuan pelengkap, seperti hashing dan pengurutan, juga tercakup dalam perpustakaan ini, menjadikannya tambahan yang berharga untuk alat pengembangan Anda.
API didokumentasikan secara luas dalam kode sumber. Selain itu, versi HTML juga tersedia agar lebih nyaman dilihat di browser.
Gunakan Komposer untuk menginstal paket:
$ composer require phpcommon/comparison
Relasi adalah alat matematika untuk menggambarkan hubungan antar elemen himpunan. Relasi banyak digunakan dalam ilmu komputer, khususnya dalam database dan aplikasi penjadwalan.
Tidak seperti kebanyakan bahasa modern, PHP tidak mendukung kelebihan operator, sehingga secara historis dihindari sebagai pilihan desain. Dengan kata lain, tidak mungkin untuk mengesampingkan perilaku default operator asli, seperti sama, identik, lebih besar dari, kurang dari, dll. Misalnya, Java menyediakan antarmuka Sebanding, sedangkan Python menyediakan beberapa metode ajaib.
Pentingnya konsep tersebut menjadi lebih jelas dalam situasi di mana gagasan tentang kesetaraan atau keteraturan berbeda-beda menurut subjek perbandingan atau konteksnya, seperti yang dibahas pada bagian berikut.
Dalam matematika, relasi ekivalen adalah relasi biner yang bersifat refleksif , simetris , dan transitif . Namun dalam bidang komputasi, ada properti lain yang harus diperhatikan: konsistensi . Konsistensi berarti suatu relasi tidak boleh memberikan hasil yang berbeda untuk input yang sama.
Relasi ekivalen yang ada di mana-mana adalah relasi kesetaraan antar elemen himpunan mana pun. Contoh lainnya termasuk:
Untuk tujuan perpustakaan ini, relasi ekivalensi dapat bersifat generik atau spesifik tipe. Relasi tipe spesifik ditentukan dengan mengimplementasikan antarmuka Equatable
atau Equivalence
, sedangkan kesetaraan generik harus mengimplementasikan antarmuka yang terakhir.
Antarmuka Equatable
mendefinisikan metode umum yang diimplementasikan oleh kelas untuk membuat metode tipe spesifik untuk menentukan kesetaraan instance.
Sebagai ilustrasi, pertimbangkan kelas Money
, yang bertujuan untuk mewakili nilai moneter. Kelas ini adalah kandidat yang baik untuk mengimplementasikan antarmuka Equatable
, karena Money
adalah Objek Nilai, yaitu gagasan kesetaraan objek-objek tersebut tidak didasarkan pada identitas. Sebaliknya, dua contoh Money
adalah sama jika keduanya mempunyai nilai yang sama. Jadi, ketika Money::USD(5) === Money::USD(5)
mengembalikan false
, Money::USD(5)->equals(Money::USD(5))
mengembalikan true
.
Inilah kelas yang disebutkan sebelumnya:
final class Money implements Equatable
{
private $ amount ;
private $ currency ;
public function __construct ( $ amount , $ currency )
{
$ this -> amount = ( int ) $ amount ;
$ this -> currency = ( string ) $ currency ;
}
public function equals ( Equatable $ other )
{
if (! $ other instanceof self) {
return false ;
}
return $ this -> amount === $ other -> amount && $ this -> currency === $ other -> currency ;
}
}
Namun, ada banyak kasus di mana diperlukan cara yang tidak standar, atau eksternal , untuk membandingkan dua nilai. Mungkin, kasus penggunaan yang paling jelas untuk relasi kustom tersebut adalah untuk digunakan dengan koleksi, namun hal ini juga berguna untuk menyediakan kemampuan tersebut pada nilai skalar atau kelas yang sudah ada yang tidak dapat menyediakannya sendiri, karena merupakan milik paket pihak ketiga atau dibangun ke dalam PHP.
Misalkan Anda sedang mengembangkan perangkat lunak untuk membantu rumah sakit mengelola donor darah. Salah satu syaratnya adalah seorang perawat tidak boleh mengambil darah dari pendonor yang mempunyai golongan darah yang sama. Relasi untuk skenario ini akan terlihat seperti ini:
use PhpCommon Comparison Equivalence ;
class BloodGroupEquivalence implements Equivalence
{
public function equals ( Equatable $ other )
{
return get_class ( $ other ) === static ::class;
}
public function equivalent ( $ left , $ right )
{
if (! $ left instanceof Person) {
UnexpectedTypeException:: forType (Person::class, $ left );
}
if (! $ right instanceof Person) {
return false ;
}
return $ left -> getBloodType () === $ right -> getBloodType ();
}
}
Hubungan ini menentukan apakah dua orang mempunyai golongan darah yang sama:
$ equivalence = new BloodGroupEquivalence ();
$ donors = new BloodDonors ( $ equivalence );
$ james = new Person ( ' James ' , ' A ' );
$ john = new Person ( ' John ' , ' A ' );
// James and John are considered equivalent once they are of the same blood group
var_dump ( $ equivalence -> equivalent ( $ james , $ john )); // Outputs bool(true)
// Initially, none of them are present in the collection
var_dump ( $ volunteers -> contains ( $ james )); // Outputs bool(false)
var_dump ( $ volunteers -> contains ( $ john )); // Outputs bool(false)
// Add James to the set of volunteers
$ donors -> add ( $ james );
// Now, considering only the blood group of each donor for equality, both of
// them are considered present in the collection
$ donors -> contains ( $ james ); // Outputs bool(true)
$ donors -> contains ( $ john ); // Outputs bool(true)
Karena BloodGroupEquivalence
membentuk hubungan kesetaraan antara orang-orang berdasarkan golongan darahnya, segala upaya untuk menambahkan John ke dalam koleksi akan diabaikan, karena James sudah ada dan mereka memiliki golongan darah yang sama.
Ini mungkin terlihat agak rumit untuk persyaratan sederhana pada awalnya, namun dalam kasus nyata ini dapat digunakan untuk membandingkan kesetaraan antara golongan darah yang kompatibel, untuk membagi donor ke dalam beberapa kelompok.
Pustaka ini menyediakan beberapa hubungan kesetaraan umum sebagai bagian dari pustaka standar, seperti dijelaskan di bawah.
Membandingkan dua nilai identitas.
Relasi ini didasarkan pada operator yang identik. Dalam sebagian besar kasus, dua nilai dianggap setara jika keduanya memiliki tipe dan nilai yang sama, namun ada beberapa pengecualian:
NAN
tidak setara dengan nilai lainnya, termasuk dirinya sendiri.Tabel berikut merangkum bagaimana operan dari berbagai jenis dibandingkan:
$A $B | BATAL | Boolean | Bilangan bulat | Mengambang | Rangkaian | Sumber | Himpunan | Obyek |
---|---|---|---|---|---|---|---|---|
BATAL | true | false | false | false | false | false | false | false |
Boolean | false | $A === $B | false | false | false | false | false | false |
Bilangan bulat | false | false | $A === $B | false | false | false | false | false |
Mengambang | false | false | false | $A === $B | false | false | false | false |
Rangkaian | false | false | false | false | $A === $B | false | false | false |
Sumber | false | false | false | false | false | $A === $B | false | false |
Himpunan | false | false | false | false | false | false | $A === $B | false |
Obyek | false | false | false | false | false | false | false | $A === $B |
Membandingkan dua nilai kesetaraan.
Kesetaraan nilai berperilaku persis seperti kesetaraan identitas, hanya saja ia mendelegasikan perbandingan antara objek Equatable
dengan objek yang dibandingkan. Selain itu, hubungan eksternal dapat ditentukan untuk membandingkan nilai-nilai jenis tertentu. Hal ini berguna jika diinginkan untuk mengganti perilaku default untuk tipe tertentu, namun tetap mempertahankan yang lainnya. Hal ini juga berguna untuk mendefinisikan relasi objek kelas milik paket pihak ketiga atau dibangun ke dalam PHP.
Aturan berikut digunakan untuk menentukan apakah dua nilai dianggap setara:
NAN
tidak setara dengan nilai lainnya, termasuk dirinya sendiri.Equatable
dan ekspresi $left->equals($right)
dievaluasi menjadi true
.$relation->equivalent($left, $right)
dievaluasi menjadi true
.Tabel berikut merangkum bagaimana operan dari berbagai jenis dibandingkan:
$A $B | BATAL | Boolean | Bilangan bulat | Mengambang | Rangkaian | Sumber | Himpunan | Obyek | Dapat disamakan |
---|---|---|---|---|---|---|---|---|---|
BATAL | true | false | false | false | false | false | false | false | false |
Boolean | false | $A === $B | false | false | false | false | false | false | false |
Bilangan bulat | false | false | $A === $B | false | false | false | false | false | false |
Mengambang | false | false | false | $A === $B | false | false | false | false | false |
Rangkaian | false | false | false | false | $A === $B | false | false | false | false |
Sumber | false | false | false | false | false | $A === $B | false | false | false |
Himpunan | false | false | false | false | false | false | eq($A, $B) | false | false |
Obyek | false | false | false | false | false | false | false | $A === $B | false |
Dapat disamakan | false | false | false | false | false | false | false | false | $A‑>equals($B) |
Dimana eq()
menunjukkan fungsi yang membandingkan setiap pasangan entri yang bersesuaian secara rekursif, sesuai dengan aturan yang dijelaskan di atas.
Relasi ini juga menyediakan cara untuk mengesampingkan logika kesetaraan untuk kelas tertentu tanpa perlu membuat relasi baru. Misalnya, Anda ingin membandingkan instance DateTime
berdasarkan nilainya, namun tetap mempertahankan perilaku default untuk tipe lainnya. Hal ini dapat dicapai dengan menentukan relasi kustom yang akan digunakan setiap kali instance DateTime
dibandingkan dengan nilai lain:
use PhpCommon Comparison Hasher ValueHasher as ValueEquivalence ;
use PhpCommon Comparison Hasher DateTimeHasher as DateTimeEquivalence ;
use DateTime ;
$ relation = new ValueEquivalence ([
DateTime::class => new DateTimeEquivalence ()
]);
$ date = ' 2017-01-01 ' ;
$ timezone = new DateTimeZone ( ' Pacific/Nauru ' );
$ left = new DateTime ( $ date , $ timezone );
$ right = new DateTime ( $ date , $ timezone );
// Outputs bool(true)
var_dump ( $ relation -> equivalent ( $ left , $ right ));
Membandingkan dua nilai untuk kesetaraan semantik.
Kesetaraan semantik direncanakan untuk versi mendatang. Ini akan memungkinkan perbandingan nilai-nilai yang terlihat mirip secara semantik - meskipun tipenya berbeda. Hal ini serupa dengan cara kerja perbandingan longgar di PHP, namun dalam kondisi yang lebih ketat, sedemikian rupa sehingga sifat refleksivitas, simetri, dan transitivitas tetap berlaku.
Membandingkan dua instance DateTime
berdasarkan tanggal, waktu, dan zona waktunya.
Relasi ini menganggap dua instance DateTime
setara jika keduanya memiliki tanggal, waktu, dan zona waktu yang sama:
use PhpCommon Comparison Hasher IdentityHasher as IdentityEquivalence ;
use PhpCommon Comparison Hasher DateTimeHasher as DateTimeEquivalence ;
use DateTime ;
$ identity = new IdentityEquivalence ();
$ value = new DateTimeEquivalence ();
$ date = ' 2017-01-01 ' ;
$ timezone = new DateTimeZone ( ' Pacific/Nauru ' );
$ left = new DateTime ( $ date , $ timezone );
$ right = new DateTime ( $ date , $ timezone );
// Outputs bool(false)
var_dump ( $ identity -> equivalent ( $ left , $ right ));
// Outputs bool(true)
var_dump ( $ value -> equivalent ( $ left , $ right ));
Di PHP, kunci array hanya dapat direpresentasikan sebagai angka dan string. Namun, ada beberapa kasus di mana menyimpan tipe kompleks sebagai kunci sangat membantu. Ambil contoh kelas yang mewakili berbagai jenis angka atau string, seperti objek GMP, string Unicode, dll. Akan lebih mudah jika dapat menggunakan objek seperti itu sebagai kunci array juga.
Untuk mengisi kesenjangan ini, perpustakaan ini memperkenalkan antarmuka Hashable
dan Hasher
, yang menentukan protokol untuk menyediakan kode hash untuk nilai. Antarmuka ini tidak memerlukan pelaksana untuk menyediakan fungsi hashing yang sempurna. Artinya, dua nilai yang tidak setara mungkin memiliki kode hash yang sama. Namun, untuk menentukan apakah dua nilai dengan kode hash yang sama sebenarnya setara, konsep hashing dan kesetaraan harus digabungkan secara saling melengkapi. Ini menjelaskan mengapa Hasher
dan Hashable
masing-masing memperluas Equivalence
dan Equatable
.
Sebuah kata peringatan
Kode hash dimaksudkan untuk penyisipan dan pencarian yang efisien dalam koleksi yang didasarkan pada tabel hash dan untuk pemeriksaan ketidaksetaraan yang cepat. Kode hash bukanlah nilai permanen. Untuk alasan ini:
- Jangan membuat serial nilai kode hash atau menyimpannya dalam database.
- Jangan gunakan kode hash sebagai kunci untuk mengambil objek dari koleksi yang dikunci.
- Jangan mengirim kode hash ke seluruh domain atau proses aplikasi. Dalam beberapa kasus, kode hash dapat dihitung berdasarkan domain per proses atau per aplikasi.
- Jangan gunakan kode hash alih-alih nilai yang dikembalikan oleh fungsi hashing kriptografi jika Anda memerlukan hash yang kuat secara kriptografis.
- Jangan menguji kesetaraan kode hash untuk menentukan apakah dua objek sama, karena nilai yang tidak sama dapat memiliki kode hash yang identik.
Ada beberapa kasus di mana Anda mungkin perlu mendefinisikan logika hashing khusus untuk suatu kelas agar paling sesuai dengan kebutuhan Anda. Misalnya, Anda memiliki kelas Point untuk mewakili titik 2D:
namespace PhpCommon Comparison Equatable ;
final class Point implements Equatable
{
private $ x ;
private $ y ;
public function __construct ( $ x , $ y )
{
$ this -> x = ( int ) $ x ;
$ this -> y = ( int ) $ y ;
}
public function equals ( Equatable $ point )
{
if (! $ point instanceof Point) {
return false ;
}
return $ this -> x === $ point -> x && $ this -> y === $ point -> y ;
}
}
Suatu Point
menampung koordinat x dan y suatu titik. Menurut definisi kelasnya, dua titik dianggap sama jika mempunyai koordinat yang sama. Namun, jika Anda ingin menyimpan instance Point
dalam peta berbasis hash, misalnya karena Anda ingin mengaitkan koordinat ke label, maka Anda harus memastikan bahwa kelas Anda menghasilkan kode hash yang koheren dengan logika yang digunakan untuk menentukan kapan dua poin dianggap sama:
namespace PhpCommon Comparison Equatable ;
final class Point implements Hashable
{
private $ x ;
private $ y ;
public function __construct ( $ x , $ y )
{
$ this -> x = ( int ) $ x ;
$ this -> y = ( int ) $ y ;
}
public function equals ( Equatable $ point )
{
if (! $ point instanceof Point) {
return false ;
}
return $ this -> x === $ point -> x && $ this -> y === $ point -> y ;
}
public function getHash ()
{
return 37 * ( 31 + $ this -> $ x ) + $ this -> $ x ;
}
}
Dengan cara itu, metode getHash()
bekerja sesuai dengan metode equals()
, meskipun algoritma hashing mungkin tidak ideal. Penerapan algoritma yang efisien untuk pembuatan kode hash berada di luar cakupan panduan ini. Namun, disarankan untuk menggunakan algoritma cepat yang menghasilkan hasil yang cukup berbeda untuk nilai yang tidak sama, dan menggeser logika perbandingan yang berat ke Equatable::equals()
.
Perhatikan bahwa objek yang dapat di-hash harus tidak dapat diubah, atau Anda perlu menerapkan disiplin untuk tidak mengubahnya setelah objek tersebut digunakan dalam struktur berbasis hash.
Hasher menyediakan fungsionalitas hashing untuk tipe primitif dan objek kelas yang tidak mengimplementasikan Hashable
.
Metode hash()
yang diperkenalkan oleh antarmuka ini dimaksudkan untuk menyediakan sarana untuk melakukan pemeriksaan ketidaksetaraan dengan cepat serta penyisipan dan pencarian yang efisien dalam struktur data berbasis hash. Metode ini selalu koheren dengan equivalent()
, yang berarti bahwa untuk referensi apa pun $x
dan $y
, jika equivalent($x, $y)
, maka hash($x) === hash($y)
. Namun, jika equivalence($x, $y)
bernilai false
, hash($x) === hash($y)
mungkin masih benar. Oleh karena itu mengapa metode hash()
cocok untuk pemeriksaan ketidaksetaraan , tetapi tidak cocok untuk pemeriksaan kesetaraan .
Semua implementasi Equivalence
yang disertakan dalam perpustakaan ini juga menyediakan fungsionalitas hashing. Informasi lebih lanjut tentang bagaimana nilai di-hash dapat ditemukan dalam dokumentasi implementasi masing-masing.
Mengikuti logika yang sama dari konsep yang telah dibahas sebelumnya, Comparable
dan Comparator
adalah antarmuka yang masing-masing menyediakan strategi penyortiran alami dan khusus. Kedua antarmuka menentukan relasi keteraturan total, relasi yang refleksif , antisimetris , dan transitif .
Antarmuka ini menerapkan pemesanan total pada objek setiap kelas yang mengimplementasikannya. Pengurutan ini disebut sebagai pengurutan alami kelas, dan metode Comparable::compareTo()
disebut sebagai metode perbandingan alaminya .
Contoh berikut menunjukkan bagaimana suatu kelas dapat menentukan urutan alami dari instance-nya:
use PhpCommon Comparison UnexpectedTypeException ;
final class BigInteger implements Comparable
{
private $ value ;
public function __construct ( $ value )
{
$ this -> value = ( string ) $ value ;
}
public function compareTo ( Comparable $ other )
{
if (! $ other instanceof self) {
throw UnexpectedTypeException:: forType (BigInteger::class, $ other );
}
return bccomp ( $ this -> value , $ other -> value );
}
}
Tujuan dari Comparator
adalah untuk memungkinkan Anda menentukan satu atau lebih strategi perbandingan yang bukan merupakan strategi perbandingan alami untuk suatu kelas. Idealnya, Comparator
harus diimplementasikan oleh kelas yang berbeda dari kelas yang ditentukan strategi perbandingannya. Jika Anda ingin mendefinisikan strategi perbandingan alami untuk sebuah kelas, Anda bisa mengimplementasikan Comparable
sebagai gantinya.
Pembanding dapat diteruskan ke metode pengurutan suatu koleksi untuk memungkinkan kontrol yang tepat atas urutan pengurutannya. Ini juga dapat digunakan untuk mengontrol urutan struktur data tertentu, seperti kumpulan yang diurutkan atau peta yang diurutkan. Misalnya, perhatikan pembanding berikut yang mengurutkan string berdasarkan panjangnya:
use PhpCommon Comparison Comparator ;
class StringLengthComparator implements Comparator
{
public function compare ( $ left , $ right )
{
return strlen ( $ left ) <=> strlen ( $ right );
}
}
$ comparator = new StringLengthComparator ();
// Outputs int(-1)
var_dump ( $ comparator -> compare ( ' ab ' , ' a ' ));
Implementasi ini mewakili salah satu dari banyak cara yang mungkin untuk mengurutkan string. Strategi lain termasuk mengurutkan berdasarkan abjad, leksikografis, dll.
Silakan lihat CHANGELOG untuk informasi lebih lanjut tentang apa yang berubah baru-baru ini.
$ composer test
Lihat Dokumentasi Tes untuk lebih jelasnya.
Kontribusi pada paket ini selalu diterima!
Silakan lihat KONTRIBUSI dan PERILAKU untuk rinciannya.
Jika Anda menemukan masalah apa pun terkait keamanan, silakan kirim email ke [email protected] alih-alih menggunakan pelacak masalah.
Semua konten paket ini dilisensikan di bawah lisensi MIT.