Objek banyak digunakan hari ini, walaupun sukar untuk dibayangkan selepas keluaran PHP 5 pada tahun 2005. Pada masa itu, saya masih tahu sedikit tentang kemungkinan bahasa ini. Versi kelima PHP dibandingkan dengan versi sebelumnya, yang keempat, dan kelebihan utama keluaran baharu ialah model objek baharu yang sangat berkuasa. Dan hari ini, sepuluh tahun kemudian, kira-kira 90% daripada semua kod PHP mengandungi objek yang tidak berubah sejak PHP 5.0. Ini bercakap banyak tentang peranan yang dimainkan oleh pengenalan model objek, yang telah diperbaiki berkali-kali selama bertahun-tahun. Dalam jawatan ini saya ingin bercakap tentang bagaimana semuanya berfungsi "di bawah tudung". Supaya orang ramai memahami intipati proses - mengapa ia dilakukan dengan cara ini dan bukan sebaliknya - dan lebih baik, lebih menggunakan sepenuhnya kemungkinan bahasa. Saya juga akan menyentuh topik penggunaan memori oleh objek, termasuk perbandingan dengan tatasusunan yang setara (jika boleh).
Saya akan bercakap tentang PHP 5.4 sebagai contoh, dan perkara yang saya terangkan adalah benar untuk 5.5 dan 5.6, kerana model objek hampir tidak berubah di sana. Sila ambil perhatian bahawa dalam versi 5.3 perkara tidak begitu baik dari segi ciri dan prestasi keseluruhan.
Dalam PHP 7, yang masih dalam pembangunan aktif, model objek tidak banyak diolah semula, hanya perubahan kecil telah dibuat. Hanya kerana semuanya berfungsi dengan baik, dan yang terbaik adalah musuh yang baik. Ciri telah ditambahkan yang tidak menjejaskan teras, tetapi ini tidak akan dibincangkan di sini.
Sebagai demonstrasi, saya akan mulakan dengan penanda aras sintetik:
Kelas Foo ( awam $a = "foobarstring"; awam $b; awam $c = ["beberapa", "nilai"]; ) untuk ($i=0; $i<1000; $i++) {
$m = memory_get_usage();
${"var".$i} = new Foo;
echo memory_get_usage() - $m"\n";
}
Di sini, kelas mudah dengan tiga atribut diisytiharkan, dan kemudian 1000 objek kelas ini dicipta dalam gelung. Perhatikan cara memori digunakan dalam contoh ini: apabila anda mencipta objek kelas Foo dan pembolehubah, 262 bait memori timbunan PHP diperuntukkan untuk menyimpannya.
Mari gantikan objek dengan tatasusunan yang setara:
Untuk ($i=0; $i<1000; $i++) {
$m = memory_get_usage();
${"var".$i} = [["some", "values"], null, "foobarstring"];
echo memory_get_usage() - $m . "\n";
}
Dalam kes ini, elemen yang sama digunakan: tatasusunan itu sendiri, null dan rentetan pembolehubah foobarstring . Itu baru sahaja menggunakan 1160 bait memori, iaitu 4.4 kali lebih banyak.
Berikut adalah contoh lain:
$kelas =<<<"CL"
class Foo {
public $a = "foobarstring";
public $b;
public $c = ["some", "values"];
}
CL;
echo memory_get_usage() . "\n";
eval($class);
echo memory_get_usage() . "\n";
Memandangkan kelas diisytiharkan pada masa penyusunan, kami menggunakan pernyataan eval() untuk mengisytiharkan dan mengukur memori yang digunakan (menggunakan pengurus memori PHP). Walau bagaimanapun, tiada objek dicipta dalam kod ini. Jumlah memori yang terlibat (memori perbezaan) ialah 2216 bait.
Sekarang mari kita lihat bagaimana semuanya berfungsi dalam kedalaman PHP, menyandarkan pemerhatian praktikal dengan teori.
Struct _zend_class_entry ( char type; const char *name; zend_uint name_length; struct _zend_class_entry *ibu bapa; int refcount; zend_uint ce_flags; HashTable function_table; HashTable properties_info; zval **default_properties_table_standard_properties **default_properties_table_statik_tetap;zvalmevalable_table_default_properties_table;zvalmevalable_table_default_properties_table;zvalmevalable_table_default_properties_table;zvalmevalable_table_default_properties int default_static_members_count; union _zend_function *constructor; union _zend_function *destructor; union _zend_function *clone; union _zend_function *__get; union _zend_function *__set; union _zend_function *__unset; union _zend_function __function _zend_union *__call_static; union _zend_function __call *__tostring; union _zend_function *serialize_func; union _zend_function *unserialize_func; zend_class_iterator_funcs iterator_funcs; /* pengendali */ zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_object*class_type TSRMLS_object*); cuba *ce, zval *objek, int by_ref TSRMLS_DC); int (*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type TSRMLS_DC); /* kelas melaksanakan antara muka ini */ union _zend_function *(*get_static_method)(zend_class_entry *ce, char* method, int method_len TSRMLS_DC); /* panggil balik serializer */ int (*serialize)(zval *objek, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC); int (*unserialize)(zval **objek, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC); zend_class_entry **antara muka; zend_uint num_interfaces; zend_class_entry **sifat; zend_uint num_traits; zend_trait_alias **trait_aliases; zend_trait_precedence **trait_precedences; kesatuan ( struct ( const char *nama fail; zend_uint line_start; zend_uint line_end; const char *doc_comment; zend_uint doc_comment_len; ) pengguna; struct ( const struct _zend_function_entry *builtin_functions; struct _zend_module_en ) internal );
Saiz struktur, berdasarkan model LP64, adalah 568 bait. Iaitu, setiap kali PHP mengisytiharkan kelas, ia terpaksa mencipta zend_class_entry menggunakan lebih daripada setengah kilobait memori timbunan sahaja. Sudah tentu, perkara ini tidak terhad kepada ini: seperti yang anda perhatikan, struktur itu mengandungi banyak petunjuk, yang juga perlu diperuntukkan dalam ingatan. Iaitu, kelas itu sendiri menggunakan lebih banyak memori daripada semua objek yang kemudiannya dicipta daripadanya.
Antara lain, kelas mengandungi atribut (statik dan dinamik) serta kaedah. Semua ini juga memerlukan ingatan. Apabila bercakap tentang kaedah, sukar untuk mengira pergantungan yang tepat di sini, tetapi satu perkara adalah benar: lebih besar badan kaedah, lebih besar OPArraynya, yang bermaksud lebih banyak memori yang digunakan. Tambahkan pada pembolehubah statik yang boleh diisytiharkan dalam kaedah. Seterusnya datang sifat-sifat, kemudian ia juga akan diletakkan dalam ingatan. Jumlahnya bergantung pada nilai lalainya: integer akan mengambil sedikit masa, tetapi tatasusunan statik yang besar akan memakan banyak memori.
Satu lagi perkara penting yang perlu diketahui tentang zend_class_entry ialah komen PHP. Ia juga dikenali sebagai anotasi. Ini adalah pembolehubah rentetan (dalam C, char* buffer), yang juga perlu diletakkan dalam ingatan. Untuk C, yang tidak menggunakan Unicode, tidak seperti PHP, peraturannya sangat mudah: satu aksara = satu bait. Lebih banyak anotasi yang anda ada dalam kelas, lebih banyak memori akan digunakan selepas menghuraikan.
Medan doc_comment zend_class_entry mengandungi anotasi kelas. Kaedah dan atribut juga mempunyai medan ini.
Ini bermakna apabila PHP selesai memproses permintaan HTTP semasa, ia mengosongkan dan memusnahkan semua kelas pengguna sebagai persediaan untuk permintaan seterusnya. Pendekatan ini dikenali sebagai seni bina share nothing. Ini telah dibina ke dalam PHP sejak awal lagi, dan belum ada rancangan untuk mengubahnya.
Jadi, setiap kali permintaan dibentuk dan kelas dihuraikan, memori diperuntukkan untuk mereka. Selepas kelas digunakan, semua yang berkaitan dengannya dimusnahkan. Jadi pastikan anda menggunakan semua kelas yang diisytiharkan, jika tidak, memori akan terbuang. Gunakan pemuat auto, mereka menangguhkan penghuraian/pengisytiharan pada masa berjalan apabila PHP perlu menggunakan kelas. Walaupun memperlahankan pelaksanaan, autoloader menggunakan memori dengan baik kerana ia tidak akan berjalan sehingga kelas benar-benar memerlukannya.
Perkara berbeza dengan kelas dalaman. Ia disimpan secara kekal dalam ingatan, sama ada ia digunakan atau tidak. Iaitu, mereka hanya dimusnahkan apabila PHP itu sendiri berhenti berfungsi - selepas semua permintaan telah diproses (bermaksud SAPI web, sebagai contoh, PHP-FPM). Oleh itu, kelas dalaman lebih cekap daripada kelas pengguna (hanya atribut statik dimusnahkan pada akhir permintaan, tiada yang lain).
Jika (EG (full_tables_cleanup)) (zend_hash_reverse_apply (EG (function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC); zend_hash_reverse_apply (EG (class_table), (apply_func_t) clean_non_persistentRM_LS_verse_table_c) (apply_func_t) clean_non_persistentRM_LS_verse_full) (apply_non_persistentRM_LS_verse_table_c) (apply_non_persistentRM_LS_verse_table) (apply_non_persistentRMLS_verse_table) TSRMLS_CC); zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class TSRMLS_CC);)
Ambil perhatian bahawa walaupun dengan caching opcode seperti OPCache, kelas dibuat dan dimusnahkan pada setiap permintaan, seperti yang berlaku dengan kelas tersuai. OPCache hanya mempercepatkan kedua-dua proses ini.
Seperti yang anda lihat, jika anda mengaktifkan banyak sambungan PHP, setiap satunya mengisytiharkan banyak kelas, tetapi hanya menggunakan sebilangan kecil daripadanya, maka memori akan terbuang. Ingat bahawa sambungan PHP mengisytiharkan kelas pada masa permulaan PHP, walaupun permintaan seterusnya tidak akan menggunakan kelas tersebut. Oleh itu, tidak disyorkan untuk memastikan sambungan aktif jika ia tidak digunakan pada masa ini, jika tidak, anda akan membazirkan memori. Terutama jika sambungan ini mengisytiharkan banyak kelas - walaupun ia boleh menyumbat memori dengan sesuatu yang lain.
Antara muka BarException ( ) kelas MyException memanjangkan Exception melaksanakan BarException ( ) cuba ( $foo->bar(): ) catch (BarException $e) ( )
Ia tidak terlalu baik bahawa 912 bait digunakan di sini hanya untuk mengisytiharkan antara muka BarException.
$kelas =<<<"CL"
interface Bar { }
CL;
$m = memory_get_usage();
eval($class);
echo memory_get_usage() - $m . "\n"; /* 912 bytes */
Saya tidak mahu mengatakan bahawa ini buruk atau bodoh, saya tidak cuba menyalahkan sesiapa atau apa-apa. Saya hanya menarik perhatian anda pada saat ini. Dari segi struktur dalaman PHP, kelas, antara muka dan sifat digunakan dengan cara yang sama. Anda tidak boleh menambah atribut pada antara muka, penghurai atau pengkompil tidak akan membenarkannya. Walau bagaimanapun, struktur zend_class_entry tidak akan ke mana-mana, cuma beberapa medan, termasuk static_members_table , tidak akan menjadi penunjuk yang diperuntukkan memori. Mengisytiharkan kelas, sifat yang setara, atau antara muka yang setara akan memerlukan jumlah memori yang sama kerana kesemuanya menggunakan struktur yang sama.
Perkara menjadi lebih rumit apabila ia datang untuk menyuntik antara muka atau mewarisi kelas. Kemudian, semasa mengikat kelas, semuanya disalin daripada objek induk dan anak (sama ada kelas atau antara muka).
/* Kelas tunggal */ kes ZEND_DECLARE_CLASS: jika (do_bind_class(CG(active_op_array), opline, CG(class_table), 1 TSRMLS_CC) == NULL) ( return; ) table = CG(class_table); pecah;
Dalam kes pengisytiharan kelas yang mudah, kami menjalankan do_bind_class() . Fungsi ini hanya mendaftarkan kelas yang layak sepenuhnya dalam jadual kelas untuk kegunaan kemudian pada masa jalan, dan juga menyemak kemungkinan kaedah abstrak:
batalkan zend_verify_abstract_class(zend_class_entry *ce TSRMLS_DC) ( zend_abstract_info ai; jika ((ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) && !(ce->ce_flags & ZEND_ACC_EXPLIC_ASS_ABSTR_saiz(>memfaksi_CLASS_ABSTRA)(membuat_jadual&0STR) , (apply_func_arg_t) zend_verify_abstract_class_function, &ai TSRMLS_CC); jika (ai.cnt) ( zend_error(E_ERROR, "Kelas %s mengandungi %d kaedah abstrak%s dan oleh itu mesti diisytiharkan abstrak atau melaksanakan kaedah yang selebihnya (" MAX_ABSTRFACT_IN_FOAB_INFACT_MAX_ABSTRFACT_IN_AB_FACT ", ce->name, ai.cnt, ai.cnt > 1 ? "s" : "", DISPLAY_ABSTRACT_FN(0), DISPLAY_ABSTRACT_FN(1), DISPLAY_ABSTRACT_FN(2)); ) ) )
Tiada apa-apa untuk ditambah di sini, kes mudah.
Apabila mengikat kelas yang melaksanakan antara muka, anda perlu melakukan perkara berikut:
ZEND_API batal zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) ( /* ... */ ) else ( if (ce->num_interfaces >= current_iface_num) ( if (ce->type == ZEND_INTERNAL_CLASS) ( ce ->interfaces = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num)); ) else ( ce->interfaces = (zend_class_entry **) eralloc(ce->interfaces, sizeof (zend_class_entry * ) * (++current_iface_num)); ) ) ce->antara muka = iface; zend_hash_merge_ex(&ce->constants_table, &iface->constants_table, (copy_ctor_func_t) zval_add_ref, sizeof(zval *), (merge_checker_funccheck_t) do_inherit_constant
Beri perhatian kepada perbezaan antara kelas dalaman dan pengguna. Yang pertama akan menggunakan realloc() untuk peruntukan memori, yang terakhir akan menggunakan eralloc() . realloc() memperuntukkan memori "kekal", manakala eralloc() beroperasi pada memori "diperuntukkan atas permintaan".
Anda boleh melihat bahawa apabila dua jadual malar (antara muka-1 dan kelas-1) digabungkan, mereka melakukannya menggunakan panggilan balik zval_add_ref. Ia tidak menyalin pemalar dari satu jadual ke jadual lain, tetapi berkongsi penunjuknya, hanya menambah bilangan rujukan.
Untuk setiap jadual fungsi (kaedah), do_inherit_method digunakan:
Statik void do_inherit_method(zend_function *function) ( function_add_ref(function); ) ZEND_API void function_add_ref(zend_function *function) ( jika (function->type == ZEND_USER_FUNCTION) ( zend_op_array *op_array = &function->op_array;->ref )++; jika (op_array->static_variables) ( HashTable *static_variables = op_array->static_variables; zval *tmp_zval; ALLOC_HASHTABLE(op_array->static_variables); zend_hash_init(op_array->static_variables_static_zendVAL_variables, NUD_static_variables, NUD_static_variables ); zend_hash_copy(op_array->static_variables, static_variables, (copy_ctor_func_t) zval_add_ref, (void *) &tmp_zval, sizeof(zval *)); ) op_array->run_time_cache = NULL; ) )
Kiraan semula ditambahkan pada OPArray fungsi, dan semua pembolehubah statik yang mungkin diisytiharkan dalam fungsi (ini adalah kaedah) disalin menggunakan zval_add_ref . Oleh itu, keseluruhan proses penyalinan memerlukan banyak sumber pengkomputeran, kerana terdapat banyak gelung dan semakan yang terlibat. Tetapi ingatan digunakan sedikit. Malangnya, pengikatan antara muka adalah masa jalan sepenuhnya hari ini, dan anda akan merasakannya pada setiap permintaan. Mungkin pemaju akan mengubahnya tidak lama lagi.
Bagi warisan, di sini, pada dasarnya, semuanya sama seperti ketika melaksanakan antara muka. Hanya lebih ramai "peserta" yang terlibat. Tetapi saya ingin ambil perhatian bahawa jika PHP sudah mengetahui tentang kelas, maka pengikatan dilakukan pada masa penyusunan, dan jika ia tidak tahu, maka pada masa larian. Jadi lebih baik untuk mengisytiharkan seperti ini:
/* baik */ kelas A ( ) kelas B memanjangkan A ( )
bukannya:
/* teruk */ kelas B memanjangkan A ( ) kelas A ( )
Dengan cara ini, prosedur mengikat kelas rutin boleh membawa kepada tingkah laku yang sangat pelik:
/* ini berfungsi */ kelas B memanjangkan A ( ) kelas A ( )
/* ini bukan */ Ralat maut: Kelas "B" tidak ditemui */ kelas C melanjutkan B ( ) kelas B melanjutkan A ( ) kelas A ( )
Dalam varian pertama, pengikatan kelas B ditangguhkan pada masa larian, kerana apabila pengkompil mencapai pengisytiharan kelas ini, ia belum mengetahui apa-apa tentang kelas A. Apabila pelaksanaan bermula, kelas A terikat tanpa persoalan, kerana ia sudah disusun, menjadi satu kelas. Dalam kes kedua, semuanya berbeza. Pengikatan kelas C ditangguhkan pada masa jalan kerana pengkompil belum mengetahui apa-apa tentang B apabila cuba menyusunnya. Tetapi apabila kelas C terikat pada masa larian, ia mencari B, yang tidak wujud kerana ia tidak disusun kerana B ialah penambahan. Mesej "Kelas B tidak wujud" keluar.
Sekarang mari kita bercakap tentang objek. Bab pertama menunjukkan bahawa penciptaan objek "klasik" (kelas yang ditentukan pengguna "klasik") memerlukan memori yang sangat sedikit, kira-kira 200 bait. Ini semua tentang kelas. Penyusunan kelas selanjutnya juga menggunakan memori, tetapi ini adalah lebih baik, kerana lebih sedikit bait diperlukan untuk mencipta objek tunggal. Malah, objek itu adalah satu set kecil struktur kecil.
Typedef _zend_function kesatuan (jenis zend_uchar; struct (jenis zend_uchar; char const * FUNCTION_NAME; zend_class_entry * skop; fn_flags zend_uint; kesatuan _zend_function * prototaip; num_args zend_uint; required_num_args zend_uint; * zend_arg_info arg_info;) bersama; op_array zend_op_array; zend_internal_function internal_function;) zend_function ;
Apabila objek cuba memanggil kaedah, enjin lalai melihat dalam jadual nilai untuk fungsi kelas objek. Jika kaedah tidak wujud, maka __call() dipanggil. Keterlihatan juga disemak - awam/dilindungi/swasta - bergantung pada tindakan berikut yang diambil:
Kesatuan statik _zend_function *zend_std_get_method(zval **object_ptr, char *method_name, int method_len, const zend_literal *key TSRMLS_DC) ( zend_function *fbc; zval *objek = *objek_ptr; zend_objek *zobj = Zob_nilai *zobj = Zob_nilai_zobj lc_method_name; ALLOCA_FLAG(use_timbunan) jika (EXPECTED(key!= NULL)) ( lc_method_name = Z_STRVAL(key->constant); hash_value = key->hash_value; ) else ( lc_method_name = do_alloca(method_len+1, use_tolo_copy_len+1, zenwerdhea_copy); lc_method_name, method_name, method_len); hash_value = zend_hash_func(lc_method_name, method_len+1); ) /* Jika kaedah tidak ditemui */ jika (TIDAK DIJANGKA(zend_hash_quick_find(&zobj->ce->function_table, lc_len_value_name, kaedah_nilai_hash, kaedah_nilai_hash, hash , (void **)&fbc) == FAILURE)) ( if (UNEXPECTED(!key)) ( free_alloca(lc_method_name, use_heap); ) if (zobj->ce->__call) ( /* jika kelas telah mendapat __call() pengendali */ return zend_get_user_call_function(zobj->ce, method_name, method_len); /* panggil __call() pengendali */ ) else ( return NULL; /* else return NULL, yang berkemungkinan akan membawa kepada ralat maut: method not found */ ) ) /* Semak tahap akses */ if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) ( zend_function *updated_fbc; updated_fbc = zend_check_private_int(fbc , Z_OBJ_HANDLER_P(objek, get_class_entry)(objek TSRMLS_CC), lc_method_name, method_len, hash_value TSRMLS_CC); if (EXPECTED(updated_fbc != NULL)) ( fbc = updated_fbc; ) else ( if (zobj->ce->__call) = zend_get_user_call_function(zobj->ce, method_name, method_len); ) else ( zend_error_noreturn(E_ERROR, "Panggil ke %s kaedah %s::%s() daripada konteks "%s"", zend_visibility_string(fbc->common.fn_flags ), ZEND_FN_SCOPE_NAME(fbc), method_name, EG(skop) ? EG(skop)->nama: ""); ) ) ) lain ( /* ... ... */ )
Anda mungkin melihat satu perkara yang menarik, lihat pada baris pertama:
Jika (EXPECTED(key!= NULL)) ( lc_method_name = Z_STRVAL(key->constant); hash_value = key->hash_value; ) else ( lc_method_name = do_alloca(method_len+1, use_heap); /* Buat zend_copy_str_tolower(dest, src, src_length); */ zend_str_tolower_copy(lc_method_name, method_name, method_len); hash_value = zend_hash_func(lc_method_name, method_len+1); )
Ini adalah manifestasi ketidakpekaan huruf PHP. Sistem mesti terlebih dahulu menggunakan huruf kecil setiap fungsi (zend_str_tolower_copy()) sebelum memanggilnya. Bukan semua orang, tetapi yang terdapat pernyataan if. Pembolehubah utama menghalang pelaksanaan fungsi huruf kecil (bahagian lain) - ini adalah sebahagian daripada pengoptimuman PHP yang dilaksanakan dalam versi 5.4. Jika panggilan kaedah tidak dinamik, maka pengkompil telah pun mengira key , dan kurang sumber yang terbuang pada masa jalan.
Kelas Foo ( fungsi awam BAR() ( ) ) $a = Foo baharu; $b = "bar"; $a->bar(); /* panggilan statik: baik */ $a->$b(); /* panggilan dinamik: buruk */
Semasa penyusunan fungsi/kaedah, ia segera ditukar kepada huruf kecil. Fungsi BAR() di atas ditukar kepada bar() oleh pengkompil apabila kaedah itu ditambahkan pada kelas dan jadual fungsi.
Dalam contoh di atas, panggilan pertama adalah statik: pengkompil telah mengira kunci untuk rentetan "bar", dan apabila tiba masanya untuk memanggil kaedah, ia mempunyai lebih sedikit kerja untuk dilakukan. Panggilan kedua sudah dinamik, pengkompil tidak tahu apa-apa tentang "$b", ia tidak boleh mengira kunci untuk memanggil kaedah. Kemudian, pada masa jalanan, kita perlu menukar rentetan kepada huruf kecil dan mengira cincangnya (zend_hash_func ()), yang tidak mempunyai kesan terbaik pada prestasi.
Setakat __call() berkenaan, ia bukanlah pencapaian yang banyak. Walau bagaimanapun, dalam kes ini, lebih banyak sumber terbuang daripada semasa memanggil fungsi sedia ada.
Seperti yang anda lihat, apabila berbilang objek dari kelas yang sama dicipta, enjin mengubah hala setiap atribut ke penunjuk yang sama seperti dalam kes atribut kelas. Sepanjang hayatnya, kelas menyimpan bukan sahaja atribut statiknya sendiri, tetapi juga atribut objek. Dalam kes kelas dalaman, sepanjang masa PHP berjalan. Mencipta objek tidak melibatkan penciptaan atributnya, jadi ini adalah pendekatan yang agak cepat dan menjimatkan. Hanya apabila objek akan menukar salah satu atributnya, enjin mencipta yang baharu untuk berbuat demikian, dengan mengandaikan anda menukar atribut $a bagi Foo #2:
Jadi apabila kita mencipta objek, kita "hanya" mencipta struktur zend_object 32-bait:
Typedef struct _zend_object ( zend_class_entry *ce; HashTable *properties; zval **properties_table; HashTable *guards; /* melindungi daripada __get/__set ... rekursi */ ) zend_object;
Struktur ini ditambah pada stor objek. Dan itu, seterusnya, adalah struktur zend_object_store. Ini ialah pendaftaran objek global enjin Zend - tempat di mana semua objek dikumpulkan dan disimpan dalam satu contoh:
ZEND_API zend_object_value zend_objects_new(zend_object **objek, zend_class_entry *class_type TSRMLS_DC) ( zend_object_value retval; *objek = emalloc(sizeof(zend_object);(*objek)->ce = class_object =NUIP;>ce = class_object *objek)->properties_table = NULL; (*objek)->guards = NULL; /* Tambahkan objek ke dalam stor */ retval.handle = zend_objects_store_put(*objek, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_object_free_object) .pengendali = &std_object_handlers; kembalikan semula; )
Seterusnya, enjin mencipta vektor ciri objek kami:
ZEND_API batal object_properties_init(zend_object *objek, zend_class_entry *class_type) ( int i; if (class_type->default_properties_count) ( object->properties_table = emalloc(sizeof(zval*) * class_type->default_properties =_count);< class_type->default_properties_count; i++) ( object->properties_table[i] = class_type->default_properties_table[i]; if (class_type->default_properties_table[i]) ( #if ZTS ALLOC_ZVAL(objek->properties_table[i]); MAKE_COPY_ZVAL(&class_jenis->default_properties [i], object->properties_table[i]); #else Z_ADDREF_P(object->properties_table[i]); #endif ) ) object->properties = NULL; ) )
Seperti yang anda lihat, kami telah memperuntukkan jadual/vektor dalam ingatan (seperti dalam bahasa C) untuk zval* , berdasarkan sifat yang diisytiharkan bagi kelas objek. Dalam kes PHP bukan thread-safe, kami hanya menambah pengiraan semula pada atribut, dan jika thread-safe Zend (ZTS, Zend thread safety) digunakan, maka kami perlu menyalin sepenuhnya zval . Ini adalah salah satu daripada banyak contoh yang menunjukkan prestasi lemah dan penggunaan sumber mod ZTS yang tinggi berbanding PHP bukan ZTS.
Anda mungkin mempunyai dua soalan:
Kedua-dua soalan dijawab oleh zend_property_info .
Typedef struct _zend_property_info ( zend_uint flags; const char *name; int name_length; ulong h; int offset; const char *doc_comment; int doc_comment_len; zend_class_entry *ce; ) zend_property_info;
Setiap diisytiharkan atribut (property) objek kami mempunyai maklumat harta yang sepadan ditambah pada medan property_info dalam zend_class_entry . Ini dilakukan pada masa penyusunan untuk atribut yang diisytiharkan dalam kelas:
Kelas Foo ( awam $a = "foo"; dilindungi $b; persendirian $c; ) struct _zend_class_entry ( /* ... ... */ HashTable function_table; HashTable properties_info; /* berikut ialah maklumat sifat tentang $a, $b dan $c */ zval **default_properties_table; /* dan di sini, kita akan menemui $a, $b dan $c dengan nilai lalai */ int default_properties_count; /* ini akan mempunyai nilai 3: 3 sifat */ /* ... ... */
properties_infos ialah jadual yang memberitahu objek tentang kewujudan atribut yang diminta. Dan jika ia wujud, ia melepasi nombor indeksnya dalam tatasusunan object->properties. Kemudian kami menyemak keterlihatan dan akses kepada skop (awam/dilindungi/swasta).
Jika atribut tidak wujud dan kita perlu menulis kepadanya, maka kita boleh cuba memanggil __set() . Sekiranya berlaku kegagalan, kami mencipta atribut dinamik yang akan disimpan dalam medan object->property_table.
Property_info = zend_get_property_info_quick(zobj->ce, ahli, (zobj->ce->__set != NULL), kunci TSRMLS_CC); jika (JANGKAAN(info_harta != NULL) && ((JANGKAAN((info_harta->bendera & ZEND_ACC_STATIC)) == 0) && info_harta->offset >= 0) ?(zobj->sifat ? ((pembolehubah_ptr = (zval**) )zobj->properties_table) != NULL) : (*(variable_ptr = &zobj->properties_table) != NULL)) : (EXPECTED(zobj->properties != NULL) && EXPECTED(zend_hash_quick_find(zobj->properties, property_info >nama, info_properti->panjang_nama+1, info_properti->h, (kosong **) &variable_ptr) == SUCCESS)))) ( /* ... ... */ ) else ( zend_guard *guard = NULL; if (zobj->ce->__set && /* kelas mempunyai __set() ? */ zend_get_property_guard(zobj, property_info, ahli, &guard) == SUCCESS && !guard->in_set) ( Z_ADDREF_P(objek); if (PZVAL_IS_REF( objek)) ( SEPARATE_ZVAL(&objek); ) guard->in_set = 1; /* halang tetapan bulat */ jika (zend_std_call_setter(objek, ahli, nilai TSRMLS_CC) != SUCCESS) ( /* call __set() */ ) guard ->in_set = 0; zval_ptr_dtor(&objek); /* ... ... */
Selagi anda tidak menulis pada objek, penggunaan memorinya tidak berubah. Selepas ditulis, ia mengambil lebih banyak ruang (sehingga ia dimusnahkan), kerana ia mengandungi semua atribut yang ditulis kepadanya.
Fungsi foo($var) ( $var = 42; ) $o = MyClass baharu; foo($o); var_dump($o); /* ini masih objek, bukan integer 42 */
Semua orang kini akan mengatakan bahawa "dalam PHP 5, objek adalah rujukan," malah manual rasmi menyebut ini. Secara teknikal, ini benar-benar palsu. Walau bagaimanapun, objek boleh berkelakuan dengan cara yang sama seperti rujukan. Sebagai contoh, apabila anda menghantar pembolehubah yang merupakan objek kepada fungsi, fungsi itu boleh mengubah suai objek yang sama.
Ini kerana zval yang diluluskan sebagai fungsi tidak melepasi objek itu sendiri, tetapi pengecam uniknya digunakan untuk mencari stor objek kongsi. Dan hasilnya adalah sama. Adalah mungkin untuk memperuntukkan tiga zval berbeza dalam ingatan, dan semuanya boleh mengandungi deskriptor objek yang sama.
Objek(Kelas Saya)#1 (0) ( ) /* #1 ialah pemegang objek (nombor), ia unik */
Zend_object_store menyediakan storan objek sekali sahaja. Satu-satunya cara untuk menulis ke kedai ialah mencipta objek baharu dengan kata kunci baharu, fungsi unserialize(), API pantulan atau kata kunci klon. Tiada operasi lain akan membenarkan anda menduplikasi atau mencipta objek baharu dalam repositori.
Typedef struct _zend_objects_store ( zend_object_store_bucket *object_buckets; zend_uint top; saiz zend_uint; int free_list_head; ) zend_objects_store; typedef struct _zend_object_store_bucket (zend_bool destructor_called; zend_bool sah; apply_count zend_uchar; kesatuan _store_bucket (struct _store_object (tidak sah * objek; dtor zend_objects_store_dtor_t; FREE_STORAGE kosong zend_objects_free_object_storage_t; klon zend_objects_store_clone_t; zend_object_handlers const * Pengendali; refcount zend_uint; gc_root_buffer * buffered;) obj; struct (int seterusnya; ) senarai_percuma; ) baldi; ) zend_object_store_bucket;
Pertama, pengkompil tidak akan membenarkan penulisan ke $this . Untuk melakukan ini, ia akan menyemak setiap tugasan yang anda buat, dan jika ia menemui tugasan kepada $this , ralat maut akan dilemparkan.
/* ... ... */ jika (opline_is_fetch_this(last_op TSRMLS_CC)) ( zend_error(E_COMPILE_ERROR, "Tidak boleh menetapkan semula $ini"); ) /* ... ... */ zend_bool statik opline_is_fetch_this(const zend_op *opline TSRMLS_DC) ( jika ((opline->opcode == ZEND_FETCH_W) && (opline->op1_type == IS_CONST) && (Z_TYPE(CONSTANT(opline->op1.constant)) == IS_STRING) && ((opline-> nilai_lanjutan & ZEND_FETCH_STATIC_MEMBER) != ZEND_FETCH_STATIC_MEMBER) && (Z_HASH_P(&CONSTANT(opline->op1.constant)) == THIS_HASHVAL) && (Z_STRLEN(CONSTANT(opline->op1.constant))" == 1)) && !memcmp(Z_STRVAL(CONSTANT(opline->op1.constant)), "this", sizeof("this"))) ( return 1; ) else ( return 0; ) )
Bagaimanakah $ini diuruskan? Penggunaannya hanya boleh dilakukan di dalam kaedah, di mana pengkompil menjana OPCode INIT_METHOD_CALL semasa panggilan. Enjin mengetahui siapa yang memanggil kaedah, dalam kes $a->foo() ia adalah $a . Selepas itu, nilai $a diambil dan disimpan dalam ruang kongsi. Seterusnya, kaedah dipanggil menggunakan OPCode DO_FCALL . Pada ketika ini, nilai yang disimpan diambil semula (objek memanggil kaedah) dan diberikan kepada penunjuk $this dalaman global - EG(This) .
Jika (fbc->taip == ZEND_USER_FUNCTION || fbc->common.scope) ( should_change_scope = 1; EX(current_this) = EG(This); EX(current_scope) = EG(skop); EX(current_called_scope) = EG( called_scope); EG(This) = EX(objek); /* ambil objek yang disediakan dalam opcode INIT_METHOD sebelumnya dan kesannya kepada EG(This) */ EG(skop) = (fbc->type == ZEND_USER_FUNCTION || !EX (objek)) ?fbc->common.scope: NULL; EG(called_scope) = EX(call)->called_scope; )
Sekarang, apabila kaedah dipanggil, jika dalam badannya anda menggunakan $this untuk bertindak pada pembolehubah atau memanggil kaedah (cth $this->a = 8), ia akan menghasilkan OPCode ZEND_ASSIGN_OBJ , yang seterusnya akan mengambil semula $ini daripada EG(This) .
Zval zend_always_inline statik **_get_obj_zval_ptr_ptr_unused(TSRMLS_D) ( if (EXPECTED(EG(This) != NULL)) ( return &EG(This); ) else ( zend_error_noreturn(E_ERROR, "Menggunakan $this apabila tidak dalam konteks objek"); NULL; ))
Sekiranya anda menggunakan $this untuk memanggil kaedah (contohnya, $this->foo()) atau menghantarnya ke panggilan fungsi lain ($this->foo($this);), maka enjin akan cuba untuk ekstrak $ini daripada jadual aksara semasa, seperti yang dilakukannya untuk setiap pembolehubah piawai. Tetapi di sini, penyediaan khas dibuat semasa penciptaan bingkai tindanan fungsi semasa:
Jika (op_array->this_var != -1 && EG(This)) ( Z_ADDREF_P(EG(This)); if (!EG(active_symbol_table)) ( EX_CV(op_array->this_var) = (zval **) EX_CV_NUM(execute_data , op_array->last_var + op_array->this_var); *EX_CV(op_array->this_var) = EG(This); ) else ( if (zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG (This), sizeof(zval *), (void **) EX_CV_NUM(execute_data, op_array->this_var))==FAILURE) ( Z_DELREF_P(EG(This)); ) ) )
Apabila kita memanggil kaedah, enjin menukar skop:
Jika (fbc->taip == ZEND_USER_FUNCTION || fbc->common.scope) ( /* ... ... */ Cth(skop) = (fbc->type == ZEND_USER_FUNCTION || !EX(objek)) ?fbc->common.scope: NULL; /* ... ... */ )
EG(skop) adalah jenis zend_class_entry . Ini ialah kelas yang memiliki kaedah yang anda minta. Dan ia akan digunakan untuk sebarang operasi pada objek yang akan anda lakukan dalam badan kaedah selepas pemeriksaan keterlihatan oleh enjin:
Statik zend_always_inline int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce TSRMLS_DC) ( suis (property_info->flags & ZEND_ACC_PPP_MASK) ( case ZEND_ACC_TEGED_PROCTED=(;_property_property_PUBLIC_DECTEDA):(;_property_property_PUBLICEND_PROCTED:(;_property_PROCED_DE_PROCTED: case ZEND_ACC_PROTYEND_PUBLIC: return_propertyEND_DECTEDA): (skop) || property_info->ce == EG(skop)) && EG(skop)) ( return 1; ) else ( return 0; ) break; ) return 0; )
Beginilah cara anda boleh mengakses ahli peribadi objek yang bukan milik anda tetapi kanak-kanak skop semasa anda:
Kelas A ( persendirian $a; fungsi awam foo(A $obj) ( $this->a = "foo"; $obj->a = "bar"; /* ya, ini mungkin */ ) ) $a = A baru; $b = baruA; $a->foo($b);
Ciri ini telah menyebabkan sejumlah besar laporan pepijat daripada pembangun. Tetapi ini adalah bagaimana model objek dalam PHP berfungsi - sebenarnya, kami menetapkan skop bukan berdasarkan objek, tetapi pada kelas. Dalam kes kelas "Foo" kami, anda boleh bekerja dengan mana-mana Foo peribadi mana-mana Foo lain, seperti yang ditunjukkan di atas.
Kelas Foo ( public function __destruct() ( echo "byebye foo"; ) ) $f = new Foo; thisfunctiondoesntexist(); /* ralat maut, fungsi tidak ditemui, pemusnah Foo TIDAK dijalankan */
Bagaimana pula dengan susunan pemusnah dipanggil jika mereka dipanggil juga? Jawapannya jelas kelihatan dalam kod:
Void shutdown_destructors(TSRMLS_D) ( zend_try ( int symbols; do ( symbols = zend_hash_num_elements(&EG(symbol_table)); zend_hash_reverse_apply(&EG(symbol_table), (apply_func_t) zval_call_destructor TSRMLS_s_table ! ); zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC); ) zend_catch ( /* jika kita tidak dapat memusnahkan dengan bersih, tandakan semua objek sebagai musnah juga */ zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_end_call ) dalam TSRMLS_end_call static); (zval **zv TSRMLS_DC) ( jika (Z_TYPE_PP(zv) == IS_OBJECT && Z_REFCOUNT_PP(zv) == 1) ( kembalikan ZEND_HASH_APPLY_REMOVE; ) lain ( kembalikan ZEND_HASH_APPLY_KEEP; ) )
Ini menunjukkan tiga peringkat memanggil pemusnah:
Kelas Foo ( fungsi awam __destruct() ( var_dump("Foo musnah"); ) )
class Bar ( public function __destruct() ( var_dump("destroyed Bar"); ) )
Contoh satu:
$a = Foo baharu; $b = Bar baharu; "bar musnah" "foo musnah"
Contoh yang sama:
$a = Bar baharu; $b = Foo baharu; "Foo musnah" "Bar musnah"
Contoh kedua:
$a = Bar baharu; $b = Foo baharu; $c = $b; /* penambahan kiraan semula objek $b */ "Bar musnah" "Foo musnah"
Contoh tiga:
Class Foo ( public function __destruct() ( var_dump("destroyed Foo"); die();) ) /* perhatikan die() di sini */ class Bar ( public function __destruct() ( var_dump("destroyed Bar"); ) ) $a = Foo baharu; $a2 = $a; $b = Bar baharu; $b2 = $b; Foo
Prosedur ini dipilih atas sebab tertentu. Tetapi jika ia tidak sesuai dengan anda, maka lebih baik untuk memusnahkan objek anda sendiri. Ini adalah satu-satunya cara untuk mengawal panggilan __destruct(). Jika anda meninggalkan PHP untuk melakukannya untuk anda, maka jangan membenci hasil kerjanya kemudian. Anda sentiasa mempunyai pilihan untuk memusnahkan objek anda secara manual untuk mempunyai kawalan penuh ke atas pesanan.
PHP tidak memanggil pemusnah sekiranya berlaku sebarang ralat yang membawa maut. Hakikatnya ialah dalam kes ini Zend tidak stabil, dan memanggil pemusnah membawa kepada pelaksanaan kod pengguna, yang boleh mengakses petunjuk yang salah dan, akibatnya, PHP ranap. Adalah lebih baik untuk memastikan sistem stabil - itulah sebabnya panggilan pemusnah disekat. Mungkin sesuatu akan berubah dalam PHP 7.
Bagi rekursi, ia dilindungi dengan lemah dalam PHP, dan ini hanya terpakai untuk __get() dan __set() . Jika anda memusnahkan objek anda di suatu tempat dalam bingkai tindanan pemusnah, anda akan berakhir dalam gelung rekursif tak terhingga yang akan memakan semua sumber tindanan proses anda (biasanya 8 kB, ulimit -s) dan memecahkan PHP.
Class Foo ( public function __destruct() ( new Foo; ) /* anda akan ranap */ )
Ringkasnya, jangan percaya pemusnah untuk kod kritikal, seperti mengurus mekanisme kunci, kerana PHP mungkin tidak memanggil pemusnah atau memanggilnya dalam urutan yang tidak terkawal. Jika, bagaimanapun, kod penting diproses oleh pemusnah, maka sekurang-kurangnya mengawal kitaran hayat objek sendiri. PHP akan memanggil pemusnah apabila kiraan semula objek anda menurun kepada sifar, yang bermaksud objek itu tidak lagi digunakan dan boleh dimusnahkan dengan selamat.
JavaScript disekat dalam penyemak imbas anda. Benarkan JavaScript untuk tapak ini berfungsi!
Objek ialah satu set pembolehubah khas - harta benda dan fungsi khas - kaedah. Apa yang dipanggil pembolehubah dalam pengaturcaraan prosedur dipanggil harta dalam OOP. Apa yang dipanggil fungsi dalam pengaturcaraan prosedur dipanggil kaedah kelas dalam OOP. Objek yang dicipta daripada kelas dipanggil contoh kelas, atau hanya objek.
Mengakses sifat daripada kaedah hanya melalui kata kunci $this: $this->name; (perhatikan kekurangan tanda dolar di hadapan nama) Memanggil kaedah lain di dalam kaedah juga melalui $this: $this->foo(); Operator "->" digunakan untuk mengakses sifat dan kaedah objek: $this->name; (perhatikan kekurangan tanda dolar di hadapan nama)
Memanggil kaedah lain di dalam kaedah juga melalui $this: $this->foo(); . Objek dicipta menggunakan operator baru berdasarkan templat yang dipanggil kelas. Kelas ditakrifkan oleh kata kunci kelas.
Akses pengubah suai dalam OOP:
Pembolehubah khas digunakan di dalam kelas ini. Ia adalah penunjuk di mana objek boleh merujuk kepada dirinya sendiri.
Untuk mengakses kaedah statik, gunakan diri sendiri::
Kaedah Bye diluluskan hujah dengan cara yang sama seperti fungsi biasa. Apabila kaedah ini dipanggil, objek menukar sifat namanya.
CONTOH 1 KEPUTUSAN:
Pembina ialah kaedah yang dipanggil secara automatik apabila objek baharu dicipta: public function __construct()() . Apabila memulakan6 objek melalui binaan utiliti baharu, PHP mencari __construct dan, jika wujud, ia dipanggil.
Ia juga mungkin untuk mencipta kaedah yang namanya sama dengan nama kelas - kaedah sedemikian juga akan dianggap sebagai pembina. Pembina boleh mengambil hujah, yang sangat memudahkan kerja dengan kelas.
CONTOH 2 KEPUTUSAN:
Dengan menggabungkan semua perkara di atas, anda boleh membuat kelas yang lebih bermakna. Sebagai contoh, kelas yang akan menyusun data dalam bentuk jadual dengan lajur bernama.
"; foreach($this->"; ) ) $test = new Table (array("a","b","c")); $test->addRow(array("a" =>1,"b" =>3,"c "=>2)); $test->addRow(array("b"=>1,"a"=>3)); $test->addRow(array("c"=>1,"b"= >3,"a" =>4)); $test->output(); ?>
"; foreach($this->
"; ) gema "
Sifat kelas Jadual ialah tatasusunan nama lajur jadual dan tatasusunan dua dimensi baris data. Pembina kelas Jadual menerima tatasusunan nama lajur jadual. Kaedah addRow menambah baris data baharu pada jadual. Kaedah output memaparkan jadual pada skrin.
CONTOH 3 KEPUTUSAN:
a b c 1 3 2 3 1 4 3 1Sifat dan kaedah kelas boleh sama ada awam (awam) atau tersembunyi (peribadi). Sifat dan kaedah tersembunyi tidak boleh diakses dari luar kelas, i.e. daripada skrip yang menggunakan kelas ini, atau daripada kelas lain.
Berdasarkan kelas sedia ada, anda boleh membuat kelas baharu menggunakan mekanisme pewarisan. Mekanisme pewarisan ialah penggunaan kelas yang ditakrifkan sebelum ini sebagai induk. Pada masa yang sama, set sifat dan kaedah kelas induk boleh dilanjutkan. Perlu diingat bahawa kelas terbitan hanya mempunyai satu induk.
Untuk mencipta kelas baharu yang mewarisi gelagat kelas sedia ada, gunakan kata kunci extends dalam pengisytiharannya. Sebagai contoh:
Kelas N2 memanjangkan kelasN1 ( ....... )
Di sini classN1 ialah kelas induk, classN2 ialah kelas terbitan.
Jika kelas terbitan tidak mengandungi pembinanya sendiri, maka pembina kelas induk digunakan semasa mencipta objeknya. Jika kelas terbitan mempunyai pembina sendiri, maka pembina kelas induk tidak dipanggil. Jika anda perlu memanggil pembina kelas induk, ini mesti dilakukan secara eksplisit. Sebagai contoh:
ClassN1::classN1();
Kelas terbitan akan mempunyai semua sifat dan kaedah kelas induk. Tetapi mereka boleh ditindih dalam kelas terbitan.
Kaedah Hello ditindih untuk kelas terbitan. Harta nama diwarisi daripada harta induk.
CONTOH 4 KEPUTUSAN:
Sejak PHP versi 4, adalah mungkin untuk memanggil kaedah kelas induk yang telah ditindih dalam objek kelas terbitan.
HASIL CONTOH 5:
Jadi, kelas terbitan boleh mewarisi, mengatasi dan melanjutkan sifat dan kaedah kelas lain.
Contoh berikut mencipta kelas HTMLTable berdasarkan kelas Jadual dalam Contoh 3. Kelas baharu menjana data yang disimpan oleh kaedah addRow kelas induk dan mengeluarkannya ke jadual HTML. Sifat $cellpadding dan $bgcolor membenarkan argumen yang sepadan ditukar, dengan pembolehubah $cellpadding ditetapkan kepada nilai lalai 2.
"; foreach ($this->headers sebagai $header) echo "$header "; echo ""; ) ) class HTMLTable memanjangkan Jadual ( public $cellpadding = "2"; public $bgcolor; function HTMLTable ($headers, $bg="FFFFFF") ( Tables::Tables($headers); $this->bgcolor = $bg; ) set fungsiCellpadding ($padding) ( $this->cellpadding = $padding; ) output fungsi () ( echo "
"; foreach ($this->data as $y) ( foreach ($y as $x) echo "$x "; echo "
"; ) gema "
bgcolor."">".$header; foreach ($this->data as $y) ( echo " |
---|
bgcolor."">$x"; ) echo " |
Ambil perhatian bahawa nilai sifat cellpadding ditukar menggunakan kaedah setCellpadding yang berasingan. Sudah tentu, nilai harta boleh diubah secara langsung, di luar objek:
$test->cellpadding = 7 ;
Tetapi ini dianggap bentuk buruk, kerana. dalam objek kompleks, apabila salah satu sifat berubah, sifat lain juga boleh berubah.
HASIL CONTOH 6:
a | b | c |
---|---|---|
1 | 3 | 2 |
3 | 1 | |
4 | 3 | 1 |
Untuk menggunakan atau tidak menggunakan teknik pengaturcaraan objek? Di satu pihak, projek yang banyak menggunakan teknologi objek boleh mengambil terlalu banyak sumber pada masa jalan. Sebaliknya, pendekatan objek yang teratur dengan ketara akan mengurangkan masa pembangunan dan menjadikan program lebih fleksibel.
Anda boleh memadam objek yang dibuat sebelum ini seperti berikut:
Unset($objName);
Berikut ialah contoh di mana objek kelas Kereta dicipta dan kemudian dipadamkan.
$myCar = Kereta baru; unset($myCar);
Selepas memanggil fungsi unset(), objek tidak lagi wujud. PHP mempunyai kaedah __destruct() khas yang dipanggil secara automatik apabila objek dimusnahkan. Di bawah ialah kelas yang mengandungi kaedah ini.
Jambatan Kelas ( function __destruct() ( echo "Jambatan musnah"; ) ) $bigBridge = Jambatan baharu; unset($bigBridge);
Apabila anda mencipta objek kelas Bridge dan kemudian memadamkannya, mesej berikut akan dipaparkan:
Jambatan musnah
Ia dipaparkan kerana panggilan kepada kaedah __destruct() apabila memanggil fungsi unset(). Apabila memadam objek, anda mungkin perlu menutup beberapa fail atau menulis maklumat ke pangkalan data.
Mengklon objek:
$a = klon $b;
Pembina tidak dipanggil apabila diklon, kaedah sihir dipanggil __klon()() . Ia TIDAK mengambil hujah dan tidak boleh dipanggil sebagai kaedah.
Untuk menukar objek kepada rentetan dan sebaliknya, fungsi berikut digunakan:
bersiri()- mengambil objek dan mengembalikan perwakilan rentetan kelas dan sifatnya;
unserialize()- mengambil rentetan yang dibuat dengan serialize() dan mengembalikan objek.
serialize() dan unserialize() berfungsi pada semua jenis data, tetapi ia tidak berfungsi pada sumber.
Kaedah khas untuk melayan fungsi serialize() dan unserialize():
__tidur()- dipanggil dengan ketat sebelum objek disiri menggunakan fungsi serialize(). Fungsi __sleep() perlu mengembalikan senarai medan kelas yang fungsi serialize() akan disertakan dalam rentetan yang dikembalikan. Anda boleh menggunakan ini untuk menanggalkan medan yang tidak diingini daripada perwakilan rentetan objek. Sebagai contoh:
Fungsi awam __sleep() ( // bersihkan kembali array_keys(get_object_vars($this)); ) __bangun()- dipanggil serta-merta selepas objek dinyahsiri menggunakan unserialize().
Antara muka ialah templat yang menentukan tingkah laku satu atau lebih kelas. Berikut ialah perbezaan utama antara antara muka dan kelas abstrak:
Sebagai contoh, anda mempunyai jadual dalam pangkalan data yang dipanggil pengguna dan terdapat beberapa medan di dalamnya, contohnya id, nama, e-mel, telefon, kata laluan, avatar Dan anda mencipta kelas untuk bekerja dengan pengguna, jadi mereka memanggilnya - Pengguna
Apakah sifat yang akan dimiliki oleh kelas ini? Jika anda melakukan perkara yang sama seperti dalam pangkalan data - id, nama, e-mel, dan sebagainya, maka ternyata setiap kali anda menukar pangkalan data, anda perlu menukar kod dalam kelas Pengguna, yang tidak begitu mudah. Sebagai contoh, anda menambah medan tapak, yang bermaksud anda perlu menambahkannya pada kelas Pengguna dan seterusnya.
Menggunakan kaedah __get() dan __set(), anda boleh mengautomasikan semua ini. Anda tidak akan mempunyai satu harta pun daripada pangkalan data dalam kelas Pengguna sama sekali, kami hanya mempunyai satu $data - kami membawanya ke sana, dan memuatkan semua yang ada dalam pangkalan data untuk pengguna ini. Dan kemudian, apabila pengaturcara meminta sesuatu, contohnya $user->email, kita hanya boleh melihat dalam kaedah __get() - jika kita telah memuatkan maklumat tersebut daripada pangkalan data, dan ia berada dalam $data["email"] - maka di sini kami untuk anda dan kembali. Dan dalam __set() ia adalah sebaliknya. Adakah terdapat medan sedemikian dalam pangkalan data? Jadi mari kita tetapkan nilai baharu kepadanya.
/** * Pengguna Kelas * @property-read integer id pengguna semasa * @property-write String tapak mengembalikan pautan ke tapak pengguna */ class User ( private $data; private $f_write=false; public function __set( $name, $ value) ($ this->data[$name] = $value; $this->f_write=true; // tanda bahawa data perlu disimpan ) public function __get($name) ( if( kosong($data))( // baca rekod dari pangkalan data ke data ) kembalikan $this->data[$name]; ) fungsi __destruct() ( if(!empty($data)&&$this->f_write) ( // simpan perubahan pada pangkalan data) ) ) $user=new User(); $user->site="http://kdg.site/"; //assign to variable echo $user->site; //paparkan nilai pembolehubah //tuliskannya ke pangkalan data. Anda jelas tidak boleh melakukan ini, kerana. pada penghujung skrip ia akan berlaku secara automatik unset($user);
Ambil perhatian bahawa rujukan dikembalikan daripada __get :
Kelas Foo ( $data peribadi = ; fungsi awam __set($nama, $value) ($ this->data[$name] = $value; ) public function & __get($name) ( return $this->data [$ nama]; ) ) $foo = new Foo(); $foo->bar = "lol"; var_dump($foo->bar);
Contoh memintas meta kelas persendirian:
Kelas _byCallStatic( // Contoh memintas kaedah kelas "peribadi", // apabila menggunakan kaedah "__callStatic()" untuk memanggil kaedah statik. fungsi statik awam __callStatic($_name, $_param) ( return call_user_func_array("static: :". $ _name, $_param); ) fungsi statik peribadi _newCall()( echo "Kaedah: ". __METHOD__; ) ) echo _byCallStatic::_newCall(114, "Integer", 157); # Keputusan: Kaedah: _byCallStatic::_newCall
Cara memanggil mana-mana kaedah dinamik melalui kaedah statik:
/** * Class o * @method static void __f(int $a1 = 1) */ class o ( public static function __callStatic($method, $args) ($class = get_called_class(); $obj = new $class( $args); $method = substr($method, 2); $pass = array_slice($args,1); $reflection = new ReflectionMethod($obj, $method); return $reflection->invokeArgs($obj, $ lulus); ) fungsi awam f($a1 = 1) ( var_dump("oo", func_get_args()); ) ) kelas a dilanjutkan o ( public function f($a1 = 1, $a2 = 2) ( var_dump(" aa", $a1); ) ) kelas b memanjangkan o ( fungsi awam f($b1 = 1) ( var_dump("bb", $b1); ) ) a::__f(1,2,3); b::__f(4,5,6);
Penerangan berguna tentang bekerja dengan ReflectionClass, apabila anda boleh menganalisis sifat dan kaedah kelas, semak parameter terhadap templat, dsb.: http://habrahabr.ru/post/139649/
Anjing diterima: tulang dan tali
Untuk melakukan ini, anda perlu mencipta objek yang melaksanakan antara muka ArrayAccess daripada SPL. Contoh berikut melaksanakan objek yang datanya boleh diakses dalam gaya mengakses tatasusunan dan melalui mendapatkan sifat:
Kelas MyArray melaksanakan ArrayAccess ( protected $arr = array(); public function offsetSet($key, $value) ($this->arr[$key] = $value; ) public function offsetUnset($key) ( unset( $this ->arr[$key]); ) public function offsetGet($key) ( return $this->arr[$key]; ) public function offsetExists($key) ( return isset ($this->arr[$ key] ); ) public function __get($key) ( return $this->offsetGet($key); ) public function __set($key, $val) ($this->offsetSet($key, $val); ) ) $ a = newMyArray(); $a["whoam"] = "Adakah saya nilai tatasusunan atau objek?
"; echo $a["whoam"]; echo $a->whoam;
Adakah saya nilai tatasusunan, atau objek? Adakah saya nilai tatasusunan, atau objek?
Anda juga boleh menggunakan definisi fungsi __autoload() untuk autoload kelas;
Pengecualian digunakan untuk mengendalikan ralat bukan kritikal.
Cuba ( $a = 1; $b = 0; if($b == 0) buang Exception baharu ("bahagi dengan sifar!"); $c = $a/$b; ) tangkap (Exception $e) ( echo $e->getMessage(); echo $e->getLine(); )
Pengecualian ialah kelas terbina dalam. Jika anda menekan lontaran, maka kod di bawah tidak dilaksanakan dan peralihan dibuat kepada blok tangkapan.
Blok cuba-tangkap digunakan dalam kedua-dua pengaturcaraan prosedural dan OOP. Ia digunakan untuk menangkap ralat - blok percubaan besar dengan banyak lontaran dan semuanya ditangkap di satu tempat - blok tangkapan.
Pengecualian boleh diwarisi, adalah wajar untuk memuatkan semula pembina: kelas MyException memanjangkan Pengecualian ( function __construct($msg)( parent::__construct($msg); ) )
Terdapat beberapa blok tangkapan - untuk setiap kelas Pengecualian.
Kemas kini terakhir: 1.11.2015
Apabila mencipta program dalam PHP dan blok individunya, fungsi yang diwakili oleh fungsi mungkin cukup untuk kita. Walau bagaimanapun, PHP mempunyai keupayaan pengaturcaraan lain yang diwakili oleh pengaturcaraan berorientasikan objek. Dalam sesetengah kes, program yang menggunakan OOP lebih mudah difahami, lebih mudah diselenggara dan diubah.
Konsep utama paradigma OOP ialah konsep "kelas" dan "objek". Penerangan objek ialah kelas, dan objek mewakili contoh kelas itu. Kita boleh membuat analogi berikut: setiap orang mempunyai idea tentang seseorang - kehadiran dua lengan, dua kaki, kepala, sistem pencernaan, sistem saraf, otak, dll. Terdapat beberapa templat - templat ini boleh dipanggil kelas. Tetapi orang yang benar-benar sedia ada (sebenarnya contoh kelas ini) ialah objek kelas ini.
Kata kunci yang digunakan untuk membuat kelas dalam PHP ialah kelas . Sebagai contoh, kelas baharu yang mewakili pengguna:
ClassUser()
Untuk mencipta objek kelas Pengguna, kata kunci baharu digunakan:
Dalam kes ini, pembolehubah $user ialah objek kelas Pengguna. Menggunakan fungsi print_r(), anda boleh mencetak kandungan objek, sama seperti tatasusunan.
Kelas boleh mengandungi sifat yang menerangkan beberapa ciri objek, dan kaedah yang menentukan kelakuannya. Mari tambahkan beberapa sifat dan kaedah pada kelas Pengguna:
nama; Umur: $ini->umur
"; ) ) $user = Pengguna baharu; $user->name="Tom"; // tetapkan $name property $user->age=30; // tetapkan $age property $user->getInfo(); // panggil ke kaedah getInfo() print_r($user); ?>
Di sini kelas Pengguna mengandungi dua sifat: $name dan $age . Sifat diisytiharkan sebagai pembolehubah biasa didahului oleh pengubah suai akses - dalam kes ini, pengubah suai awam.
Kaedah mewakili fungsi biasa yang melakukan tindakan tertentu. Di sini, fungsi getInfo() mengeluarkan kandungan pembolehubah yang ditakrifkan sebelum ini.
Untuk merujuk kepada objek semasa daripada kelas yang sama, ungkapan $this digunakan - ia mewakili objek semasa. Pengendali capaian -> digunakan untuk mengakses sifat dan kaedah sesuatu objek. Sebagai contoh, untuk mendapatkan nilai harta $name, anda akan menggunakan ungkapan $this->name . Selain itu, apabila mengakses sifat, tanda $ tidak digunakan.
Apabila menggunakan objek kelas Pengguna, pernyataan akses juga digunakan untuk mendapatkan atau menetapkan nilai sifat, serta untuk memanggil kaedah.
Pembina ialah kaedah khas yang dilaksanakan apabila objek dicipta dan berfungsi untuk memulakan sifatnya. Untuk mencipta pembina, anda mesti mengisytiharkan fungsi bernama __construct (dengan dua garis bawah di hadapan):
nama = $nama; $ini->umur = $umur; ) function getInfo() ( echo "Nama: $this->name ; Umur: $this->age
"; ) ) $user2 = new User("John", 33); $user2->getInfo(); ?>
Fungsi pembina dalam kes ini mengambil dua parameter. Nilai mereka dihantar ke sifat kelas. Dan sekarang, untuk mencipta objek, kita perlu menghantar nilai untuk parameter yang sepadan: $user2 = new User("John", 33);
Untuk menjadikan pembina lebih fleksibel, kita boleh menetapkan satu atau lebih parameter sebagai pilihan. Kemudian, apabila mencipta objek, tidak perlu menentukan semua parameter. Sebagai contoh, mari tukar pembina seperti ini:
Fungsi __build($name="Volume", $age=33) ( $this->name = $name; $this->age = $age; )
Oleh itu, jika tiada parameter diberikan, nilai "Volume" dan 33 akan digunakan sebaliknya. Dan kini kita boleh mencipta objek Pengguna dalam beberapa cara:
$user1 = new User("John", 25); $user1->getInfo(); $user2 = new User("Jack"); $user2->getInfo(); $user3 = new User(); $user3->getInfo();
Pemusnah digunakan untuk membebaskan sumber yang digunakan oleh program - untuk membebaskan fail terbuka, membuka sambungan pangkalan data, dan sebagainya. Pemusnah objek dipanggil oleh penterjemah PHP itu sendiri selepas rujukan terakhir kepada objek itu dalam program telah hilang.
Pemusnah ditakrifkan menggunakan fungsi __destruct (dua garis bawah utama):
Pengguna Kelas ( public $name, $age; function __construct($name, $age) ( $this->name = $name; $this->age = $age; ) function getInfo() ( echo "Nama: $this ->nama ; Umur: $ini->umur
"; ) fungsi __destruct() ( echo "Panggil pemusnah"; ) )
Fungsi pemusnah ditakrifkan tanpa parameter, dan apabila objek tidak lagi dirujuk dalam program, ia akan dimusnahkan dan pemusnah akan dipanggil.
Pembolehubah yang menjadi ahli kelas dipanggil "sifat". Ia juga dipanggil menggunakan istilah lain seperti "atribut" atau "medan", tetapi untuk tujuan dokumentasi ini, kami akan merujuknya sebagai sifat. Mereka ditakrifkan dengan kata kunci awam, dilindungi atau persendirian, mengikut peraturan untuk pengisytiharan pembolehubah yang betul. Pengisytiharan ini mungkin mengandungi pemulaan, tetapi pemulaan itu mestilah nilai malar, iaitu, nilai mesti dikira pada masa penyusunan dan tidak boleh bergantung pada maklumat yang diperoleh pada masa berjalan untuk mengiranya.
Pembolehubah pseudo $this tersedia dalam mana-mana kaedah kelas apabila kaedah itu dipanggil daripada konteks objek. $ini ialah rujukan kepada objek yang dipanggil (biasanya objek kepunyaan kaedah, tetapi mungkin objek lain jika kaedah dipanggil secara statik daripada konteks objek kedua).
Beispiel #1 Menentukan sifat
kelas SimpleClass
{
public $var1 = "hello " . "dunia" ;
awam $var2 =<<
EOD;
// takrifan harta yang betul pada PHP 5.6.0:
awam $var3 = 1 + 2 ;
// takrif harta yang salah:
awam $var4 = diri :: myStaticMethod();
awam $var5 = $myVar ;
// takrifan sifat yang betul:
awam $var6 = myConstant ;
public $var7 = array(true , false );
// definisi harta yang betul pada PHP 5.3.0:
awam $var8 =<<<"EOD"
Hai dunia
EOD;
}
?>
Komen:
Memandangkan PHP 5.3.0 dan nowdocs boleh digunakan dalam mana-mana konteks data statik, termasuk mentakrifkan sifat.
Beispiel #2 nowdoc contoh untuk memulakan sifat
kelas foo(
// Sejak PHP 5.3.0
awam $bar =<<<"EOT"
bar
EOT;
awam $asas =<<
EOT;
}
?>
Komen:
Sokongan untuk Nowdoc dan Heredoc telah ditambah dalam PHP 5.3.0.
7 tahun lepas
Sekiranya ini menjimatkan sesiapa sahaja pada bila-bila masa, saya menghabiskan masa yang lama untuk mencari sebab yang berikut tidak berfungsi:
kelas MyClass
{
persendirian $foo = SALAH;
{
$this->$foo = BENAR;
echo($this->$foo);
}
}
$bar = MyClass baharu();
memberikan "Ralat maut: Tidak dapat mengakses harta kosong dalam ...test_class.php pada baris 8"
Perubahan halus untuk mengalih keluar $ sebelum akses $foo membetulkan ini:
kelas MyClass
{
persendirian $foo = SALAH;
Fungsi awam __construct()
{
$this->foo = BENAR;
echo($this->foo);
}
}
$bar = MyClass baharu();
Saya rasa kerana ia menganggap $foo seperti pembolehubah dalam contoh pertama, jadi cuba panggil $this->FALSE (atau sesuatu di sepanjang baris tersebut) yang tidak masuk akal. Ia jelas apabila anda sedar, tetapi ada bukankah sebarang contoh akses pada halaman ini yang menunjukkan perkara itu.
4 tahun lepas
Anda boleh mengakses nama harta dengan sengkang di dalamnya (contohnya, kerana anda menukar fail XML kepada objek) dengan cara berikut:
$ref = new StdClass();
$ref ->( "ref-type" ) = "Artikel Jurnal" ;
var_dump($ref);
?>
8 tahun lepas
$ini boleh dihantar ke tatasusunan. Tetapi apabila berbuat demikian, ia memberi awalan kepada nama harta/kunci tatasusunan baharu dengan data tertentu bergantung pada klasifikasi harta. Nama harta awam tidak ditukar. Sifat yang dilindungi diawali dengan "*" berlapik ruang. Harta persendirian diawali dengan nama kelas berlapik ruang...
ujian kelas
{
awam $var1 = 1;
dilindungi $var2 = 2 ;
persendirian $var3 = 3 ;
statik $var4 = 4 ;
fungsi awam toArray()
{
pulangkan (susun) $this ;
}
}
$t = ujian baharu ;
print_r($t -> toArray());
/* output:
tatasusunan
=> 1
[*var2] => 2
[ujian var3] => 3
)
*/
?>
Ini adalah tingkah laku yang didokumenkan apabila menukar sebarang objek kepada tatasusunan (lihathalaman manual PHP). Semua sifat tanpa mengira keterlihatan akan ditunjukkan apabila menghantar objek ke tatasusunan (dengan pengecualian beberapa objek terbina dalam).
Untuk mendapatkan tatasusunan dengan semua nama sifat tidak diubah, gunakan fungsi "get_object_vars($this)" dalam mana-mana kaedah dalam skop kelas untuk mendapatkan tatasusunan semua sifat tanpa mengira keterlihatan luaran atau "get_object_vars($object)" di luar skop kelas untuk dapatkan semula tatasusunan harta awam sahaja (lihat:halaman manual PHP).
9 tahun lepas
Jangan mengelirukan versi php sifat dengan sifat dalam bahasa lain (C++ sebagai contoh). Dalam php, sifat adalah sama dengan atribut, pembolehubah mudah tanpa fungsi. Ia harus dipanggil atribut, bukan sifat.
Sifat mempunyai kefungsian pengakses tersirat dan mutator. Saya telah mencipta kelas abstrak yang membenarkan fungsi harta tersirat.
Kelas abstrak PropertyObject
{
fungsi awam __get($name )
{
if (method_exists ($this , ($method = "get_" . $name )))
{
pulangkan $ini -> $kaedah ();
}
lain kembali;
}
Fungsi awam __isset($name )
{
if (method_exists ($this , ($method = "isset_" . $name )))
{
pulangkan $ini -> $kaedah ();
}
lain kembali;
}
fungsi awam __set ($name , $value )
{
jika (kaedah_wujud ($ini , ($kaedah = "set_" . $nama )))
{
$this -> $method ($value );
}
}
Fungsi awam __unset($name )
{
if (method_exists ($this , ($method = "unset_" . $name )))
{
$this -> $method();
}
}
}
?>
selepas melanjutkan kelas ini, anda boleh mencipta aksesori dan mutator yang akan dipanggil secara automatik, menggunakan kaedah ajaib php, apabila harta yang sepadan diakses.
5 tahun lepas
Kaedah yang dikemas kini objectThis() kepada sifat tatasusunan kelas transtypage atau tatasusunan kepada stdClass.
Semoga ia membantu anda.
fungsi awam objectThis($array = null) (
jika (!$array) (
foreach ($ini sebagai $property_name => $property_values) (
if (is_array($property_values) && !empty($property_values)) (
$this->($property_name) = $this->objectThis($property_values);
) else if (is_array($property_values) && kosong($property_values)) (
$this->($property_name) = new stdClass();
}
}
) lain (
$objek = new stdClass();
foreach ($array as $index => $values) (
if (is_array($values) && kosong($values)) (
$objek->($index) = new stdClass();
) else if (is_array($values)) (
$objek->($indeks) = $ini->objekIni($nilai);
) lain (
$objek->($indeks) = $nilai;
}
}
pulangkan $objek;
}
}
Objek adalah salah satu konsep asas pengaturcaraan berorientasikan objek.
Objek ialah pembolehubah yang di instantiated mengikut corak khas yang dipanggil kelas. Konsep objek dan kelas adalah sebahagian daripada paradigma pengaturcaraan berorientasikan objek (OOP).
Objek ialah koleksi data (sifat) dan fungsi (kaedah) untuk memprosesnya. Data dan kaedah dipanggil ahli kelas. Secara umum, objek ialah apa sahaja yang menyokong pengkapsulan.
Struktur dalaman objek adalah serupa dengan cincang, kecuali operator -> digunakan untuk mengakses elemen dan fungsi individu dan bukannya kurungan segi empat sama.
Untuk memulakan objek, gunakan ungkapan new , yang mencipta contoh objek dalam pembolehubah.
kelas foo
{
fungsi do_foo()
{
echo "Melakukan foo." ;
}
}
$bar = foo baharu ;
$bar -> do_foo();
?>
Dalam objek, data dan kod (ahli kelas) boleh sama ada awam atau tidak. Data awam dan ahli kelas boleh diakses oleh bahagian lain program yang bukan sebahagian daripada objek. Tetapi data peribadi dan ahli kelas hanya tersedia di dalam objek ini.
Penerangan kelas dalam PHP bermula dengan perkataan fungsi kelas:
nama_kelas kelas (
// penerangan ahli kelas - data dan kaedah untuk memprosesnya
}
Untuk pengumuman objek operator mesti digunakan baru:
Objek = Nama Kelas baharu;
Data diterangkan menggunakan perkataan perkhidmatan var. Kaedah ini diterangkan dengan cara yang sama seperti fungsi biasa. Anda juga boleh menghantar parameter kepada kaedah.
Contoh kelas PHP:
php
// Buat kelas Coor baharu:
classCoor(
// data (sifat):
var$name;
var$addr;
// kaedah:
nama fungsi()(
bergema"
Mengakses Kelas dan Objek dalam PHP
Kami telah melihat bagaimana kelas ditakrifkan dan objek dicipta. Sekarang kita perlu mengakses ahli kelas, untuk PHP ini menyediakan pengendali -> . Berikut adalah contoh:
php
// Buat kelas Coor baharu:
classCoor(
// data (sifat):
var$name;
// kaedah:
function getname()(
bergema"
Untuk mengakses ahli kelas dalam kelas, anda perlu menggunakan penunjuk $ini, yang sentiasa merujuk kepada objek semasa. kaedah yang diubah suai getname():
fungsi Getname() (
echo $ini->nama;
}
Dengan cara yang sama, anda boleh menulis kaedah setname():
function Setname($name) (
$ini->nama = $nama;
}
Sekarang anda boleh menggunakan kaedah untuk menukar nama setname():
$objek->Setname("Peter");
$objek->Getname();
Dan berikut ialah penyenaraian kod lengkap:
php
// Buat kelas Coor baharu:
classCoor(
// data (sifat):
var$name;
// kaedah:
function getname()(
echo $ini -> nama ;
}
function Setname($name)(
$ini -> nama = $nama;
}
}
// Buat objek kelas Coor:
$objek = baru Coor ;
// Sekarang kita menggunakan kaedah Setname() untuk menukar nama:
$objek -> Nama Set("Nick");
// Dan untuk akses, seperti sebelumnya, Getname():
$objek -> getname();
// Keluaran skrip "Nick"
?>
penunjuk $ini juga boleh digunakan untuk mengakses kaedah, bukan hanya akses data:
function Setname($name) (
$ini->nama = $nama;
$this->Getname();
}
Inisialisasi objek
Kadang-kadang menjadi perlu untuk memulakan objek - untuk memberikan nilai awal kepada sifatnya. Katakan nama kelas ialah Coor dan ia mengandungi dua sifat: nama orang dan bandar kediamannya. Anda boleh menulis kaedah (fungsi) yang akan melaksanakan pemulaan objek, sebagai contoh Di dalamnya():
php
// Buat kelas Coor baharu:
classCoor(
// data (sifat):
var$name;
var$city;
// Kaedah permulaan:
function Init($name)(
$ini -> nama = $nama;
$ this -> city = "London" ;
}
}
// Buat objek kelas Coor:
$objek = baru Coor ;
// Untuk memulakan objek, panggil kaedah dengan segera:
$objek -> init();
?>
Perkara utama adalah jangan lupa untuk memanggil fungsi dengan segera selepas mencipta objek, atau memanggil beberapa kaedah antara penciptaan (operator baru) objek dan permulaannya (dengan memanggil Di dalamnya).
Agar PHP mengetahui bahawa kaedah tertentu harus dipanggil secara automatik apabila objek dicipta, ia perlu diberi nama yang sama dengan kelas ( Coor):
functionCoor($nama)
$ini->nama = $nama;
$this->city = "London";
}
Kaedah yang memulakan objek dipanggil pembina. Walau bagaimanapun, PHP tidak mempunyai pemusnah, kerana sumber dibebaskan secara automatik apabila skrip keluar.
Tukar kepada objek
Jika objek ditukar kepada objek, ia tidak berubah. Jika nilai mana-mana jenis lain ditukar kepada objek, contoh baharu kelas terbina dalam stdClass dicipta. Jika nilai itu kosong, kejadian baharu juga akan kosong. Sebarang nilai lain akan mengandunginya dalam pembolehubah ahli skalar:
Forum Portal PHP. SU
kayabaparts.ru - Dewan masuk, dapur, ruang tamu. Taman. kerusi. Bilik tidur